一、应付账款管理核心流程 1.1 三单匹配原则 三单匹配:采购订单(PO)+ 收货单(GR)+ 发票(Invoice)
采购订单(PO) ─┐ ├─→ 三单匹配 ─→ 生成应付单 收货单(GR) ─┤ │ 发票(Invoice)─┘ 匹配规则:
public class ThreeWayMatchingService { public MatchResult match(PurchaseOrder po, GoodsReceipt gr, Invoice invoice) { MatchResult result = new MatchResult(); // 1. 数量匹配 if (!matchQuantity(po, gr, invoice)) { result.addError("数量不匹配"); } // 2. 价格匹配(允许±5%误差) if (!matchPrice(po, invoice, 0.05)) { result.addError("价格超出允许误差范围"); } // 3. 金额匹配 if (!matchAmount(po, gr, invoice)) { result.addError("金额不匹配"); } return result; } private boolean matchQuantity(PurchaseOrder po, GoodsReceipt gr, Invoice invoice) { // PO数量 = 收货数量 = 发票数量 return po.getTotalQuantity() == gr.getTotalQuantity() && gr.getTotalQuantity() == invoice.getTotalQuantity(); } private boolean matchPrice(PurchaseOrder po, Invoice invoice, double tolerance) { for (PurchaseOrderItem poItem : po.getItems()) { InvoiceItem invItem = invoice.getItemBySku(poItem.getSku()); if (invItem == null) { return false; } BigDecimal priceD更ff = invItem.getUnitPrice() .subtract(poItem.getUnitPrice()) .abs(); BigDecimal maxDiff = poItem.getUnitPrice() .multiply(new BigDecimal(tolerance)); if (priceDiff.compareTo(maxDiff) > 0) { return false; } } return true; } } 1.2 应付入账流程 public class APAccrualService { @Transactional public AccountsPayable createAP(Invoice invoice) { // 1. 三单匹配 MatchResult matchResult = threeWayMatchingService.match( invoice.getPurchaseOrder(), invoice.getGoodsReceipt(), invoice ); if (!matchResult.isSuccess()) { throw new BusinessException("三单匹配失败:" + matchResult.getErrors()); } // 2. 创建应付单 AccountsPayable ap = new AccountsPayable(); ap.setApNo(generateAPNo()); ap.setSupplierId(invoice.getSupplierId()); ap.setPurchaseOrderNo(invoice.getPurchaseOrderNo()); ap.setInvoiceNo(invoice.getInvoiceNo()); // 3. 计算金额 ap.setTotalAmount(invoice.getTotalAmount()); ap.setUnpaidAmount(invoice.getTotalAmount()); // 4. 计算到期日期 Supplier supplier = supplierMapper.selectById(invoice.getSupplierId()); LocalDate dueDate = invoice.getInvoiceDate() .plusDays(supplier.getPaymentTerm()); ap.setDueDate(dueDate); // 5. 保存应付单 apMapper.insert(ap); // 6. 保存应付明细 for (InvoiceItem item : invoice.getItems()) { APDetail detail = new APDetail(); detail.setApNo(ap.getApNo()); detail.setSku(item.getSku()); detail.setQuantity(item.getQuantity()); detail.setUnitPrice(item.getUnitPrice()); detail.setAmount(item.getAmount()); apDetailMapper.insert(detail); } return ap; } } 二、付款计划制定 2.1 付款策略 策略1:按到期日付款
...