| | |
| | | */ |
| | | private static final Integer LEDGER_TYPE_PURCHASE = 2; |
| | | |
| | | /** |
| | | * 备件流水来源:采购入库。见 spare_parts_record.source_type |
| | | */ |
| | | private static final Integer SPARE_PARTS_SOURCE_PURCHASE = 1; |
| | | |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | |
| | | salesLedgerProduct.setRegisterDate(localDateTime); |
| | | salesLedgerProductMapper.insert(salesLedgerProduct); |
| | | } |
| | | // 仅新增明细回写备件,编辑走 update 分支天然不重复计数 |
| | | if (LEDGER_TYPE_PURCHASE.equals(ledgerType)) { |
| | | recordSparePartsInbound(insertList, purchaseLedger); |
| | | } |
| | | } |
| | | // 采购明细回写备件:先按台账回滚上一次的流水,再按当前明细整体重建。 |
| | | // 编辑时明细行可能被移除,只认新增行会漏掉移除的那些;整体重建天然与当前明细对齐。 |
| | | // 注意这段必须在 insertList 之外,否则「只删明细、不新增」的编辑不会触发回滚 |
| | | if (LEDGER_TYPE_PURCHASE.equals(ledgerType)) { |
| | | rollbackSparePartsInbound(salesLedgerId); |
| | | recordSparePartsInbound(products, purchaseLedger); |
| | | } |
| | | |
| | | // 计算总含税金额 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 采购台账新增明细按规格匹配备件:累加备件库存并记一条入库流水。 |
| | | * 未维护规格的备件不参与,静默跳过。 |
| | | * 按采购台账回滚备件:把该台账产生的入库流水逐备件减回库存,然后删除这些流水。 |
| | | * |
| | | * <p>备件先由采购入账、后续被维修/保养领用出账,若入账量已经被领用掉,减回会出现负库存, |
| | | * 此时直接拦住不让删,避免把备件账做花。删除台账与编辑重建都走这里,口径统一。</p> |
| | | */ |
| | | private void recordSparePartsInbound(List<SalesLedgerProduct> insertList, PurchaseLedger purchaseLedger) { |
| | | List<Long> modelIds = insertList.stream() |
| | | private void rollbackSparePartsInbound(Long ledgerId) { |
| | | List<SparePartsRecord> records = sparePartsRecordService.list(new LambdaQueryWrapper<SparePartsRecord>() |
| | | .eq(SparePartsRecord::getSourceType, SPARE_PARTS_SOURCE_PURCHASE) |
| | | .eq(SparePartsRecord::getSourceId, ledgerId)); |
| | | if (CollectionUtils.isEmpty(records)) { |
| | | return; |
| | | } |
| | | Map<Long, BigDecimal> rollbackBySparePartsId = new HashMap<>(); |
| | | for (SparePartsRecord record : records) { |
| | | BigDecimal quantity = record.getQuantity() == null ? BigDecimal.ZERO : record.getQuantity(); |
| | | rollbackBySparePartsId.merge(record.getSparePartsId(), quantity, BigDecimal::add); |
| | | } |
| | | for (Map.Entry<Long, BigDecimal> entry : rollbackBySparePartsId.entrySet()) { |
| | | if (entry.getKey() == null) { |
| | | continue; |
| | | } |
| | | SpareParts spareParts = sparePartsMapper.selectById(entry.getKey()); |
| | | if (spareParts == null) { |
| | | continue; |
| | | } |
| | | BigDecimal current = spareParts.getQuantity() == null ? BigDecimal.ZERO : spareParts.getQuantity(); |
| | | if (current.compareTo(entry.getValue()) < 0) { |
| | | throw new BaseException("备件「" + spareParts.getName() + "」当前库存 " + current |
| | | + ",不足以回滚本次采购入库的 " + entry.getValue() + ",请先处理领用记录"); |
| | | } |
| | | spareParts.setQuantity(current.subtract(entry.getValue())); |
| | | sparePartsMapper.updateById(spareParts); |
| | | } |
| | | sparePartsRecordService.removeByIds(records.stream() |
| | | .map(SparePartsRecord::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList())); |
| | | } |
| | | |
| | | /** |
| | | * 采购台账明细按规格匹配备件:累加备件库存并记一条入库流水。 |
| | | * 未维护规格的备件不参与,静默跳过。 |
| | | * |
| | | * <p>调用前必须先 {@link #rollbackSparePartsInbound(Long)},两者成对出现: |
| | | * 回滚旧流水 + 按当前明细重建,才能保证编辑台账后备件数量与明细一致。</p> |
| | | */ |
| | | private void recordSparePartsInbound(List<SalesLedgerProduct> products, PurchaseLedger purchaseLedger) { |
| | | List<Long> modelIds = products.stream() |
| | | .map(SalesLedgerProduct::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | |
| | | } |
| | | Map<Long, SpareParts> sparePartsByModelId = sparePartsList.stream() |
| | | .collect(Collectors.toMap(SpareParts::getProductModelId, s -> s, (a, b) -> a)); |
| | | for (SalesLedgerProduct product : insertList) { |
| | | for (SalesLedgerProduct product : products) { |
| | | SpareParts spareParts = sparePartsByModelId.get(product.getProductModelId()); |
| | | if (spareParts == null || product.getQuantity() == null |
| | | || product.getQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | |
| | | if (purchaseLedger.getApprovalStatus().equals(3)) { |
| | | throw new BaseException(purchaseLedger.getPurchaseContractNumber()+"已经审批通过,不允许删除"); |
| | | } |
| | | // 台账删除要把该台账采购入库时累加到备件上的数量减回去,并删掉对应流水, |
| | | // 否则备件库存虚高、流水残留。备件已领用导致减回为负时会在这里拦住 |
| | | rollbackSparePartsInbound(id); |
| | | } |
| | | // 批量删除关联的采购入库记录 |
| | | LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductQueryWrapper = new LambdaQueryWrapper<>(); |