diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IReceivablePayableDetailService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IReceivablePayableDetailService.java index cf922ac..b5150b0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IReceivablePayableDetailService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IReceivablePayableDetailService.java @@ -77,8 +77,8 @@ public interface IReceivablePayableDetailService extends BaseService waybillIds); - /** 完成配载单后按其记录的承运商合同自动生成应付明细。 */ - void generateForCompletedLoading(List waybillIds, Long carrierContractId); + /** 完成配载单后按其记录的承运商合同汇总生成一条应付明细。 */ + void generateForCompletedLoading(List waybillIds, Long carrierContractId, String loadingNo); /** 关闭总单调度后生成总单客户合同应收,并按所属运单记录的承运商合同生成应付。 */ void generateForClosedMasterOrder(MasterOrder masterOrder); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java index a6b2113..b8550c2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java @@ -300,7 +300,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl()); if (result) { receivablePayableDetailService.generateForCompletedLoading( - associatedWaybillIds, loadingManage.getCarrierContractId()); + associatedWaybillIds, loadingManage.getCarrierContractId(), loadingManage.getLoadingNo()); } return result; } @@ -338,7 +338,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl waybillIds, Long carrierContractId) { + public void generateForCompletedLoading(List waybillIds, Long carrierContractId, String loadingNo) { + if (Func.isEmpty(loadingNo)) { + throw new ServiceException("配载单号不能为空"); + } ContractManage carrierContract = carrierContractId == null ? null : contractManageService.getById(carrierContractId); if (carrierContractId != null && (carrierContract == null || !"承运商合同".equals(carrierContract.getContractCategory()))) { throw new ServiceException("配载单记录的承运商合同不存在或合同类别不正确"); } - generateAutomaticWaybillDetails(loadWaybills(waybillIds), true, - waybill -> carrierContract, AuthUtil.getUserId(), new Date()); + List waybills = loadWaybills(waybillIds); + if (waybills.isEmpty()) return; + Long currentUserId = AuthUtil.getUserId(); + Date generateTime = new Date(); + generateAutomaticWaybillDetails(waybills, true, waybill -> null, currentUserId, generateTime); + generateAutomaticDetail(carrierContract, "payable", + () -> existsByLoading(loadingNo, "payable"), waybills, + (fees, unitPrice) -> { + normalizeWaybillFeeLines(fees); + return buildLoadingDetail(loadingNo, carrierContract, waybills, fees, unitPrice); + }, + false, currentUserId, generateTime); } private List loadWaybills(List waybillIds) { @@ -679,13 +693,23 @@ public class ReceivablePayableDetailServiceImpl Map carrierContracts = contractManageService.listByIds(waybills.stream() .map(Waybill::getCarrierContractId).distinct().toList()).stream() .collect(Collectors.toMap(ContractManage::getId, contract -> contract)); - generateAutomaticWaybillDetails(waybills, false, waybill -> { - ContractManage carrierContract = carrierContracts.get(waybill.getCarrierContractId()); + Map> carrierWaybills = waybills.stream().collect(Collectors.groupingBy( + Waybill::getCarrierContractId, LinkedHashMap::new, Collectors.toList())); + for (Map.Entry> entry : carrierWaybills.entrySet()) { + ContractManage carrierContract = carrierContracts.get(entry.getKey()); if (carrierContract == null || !"承运商合同".equals(carrierContract.getContractCategory())) { - throw new ServiceException("运单【" + waybill.getWaybillNo() + "】记录的承运商合同不存在或合同类别不正确"); + throw new ServiceException("总单【" + masterOrder.getMasterNo() + "】记录的承运商合同不存在或合同类别不正确"); } - return carrierContract; - }, currentUserId, generateTime); + List currentCarrierWaybills = entry.getValue(); + generateAutomaticDetail(carrierContract, "payable", + () -> existsByMasterOrderContract(masterOrder.getMasterNo(), "payable", carrierContract.getId()), + currentCarrierWaybills, + (fees, unitPrice) -> { + normalizeWaybillFeeLines(fees); + return buildMasterOrderPayableDetail(masterOrder, carrierContract, + currentCarrierWaybills, fees, unitPrice); + }, false, currentUserId, generateTime); + } } private boolean isAutomaticContract(ContractManage contract, String settlementType) { @@ -972,6 +996,46 @@ public class ReceivablePayableDetailServiceImpl return detail; } + private ReceivablePayableDetail buildLoadingDetail(String loadingNo, ContractManage contract, + List waybills, + List fees, BigDecimal unitPrice) { + Waybill firstWaybill = waybills.get(0); + ReceivablePayableDetail detail = buildDetail(firstWaybill, contract, "payable", fees, unitPrice); + detail.setSourceType(SOURCE_LOADING_ORDER); + detail.setWaybillId(null); + detail.setWaybillNo(loadingNo); + detail.setCargoName(joinWaybillField(waybills, Waybill::getCargoName)); + detail.setCargoType(joinWaybillField(waybills, Waybill::getCargoType)); + detail.setTransportQuantity(waybills.stream().map(Waybill::getQuantity) + .filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)); + detail.setQuantityUnit(commonWaybillValue(waybills, Waybill::getQuantityUnit)); + detail.setBatchNo(commonWaybillValue(waybills, Waybill::getBatchNo)); + return detail; + } + + private ReceivablePayableDetail buildMasterOrderPayableDetail(MasterOrder masterOrder, + ContractManage contract, List waybills, + List fees, BigDecimal unitPrice) { + ReceivablePayableDetail detail = buildLoadingDetail( + masterOrder.getMasterNo(), contract, waybills, fees, unitPrice); + detail.setSourceType(SOURCE_MASTER_ORDER); + detail.setTransportType(masterOrder.getTransportOrganizationType()); + detail.setRemark(masterOrder.getRemark()); + return detail; + } + + private String joinWaybillField(List waybills, + java.util.function.Function getter) { + return waybills.stream().map(getter).filter(Func::isNotEmpty).distinct() + .collect(Collectors.joining(",")); + } + + private String commonWaybillValue(List waybills, + java.util.function.Function getter) { + List values = waybills.stream().map(getter).filter(Func::isNotEmpty).distinct().toList(); + return values.size() == 1 ? values.get(0) : ""; + } + private List masterOrderGoods(MasterOrder masterOrder) { List result = new ArrayList<>(); for (Map goods : parseList(masterOrder.getGoodsJson())) { @@ -1013,6 +1077,12 @@ public class ReceivablePayableDetailServiceImpl } } + private void normalizeWaybillFeeLines(List fees) { + for (int index = 0; index < fees.size(); index++) { + fees.get(index).setLineNo(String.format("%04d", index + 1)); + } + } + private String joinMasterGoodsField(List masterGoods, java.util.function.Function getter) { return masterGoods.stream().map(getter).filter(Func::isNotEmpty).distinct() @@ -1616,7 +1686,15 @@ public class ReceivablePayableDetailServiceImpl private void rebuildDetailFee(ReceivablePayableDetail detail, String billingPlanId) { if (SOURCE_MASTER_ORDER.equals(detail.getSourceType())) { - rebuildMasterOrderDetailFee(detail, billingPlanId); + if ("payable".equals(detail.getSettlementType())) { + rebuildAggregatedWaybillDetailFee(detail, billingPlanId, "总单关联运单不存在"); + } else { + rebuildMasterOrderDetailFee(detail, billingPlanId); + } + return; + } + if (SOURCE_LOADING_ORDER.equals(detail.getSourceType())) { + rebuildAggregatedWaybillDetailFee(detail, billingPlanId, "配载单关联运单不存在"); return; } Waybill waybill = waybillService.getById(detail.getWaybillId()); @@ -1669,6 +1747,40 @@ public class ReceivablePayableDetailServiceImpl updateById(detail); } + private void rebuildAggregatedWaybillDetailFee(ReceivablePayableDetail detail, String billingPlanId, + String missingMessage) { + List waybillIds = cargoFeeMapper.selectList(Wrappers.lambdaQuery() + .select(ReceivablePayableCargoFee::getWaybillId) + .eq(ReceivablePayableCargoFee::getDetailId, detail.getId()) + .eq(ReceivablePayableCargoFee::getIsDeleted, 0) + .isNotNull(ReceivablePayableCargoFee::getWaybillId)) + .stream().map(ReceivablePayableCargoFee::getWaybillId).distinct().toList(); + List waybills = loadWaybills(waybillIds); + if (waybills.isEmpty()) { + throw new ServiceException(missingMessage); + } + ContractManage contract = contractManageService.getById(detail.getContractId()); + List fees = waybills.stream() + .flatMap(waybill -> calculatedFees(waybill, contract, billingPlanId).stream()).toList(); + normalizeWaybillFeeLines(fees); + cargoFeeMapper.delete(Wrappers.lambdaQuery() + .eq(ReceivablePayableCargoFee::getDetailId, detail.getId())); + fees.forEach(fee -> { + fee.setDetailId(detail.getId()); + cargoFeeMapper.insert(fee); + }); + BigDecimal freight = fees.stream().map(fee -> money(fee.getFreightAmount())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal total = fees.stream().map(ReceivablePayableCargoFee::getAfterAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + detail.setFreightAmount(freight); + detail.setOtherFeeAmount(total.subtract(freight)); + detail.setTotalAmount(total); + detail.setUnitPrice(resolveContractUnitPrice(fees)); + detail.setFeeItemsJson(aggregateFeeItemsJson(fees)); + updateById(detail); + } + private void closeDetails(List ids, String settlementType) { if (Func.isEmpty(ids)) { throw new ServiceException("请选择需要关闭的明细"); @@ -1766,6 +1878,23 @@ public class ReceivablePayableDetailServiceImpl .eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0; } + private boolean existsByMasterOrderContract(String masterNo, String settlementType, Long contractId) { + return count(Wrappers.lambdaQuery() + .eq(ReceivablePayableDetail::getSourceType, SOURCE_MASTER_ORDER) + .eq(ReceivablePayableDetail::getWaybillNo, masterNo) + .eq(ReceivablePayableDetail::getSettlementType, settlementType) + .eq(ReceivablePayableDetail::getContractId, contractId) + .eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0; + } + + private boolean existsByLoading(String loadingNo, String settlementType) { + return count(Wrappers.lambdaQuery() + .eq(ReceivablePayableDetail::getSourceType, SOURCE_LOADING_ORDER) + .eq(ReceivablePayableDetail::getWaybillNo, loadingNo) + .eq(ReceivablePayableDetail::getSettlementType, settlementType) + .eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0; + } + private String settlementType(String value) { if (Func.isEmpty(value)) return "receivable"; if (!List.of("receivable", "payable").contains(value)) {