提交试算落库
This commit is contained in:
@@ -36,3 +36,74 @@ no.update.permission=您没有修改数据的权限,请联系管理员添加
|
|||||||
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
||||||
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
||||||
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
||||||
|
|
||||||
|
##价格模块提示语(price)
|
||||||
|
# 附加费 PrcSurcharge
|
||||||
|
price.surcharge.scName.empty=附加费名称不能为空
|
||||||
|
price.surcharge.chargeType.empty=计费方式不能为空
|
||||||
|
price.surcharge.chargeValue.empty=计费数值不能为空
|
||||||
|
price.surcharge.chargeValue.negative=计费数值不能为负数
|
||||||
|
# 附加费关联 PrcSurchargeRel
|
||||||
|
price.surchargeRel.scId.empty=附加费ID不能为空
|
||||||
|
price.surchargeRel.productId.empty=产品ID不能为空
|
||||||
|
price.surchargeRel.productCode.empty=产品代码不能为空
|
||||||
|
# 套餐 PrcPriceSet
|
||||||
|
price.set.notExists=套餐不存在
|
||||||
|
price.set.name.empty=套餐名称不能为空
|
||||||
|
price.set.name.exists=套餐名称已存在
|
||||||
|
price.set.effectiveDate.empty=生效日期不能为空
|
||||||
|
price.set.date.invalid=生效日期不能晚于失效日期
|
||||||
|
price.set.discount.range=折扣模式默认折扣需在 1~99
|
||||||
|
price.set.delete.statusNotAllowed=仅草稿或驳回状态的套餐允许删除:{0}
|
||||||
|
price.set.delete.boundCustomer=套餐【{0}】已绑定客户,请先解绑后再删除
|
||||||
|
price.set.submit.statusNotAllowed=当前状态不允许提交审核
|
||||||
|
price.set.approve.statusNotAllowed=仅审核中的套餐可以生效
|
||||||
|
price.set.reject.statusNotAllowed=仅审核中的套餐可以驳回
|
||||||
|
price.set.disable.statusNotAllowed=仅生效中的套餐可以停用
|
||||||
|
price.set.edit.audit=审核中的套餐不允许修改
|
||||||
|
price.set.edit.stopped=已停用的套餐不允许修改,请使用复用新建
|
||||||
|
price.set.childEdit.audit=审核中的套餐不允许改明细或客户
|
||||||
|
price.set.childEdit.stopped=已停用的套餐不允许改明细或客户
|
||||||
|
price.set.detail.notExists=套餐明细不存在
|
||||||
|
price.set.detail.productCode.empty=明细产品代码不能为空
|
||||||
|
price.set.detail.country.empty=固定价明细必须填写国家
|
||||||
|
price.set.detail.weight.empty=固定价明细必须填写重量段
|
||||||
|
price.set.detail.freight.empty=固定价明细必须填写运费单价
|
||||||
|
price.set.detail.registration.empty=固定价明细必须填写挂号费
|
||||||
|
price.set.detail.roundMode.invalid=取值方式仅支持 UP / DOWN / HALF_UP
|
||||||
|
price.set.customer.notExists=客户绑定不存在
|
||||||
|
price.set.customer.unbindAudit=审核中的套餐不允许解绑客户
|
||||||
|
price.set.customer.customerId.empty=客户ID不能为空
|
||||||
|
price.set.customer.beginDate.empty=客户绑定生效日期不能为空
|
||||||
|
price.set.customer.date.invalid=客户绑定生效日期不能晚于失效日期
|
||||||
|
price.set.customer.priority.invalid=优先级必须大于等于 1
|
||||||
|
# 重量段 PrcWeight
|
||||||
|
price.weight.range.empty=起始重量和结束重量不能为空
|
||||||
|
price.weight.range.invalid=起始重量必须小于结束重量
|
||||||
|
# 分区 XtdZone
|
||||||
|
price.zone.delete.hasDivision=分区方案【{0}】存在区划明细,不允许删除
|
||||||
|
# 产品导入 BmsProduct
|
||||||
|
price.product.import.fileEmpty=上传文件不能为空
|
||||||
|
price.product.import.fileType=仅支持 xls / xlsx 文件
|
||||||
|
price.product.import.sheetNotFound=未找到「产品目录」sheet,请上传威狮B2C小包价表格格式文件
|
||||||
|
price.product.import.noData=产品目录无有效数据行
|
||||||
|
price.product.import.regionEmpty=bms_region 无可用国家数据,请先同步/维护国家表后再导入
|
||||||
|
price.product.import.partialFail=导入完成但存在失败项:新增 {0},更新 {1},跳过 {2};失败明细:{3}
|
||||||
|
price.product.import.effTooEarly=生效日期 {0} 早于当前开放版 {1},请使用更晚的生效日期导入
|
||||||
|
price.product.import.needFeeAlter=请先执行 SQL:bms-back/sql/price/product_country_fee_alter.sql(缺少运费/挂号费字段)
|
||||||
|
price.product.import.needExpireAlter=请先执行 SQL:bms-back/sql/price/product_country_expire_alter.sql(缺少失效日期/版本唯一键)
|
||||||
|
price.product.import.success=导入成功:新增 {0} 个产品,更新 {1} 个,跳过 {2} 个;写入国家/重量段明细 {3} 行。
|
||||||
|
price.product.import.success.detail1=国家明细:若新生效日晚于上次,保留历史版且上次失效日=本次生效日;同日则替换当前开放版。
|
||||||
|
price.product.import.success.detail2=范围:产品目录 + 重量段 + 运费/挂号费 + 尺寸/尾程/时效;国家码来自 bms_region。
|
||||||
|
price.product.import.success.unmatched=未匹配国家表(country_code 为空):{0}
|
||||||
|
# 报价试算 TmsPriceQuote(对外 TMS API 错误码,保持英文契约)
|
||||||
|
price.quote.priceNotHit=PRICE_NOT_HIT: no bms_product_country band for product/country/weight
|
||||||
|
price.quote.freightMissing1=FREIGHT_VALUE_MISSING: product default freight rate is null
|
||||||
|
price.quote.freightMissing2=FREIGHT_VALUE_MISSING: product default freight or registration is null
|
||||||
|
price.quote.freightMissing3=FREIGHT_VALUE_MISSING: freight rate is null
|
||||||
|
price.quote.requestBodyRequired=request body is required
|
||||||
|
price.quote.customerIdRequired=customerId is required
|
||||||
|
price.quote.productCodeRequired=productCode is required
|
||||||
|
price.quote.countryRequired=countryCode or countryName is required
|
||||||
|
price.quote.weightInvalid=weight must be > 0
|
||||||
|
price.quote.bizDateInvalid=bizDate format must be yyyy-MM-dd
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.ruoyi.price.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/** billing quote header prc_quote */
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class PrcQuote extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long quoteId;
|
||||||
|
private String quoteKey;
|
||||||
|
private Long customerId;
|
||||||
|
private String productCode;
|
||||||
|
private String productName;
|
||||||
|
private String countryCode;
|
||||||
|
private String countryName;
|
||||||
|
private BigDecimal weight;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date bizDate;
|
||||||
|
private Long tmsId;
|
||||||
|
|
||||||
|
private String matchSource;
|
||||||
|
private String matchMessage;
|
||||||
|
private Long priceSetId;
|
||||||
|
private String priceSetCode;
|
||||||
|
private String priceSetName;
|
||||||
|
private String setType;
|
||||||
|
private Integer bindPriority;
|
||||||
|
private Long customerPricesetId;
|
||||||
|
private Long productCountryId;
|
||||||
|
private BigDecimal weightMin;
|
||||||
|
private BigDecimal weightMax;
|
||||||
|
private String weightLabel;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date effectiveDate;
|
||||||
|
private BigDecimal chargeWeight;
|
||||||
|
private BigDecimal freightRate;
|
||||||
|
private BigDecimal baseFreightRate;
|
||||||
|
private BigDecimal discountPercent;
|
||||||
|
private BigDecimal registrationFee;
|
||||||
|
private BigDecimal freightAmount;
|
||||||
|
private BigDecimal surchargeAmount;
|
||||||
|
private BigDecimal payableTotal;
|
||||||
|
private String currency;
|
||||||
|
private String roundMode;
|
||||||
|
|
||||||
|
private List<PrcQuoteFee> fees;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ruoyi.price.domain;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** billing quote fee detail prc_quote_fee, one row per bms_fee_type */
|
||||||
|
@Data
|
||||||
|
public class PrcQuoteFee implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long feeId;
|
||||||
|
private Long quoteId;
|
||||||
|
/** bms_fee_type: BASIC_FREIGHT / REGISTRATION_FEE / SURCHARGE */
|
||||||
|
private String feeType;
|
||||||
|
private String feeName;
|
||||||
|
/** prc_surcharge.sc_id, 0 for freight/registration */
|
||||||
|
private Long scId;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private String currency;
|
||||||
|
private String roundMode;
|
||||||
|
private String remark;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createdAt;
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ruoyi.price.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** quote fee line keyed by bms_fee_type */
|
||||||
|
@Data
|
||||||
|
public class TmsPriceQuoteFeeLine implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String feeType;
|
||||||
|
private String feeName;
|
||||||
|
/** surcharge config id, 0 for freight/registration */
|
||||||
|
private Long scId;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private String currency;
|
||||||
|
private String roundMode;
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.ruoyi.price.domain.vo;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,8 +44,15 @@ public class TmsPriceQuoteResult implements Serializable
|
|||||||
private BigDecimal discountPercent;
|
private BigDecimal discountPercent;
|
||||||
private BigDecimal registrationFee;
|
private BigDecimal registrationFee;
|
||||||
private BigDecimal freightAmount;
|
private BigDecimal freightAmount;
|
||||||
|
/** 附加费合计(按产品关联命中后汇总) */
|
||||||
|
private BigDecimal surchargeAmount;
|
||||||
private BigDecimal payableTotal;
|
private BigDecimal payableTotal;
|
||||||
private String currency = "RMB";
|
private String currency = "RMB";
|
||||||
/** UP / DOWN / HALF_UP */
|
/** UP / DOWN / HALF_UP */
|
||||||
private String roundMode;
|
private String roundMode;
|
||||||
|
/** 费用明细:运费/挂号费各一行,附加费按具体名称各一行 */
|
||||||
|
private List<TmsPriceQuoteFeeLine> feeDetails;
|
||||||
|
private Long quoteId;
|
||||||
|
/** true=命中已落库试算 */
|
||||||
|
private Boolean fromCache;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.ruoyi.price.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import com.ruoyi.price.domain.PrcQuoteFee;
|
||||||
|
|
||||||
|
/** billing quote fee detail mapper */
|
||||||
|
public interface PrcQuoteFeeMapper
|
||||||
|
{
|
||||||
|
public List<PrcQuoteFee> selectByQuoteId(Long quoteId);
|
||||||
|
|
||||||
|
public int insertPrcQuoteFee(PrcQuoteFee fee);
|
||||||
|
|
||||||
|
public int insertBatch(@Param("list") List<PrcQuoteFee> fees);
|
||||||
|
|
||||||
|
public int deleteByQuoteId(Long quoteId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ruoyi.price.mapper;
|
||||||
|
|
||||||
|
import com.ruoyi.price.domain.PrcQuote;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/** billing quote header mapper */
|
||||||
|
public interface PrcQuoteMapper
|
||||||
|
{
|
||||||
|
public PrcQuote selectPrcQuoteByKey(String quoteKey);
|
||||||
|
|
||||||
|
public PrcQuote selectPrcQuoteById(Long quoteId);
|
||||||
|
|
||||||
|
public int insertPrcQuote(PrcQuote quote);
|
||||||
|
|
||||||
|
public int updateQuoteAmounts(@Param("quoteId") Long quoteId,
|
||||||
|
@Param("surchargeAmount") BigDecimal surchargeAmount,
|
||||||
|
@Param("payableTotal") BigDecimal payableTotal);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
|||||||
*
|
*
|
||||||
* Match order: effective customer package (bind_priority ASC) FIXED override
|
* Match order: effective customer package (bind_priority ASC) FIXED override
|
||||||
* then DISCOUNT, else bms_product_country default freight/registration.
|
* then DISCOUNT, else bms_product_country default freight/registration.
|
||||||
|
* After calc, persist fee lines by bms_fee_type; same trial params return stored result.
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
|
|||||||
+16
-19
@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.BmsProduct;
|
import com.ruoyi.price.domain.BmsProduct;
|
||||||
import com.ruoyi.price.domain.BmsProductCountry;
|
import com.ruoyi.price.domain.BmsProductCountry;
|
||||||
@@ -139,13 +140,13 @@ public class BmsProductServiceImpl implements IBmsProductService
|
|||||||
{
|
{
|
||||||
if (file == null || file.isEmpty())
|
if (file == null || file.isEmpty())
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u4e0a\u4f20\u6587\u4ef6\u4e0d\u80fd\u4e3a\u7a7a");
|
throw new ServiceException(MessageUtils.message("price.product.import.fileEmpty"));
|
||||||
}
|
}
|
||||||
String filename = file.getOriginalFilename();
|
String filename = file.getOriginalFilename();
|
||||||
if (filename == null || !(filename.toLowerCase(Locale.ROOT).endsWith(".xlsx")
|
if (filename == null || !(filename.toLowerCase(Locale.ROOT).endsWith(".xlsx")
|
||||||
|| filename.toLowerCase(Locale.ROOT).endsWith(".xls")))
|
|| filename.toLowerCase(Locale.ROOT).endsWith(".xls")))
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u4ec5\u652f\u6301 xls / xlsx \u6587\u4ef6");
|
throw new ServiceException(MessageUtils.message("price.product.import.fileType"));
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream is = file.getInputStream(); Workbook workbook = WorkbookFactory.create(is))
|
try (InputStream is = file.getInputStream(); Workbook workbook = WorkbookFactory.create(is))
|
||||||
@@ -153,19 +154,19 @@ public class BmsProductServiceImpl implements IBmsProductService
|
|||||||
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
Sheet catalog = workbook.getSheet(CATALOG_SHEET);
|
||||||
if (catalog == null)
|
if (catalog == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u672a\u627e\u5230\u300c\u4ea7\u54c1\u76ee\u5f55\u300dsheet\uff0c\u8bf7\u4e0a\u4f20\u5a01\u72eeB2C\u5c0f\u5305\u4ef7\u8868\u683c\u5f0f\u6587\u4ef6");
|
throw new ServiceException(MessageUtils.message("price.product.import.sheetNotFound"));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BmsProduct> products = parseCatalog(catalog);
|
List<BmsProduct> products = parseCatalog(catalog);
|
||||||
if (products.isEmpty())
|
if (products.isEmpty())
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u4ea7\u54c1\u76ee\u5f55\u65e0\u6709\u6548\u6570\u636e\u884c");
|
throw new ServiceException(MessageUtils.message("price.product.import.noData"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, BmsRegion> regionByNameCn = loadRegionByNameCn();
|
Map<String, BmsRegion> regionByNameCn = loadRegionByNameCn();
|
||||||
if (regionByNameCn.isEmpty())
|
if (regionByNameCn.isEmpty())
|
||||||
{
|
{
|
||||||
throw new ServiceException("bms_region \u65e0\u53ef\u7528\u56fd\u5bb6\u6570\u636e\uff0c\u8bf7\u5148\u540c\u6b65/\u7ef4\u62a4\u56fd\u5bb6\u8868\u540e\u518d\u5bfc\u5165");
|
throw new ServiceException(MessageUtils.message("price.product.import.regionEmpty"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int successNum = 0;
|
int successNum = 0;
|
||||||
@@ -219,16 +220,15 @@ public class BmsProductServiceImpl implements IBmsProductService
|
|||||||
|
|
||||||
if (failureMsg.length() > 0)
|
if (failureMsg.length() > 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u5bfc\u5165\u5b8c\u6210\u4f46\u5b58\u5728\u5931\u8d25\u9879\uff1a\u65b0\u589e " + successNum + "\uff0c\u66f4\u65b0 " + updateNum
|
throw new ServiceException(MessageUtils.message("price.product.import.partialFail",
|
||||||
+ "\uff0c\u8df3\u8fc7 " + skipNum + "\uff1b\u5931\u8d25\u660e\u7ec6\uff1a" + failureMsg);
|
successNum, updateNum, skipNum, failureMsg.toString()));
|
||||||
}
|
}
|
||||||
String msg = "\u5bfc\u5165\u6210\u529f\uff1a\u65b0\u589e " + successNum + " \u4e2a\u4ea7\u54c1\uff0c\u66f4\u65b0 " + updateNum + " \u4e2a\uff0c\u8df3\u8fc7 "
|
String msg = MessageUtils.message("price.product.import.success", successNum, updateNum, skipNum, countryNum)
|
||||||
+ skipNum + " \u4e2a\uff1b\u5199\u5165\u56fd\u5bb6/\u91cd\u91cf\u6bb5\u660e\u7ec6 " + countryNum + " \u884c\u3002"
|
+ "<br/>" + MessageUtils.message("price.product.import.success.detail1")
|
||||||
+ "<br/>\u56fd\u5bb6\u660e\u7ec6\uff1a\u82e5\u65b0\u751f\u6548\u65e5\u665a\u4e8e\u4e0a\u6b21\uff0c\u4fdd\u7559\u5386\u53f2\u7248\u4e14\u4e0a\u6b21\u5931\u6548\u65e5=\u672c\u6b21\u751f\u6548\u65e5\uff1b\u540c\u65e5\u5219\u66ff\u6362\u5f53\u524d\u5f00\u653e\u7248\u3002"
|
+ "<br/>" + MessageUtils.message("price.product.import.success.detail2");
|
||||||
+ "<br/>\u8303\u56f4\uff1a\u4ea7\u54c1\u76ee\u5f55 + \u91cd\u91cf\u6bb5 + \u8fd0\u8d39/\u6302\u53f7\u8d39 + \u5c3a\u5bf8/\u5c3e\u7a0b/\u65f6\u6548\uff1b\u56fd\u5bb6\u7801\u6765\u81ea bms_region\u3002";
|
|
||||||
if (!unmatchedCountries.isEmpty())
|
if (!unmatchedCountries.isEmpty())
|
||||||
{
|
{
|
||||||
msg += "<br/>\u672a\u5339\u914d\u56fd\u5bb6\u8868\uff08country_code \u4e3a\u7a7a\uff09\uff1a" + String.join("\u3001", unmatchedCountries);
|
msg += "<br/>" + MessageUtils.message("price.product.import.success.unmatched", String.join("\u3001", unmatchedCountries));
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
@@ -251,9 +251,8 @@ public class BmsProductServiceImpl implements IBmsProductService
|
|||||||
|
|
||||||
if (lastOpenEff != null && newEff != null && newEff.before(lastOpenEff))
|
if (lastOpenEff != null && newEff != null && newEff.before(lastOpenEff))
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u751f\u6548\u65e5\u671f "
|
throw new ServiceException(MessageUtils.message("price.product.import.effTooEarly",
|
||||||
+ formatDate(newEff) + " \u65e9\u4e8e\u5f53\u524d\u5f00\u653e\u7248 "
|
formatDate(newEff), formatDate(lastOpenEff)));
|
||||||
+ formatDate(lastOpenEff) + "\uff0c\u8bf7\u4f7f\u7528\u66f4\u665a\u7684\u751f\u6548\u65e5\u671f\u5bfc\u5165");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastOpenEff != null && newEff != null && newEff.after(lastOpenEff))
|
if (lastOpenEff != null && newEff != null && newEff.after(lastOpenEff))
|
||||||
@@ -284,13 +283,11 @@ public class BmsProductServiceImpl implements IBmsProductService
|
|||||||
String tip = e.getMessage() == null ? "" : e.getMessage();
|
String tip = e.getMessage() == null ? "" : e.getMessage();
|
||||||
if (tip.contains("freight_price") || tip.contains("registration_fee") || tip.contains("Unknown column"))
|
if (tip.contains("freight_price") || tip.contains("registration_fee") || tip.contains("Unknown column"))
|
||||||
{
|
{
|
||||||
throw new ServiceException(
|
throw new ServiceException(MessageUtils.message("price.product.import.needFeeAlter"));
|
||||||
"\u8bf7\u5148\u6267\u884c SQL\uff1abms-back/sql/price/product_country_fee_alter.sql\uff08\u7f3a\u5c11\u8fd0\u8d39/\u6302\u53f7\u8d39\u5b57\u6bb5\uff09");
|
|
||||||
}
|
}
|
||||||
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight"))
|
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight"))
|
||||||
{
|
{
|
||||||
throw new ServiceException(
|
throw new ServiceException(MessageUtils.message("price.product.import.needExpireAlter"));
|
||||||
"\u8bf7\u5148\u6267\u884c SQL\uff1abms-back/sql/price/product_country_expire_alter.sql\uff08\u7f3a\u5c11\u5931\u6548\u65e5\u671f/\u7248\u672c\u552f\u4e00\u952e\uff09");
|
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-30
@@ -11,6 +11,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.DateUtils;
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.BmsProductCountry;
|
import com.ruoyi.price.domain.BmsProductCountry;
|
||||||
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||||
@@ -139,12 +140,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(id);
|
PrcPriceSet db = requireSet(id);
|
||||||
if (!STATUS_DRAFT.equals(db.getStatus()) && !STATUS_REJECT.equals(db.getStatus()))
|
if (!STATUS_DRAFT.equals(db.getStatus()) && !STATUS_REJECT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("仅草稿或驳回状态的套餐允许删除:" + displayName(db));
|
throw new ServiceException(MessageUtils.message("price.set.delete.statusNotAllowed", displayName(db)));
|
||||||
}
|
}
|
||||||
int bound = customerMapper.countByPriceSetId(id);
|
int bound = customerMapper.countByPriceSetId(id);
|
||||||
if (bound > 0)
|
if (bound > 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐【" + displayName(db) + "】已绑定客户,请先解绑后再删除");
|
throw new ServiceException(MessageUtils.message("price.set.delete.boundCustomer", displayName(db)));
|
||||||
}
|
}
|
||||||
detailMapper.deletePrcPriceSetDetailByPriceSetId(id);
|
detailMapper.deletePrcPriceSetDetailByPriceSetId(id);
|
||||||
}
|
}
|
||||||
@@ -157,7 +158,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(priceSetId);
|
PrcPriceSet db = requireSet(priceSetId);
|
||||||
if (!STATUS_DRAFT.equals(db.getStatus()) && !STATUS_REJECT.equals(db.getStatus()))
|
if (!STATUS_DRAFT.equals(db.getStatus()) && !STATUS_REJECT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("当前状态不允许提交审核");
|
throw new ServiceException(MessageUtils.message("price.set.submit.statusNotAllowed"));
|
||||||
}
|
}
|
||||||
PrcPriceSet payload = selectPrcPriceSetById(priceSetId);
|
PrcPriceSet payload = selectPrcPriceSetById(priceSetId);
|
||||||
List<PrcPriceSetCheckItem> items = validate(payload);
|
List<PrcPriceSetCheckItem> items = validate(payload);
|
||||||
@@ -175,7 +176,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(priceSetId);
|
PrcPriceSet db = requireSet(priceSetId);
|
||||||
if (!STATUS_AUDIT.equals(db.getStatus()))
|
if (!STATUS_AUDIT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("仅审核中的套餐可以生效");
|
throw new ServiceException(MessageUtils.message("price.set.approve.statusNotAllowed"));
|
||||||
}
|
}
|
||||||
PrcPriceSet upd = new PrcPriceSet();
|
PrcPriceSet upd = new PrcPriceSet();
|
||||||
upd.setPriceSetId(priceSetId);
|
upd.setPriceSetId(priceSetId);
|
||||||
@@ -190,7 +191,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(priceSetId);
|
PrcPriceSet db = requireSet(priceSetId);
|
||||||
if (!STATUS_AUDIT.equals(db.getStatus()))
|
if (!STATUS_AUDIT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("仅审核中的套餐可以驳回");
|
throw new ServiceException(MessageUtils.message("price.set.reject.statusNotAllowed"));
|
||||||
}
|
}
|
||||||
PrcPriceSet upd = new PrcPriceSet();
|
PrcPriceSet upd = new PrcPriceSet();
|
||||||
upd.setPriceSetId(priceSetId);
|
upd.setPriceSetId(priceSetId);
|
||||||
@@ -205,7 +206,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(priceSetId);
|
PrcPriceSet db = requireSet(priceSetId);
|
||||||
if (!STATUS_EFFECT.equals(db.getStatus()))
|
if (!STATUS_EFFECT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("仅生效中的套餐可以停用");
|
throw new ServiceException(MessageUtils.message("price.set.disable.statusNotAllowed"));
|
||||||
}
|
}
|
||||||
PrcPriceSet upd = new PrcPriceSet();
|
PrcPriceSet upd = new PrcPriceSet();
|
||||||
upd.setPriceSetId(priceSetId);
|
upd.setPriceSetId(priceSetId);
|
||||||
@@ -221,7 +222,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet src = selectPrcPriceSetById(priceSetId);
|
PrcPriceSet src = selectPrcPriceSetById(priceSetId);
|
||||||
if (src == null)
|
if (src == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐不存在");
|
throw new ServiceException(MessageUtils.message("price.set.notExists"));
|
||||||
}
|
}
|
||||||
PrcPriceSet copy = new PrcPriceSet();
|
PrcPriceSet copy = new PrcPriceSet();
|
||||||
copy.setPriceSetName(src.getPriceSetName() + "-副本");
|
copy.setPriceSetName(src.getPriceSetName() + "-副本");
|
||||||
@@ -462,7 +463,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSetDetail old = detailMapper.selectPrcPriceSetDetailById(detail.getPsdId());
|
PrcPriceSetDetail old = detailMapper.selectPrcPriceSetDetailById(detail.getPsdId());
|
||||||
if (old == null)
|
if (old == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐明细不存在");
|
throw new ServiceException(MessageUtils.message("price.set.detail.notExists"));
|
||||||
}
|
}
|
||||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||||
assertChildEditable(db);
|
assertChildEditable(db);
|
||||||
@@ -512,7 +513,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcCustomerPriceSet old = customerMapper.selectPrcCustomerPriceSetById(customer.getCustomerPricesetId());
|
PrcCustomerPriceSet old = customerMapper.selectPrcCustomerPriceSetById(customer.getCustomerPricesetId());
|
||||||
if (old == null)
|
if (old == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("客户绑定不存在");
|
throw new ServiceException(MessageUtils.message("price.set.customer.notExists"));
|
||||||
}
|
}
|
||||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||||
assertChildEditable(db);
|
assertChildEditable(db);
|
||||||
@@ -534,7 +535,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||||
if (STATUS_AUDIT.equals(db.getStatus()))
|
if (STATUS_AUDIT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("审核中的套餐不允许解绑客户");
|
throw new ServiceException(MessageUtils.message("price.set.customer.unbindAudit"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return customerMapper.deletePrcCustomerPriceSetByIds(customerPricesetIds);
|
return customerMapper.deletePrcCustomerPriceSetByIds(customerPricesetIds);
|
||||||
@@ -575,15 +576,15 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(set.getPriceSetName()))
|
if (StringUtils.isEmpty(set.getPriceSetName()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐名称不能为空");
|
throw new ServiceException(MessageUtils.message("price.set.name.empty"));
|
||||||
}
|
}
|
||||||
if (set.getEffectiveDate() == null)
|
if (set.getEffectiveDate() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("生效日期不能为空");
|
throw new ServiceException(MessageUtils.message("price.set.effectiveDate.empty"));
|
||||||
}
|
}
|
||||||
if (set.getExpireDate() != null && set.getEffectiveDate().after(set.getExpireDate()))
|
if (set.getExpireDate() != null && set.getEffectiveDate().after(set.getExpireDate()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("生效日期不能晚于失效日期");
|
throw new ServiceException(MessageUtils.message("price.set.date.invalid"));
|
||||||
}
|
}
|
||||||
if (TYPE_DISCOUNT.equals(set.getSetType()))
|
if (TYPE_DISCOUNT.equals(set.getSetType()))
|
||||||
{
|
{
|
||||||
@@ -591,12 +592,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
|| set.getDefaultDiscount().compareTo(BigDecimal.ONE) < 0
|
|| set.getDefaultDiscount().compareTo(BigDecimal.ONE) < 0
|
||||||
|| set.getDefaultDiscount().compareTo(new BigDecimal("99")) > 0)
|
|| set.getDefaultDiscount().compareTo(new BigDecimal("99")) > 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("折扣模式默认折扣需在 1~99");
|
throw new ServiceException(MessageUtils.message("price.set.discount.range"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (setMapper.countByName(set) > 0)
|
if (setMapper.countByName(set) > 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐名称已存在");
|
throw new ServiceException(MessageUtils.message("price.set.name.exists"));
|
||||||
}
|
}
|
||||||
if (strictSubmit)
|
if (strictSubmit)
|
||||||
{
|
{
|
||||||
@@ -608,25 +609,25 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(detail.getProductCode()))
|
if (StringUtils.isEmpty(detail.getProductCode()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("明细产品代码不能为空");
|
throw new ServiceException(MessageUtils.message("price.set.detail.productCode.empty"));
|
||||||
}
|
}
|
||||||
if (TYPE_FIXED.equals(set.getSetType()))
|
if (TYPE_FIXED.equals(set.getSetType()))
|
||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(detail.getCountryCode()) && StringUtils.isEmpty(detail.getCountryName()))
|
if (StringUtils.isEmpty(detail.getCountryCode()) && StringUtils.isEmpty(detail.getCountryName()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("\u56fa\u5b9a\u4ef7\u660e\u7ec6\u5fc5\u987b\u586b\u5199\u56fd\u5bb6");
|
throw new ServiceException(MessageUtils.message("price.set.detail.country.empty"));
|
||||||
}
|
}
|
||||||
if (detail.getWsdId() == null && StringUtils.isEmpty(detail.getWeightLabel()))
|
if (detail.getWsdId() == null && StringUtils.isEmpty(detail.getWeightLabel()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("固定价明细必须填写重量段");
|
throw new ServiceException(MessageUtils.message("price.set.detail.weight.empty"));
|
||||||
}
|
}
|
||||||
if (detail.getFvValue() == null)
|
if (detail.getFvValue() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("固定价明细必须填写运费单价");
|
throw new ServiceException(MessageUtils.message("price.set.detail.freight.empty"));
|
||||||
}
|
}
|
||||||
if (detail.getFvRegistrationFee() == null)
|
if (detail.getFvRegistrationFee() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("固定价明细必须填写挂号费");
|
throw new ServiceException(MessageUtils.message("price.set.detail.registration.empty"));
|
||||||
}
|
}
|
||||||
fillProductCountryId(detail);
|
fillProductCountryId(detail);
|
||||||
}
|
}
|
||||||
@@ -639,7 +640,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
String mode = detail.getRoundMode().trim().toUpperCase();
|
String mode = detail.getRoundMode().trim().toUpperCase();
|
||||||
if (!"UP".equals(mode) && !"DOWN".equals(mode) && !"HALF_UP".equals(mode))
|
if (!"UP".equals(mode) && !"DOWN".equals(mode) && !"HALF_UP".equals(mode))
|
||||||
{
|
{
|
||||||
throw new ServiceException("取值方式仅支持 UP / DOWN / HALF_UP");
|
throw new ServiceException(MessageUtils.message("price.set.detail.roundMode.invalid"));
|
||||||
}
|
}
|
||||||
detail.setRoundMode(mode);
|
detail.setRoundMode(mode);
|
||||||
}
|
}
|
||||||
@@ -720,7 +721,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
if (customer.getCustomerId() == null || customer.getCustomerId().longValue() <= 0)
|
if (customer.getCustomerId() == null || customer.getCustomerId().longValue() <= 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("客户ID不能为空");
|
throw new ServiceException(MessageUtils.message("price.set.customer.customerId.empty"));
|
||||||
}
|
}
|
||||||
if (customer.getPrBegindate() == null)
|
if (customer.getPrBegindate() == null)
|
||||||
{
|
{
|
||||||
@@ -728,15 +729,15 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
if (customer.getPrBegindate() == null)
|
if (customer.getPrBegindate() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("客户绑定生效日期不能为空");
|
throw new ServiceException(MessageUtils.message("price.set.customer.beginDate.empty"));
|
||||||
}
|
}
|
||||||
if (customer.getPrEnddate() != null && customer.getPrBegindate().after(customer.getPrEnddate()))
|
if (customer.getPrEnddate() != null && customer.getPrBegindate().after(customer.getPrEnddate()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("客户绑定生效日期不能晚于失效日期");
|
throw new ServiceException(MessageUtils.message("price.set.customer.date.invalid"));
|
||||||
}
|
}
|
||||||
if (customer.getBindPriority() != null && customer.getBindPriority().intValue() < 1)
|
if (customer.getBindPriority() != null && customer.getBindPriority().intValue() < 1)
|
||||||
{
|
{
|
||||||
throw new ServiceException("优先级必须大于等于 1");
|
throw new ServiceException(MessageUtils.message("price.set.customer.priority.invalid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,11 +745,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
if (STATUS_AUDIT.equals(db.getStatus()))
|
if (STATUS_AUDIT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("审核中的套餐不允许修改");
|
throw new ServiceException(MessageUtils.message("price.set.edit.audit"));
|
||||||
}
|
}
|
||||||
if (STATUS_STOP.equals(db.getStatus()))
|
if (STATUS_STOP.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("已停用的套餐不允许修改,请使用复用新建");
|
throw new ServiceException(MessageUtils.message("price.set.edit.stopped"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,11 +757,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
if (STATUS_AUDIT.equals(db.getStatus()))
|
if (STATUS_AUDIT.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("审核中的套餐不允许改明细或客户");
|
throw new ServiceException(MessageUtils.message("price.set.childEdit.audit"));
|
||||||
}
|
}
|
||||||
if (STATUS_STOP.equals(db.getStatus()))
|
if (STATUS_STOP.equals(db.getStatus()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("已停用的套餐不允许改明细或客户");
|
throw new ServiceException(MessageUtils.message("price.set.childEdit.stopped"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +781,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSet db = setMapper.selectPrcPriceSetById(priceSetId);
|
PrcPriceSet db = setMapper.selectPrcPriceSetById(priceSetId);
|
||||||
if (db == null)
|
if (db == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("套餐不存在");
|
throw new ServiceException(MessageUtils.message("price.set.notExists"));
|
||||||
}
|
}
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.PrcSurchargeRel;
|
import com.ruoyi.price.domain.PrcSurchargeRel;
|
||||||
import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
|
import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
|
||||||
@@ -65,15 +66,15 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService
|
|||||||
{
|
{
|
||||||
if (rel.getScId() == null)
|
if (rel.getScId() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("附加费ID不能为空");
|
throw new ServiceException(MessageUtils.message("price.surchargeRel.scId.empty"));
|
||||||
}
|
}
|
||||||
if (rel.getProductId() == null)
|
if (rel.getProductId() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("产品ID不能为空");
|
throw new ServiceException(MessageUtils.message("price.surchargeRel.productId.empty"));
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(rel.getProductCode()))
|
if (StringUtils.isEmpty(rel.getProductCode()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("产品代码不能为空");
|
throw new ServiceException(MessageUtils.message("price.surchargeRel.productCode.empty"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.PrcSurcharge;
|
import com.ruoyi.price.domain.PrcSurcharge;
|
||||||
import com.ruoyi.price.mapper.PrcSurchargeMapper;
|
import com.ruoyi.price.mapper.PrcSurchargeMapper;
|
||||||
@@ -68,19 +69,19 @@ public class PrcSurchargeServiceImpl implements IPrcSurchargeService
|
|||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(surcharge.getScName()))
|
if (StringUtils.isEmpty(surcharge.getScName()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("附加费名称不能为空");
|
throw new ServiceException(MessageUtils.message("price.surcharge.scName.empty"));
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(surcharge.getChargeType()))
|
if (StringUtils.isEmpty(surcharge.getChargeType()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("计费方式不能为空");
|
throw new ServiceException(MessageUtils.message("price.surcharge.chargeType.empty"));
|
||||||
}
|
}
|
||||||
if (surcharge.getChargeValue() == null)
|
if (surcharge.getChargeValue() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("计费数值不能为空");
|
throw new ServiceException(MessageUtils.message("price.surcharge.chargeValue.empty"));
|
||||||
}
|
}
|
||||||
if (surcharge.getChargeValue().compareTo(java.math.BigDecimal.ZERO) < 0)
|
if (surcharge.getChargeValue().compareTo(java.math.BigDecimal.ZERO) < 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("计费数值不能为负数");
|
throw new ServiceException(MessageUtils.message("price.surcharge.chargeValue.negative"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.PrcWeightSectionDetail;
|
import com.ruoyi.price.domain.PrcWeightSectionDetail;
|
||||||
import com.ruoyi.price.domain.PrcWeightSectionTemplate;
|
import com.ruoyi.price.domain.PrcWeightSectionTemplate;
|
||||||
@@ -106,11 +107,11 @@ public class PrcWeightServiceImpl implements IPrcWeightService
|
|||||||
{
|
{
|
||||||
if (detail.getWlStartweight() == null || detail.getWlEndweight() == null)
|
if (detail.getWlStartweight() == null || detail.getWlEndweight() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("起始重量和结束重量不能为空");
|
throw new ServiceException(MessageUtils.message("price.weight.range.empty"));
|
||||||
}
|
}
|
||||||
if (detail.getWlStartweight().compareTo(detail.getWlEndweight()) >= 0)
|
if (detail.getWlStartweight().compareTo(detail.getWlEndweight()) >= 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("起始重量必须小于结束重量");
|
throw new ServiceException(MessageUtils.message("price.weight.range.invalid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+457
-12
@@ -4,17 +4,28 @@ import java.math.BigDecimal;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.sign.Md5Utils;
|
||||||
import com.ruoyi.price.domain.BmsProduct;
|
import com.ruoyi.price.domain.BmsProduct;
|
||||||
import com.ruoyi.price.domain.BmsProductCountry;
|
import com.ruoyi.price.domain.BmsProductCountry;
|
||||||
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
import com.ruoyi.price.domain.PrcCustomerPriceSet;
|
||||||
import com.ruoyi.price.domain.PrcPriceSet;
|
import com.ruoyi.price.domain.PrcPriceSet;
|
||||||
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
import com.ruoyi.price.domain.PrcPriceSetDetail;
|
||||||
|
import com.ruoyi.price.domain.PrcQuote;
|
||||||
|
import com.ruoyi.price.domain.PrcQuoteFee;
|
||||||
|
import com.ruoyi.price.domain.PrcSurchargeRel;
|
||||||
|
import com.ruoyi.price.domain.vo.TmsPriceQuoteFeeLine;
|
||||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
import com.ruoyi.price.domain.vo.TmsPriceQuoteRequest;
|
||||||
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
|
||||||
import com.ruoyi.price.mapper.BmsProductCountryMapper;
|
import com.ruoyi.price.mapper.BmsProductCountryMapper;
|
||||||
@@ -22,10 +33,14 @@ import com.ruoyi.price.mapper.BmsProductMapper;
|
|||||||
import com.ruoyi.price.mapper.PrcCustomerPriceSetMapper;
|
import com.ruoyi.price.mapper.PrcCustomerPriceSetMapper;
|
||||||
import com.ruoyi.price.mapper.PrcPriceSetDetailMapper;
|
import com.ruoyi.price.mapper.PrcPriceSetDetailMapper;
|
||||||
import com.ruoyi.price.mapper.PrcPriceSetMapper;
|
import com.ruoyi.price.mapper.PrcPriceSetMapper;
|
||||||
|
import com.ruoyi.price.mapper.PrcQuoteFeeMapper;
|
||||||
|
import com.ruoyi.price.mapper.PrcQuoteMapper;
|
||||||
|
import com.ruoyi.price.mapper.PrcSurchargeRelMapper;
|
||||||
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
import com.ruoyi.price.service.ITmsPriceQuoteService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TMS freight quote: customer package first, then product default.
|
* TMS freight quote: customer package first, then product default.
|
||||||
|
* After calc: persist fee lines by bms_fee_type; same trial params return stored rows.
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@@ -38,6 +53,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
private static final String SRC_DISCOUNT = "PACKAGE_DISCOUNT";
|
private static final String SRC_DISCOUNT = "PACKAGE_DISCOUNT";
|
||||||
private static final String SRC_DEFAULT = "PRODUCT_DEFAULT";
|
private static final String SRC_DEFAULT = "PRODUCT_DEFAULT";
|
||||||
|
|
||||||
|
private static final String FEE_BASIC = "BASIC_FREIGHT";
|
||||||
|
private static final String FEE_REG = "REGISTRATION_FEE";
|
||||||
|
private static final String FEE_SUR = "SURCHARGE";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PrcCustomerPriceSetMapper customerPriceSetMapper;
|
private PrcCustomerPriceSetMapper customerPriceSetMapper;
|
||||||
|
|
||||||
@@ -53,11 +72,48 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
@Autowired
|
@Autowired
|
||||||
private BmsProductMapper productMapper;
|
private BmsProductMapper productMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrcSurchargeRelMapper surchargeRelMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrcQuoteMapper quoteMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrcQuoteFeeMapper quoteFeeMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request)
|
public TmsPriceQuoteResult quote(TmsPriceQuoteRequest request)
|
||||||
{
|
{
|
||||||
validateRequest(request);
|
validateRequest(request);
|
||||||
|
Date bizDate = resolveBizDate(request.getBizDate());
|
||||||
|
BigDecimal weight = request.getWeight().setScale(3, RoundingMode.HALF_UP);
|
||||||
|
String quoteKey = buildQuoteKey(request, weight, bizDate);
|
||||||
|
|
||||||
|
PrcQuote cached = quoteMapper.selectPrcQuoteByKey(quoteKey);
|
||||||
|
if (cached != null)
|
||||||
|
{
|
||||||
|
List<PrcQuoteFee> fees = quoteFeeMapper.selectByQuoteId(cached.getQuoteId());
|
||||||
|
if (!isStaleSurchargeFees(fees))
|
||||||
|
{
|
||||||
|
return toResult(cached, fees, true);
|
||||||
|
}
|
||||||
|
TmsPriceQuoteResult refreshed = doQuote(request, bizDate);
|
||||||
|
applySurchargesAndFees(refreshed);
|
||||||
|
replaceQuoteFees(cached.getQuoteId(), refreshed);
|
||||||
|
refreshed.setQuoteId(cached.getQuoteId());
|
||||||
|
refreshed.setFromCache(false);
|
||||||
|
return refreshed;
|
||||||
|
}
|
||||||
|
|
||||||
|
TmsPriceQuoteResult result = doQuote(request, bizDate);
|
||||||
|
applySurchargesAndFees(result);
|
||||||
|
persistQuote(quoteKey, request, weight, bizDate, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TmsPriceQuoteResult doQuote(TmsPriceQuoteRequest request, Date bizDate)
|
||||||
|
{
|
||||||
String productCode = request.getProductCode().trim();
|
String productCode = request.getProductCode().trim();
|
||||||
String countryCode = emptyToNull(request.getCountryCode());
|
String countryCode = emptyToNull(request.getCountryCode());
|
||||||
String countryName = emptyToNull(request.getCountryName());
|
String countryName = emptyToNull(request.getCountryName());
|
||||||
@@ -66,13 +122,12 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
countryCode = countryCode.toUpperCase();
|
countryCode = countryCode.toUpperCase();
|
||||||
}
|
}
|
||||||
BigDecimal weight = request.getWeight();
|
BigDecimal weight = request.getWeight();
|
||||||
Date bizDate = resolveBizDate(request.getBizDate());
|
|
||||||
|
|
||||||
BmsProductCountry base = productCountryMapper.selectMatchByProductCountryWeight(
|
BmsProductCountry base = productCountryMapper.selectMatchByProductCountryWeight(
|
||||||
productCode, countryCode, countryName, weight, bizDate);
|
productCode, countryCode, countryName, weight, bizDate);
|
||||||
if (base == null)
|
if (base == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("PRICE_NOT_HIT: no bms_product_country band for product/country/weight");
|
throw new ServiceException(MessageUtils.message("price.quote.priceNotHit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String productName = resolveProductName(productCode);
|
String productName = resolveProductName(productCode);
|
||||||
@@ -185,7 +240,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
}
|
}
|
||||||
if (base.getFreightPrice() == null)
|
if (base.getFreightPrice() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight rate is null");
|
throw new ServiceException(MessageUtils.message("price.quote.freightMissing1"));
|
||||||
}
|
}
|
||||||
BigDecimal baseRate = base.getFreightPrice();
|
BigDecimal baseRate = base.getFreightPrice();
|
||||||
RoundingMode rm = resolveRoundingMode(roundMode);
|
RoundingMode rm = resolveRoundingMode(roundMode);
|
||||||
@@ -204,7 +259,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
{
|
{
|
||||||
if (base.getFreightPrice() == null || base.getRegistrationFee() == null)
|
if (base.getFreightPrice() == null || base.getRegistrationFee() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("FREIGHT_VALUE_MISSING: product default freight or registration is null");
|
throw new ServiceException(MessageUtils.message("price.quote.freightMissing2"));
|
||||||
}
|
}
|
||||||
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
||||||
base.getRegistrationFee(), base, productCode, productName, weight, null);
|
base.getRegistrationFee(), base, productCode, productName, weight, null);
|
||||||
@@ -220,7 +275,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
{
|
{
|
||||||
if (freightRate == null)
|
if (freightRate == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("FREIGHT_VALUE_MISSING: freight rate is null");
|
throw new ServiceException(MessageUtils.message("price.quote.freightMissing3"));
|
||||||
}
|
}
|
||||||
if (registrationFee == null)
|
if (registrationFee == null)
|
||||||
{
|
{
|
||||||
@@ -249,12 +304,384 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
r.setDiscountPercent(discountPercent);
|
r.setDiscountPercent(discountPercent);
|
||||||
r.setRegistrationFee(registrationFee);
|
r.setRegistrationFee(registrationFee);
|
||||||
r.setFreightAmount(freightAmount);
|
r.setFreightAmount(freightAmount);
|
||||||
|
r.setSurchargeAmount(BigDecimal.ZERO);
|
||||||
r.setPayableTotal(payable);
|
r.setPayableTotal(payable);
|
||||||
r.setCurrency("RMB");
|
r.setCurrency("RMB");
|
||||||
r.setRoundMode(mode);
|
r.setRoundMode(mode);
|
||||||
|
r.setFromCache(false);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applySurchargesAndFees(TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
BigDecimal freightAmount = nvl(result.getFreightAmount());
|
||||||
|
BigDecimal registrationFee = nvl(result.getRegistrationFee());
|
||||||
|
RoundingMode rm = resolveRoundingMode(result.getRoundMode());
|
||||||
|
String currency = StringUtils.isEmpty(result.getCurrency()) ? "RMB" : result.getCurrency();
|
||||||
|
|
||||||
|
List<PrcSurchargeRel> rels = loadMatchedSurcharges(result);
|
||||||
|
List<TmsPriceQuoteFeeLine> lines = new ArrayList<TmsPriceQuoteFeeLine>();
|
||||||
|
lines.add(feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, freightAmount, currency, result.getRoundMode(), null));
|
||||||
|
lines.add(feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, registrationFee, currency, result.getRoundMode(), null));
|
||||||
|
|
||||||
|
BigDecimal surchargeTotal = BigDecimal.ZERO;
|
||||||
|
Map<Long, TmsPriceQuoteFeeLine> surchargeLines = new LinkedHashMap<Long, TmsPriceQuoteFeeLine>();
|
||||||
|
for (PrcSurchargeRel rel : rels)
|
||||||
|
{
|
||||||
|
BigDecimal itemAmount = calcSurchargeAmount(rel, result.getWeight(), freightAmount);
|
||||||
|
if (itemAmount == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Long scId = rel.getScId() == null ? 0L : rel.getScId();
|
||||||
|
TmsPriceQuoteFeeLine exist = surchargeLines.get(scId);
|
||||||
|
if (exist != null)
|
||||||
|
{
|
||||||
|
exist.setAmount(nvl(exist.getAmount()).add(itemAmount));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String feeType = StringUtils.isEmpty(rel.getFeeType()) ? FEE_SUR : rel.getFeeType();
|
||||||
|
String feeName = StringUtils.isEmpty(rel.getScName()) ? feeNameOf(FEE_SUR) : rel.getScName();
|
||||||
|
String itemCcy = StringUtils.isEmpty(rel.getCurrency()) ? currency : rel.getCurrency();
|
||||||
|
String itemRound = StringUtils.isEmpty(rel.getRoundMode()) ? result.getRoundMode() : rel.getRoundMode();
|
||||||
|
surchargeLines.put(scId, feeLine(feeType, feeName, scId, itemAmount, itemCcy, itemRound, rel.getScNote()));
|
||||||
|
}
|
||||||
|
for (TmsPriceQuoteFeeLine line : surchargeLines.values())
|
||||||
|
{
|
||||||
|
line.setAmount(nvl(line.getAmount()).setScale(2, rm));
|
||||||
|
surchargeTotal = surchargeTotal.add(nvl(line.getAmount()));
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
surchargeTotal = surchargeTotal.setScale(2, rm);
|
||||||
|
result.setSurchargeAmount(surchargeTotal);
|
||||||
|
result.setPayableTotal(freightAmount.add(registrationFee).add(surchargeTotal).setScale(2, rm));
|
||||||
|
result.setFeeDetails(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PrcSurchargeRel> loadMatchedSurcharges(TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
List<PrcSurchargeRel> matched = new ArrayList<PrcSurchargeRel>();
|
||||||
|
if (result == null || StringUtils.isEmpty(result.getProductCode()))
|
||||||
|
{
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
PrcSurchargeRel query = new PrcSurchargeRel();
|
||||||
|
query.setProductCode(result.getProductCode());
|
||||||
|
List<PrcSurchargeRel> rels = surchargeRelMapper.selectPrcSurchargeRelList(query);
|
||||||
|
if (rels == null)
|
||||||
|
{
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
for (PrcSurchargeRel rel : rels)
|
||||||
|
{
|
||||||
|
if (surchargeApplies(rel, result))
|
||||||
|
{
|
||||||
|
matched.add(rel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
if (rel == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(rel.getSsCode()) && !"on".equalsIgnoreCase(rel.getSsCode()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(rel.getCountryCode()) && StringUtils.isNotEmpty(result.getCountryCode())
|
||||||
|
&& !rel.getCountryCode().equalsIgnoreCase(result.getCountryCode()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rel.getDetailId() == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return result.getProductCountryId() != null && rel.getDetailId().equals(result.getProductCountryId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calcSurchargeAmount(PrcSurchargeRel rel, BigDecimal weight, BigDecimal freightAmount)
|
||||||
|
{
|
||||||
|
if (rel.getChargeValue() == null)
|
||||||
|
{
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
RoundingMode rm = resolveRoundingMode(rel.getRoundMode());
|
||||||
|
String chargeType = rel.getChargeType() == null ? "FIXED" : rel.getChargeType().toUpperCase();
|
||||||
|
BigDecimal amount;
|
||||||
|
if ("WEIGHT".equals(chargeType))
|
||||||
|
{
|
||||||
|
amount = rel.getChargeValue().multiply(nvl(weight));
|
||||||
|
}
|
||||||
|
else if ("RATE".equals(chargeType))
|
||||||
|
{
|
||||||
|
amount = nvl(freightAmount).multiply(rel.getChargeValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
amount = rel.getChargeValue();
|
||||||
|
}
|
||||||
|
return amount.setScale(2, rm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistQuote(String quoteKey, TmsPriceQuoteRequest request, BigDecimal weight,
|
||||||
|
Date bizDate, TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
PrcQuote quote = new PrcQuote();
|
||||||
|
quote.setQuoteKey(quoteKey);
|
||||||
|
quote.setCustomerId(request.getCustomerId());
|
||||||
|
quote.setProductCode(result.getProductCode());
|
||||||
|
quote.setProductName(result.getProductName());
|
||||||
|
quote.setCountryCode(result.getCountryCode());
|
||||||
|
quote.setCountryName(result.getCountryName());
|
||||||
|
quote.setWeight(weight);
|
||||||
|
quote.setBizDate(bizDate);
|
||||||
|
quote.setTmsId(request.getTmsId() == null ? null : Long.valueOf(request.getTmsId()));
|
||||||
|
quote.setMatchSource(result.getMatchSource());
|
||||||
|
quote.setMatchMessage(result.getMatchMessage());
|
||||||
|
quote.setPriceSetId(result.getPriceSetId());
|
||||||
|
quote.setPriceSetCode(result.getPriceSetCode());
|
||||||
|
quote.setPriceSetName(result.getPriceSetName());
|
||||||
|
quote.setSetType(result.getSetType());
|
||||||
|
quote.setBindPriority(result.getBindPriority());
|
||||||
|
quote.setCustomerPricesetId(result.getCustomerPricesetId());
|
||||||
|
quote.setProductCountryId(result.getProductCountryId());
|
||||||
|
quote.setWeightMin(result.getWeightMin());
|
||||||
|
quote.setWeightMax(result.getWeightMax());
|
||||||
|
quote.setWeightLabel(result.getWeightLabel());
|
||||||
|
quote.setEffectiveDate(result.getEffectiveDate());
|
||||||
|
quote.setChargeWeight(result.getChargeWeight());
|
||||||
|
quote.setFreightRate(result.getFreightRate());
|
||||||
|
quote.setBaseFreightRate(result.getBaseFreightRate());
|
||||||
|
quote.setDiscountPercent(result.getDiscountPercent());
|
||||||
|
quote.setRegistrationFee(result.getRegistrationFee());
|
||||||
|
quote.setFreightAmount(result.getFreightAmount());
|
||||||
|
quote.setSurchargeAmount(result.getSurchargeAmount());
|
||||||
|
quote.setPayableTotal(result.getPayableTotal());
|
||||||
|
quote.setCurrency(result.getCurrency());
|
||||||
|
quote.setRoundMode(result.getRoundMode());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
quoteMapper.insertPrcQuote(quote);
|
||||||
|
}
|
||||||
|
catch (DuplicateKeyException ex)
|
||||||
|
{
|
||||||
|
PrcQuote existed = quoteMapper.selectPrcQuoteByKey(quoteKey);
|
||||||
|
if (existed == null)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
TmsPriceQuoteResult cached = toResult(existed, quoteFeeMapper.selectByQuoteId(existed.getQuoteId()), true);
|
||||||
|
copyResult(cached, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PrcQuoteFee> fees = toFeeRows(quote.getQuoteId(), result);
|
||||||
|
if (!fees.isEmpty())
|
||||||
|
{
|
||||||
|
quoteFeeMapper.insertBatch(fees);
|
||||||
|
}
|
||||||
|
result.setQuoteId(quote.getQuoteId());
|
||||||
|
result.setFromCache(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStaleSurchargeFees(List<PrcQuoteFee> fees)
|
||||||
|
{
|
||||||
|
if (fees == null || fees.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (PrcQuoteFee fee : fees)
|
||||||
|
{
|
||||||
|
if (!FEE_SUR.equals(fee.getFeeType()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fee.getScId() == null || fee.getScId().longValue() == 0L)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String name = fee.getFeeName() == null ? "" : fee.getFeeName().trim();
|
||||||
|
if (name.isEmpty() || "SURCHARGE".equalsIgnoreCase(name)
|
||||||
|
|| "\u9644\u52a0\u8d39".equals(name))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceQuoteFees(Long quoteId, TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
quoteFeeMapper.deleteByQuoteId(quoteId);
|
||||||
|
List<PrcQuoteFee> fees = toFeeRows(quoteId, result);
|
||||||
|
if (!fees.isEmpty())
|
||||||
|
{
|
||||||
|
quoteFeeMapper.insertBatch(fees);
|
||||||
|
}
|
||||||
|
quoteMapper.updateQuoteAmounts(quoteId, result.getSurchargeAmount(), result.getPayableTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PrcQuoteFee> toFeeRows(Long quoteId, TmsPriceQuoteResult result)
|
||||||
|
{
|
||||||
|
List<PrcQuoteFee> fees = new ArrayList<PrcQuoteFee>();
|
||||||
|
if (result.getFeeDetails() == null)
|
||||||
|
{
|
||||||
|
return fees;
|
||||||
|
}
|
||||||
|
for (TmsPriceQuoteFeeLine line : result.getFeeDetails())
|
||||||
|
{
|
||||||
|
PrcQuoteFee fee = new PrcQuoteFee();
|
||||||
|
fee.setQuoteId(quoteId);
|
||||||
|
fee.setFeeType(line.getFeeType());
|
||||||
|
fee.setFeeName(line.getFeeName());
|
||||||
|
fee.setScId(line.getScId() == null ? 0L : line.getScId());
|
||||||
|
fee.setAmount(nvl(line.getAmount()));
|
||||||
|
fee.setCurrency(line.getCurrency());
|
||||||
|
fee.setRoundMode(line.getRoundMode());
|
||||||
|
fee.setRemark(line.getRemark());
|
||||||
|
fees.add(fee);
|
||||||
|
}
|
||||||
|
return fees;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TmsPriceQuoteResult toResult(PrcQuote quote, List<PrcQuoteFee> fees, boolean fromCache)
|
||||||
|
{
|
||||||
|
TmsPriceQuoteResult r = new TmsPriceQuoteResult();
|
||||||
|
r.setQuoteId(quote.getQuoteId());
|
||||||
|
r.setFromCache(fromCache);
|
||||||
|
r.setMatchSource(quote.getMatchSource());
|
||||||
|
r.setMatchMessage(quote.getMatchMessage());
|
||||||
|
r.setCustomerId(quote.getCustomerId());
|
||||||
|
r.setPriceSetId(quote.getPriceSetId());
|
||||||
|
r.setPriceSetCode(quote.getPriceSetCode());
|
||||||
|
r.setPriceSetName(quote.getPriceSetName());
|
||||||
|
r.setSetType(quote.getSetType());
|
||||||
|
r.setBindPriority(quote.getBindPriority());
|
||||||
|
r.setCustomerPricesetId(quote.getCustomerPricesetId());
|
||||||
|
r.setProductCode(quote.getProductCode());
|
||||||
|
r.setProductName(quote.getProductName());
|
||||||
|
r.setCountryCode(quote.getCountryCode());
|
||||||
|
r.setCountryName(quote.getCountryName());
|
||||||
|
r.setProductCountryId(quote.getProductCountryId());
|
||||||
|
r.setWeightMin(quote.getWeightMin());
|
||||||
|
r.setWeightMax(quote.getWeightMax());
|
||||||
|
r.setWeightLabel(quote.getWeightLabel());
|
||||||
|
r.setEffectiveDate(quote.getEffectiveDate());
|
||||||
|
r.setWeight(quote.getWeight());
|
||||||
|
r.setChargeWeight(quote.getChargeWeight());
|
||||||
|
r.setFreightRate(quote.getFreightRate());
|
||||||
|
r.setBaseFreightRate(quote.getBaseFreightRate());
|
||||||
|
r.setDiscountPercent(quote.getDiscountPercent());
|
||||||
|
r.setRegistrationFee(quote.getRegistrationFee());
|
||||||
|
r.setFreightAmount(quote.getFreightAmount());
|
||||||
|
r.setSurchargeAmount(quote.getSurchargeAmount());
|
||||||
|
r.setPayableTotal(quote.getPayableTotal());
|
||||||
|
r.setCurrency(quote.getCurrency());
|
||||||
|
r.setRoundMode(quote.getRoundMode());
|
||||||
|
List<TmsPriceQuoteFeeLine> lines = new ArrayList<TmsPriceQuoteFeeLine>();
|
||||||
|
if (fees != null)
|
||||||
|
{
|
||||||
|
for (PrcQuoteFee fee : fees)
|
||||||
|
{
|
||||||
|
lines.add(feeLine(fee.getFeeType(), fee.getFeeName(), fee.getScId(), fee.getAmount(),
|
||||||
|
fee.getCurrency(), fee.getRoundMode(), fee.getRemark()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.setFeeDetails(lines);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyResult(TmsPriceQuoteResult from, TmsPriceQuoteResult to)
|
||||||
|
{
|
||||||
|
to.setQuoteId(from.getQuoteId());
|
||||||
|
to.setFromCache(from.getFromCache());
|
||||||
|
to.setMatchSource(from.getMatchSource());
|
||||||
|
to.setMatchMessage(from.getMatchMessage());
|
||||||
|
to.setCustomerId(from.getCustomerId());
|
||||||
|
to.setPriceSetId(from.getPriceSetId());
|
||||||
|
to.setPriceSetCode(from.getPriceSetCode());
|
||||||
|
to.setPriceSetName(from.getPriceSetName());
|
||||||
|
to.setSetType(from.getSetType());
|
||||||
|
to.setBindPriority(from.getBindPriority());
|
||||||
|
to.setCustomerPricesetId(from.getCustomerPricesetId());
|
||||||
|
to.setProductCode(from.getProductCode());
|
||||||
|
to.setProductName(from.getProductName());
|
||||||
|
to.setCountryCode(from.getCountryCode());
|
||||||
|
to.setCountryName(from.getCountryName());
|
||||||
|
to.setProductCountryId(from.getProductCountryId());
|
||||||
|
to.setWeightMin(from.getWeightMin());
|
||||||
|
to.setWeightMax(from.getWeightMax());
|
||||||
|
to.setWeightLabel(from.getWeightLabel());
|
||||||
|
to.setEffectiveDate(from.getEffectiveDate());
|
||||||
|
to.setWeight(from.getWeight());
|
||||||
|
to.setChargeWeight(from.getChargeWeight());
|
||||||
|
to.setFreightRate(from.getFreightRate());
|
||||||
|
to.setBaseFreightRate(from.getBaseFreightRate());
|
||||||
|
to.setDiscountPercent(from.getDiscountPercent());
|
||||||
|
to.setRegistrationFee(from.getRegistrationFee());
|
||||||
|
to.setFreightAmount(from.getFreightAmount());
|
||||||
|
to.setSurchargeAmount(from.getSurchargeAmount());
|
||||||
|
to.setPayableTotal(from.getPayableTotal());
|
||||||
|
to.setCurrency(from.getCurrency());
|
||||||
|
to.setRoundMode(from.getRoundMode());
|
||||||
|
to.setFeeDetails(from.getFeeDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildQuoteKey(TmsPriceQuoteRequest request, BigDecimal weight, Date bizDate)
|
||||||
|
{
|
||||||
|
String countryCode = emptyToNull(request.getCountryCode());
|
||||||
|
if (countryCode != null)
|
||||||
|
{
|
||||||
|
countryCode = countryCode.toUpperCase();
|
||||||
|
}
|
||||||
|
String countryName = emptyToNull(request.getCountryName());
|
||||||
|
String biz = new SimpleDateFormat("yyyy-MM-dd").format(bizDate);
|
||||||
|
String raw = request.getCustomerId() + "|"
|
||||||
|
+ request.getProductCode().trim() + "|"
|
||||||
|
+ nvlStr(countryCode) + "|"
|
||||||
|
+ nvlStr(countryName) + "|"
|
||||||
|
+ weight.toPlainString() + "|"
|
||||||
|
+ biz + "|"
|
||||||
|
+ (request.getTmsId() == null ? "" : request.getTmsId());
|
||||||
|
return Md5Utils.hash(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TmsPriceQuoteFeeLine feeLine(String feeType, String feeName, Long scId, BigDecimal amount,
|
||||||
|
String currency, String roundMode, String remark)
|
||||||
|
{
|
||||||
|
TmsPriceQuoteFeeLine line = new TmsPriceQuoteFeeLine();
|
||||||
|
line.setFeeType(feeType);
|
||||||
|
line.setFeeName(feeName);
|
||||||
|
line.setScId(scId == null ? 0L : scId);
|
||||||
|
line.setAmount(nvl(amount));
|
||||||
|
line.setCurrency(currency);
|
||||||
|
line.setRoundMode(roundMode);
|
||||||
|
line.setRemark(remark);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String feeNameOf(String feeType)
|
||||||
|
{
|
||||||
|
if (FEE_BASIC.equals(feeType))
|
||||||
|
{
|
||||||
|
return "\u57fa\u7840\u8fd0\u8d39";
|
||||||
|
}
|
||||||
|
if (FEE_REG.equals(feeType))
|
||||||
|
{
|
||||||
|
return "\u6302\u53f7\u8d39";
|
||||||
|
}
|
||||||
|
if (FEE_SUR.equals(feeType))
|
||||||
|
{
|
||||||
|
return "\u9644\u52a0\u8d39";
|
||||||
|
}
|
||||||
|
return feeType;
|
||||||
|
}
|
||||||
|
|
||||||
private static String normalizeRoundMode(String roundMode)
|
private static String normalizeRoundMode(String roundMode)
|
||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(roundMode))
|
if (StringUtils.isEmpty(roundMode))
|
||||||
@@ -297,23 +724,23 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
{
|
{
|
||||||
if (request == null)
|
if (request == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("request body is required");
|
throw new ServiceException(MessageUtils.message("price.quote.requestBodyRequired"));
|
||||||
}
|
}
|
||||||
if (request.getCustomerId() == null)
|
if (request.getCustomerId() == null)
|
||||||
{
|
{
|
||||||
throw new ServiceException("customerId is required");
|
throw new ServiceException(MessageUtils.message("price.quote.customerIdRequired"));
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(request.getProductCode()))
|
if (StringUtils.isEmpty(request.getProductCode()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("productCode is required");
|
throw new ServiceException(MessageUtils.message("price.quote.productCodeRequired"));
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(request.getCountryCode()) && StringUtils.isEmpty(request.getCountryName()))
|
if (StringUtils.isEmpty(request.getCountryCode()) && StringUtils.isEmpty(request.getCountryName()))
|
||||||
{
|
{
|
||||||
throw new ServiceException("countryCode or countryName is required");
|
throw new ServiceException(MessageUtils.message("price.quote.countryRequired"));
|
||||||
}
|
}
|
||||||
if (request.getWeight() == null || request.getWeight().compareTo(BigDecimal.ZERO) <= 0)
|
if (request.getWeight() == null || request.getWeight().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
{
|
{
|
||||||
throw new ServiceException("weight must be > 0");
|
throw new ServiceException(MessageUtils.message("price.quote.weightInvalid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +748,15 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(bizDate))
|
if (StringUtils.isEmpty(bizDate))
|
||||||
{
|
{
|
||||||
return new Date();
|
try
|
||||||
|
{
|
||||||
|
return new SimpleDateFormat("yyyy-MM-dd").parse(
|
||||||
|
new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
|
||||||
|
}
|
||||||
|
catch (ParseException e)
|
||||||
|
{
|
||||||
|
return new Date();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -329,7 +764,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
}
|
}
|
||||||
catch (ParseException e)
|
catch (ParseException e)
|
||||||
{
|
{
|
||||||
throw new ServiceException("bizDate format must be yyyy-MM-dd");
|
throw new ServiceException(MessageUtils.message("price.quote.bizDateInvalid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,6 +779,16 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
|||||||
return StringUtils.isEmpty(s) ? null : s.trim();
|
return StringUtils.isEmpty(s) ? null : s.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String nvlStr(String s)
|
||||||
|
{
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigDecimal nvl(BigDecimal v)
|
||||||
|
{
|
||||||
|
return v == null ? BigDecimal.ZERO : v;
|
||||||
|
}
|
||||||
|
|
||||||
private static String formatWeightLabel(BigDecimal min, BigDecimal max)
|
private static String formatWeightLabel(BigDecimal min, BigDecimal max)
|
||||||
{
|
{
|
||||||
return strip(min) + "\u2264W\u2264" + strip(max);
|
return strip(min) + "\u2264W\u2264" + strip(max);
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.price.domain.XtdZoneCountry;
|
import com.ruoyi.price.domain.XtdZoneCountry;
|
||||||
import com.ruoyi.price.domain.XtdZoneDivision;
|
import com.ruoyi.price.domain.XtdZoneDivision;
|
||||||
@@ -78,7 +79,7 @@ public class XtdZoneServiceImpl implements IXtdZoneService
|
|||||||
{
|
{
|
||||||
XtdZoneScheme scheme = schemeMapper.selectXtdZoneSchemeById(zsId);
|
XtdZoneScheme scheme = schemeMapper.selectXtdZoneSchemeById(zsId);
|
||||||
String name = scheme != null ? scheme.getZsName() : String.valueOf(zsId);
|
String name = scheme != null ? scheme.getZsName() : String.valueOf(zsId);
|
||||||
throw new ServiceException("分区方案【" + name + "】存在区划明细,不允许删除");
|
throw new ServiceException(MessageUtils.message("price.zone.delete.hasDivision", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return schemeMapper.deleteXtdZoneSchemeByIds(zsIds);
|
return schemeMapper.deleteXtdZoneSchemeByIds(zsIds);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.ruoyi.price.mapper.PrcQuoteFeeMapper">
|
||||||
|
|
||||||
|
<resultMap type="PrcQuoteFee" id="PrcQuoteFeeResult">
|
||||||
|
<id property="feeId" column="fee_id" />
|
||||||
|
<result property="quoteId" column="quote_id" />
|
||||||
|
<result property="feeType" column="fee_type" />
|
||||||
|
<result property="feeName" column="fee_name" />
|
||||||
|
<result property="scId" column="sc_id" />
|
||||||
|
<result property="amount" column="amount" />
|
||||||
|
<result property="currency" column="currency" />
|
||||||
|
<result property="roundMode" column="round_mode" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
<result property="createdAt" column="created_at" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectByQuoteId" parameterType="Long" resultMap="PrcQuoteFeeResult">
|
||||||
|
select fee_id, quote_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, created_at
|
||||||
|
from prc_quote_fee
|
||||||
|
where quote_id = #{quoteId}
|
||||||
|
order by field(fee_type, 'BASIC_FREIGHT', 'REGISTRATION_FEE', 'SURCHARGE'), fee_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertPrcQuoteFee" parameterType="PrcQuoteFee" useGeneratedKeys="true" keyProperty="feeId">
|
||||||
|
insert into prc_quote_fee (
|
||||||
|
quote_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, created_at
|
||||||
|
) values (
|
||||||
|
#{quoteId}, #{feeType}, #{feeName}, #{scId}, #{amount}, #{currency}, #{roundMode}, #{remark}, sysdate()
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<insert id="insertBatch" parameterType="java.util.List">
|
||||||
|
insert into prc_quote_fee (
|
||||||
|
quote_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark, created_at
|
||||||
|
) values
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.quoteId}, #{item.feeType}, #{item.feeName}, #{item.scId}, #{item.amount},
|
||||||
|
#{item.currency}, #{item.roundMode}, #{item.remark}, sysdate())
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="deleteByQuoteId" parameterType="Long">
|
||||||
|
delete from prc_quote_fee where quote_id = #{quoteId}
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.ruoyi.price.mapper.PrcQuoteMapper">
|
||||||
|
|
||||||
|
<resultMap type="PrcQuote" id="PrcQuoteResult">
|
||||||
|
<id property="quoteId" column="quote_id" />
|
||||||
|
<result property="quoteKey" column="quote_key" />
|
||||||
|
<result property="customerId" column="customer_id" />
|
||||||
|
<result property="productCode" column="product_code" />
|
||||||
|
<result property="productName" column="product_name" />
|
||||||
|
<result property="countryCode" column="country_code" />
|
||||||
|
<result property="countryName" column="country_name" />
|
||||||
|
<result property="weight" column="weight" />
|
||||||
|
<result property="bizDate" column="biz_date" />
|
||||||
|
<result property="tmsId" column="tms_id" />
|
||||||
|
<result property="matchSource" column="match_source" />
|
||||||
|
<result property="matchMessage" column="match_message" />
|
||||||
|
<result property="priceSetId" column="price_set_id" />
|
||||||
|
<result property="priceSetCode" column="price_set_code" />
|
||||||
|
<result property="priceSetName" column="price_set_name" />
|
||||||
|
<result property="setType" column="set_type" />
|
||||||
|
<result property="bindPriority" column="bind_priority" />
|
||||||
|
<result property="customerPricesetId" column="customer_priceset_id" />
|
||||||
|
<result property="productCountryId" column="product_country_id" />
|
||||||
|
<result property="weightMin" column="weight_min" />
|
||||||
|
<result property="weightMax" column="weight_max" />
|
||||||
|
<result property="weightLabel" column="weight_label" />
|
||||||
|
<result property="effectiveDate" column="effective_date" />
|
||||||
|
<result property="chargeWeight" column="charge_weight" />
|
||||||
|
<result property="freightRate" column="freight_rate" />
|
||||||
|
<result property="baseFreightRate" column="base_freight_rate" />
|
||||||
|
<result property="discountPercent" column="discount_percent" />
|
||||||
|
<result property="registrationFee" column="registration_fee" />
|
||||||
|
<result property="freightAmount" column="freight_amount" />
|
||||||
|
<result property="surchargeAmount" column="surcharge_amount" />
|
||||||
|
<result property="payableTotal" column="payable_total" />
|
||||||
|
<result property="currency" column="currency" />
|
||||||
|
<result property="roundMode" column="round_mode" />
|
||||||
|
<result property="createBy" column="created_by" />
|
||||||
|
<result property="createTime" column="created_at" />
|
||||||
|
<result property="updateBy" column="updated_by" />
|
||||||
|
<result property="updateTime" column="updated_at" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectPrcQuoteVo">
|
||||||
|
select quote_id, quote_key, customer_id, product_code, product_name,
|
||||||
|
country_code, country_name, weight, biz_date, tms_id,
|
||||||
|
match_source, match_message, price_set_id, price_set_code, price_set_name,
|
||||||
|
set_type, bind_priority, customer_priceset_id, product_country_id,
|
||||||
|
weight_min, weight_max, weight_label, effective_date, charge_weight,
|
||||||
|
freight_rate, base_freight_rate, discount_percent, registration_fee,
|
||||||
|
freight_amount, surcharge_amount, payable_total, currency, round_mode,
|
||||||
|
created_by, created_at, updated_by, updated_at
|
||||||
|
from prc_quote
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectPrcQuoteByKey" parameterType="String" resultMap="PrcQuoteResult">
|
||||||
|
<include refid="selectPrcQuoteVo"/>
|
||||||
|
where quote_key = #{quoteKey}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPrcQuoteById" parameterType="Long" resultMap="PrcQuoteResult">
|
||||||
|
<include refid="selectPrcQuoteVo"/>
|
||||||
|
where quote_id = #{quoteId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertPrcQuote" parameterType="PrcQuote" useGeneratedKeys="true" keyProperty="quoteId">
|
||||||
|
insert into prc_quote (
|
||||||
|
quote_key, customer_id, product_code, product_name,
|
||||||
|
country_code, country_name, weight, biz_date, tms_id,
|
||||||
|
match_source, match_message, price_set_id, price_set_code, price_set_name,
|
||||||
|
set_type, bind_priority, customer_priceset_id, product_country_id,
|
||||||
|
weight_min, weight_max, weight_label, effective_date, charge_weight,
|
||||||
|
freight_rate, base_freight_rate, discount_percent, registration_fee,
|
||||||
|
freight_amount, surcharge_amount, payable_total, currency, round_mode,
|
||||||
|
created_at
|
||||||
|
) values (
|
||||||
|
#{quoteKey}, #{customerId}, #{productCode}, #{productName},
|
||||||
|
#{countryCode}, #{countryName}, #{weight}, #{bizDate}, #{tmsId},
|
||||||
|
#{matchSource}, #{matchMessage}, #{priceSetId}, #{priceSetCode}, #{priceSetName},
|
||||||
|
#{setType}, #{bindPriority}, #{customerPricesetId}, #{productCountryId},
|
||||||
|
#{weightMin}, #{weightMax}, #{weightLabel}, #{effectiveDate}, #{chargeWeight},
|
||||||
|
#{freightRate}, #{baseFreightRate}, #{discountPercent}, #{registrationFee},
|
||||||
|
#{freightAmount}, #{surchargeAmount}, #{payableTotal}, #{currency}, #{roundMode},
|
||||||
|
sysdate()
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateQuoteAmounts">
|
||||||
|
update prc_quote
|
||||||
|
set surcharge_amount = #{surchargeAmount},
|
||||||
|
payable_total = #{payableTotal},
|
||||||
|
updated_at = sysdate()
|
||||||
|
where quote_id = #{quoteId}
|
||||||
|
</update>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- BMS 下单计费试算落库
|
||||||
|
-- charset: utf8mb4
|
||||||
|
-- prc_quote:相同试算参数唯一,命中则直接返回
|
||||||
|
-- prc_quote_fee:运费/挂号费各一行,附加费按具体名称各一行
|
||||||
|
-- ----------------------------
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS prc_quote (
|
||||||
|
quote_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '试算ID',
|
||||||
|
quote_key CHAR(32) NOT NULL COMMENT '试算指纹 MD5(客户|产品|国家|重量|计费日期)',
|
||||||
|
customer_id BIGINT(20) NOT NULL COMMENT '客户ID',
|
||||||
|
product_code VARCHAR(20) NOT NULL COMMENT '产品代码',
|
||||||
|
product_name VARCHAR(100) DEFAULT NULL COMMENT '产品名称',
|
||||||
|
country_code CHAR(2) DEFAULT NULL COMMENT '国家二字码',
|
||||||
|
country_name VARCHAR(50) DEFAULT NULL COMMENT '国家名称',
|
||||||
|
weight DECIMAL(10,3) NOT NULL COMMENT '计费重量KG',
|
||||||
|
biz_date DATE NOT NULL COMMENT '计费日期',
|
||||||
|
tms_id BIGINT(20) DEFAULT NULL COMMENT '多租户系统ID',
|
||||||
|
match_source VARCHAR(30) DEFAULT NULL COMMENT '命中来源 PACKAGE_FIXED/套餐固定价 PACKAGE_DISCOUNT/套餐折扣 PRODUCT_DEFAULT/产品默认价',
|
||||||
|
match_message VARCHAR(200) DEFAULT NULL COMMENT '命中说明',
|
||||||
|
price_set_id BIGINT(20) DEFAULT NULL COMMENT '价格套餐ID',
|
||||||
|
price_set_code VARCHAR(20) DEFAULT NULL COMMENT '套餐编码',
|
||||||
|
price_set_name VARCHAR(50) DEFAULT NULL COMMENT '套餐名称',
|
||||||
|
set_type VARCHAR(10) DEFAULT NULL COMMENT '计价类型 FIXED=固定价 DISCOUNT=折扣',
|
||||||
|
bind_priority INT(11) DEFAULT NULL COMMENT '客户套餐优先级',
|
||||||
|
customer_priceset_id BIGINT(20) DEFAULT NULL COMMENT '客户套餐绑定ID',
|
||||||
|
product_country_id BIGINT(20) DEFAULT NULL COMMENT '关联 bms_product_country.detail_id',
|
||||||
|
weight_min DECIMAL(10,4) DEFAULT NULL COMMENT '重量段下限',
|
||||||
|
weight_max DECIMAL(10,4) DEFAULT NULL COMMENT '重量段上限',
|
||||||
|
weight_label VARCHAR(50) DEFAULT NULL COMMENT '重量段展示',
|
||||||
|
effective_date DATE DEFAULT NULL COMMENT '产品明细生效日',
|
||||||
|
charge_weight DECIMAL(10,3) DEFAULT NULL COMMENT '计费重量',
|
||||||
|
freight_rate DECIMAL(10,4) DEFAULT NULL COMMENT '运费单价',
|
||||||
|
base_freight_rate DECIMAL(10,4) DEFAULT NULL COMMENT '折扣前单价',
|
||||||
|
discount_percent DECIMAL(5,2) DEFAULT NULL COMMENT '折扣百分数',
|
||||||
|
registration_fee DECIMAL(10,2) DEFAULT NULL COMMENT '挂号费',
|
||||||
|
freight_amount DECIMAL(10,2) DEFAULT NULL COMMENT '运费金额',
|
||||||
|
surcharge_amount DECIMAL(10,2) DEFAULT NULL COMMENT '附加费合计',
|
||||||
|
payable_total DECIMAL(10,2) DEFAULT NULL COMMENT '应付总额',
|
||||||
|
currency VARCHAR(10) NOT NULL DEFAULT 'RMB' COMMENT '币种',
|
||||||
|
round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||||
|
created_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
created_at DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||||
|
updated_by VARCHAR(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
updated_at DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (quote_id),
|
||||||
|
UNIQUE KEY uk_quote_key (quote_key),
|
||||||
|
INDEX idx_quote_customer (customer_id, biz_date),
|
||||||
|
INDEX idx_quote_product (product_code, country_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计费试算主表';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS prc_quote_fee (
|
||||||
|
fee_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '费用明细ID',
|
||||||
|
quote_id BIGINT(20) NOT NULL COMMENT '试算ID',
|
||||||
|
fee_type VARCHAR(30) NOT NULL COMMENT '费用类型(字典bms_fee_type)',
|
||||||
|
fee_name VARCHAR(100) DEFAULT NULL COMMENT '费用名称(附加费取配置名称)',
|
||||||
|
sc_id BIGINT(20) NOT NULL DEFAULT 0 COMMENT '附加费配置ID,运费/挂号费为0',
|
||||||
|
amount DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '金额',
|
||||||
|
currency VARCHAR(10) NOT NULL DEFAULT 'RMB' COMMENT '币种',
|
||||||
|
round_mode VARCHAR(20) DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||||
|
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||||
|
created_at DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||||
|
PRIMARY KEY (fee_id),
|
||||||
|
UNIQUE KEY uk_quote_fee_item (quote_id, fee_type, sc_id),
|
||||||
|
INDEX idx_quote_fee_type (fee_type),
|
||||||
|
CONSTRAINT fk_quote_fee_quote FOREIGN KEY (quote_id) REFERENCES prc_quote (quote_id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计费试算费用明细-附加费按具体名称一行';
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- prc_quote_fee:附加费按具体名称多行
|
||||||
|
-- 原 uk_quote_fee_type(quote_id, fee_type) 被外键 fk_quote_fee_quote 依赖,
|
||||||
|
-- 必须先补 quote_id 索引再删旧唯一键
|
||||||
|
-- ----------------------------
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
-- 1) 外键所需的 quote_id 索引(与旧唯一键分离)
|
||||||
|
ALTER TABLE prc_quote_fee ADD INDEX idx_quote_fee_quote_id (quote_id);
|
||||||
|
|
||||||
|
-- 2) 附加费配置ID
|
||||||
|
ALTER TABLE prc_quote_fee ADD COLUMN sc_id BIGINT(20) NOT NULL DEFAULT 0 COMMENT '附加费配置ID,运费/挂号费为0' AFTER fee_name;
|
||||||
|
|
||||||
|
-- 3) 新唯一:同一试算可有多条附加费
|
||||||
|
ALTER TABLE prc_quote_fee ADD UNIQUE KEY uk_quote_fee_item (quote_id, fee_type, sc_id);
|
||||||
|
|
||||||
|
-- 4) 现可安全删掉旧唯一键
|
||||||
|
ALTER TABLE prc_quote_fee DROP INDEX uk_quote_fee_type;
|
||||||
|
|
||||||
|
-- 5) 更新费用名称注释
|
||||||
|
ALTER TABLE prc_quote_fee MODIFY COLUMN fee_name VARCHAR(100) DEFAULT NULL COMMENT '费用名称(附加费取配置名称)';
|
||||||
@@ -102,12 +102,43 @@
|
|||||||
<el-descriptions-item label="取值方式">{{ roundModeLabel(result.roundMode) }}</el-descriptions-item>
|
<el-descriptions-item label="取值方式">{{ roundModeLabel(result.roundMode) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="运费金额">{{ formatMoney(result.freightAmount) }}</el-descriptions-item>
|
<el-descriptions-item label="运费金额">{{ formatMoney(result.freightAmount) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="挂号费">{{ formatMoney(result.registrationFee) }}</el-descriptions-item>
|
<el-descriptions-item label="挂号费">{{ formatMoney(result.registrationFee) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="附加费">{{ formatMoney(result.surchargeAmount) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="应付总额">
|
<el-descriptions-item label="应付总额">
|
||||||
<span class="payable">{{ formatMoney(result.payableTotal) }}</span>
|
<span class="payable">{{ formatMoney(result.payableTotal) }}</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="试算来源">
|
||||||
|
<el-tag v-if="result.fromCache" size="small" type="success">命中历史试算</el-tag>
|
||||||
|
<el-tag v-else size="small" type="primary">实时计算并落库</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="命中说明" :span="2">{{ result.matchMessage || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="命中说明" :span="2">{{ result.matchMessage || '-' }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<div class="formula-tip">计算公式:运费金额 = 运费单价 × 计费重量;应付总额 = 运费金额 + 挂号费</div>
|
<el-table
|
||||||
|
v-if="result.feeDetails && result.feeDetails.length"
|
||||||
|
:data="result.feeDetails"
|
||||||
|
border
|
||||||
|
size="small"
|
||||||
|
class="fee-table"
|
||||||
|
>
|
||||||
|
<el-table-column label="费用名称" min-width="140" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ displayFeeName(row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="费用类型" width="110" align="center">
|
||||||
|
<template #default="{ row }">{{ feeTypeLabel(row.feeType) || row.feeType }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="金额" min-width="120" align="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="{ payable: row.feeType === 'SURCHARGE' && Number(row.amount) > 0 }">{{ formatMoney(row.amount) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="币种" width="80" align="center" prop="currency" />
|
||||||
|
<el-table-column label="取值方式" width="110" align="center">
|
||||||
|
<template #default="{ row }">{{ roundModeLabel(row.roundMode) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="说明" min-width="180" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ row.remark || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="formula-tip">计算公式:运费金额 = 运费单价 × 计费重量;应付总额 = 运费金额 + 挂号费 + 附加费。相同试算参数直接返回已落库费用明细。</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -120,6 +151,7 @@ import { quoteBilling } from '@/api/price/billing'
|
|||||||
import { listProduct, listProductCountries } from '@/api/price/product'
|
import { listProduct, listProductCountries } from '@/api/price/product'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
|
const { bms_fee_type } = useDict('bms_fee_type')
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const productOptions = ref([])
|
const productOptions = ref([])
|
||||||
@@ -257,6 +289,33 @@ function roundModeLabel(mode) {
|
|||||||
return map[mode] || mode || '四舍五入'
|
return map[mode] || mode || '四舍五入'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function feeTypeLabel(type) {
|
||||||
|
const found = (bms_fee_type.value || []).find(d => d.value === type)
|
||||||
|
return found ? found.label : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayFeeName(row) {
|
||||||
|
const typeLabel = feeTypeLabel(row.feeType)
|
||||||
|
const name = String(row.feeName || row.fee_name || '').trim()
|
||||||
|
const isSurcharge = row.feeType === 'SURCHARGE' || row.feeType === '附加费'
|
||||||
|
if (isSurcharge) {
|
||||||
|
if (name && name !== typeLabel && name !== 'SURCHARGE' && name !== '附加费') {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
const fromRemark = specificNameFromRemark(row.remark)
|
||||||
|
if (fromRemark) {
|
||||||
|
return fromRemark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name || typeLabel || row.feeType || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function specificNameFromRemark(remark) {
|
||||||
|
if (!remark) return ''
|
||||||
|
const first = String(remark).split(';')[0].trim()
|
||||||
|
return first.replace(/\s+-?\d+(\.\d+)?$/, '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
function sourceTagType(source) {
|
function sourceTagType(source) {
|
||||||
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
||||||
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
||||||
@@ -284,4 +343,7 @@ loadProducts()
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
.billing-quote .fee-table {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user