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;
/**
*
* 服务实现类
*
*
* @author 芯导软件(江苏)有限公司
* @since 2026-05-19 09:42:47
*/
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class AccountStatementServiceImpl extends ServiceImpl 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 accountStatements = accountStatementMapper.selectList(Wrappers.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 ids) {
//删除对账单明细
accountStatementDetailsMapper.delete(Wrappers.lambdaQuery().in(AccountStatementDetails::getAccountStatementId, ids));
return removeByIds(ids);
}
@Override
public IPage listPageAccountStatement(Page page, StatementAccountDto statementAccountDto) {
return accountStatementMapper.listPageAccountStatement(page, statementAccountDto);
}
@Override
public void exportAccountStatement(HttpServletResponse response, StatementAccountDto statementAccountDto) {
List list = accountStatementMapper.listPageAccountStatement(new Page(1,-1),statementAccountDto).getRecords();
ExcelUtil util = new ExcelUtil<>(StatementAccountVo.class);
util.exportExcel(response, list , "对账单");
}
//根据客户和月份获取对账详情(销售)
private StatementAccountVo getAccountStatementDetailsByCustomerAndMonth(StatementAccountDto statementAccountDto) {
StatementAccountVo statementAccountVo = new StatementAccountVo();
statementAccountVo.setAccountType(1);//应收对账
List accountStatementDetailsList = new ArrayList<>();
/*查询出库明细*/
SalesOutboundDto salesOutboundDto = new SalesOutboundDto();
salesOutboundDto.setCustomerId(statementAccountDto.getCustomerId());
salesOutboundDto.setStartDate(statementAccountDto.getStartDate());
salesOutboundDto.setEndDate(statementAccountDto.getEndDate());
List 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 accountSalesCollections = accountSalesCollectionMapper.selectList(Wrappers.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 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 accountStatements = accountStatementMapper.selectList(Wrappers.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 accountStatementDetailsList = new ArrayList<>();
/*查询入库明细*/
PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto();
purchaseInboundDto.setSupplierId(statementAccountDto.getCustomerId());
purchaseInboundDto.setStartDate(statementAccountDto.getStartDate());
purchaseInboundDto.setEndDate(statementAccountDto.getEndDate());
List 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 accountPurchasePayments = accountPurchasePaymentMapper.selectList(Wrappers.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 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 accountStatements = accountStatementMapper.selectList(Wrappers.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);
}
}