+12
@@ -103,6 +103,18 @@ public class ContractChange extends TenantEntity {
|
|||||||
@Schema(description = "乙方")
|
@Schema(description = "乙方")
|
||||||
private String partyB;
|
private String partyB;
|
||||||
|
|
||||||
|
@Schema(description = "甲方客商明细JSON")
|
||||||
|
private String partyAJson;
|
||||||
|
|
||||||
|
@Schema(description = "乙方客商明细JSON")
|
||||||
|
private String partyBJson;
|
||||||
|
|
||||||
|
@Schema(description = "丙方客商明细JSON")
|
||||||
|
private String partyCJson;
|
||||||
|
|
||||||
|
@Schema(description = "丙方")
|
||||||
|
private String partyC;
|
||||||
|
|
||||||
@Schema(description = "开始日期")
|
@Schema(description = "开始日期")
|
||||||
private LocalDate startDate;
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
|||||||
+12
@@ -79,6 +79,18 @@ public class ContractManage extends TenantEntity {
|
|||||||
@Schema(description = "乙方")
|
@Schema(description = "乙方")
|
||||||
private String partyB;
|
private String partyB;
|
||||||
|
|
||||||
|
@Schema(description = "甲方客商明细JSON")
|
||||||
|
private String partyAJson;
|
||||||
|
|
||||||
|
@Schema(description = "乙方客商明细JSON")
|
||||||
|
private String partyBJson;
|
||||||
|
|
||||||
|
@Schema(description = "丙方客商明细JSON")
|
||||||
|
private String partyCJson;
|
||||||
|
|
||||||
|
@Schema(description = "丙方")
|
||||||
|
private String partyC;
|
||||||
|
|
||||||
@Schema(description = "开始日期")
|
@Schema(description = "开始日期")
|
||||||
private LocalDate startDate;
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
|||||||
+6
@@ -86,6 +86,12 @@ public class LoadingManage extends TenantEntity {
|
|||||||
@Schema(description = "承运商合同ID")
|
@Schema(description = "承运商合同ID")
|
||||||
private Long carrierContractId;
|
private Long carrierContractId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商明细行ID")
|
||||||
|
private String carrierPartyId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商角色:B/C")
|
||||||
|
private String carrierPartyRole;
|
||||||
|
|
||||||
@Schema(description = "发货地址")
|
@Schema(description = "发货地址")
|
||||||
private String departureAddress;
|
private String departureAddress;
|
||||||
|
|
||||||
|
|||||||
+6
@@ -136,6 +136,12 @@ public class Waybill extends TenantEntity {
|
|||||||
@Schema(description = "承运商合同ID")
|
@Schema(description = "承运商合同ID")
|
||||||
private Long carrierContractId;
|
private Long carrierContractId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商明细行ID")
|
||||||
|
private String carrierPartyId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商角色:B/C")
|
||||||
|
private String carrierPartyRole;
|
||||||
|
|
||||||
@Schema(description = "司机ID")
|
@Schema(description = "司机ID")
|
||||||
private Long driverId;
|
private Long driverId;
|
||||||
|
|
||||||
|
|||||||
+13
@@ -55,4 +55,17 @@ public class LoadingCarrierContractVO implements Serializable {
|
|||||||
@Schema(description = "承运商名称")
|
@Schema(description = "承运商名称")
|
||||||
private String carrierName;
|
private String carrierName;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商明细行ID")
|
||||||
|
private String carrierPartyId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商角色:B/C")
|
||||||
|
private String carrierPartyRole;
|
||||||
|
|
||||||
|
@Schema(description = "统一信用代码")
|
||||||
|
private String creditCode;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "客商档案ID")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -34,4 +34,17 @@ public class MasterOrderCarrierVO implements Serializable {
|
|||||||
@Schema(description = "承运商名称")
|
@Schema(description = "承运商名称")
|
||||||
private String carrierName;
|
private String carrierName;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商明细行ID")
|
||||||
|
private String carrierPartyId;
|
||||||
|
|
||||||
|
@Schema(description = "承运客商角色:B/C")
|
||||||
|
private String carrierPartyRole;
|
||||||
|
|
||||||
|
@Schema(description = "统一信用代码")
|
||||||
|
private String creditCode;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "客商档案ID")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -66,6 +66,8 @@ public class ContractManageExcel implements Serializable {
|
|||||||
private String partyA;
|
private String partyA;
|
||||||
@ExcelProperty("乙方")
|
@ExcelProperty("乙方")
|
||||||
private String partyB;
|
private String partyB;
|
||||||
|
@ExcelProperty("丙方")
|
||||||
|
private String partyC;
|
||||||
@ExcelProperty("开始日期")
|
@ExcelProperty("开始日期")
|
||||||
private LocalDate startDate;
|
private LocalDate startDate;
|
||||||
@ExcelProperty("结束日期")
|
@ExcelProperty("结束日期")
|
||||||
|
|||||||
+20
-1
@@ -42,6 +42,7 @@ import org.springblade.transport.pojo.vo.ContractChangeVO;
|
|||||||
import org.springblade.transport.pojo.vo.ContractManageVO;
|
import org.springblade.transport.pojo.vo.ContractManageVO;
|
||||||
import org.springblade.transport.service.IContractChangeService;
|
import org.springblade.transport.service.IContractChangeService;
|
||||||
import org.springblade.transport.service.IContractManageService;
|
import org.springblade.transport.service.IContractManageService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
import org.springblade.transport.wrapper.ContractChangeWrapper;
|
import org.springblade.transport.wrapper.ContractChangeWrapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -308,7 +309,20 @@ public class ContractChangeServiceImpl extends BaseServiceImpl<ContractChangeMap
|
|||||||
target.setSignType(source.getSignType());
|
target.setSignType(source.getSignType());
|
||||||
target.setContractNo(source.getContractNo());
|
target.setContractNo(source.getContractNo());
|
||||||
target.setContractName(source.getContractName());
|
target.setContractName(source.getContractName());
|
||||||
target.setPartyB(source.getPartyB());
|
target.setPartyAJson(source.getPartyAJson());
|
||||||
|
target.setPartyBJson(source.getPartyBJson());
|
||||||
|
target.setPartyCJson(source.getPartyCJson());
|
||||||
|
ContractManage partyHolder = new ContractManage();
|
||||||
|
partyHolder.setPartyAJson(target.getPartyAJson());
|
||||||
|
partyHolder.setPartyBJson(target.getPartyBJson());
|
||||||
|
partyHolder.setPartyCJson(target.getPartyCJson());
|
||||||
|
ContractPartySupport.normalizeAndFillSummary(partyHolder);
|
||||||
|
target.setPartyAJson(partyHolder.getPartyAJson());
|
||||||
|
target.setPartyBJson(partyHolder.getPartyBJson());
|
||||||
|
target.setPartyCJson(partyHolder.getPartyCJson());
|
||||||
|
target.setPartyA(partyHolder.getPartyA());
|
||||||
|
target.setPartyB(partyHolder.getPartyB());
|
||||||
|
target.setPartyC(partyHolder.getPartyC());
|
||||||
target.setStartDate(source.getStartDate());
|
target.setStartDate(source.getStartDate());
|
||||||
target.setEndDate(source.getEndDate());
|
target.setEndDate(source.getEndDate());
|
||||||
target.setContractFormat(source.getContractFormat());
|
target.setContractFormat(source.getContractFormat());
|
||||||
@@ -341,7 +355,12 @@ public class ContractChangeServiceImpl extends BaseServiceImpl<ContractChangeMap
|
|||||||
data.put("contractNo", contractManage.getContractNo());
|
data.put("contractNo", contractManage.getContractNo());
|
||||||
data.put("contractName", contractManage.getContractName());
|
data.put("contractName", contractManage.getContractName());
|
||||||
data.put("signType", contractManage.getSignType());
|
data.put("signType", contractManage.getSignType());
|
||||||
|
data.put("partyA", contractManage.getPartyA());
|
||||||
data.put("partyB", contractManage.getPartyB());
|
data.put("partyB", contractManage.getPartyB());
|
||||||
|
data.put("partyC", contractManage.getPartyC());
|
||||||
|
data.put("partyAJson", contractManage.getPartyAJson());
|
||||||
|
data.put("partyBJson", contractManage.getPartyBJson());
|
||||||
|
data.put("partyCJson", contractManage.getPartyCJson());
|
||||||
data.put("startDate", contractManage.getStartDate());
|
data.put("startDate", contractManage.getStartDate());
|
||||||
data.put("endDate", contractManage.getEndDate());
|
data.put("endDate", contractManage.getEndDate());
|
||||||
data.put("contractFormat", contractManage.getContractFormat());
|
data.put("contractFormat", contractManage.getContractFormat());
|
||||||
|
|||||||
+29
-7
@@ -47,6 +47,7 @@ import org.springblade.transport.pojo.entity.HcsContractExeStage;
|
|||||||
import org.springblade.transport.pojo.vo.ContractManageVO;
|
import org.springblade.transport.pojo.vo.ContractManageVO;
|
||||||
import org.springblade.transport.service.IContractExeStageService;
|
import org.springblade.transport.service.IContractExeStageService;
|
||||||
import org.springblade.transport.service.IContractManageService;
|
import org.springblade.transport.service.IContractManageService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
import org.springblade.transport.support.hcs.HcsCreditCheckSupport;
|
import org.springblade.transport.support.hcs.HcsCreditCheckSupport;
|
||||||
import org.springblade.transport.wrapper.ContractManageWrapper;
|
import org.springblade.transport.wrapper.ContractManageWrapper;
|
||||||
@@ -403,7 +404,10 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
request.setSignType(change.getSignType());
|
request.setSignType(change.getSignType());
|
||||||
request.setContractNo(change.getContractNo());
|
request.setContractNo(change.getContractNo());
|
||||||
request.setContractName(change.getContractName());
|
request.setContractName(change.getContractName());
|
||||||
request.setPartyB(change.getPartyB());
|
request.setPartyAJson(change.getPartyAJson());
|
||||||
|
request.setPartyBJson(change.getPartyBJson());
|
||||||
|
request.setPartyCJson(change.getPartyCJson());
|
||||||
|
ContractPartySupport.normalizeAndFillSummary(request);
|
||||||
request.setStartDate(change.getStartDate());
|
request.setStartDate(change.getStartDate());
|
||||||
request.setEndDate(change.getEndDate());
|
request.setEndDate(change.getEndDate());
|
||||||
request.setContractFormat(change.getContractFormat());
|
request.setContractFormat(change.getContractFormat());
|
||||||
@@ -464,13 +468,23 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
TransportBusinessSupport.validateRequired(request.getSettlementCurrency(), "请选择结算币种");
|
TransportBusinessSupport.validateRequired(request.getSettlementCurrency(), "请选择结算币种");
|
||||||
ContractManage candidate = new ContractManage();
|
ContractManage candidate = new ContractManage();
|
||||||
BeanUtil.copyProperties(source, candidate);
|
BeanUtil.copyProperties(source, candidate);
|
||||||
candidate.setPartyB(TransportBusinessSupport.trimToNull(request.getPartyB()));
|
candidate.setPartyAJson(request.getPartyAJson());
|
||||||
|
candidate.setPartyBJson(request.getPartyBJson());
|
||||||
|
candidate.setPartyCJson(request.getPartyCJson());
|
||||||
|
ContractPartySupport.normalizeAndFillSummary(candidate);
|
||||||
|
ContractPartySupport.validateRequiredParties(candidate, true);
|
||||||
|
ContractPartySupport.validateBillingPlanBindings(candidate);
|
||||||
validateContractPartyCombinationUnique(candidate);
|
validateContractPartyCombinationUnique(candidate);
|
||||||
validatePaymentRatios(request.getPaymentRatioJson());
|
validatePaymentRatios(request.getPaymentRatioJson());
|
||||||
applyContractNo(source, request.getContractNo(), false);
|
applyContractNo(source, request.getContractNo(), false);
|
||||||
source.setContractName(request.getContractName());
|
source.setContractName(request.getContractName());
|
||||||
source.setSignType(request.getSignType());
|
source.setSignType(request.getSignType());
|
||||||
source.setPartyB(request.getPartyB());
|
source.setPartyAJson(candidate.getPartyAJson());
|
||||||
|
source.setPartyBJson(candidate.getPartyBJson());
|
||||||
|
source.setPartyCJson(candidate.getPartyCJson());
|
||||||
|
source.setPartyA(candidate.getPartyA());
|
||||||
|
source.setPartyB(candidate.getPartyB());
|
||||||
|
source.setPartyC(candidate.getPartyC());
|
||||||
source.setStartDate(request.getStartDate());
|
source.setStartDate(request.getStartDate());
|
||||||
source.setEndDate(request.getEndDate());
|
source.setEndDate(request.getEndDate());
|
||||||
source.setContractFormat(request.getContractFormat());
|
source.setContractFormat(request.getContractFormat());
|
||||||
@@ -947,8 +961,10 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
contractManage.setOrganizationName(TransportBusinessSupport.trimToNull(contractManage.getOrganizationName()));
|
contractManage.setOrganizationName(TransportBusinessSupport.trimToNull(contractManage.getOrganizationName()));
|
||||||
contractManage.setContractCategory(TransportBusinessSupport.trimToNull(contractManage.getContractCategory()));
|
contractManage.setContractCategory(TransportBusinessSupport.trimToNull(contractManage.getContractCategory()));
|
||||||
contractManage.setSignType(TransportBusinessSupport.trimToNull(contractManage.getSignType()));
|
contractManage.setSignType(TransportBusinessSupport.trimToNull(contractManage.getSignType()));
|
||||||
contractManage.setPartyA(TransportBusinessSupport.trimToNull(contractManage.getPartyA()));
|
contractManage.setPartyAJson(TransportBusinessSupport.trimToNull(contractManage.getPartyAJson()));
|
||||||
contractManage.setPartyB(TransportBusinessSupport.trimToNull(contractManage.getPartyB()));
|
contractManage.setPartyBJson(TransportBusinessSupport.trimToNull(contractManage.getPartyBJson()));
|
||||||
|
contractManage.setPartyCJson(TransportBusinessSupport.trimToNull(contractManage.getPartyCJson()));
|
||||||
|
ContractPartySupport.normalizeAndFillSummary(contractManage);
|
||||||
contractManage.setSettlementMode(TransportBusinessSupport.trimToNull(contractManage.getSettlementMode()));
|
contractManage.setSettlementMode(TransportBusinessSupport.trimToNull(contractManage.getSettlementMode()));
|
||||||
contractManage.setContractFormat(TransportBusinessSupport.trimToNull(contractManage.getContractFormat()));
|
contractManage.setContractFormat(TransportBusinessSupport.trimToNull(contractManage.getContractFormat()));
|
||||||
contractManage.setRemark(TransportBusinessSupport.trimToNull(contractManage.getRemark()));
|
contractManage.setRemark(TransportBusinessSupport.trimToNull(contractManage.getRemark()));
|
||||||
@@ -1005,6 +1021,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
// 合同名称允许重复,唯一性由合同编号保证。
|
// 合同名称允许重复,唯一性由合同编号保证。
|
||||||
TransportBusinessSupport.validateLength(contractManage.getMainContent(), 1000, "合同主要内容不能超过1000字");
|
TransportBusinessSupport.validateLength(contractManage.getMainContent(), 1000, "合同主要内容不能超过1000字");
|
||||||
TransportBusinessSupport.validateLength(contractManage.getRemark(), 2000, "其他事项说明不能超过2000字");
|
TransportBusinessSupport.validateLength(contractManage.getRemark(), 2000, "其他事项说明不能超过2000字");
|
||||||
|
// 草稿不强制甲/乙客商与计费绑定,提交临时/正式时再完整校验
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateBillingPlans(String billingPlanJson) {
|
private void validateBillingPlans(String billingPlanJson) {
|
||||||
@@ -1146,8 +1163,8 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
TransportBusinessSupport.validateRequired(contractManage.getContractCategory(), "请选择合同类型");
|
TransportBusinessSupport.validateRequired(contractManage.getContractCategory(), "请选择合同类型");
|
||||||
TransportBusinessSupport.validateRequired(contractManage.getProjectName(), "请选择所属项目");
|
TransportBusinessSupport.validateRequired(contractManage.getProjectName(), "请选择所属项目");
|
||||||
TransportBusinessSupport.validateRequired(contractManage.getOrganizationName(), "请选择合同负责部门");
|
TransportBusinessSupport.validateRequired(contractManage.getOrganizationName(), "请选择合同负责部门");
|
||||||
TransportBusinessSupport.validateRequired(contractManage.getPartyA(), "请选择甲方");
|
ContractPartySupport.validateRequiredParties(contractManage, true);
|
||||||
TransportBusinessSupport.validateRequired(contractManage.getPartyB(), "请选择乙方");
|
ContractPartySupport.validateBillingPlanBindings(contractManage);
|
||||||
validateContractAmountRequired(contractManage);
|
validateContractAmountRequired(contractManage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1298,7 +1315,12 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
|||||||
Map<String, Object> data = new LinkedHashMap<>();
|
Map<String, Object> data = new LinkedHashMap<>();
|
||||||
data.put("contractNo", contractManage.getContractNo());
|
data.put("contractNo", contractManage.getContractNo());
|
||||||
data.put("contractName", contractManage.getContractName());
|
data.put("contractName", contractManage.getContractName());
|
||||||
|
data.put("partyA", contractManage.getPartyA());
|
||||||
data.put("partyB", contractManage.getPartyB());
|
data.put("partyB", contractManage.getPartyB());
|
||||||
|
data.put("partyC", contractManage.getPartyC());
|
||||||
|
data.put("partyAJson", contractManage.getPartyAJson());
|
||||||
|
data.put("partyBJson", contractManage.getPartyBJson());
|
||||||
|
data.put("partyCJson", contractManage.getPartyCJson());
|
||||||
data.put("startDate", contractManage.getStartDate());
|
data.put("startDate", contractManage.getStartDate());
|
||||||
data.put("endDate", contractManage.getEndDate());
|
data.put("endDate", contractManage.getEndDate());
|
||||||
data.put("contractFormat", contractManage.getContractFormat());
|
data.put("contractFormat", contractManage.getContractFormat());
|
||||||
|
|||||||
+94
-26
@@ -35,6 +35,7 @@ import org.springblade.transport.service.IDriverService;
|
|||||||
import org.springblade.transport.service.ILoadingManageService;
|
import org.springblade.transport.service.ILoadingManageService;
|
||||||
import org.springblade.transport.service.IProcessConfigService;
|
import org.springblade.transport.service.IProcessConfigService;
|
||||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.LiveProcessConfigSupport;
|
import org.springblade.transport.support.LiveProcessConfigSupport;
|
||||||
import org.springblade.transport.support.LoadableAdmissionSupport;
|
import org.springblade.transport.support.LoadableAdmissionSupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
@@ -118,23 +119,27 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
List<ContractManage> contracts = availableCarrierContracts(projectIds);
|
List<ContractManage> contracts = availableCarrierContracts(projectIds);
|
||||||
|
Set<String> allowedCarrierNames = null;
|
||||||
if (Func.isNotEmpty(waybillIds)) {
|
if (Func.isNotEmpty(waybillIds)) {
|
||||||
List<Waybill> waybills = loadWaybills(waybillIds);
|
List<Waybill> waybills = loadWaybills(waybillIds);
|
||||||
Set<String> allowedPartyBs = intersectCarrierPartyBs(waybills, contracts);
|
allowedCarrierNames = intersectCarrierPartyBs(waybills, contracts);
|
||||||
if (allowedPartyBs.isEmpty()) {
|
if (allowedCarrierNames.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
Set<String> allowedNames = allowedCarrierNames;
|
||||||
contracts = contracts.stream()
|
contracts = contracts.stream()
|
||||||
.filter(contract -> allowedPartyBs.contains(normalizePartyName(contract.getPartyB())))
|
.filter(contract -> ContractPartySupport.carrierPartyNames(contract).stream()
|
||||||
|
.anyMatch(allowedNames::contains))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
return contracts.stream().map(contract -> {
|
List<LoadingCarrierContractVO> options = ContractPartySupport.expandCarrierOptions(contracts);
|
||||||
LoadingCarrierContractVO option = new LoadingCarrierContractVO();
|
if (allowedCarrierNames == null) {
|
||||||
option.setId(contract.getId());
|
return options;
|
||||||
option.setContractName(contract.getContractName());
|
}
|
||||||
option.setCarrierName(contract.getPartyB());
|
Set<String> allowedNames = allowedCarrierNames;
|
||||||
return option;
|
return options.stream()
|
||||||
}).toList();
|
.filter(option -> allowedNames.contains(normalizePartyName(option.getCarrierName())))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -973,6 +978,8 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
private void validateCarrierContract(LoadingManage loadingManage, boolean required, List<Waybill> waybills) {
|
private void validateCarrierContract(LoadingManage loadingManage, boolean required, List<Waybill> waybills) {
|
||||||
if (!Objects.equals(loadingManage.getCarrierType(), "承运商")) {
|
if (!Objects.equals(loadingManage.getCarrierType(), "承运商")) {
|
||||||
loadingManage.setCarrierContractId(null);
|
loadingManage.setCarrierContractId(null);
|
||||||
|
loadingManage.setCarrierPartyId(null);
|
||||||
|
loadingManage.setCarrierPartyRole(null);
|
||||||
if (!Objects.equals(loadingManage.getCarrierType(), CARRIER_SELF)) {
|
if (!Objects.equals(loadingManage.getCarrierType(), CARRIER_SELF)) {
|
||||||
loadingManage.setCarrierName(null);
|
loadingManage.setCarrierName(null);
|
||||||
}
|
}
|
||||||
@@ -983,6 +990,8 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
throw new ServiceException("请选择承运商合同");
|
throw new ServiceException("请选择承运商合同");
|
||||||
}
|
}
|
||||||
loadingManage.setCarrierName(null);
|
loadingManage.setCarrierName(null);
|
||||||
|
loadingManage.setCarrierPartyId(null);
|
||||||
|
loadingManage.setCarrierPartyRole(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContractManage carrierContract = availableCarrierContractById(loadingManage.getCarrierContractId());
|
ContractManage carrierContract = availableCarrierContractById(loadingManage.getCarrierContractId());
|
||||||
@@ -1003,19 +1012,35 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
? "所选承运商合同不属于本项目"
|
? "所选承运商合同不属于本项目"
|
||||||
: "所选承运商合同不属于运单" + waybillNos + "的项目");
|
: "所选承运商合同不属于运单" + waybillNos + "的项目");
|
||||||
}
|
}
|
||||||
|
Map<String, Object> selectedParty = ContractPartySupport.findCarrierParty(
|
||||||
|
carrierContract, loadingManage.getCarrierPartyId(), loadingManage.getCarrierName());
|
||||||
|
if (selectedParty == null && Func.isNotEmpty(loadingManage.getCarrierName())
|
||||||
|
&& !ContractPartySupport.matchesCarrier(carrierContract, loadingManage.getCarrierName(), loadingManage.getCarrierPartyId())) {
|
||||||
|
throw new ServiceException("所选承运商不属于该承运商合同的乙方或丙方客商");
|
||||||
|
}
|
||||||
|
if (selectedParty != null) {
|
||||||
|
Object customerName = selectedParty.get("customerName");
|
||||||
|
Object partyId = selectedParty.get("id");
|
||||||
|
Object partyRole = selectedParty.get("partyRole");
|
||||||
|
loadingManage.setCarrierName(customerName == null ? null : normalizePartyName(String.valueOf(customerName)));
|
||||||
|
loadingManage.setCarrierPartyId(partyId == null ? null : normalizePartyName(String.valueOf(partyId)));
|
||||||
|
loadingManage.setCarrierPartyRole(partyRole == null ? null : ContractPartySupport.normalizeRole(String.valueOf(partyRole)));
|
||||||
|
} else if (Func.isEmpty(loadingManage.getCarrierName())) {
|
||||||
|
loadingManage.setCarrierName(ContractPartySupport.firstPartyName(carrierContract.getPartyBJson(), carrierContract.getPartyB()));
|
||||||
|
loadingManage.setCarrierPartyRole(ContractPartySupport.ROLE_B);
|
||||||
|
}
|
||||||
if (Func.isNotEmpty(childWaybills)) {
|
if (Func.isNotEmpty(childWaybills)) {
|
||||||
List<ContractManage> projectContracts = availableCarrierContracts(projectIdsOf(childWaybills));
|
List<ContractManage> projectContracts = availableCarrierContracts(projectIdsOf(childWaybills));
|
||||||
Set<String> allowedPartyBs = intersectCarrierPartyBs(childWaybills, projectContracts);
|
Set<String> allowedPartyBs = intersectCarrierPartyBs(childWaybills, projectContracts);
|
||||||
String partyB = normalizePartyName(carrierContract.getPartyB());
|
String carrierName = normalizePartyName(loadingManage.getCarrierName());
|
||||||
if (Func.isNotEmpty(allowedPartyBs) && !allowedPartyBs.contains(partyB)) {
|
if (Func.isNotEmpty(allowedPartyBs) && !allowedPartyBs.contains(carrierName)) {
|
||||||
throw new ServiceException("所选承运商合同乙方与子运单可共同承运的乙方不一致,可选:"
|
throw new ServiceException("所选承运客商与子运单可共同承运的客商不一致,可选:"
|
||||||
+ String.join("、", allowedPartyBs));
|
+ String.join("、", allowedPartyBs));
|
||||||
}
|
}
|
||||||
if (allowedPartyBs.isEmpty()) {
|
if (allowedPartyBs.isEmpty()) {
|
||||||
throw new ServiceException("所选运单在同一项目下没有共同的承运商合同乙方,不能配载到同一配载单");
|
throw new ServiceException("所选运单在同一项目下没有共同的承运商合同客商,不能配载到同一配载单");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadingManage.setCarrierName(carrierContract.getPartyB());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCompletedCarrierContract(LoadingManage loadingManage) {
|
private void validateCompletedCarrierContract(LoadingManage loadingManage) {
|
||||||
@@ -1054,12 +1079,11 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 各子运单按「客户合同乙方 = 承运商合同甲方」匹配可用承运商合同,再对乙方名称取交集。
|
* 各子运单按「客户合同乙方 ≈ 承运商合同甲方」匹配可用承运商合同,再对乙/丙方客商名称取交集。
|
||||||
* 例:客户合同 盛达(甲)-捷联(乙),承运商合同 捷联(甲)-驰远(乙);子单1可选 A/B/C,子单2可选 B,则交集为 B。
|
|
||||||
*/
|
*/
|
||||||
private Set<String> intersectCarrierPartyBs(List<Waybill> waybills, List<ContractManage> projectContracts) {
|
private Set<String> intersectCarrierPartyBs(List<Waybill> waybills, List<ContractManage> projectContracts) {
|
||||||
Set<String> intersection = null;
|
Set<String> intersection = null;
|
||||||
Map<Long, String> customerPartyBMap = loadCustomerContractPartyBMap(waybills);
|
Map<Long, Set<String>> customerPartyBMap = loadCustomerContractPartyBMap(waybills);
|
||||||
for (Waybill waybill : waybills) {
|
for (Waybill waybill : waybills) {
|
||||||
Set<String> available = availableCarrierPartyBs(waybill, projectContracts, customerPartyBMap);
|
Set<String> available = availableCarrierPartyBs(waybill, projectContracts, customerPartyBMap);
|
||||||
if (intersection == null) {
|
if (intersection == null) {
|
||||||
@@ -1075,23 +1099,23 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> availableCarrierPartyBs(Waybill waybill, List<ContractManage> projectContracts,
|
private Set<String> availableCarrierPartyBs(Waybill waybill, List<ContractManage> projectContracts,
|
||||||
Map<Long, String> customerPartyBMap) {
|
Map<Long, Set<String>> customerPartyBMap) {
|
||||||
if (waybill == null || Func.isEmpty(projectContracts)) {
|
if (waybill == null || Func.isEmpty(projectContracts)) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
}
|
}
|
||||||
Long contractId = waybill.getContractId();
|
Long contractId = waybill.getContractId();
|
||||||
// 运单没有客户合同时不按乙方过滤;客户合同乙方即我方,应等于承运商合同甲方
|
Set<String> customerPartyBs = contractId == null
|
||||||
String partyB = contractId == null ? "" : normalizePartyName(customerPartyBMap.get(contractId));
|
? Set.of()
|
||||||
|
: customerPartyBMap.getOrDefault(contractId, Set.of());
|
||||||
return projectContracts.stream()
|
return projectContracts.stream()
|
||||||
.filter(contract -> Objects.equals(contract.getProjectId(), waybill.getProjectId()))
|
.filter(contract -> Objects.equals(contract.getProjectId(), waybill.getProjectId()))
|
||||||
.filter(contract -> Func.isEmpty(partyB) || Objects.equals(normalizePartyName(contract.getPartyA()), partyB))
|
.filter(contract -> customerPartyBs.isEmpty() || partyNamesOverlap(partyANames(contract), customerPartyBs))
|
||||||
.map(ContractManage::getPartyB)
|
.flatMap(contract -> ContractPartySupport.carrierPartyNames(contract).stream())
|
||||||
.map(this::normalizePartyName)
|
|
||||||
.filter(Func::isNotEmpty)
|
.filter(Func::isNotEmpty)
|
||||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Long, String> loadCustomerContractPartyBMap(List<Waybill> waybills) {
|
private Map<Long, Set<String>> loadCustomerContractPartyBMap(List<Waybill> waybills) {
|
||||||
List<Long> contractIds = waybills.stream()
|
List<Long> contractIds = waybills.stream()
|
||||||
.map(Waybill::getContractId)
|
.map(Waybill::getContractId)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
@@ -1105,11 +1129,55 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
.in(ContractManage::getId, contractIds))
|
.in(ContractManage::getId, contractIds))
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(ContractManage::getId,
|
.collect(Collectors.toMap(ContractManage::getId,
|
||||||
contract -> normalizePartyName(contract.getPartyB()),
|
this::partyBNames,
|
||||||
(left, right) -> left,
|
(left, right) -> left,
|
||||||
LinkedHashMap::new));
|
LinkedHashMap::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> partyANames(ContractManage contract) {
|
||||||
|
Set<String> names = ContractPartySupport.parsePartyRows(contract.getPartyAJson()).stream()
|
||||||
|
.map(row -> normalizePartyName(String.valueOf(row.get("customerName"))))
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
if (names.isEmpty() && Func.isNotEmpty(contract.getPartyA())) {
|
||||||
|
for (String part : contract.getPartyA().split("[、,]")) {
|
||||||
|
String name = normalizePartyName(part);
|
||||||
|
if (Func.isNotEmpty(name)) {
|
||||||
|
names.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> partyBNames(ContractManage contract) {
|
||||||
|
Set<String> names = ContractPartySupport.parsePartyRows(contract.getPartyBJson()).stream()
|
||||||
|
.map(row -> normalizePartyName(String.valueOf(row.get("customerName"))))
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
if (names.isEmpty() && Func.isNotEmpty(contract.getPartyB())) {
|
||||||
|
for (String part : contract.getPartyB().split("[、,]")) {
|
||||||
|
String name = normalizePartyName(part);
|
||||||
|
if (Func.isNotEmpty(name)) {
|
||||||
|
names.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean partyNamesOverlap(Set<String> left, Set<String> right) {
|
||||||
|
if (Func.isEmpty(left) || Func.isEmpty(right)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String name : left) {
|
||||||
|
if (right.contains(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizePartyName(String value) {
|
private String normalizePartyName(String value) {
|
||||||
return value == null ? "" : value.trim();
|
return value == null ? "" : value.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-8
@@ -30,6 +30,7 @@ import org.springblade.transport.service.IDriverService;
|
|||||||
import org.springblade.transport.service.IProjectApplyService;
|
import org.springblade.transport.service.IProjectApplyService;
|
||||||
import org.springblade.transport.service.ITransportPlanService;
|
import org.springblade.transport.service.ITransportPlanService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
import org.springblade.transport.support.WaybillProcessSupport;
|
import org.springblade.transport.support.WaybillProcessSupport;
|
||||||
import org.springblade.transport.wrapper.WaybillWrapper;
|
import org.springblade.transport.wrapper.WaybillWrapper;
|
||||||
@@ -87,11 +88,15 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MasterOrderCarrierVO> carriers(Long id) {
|
public List<MasterOrderCarrierVO> carriers(Long id) {
|
||||||
return availableCarrierContracts(getRequired(id)).stream().map(contract -> {
|
return ContractPartySupport.expandCarrierOptions(availableCarrierContracts(getRequired(id))).stream().map(option -> {
|
||||||
MasterOrderCarrierVO carrier = new MasterOrderCarrierVO();
|
MasterOrderCarrierVO carrier = new MasterOrderCarrierVO();
|
||||||
carrier.setContractId(contract.getId());
|
carrier.setContractId(option.getId());
|
||||||
carrier.setContractName(contract.getContractName());
|
carrier.setContractName(option.getContractName());
|
||||||
carrier.setCarrierName(contract.getPartyB());
|
carrier.setCarrierName(option.getCarrierName());
|
||||||
|
carrier.setCarrierPartyId(option.getCarrierPartyId());
|
||||||
|
carrier.setCarrierPartyRole(option.getCarrierPartyRole());
|
||||||
|
carrier.setCreditCode(option.getCreditCode());
|
||||||
|
carrier.setCustomerId(option.getCustomerId());
|
||||||
return carrier;
|
return carrier;
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
@@ -267,7 +272,11 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
waybill.setContractId(masterOrder.getContractId()); waybill.setContractName(masterOrder.getContractName());
|
waybill.setContractId(masterOrder.getContractId()); waybill.setContractName(masterOrder.getContractName());
|
||||||
waybill.setCustomerName(masterOrder.getCustomerName()); waybill.setMasterNo(masterOrder.getMasterNo());
|
waybill.setCustomerName(masterOrder.getCustomerName()); waybill.setMasterNo(masterOrder.getMasterNo());
|
||||||
waybill.setTransportType(string(dispatch, "transportType")); waybill.setCarrierType(string(dispatch, "carrierType"));
|
waybill.setTransportType(string(dispatch, "transportType")); waybill.setCarrierType(string(dispatch, "carrierType"));
|
||||||
waybill.setCarrierContractId(longValue(dispatch, "carrierContractId")); waybill.setCarrierName(string(dispatch, "carrierName")); waybill.setDriverName(string(dispatch, "driverName"));
|
waybill.setCarrierContractId(longValue(dispatch, "carrierContractId"));
|
||||||
|
waybill.setCarrierName(string(dispatch, "carrierName"));
|
||||||
|
waybill.setCarrierPartyId(string(dispatch, "carrierPartyId"));
|
||||||
|
waybill.setCarrierPartyRole(string(dispatch, "carrierPartyRole"));
|
||||||
|
waybill.setDriverName(string(dispatch, "driverName"));
|
||||||
waybill.setDriverPhone(string(dispatch, "driverPhone")); waybill.setVehicleNo(string(dispatch, "vehicleNo")); waybill.setTrailerVehicleNo(string(dispatch, "trailerVehicleNo"));
|
waybill.setDriverPhone(string(dispatch, "driverPhone")); waybill.setVehicleNo(string(dispatch, "vehicleNo")); waybill.setTrailerVehicleNo(string(dispatch, "trailerVehicleNo"));
|
||||||
// 归属主键是档案 ID:调度带了就用它,否则按手机号解析;解析入口只返回 null 或唯一命中的
|
// 归属主键是档案 ID:调度带了就用它,否则按手机号解析;解析入口只返回 null 或唯一命中的
|
||||||
// 档案 ID,承运商司机解析为空也照常派单,落空时留 WARN
|
// 档案 ID,承运商司机解析为空也照常派单,落空时留 WARN
|
||||||
@@ -603,7 +612,7 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
.and(wrapper -> wrapper.isNull(ContractManage::getContractStage)
|
.and(wrapper -> wrapper.isNull(ContractManage::getContractStage)
|
||||||
.or().ne(ContractManage::getContractStage, "terminated"))
|
.or().ne(ContractManage::getContractStage, "terminated"))
|
||||||
.orderByDesc(ContractManage::getCreateTime))
|
.orderByDesc(ContractManage::getCreateTime))
|
||||||
.stream().filter(contract -> Func.isNotEmpty(contract.getPartyB())).toList();
|
.stream().filter(contract -> Func.isNotEmpty(ContractPartySupport.carrierPartyNames(contract))).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCarrier(Map<String, Object> dispatch, Map<Long, ContractManage> availableCarrierContracts) {
|
private void validateCarrier(Map<String, Object> dispatch, Map<Long, ContractManage> availableCarrierContracts) {
|
||||||
@@ -615,11 +624,29 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String carrierName = string(dispatch, "carrierName");
|
String carrierName = string(dispatch, "carrierName");
|
||||||
|
String carrierPartyId = string(dispatch, "carrierPartyId");
|
||||||
Long carrierContractId = longValue(dispatch, "carrierContractId");
|
Long carrierContractId = longValue(dispatch, "carrierContractId");
|
||||||
ContractManage carrierContract = carrierContractId == null ? null : availableCarrierContracts.get(carrierContractId);
|
ContractManage carrierContract = carrierContractId == null ? null : availableCarrierContracts.get(carrierContractId);
|
||||||
if ("承运商".equals(carrierType) && (carrierContract == null
|
if ("承运商".equals(carrierType) && (carrierContract == null
|
||||||
|| !Objects.equals(carrierContract.getPartyB(), carrierName))) {
|
|| !ContractPartySupport.matchesCarrier(carrierContract, carrierName, carrierPartyId))) {
|
||||||
throw new ServiceException("所选承运商不属于总单客户合同对应项目的有效承运商合同乙方");
|
throw new ServiceException("所选承运商不属于总单客户合同对应项目的有效承运商合同乙/丙方客商");
|
||||||
|
}
|
||||||
|
if ("承运商".equals(carrierType) && carrierContract != null) {
|
||||||
|
Map<String, Object> selectedParty = ContractPartySupport.findCarrierParty(carrierContract, carrierPartyId, carrierName);
|
||||||
|
if (selectedParty != null) {
|
||||||
|
Object selectedName = selectedParty.get("customerName");
|
||||||
|
Object selectedPartyId = selectedParty.get("id");
|
||||||
|
Object selectedPartyRole = selectedParty.get("partyRole");
|
||||||
|
if (selectedName != null) {
|
||||||
|
dispatch.put("carrierName", String.valueOf(selectedName).trim());
|
||||||
|
}
|
||||||
|
if (selectedPartyId != null) {
|
||||||
|
dispatch.put("carrierPartyId", String.valueOf(selectedPartyId).trim());
|
||||||
|
}
|
||||||
|
if (selectedPartyRole != null) {
|
||||||
|
dispatch.put("carrierPartyRole", ContractPartySupport.normalizeRole(String.valueOf(selectedPartyRole)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
String transportType = string(dispatch, "transportType", "");
|
String transportType = string(dispatch, "transportType", "");
|
||||||
boolean road = transportType.toLowerCase().contains("road") || transportType.contains("公路");
|
boolean road = transportType.toLowerCase().contains("road") || transportType.contains("公路");
|
||||||
|
|||||||
+62
-9
@@ -61,6 +61,7 @@ import org.springblade.transport.service.IFormalSettlementService;
|
|||||||
import org.springblade.transport.service.IPreSettlementService;
|
import org.springblade.transport.service.IPreSettlementService;
|
||||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
import org.springblade.transport.support.WaybillProcessSupport;
|
import org.springblade.transport.support.WaybillProcessSupport;
|
||||||
import org.springblade.transport.wrapper.ReceivablePayableDetailWrapper;
|
import org.springblade.transport.wrapper.ReceivablePayableDetailWrapper;
|
||||||
@@ -566,6 +567,12 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
item.put("id", contract.getId());
|
item.put("id", contract.getId());
|
||||||
item.put("contractNo", contract.getContractNo());
|
item.put("contractNo", contract.getContractNo());
|
||||||
item.put("contractName", contract.getContractName());
|
item.put("contractName", contract.getContractName());
|
||||||
|
item.put("partyA", contract.getPartyA());
|
||||||
|
item.put("partyB", contract.getPartyB());
|
||||||
|
item.put("partyC", contract.getPartyC());
|
||||||
|
item.put("partyAJson", contract.getPartyAJson());
|
||||||
|
item.put("partyBJson", contract.getPartyBJson());
|
||||||
|
item.put("partyCJson", contract.getPartyCJson());
|
||||||
item.put("billingPlanJson", contract.getBillingPlanJson());
|
item.put("billingPlanJson", contract.getBillingPlanJson());
|
||||||
return item;
|
return item;
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -1258,9 +1265,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
&& !Objects.equals(waybill.getProjectId(), contract.getProjectId())) {
|
&& !Objects.equals(waybill.getProjectId(), contract.getProjectId())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String carrierName = trimToNull(waybill.getCarrierName());
|
return ContractPartySupport.matchesCarrier(contract, waybill.getCarrierName(), waybill.getCarrierPartyId());
|
||||||
String partyB = trimToNull(contract.getPartyB());
|
|
||||||
return carrierName != null && carrierName.equals(partyB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContractManage findCarrierContract(Waybill waybill) {
|
private ContractManage findCarrierContract(Waybill waybill) {
|
||||||
@@ -1268,20 +1273,50 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
if (waybill.getProjectId() == null || Func.isEmpty(waybill.getContractId()) || carrierName == null) {
|
if (waybill.getProjectId() == null || Func.isEmpty(waybill.getContractId()) || carrierName == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 承运商合同甲方应与运单绑定的客户合同乙方(我方)一致,乙方必须是运单选择的承运商。
|
// 承运商合同甲方应与运单绑定的客户合同乙方(我方)一致,乙/丙方必须包含运单选择的承运商。
|
||||||
ContractManage customerContract = contractManageService.getById(waybill.getContractId());
|
ContractManage customerContract = contractManageService.getById(waybill.getContractId());
|
||||||
if (customerContract == null || Func.isEmpty(customerContract.getPartyB())) {
|
if (customerContract == null || Func.isEmpty(customerContract.getPartyB())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return contractManageService.getOne(Wrappers.<ContractManage>lambdaQuery()
|
List<ContractManage> contracts = contractManageService.list(Wrappers.<ContractManage>lambdaQuery()
|
||||||
.eq(ContractManage::getIsDeleted, 0)
|
.eq(ContractManage::getIsDeleted, 0)
|
||||||
.eq(ContractManage::getProjectId, waybill.getProjectId())
|
.eq(ContractManage::getProjectId, waybill.getProjectId())
|
||||||
.eq(ContractManage::getPartyA, customerContract.getPartyB())
|
|
||||||
.eq(ContractManage::getPartyB, carrierName)
|
|
||||||
.eq(ContractManage::getContractCategory, "承运商合同")
|
.eq(ContractManage::getContractCategory, "承运商合同")
|
||||||
.and(wrapper -> wrapper.isNull(ContractManage::getContractStage)
|
.and(wrapper -> wrapper.isNull(ContractManage::getContractStage)
|
||||||
.or().ne(ContractManage::getContractStage, "terminated"))
|
.or().ne(ContractManage::getContractStage, "terminated"))
|
||||||
.orderByDesc(ContractManage::getCreateTime), false);
|
.orderByDesc(ContractManage::getCreateTime));
|
||||||
|
Set<String> customerPartyBs = splitPartyNames(customerContract.getPartyBJson(), customerContract.getPartyB());
|
||||||
|
for (ContractManage contract : contracts) {
|
||||||
|
Set<String> partyANames = splitPartyNames(contract.getPartyAJson(), contract.getPartyA());
|
||||||
|
boolean partyAMatched = partyANames.isEmpty() || partyANames.stream().anyMatch(customerPartyBs::contains)
|
||||||
|
|| customerPartyBs.contains(trimToNull(contract.getPartyA()));
|
||||||
|
if (!partyAMatched) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ContractPartySupport.matchesCarrier(contract, carrierName, waybill.getCarrierPartyId())) {
|
||||||
|
return contract;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> splitPartyNames(String partyJson, String fallback) {
|
||||||
|
Set<String> names = new LinkedHashSet<>();
|
||||||
|
for (Map<String, Object> row : ContractPartySupport.parsePartyRows(partyJson)) {
|
||||||
|
Object customerName = row.get("customerName");
|
||||||
|
if (customerName != null && Func.isNotEmpty(String.valueOf(customerName).trim())) {
|
||||||
|
names.add(String.valueOf(customerName).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (names.isEmpty() && Func.isNotEmpty(fallback)) {
|
||||||
|
for (String part : fallback.split("[、,]")) {
|
||||||
|
String name = trimToNull(part);
|
||||||
|
if (name != null) {
|
||||||
|
names.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String trimToNull(String value) {
|
private String trimToNull(String value) {
|
||||||
@@ -2058,13 +2093,31 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
*/
|
*/
|
||||||
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, ContractManage contract, String planId,
|
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, ContractManage contract, String planId,
|
||||||
boolean matchOnly, boolean strictPlan) {
|
boolean matchOnly, boolean strictPlan) {
|
||||||
List<Map<String, Object>> plans = parseList(contract == null ? null : contract.getBillingPlanJson());
|
List<Map<String, Object>> plans = billingPlansForWaybill(contract, waybill);
|
||||||
Map<String, Object> plan = "__matched__".equals(planId)
|
Map<String, Object> plan = "__matched__".equals(planId)
|
||||||
? resolveDefaultBillingPlan(plans, waybill.getTransportType())
|
? resolveDefaultBillingPlan(plans, waybill.getTransportType())
|
||||||
: resolveBillingPlan(plans, planId);
|
: resolveBillingPlan(plans, planId);
|
||||||
return calculatedFees(waybill, plan, matchOnly, strictPlan);
|
return calculatedFees(waybill, plan, matchOnly, strictPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按运单客商过滤合同计费方案:承运商合同用承运客商,客户合同用客户名称。
|
||||||
|
*/
|
||||||
|
private List<Map<String, Object>> billingPlansForWaybill(ContractManage contract, Waybill waybill) {
|
||||||
|
if (contract == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
if (waybill == null) {
|
||||||
|
return parseList(contract.getBillingPlanJson());
|
||||||
|
}
|
||||||
|
if ("承运商合同".equals(contract.getContractCategory())) {
|
||||||
|
return ContractPartySupport.filterBillingPlans(
|
||||||
|
contract.getBillingPlanJson(), null, waybill.getCarrierName(), waybill.getCarrierPartyId());
|
||||||
|
}
|
||||||
|
return ContractPartySupport.filterBillingPlans(
|
||||||
|
contract.getBillingPlanJson(), null, waybill.getCustomerName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, Map<String, Object> plan,
|
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, Map<String, Object> plan,
|
||||||
boolean matchOnly) {
|
boolean matchOnly) {
|
||||||
return calculatedFees(waybill, plan, matchOnly, false);
|
return calculatedFees(waybill, plan, matchOnly, false);
|
||||||
|
|||||||
+21
-2
@@ -69,6 +69,7 @@ import org.springblade.transport.service.IProjectApplyService;
|
|||||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
import org.springblade.transport.service.IContractManageService;
|
import org.springblade.transport.service.IContractManageService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springblade.transport.support.LiveProcessConfigSupport;
|
import org.springblade.transport.support.LiveProcessConfigSupport;
|
||||||
import org.springblade.transport.support.LoadableAdmissionSupport;
|
import org.springblade.transport.support.LoadableAdmissionSupport;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
@@ -2253,6 +2254,8 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
private void validateCarrierContract(Waybill waybill) {
|
private void validateCarrierContract(Waybill waybill) {
|
||||||
if ("自运".equals(waybill.getCarrierType())) {
|
if ("自运".equals(waybill.getCarrierType())) {
|
||||||
waybill.setCarrierContractId(null);
|
waybill.setCarrierContractId(null);
|
||||||
|
waybill.setCarrierPartyId(null);
|
||||||
|
waybill.setCarrierPartyRole(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Func.isEmpty(waybill.getCarrierContractId())) {
|
if (Func.isEmpty(waybill.getCarrierContractId())) {
|
||||||
@@ -2262,8 +2265,24 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
if (carrierContract == null || Objects.equals(carrierContract.getIsDeleted(), 1)
|
if (carrierContract == null || Objects.equals(carrierContract.getIsDeleted(), 1)
|
||||||
|| !"承运商合同".equals(carrierContract.getContractCategory())
|
|| !"承运商合同".equals(carrierContract.getContractCategory())
|
||||||
|| !Objects.equals(carrierContract.getProjectId(), waybill.getProjectId())
|
|| !Objects.equals(carrierContract.getProjectId(), waybill.getProjectId())
|
||||||
|| !Objects.equals(carrierContract.getPartyB(), waybill.getCarrierName())) {
|
|| !ContractPartySupport.matchesCarrier(carrierContract, waybill.getCarrierName(), waybill.getCarrierPartyId())) {
|
||||||
throw new ServiceException("承运商合同必须属于所选项目,且合同乙方须与承运商一致");
|
throw new ServiceException("承运商合同必须属于所选项目,且承运商须为合同乙方或丙方客商");
|
||||||
|
}
|
||||||
|
Map<String, Object> selectedParty = ContractPartySupport.findCarrierParty(
|
||||||
|
carrierContract, waybill.getCarrierPartyId(), waybill.getCarrierName());
|
||||||
|
if (selectedParty != null) {
|
||||||
|
Object customerName = selectedParty.get("customerName");
|
||||||
|
Object partyId = selectedParty.get("id");
|
||||||
|
Object partyRole = selectedParty.get("partyRole");
|
||||||
|
if (customerName != null) {
|
||||||
|
waybill.setCarrierName(String.valueOf(customerName).trim());
|
||||||
|
}
|
||||||
|
if (partyId != null) {
|
||||||
|
waybill.setCarrierPartyId(String.valueOf(partyId).trim());
|
||||||
|
}
|
||||||
|
if (partyRole != null) {
|
||||||
|
waybill.setCarrierPartyRole(ContractPartySupport.normalizeRole(String.valueOf(partyRole)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+507
@@ -0,0 +1,507 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.support;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.transport.pojo.entity.ContractManage;
|
||||||
|
import org.springblade.transport.pojo.vo.LoadingCarrierContractVO;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合同甲/乙/丙多方客商明细支持
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public final class ContractPartySupport {
|
||||||
|
|
||||||
|
public static final String ROLE_A = "A";
|
||||||
|
public static final String ROLE_B = "B";
|
||||||
|
public static final String ROLE_C = "C";
|
||||||
|
|
||||||
|
private ContractPartySupport() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规范化三方客商明细并回写名称汇总
|
||||||
|
*/
|
||||||
|
public static void normalizeAndFillSummary(ContractManage contractManage) {
|
||||||
|
List<Map<String, Object>> partyARows = normalizePartyRows(contractManage.getPartyAJson(), true);
|
||||||
|
List<Map<String, Object>> partyBRows = normalizePartyRows(contractManage.getPartyBJson(), true);
|
||||||
|
List<Map<String, Object>> partyCRows = normalizePartyRows(contractManage.getPartyCJson(), false);
|
||||||
|
contractManage.setPartyAJson(toJsonOrNull(partyARows));
|
||||||
|
contractManage.setPartyBJson(toJsonOrNull(partyBRows));
|
||||||
|
contractManage.setPartyCJson(toJsonOrNull(partyCRows));
|
||||||
|
contractManage.setPartyA(joinCustomerNames(partyARows));
|
||||||
|
contractManage.setPartyB(joinCustomerNames(partyBRows));
|
||||||
|
contractManage.setPartyC(joinCustomerNames(partyCRows));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交级校验:甲/乙至少一行,计费方案必须绑定明细客商
|
||||||
|
*/
|
||||||
|
public static void validateRequiredParties(ContractManage contractManage, boolean requireCustomerId) {
|
||||||
|
List<Map<String, Object>> partyARows = parsePartyRows(contractManage.getPartyAJson());
|
||||||
|
List<Map<String, Object>> partyBRows = parsePartyRows(contractManage.getPartyBJson());
|
||||||
|
List<Map<String, Object>> partyCRows = parsePartyRows(contractManage.getPartyCJson());
|
||||||
|
if (partyARows.isEmpty()) {
|
||||||
|
throw new ServiceException("请至少添加一个甲方客商");
|
||||||
|
}
|
||||||
|
if (partyBRows.isEmpty()) {
|
||||||
|
throw new ServiceException("请至少添加一个乙方客商");
|
||||||
|
}
|
||||||
|
validatePartyRows("甲方", partyARows, requireCustomerId);
|
||||||
|
validatePartyRows("乙方", partyBRows, requireCustomerId);
|
||||||
|
if (!partyCRows.isEmpty()) {
|
||||||
|
validatePartyRows("丙方", partyCRows, requireCustomerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验计费方案均已绑定合同明细中的客商
|
||||||
|
*/
|
||||||
|
public static void validateBillingPlanBindings(ContractManage contractManage) {
|
||||||
|
String billingPlanJson = contractManage.getBillingPlanJson();
|
||||||
|
if (Func.isEmpty(billingPlanJson)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object parsed = JsonUtil.parse(billingPlanJson, List.class);
|
||||||
|
if (!(parsed instanceof List<?> plans) || plans.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, Map<String, Object>> partyByRowId = indexPartyRows(contractManage);
|
||||||
|
Set<String> customerKeys = partyByRowId.values().stream()
|
||||||
|
.map(ContractPartySupport::customerMatchKey)
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
for (int index = 0; index < plans.size(); index++) {
|
||||||
|
Object value = plans.get(index);
|
||||||
|
if (!(value instanceof Map<?, ?> plan)) {
|
||||||
|
throw new ServiceException("计费方案设置格式不正确");
|
||||||
|
}
|
||||||
|
String partyRowId = trimToNull(plan.get("partyRowId"));
|
||||||
|
String customerId = trimToNull(plan.get("customerId"));
|
||||||
|
String customerName = trimToNull(plan.get("customerName"));
|
||||||
|
String partyRole = normalizeRole(trimToNull(plan.get("partyRole")));
|
||||||
|
if (Func.isEmpty(partyRowId) && Func.isEmpty(customerId) && Func.isEmpty(customerName)) {
|
||||||
|
throw new ServiceException("计费方案第" + (index + 1) + "个必须选择适用客商");
|
||||||
|
}
|
||||||
|
boolean matched = false;
|
||||||
|
if (Func.isNotEmpty(partyRowId) && partyByRowId.containsKey(partyRowId)) {
|
||||||
|
matched = true;
|
||||||
|
} else {
|
||||||
|
String key = customerMatchKey(customerId, customerName, partyRole);
|
||||||
|
matched = Func.isNotEmpty(key) && customerKeys.contains(key);
|
||||||
|
if (!matched && Func.isNotEmpty(customerName)) {
|
||||||
|
matched = partyByRowId.values().stream()
|
||||||
|
.anyMatch(row -> Objects.equals(trimToNull(row.get("customerName")), customerName)
|
||||||
|
&& (Func.isEmpty(partyRole) || Objects.equals(normalizeRole(String.valueOf(row.get("partyRole"))), partyRole)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched) {
|
||||||
|
throw new ServiceException("计费方案第" + (index + 1) + "个适用客商不在合同甲/乙/丙明细中");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, Object>> parsePartyRows(String partyJson) {
|
||||||
|
if (Func.isEmpty(partyJson)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object parsed = JsonUtil.parse(partyJson, List.class);
|
||||||
|
if (!(parsed instanceof List<?> rows) || rows.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
for (Object row : rows) {
|
||||||
|
if (row instanceof Map<?, ?> map) {
|
||||||
|
Map<String, Object> values = new LinkedHashMap<>();
|
||||||
|
map.forEach((key, value) -> values.put(String.valueOf(key), value));
|
||||||
|
result.add(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, Object>> carrierPartyRows(ContractManage contractManage) {
|
||||||
|
List<Map<String, Object>> rows = new ArrayList<>();
|
||||||
|
parsePartyRows(contractManage == null ? null : contractManage.getPartyBJson()).forEach(row -> {
|
||||||
|
row.put("partyRole", ROLE_B);
|
||||||
|
rows.add(row);
|
||||||
|
});
|
||||||
|
parsePartyRows(contractManage == null ? null : contractManage.getPartyCJson()).forEach(row -> {
|
||||||
|
row.put("partyRole", ROLE_C);
|
||||||
|
rows.add(row);
|
||||||
|
});
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> carrierPartyNames(ContractManage contractManage) {
|
||||||
|
Set<String> names = new LinkedHashSet<>();
|
||||||
|
for (Map<String, Object> row : carrierPartyRows(contractManage)) {
|
||||||
|
String name = normalizePartyName(trimToNull(row.get("customerName")));
|
||||||
|
if (Func.isNotEmpty(name)) {
|
||||||
|
names.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 兼容历史仅有汇总字段的合同
|
||||||
|
if (names.isEmpty() && contractManage != null) {
|
||||||
|
String partyB = normalizePartyName(contractManage.getPartyB());
|
||||||
|
if (Func.isNotEmpty(partyB)) {
|
||||||
|
names.add(partyB);
|
||||||
|
}
|
||||||
|
String partyC = normalizePartyName(contractManage.getPartyC());
|
||||||
|
if (Func.isNotEmpty(partyC)) {
|
||||||
|
names.add(partyC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean matchesCarrier(ContractManage contractManage, String carrierName, String carrierPartyId) {
|
||||||
|
if (contractManage == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> rows = carrierPartyRows(contractManage);
|
||||||
|
if (Func.isNotEmpty(carrierPartyId)) {
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
if (Objects.equals(trimToNull(row.get("id")), trimToNull(carrierPartyId))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String normalizedName = normalizePartyName(carrierName);
|
||||||
|
if (Func.isEmpty(normalizedName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
return Objects.equals(normalizePartyName(contractManage.getPartyB()), normalizedName)
|
||||||
|
|| Objects.equals(normalizePartyName(contractManage.getPartyC()), normalizedName);
|
||||||
|
}
|
||||||
|
return rows.stream()
|
||||||
|
.anyMatch(row -> Objects.equals(normalizePartyName(trimToNull(row.get("customerName"))), normalizedName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> findCarrierParty(ContractManage contractManage, String carrierPartyId, String carrierName) {
|
||||||
|
List<Map<String, Object>> rows = carrierPartyRows(contractManage);
|
||||||
|
if (Func.isNotEmpty(carrierPartyId)) {
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
if (Objects.equals(trimToNull(row.get("id")), trimToNull(carrierPartyId))) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String normalizedName = normalizePartyName(carrierName);
|
||||||
|
if (Func.isEmpty(normalizedName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
if (Objects.equals(normalizePartyName(trimToNull(row.get("customerName"))), normalizedName)) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, Object>> filterBillingPlans(String billingPlanJson, Long customerId, String customerName, String partyRowId) {
|
||||||
|
List<Map<String, Object>> plans = parseObjectList(billingPlanJson);
|
||||||
|
if (plans.isEmpty()) {
|
||||||
|
return plans;
|
||||||
|
}
|
||||||
|
String normalizedName = normalizePartyName(customerName);
|
||||||
|
String normalizedPartyRowId = trimToNull(partyRowId);
|
||||||
|
String customerIdText = customerId == null ? null : String.valueOf(customerId);
|
||||||
|
List<Map<String, Object>> matched = plans.stream()
|
||||||
|
.filter(plan -> {
|
||||||
|
if (Func.isNotEmpty(normalizedPartyRowId)
|
||||||
|
&& Objects.equals(trimToNull(plan.get("partyRowId")), normalizedPartyRowId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Func.isNotEmpty(customerIdText)
|
||||||
|
&& Objects.equals(trimToNull(plan.get("customerId")), customerIdText)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Func.isNotEmpty(normalizedName)
|
||||||
|
&& Objects.equals(normalizePartyName(trimToNull(plan.get("customerName"))), normalizedName);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
// 历史方案未绑定客商时,不强制过滤,避免老数据无法计费
|
||||||
|
if (matched.isEmpty() && plans.stream().noneMatch(ContractPartySupport::hasCustomerBinding)) {
|
||||||
|
return plans;
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<LoadingCarrierContractVO> expandCarrierOptions(Collection<ContractManage> contracts) {
|
||||||
|
if (Func.isEmpty(contracts)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<LoadingCarrierContractVO> options = new ArrayList<>();
|
||||||
|
for (ContractManage contract : contracts) {
|
||||||
|
if (contract == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> rows = carrierPartyRows(contract);
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
LoadingCarrierContractVO option = new LoadingCarrierContractVO();
|
||||||
|
option.setId(contract.getId());
|
||||||
|
option.setContractName(contract.getContractName());
|
||||||
|
option.setCarrierName(contract.getPartyB());
|
||||||
|
option.setCarrierPartyRole(ROLE_B);
|
||||||
|
options.add(option);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
LoadingCarrierContractVO option = new LoadingCarrierContractVO();
|
||||||
|
option.setId(contract.getId());
|
||||||
|
option.setContractName(contract.getContractName());
|
||||||
|
option.setCarrierName(trimToNull(row.get("customerName")));
|
||||||
|
option.setCarrierPartyId(trimToNull(row.get("id")));
|
||||||
|
option.setCarrierPartyRole(normalizeRole(String.valueOf(row.get("partyRole"))));
|
||||||
|
option.setCreditCode(trimToNull(row.get("creditCode")));
|
||||||
|
option.setCustomerId(toLong(row.get("customerId")));
|
||||||
|
options.add(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String normalizePartyName(String value) {
|
||||||
|
return Func.isEmpty(value) ? "" : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String normalizeRole(String role) {
|
||||||
|
if (Func.isEmpty(role)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String text = role.trim().toUpperCase();
|
||||||
|
if ("A".equals(text) || "甲方".equals(role) || "甲".equals(role)) {
|
||||||
|
return ROLE_A;
|
||||||
|
}
|
||||||
|
if ("B".equals(text) || "乙方".equals(role) || "乙".equals(role)) {
|
||||||
|
return ROLE_B;
|
||||||
|
}
|
||||||
|
if ("C".equals(text) || "丙方".equals(role) || "丙".equals(role)) {
|
||||||
|
return ROLE_C;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String firstPartyName(String partyJson, String fallback) {
|
||||||
|
List<Map<String, Object>> rows = parsePartyRows(partyJson);
|
||||||
|
if (!rows.isEmpty()) {
|
||||||
|
String name = trimToNull(rows.get(0).get("customerName"));
|
||||||
|
if (Func.isNotEmpty(name)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TransportBusinessSupport.trimToNull(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Map<String, Object>> normalizePartyRows(String partyJson, boolean keepEmptyPlaceholder) {
|
||||||
|
List<Map<String, Object>> source = parsePartyRows(partyJson);
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
for (Map<String, Object> sourceRow : source) {
|
||||||
|
Map<String, Object> row = new LinkedHashMap<>();
|
||||||
|
String id = trimToNull(sourceRow.get("id"));
|
||||||
|
if (Func.isEmpty(id)) {
|
||||||
|
id = String.valueOf(IdWorker.getId());
|
||||||
|
}
|
||||||
|
row.put("id", id);
|
||||||
|
Long customerId = toLong(sourceRow.get("customerId"));
|
||||||
|
if (customerId != null) {
|
||||||
|
row.put("customerId", customerId);
|
||||||
|
}
|
||||||
|
String customerName = trimToNull(sourceRow.get("customerName"));
|
||||||
|
if (Func.isNotEmpty(customerName)) {
|
||||||
|
row.put("customerName", customerName);
|
||||||
|
}
|
||||||
|
String creditCode = trimToNull(sourceRow.get("creditCode"));
|
||||||
|
if (Func.isNotEmpty(creditCode)) {
|
||||||
|
row.put("creditCode", creditCode);
|
||||||
|
}
|
||||||
|
Long contactId = toLong(sourceRow.get("contactId"));
|
||||||
|
if (contactId != null) {
|
||||||
|
row.put("contactId", contactId);
|
||||||
|
}
|
||||||
|
String contactName = trimToNull(sourceRow.get("contactName"));
|
||||||
|
if (Func.isNotEmpty(contactName)) {
|
||||||
|
row.put("contactName", contactName);
|
||||||
|
}
|
||||||
|
String contactPhone = trimToNull(sourceRow.get("contactPhone"));
|
||||||
|
if (Func.isNotEmpty(contactPhone)) {
|
||||||
|
row.put("contactPhone", contactPhone);
|
||||||
|
}
|
||||||
|
if (Func.isEmpty(customerName) && customerId == null && !keepEmptyPlaceholder) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.add(row);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validatePartyRows(String roleLabel, List<Map<String, Object>> rows, boolean requireCustomerId) {
|
||||||
|
Set<String> customerKeys = new LinkedHashSet<>();
|
||||||
|
for (int index = 0; index < rows.size(); index++) {
|
||||||
|
Map<String, Object> row = rows.get(index);
|
||||||
|
String customerName = trimToNull(row.get("customerName"));
|
||||||
|
Long customerId = toLong(row.get("customerId"));
|
||||||
|
if (Func.isEmpty(customerName)) {
|
||||||
|
throw new ServiceException(roleLabel + "第" + (index + 1) + "行请选择客商");
|
||||||
|
}
|
||||||
|
if (requireCustomerId && customerId == null) {
|
||||||
|
throw new ServiceException(roleLabel + "第" + (index + 1) + "行请选择有效客商");
|
||||||
|
}
|
||||||
|
String contactName = trimToNull(row.get("contactName"));
|
||||||
|
String contactPhone = trimToNull(row.get("contactPhone"));
|
||||||
|
if (Func.isNotEmpty(contactName) && Func.isEmpty(contactPhone)) {
|
||||||
|
throw new ServiceException(roleLabel + "第" + (index + 1) + "行联系人需带出联系电话");
|
||||||
|
}
|
||||||
|
String key = customerId == null ? customerName : String.valueOf(customerId);
|
||||||
|
if (!customerKeys.add(key)) {
|
||||||
|
throw new ServiceException(roleLabel + "存在重复客商:" + customerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Map<String, Object>> indexPartyRows(ContractManage contractManage) {
|
||||||
|
Map<String, Map<String, Object>> indexed = new LinkedHashMap<>();
|
||||||
|
putIndexedRows(indexed, parsePartyRows(contractManage.getPartyAJson()), ROLE_A);
|
||||||
|
putIndexedRows(indexed, parsePartyRows(contractManage.getPartyBJson()), ROLE_B);
|
||||||
|
putIndexedRows(indexed, parsePartyRows(contractManage.getPartyCJson()), ROLE_C);
|
||||||
|
return indexed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void putIndexedRows(Map<String, Map<String, Object>> indexed, List<Map<String, Object>> rows, String role) {
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
Map<String, Object> copy = new LinkedHashMap<>(row);
|
||||||
|
copy.put("partyRole", role);
|
||||||
|
String id = trimToNull(copy.get("id"));
|
||||||
|
if (Func.isNotEmpty(id)) {
|
||||||
|
indexed.put(id, copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String customerMatchKey(Map<String, Object> row) {
|
||||||
|
return customerMatchKey(trimToNull(row.get("customerId")), trimToNull(row.get("customerName")),
|
||||||
|
normalizeRole(String.valueOf(row.get("partyRole"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String customerMatchKey(String customerId, String customerName, String partyRole) {
|
||||||
|
if (Func.isNotEmpty(customerId)) {
|
||||||
|
return normalizeRole(partyRole) + ":" + customerId;
|
||||||
|
}
|
||||||
|
if (Func.isNotEmpty(customerName)) {
|
||||||
|
return normalizeRole(partyRole) + ":" + customerName;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasCustomerBinding(Map<String, Object> plan) {
|
||||||
|
return Func.isNotEmpty(trimToNull(plan.get("partyRowId")))
|
||||||
|
|| Func.isNotEmpty(trimToNull(plan.get("customerId")))
|
||||||
|
|| Func.isNotEmpty(trimToNull(plan.get("customerName")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Map<String, Object>> parseObjectList(String json) {
|
||||||
|
if (Func.isEmpty(json)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object parsed = JsonUtil.parse(json, List.class);
|
||||||
|
if (!(parsed instanceof List<?> list) || list.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
for (Object item : list) {
|
||||||
|
if (item instanceof Map<?, ?> map) {
|
||||||
|
Map<String, Object> values = new LinkedHashMap<>();
|
||||||
|
map.forEach((key, value) -> values.put(String.valueOf(key), value));
|
||||||
|
result.add(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String joinCustomerNames(List<Map<String, Object>> rows) {
|
||||||
|
List<String> names = rows.stream()
|
||||||
|
.map(row -> trimToNull(row.get("customerName")))
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
return names.isEmpty() ? null : String.join("、", names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toJsonOrNull(List<Map<String, Object>> rows) {
|
||||||
|
return rows == null || rows.isEmpty() ? null : JsonUtil.toJson(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trimToNull(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String text = String.valueOf(value).trim();
|
||||||
|
return text.isEmpty() || "null".equalsIgnoreCase(text) ? null : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long toLong(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Number number) {
|
||||||
|
return number.longValue();
|
||||||
|
}
|
||||||
|
String text = trimToNull(value);
|
||||||
|
if (Func.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Long.valueOf(text);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
-13
@@ -32,6 +32,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.tool.jackson.JsonUtil;
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.hcs.config.HcsProperties;
|
import org.springblade.hcs.config.HcsProperties;
|
||||||
import org.springblade.hcs.constant.HcsConstant;
|
import org.springblade.hcs.constant.HcsConstant;
|
||||||
@@ -61,6 +62,7 @@ import org.springblade.transport.pojo.entity.ContractManage;
|
|||||||
import org.springblade.transport.pojo.entity.CustomerArchive;
|
import org.springblade.transport.pojo.entity.CustomerArchive;
|
||||||
import org.springblade.transport.pojo.entity.HcsContractCheckLog;
|
import org.springblade.transport.pojo.entity.HcsContractCheckLog;
|
||||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||||
|
import org.springblade.transport.support.ContractPartySupport;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -532,44 +534,74 @@ public class HcsContractSyncSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* signer 相对方(签署方):甲/乙双方。fdType 档案优先:客商档案为内部组织(customerKind=internal/内部组织)
|
* signer 相对方(签署方):甲/乙/丙多方客商。fdType 档案优先:客商档案为内部组织(customerKind=internal/内部组织)
|
||||||
* 的一方为我方签约主体传 inner,另一方传 customer(外部相对方,2026-10-14 联调口径);
|
* 的一方为我方签约主体传 inner,另一方传 customer(外部相对方,2026-10-14 联调口径);
|
||||||
* 双方均非内部组织或查不到档案时按合同类别推定(承运商合同甲=inner,客户合同乙=inner,其余 customer);
|
* 双方均非内部组织或查不到档案时按合同类别推定(承运商合同甲=inner,客户合同乙=inner,其余 customer);
|
||||||
* 丙方(三方合同)TMS 无签约主体字段来源,暂不支持
|
* 多方时按明细展开,同地位多客商分别签署
|
||||||
*/
|
*/
|
||||||
private List<HcsSignerDTO> buildSigners(ContractManage contractManage) {
|
private List<HcsSignerDTO> buildSigners(ContractManage contractManage) {
|
||||||
List<HcsSignerDTO> signers = new ArrayList<>();
|
List<HcsSignerDTO> signers = new ArrayList<>();
|
||||||
// 甲乙方各反查一次客商档案,编号与内部组织判定共用查询结果
|
List<String> partyANames = partyNames(contractManage.getPartyAJson(), contractManage.getPartyA());
|
||||||
CustomerArchive partyAArchive = queryCustomerArchive(contractManage.getPartyA());
|
List<String> partyBNames = partyNames(contractManage.getPartyBJson(), contractManage.getPartyB());
|
||||||
CustomerArchive partyBArchive = queryCustomerArchive(contractManage.getPartyB());
|
List<String> partyCNames = partyNames(contractManage.getPartyCJson(), contractManage.getPartyC());
|
||||||
|
String firstPartyA = partyANames.isEmpty() ? null : partyANames.get(0);
|
||||||
|
String firstPartyB = partyBNames.isEmpty() ? null : partyBNames.get(0);
|
||||||
|
CustomerArchive partyAArchive = queryCustomerArchive(firstPartyA);
|
||||||
|
CustomerArchive partyBArchive = queryCustomerArchive(firstPartyB);
|
||||||
boolean partyAInternal = HcsCreditCheckSupport.isInternalCustomer(partyAArchive);
|
boolean partyAInternal = HcsCreditCheckSupport.isInternalCustomer(partyAArchive);
|
||||||
boolean partyBInternal = HcsCreditCheckSupport.isInternalCustomer(partyBArchive);
|
boolean partyBInternal = HcsCreditCheckSupport.isInternalCustomer(partyBArchive);
|
||||||
String partyAType;
|
String partyAType;
|
||||||
String partyBType;
|
String partyBType;
|
||||||
|
String partyCType;
|
||||||
if (partyAInternal || partyBInternal) {
|
if (partyAInternal || partyBInternal) {
|
||||||
// 档案命中:内部组织一方=我方签约主体(inner),外部一方统一 customer
|
|
||||||
partyAType = partyAInternal ? HcsConstant.SIGNER_FD_TYPE_INNER : HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyAType = partyAInternal ? HcsConstant.SIGNER_FD_TYPE_INNER : HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
partyBType = partyBInternal ? HcsConstant.SIGNER_FD_TYPE_INNER : HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyBType = partyBInternal ? HcsConstant.SIGNER_FD_TYPE_INNER : HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
|
partyCType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
} else if ("承运商合同".equals(contractManage.getContractCategory())) {
|
} else if ("承运商合同".equals(contractManage.getContractCategory())) {
|
||||||
// 兜底:档案未命中内部组织,按类别推定——我方为付款/发包方,签约主体在甲方
|
|
||||||
partyAType = HcsConstant.SIGNER_FD_TYPE_INNER;
|
partyAType = HcsConstant.SIGNER_FD_TYPE_INNER;
|
||||||
partyBType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyBType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
|
partyCType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
} else if ("客户合同".equals(contractManage.getContractCategory())) {
|
} else if ("客户合同".equals(contractManage.getContractCategory())) {
|
||||||
// 兜底:我方为收款/承运方,签约主体在乙方
|
|
||||||
partyAType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyAType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
partyBType = HcsConstant.SIGNER_FD_TYPE_INNER;
|
partyBType = HcsConstant.SIGNER_FD_TYPE_INNER;
|
||||||
|
partyCType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
} else {
|
} else {
|
||||||
// 兜底:其他类别双方统一 customer(2026-10-14 联调口径)
|
|
||||||
partyAType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyAType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
partyBType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
partyBType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
|
partyCType = HcsConstant.SIGNER_FD_TYPE_CUSTOMER;
|
||||||
|
}
|
||||||
|
for (String partyName : partyANames) {
|
||||||
|
CustomerArchive archive = Objects.equals(partyName, firstPartyA) ? partyAArchive : queryCustomerArchive(partyName);
|
||||||
|
signers.add(signer("甲", partyAType, partyName, archive));
|
||||||
|
}
|
||||||
|
for (String partyName : partyBNames) {
|
||||||
|
CustomerArchive archive = Objects.equals(partyName, firstPartyB) ? partyBArchive : queryCustomerArchive(partyName);
|
||||||
|
signers.add(signer("乙", partyBType, partyName, archive));
|
||||||
|
}
|
||||||
|
for (String partyName : partyCNames) {
|
||||||
|
signers.add(signer("丙", partyCType, partyName, queryCustomerArchive(partyName)));
|
||||||
}
|
}
|
||||||
// 甲方相对方:fdStanding=甲(编号按名称反查,反查不到留空,§9-U6)
|
|
||||||
signers.add(signer("甲", partyAType, contractManage.getPartyA(), partyAArchive));
|
|
||||||
// 乙方相对方:fdStanding=乙
|
|
||||||
signers.add(signer("乙", partyBType, contractManage.getPartyB(), partyBArchive));
|
|
||||||
return signers;
|
return signers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> partyNames(String partyJson, String fallback) {
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
for (Map<String, Object> row : ContractPartySupport.parsePartyRows(partyJson)) {
|
||||||
|
Object customerName = row.get("customerName");
|
||||||
|
if (customerName != null && Func.isNotEmpty(String.valueOf(customerName).trim())) {
|
||||||
|
names.add(String.valueOf(customerName).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (names.isEmpty() && Func.isNotEmpty(fallback)) {
|
||||||
|
for (String part : fallback.split("[、,]")) {
|
||||||
|
if (Func.isNotEmpty(part) && Func.isNotEmpty(part.trim())) {
|
||||||
|
names.add(part.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单个相对方:fdType 由签约主体落位规则决定(我方主体 inner,外部相对方 customer)
|
* 单个相对方:fdType 由签约主体落位规则决定(我方主体 inner,外部相对方 customer)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
-- 合同甲/乙/丙多方客商明细 + 运单/配载承运客商明细定位字段
|
||||||
|
-- utf8mb4_general_ci
|
||||||
|
|
||||||
|
-- 合同管理:多方客商 JSON 与丙方汇总
|
||||||
|
ALTER TABLE `blade_contract_manage`
|
||||||
|
ADD COLUMN `party_a_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '甲方客商明细JSON' AFTER `party_b`,
|
||||||
|
ADD COLUMN `party_b_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '乙方客商明细JSON' AFTER `party_a_json`,
|
||||||
|
ADD COLUMN `party_c_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '丙方客商明细JSON' AFTER `party_b_json`,
|
||||||
|
ADD COLUMN `party_c` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '丙方' AFTER `party_c_json`;
|
||||||
|
|
||||||
|
-- 合同变更:同步多方字段
|
||||||
|
ALTER TABLE `blade_contract_change`
|
||||||
|
ADD COLUMN `party_a_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '甲方客商明细JSON' AFTER `party_b`,
|
||||||
|
ADD COLUMN `party_b_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '乙方客商明细JSON' AFTER `party_a_json`,
|
||||||
|
ADD COLUMN `party_c_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '丙方客商明细JSON' AFTER `party_b_json`,
|
||||||
|
ADD COLUMN `party_c` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '丙方' AFTER `party_c_json`;
|
||||||
|
|
||||||
|
-- 历史数据迁移:原甲/乙方名称生成单行明细(仅名称,后续编辑补全客商ID)
|
||||||
|
UPDATE `blade_contract_manage`
|
||||||
|
SET `party_a_json` = CASE
|
||||||
|
WHEN `party_a` IS NOT NULL AND TRIM(`party_a`) <> '' AND (`party_a_json` IS NULL OR TRIM(`party_a_json`) = '')
|
||||||
|
THEN CONCAT('[{"id":"', REPLACE(UUID(), '-', ''), '","customerName":"', REPLACE(REPLACE(`party_a`, '\\', '\\\\'), '"', '\\"'), '"}]')
|
||||||
|
ELSE `party_a_json`
|
||||||
|
END,
|
||||||
|
`party_b_json` = CASE
|
||||||
|
WHEN `party_b` IS NOT NULL AND TRIM(`party_b`) <> '' AND (`party_b_json` IS NULL OR TRIM(`party_b_json`) = '')
|
||||||
|
THEN CONCAT('[{"id":"', REPLACE(UUID(), '-', ''), '","customerName":"', REPLACE(REPLACE(`party_b`, '\\', '\\\\'), '"', '\\"'), '"}]')
|
||||||
|
ELSE `party_b_json`
|
||||||
|
END
|
||||||
|
WHERE `is_deleted` = 0;
|
||||||
|
|
||||||
|
UPDATE `blade_contract_change`
|
||||||
|
SET `party_a_json` = CASE
|
||||||
|
WHEN `party_a` IS NOT NULL AND TRIM(`party_a`) <> '' AND (`party_a_json` IS NULL OR TRIM(`party_a_json`) = '')
|
||||||
|
THEN CONCAT('[{"id":"', REPLACE(UUID(), '-', ''), '","customerName":"', REPLACE(REPLACE(`party_a`, '\\', '\\\\'), '"', '\\"'), '"}]')
|
||||||
|
ELSE `party_a_json`
|
||||||
|
END,
|
||||||
|
`party_b_json` = CASE
|
||||||
|
WHEN `party_b` IS NOT NULL AND TRIM(`party_b`) <> '' AND (`party_b_json` IS NULL OR TRIM(`party_b_json`) = '')
|
||||||
|
THEN CONCAT('[{"id":"', REPLACE(UUID(), '-', ''), '","customerName":"', REPLACE(REPLACE(`party_b`, '\\', '\\\\'), '"', '\\"'), '"}]')
|
||||||
|
ELSE `party_b_json`
|
||||||
|
END
|
||||||
|
WHERE `is_deleted` = 0;
|
||||||
|
|
||||||
|
-- 运单:承运客商明细定位
|
||||||
|
ALTER TABLE `blade_waybill`
|
||||||
|
ADD COLUMN `carrier_party_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承运客商明细行ID' AFTER `carrier_contract_id`,
|
||||||
|
ADD COLUMN `carrier_party_role` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承运客商角色 B/C' AFTER `carrier_party_id`;
|
||||||
|
|
||||||
|
-- 配载:承运客商明细定位
|
||||||
|
ALTER TABLE `blade_loading_manage`
|
||||||
|
ADD COLUMN `carrier_party_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承运客商明细行ID' AFTER `carrier_contract_id`,
|
||||||
|
ADD COLUMN `carrier_party_role` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承运客商角色 B/C' AFTER `carrier_party_id`;
|
||||||
Reference in New Issue
Block a user