gaoluyang
2026-10-29 5276c9fe5c3e5c2a5bca3ed1510ecdd0e728f516
src/main/java/com/ruoyi/account/service/impl/AccountStatementServiceImpl.java
@@ -37,6 +37,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
@@ -69,11 +70,18 @@
    @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());
        //对账期间支持按日(yyyy-MM-dd)或按月(yyyy-MM),转换成开始日期和结束日期区间
        String statementMonth = statementAccountDto.getStatementMonth();
        if (statementMonth != null && statementMonth.length() == 10) {
            LocalDate day = LocalDate.parse(statementMonth, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            statementAccountDto.setStartDate(day);
            statementAccountDto.setEndDate(day);
        } else {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
            YearMonth yearMonth = YearMonth.parse(statementMonth, formatter);
            statementAccountDto.setStartDate(yearMonth.atDay(1));
            statementAccountDto.setEndDate(yearMonth.atEndOfMonth());
        }
        if (statementAccountDto.getAccountType() == 1){
            //应收对账--Customer
            return getAccountStatementDetailsByCustomerAndMonth(statementAccountDto);
@@ -85,13 +93,13 @@
    @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("同一客户或者同一供应商,一个月份只能有一个对账单");
            throw new ServiceException("同一客户或者同一供应商,同一对账期间只能有一个对账单");
        }
        AccountStatement accountStatement = new AccountStatement();
        BeanUtils.copyProperties(statementAccountVo, accountStatement);
@@ -187,16 +195,8 @@
            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.setOpeningBalance(getPreviousClosingBalance(1, statementAccountDto.getCustomerId(), statementAccountDto.getStatementMonth()));
        //本期应收=出库-退货金额累计
        statementAccountVo.setCurrentPlan(salesOutboundVos.stream().map(SalesOutboundVo::getOutboundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)
                .subtract(salesReturnVos.stream().map(SalesReturnVo::getRefundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
@@ -271,16 +271,8 @@
            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.setOpeningBalance(getPreviousClosingBalance(2, statementAccountDto.getCustomerId(), statementAccountDto.getStatementMonth()));
        //本期应付=入库-退货金额累计
        statementAccountVo.setCurrentPlan(purchaseInboundVos.stream().map(PurchaseInboundVo::getInboundAmount).reduce(BigDecimal.ZERO, BigDecimal::add)
                .subtract(purchaseReturnVos.stream().map(PurchaseReturnVo::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
@@ -292,6 +284,21 @@
        return statementAccountVo;
    }
    /**
     * 取同一客户/供应商在当前对账期间之前最近一期对账单的期末余额,作为本期期初余额。
     * 支持按月(yyyy-MM)和按日(yyyy-MM-dd)两种对账期间。
     */
    private BigDecimal getPreviousClosingBalance(Integer accountType, Long customerId, String statementMonth) {
        AccountStatement previous = accountStatementMapper.selectOne(Wrappers.<AccountStatement>lambdaQuery()
                .eq(AccountStatement::getAccountType, accountType)
                .eq(AccountStatement::getCustomerId, customerId)
                .lt(AccountStatement::getStatementMonth, statementMonth)
                .orderByDesc(AccountStatement::getStatementMonth)
                .last("LIMIT 1"));
        return previous == null || previous.getClosingBalance() == null
                ? BigDecimal.ZERO : previous.getClosingBalance();
    }
    private String genStatementAccountNo() {
        return "DZ" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }