package cn.iocoder.yudao.module.erp.service.purchase;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.statement.ErpSupplierStatementReqVO;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.statement.ErpSupplierStatementRespVO;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.statement.ErpSupplierStatementSummaryReqVO;
|
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.statement.ErpSupplierStatementSummaryRespVO;
|
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;
|
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO;
|
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
|
import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper;
|
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInvoiceMapper;
|
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;
|
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
|
import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
|
import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.util.*;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.SUPPLIER_NOT_EXISTS;
|
|
/**
|
* ERP 供应商往来 Service 实现
|
*/
|
@Service
|
@Validated
|
public class ErpSupplierStatementServiceImpl implements ErpSupplierStatementService {
|
|
@Resource
|
private ErpPurchaseOrderMapper purchaseOrderMapper;
|
@Resource
|
private ErpPurchaseInvoiceMapper purchaseInvoiceMapper;
|
@Resource
|
private ErpFinancePaymentMapper financePaymentMapper;
|
@Resource
|
private ErpPurchaseInvoiceService purchaseInvoiceService;
|
@Resource
|
private SrmSupplierApi srmSupplierApi;
|
|
@Override
|
public ErpSupplierStatementRespVO getSupplierStatement(ErpSupplierStatementReqVO reqVO) {
|
// 1. 校验供应商
|
SrmSupplierRespDTO supplier = srmSupplierApi.getSupplier(reqVO.getSupplierId()).getCheckedData();
|
if (supplier == null) {
|
throw ServiceExceptionUtil.exception(SUPPLIER_NOT_EXISTS);
|
}
|
|
// 2. 查询订单 / 来票 / 付款
|
List<Integer> orderStatuses = Boolean.TRUE.equals(reqVO.getIncludeUnapprovedOrder())
|
? List.of(ErpAuditStatus.PROCESS.getStatus(), ErpAuditStatus.APPROVING.getStatus(),
|
ErpAuditStatus.APPROVE.getStatus())
|
: List.of(ErpAuditStatus.APPROVE.getStatus());
|
List<ErpPurchaseOrderDO> orders = purchaseOrderMapper.selectListBySupplierIdAndOrderTimeBetween(
|
reqVO.getSupplierId(), reqVO.getBeginTime(), reqVO.getEndTime(), orderStatuses);
|
List<ErpPurchaseInvoiceDO> invoices = purchaseInvoiceMapper.selectListBySupplierIdAndInvoiceTimeBetween(
|
reqVO.getSupplierId(), reqVO.getBeginTime(), reqVO.getEndTime());
|
List<Integer> paymentStatuses = List.of(ErpAuditStatus.PROCESS.getStatus(),
|
ErpAuditStatus.APPROVING.getStatus(), ErpAuditStatus.APPROVE.getStatus());
|
List<ErpFinancePaymentDO> payments = financePaymentMapper.selectListBySupplierIdAndPaymentTimeBetween(
|
reqVO.getSupplierId(), reqVO.getBeginTime(), reqVO.getEndTime(), paymentStatuses);
|
|
// 3. 订单已开票金额(不按开票日期过滤,保证未开票金额准确)
|
Map<Long, BigDecimal> invoicedMap = CollUtil.isEmpty(orders) ? Collections.emptyMap()
|
: purchaseInvoiceService.getPurchaseInvoicePriceSumMapByOrderIds(
|
orders.stream().map(ErpPurchaseOrderDO::getId).toList());
|
// 4. 来票已付金额(按所有有效付款单汇总)
|
Map<Long, BigDecimal> paidMap = CollUtil.isEmpty(invoices) ? Collections.emptyMap()
|
: financePaymentMapper.selectPaymentPriceMapByInvoiceId(
|
invoices.stream().map(ErpPurchaseInvoiceDO::getId).toList());
|
// 5. 来票关联订单号
|
Set<Long> invoiceOrderIds = invoices.stream().map(ErpPurchaseInvoiceDO::getPurchaseOrderId)
|
.filter(Objects::nonNull).collect(Collectors.toSet());
|
Map<Long, ErpPurchaseOrderDO> invoiceOrderMap = invoiceOrderIds.isEmpty() ? Collections.emptyMap()
|
: purchaseOrderMapper.selectBatchIds(invoiceOrderIds).stream()
|
.collect(Collectors.toMap(ErpPurchaseOrderDO::getId, Function.identity()));
|
// 6. 付款关联来票号
|
Set<Long> paymentInvoiceIds = payments.stream().map(ErpFinancePaymentDO::getInvoiceId)
|
.filter(Objects::nonNull).collect(Collectors.toSet());
|
Map<Long, ErpPurchaseInvoiceDO> paymentInvoiceMap = paymentInvoiceIds.isEmpty() ? Collections.emptyMap()
|
: purchaseInvoiceMapper.selectBatchIds(paymentInvoiceIds).stream()
|
.collect(Collectors.toMap(ErpPurchaseInvoiceDO::getId, Function.identity()));
|
|
// 7. 组装
|
ErpSupplierStatementRespVO respVO = new ErpSupplierStatementRespVO();
|
respVO.setSupplierId(reqVO.getSupplierId());
|
respVO.setSupplierName(supplier.getName());
|
|
List<ErpSupplierStatementRespVO.OrderRow> orderRows = new ArrayList<>();
|
for (ErpPurchaseOrderDO order : orders) {
|
BigDecimal orderAmount = zero(order.getTotalPrice());
|
BigDecimal invoicedAmount = zero(invoicedMap.get(order.getId()));
|
ErpSupplierStatementRespVO.OrderRow row = new ErpSupplierStatementRespVO.OrderRow();
|
row.setId(order.getId());
|
row.setNo(order.getNo());
|
row.setOrderTime(order.getOrderTime());
|
row.setOrderAmount(orderAmount);
|
row.setInvoicedAmount(invoicedAmount);
|
row.setUninvoicedAmount(orderAmount.subtract(invoicedAmount));
|
row.setStatus(order.getStatus());
|
row.setInStatus(order.getInStatus());
|
orderRows.add(row);
|
}
|
respVO.setOrders(orderRows);
|
|
List<ErpSupplierStatementRespVO.InvoiceRow> invoiceRows = new ArrayList<>();
|
for (ErpPurchaseInvoiceDO invoice : invoices) {
|
BigDecimal invoiceAmount = zero(invoice.getPrice());
|
BigDecimal paidAmount = zero(paidMap.get(invoice.getId()));
|
ErpSupplierStatementRespVO.InvoiceRow row = new ErpSupplierStatementRespVO.InvoiceRow();
|
row.setId(invoice.getId());
|
row.setNo(invoice.getNo());
|
row.setInvoiceNo(invoice.getInvoiceNo());
|
row.setInvoiceTime(invoice.getInvoiceTime());
|
row.setInvoiceAmount(invoiceAmount);
|
row.setPaidAmount(paidAmount);
|
row.setRemainingPayable(invoiceAmount.subtract(paidAmount));
|
row.setPaymentStatus(paidAmount.compareTo(BigDecimal.ZERO) <= 0 ? 0
|
: paidAmount.compareTo(invoiceAmount) >= 0 ? 2 : 1);
|
row.setPurchaseOrderId(invoice.getPurchaseOrderId());
|
ErpPurchaseOrderDO order = invoiceOrderMap.get(invoice.getPurchaseOrderId());
|
row.setPurchaseOrderNo(order != null ? order.getNo() : null);
|
invoiceRows.add(row);
|
}
|
respVO.setInvoices(invoiceRows);
|
|
List<ErpSupplierStatementRespVO.PaymentRow> paymentRows = new ArrayList<>();
|
for (ErpFinancePaymentDO payment : payments) {
|
ErpSupplierStatementRespVO.PaymentRow row = new ErpSupplierStatementRespVO.PaymentRow();
|
row.setId(payment.getId());
|
row.setNo(payment.getNo());
|
row.setPaymentTime(payment.getPaymentTime());
|
row.setPaymentAmount(zero(payment.getPaymentPrice()));
|
row.setStatus(payment.getStatus());
|
row.setInvoiceId(payment.getInvoiceId());
|
ErpPurchaseInvoiceDO invoice = paymentInvoiceMap.get(payment.getInvoiceId());
|
row.setInvoiceNo(invoice != null ? invoice.getNo() : null);
|
paymentRows.add(row);
|
}
|
respVO.setPayments(paymentRows);
|
|
// 8. 汇总
|
ErpSupplierStatementRespVO.Summary summary = new ErpSupplierStatementRespVO.Summary();
|
summary.setTotalPurchaseAmount(orderRows.stream().map(ErpSupplierStatementRespVO.OrderRow::getOrderAmount)
|
.reduce(BigDecimal.ZERO, BigDecimal::add));
|
summary.setTotalInvoiceAmount(invoiceRows.stream().map(ErpSupplierStatementRespVO.InvoiceRow::getInvoiceAmount)
|
.reduce(BigDecimal.ZERO, BigDecimal::add));
|
summary.setTotalPaymentAmount(paymentRows.stream().map(ErpSupplierStatementRespVO.PaymentRow::getPaymentAmount)
|
.reduce(BigDecimal.ZERO, BigDecimal::add));
|
summary.setUnpaidAmount(summary.getTotalInvoiceAmount().subtract(summary.getTotalPaymentAmount()));
|
respVO.setSummary(summary);
|
return respVO;
|
}
|
|
@Override
|
public List<ErpSupplierStatementSummaryRespVO> getSupplierSummaryList(ErpSupplierStatementSummaryReqVO reqVO) {
|
// 1. 口径与单个供应商详情保持一致:订单按审核状态过滤、来票不过滤状态、付款含未审核/审批中/已审核
|
List<Integer> orderStatuses = Boolean.TRUE.equals(reqVO.getIncludeUnapprovedOrder())
|
? List.of(ErpAuditStatus.PROCESS.getStatus(), ErpAuditStatus.APPROVING.getStatus(),
|
ErpAuditStatus.APPROVE.getStatus())
|
: List.of(ErpAuditStatus.APPROVE.getStatus());
|
List<ErpPurchaseOrderDO> orders = purchaseOrderMapper.selectListByOrderTimeBetween(
|
reqVO.getBeginTime(), reqVO.getEndTime(), orderStatuses);
|
List<ErpPurchaseInvoiceDO> invoices = purchaseInvoiceMapper.selectListByInvoiceTimeBetween(
|
reqVO.getBeginTime(), reqVO.getEndTime());
|
List<Integer> paymentStatuses = List.of(ErpAuditStatus.PROCESS.getStatus(),
|
ErpAuditStatus.APPROVING.getStatus(), ErpAuditStatus.APPROVE.getStatus());
|
List<ErpFinancePaymentDO> payments = financePaymentMapper.selectListByPaymentTimeBetween(
|
reqVO.getBeginTime(), reqVO.getEndTime(), paymentStatuses);
|
|
// 2. 按供应商汇总
|
Map<Long, ErpSupplierStatementSummaryRespVO> summaryMap = new HashMap<>();
|
for (ErpPurchaseOrderDO order : orders) {
|
if (order.getSupplierId() == null) {
|
continue;
|
}
|
ErpSupplierStatementSummaryRespVO summary = getOrCreateSummary(summaryMap, order.getSupplierId());
|
summary.setOrderCount(summary.getOrderCount() + 1);
|
summary.setTotalPurchaseAmount(summary.getTotalPurchaseAmount().add(zero(order.getTotalPrice())));
|
}
|
for (ErpPurchaseInvoiceDO invoice : invoices) {
|
if (invoice.getSupplierId() == null) {
|
continue;
|
}
|
ErpSupplierStatementSummaryRespVO summary = getOrCreateSummary(summaryMap, invoice.getSupplierId());
|
summary.setInvoiceCount(summary.getInvoiceCount() + 1);
|
summary.setTotalInvoiceAmount(summary.getTotalInvoiceAmount().add(zero(invoice.getPrice())));
|
}
|
for (ErpFinancePaymentDO payment : payments) {
|
if (payment.getSupplierId() == null) {
|
continue;
|
}
|
ErpSupplierStatementSummaryRespVO summary = getOrCreateSummary(summaryMap, payment.getSupplierId());
|
summary.setPaymentCount(summary.getPaymentCount() + 1);
|
summary.setTotalPaymentAmount(summary.getTotalPaymentAmount().add(zero(payment.getPaymentPrice())));
|
}
|
|
// 3. 填充供应商编码 / 名称(SRM 中已删除的供应商用 ID 兜底展示,避免出现空行)
|
Map<Long, SrmSupplierRespDTO> supplierMap = new HashMap<>();
|
if (!summaryMap.isEmpty()) {
|
List<SrmSupplierRespDTO> suppliers = srmSupplierApi.getSupplierList(summaryMap.keySet()).getCheckedData();
|
if (CollUtil.isNotEmpty(suppliers)) {
|
suppliers.forEach(item -> supplierMap.put(item.getId(), item));
|
}
|
}
|
for (ErpSupplierStatementSummaryRespVO summary : summaryMap.values()) {
|
summary.setUnpaidAmount(summary.getTotalInvoiceAmount().subtract(summary.getTotalPaymentAmount()));
|
SrmSupplierRespDTO supplier = supplierMap.get(summary.getSupplierId());
|
if (supplier == null) {
|
summary.setSupplierName("未知供应商(ID=" + summary.getSupplierId() + ")");
|
continue;
|
}
|
summary.setSupplierCode(supplier.getCode());
|
summary.setSupplierName(StrUtil.blankToDefault(supplier.getName(),
|
"未命名供应商(ID=" + summary.getSupplierId() + ")"));
|
}
|
|
// 4. 未付余额多的排前面,其次按采购额、供应商名称
|
Comparator<ErpSupplierStatementSummaryRespVO> comparator =
|
Comparator.comparing(ErpSupplierStatementSummaryRespVO::getUnpaidAmount,
|
Comparator.nullsFirst(Comparator.<BigDecimal>naturalOrder()))
|
.thenComparing(ErpSupplierStatementSummaryRespVO::getTotalPurchaseAmount,
|
Comparator.nullsFirst(Comparator.<BigDecimal>naturalOrder()))
|
.reversed()
|
.thenComparing(ErpSupplierStatementSummaryRespVO::getSupplierName,
|
Comparator.nullsLast(Comparator.naturalOrder()));
|
return summaryMap.values().stream()
|
.sorted(comparator)
|
.collect(Collectors.toList());
|
}
|
|
private ErpSupplierStatementSummaryRespVO getOrCreateSummary(
|
Map<Long, ErpSupplierStatementSummaryRespVO> summaryMap, Long supplierId) {
|
return summaryMap.computeIfAbsent(supplierId, id -> {
|
ErpSupplierStatementSummaryRespVO summary = new ErpSupplierStatementSummaryRespVO();
|
summary.setSupplierId(id);
|
summary.setOrderCount(0);
|
summary.setInvoiceCount(0);
|
summary.setPaymentCount(0);
|
summary.setTotalPurchaseAmount(BigDecimal.ZERO);
|
summary.setTotalInvoiceAmount(BigDecimal.ZERO);
|
summary.setTotalPaymentAmount(BigDecimal.ZERO);
|
summary.setUnpaidAmount(BigDecimal.ZERO);
|
return summary;
|
});
|
}
|
|
private BigDecimal zero(BigDecimal value) {
|
return value != null ? value : BigDecimal.ZERO;
|
}
|
|
}
|