调整配载、总单

This commit is contained in:
2026-08-24 12:10:29 +08:00
parent 8c5704caba
commit 8ad8b2df90
3 changed files with 142 additions and 13 deletions
@@ -77,8 +77,8 @@ public interface IReceivablePayableDetailService extends BaseService<ReceivableP
/** 完成运单后按合同系统计费模式自动生成应收、应付明细。 */ /** 完成运单后按合同系统计费模式自动生成应收、应付明细。 */
void generateForCompletedWaybills(List<Long> waybillIds); void generateForCompletedWaybills(List<Long> waybillIds);
/** 完成配载单后按其记录的承运商合同自动生成应付明细。 */ /** 完成配载单后按其记录的承运商合同汇总生成一条应付明细。 */
void generateForCompletedLoading(List<Long> waybillIds, Long carrierContractId); void generateForCompletedLoading(List<Long> waybillIds, Long carrierContractId, String loadingNo);
/** 关闭总单调度后生成总单客户合同应收,并按所属运单记录的承运商合同生成应付。 */ /** 关闭总单调度后生成总单客户合同应收,并按所属运单记录的承运商合同生成应付。 */
void generateForClosedMasterOrder(MasterOrder masterOrder); void generateForClosedMasterOrder(MasterOrder masterOrder);
@@ -300,7 +300,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
syncAssociatedWaybills(loadingManage, STATUS_COMPLETED, new ArrayList<>()); syncAssociatedWaybills(loadingManage, STATUS_COMPLETED, new ArrayList<>());
if (result) { if (result) {
receivablePayableDetailService.generateForCompletedLoading( receivablePayableDetailService.generateForCompletedLoading(
associatedWaybillIds, loadingManage.getCarrierContractId()); associatedWaybillIds, loadingManage.getCarrierContractId(), loadingManage.getLoadingNo());
} }
return result; return result;
} }
@@ -338,7 +338,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
boolean result = updateById(loadingManage); boolean result = updateById(loadingManage);
if (result) { if (result) {
receivablePayableDetailService.generateForCompletedLoading( receivablePayableDetailService.generateForCompletedLoading(
waybillIdList, loadingManage.getCarrierContractId()); waybillIdList, loadingManage.getCarrierContractId(), loadingManage.getLoadingNo());
} }
return result; return result;
} }
@@ -101,6 +101,7 @@ public class ReceivablePayableDetailServiceImpl
implements IReceivablePayableDetailService { implements IReceivablePayableDetailService {
private static final String SOURCE_MASTER_ORDER = "总单系统生成"; private static final String SOURCE_MASTER_ORDER = "总单系统生成";
private static final String SOURCE_LOADING_ORDER = "配载单系统生成";
private final ReceivablePayableCargoFeeMapper cargoFeeMapper; private final ReceivablePayableCargoFeeMapper cargoFeeMapper;
private final ReceivablePayableChangeRecordMapper changeRecordMapper; private final ReceivablePayableChangeRecordMapper changeRecordMapper;
@@ -546,15 +547,28 @@ public class ReceivablePayableDetailServiceImpl
@Override @Override
@Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW, @Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW,
rollbackFor = Exception.class) rollbackFor = Exception.class)
public void generateForCompletedLoading(List<Long> waybillIds, Long carrierContractId) { public void generateForCompletedLoading(List<Long> waybillIds, Long carrierContractId, String loadingNo) {
if (Func.isEmpty(loadingNo)) {
throw new ServiceException("配载单号不能为空");
}
ContractManage carrierContract = carrierContractId == null ContractManage carrierContract = carrierContractId == null
? null : contractManageService.getById(carrierContractId); ? null : contractManageService.getById(carrierContractId);
if (carrierContractId != null && (carrierContract == null if (carrierContractId != null && (carrierContract == null
|| !"承运商合同".equals(carrierContract.getContractCategory()))) { || !"承运商合同".equals(carrierContract.getContractCategory()))) {
throw new ServiceException("配载单记录的承运商合同不存在或合同类别不正确"); throw new ServiceException("配载单记录的承运商合同不存在或合同类别不正确");
} }
generateAutomaticWaybillDetails(loadWaybills(waybillIds), true, List<Waybill> waybills = loadWaybills(waybillIds);
waybill -> carrierContract, AuthUtil.getUserId(), new Date()); 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<Waybill> loadWaybills(List<Long> waybillIds) { private List<Waybill> loadWaybills(List<Long> waybillIds) {
@@ -679,13 +693,23 @@ public class ReceivablePayableDetailServiceImpl
Map<Long, ContractManage> carrierContracts = contractManageService.listByIds(waybills.stream() Map<Long, ContractManage> carrierContracts = contractManageService.listByIds(waybills.stream()
.map(Waybill::getCarrierContractId).distinct().toList()).stream() .map(Waybill::getCarrierContractId).distinct().toList()).stream()
.collect(Collectors.toMap(ContractManage::getId, contract -> contract)); .collect(Collectors.toMap(ContractManage::getId, contract -> contract));
generateAutomaticWaybillDetails(waybills, false, waybill -> { Map<Long, List<Waybill>> carrierWaybills = waybills.stream().collect(Collectors.groupingBy(
ContractManage carrierContract = carrierContracts.get(waybill.getCarrierContractId()); Waybill::getCarrierContractId, LinkedHashMap::new, Collectors.toList()));
for (Map.Entry<Long, List<Waybill>> entry : carrierWaybills.entrySet()) {
ContractManage carrierContract = carrierContracts.get(entry.getKey());
if (carrierContract == null || !"承运商合同".equals(carrierContract.getContractCategory())) { if (carrierContract == null || !"承运商合同".equals(carrierContract.getContractCategory())) {
throw new ServiceException("单【" + waybill.getWaybillNo() + "】记录的承运商合同不存在或合同类别不正确"); throw new ServiceException("单【" + masterOrder.getMasterNo() + "】记录的承运商合同不存在或合同类别不正确");
} }
return carrierContract; List<Waybill> currentCarrierWaybills = entry.getValue();
}, currentUserId, generateTime); 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) { private boolean isAutomaticContract(ContractManage contract, String settlementType) {
@@ -972,6 +996,46 @@ public class ReceivablePayableDetailServiceImpl
return detail; return detail;
} }
private ReceivablePayableDetail buildLoadingDetail(String loadingNo, ContractManage contract,
List<Waybill> waybills,
List<ReceivablePayableCargoFee> 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<Waybill> waybills,
List<ReceivablePayableCargoFee> 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<Waybill> waybills,
java.util.function.Function<Waybill, String> getter) {
return waybills.stream().map(getter).filter(Func::isNotEmpty).distinct()
.collect(Collectors.joining(","));
}
private String commonWaybillValue(List<Waybill> waybills,
java.util.function.Function<Waybill, String> getter) {
List<String> values = waybills.stream().map(getter).filter(Func::isNotEmpty).distinct().toList();
return values.size() == 1 ? values.get(0) : "";
}
private List<Waybill> masterOrderGoods(MasterOrder masterOrder) { private List<Waybill> masterOrderGoods(MasterOrder masterOrder) {
List<Waybill> result = new ArrayList<>(); List<Waybill> result = new ArrayList<>();
for (Map<String, Object> goods : parseList(masterOrder.getGoodsJson())) { for (Map<String, Object> goods : parseList(masterOrder.getGoodsJson())) {
@@ -1013,6 +1077,12 @@ public class ReceivablePayableDetailServiceImpl
} }
} }
private void normalizeWaybillFeeLines(List<ReceivablePayableCargoFee> fees) {
for (int index = 0; index < fees.size(); index++) {
fees.get(index).setLineNo(String.format("%04d", index + 1));
}
}
private String joinMasterGoodsField(List<Waybill> masterGoods, private String joinMasterGoodsField(List<Waybill> masterGoods,
java.util.function.Function<Waybill, String> getter) { java.util.function.Function<Waybill, String> getter) {
return masterGoods.stream().map(getter).filter(Func::isNotEmpty).distinct() return masterGoods.stream().map(getter).filter(Func::isNotEmpty).distinct()
@@ -1616,7 +1686,15 @@ public class ReceivablePayableDetailServiceImpl
private void rebuildDetailFee(ReceivablePayableDetail detail, String billingPlanId) { private void rebuildDetailFee(ReceivablePayableDetail detail, String billingPlanId) {
if (SOURCE_MASTER_ORDER.equals(detail.getSourceType())) { 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; return;
} }
Waybill waybill = waybillService.getById(detail.getWaybillId()); Waybill waybill = waybillService.getById(detail.getWaybillId());
@@ -1669,6 +1747,40 @@ public class ReceivablePayableDetailServiceImpl
updateById(detail); updateById(detail);
} }
private void rebuildAggregatedWaybillDetailFee(ReceivablePayableDetail detail, String billingPlanId,
String missingMessage) {
List<Long> waybillIds = cargoFeeMapper.selectList(Wrappers.<ReceivablePayableCargoFee>lambdaQuery()
.select(ReceivablePayableCargoFee::getWaybillId)
.eq(ReceivablePayableCargoFee::getDetailId, detail.getId())
.eq(ReceivablePayableCargoFee::getIsDeleted, 0)
.isNotNull(ReceivablePayableCargoFee::getWaybillId))
.stream().map(ReceivablePayableCargoFee::getWaybillId).distinct().toList();
List<Waybill> waybills = loadWaybills(waybillIds);
if (waybills.isEmpty()) {
throw new ServiceException(missingMessage);
}
ContractManage contract = contractManageService.getById(detail.getContractId());
List<ReceivablePayableCargoFee> fees = waybills.stream()
.flatMap(waybill -> calculatedFees(waybill, contract, billingPlanId).stream()).toList();
normalizeWaybillFeeLines(fees);
cargoFeeMapper.delete(Wrappers.<ReceivablePayableCargoFee>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<Long> ids, String settlementType) { private void closeDetails(List<Long> ids, String settlementType) {
if (Func.isEmpty(ids)) { if (Func.isEmpty(ids)) {
throw new ServiceException("请选择需要关闭的明细"); throw new ServiceException("请选择需要关闭的明细");
@@ -1766,6 +1878,23 @@ public class ReceivablePayableDetailServiceImpl
.eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0; .eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0;
} }
private boolean existsByMasterOrderContract(String masterNo, String settlementType, Long contractId) {
return count(Wrappers.<ReceivablePayableDetail>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.<ReceivablePayableDetail>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) { private String settlementType(String value) {
if (Func.isEmpty(value)) return "receivable"; if (Func.isEmpty(value)) return "receivable";
if (!List.of("receivable", "payable").contains(value)) { if (!List.of("receivable", "payable").contains(value)) {