diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java index 86bbc07f..8f4c3238 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcOrderBillServiceImpl.java @@ -207,21 +207,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService assertOrderBillEditable(request.getOrderNo()); PrcOrderBill existed = orderBillMapper.selectPrcOrderBillByOrderNo(request.getOrderNo()); BigDecimal systemWeight = request.getWeight(); - // Upstream Oracle sync: always use TAB_ORDER weight (same overwrite as weight/charge_weight). - // Local rebill / manual charge_weight edit: keep resolveCalcWeight preference. - BigDecimal calcWeight = isUpstreamSyncRequest(request) - ? systemWeight - : resolveCalcWeight(existed, systemWeight); - // Three-side sum rule disabled: upstream already applies it. - // Manual charge_weight wins; otherwise apply active biz rules (e.g. three-side). - // if (!isManualChargeWeight(existed)) - // { - // BigDecimal ruled = bizRuleService.resolveWeightByRule(request, systemWeight); - // if (ruled != null) - // { - // calcWeight = ruled; - // } - // } + BigDecimal calcWeight = resolveBillCalcWeight(request, existed, systemWeight); request.setWeight(calcWeight); TmsPriceQuoteResult result = tmsPriceQuoteService.calculate(request); persistBill(request, systemWeight, bizDate, result); @@ -1836,8 +1822,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService { bill.setCountryName(result.getCountryName()); } - // weight = system/original; charge_weight follows weight on upstream sync, - // otherwise may keep manual override via resolvePersistChargeWeight. + // weight = system/original upstream weight; charge_weight may differ when VOL_8000 + // (or manual override) raised/lowered the billed weight. bill.setWeight(weight); bill.setBizDate(bizDate); bill.setTmsId(request.getTmsId() == null ? null : Long.valueOf(request.getTmsId())); @@ -1857,7 +1843,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService bill.setEffectiveDate(result.getEffectiveDate()); if (isUpstreamSyncRequest(request)) { - bill.setChargeWeight(weight); + // request.weight already carries ruled/calc weight used for quote + BigDecimal cw = request.getWeight() != null ? request.getWeight() : weight; + bill.setChargeWeight(cw); } else { @@ -2883,6 +2871,51 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService return systemWeight; } + /** + * Resolve weight used for quote/persist. + * Upstream: TAB_ORDER weight then biz-rule. Local: manual charge_weight wins, else biz-rule. + */ + private BigDecimal resolveBillCalcWeight(TmsPriceBillRequest request, PrcOrderBill existed, + BigDecimal systemWeight) + { + BigDecimal calcWeight = isUpstreamSyncRequest(request) + ? systemWeight + : resolveCalcWeight(existed, systemWeight); + if (isUpstreamSyncRequest(request) || !isManualChargeWeight(existed)) + { + calcWeight = applyBizRuleWeight(request, calcWeight); + } + return calcWeight; + } + + /** + * Upstream sync / soft-fail: keep original weight, charge_weight may follow biz-rule. + */ + private void applyUpstreamWeightFields(PrcOrderBill bill, TmsPriceBillRequest request) + { + if (bill == null || request == null || request.getWeight() == null) + { + return; + } + BigDecimal systemWeight = request.getWeight(); + bill.setWeight(systemWeight); + bill.setChargeWeight(applyBizRuleWeight(request, systemWeight)); + } + + /** + * Biz-rule charge weight (VOL_8000 etc). + * To disable: comment out the resolveWeightByRule call and always return baseWeight. + */ + private BigDecimal applyBizRuleWeight(TmsPriceBillRequest request, BigDecimal baseWeight) + { + if (request == null || baseWeight == null) + { + return baseWeight; + } + BigDecimal ruled = bizRuleService.resolveWeightByRule(request, baseWeight); + return ruled != null ? ruled : baseWeight; + } + /** * Persist charge_weight: keep manual override when calc weight != system weight, * or when existing bill already had a manual override. @@ -3077,12 +3110,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService applyProductCodeMapping(bill, request); Long customerId = resolveCustomerIdByCode(request.getCustomerCode(), bill.getCustomerId()); bill.setCustomerId(customerId == null ? Long.valueOf(0L) : customerId); - // Order-source sync: overwrite weight and charge_weight the same way from upstream. - if (request.getWeight() != null) - { - bill.setWeight(request.getWeight()); - bill.setChargeWeight(request.getWeight()); - } + applyUpstreamWeightFields(bill, request); if (creating) { Date bizDate = request.getPickupDate() != null ? request.getPickupDate() : new Date(); @@ -3194,12 +3222,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService { bill.setCustomerActiveStatus(BillCustomerActiveStatus.ACTIVE.getCode()); } - // Soft-fail snapshot: overwrite weight and charge_weight the same way from upstream. - bill.setWeight(request.getWeight()); - if (request.getWeight() != null) - { - bill.setChargeWeight(request.getWeight()); - } + applyUpstreamWeightFields(bill, request); Date bizDate = StringUtils.isEmpty(request.getBizDate()) ? new Date() : resolveBizDate(request.getBizDate()); bill.setBizDate(bizDate); if (StringUtils.isEmpty(bill.getCurrency())) @@ -3267,11 +3290,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService { applyUpstreamBillHeaderFields(bill, request); applyProductCodeMapping(bill, request); - if (request.getWeight() != null) - { - bill.setWeight(request.getWeight()); - bill.setChargeWeight(request.getWeight()); - } + applyUpstreamWeightFields(bill, request); } applyBillingFailText(bill, billingFailStatus, errorMessage); orderBillMapper.updatePrcOrderBill(bill); diff --git a/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java b/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java index f844905c..06daa38d 100644 --- a/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java +++ b/bms-back/bms-system/src/main/java/com/bms/system/service/IBmsBizRuleService.java @@ -21,8 +21,8 @@ public interface IBmsBizRuleService int deleteByIds(Long[] ruleIds); /** - * Apply active weight rules to bill request. - * @return overridden weight when a rule matches; null when no rule applies + * Apply active weight rules (currently VOL_8000) to bill request. + * @return overridden charge weight when a rule matches; null when no rule applies */ BigDecimal resolveWeightByRule(TmsPriceBillRequest request, BigDecimal systemWeight); } diff --git a/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java index 95e77f08..f7fe7b47 100644 --- a/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/system/service/impl/BmsBizRuleServiceImpl.java @@ -1,6 +1,7 @@ package com.bms.system.service.impl; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; @@ -12,6 +13,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bms.common.exception.ServiceException; import com.bms.common.utils.StringUtils; +import com.bms.customer.customerinfo.domain.BmsCustomer; +import com.bms.customer.customerinfo.mapper.BmsCustomerMapper; import com.bms.price.domain.vo.TmsPriceBillRequest; import com.bms.system.domain.BmsBizRule; import com.bms.system.mapper.BmsBizRuleMapper; @@ -22,8 +25,12 @@ import com.bms.system.service.IBmsProductCodeMappingService; public class BmsBizRuleServiceImpl implements IBmsBizRuleService { public static final String RULE_TYPE_THREE_SIDE = "THREE_SIDE"; + /** Charge weight = max(L*W*H/8000, weightSource). */ + public static final String RULE_TYPE_VOL_8000 = "VOL_8000"; public static final String WEIGHT_SOURCE_RECEIVE = "RECEIVE_WEIGHT"; public static final String WEIGHT_SOURCE_CHARGE = "CHARGE_WEIGHT"; + public static final String WEIGHT_SOURCE_VOLUME = "VOLUME_WEIGHT"; + private static final BigDecimal VOL_DIVISOR_8000 = new BigDecimal("8000"); @Autowired private BmsBizRuleMapper bizRuleMapper; @@ -31,6 +38,9 @@ public class BmsBizRuleServiceImpl implements IBmsBizRuleService @Autowired private IBmsProductCodeMappingService productCodeMappingService; + @Autowired + private BmsCustomerMapper bmsCustomerMapper; + @Override public List selectList(BmsBizRule query) { @@ -126,21 +136,88 @@ public class BmsBizRuleServiceImpl implements IBmsBizRuleService { return null; } - List rules = bizRuleMapper.selectActiveByType(RULE_TYPE_THREE_SIDE); + // VOL_8000 is applied on local ingest / bill. THREE_SIDE stays config-only + // (upstream already applies that product rule). + BigDecimal vol8000 = resolveVol8000(request, systemWeight); + if (vol8000 != null) + { + return vol8000; + } + return null; + } + + /** + * VOL_8000: chargeWeight = max(L*W*H/8000, weightSource). + * weightSource RECEIVE_WEIGHT / CHARGE_WEIGHT / VOLUME_WEIGHT. + */ + private BigDecimal resolveVol8000(TmsPriceBillRequest request, BigDecimal systemWeight) + { + List rules = bizRuleMapper.selectActiveByType(RULE_TYPE_VOL_8000); if (rules == null || rules.isEmpty()) { return null; } + BigDecimal dimVol = calcVolumeWeight8000(request); + if (dimVol == null) + { + return null; + } for (BmsBizRule rule : rules) { - if (matchThreeSide(rule, request, systemWeight)) + if (!matchVol8000(rule, request, systemWeight)) { - return pickWeight(rule.getWeightSource(), request, systemWeight); + continue; } + BigDecimal other = pickWeight(rule.getWeightSource(), request, systemWeight); + if (other == null) + { + return dimVol; + } + return other.max(dimVol).setScale(3, RoundingMode.HALF_UP); } return null; } + private boolean matchVol8000(BmsBizRule rule, TmsPriceBillRequest request, BigDecimal systemWeight) + { + if (rule == null) + { + return false; + } + if (!dimMatch(request.getProductCode(), rule.getProductCode(), true)) + { + return false; + } + if (!dimMatch(request.getCountryCode(), rule.getCountryCode(), false)) + { + return false; + } + if (!matchCustomerDim(request.getCustomerCode(), rule.getCustomerCode())) + { + return false; + } + BigDecimal checkWeight = firstPositive(request.getReceiveWeight(), request.getActualWeight(), + systemWeight, request.getWeight(), request.getVolumeWeight()); + return inWeightRange(checkWeight, rule.getWeightMin(), rule.getWeightMax()); + } + + private static BigDecimal calcVolumeWeight8000(TmsPriceBillRequest request) + { + if (request == null) + { + return null; + } + BigDecimal l = nzDim(request.getPkgLength()); + BigDecimal w = nzDim(request.getPkgWidth()); + BigDecimal h = nzDim(request.getPkgHeight()); + if (l == null || w == null || h == null) + { + return null; + } + return l.multiply(w).multiply(h) + .divide(VOL_DIVISOR_8000, 3, RoundingMode.HALF_UP); + } + private void assertUnique(BmsBizRule row, Long excludeId) { int cnt = bizRuleMapper.countByDim(row.getRuleType(), row.getProductCode(), row.getCountryCode(), @@ -210,7 +287,7 @@ public class BmsBizRuleServiceImpl implements IBmsBizRuleService { return false; } - if (!dimMatch(request.getCustomerCode(), rule.getCustomerCode(), false)) + if (!matchCustomerDim(request.getCustomerCode(), rule.getCustomerCode())) { return false; } @@ -254,17 +331,109 @@ public class BmsBizRuleServiceImpl implements IBmsBizRuleService return false; } + /** + * Customer dim: rule may store customer_code or customer_code_dw (对外代码). + * Request may carry either form from upstream / local bill. + */ + private boolean matchCustomerDim(String requestCode, String ruleCode) + { + if (StringUtils.isEmpty(ruleCode)) + { + return true; + } + if (StringUtils.isEmpty(requestCode)) + { + return false; + } + String req = normalizeCode(requestCode); + String rule = normalizeCode(ruleCode); + if (req == null || rule == null) + { + return false; + } + if (req.equals(rule)) + { + return true; + } + BmsCustomer byReq = findCustomerByCodeOrDw(req); + if (byReq != null && customerHasCode(byReq, rule)) + { + return true; + } + BmsCustomer byRule = findCustomerByCodeOrDw(rule); + return byRule != null && customerHasCode(byRule, req); + } + + private static boolean customerHasCode(BmsCustomer customer, String code) + { + if (customer == null || code == null) + { + return false; + } + String c = normalizeCode(customer.getCustomerCode()); + String dw = normalizeCode(customer.getCustomerCodeDw()); + return code.equals(c) || code.equals(dw); + } + + private BmsCustomer findCustomerByCodeOrDw(String code) + { + if (code == null || bmsCustomerMapper == null) + { + return null; + } + try + { + BmsCustomer q = new BmsCustomer(); + q.setCustomerCode(code); + List list = bmsCustomerMapper.selectBmsCustomerList(q); + BmsCustomer hit = firstCustomerWithCode(list, code); + if (hit != null) + { + return hit; + } + q = new BmsCustomer(); + q.setCustomerCodeDw(code); + list = bmsCustomerMapper.selectBmsCustomerList(q); + return firstCustomerWithCode(list, code); + } + catch (RuntimeException e) + { + return null; + } + } + + private static BmsCustomer firstCustomerWithCode(List list, String code) + { + if (list == null || list.isEmpty() || code == null) + { + return null; + } + for (BmsCustomer row : list) + { + if (customerHasCode(row, code)) + { + return row; + } + } + return null; + } + private static BigDecimal pickWeight(String weightSource, TmsPriceBillRequest request, BigDecimal systemWeight) { if (WEIGHT_SOURCE_RECEIVE.equalsIgnoreCase(weightSource)) { - BigDecimal rw = firstPositive(request.getReceiveWeight(), request.getActualWeight()); + // Prefer actual (实重), then receive weight. + BigDecimal rw = firstPositive(request.getActualWeight(), request.getReceiveWeight()); return rw != null ? rw : systemWeight; } if (WEIGHT_SOURCE_CHARGE.equalsIgnoreCase(weightSource)) { return systemWeight != null ? systemWeight : request.getWeight(); } + if (WEIGHT_SOURCE_VOLUME.equalsIgnoreCase(weightSource)) + { + return firstPositive(request.getVolumeWeight()); + } return null; } @@ -372,12 +541,12 @@ public class BmsBizRuleServiceImpl implements IBmsBizRuleService } if (RULE_TYPE_THREE_SIDE.equalsIgnoreCase(row.getRuleType()) && row.getSideSumMax() == null) { - throw new ServiceException("三边规则必须填写三边和上限"); + throw new ServiceException("\u4e09\u8fb9\u89c4\u5219\u5fc5\u987b\u586b\u5199\u4e09\u8fb9\u548c\u4e0a\u9650"); } if (row.getWeightMin() != null && row.getWeightMax() != null && row.getWeightMin().compareTo(row.getWeightMax()) > 0) { - throw new ServiceException("重量段最小值不能大于最大值"); + throw new ServiceException("\u91cd\u91cf\u6bb5\u6700\u5c0f\u503c\u4e0d\u80fd\u5927\u4e8e\u6700\u5927\u503c"); } } } diff --git a/bms-back/sql/data/biz_rule_vol_8000.sql b/bms-back/sql/data/biz_rule_vol_8000.sql new file mode 100644 index 00000000..6ce35bb0 --- /dev/null +++ b/bms-back/sql/data/biz_rule_vol_8000.sql @@ -0,0 +1,26 @@ +-- VOL_8000: charge_weight = max(L*W*H/8000, weight_source) +-- customer_code column stores 对外代码 customer_code_dw (C00222/C00562/C00521/C00805) +SET NAMES utf8mb4; + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 2, N'材积重8000取大', 'VOL_8000', 'bms_biz_rule_type', '', 'success', 'N', '0', 'admin', sysdate(), + N'计费重=max(长×宽×高/8000, 取值字段)' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'bms_biz_rule_type' AND dict_value = 'VOL_8000'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +SELECT 3, N'材积重', 'VOLUME_WEIGHT', 'bms_biz_weight_source', '', 'info', 'N', '0', 'admin', sysdate(), + N'订单材积重 volume_weight' +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'bms_biz_weight_source' AND dict_value = 'VOLUME_WEIGHT'); + +-- Seed by 对外代码 (customer_code_dw). Matcher also accepts customer_code. +INSERT INTO biz_rule (rule_name, rule_type, product_code, country_code, customer_code, weight_min, weight_max, weight_source, side_sum_max, status, remark, create_by, create_time, del_flag) +SELECT CONCAT(N'对外代码', v.customer_code, N' 材积重8000取大'), 'VOL_8000', NULL, NULL, v.customer_code, NULL, NULL, 'RECEIVE_WEIGHT', NULL, '0', + N'客户维度为对外代码customer_code_dw;计费重=max(长×宽×高/8000, 实重)', 'admin', sysdate(), '0' +FROM ( + SELECT 'C00222' AS customer_code UNION ALL SELECT 'C00562' UNION ALL SELECT 'C00521' UNION ALL SELECT 'C00805' +) v +WHERE NOT EXISTS ( + SELECT 1 FROM biz_rule r + WHERE r.del_flag = '0' AND r.rule_type = 'VOL_8000' AND r.customer_code = v.customer_code + AND r.product_code IS NULL AND r.country_code IS NULL +); diff --git a/bms-front/src/views/data/rule/config/index.vue b/bms-front/src/views/data/rule/config/index.vue index 50a87e4f..49f19f92 100644 --- a/bms-front/src/views/data/rule/config/index.vue +++ b/bms-front/src/views/data/rule/config/index.vue @@ -73,14 +73,14 @@ /> - + @@ -104,6 +104,14 @@ + 正常