zss
7 天以前 11502c7f9f26edda0733c43a17b5b773ee8447e4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.ruoyi.account.service.impl;
 
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.bean.dto.StatementAccountDto;
import com.ruoyi.account.bean.dto.purchase.PurchaseInboundDto;
import com.ruoyi.account.bean.dto.purchase.PurchaseReturnDto;
import com.ruoyi.account.bean.dto.sales.SalesOutboundDto;
import com.ruoyi.account.bean.dto.sales.SalesReturnDto;
import com.ruoyi.account.bean.vo.StatementAccountVo;
import com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo;
import com.ruoyi.account.bean.vo.purchase.PurchaseReturnVo;
import com.ruoyi.account.bean.vo.sales.SalesOutboundVo;
import com.ruoyi.account.bean.vo.sales.SalesReturnVo;
import com.ruoyi.account.mapper.AccountStatementDetailsMapper;
import com.ruoyi.account.mapper.AccountStatementMapper;
import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper;
import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
import com.ruoyi.account.pojo.AccountStatement;
import com.ruoyi.account.pojo.AccountStatementDetails;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.account.service.AccountStatementService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 芯导软件(江苏)有限公司
 * @since 2026-05-19 09:42:47
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class AccountStatementServiceImpl extends ServiceImpl<AccountStatementMapper, AccountStatement> implements AccountStatementService {
 
    private final AccountStatementMapper accountStatementMapper;
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    private final StockOutRecordMapper stockOutRecordMapper;
    private final StockInRecordMapper stockInRecordMapper;
    private final ReturnManagementMapper returnManagementMapper;
    private final AccountStatementDetailsMapper accountStatementDetailsMapper;
    private final AccountPurchasePaymentMapper accountPurchasePaymentMapper;
    private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
    private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
 
    @Override
    public StatementAccountVo getAccountStatementDetailsByMonth(StatementAccountDto statementAccountDto) {
        //对账月份转换成开始日期和结束日期区间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        YearMonth yearMonth = YearMonth.parse(statementAccountDto.getStatementMonth(), formatter);
        statementAccountDto.setStartDate(yearMonth.atDay(1));
        statementAccountDto.setEndDate(yearMonth.atEndOfMonth());
        if (statementAccountDto.getAccountType() == 1){
            //应收对账--Customer
            return getAccountStatementDetailsByCustomerAndMonth(statementAccountDto);
        }else {
            //应付对账--SupplierManage
            return getAccountStatementDetailsBySupplierAndMonth(statementAccountDto);
        }
    }
 
    @Override
    public boolean addAccountStatement(StatementAccountVo statementAccountVo) {
        //同一客户或者同一供应商,一个月份只能有一个对账单
        List<AccountStatement> accountStatements = accountStatementMapper.selectList(Wrappers.<AccountStatement>lambdaQuery()
                .eq(AccountStatement::getStatementMonth, statementAccountVo.getStatementMonth())
                .eq(AccountStatement::getAccountType, statementAccountVo.getAccountType())
                .eq(AccountStatement::getCustomerId, statementAccountVo.getCustomerId()));
        if (CollectionUtils.isNotEmpty(accountStatements)){
            throw new ServiceException("同一客户或者同一供应商,一个月份只能有一个对账单");
        }
        AccountStatement accountStatement = new AccountStatement();
        BeanUtils.copyProperties(statementAccountVo, accountStatement);
        accountStatement.setStatementNumber(genStatementAccountNo());
        boolean save = save(accountStatement);
        statementAccountVo.getAccountStatementDetails().stream().forEach(accountStatementDetails -> {
            accountStatementDetails.setAccountStatementId(accountStatement.getId());
            //添加对账单明细
            accountStatementDetailsMapper.insert(accountStatementDetails);
        });
        return save;
    }
 
    @Override
    public boolean deleteAccountStatement(List<Long> ids) {
        //删除对账单明细
        accountStatementDetailsMapper.delete(Wrappers.<AccountStatementDetails>lambdaQuery().in(AccountStatementDetails::getAccountStatementId, ids));
        return removeByIds(ids);
    }
 
    @Override
    public IPage<StatementAccountVo> listPageAccountStatement(Page page, StatementAccountDto statementAccountDto) {
        return accountStatementMapper.listPageAccountStatement(page, statementAccountDto);
    }
 
    @Override
    public void exportAccountStatement(HttpServletResponse response, StatementAccountDto statementAccountDto) {
        List<StatementAccountVo> list = accountStatementMapper.listPageAccountStatement(new Page(1,-1),statementAccountDto).getRecords();
        ExcelUtil<StatementAccountVo> util = new ExcelUtil<>(StatementAccountVo.class);
        util.exportExcel(response, list , "对账单");
    }
 
    //根据客户和月份获取对账详情(销售)
    private StatementAccountVo getAccountStatementDetailsByCustomerAndMonth(StatementAccountDto statementAccountDto) {
        StatementAccountVo statementAccountVo = new StatementAccountVo();
        statementAccountVo.setAccountType(1);//应收对账
        List<AccountStatementDetails> accountStatementDetailsList = new ArrayList<>();
        /*查询出库明细*/
        SalesOutboundDto salesOutboundDto = new SalesOutboundDto();
        salesOutboundDto.setCustomerId(statementAccountDto.getCustomerId());
        salesOutboundDto.setStartDate(statementAccountDto.getStartDate());
        salesOutboundDto.setEndDate(statementAccountDto.getEndDate());
        List<SalesOutboundVo> salesOutboundVos = stockOutRecordMapper.listPageAccountSales(new Page(1, -1), salesOutboundDto).getRecords();
        salesOutboundVos.stream().forEach(salesOutboundVo -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=出库日期
            accountStatementDetails.setOccurrenceDate(salesOutboundVo.getShippingDate());
            //单据编号=出库单号
            accountStatementDetails.setReceiptNumber(salesOutboundVo.getOutboundBatches());
            //类型=出库
            accountStatementDetails.setType(1);
            //金额=出库金额
            accountStatementDetails.setAmount(salesOutboundVo.getOutboundAmount());
            //备注
            accountStatementDetails.setRemark("产品销售出库,产品:"+salesOutboundVo.getProductName());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        /*查询收款明细*/
        List<AccountSalesCollection> accountSalesCollections = accountSalesCollectionMapper.selectList(Wrappers.<AccountSalesCollection>lambdaQuery()
                .eq(AccountSalesCollection::getCustomerId, statementAccountDto.getCustomerId())
                .between(AccountSalesCollection::getCollectionDate, statementAccountDto.getStartDate(), statementAccountDto.getEndDate()));
        accountSalesCollections.stream().forEach(accountSalesCollection -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=收款日期
            accountStatementDetails.setOccurrenceDate(accountSalesCollection.getCollectionDate());
            //单据编号=收款单号
            accountStatementDetails.setReceiptNumber(accountSalesCollection.getCollectionNumber());
            //类型=收款
            accountStatementDetails.setType(3);
            //金额=收款金额
            accountStatementDetails.setAmount(accountSalesCollection.getCollectionAmount());
            //备注
            accountStatementDetails.setRemark("客户回款,备注:"+accountSalesCollection.getRemark());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        /*查询退货明细*/
        SalesReturnDto salesReturnDto = new SalesReturnDto();
        salesReturnDto.setCustomerId(statementAccountDto.getCustomerId());
        salesReturnDto.setStartDate(statementAccountDto.getStartDate());
        salesReturnDto.setEndDate(statementAccountDto.getEndDate());
        List<SalesReturnVo> salesReturnVos = returnManagementMapper.listPageAccountSalesReturn(new Page(1, -1), salesReturnDto).getRecords();
        salesReturnVos.stream().forEach(salesReturnVo -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=退货日期
            accountStatementDetails.setOccurrenceDate(salesReturnVo.getMakeTime().toLocalDate());
            //单据编号=退货单号
            accountStatementDetails.setReceiptNumber(salesReturnVo.getReturnNo());
            //类型=退货
            accountStatementDetails.setType(5);
            //金额=退款金额
            accountStatementDetails.setAmount(salesReturnVo.getRefundAmount());
            //备注
            accountStatementDetails.setRemark("产品退货,原因:"+salesReturnVo.getReturnReason());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        //期初余额=上个月的期末余额
        statementAccountVo.setOpeningBalance(BigDecimal.ZERO);
        List<AccountStatement> accountStatements = accountStatementMapper.selectList(Wrappers.<AccountStatement>lambdaQuery()
                .eq(AccountStatement::getAccountType, 1)
                .eq(AccountStatement::getCustomerId, statementAccountDto.getCustomerId())
                .eq(AccountStatement::getStatementMonth,
                        YearMonth.parse(statementAccountDto.getStatementMonth()).minusMonths(1).toString()));
        if (CollectionUtils.isNotEmpty(accountStatements)){
            statementAccountVo.setOpeningBalance(accountStatements.get(accountStatements.size() - 1).getClosingBalance());
        }
        //本期应收=出库-退货金额累计
        statementAccountVo.setCurrentPlan(salesOutboundVos.stream().map(SalesOutboundVo::getOutboundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)
                .subtract(salesReturnVos.stream().map(SalesReturnVo::getRefundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
        //本期收款=收款金额累计
        statementAccountVo.setCurrentActually(accountSalesCollections.stream().map(AccountSalesCollection::getCollectionAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        //期末余额=期初+应收-收款
        statementAccountVo.setClosingBalance(statementAccountVo.getOpeningBalance().add(statementAccountVo.getCurrentPlan()).subtract(statementAccountVo.getCurrentActually()));
        statementAccountVo.setAccountStatementDetails(accountStatementDetailsList);
        return statementAccountVo;
    }
 
    //根据供应商和月份获取对账详情(采购)
    private StatementAccountVo getAccountStatementDetailsBySupplierAndMonth(StatementAccountDto statementAccountDto) {
        StatementAccountVo statementAccountVo = new StatementAccountVo();
        statementAccountVo.setAccountType(2);//应付对账
        List<AccountStatementDetails> accountStatementDetailsList = new ArrayList<>();
        /*查询入库明细*/
        PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto();
        purchaseInboundDto.setSupplierId(statementAccountDto.getCustomerId());
        purchaseInboundDto.setStartDate(statementAccountDto.getStartDate());
        purchaseInboundDto.setEndDate(statementAccountDto.getEndDate());
        List<PurchaseInboundVo> purchaseInboundVos = stockInRecordMapper.listPageAccountPurchase(new Page(1, -1), purchaseInboundDto).getRecords();
        purchaseInboundVos.stream().forEach(purchaseInboundVo -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=入库日期
            accountStatementDetails.setOccurrenceDate(purchaseInboundVo.getInboundDate());
            //单据编号=入库单号
            accountStatementDetails.setReceiptNumber(purchaseInboundVo.getInboundBatches());
            //类型=入库
            accountStatementDetails.setType(2);
            //金额=入库金额
            accountStatementDetails.setAmount(purchaseInboundVo.getInboundAmount());
            //备注
            accountStatementDetails.setRemark("产品采购入库,产品:"+purchaseInboundVo.getProductName());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        /*查询付款明细*/
        List<AccountPurchasePayment> accountPurchasePayments = accountPurchasePaymentMapper.selectList(Wrappers.<AccountPurchasePayment>lambdaQuery()
                .eq(AccountPurchasePayment::getSupplierId, statementAccountDto.getCustomerId())
                .between(AccountPurchasePayment::getPaymentDate, statementAccountDto.getStartDate(), statementAccountDto.getEndDate()));
        accountPurchasePayments.stream().forEach(accountPurchasePayment -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=付款日期
            accountStatementDetails.setOccurrenceDate(accountPurchasePayment.getPaymentDate());
            //单据编号=付款单号
            accountStatementDetails.setReceiptNumber(accountPurchasePayment.getPaymentNumber());
            //类型=付款
            accountStatementDetails.setType(4);
            //金额=付款金额
            accountStatementDetails.setAmount(accountPurchasePayment.getPaymentAmount());
            //备注
            accountStatementDetails.setRemark("支付货款,备注:"+accountPurchasePayment.getRemark());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        /*查询退货明细*/
        PurchaseReturnDto purchaseReturnDto = new PurchaseReturnDto();
        purchaseReturnDto.setSupplierId(statementAccountDto.getCustomerId());
        purchaseReturnDto.setStartDate(statementAccountDto.getStartDate());
        purchaseReturnDto.setEndDate(statementAccountDto.getEndDate());
        List<PurchaseReturnVo> purchaseReturnVos = purchaseReturnOrdersMapper.listPageAccountPurchaseReturn(new Page(1, -1), purchaseReturnDto).getRecords();
        purchaseReturnVos.stream().forEach(purchaseReturnVo -> {
            AccountStatementDetails accountStatementDetails = new AccountStatementDetails();
            //数据日期=退货日期
            accountStatementDetails.setOccurrenceDate(purchaseReturnVo.getPreparedAt().toLocalDate());
            //单据编号=退货单号
            accountStatementDetails.setReceiptNumber(purchaseReturnVo.getReturnNo());
            //类型=退货
            accountStatementDetails.setType(5);
            //金额=退款金额
            accountStatementDetails.setAmount(purchaseReturnVo.getTotalAmount());
            //备注
            accountStatementDetails.setRemark("产品退货,退货方式:"+purchaseReturnVo.getReturnType());
            accountStatementDetailsList.add(accountStatementDetails);
        });
        //期初余额=上个月的期末余额
        statementAccountVo.setOpeningBalance(BigDecimal.ZERO);
        List<AccountStatement> accountStatements = accountStatementMapper.selectList(Wrappers.<AccountStatement>lambdaQuery()
                .eq(AccountStatement::getAccountType, 2)
                .eq(AccountStatement::getCustomerId, statementAccountDto.getCustomerId())
                .eq(AccountStatement::getStatementMonth,
                        YearMonth.parse(statementAccountDto.getStatementMonth()).minusMonths(1).toString()));
        if (CollectionUtils.isNotEmpty(accountStatements)){
            statementAccountVo.setOpeningBalance(accountStatements.get(accountStatements.size() - 1).getClosingBalance());
        }
        //本期应付=入库-退货金额累计
        statementAccountVo.setCurrentPlan(purchaseInboundVos.stream().map(PurchaseInboundVo::getInboundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)
                .subtract(purchaseReturnVos.stream().map(PurchaseReturnVo::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
        //本期付款=付款金额累计
        statementAccountVo.setCurrentActually(accountPurchasePayments.stream().map(AccountPurchasePayment::getPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        //期末余额=期初+应收-收款
        statementAccountVo.setClosingBalance(statementAccountVo.getOpeningBalance().add(statementAccountVo.getCurrentPlan()).subtract(statementAccountVo.getCurrentActually()));
        statementAccountVo.setAccountStatementDetails(accountStatementDetailsList);
        return statementAccountVo;
    }
 
    private String genStatementAccountNo() {
        return "DZ" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }
}