diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java index feecfbb1..5f42a8f2 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java @@ -30,6 +30,8 @@ public class PrcPriceSetDetail implements Serializable private BigDecimal fvValue; private BigDecimal fvRegistrationFee; private BigDecimal discountValue; + /** 取值方式:UP / DOWN / HALF_UP(字典 bms_round_mode) */ + private String roundMode; private Integer prId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createdAt; @@ -62,6 +64,8 @@ public class PrcPriceSetDetail implements Serializable public void setFvRegistrationFee(BigDecimal fvRegistrationFee) { this.fvRegistrationFee = fvRegistrationFee; } public BigDecimal getDiscountValue() { return discountValue; } public void setDiscountValue(BigDecimal discountValue) { this.discountValue = discountValue; } + public String getRoundMode() { return roundMode; } + public void setRoundMode(String roundMode) { this.roundMode = roundMode; } public Integer getPrId() { return prId; } public void setPrId(Integer prId) { this.prId = prId; } public Date getCreatedAt() { return createdAt; } diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsPriceQuoteResult.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsPriceQuoteResult.java index 3617cff4..8b0ad24f 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsPriceQuoteResult.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsPriceQuoteResult.java @@ -49,6 +49,8 @@ public class TmsPriceQuoteResult implements Serializable /** freightAmount + registrationFee */ private BigDecimal payableTotal; private String currency = "RMB"; + /** ????? UP / DOWN / HALF_UP */ + private String roundMode; public String getMatchSource() { return matchSource; } public void setMatchSource(String matchSource) { this.matchSource = matchSource; } @@ -102,4 +104,6 @@ public class TmsPriceQuoteResult implements Serializable public void setPayableTotal(BigDecimal payableTotal) { this.payableTotal = payableTotal; } public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } + public String getRoundMode() { return roundMode; } + public void setRoundMode(String roundMode) { this.roundMode = roundMode; } } diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java index 927a1e47..ed0a6736 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java @@ -248,9 +248,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService nd.setZdId(d.getZdId()); nd.setWsdId(d.getWsdId()); nd.setWeightLabel(d.getWeightLabel()); + nd.setProductCountryId(d.getProductCountryId()); nd.setFvValue(d.getFvValue()); nd.setFvRegistrationFee(d.getFvRegistrationFee()); nd.setDiscountValue(d.getDiscountValue()); + nd.setRoundMode(d.getRoundMode()); nd.setPrId(d.getPrId()); details.add(nd); } @@ -628,6 +630,19 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } fillProductCountryId(detail); } + if (StringUtils.isEmpty(detail.getRoundMode())) + { + detail.setRoundMode("HALF_UP"); + } + else + { + String mode = detail.getRoundMode().trim().toUpperCase(); + if (!"UP".equals(mode) && !"DOWN".equals(mode) && !"HALF_UP".equals(mode)) + { + throw new ServiceException("取值方式仅支持 UP / DOWN / HALF_UP"); + } + detail.setRoundMode(mode); + } if (StringUtils.isNotEmpty(detail.getCountryCode())) { detail.setCountryCode(detail.getCountryCode().toUpperCase()); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java index 9f20a374..8701520c 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -119,7 +119,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService if (detailCount == 0) { TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null, - base.getRegistrationFee(), base, productCode, productName, weight); + base.getRegistrationFee(), base, productCode, productName, weight, null); fillPackage(r, bind, set); r.setMatchMessage("FIXED package has no details; use bms_product_country default"); r.setCustomerId(bind.getCustomerId()); @@ -139,7 +139,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService return null; } TmsPriceQuoteResult r = buildFromRates(SRC_FIXED, detail.getFvValue(), null, null, - detail.getFvRegistrationFee(), base, productCode, productName, weight); + detail.getFvRegistrationFee(), base, productCode, productName, weight, detail.getRoundMode()); fillPackage(r, bind, set); if (StringUtils.isNotEmpty(detail.getProductName())) { @@ -163,6 +163,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { int detailCount = detailMapper.countByPriceSetId(set.getPriceSetId()); BigDecimal discount = set.getDefaultDiscount(); + String roundMode = null; if (detailCount > 0) { PrcPriceSetDetail detail = detailMapper.selectDiscountMatch(set.getPriceSetId(), productCode); @@ -174,6 +175,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { discount = detail.getDiscountValue(); } + roundMode = detail.getRoundMode(); } if (discount == null || discount.compareTo(BigDecimal.ONE) < 0 @@ -186,10 +188,11 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight rate is null"); } BigDecimal baseRate = base.getFreightPrice(); + RoundingMode rm = resolveRoundingMode(roundMode); BigDecimal rate = baseRate.multiply(discount) - .divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP); + .divide(new BigDecimal("100"), 4, rm); TmsPriceQuoteResult r = buildFromRates(SRC_DISCOUNT, rate, baseRate, discount, - base.getRegistrationFee(), base, productCode, productName, weight); + base.getRegistrationFee(), base, productCode, productName, weight, roundMode); fillPackage(r, bind, set); r.setMatchMessage("hit customer DISCOUNT package; freight = default * discount%"); r.setCustomerId(bind.getCustomerId()); @@ -204,7 +207,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight or registration is null"); } TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null, - base.getRegistrationFee(), base, productCode, productName, weight); + base.getRegistrationFee(), base, productCode, productName, weight, null); r.setCustomerId(customerId); r.setMatchMessage(message); return r; @@ -212,7 +215,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService private TmsPriceQuoteResult buildFromRates(String source, BigDecimal freightRate, BigDecimal baseFreightRate, BigDecimal discountPercent, BigDecimal registrationFee, - BmsProductCountry base, String productCode, String productName, BigDecimal weight) + BmsProductCountry base, String productCode, String productName, BigDecimal weight, + String roundMode) { if (freightRate == null) { @@ -222,8 +226,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { registrationFee = BigDecimal.ZERO; } - BigDecimal freightAmount = freightRate.multiply(weight).setScale(2, RoundingMode.HALF_UP); - BigDecimal payable = freightAmount.add(registrationFee).setScale(2, RoundingMode.HALF_UP); + RoundingMode rm = resolveRoundingMode(roundMode); + String mode = normalizeRoundMode(roundMode); + BigDecimal freightAmount = freightRate.multiply(weight).setScale(2, rm); + BigDecimal payable = freightAmount.add(registrationFee).setScale(2, rm); TmsPriceQuoteResult r = new TmsPriceQuoteResult(); r.setMatchSource(source); @@ -244,9 +250,38 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService r.setFreightAmount(freightAmount); r.setPayableTotal(payable); r.setCurrency("RMB"); + r.setRoundMode(mode); return r; } + private static String normalizeRoundMode(String roundMode) + { + if (StringUtils.isEmpty(roundMode)) + { + return "HALF_UP"; + } + String mode = roundMode.trim().toUpperCase(); + if ("UP".equals(mode) || "DOWN".equals(mode) || "HALF_UP".equals(mode)) + { + return mode; + } + return "HALF_UP"; + } + + private static RoundingMode resolveRoundingMode(String roundMode) + { + String mode = normalizeRoundMode(roundMode); + if ("UP".equals(mode)) + { + return RoundingMode.UP; + } + if ("DOWN".equals(mode)) + { + return RoundingMode.DOWN; + } + return RoundingMode.HALF_UP; + } + private void fillPackage(TmsPriceQuoteResult r, PrcCustomerPriceSet bind, PrcPriceSet set) { r.setPriceSetId(set.getPriceSetId()); diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml index 60a89e14..4b845448 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml @@ -18,6 +18,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -26,7 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select psd_id, price_set_id, product_code, product_name, country_code, country_name, zd_id, wsd_id, weight_label, product_country_id, fv_value, fv_registration_fee, - discount_value, pr_id, created_at, updated_at + discount_value, round_mode, pr_id, created_at, updated_at from prc_price_set_detail @@ -73,6 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" fv_value, fv_registration_fee, discount_value, + round_mode, pr_id, created_at ) values ( @@ -88,6 +90,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{fvValue}, #{fvRegistrationFee}, #{discountValue}, + #{roundMode}, #{prId}, sysdate() ) @@ -107,6 +110,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" fv_value = #{fvValue}, fv_registration_fee = #{fvRegistrationFee}, discount_value = #{discountValue}, + round_mode = #{roundMode}, pr_id = #{prId}, updated_at = sysdate() diff --git a/bms-back/sql/price/price_set.sql b/bms-back/sql/price/price_set.sql index fa732e85..9b970e7a 100644 --- a/bms-back/sql/price/price_set.sql +++ b/bms-back/sql/price/price_set.sql @@ -47,6 +47,7 @@ CREATE TABLE IF NOT EXISTS prc_price_set_detail ( fv_value DECIMAL(10,4) DEFAULT NULL COMMENT '运费单价 RMB/KG', fv_registration_fee DECIMAL(10,2) DEFAULT NULL COMMENT '挂号费 RMB/票', discount_value DECIMAL(5,2) DEFAULT NULL COMMENT '明细折扣百分数,DISCOUNT 可覆盖默认', + round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)', pr_id INT(11) DEFAULT NULL COMMENT '关联价格规则id', created_at DATETIME DEFAULT NULL COMMENT '创建时间', updated_at DATETIME DEFAULT NULL COMMENT '更新时间', diff --git a/bms-back/sql/price/price_set_detail_round_mode_alter.sql b/bms-back/sql/price/price_set_detail_round_mode_alter.sql new file mode 100644 index 00000000..3cb34d1e --- /dev/null +++ b/bms-back/sql/price/price_set_detail_round_mode_alter.sql @@ -0,0 +1,6 @@ +-- Add round_mode to price set detail +-- dict: bms_round_mode (UP / DOWN / HALF_UP) +SET NAMES utf8mb4; + +ALTER TABLE prc_price_set_detail + ADD COLUMN round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT 'round mode (dict bms_round_mode)' AFTER discount_value; diff --git a/bms-front/src/components/BasicTable/index.vue b/bms-front/src/components/BasicTable/index.vue index c96c02c0..1a1d849a 100644 --- a/bms-front/src/components/BasicTable/index.vue +++ b/bms-front/src/components/BasicTable/index.vue @@ -46,6 +46,7 @@ :height="height" :max-height="maxHeight" :default-sort="defaultSort" + :span-method="spanMethod" @selection-change="handleSelectionChange" @row-click="(row, col, e) => $emit('row-click', row, col, e)" @sort-change="(c) => $emit('sort-change', c)" @@ -241,6 +242,11 @@ const props = defineProps({ type: Object, default: undefined }, + // 单元格合并方法(透传 el-table span-method),签名同 el-table:({ row, column, rowIndex, columnIndex }) => { rowspan, colspan } + spanMethod: { + type: Function, + default: null + }, // 每页条数可选项 pageSizes: { type: Array, @@ -425,7 +431,10 @@ defineExpose({ getTableRef, getSearchFormRef, getQueryParams, - setQueryParams + setQueryParams, + getData: () => tableData.value, + /** 当前页数据(响应式,父组件 span-method 可直接订阅) */ + data: tableData }) if (props.autoLoad) { diff --git a/bms-front/src/components/ExcelImportDialog/index.vue b/bms-front/src/components/ExcelImportDialog/index.vue index 3495f51e..e3c6c92d 100644 --- a/bms-front/src/components/ExcelImportDialog/index.vue +++ b/bms-front/src/components/ExcelImportDialog/index.vue @@ -1,22 +1,69 @@