修改产品功能

This commit is contained in:
wuweihua
2026-09-28 21:43:39 +08:00
parent b247bc2fd2
commit 0ad90f6183
32 changed files with 339 additions and 660 deletions
@@ -7,7 +7,7 @@ import com.bms.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** BMS产品主表 bms_product */
/** BMS产品主表 product */
@Data
@EqualsAndHashCode(callSuper = true)
public class BmsProduct extends BaseEntity
@@ -6,19 +6,15 @@ import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/** 产品国家服务明细 bms_product_country */
/** 产品国家服务明细 product_country */
@Data
public class BmsProductCountry implements Serializable
{
private static final long serialVersionUID = 1L;
private Long detailId;
/** FK to bms_product.product_id (one country version belongs to one product row) */
/** FK to product.product_id */
private Long productId;
/** FK to bms_product_price_version.ppv_id */
private Long ppvId;
/** Display only: joined from bms_product_price_version.version_no */
private String versionNo;
private String productCode;
private String countryName;
private String countryCode;
@@ -1,32 +0,0 @@
package com.bms.price.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.bms.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/** Product base price version bms_product_price_version */
@Data
@EqualsAndHashCode(callSuper = true)
public class BmsProductPriceVersion extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long ppvId;
private String productCode;
/** Linked bms_product.product_id for this version row */
private Long productId;
/** V1 / V2 */
private String versionNo;
private Integer versionSeq;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date effectiveDate;
/** null = open; set to next version effective_date when that version starts */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date expireDate;
/** 0 pending / 1 active / 2 expired */
private String status;
/** query only: product count for this effective_date option */
private Integer productCount;
}
@@ -30,16 +30,16 @@ public class PrcPriceSetDetail implements Serializable
private Long zdId;
private Long wsdId;
private String weightLabel;
/** KG inclusive, same as bms_product_country */
/** KG inclusive, same as product_country */
private BigDecimal weightMin;
private BigDecimal weightMax;
/** bms_product_country.detail_id */
/** product_country.detail_id */
private Long productCountryId;
private BigDecimal fvValue;
private BigDecimal fvRegistrationFee;
/** product base freight from bms_product_country (display only) */
/** product base freight from product_country (display only) */
private BigDecimal baseFreightPrice;
/** product base registration from bms_product_country (display only) */
/** product base registration from product_country (display only) */
private BigDecimal baseRegistrationFee;
private BigDecimal discountValue;
/** UP / DOWN / HALF_UP */
@@ -52,6 +52,10 @@ public class PrcQuote extends BaseEntity
private BigDecimal freightAmount;
private BigDecimal surchargeAmount;
private BigDecimal payableTotal;
private BigDecimal productAmount;
private BigDecimal redlineAmount;
private BigDecimal costAmount;
private BigDecimal payTotal;
private String currency;
private String roundMode;
@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
/**
* 附加费与产品/明细关联 prc_surcharge_rel
* 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN bms_product_country),
* 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN product_country),
* 便于产品列表抽屉直接渲染。
*
* 语义:
@@ -44,7 +44,7 @@ public class TmsPriceQuoteResult implements Serializable
private BigDecimal weightMax;
private String weightLabel;
private Date bizDate;
/** bms_product_country.effective_date */
/** product_country.effective_date */
private Date effectiveDate;
private BigDecimal weight;
@@ -56,6 +56,12 @@ public class TmsPriceQuoteResult implements Serializable
private BigDecimal freightAmount;
/** 附加费合计(按产品关联命中后汇总) */
private BigDecimal surchargeAmount;
/** product catalog total: base freight*weight + base registration (no package surcharge) */
private BigDecimal productAmount;
/** redline total reserved */
private BigDecimal redlineAmount;
/** cost total (currently = product catalog amount) */
private BigDecimal costAmount;
/** receivable total (order bill) */
private BigDecimal incomeTotal;
/** payable total (order bill); also quote payable */
@@ -23,9 +23,6 @@ public interface BmsProductCountryMapper
/** Import overlay: update price/zone/attrs on matched country row */
public int updateBmsProductCountryImport(BmsProductCountry country);
/** Bind legacy open row to a version */
public int updatePpvId(@Param("detailId") Long detailId, @Param("ppvId") Long ppvId);
public int deleteByProductId(Long productId);
public int deleteByProductCode(String productCode);
@@ -53,22 +50,9 @@ public interface BmsProductCountryMapper
/** Open country rows (expire_date is null) for product */
public List<BmsProductCountry> selectOpenByProductCode(@Param("productCode") String productCode);
/** Country rows under a price version */
public List<BmsProductCountry> selectByPpvId(@Param("ppvId") Long ppvId);
/** Copy open/source version rows into a new version (new ppv_id / product_id / effective_date) */
public int copyCountriesFromVersion(
@Param("srcPpvId") Long srcPpvId,
@Param("newPpvId") Long newPpvId,
@Param("productId") Long productId,
/** Copy previously open rows into a new effective_date (caller must expire first) */
public int copyOpenCountries(
@Param("productCode") String productCode,
@Param("effectiveDate") Date effectiveDate,
@Param("status") String status);
/** Copy currently open rows (legacy / no ppv_id) into a new version */
public int copyOpenCountriesToVersion(
@Param("productCode") String productCode,
@Param("newPpvId") Long newPpvId,
@Param("productId") Long productId,
@Param("effectiveDate") Date effectiveDate,
@Param("status") String status);
@@ -1,38 +0,0 @@
package com.bms.price.mapper;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.bms.price.domain.BmsProductPriceVersion;
/**
* Product base price version
*/
public interface BmsProductPriceVersionMapper
{
public BmsProductPriceVersion selectBmsProductPriceVersionById(Long ppvId);
/** Open version (expire_date is null), prefer latest effective_date */
public BmsProductPriceVersion selectOpenByProductCode(@Param("productCode") String productCode);
public BmsProductPriceVersion selectByProductCodeAndEffectiveDate(
@Param("productCode") String productCode,
@Param("effectiveDate") Date effectiveDate);
public Integer selectMaxVersionSeq(@Param("productCode") String productCode);
/** Distinct effective_date options for package create dropdown (desc) */
public List<BmsProductPriceVersion> selectVersionOptions();
public int insertBmsProductPriceVersion(BmsProductPriceVersion version);
public int updateBmsProductPriceVersion(BmsProductPriceVersion version);
/** Close open versions: set expire_date = newEffectiveDate where effective_date &lt; newEffectiveDate */
public int expireOpenByProductCode(
@Param("productCode") String productCode,
@Param("newEffectiveDate") Date newEffectiveDate);
/** Recompute status from effective_date / expire_date vs today */
public int refreshStatusByDates();
}
@@ -42,23 +42,17 @@ public interface PrcPriceSetDetailMapper
/** Copy all details from src version into new version (new psd_id, same price_set_id) */
public int copyDetailsFromVersion(@Param("srcPsvId") Long srcPsvId, @Param("newPsvId") Long newPsvId);
/**
* FIXED: insert details from product country library.
* When baseVersionNo is set, pull that base-price version only;
* otherwise asOf window or openOnly fallback.
*/
/** FIXED: insert details from product country library (asOf window or openOnly). */
public int insertFixedDetailsFromLibrary(
@Param("priceSetId") Long priceSetId,
@Param("psvId") Long psvId,
@Param("asOf") Date asOf,
@Param("openOnly") boolean openOnly,
@Param("baseVersionNo") String baseVersionNo);
@Param("openOnly") boolean openOnly);
/** DISCOUNT: one detail row per product (optionally scoped to base version_no) */
/** DISCOUNT: one detail row per effective product */
public int insertDiscountDetailsFromLibrary(
@Param("priceSetId") Long priceSetId,
@Param("psvId") Long psvId,
@Param("baseVersionNo") String baseVersionNo);
@Param("psvId") Long psvId);
/** FIXED: product + country + zone + productCountryId / weight; optional psvId scopes to version */
public PrcPriceSetDetail selectFixedMatch(
@@ -18,5 +18,8 @@ public interface PrcQuoteMapper
public int updateQuoteAmounts(@Param("quoteId") String quoteId,
@Param("surchargeAmount") BigDecimal surchargeAmount,
@Param("payableTotal") BigDecimal payableTotal);
@Param("payableTotal") BigDecimal payableTotal,
@Param("productAmount") BigDecimal productAmount,
@Param("costAmount") BigDecimal costAmount,
@Param("payTotal") BigDecimal payTotal);
}
@@ -5,7 +5,6 @@ import java.util.List;
import org.springframework.web.multipart.MultipartFile;
import com.bms.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.domain.BmsProductPriceVersion;
/**
* 产品列表 服务层
@@ -56,8 +55,6 @@ public interface IBmsProductService
*/
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName) throws Exception;
/** Distinct base-price version options (by effective_date desc) for package create */
public List<BmsProductPriceVersion> selectPriceVersionOptions();
/**
* Parse Weishi B2C quote Excel into productCode -> country/zone/weight price rows (no persist).
@@ -35,11 +35,9 @@ import com.bms.common.utils.MessageUtils;
import com.bms.common.utils.StringUtils;
import com.bms.price.domain.BmsProduct;
import com.bms.price.domain.BmsProductCountry;
import com.bms.price.domain.BmsProductPriceVersion;
import com.bms.price.domain.PrcZone;
import com.bms.price.mapper.BmsProductCountryMapper;
import com.bms.price.mapper.BmsProductMapper;
import com.bms.price.mapper.BmsProductPriceVersionMapper;
import com.bms.price.mapper.PrcZoneMapper;
import com.bms.price.service.IBmsProductService;
import com.bms.price.service.IPrcSurchargeRelService;
@@ -77,8 +75,6 @@ public class BmsProductServiceImpl implements IBmsProductService
@Autowired
private BmsProductCountryMapper countryMapper;
@Autowired
private BmsProductPriceVersionMapper priceVersionMapper;
@Autowired
private BmsRegionMapper regionMapper;
@@ -103,7 +99,6 @@ public class BmsProductServiceImpl implements IBmsProductService
{
productMapper.refreshStatusByDates();
countryMapper.refreshStatusByDates();
priceVersionMapper.refreshStatusByDates();
if (product != null)
{
product.setStatus(normalizeStatusCode(product.getStatus()));
@@ -489,12 +484,6 @@ public class BmsProductServiceImpl implements IBmsProductService
}
}
@Override
public List<BmsProductPriceVersion> selectPriceVersionOptions()
{
List<BmsProductPriceVersion> list = priceVersionMapper.selectVersionOptions();
return list == null ? new ArrayList<>() : list;
}
/** @return int[]{added, skipped, countryRows} */
private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName,
@@ -532,7 +521,7 @@ public class BmsProductServiceImpl implements IBmsProductService
if (existing != null)
{
product.setProductId(existing.getProductId());
int countryRows = saveCountriesVersioned(product, countries);
int countryRows = saveCountries(product, countries);
return new int[] { 0, 1, countryRows };
}
@@ -551,19 +540,18 @@ public class BmsProductServiceImpl implements IBmsProductService
}
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
productMapper.refreshStatusByDates();
int countryRows = saveCountriesVersioned(product, countries);
int countryRows = saveCountries(product, countries);
return new int[] { 1, 0, countryRows };
}
/**
* Versioned country save:
* - require effective_date
* - newEff &gt; last open: expire previous version (expire_date = newEff), create new version,
* copy previous country rows, then match import (hit=update, miss=insert)
* - newEff equals last open: reuse open version, match update/insert (no full replace)
* Save country rows by effective_date:
* - newEff &gt; last open: expire previous open rows, copy them to newEff, then match import (hit=update, miss=insert)
* - newEff equals last open: match update/insert on open rows
* - newEff &lt; last open: reject
*/
private int saveCountriesVersioned(BmsProduct product, List<BmsProductCountry> countries)
private int saveCountries(BmsProduct product, List<BmsProductCountry> countries)
{
if (product == null || countries == null || countries.isEmpty())
{
@@ -581,72 +569,36 @@ public class BmsProductServiceImpl implements IBmsProductService
throw new ServiceException(MessageUtils.message("price.product.import.effEmpty"));
}
BmsProductPriceVersion openVer = priceVersionMapper.selectOpenByProductCode(productCode);
Date lastOpenEff = openVer != null ? toDateOnly(openVer.getEffectiveDate())
: toDateOnly(countryMapper.selectMaxOpenEffectiveDate(productCode));
Date lastOpenEff = toDateOnly(countryMapper.selectMaxOpenEffectiveDate(productCode));
if (lastOpenEff != null && newEff.before(lastOpenEff))
{
throw new ServiceException(MessageUtils.message("price.product.import.effTooEarly",
formatDate(newEff), formatDate(lastOpenEff)));
}
BmsProductPriceVersion targetVer;
boolean newVersionDay = lastOpenEff == null || newEff.after(lastOpenEff);
if (newVersionDay)
if (newVersionDay && lastOpenEff != null)
{
List<BmsProductCountry> prevSnapshot = null;
Long srcPpvId = openVer != null ? openVer.getPpvId() : null;
if (lastOpenEff != null)
{
if (srcPpvId != null)
{
prevSnapshot = null; // copy via SQL by ppv_id after expire
}
else
{
prevSnapshot = countryMapper.selectOpenByProductCode(productCode);
}
priceVersionMapper.expireOpenByProductCode(productCode, newEff);
countryMapper.expireOpenByProductCode(productCode, newEff);
}
targetVer = createPriceVersion(product, newEff);
String newStatus = resolveStoredStatus(newEff, null);
if (srcPpvId != null)
{
countryMapper.copyCountriesFromVersion(srcPpvId, targetVer.getPpvId(), productId,
productCode, newEff, newStatus);
}
else if (prevSnapshot != null && !prevSnapshot.isEmpty())
{
insertCountryCopies(prevSnapshot, targetVer.getPpvId(), productId, productCode, newEff, newStatus);
}
}
else
{
targetVer = priceVersionMapper.selectByProductCodeAndEffectiveDate(productCode, newEff);
if (targetVer == null)
{
targetVer = openVer != null ? openVer : createPriceVersion(product, newEff);
}
else if (targetVer.getProductId() == null && productId != null)
{
targetVer.setProductId(productId);
targetVer.setUpdateBy(product.getUpdateBy());
priceVersionMapper.updateBmsProductPriceVersion(targetVer);
}
assignOpenRowsToVersion(productCode, targetVer.getPpvId());
// Copy open rows first (effective_date < newEff), then expire the previous open set.
countryMapper.copyOpenCountries(productCode, productId, newEff, newStatus);
countryMapper.expireOpenByProductCode(productCode, newEff);
}
List<BmsProductCountry> baseRows = countryMapper.selectByPpvId(targetVer.getPpvId());
List<BmsProductCountry> baseRows = countryMapper.selectOpenByProductCode(productCode);
if (baseRows == null)
{
baseRows = new ArrayList<>();
}
// keep only rows for this effective date when same-day / after copy
Map<String, BmsProductCountry> byKey = new HashMap<>();
for (BmsProductCountry row : baseRows)
{
Date rowEff = toDateOnly(row.getEffectiveDate());
if (rowEff != null && !rowEff.equals(newEff))
{
continue;
}
byKey.put(countryMatchKey(row), row);
}
@@ -655,7 +607,6 @@ public class BmsProductServiceImpl implements IBmsProductService
{
incoming.setProductId(productId);
incoming.setProductCode(productCode);
incoming.setPpvId(targetVer.getPpvId());
incoming.setEffectiveDate(newEff);
incoming.setExpireDate(null);
incoming.setStatus(resolveStoredStatus(newEff, null));
@@ -667,7 +618,6 @@ public class BmsProductServiceImpl implements IBmsProductService
if (existing != null)
{
existing.setProductId(productId);
existing.setPpvId(targetVer.getPpvId());
existing.setCountryCode(incoming.getCountryCode());
existing.setZoneId(incoming.getZoneId());
existing.setZoneCode(incoming.getZoneCode());
@@ -698,10 +648,6 @@ public class BmsProductServiceImpl implements IBmsProductService
{
throw new ServiceException(MessageUtils.message("price.product.import.needFeeAlter"));
}
if (tip.contains("ppv_id") || tip.contains("bms_product_price_version"))
{
throw new ServiceException(MessageUtils.message("price.product.import.needVersionAlter"));
}
if (tip.contains("expire_date") || tip.contains("uk_product_country_weight")
|| tip.contains("uk_product_country_zone_weight"))
{
@@ -711,76 +657,9 @@ public class BmsProductServiceImpl implements IBmsProductService
}
}
countryMapper.refreshStatusByDates();
priceVersionMapper.refreshStatusByDates();
return touched;
}
private BmsProductPriceVersion createPriceVersion(BmsProduct product, Date effectiveDate)
{
Integer maxSeq = priceVersionMapper.selectMaxVersionSeq(product.getProductCode());
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
BmsProductPriceVersion ver = new BmsProductPriceVersion();
ver.setProductCode(product.getProductCode());
ver.setProductId(product.getProductId());
ver.setVersionSeq(Integer.valueOf(seq));
ver.setVersionNo("V" + seq);
ver.setEffectiveDate(effectiveDate);
ver.setExpireDate(null);
ver.setStatus(resolveStoredStatus(effectiveDate, null));
ver.setCreateBy(product.getCreateBy() != null ? product.getCreateBy() : product.getUpdateBy());
priceVersionMapper.insertBmsProductPriceVersion(ver);
return ver;
}
/** After expire, insert in-memory previous open rows into the new version */
private void insertCountryCopies(List<BmsProductCountry> prevSnapshot, Long newPpvId, Long productId,
String productCode, Date newEff, String status)
{
for (BmsProductCountry src : prevSnapshot)
{
BmsProductCountry copy = new BmsProductCountry();
copy.setProductId(productId);
copy.setPpvId(newPpvId);
copy.setProductCode(productCode);
copy.setCountryName(src.getCountryName());
copy.setCountryCode(src.getCountryCode());
copy.setZoneId(src.getZoneId());
copy.setZoneCode(src.getZoneCode());
copy.setZoneName(src.getZoneName());
copy.setWeightMin(src.getWeightMin());
copy.setWeightMax(src.getWeightMax());
copy.setFreightPrice(src.getFreightPrice());
copy.setRegistrationFee(src.getRegistrationFee());
copy.setSizeLimit(src.getSizeLimit());
copy.setLastMile(src.getLastMile());
copy.setLeadTime(src.getLeadTime());
copy.setEffectiveDate(newEff);
copy.setExpireDate(null);
copy.setStatus(status);
countryMapper.insertBmsProductCountry(copy);
}
}
private void assignOpenRowsToVersion(String productCode, Long ppvId)
{
if (ppvId == null)
{
return;
}
List<BmsProductCountry> openRows = countryMapper.selectOpenByProductCode(productCode);
if (openRows == null)
{
return;
}
for (BmsProductCountry row : openRows)
{
if (row.getPpvId() == null)
{
countryMapper.updatePpvId(row.getDetailId(), ppvId);
}
}
}
/** Match key: country + zone + weight band */
private String countryMatchKey(BmsProductCountry row)
{
@@ -912,7 +791,7 @@ public class BmsProductServiceImpl implements IBmsProductService
{
BmsProduct p = new BmsProduct();
p.setProductCode(productCode);
saveCountriesVersioned(p, countries);
saveCountries(p, countries);
}
private List<BmsProduct> parseCatalog(Sheet sheet)
@@ -1936,7 +1815,7 @@ public class BmsProductServiceImpl implements IBmsProductService
if (existing != null)
{
product.setProductId(existing.getProductId());
int countryRows = saveCountriesVersioned(product, countries);
int countryRows = saveCountries(product, countries);
return new int[] { 0, 1, countryRows };
}
applyProductStatus(product);
@@ -1954,7 +1833,7 @@ public class BmsProductServiceImpl implements IBmsProductService
}
productMapper.expireOpenByProductCode(product.getProductCode(), newEff, product.getProductId());
productMapper.refreshStatusByDates();
int countryRows = saveCountriesVersioned(product, countries);
int countryRows = saveCountries(product, countries);
return new int[] { 1, 0, countryRows };
}
@@ -187,7 +187,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
PrcIncomeBill incomeBill = incomeBillMapper.selectByBillId(bill.getBillId());
if (incomeBill == null)
{
incomeBill = persistIncomeBill(bill);
incomeBill = persistIncomeBill(bill, null);
}
PrcIncomeBillFee refundFee = new PrcIncomeBillFee();
@@ -854,7 +854,12 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
bill.setFreightAmount(result.getFreightAmount());
bill.setSurchargeAmount(result.getSurchargeAmount());
bill.setIncomeTotal(result.getPayableTotal());
bill.setPayTotal(result.getPayTotal() == null ? BigDecimal.ZERO : result.getPayTotal());
BigDecimal payTotal = result.getPayTotal();
if (payTotal == null)
{
payTotal = result.getProductAmount();
}
bill.setPayTotal(payTotal == null ? BigDecimal.ZERO : payTotal);
bill.setCurrency(result.getCurrency());
bill.setRoundMode(result.getRoundMode());
// Credit soft-fail before persist so insert/update stores arrears flag in one write.
@@ -883,7 +888,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
payBillFeeMapper.deleteByBillId(bill.getBillId());
}
PrcIncomeBill incomeBill = persistIncomeBill(bill);
PrcIncomeBill incomeBill = persistIncomeBill(bill, result);
List<PrcIncomeBillFee> fees = toFeeRows(bill.getBillId(),
incomeBill == null ? null : incomeBill.getIncomeBillId(), result);
if (!fees.isEmpty())
@@ -891,7 +896,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
incomeBillFeeMapper.insertBatch(fees);
}
orderBillMapper.refreshArBillStatusByBillIds(Arrays.asList(bill.getBillId()));
PrcPayBill payBill = persistPayBill(bill);
PrcPayBill payBill = persistPayBill(bill, result);
List<PrcPayBillFee> payFees = toPayFeeRows(bill.getBillId(),
payBill == null ? null : payBill.getPayBillId(), result);
if (!payFees.isEmpty())
@@ -1101,8 +1106,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
return fees;
}
/** 应收主表与订单计费一对一落库;当前写入销售价/应收合计,产品价/红线价预留。 */
private PrcIncomeBill persistIncomeBill(PrcOrderBill bill)
/** 应收主表与订单计费一对一落库;销售价=套餐计费,产品价=产品目录价;红线价预留。 */
private PrcIncomeBill persistIncomeBill(PrcOrderBill bill, TmsPriceQuoteResult result)
{
if (bill == null || StringUtils.isEmpty(bill.getBillId()))
{
@@ -1118,7 +1123,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
row.setCreateBy(bill.getCreateBy());
}
row.setCurrency(StringUtils.isEmpty(bill.getCurrency()) ? "CNY" : bill.getCurrency());
// 产品价/红线价预留
// 产品价=目录价;红线价预留
row.setProductAmount(result == null ? null : result.getProductAmount());
row.setSalesAmount(sales);
row.setIncomeTotal(sales);
row.setBelongSaler(bill.getBelongSaler());
@@ -1163,8 +1169,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
return fees;
}
/** 应付主表与订单计费一对一落库;当前写入成本价/应付合计,产品价/红线价预留。 */
private PrcPayBill persistPayBill(PrcOrderBill bill)
/** 应付主表与订单计费一对一落库;成本价/应付=产品目录价,同步写入 product_amount;红线价预留。 */
private PrcPayBill persistPayBill(PrcOrderBill bill, TmsPriceQuoteResult result)
{
if (bill == null || StringUtils.isEmpty(bill.getBillId()))
{
@@ -1180,7 +1186,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
row.setCreateBy(bill.getCreateBy());
}
row.setCurrency(StringUtils.isEmpty(bill.getCurrency()) ? "CNY" : bill.getCurrency());
// 产品价/红线价预留
// 产品价/成本价=目录价;红线价预留
BigDecimal product = result == null ? null : result.getProductAmount();
row.setProductAmount(product);
row.setCostAmount(pay);
row.setPayTotal(pay);
row.setBelongSaler(bill.getBelongSaler());
@@ -577,7 +577,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
for (PrcPriceSetDetail d : details)
{
boolean noCountry = StringUtils.isEmpty(d.getCountryCode()) && StringUtils.isEmpty(d.getCountryName());
// 运费单价 / 挂号费不参与完整性校验:原基础表 bms_product_country 可能没有值,
// 运费单价 / 挂号费不参与完整性校验:原基础表 product_country 可能没有值,
// 留空表示该条明细不覆盖该项,计费回退产品默认价(见 TmsPriceQuoteServiceImpl.tryFixed)。
if (StringUtils.isEmpty(d.getProductCode()) || noCountry
|| (d.getWsdId() == null && StringUtils.isEmpty(d.getWeightLabel())))
@@ -588,7 +588,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
if (details.isEmpty())
{
items.add(new PrcPriceSetCheckItem("FIXED 明细完整性", "PASS",
"\u65e0\u5957\u9910\u660e\u7ec6\uff1a\u8ba1\u8d39\u53d6 bms_product / bms_product_country \u9ed8\u8ba4\u8fd0\u8d39\u4e0e\u6302\u53f7\u8d39"));
"\u65e0\u5957\u9910\u660e\u7ec6\uff1a\u8ba1\u8d39\u53d6 product / product_country \u9ed8\u8ba4\u8fd0\u8d39\u4e0e\u6302\u53f7\u8d39"));
}
else if (incomplete > 0)
{
@@ -673,11 +673,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
if (TYPE_FIXED.equals(setType) && !details.isEmpty())
{
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u6709\u8986\u76d6\u660e\u7ec6\u65f6\u4f18\u5148\u53d6\u5957\u9910\u56fa\u5b9a\u4ef7\uff1b\u672a\u547d\u4e2d\u5219\u56de\u9000 bms_product_country"));
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u6709\u8986\u76d6\u660e\u7ec6\u65f6\u4f18\u5148\u53d6\u5957\u9910\u56fa\u5b9a\u4ef7\uff1b\u672a\u547d\u4e2d\u5219\u56de\u9000 product_country"));
}
else if (TYPE_FIXED.equals(setType))
{
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u65e0\u5957\u9910\u660e\u7ec6\uff0c\u5168\u90e8\u8d70 bms_product_country \u9ed8\u8ba4\u8fd0\u8d39/\u6302\u53f7\u8d39"));
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u65e0\u5957\u9910\u660e\u7ec6\uff0c\u5168\u90e8\u8d70 product_country \u9ed8\u8ba4\u8fd0\u8d39/\u6302\u53f7\u8d39"));
}
else if (TYPE_DISCOUNT.equals(setType))
{
@@ -1337,7 +1337,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
{
throw new ServiceException(MessageUtils.message("price.set.detail.weight.empty"));
}
// 运费单价 / 挂号费不做必填校验:原基础表 bms_product_country 可能没有值,
// 运费单价 / 挂号费不做必填校验:原基础表 product_country 可能没有值,
// 前端对应单元格会禁用(PriceEditDrawer.isBaseMissing)。
// 任一为空时该条明细不参与覆盖,计费按「未命中」回退产品默认价
// (见 TmsPriceQuoteServiceImpl.tryFixed)。
@@ -1466,7 +1466,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
}
/**
* Resolve / verify product_country_id from bms_product_country when missing.
* Resolve / verify product_country_id from product_country when missing.
*/
private void fillProductCountryId(PrcPriceSetDetail detail)
{
@@ -2100,17 +2100,17 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
if (TYPE_DISCOUNT.equals(set.getSetType()))
{
inserted = detailMapper.insertDiscountDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), null);
set.getPriceSetId(), version.getPsvId());
}
else
{
// Prefer currently effective open country rows; fallback asOf window if needed
inserted = detailMapper.insertFixedDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), null, true, null);
set.getPriceSetId(), version.getPsvId(), null, true);
if (inserted <= 0)
{
inserted = detailMapper.insertFixedDetailsFromLibrary(
set.getPriceSetId(), version.getPsvId(), new Date(), false, null);
set.getPriceSetId(), version.getPsvId(), new Date(), false);
}
}
if (inserted <= 0)
@@ -645,9 +645,35 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setCurrency("CNY");
r.setRoundMode(mode);
r.setFromCache(false);
fillProductAmountAndPay(r, base, weight, rm, mode);
return r;
}
/**
* Product catalog amount (product_amount) and payable cost (pay_total / payFeeDetails).
* Formula: product_country.freight_price * weight + registration_fee.
* Cost currently equals product catalog amount (no separate cost table yet).
*/
private void fillProductAmountAndPay(TmsPriceQuoteResult r, BmsProductCountry base,
BigDecimal weight, RoundingMode rm, String roundMode)
{
if (r == null || base == null || weight == null || base.getFreightPrice() == null)
{
return;
}
BigDecimal baseReg = base.getRegistrationFee() == null ? BigDecimal.ZERO : base.getRegistrationFee();
BigDecimal productFreight = base.getFreightPrice().multiply(weight).setScale(2, rm);
BigDecimal productAmount = productFreight.add(baseReg).setScale(2, rm);
r.setProductAmount(productAmount);
r.setCostAmount(productAmount);
r.setPayTotal(productAmount);
String currency = normalizeCurrency(r.getCurrency());
List<TmsPriceQuoteFeeLine> payLines = new ArrayList<TmsPriceQuoteFeeLine>();
payLines.add(feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, productFreight, currency, roundMode, null));
payLines.add(feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, baseReg, currency, roundMode, null));
r.setPayFeeDetails(payLines);
}
private void applySurchargesAndFees(TmsPriceQuoteResult result)
{
BigDecimal freightAmount = nvl(result.getFreightAmount());
@@ -691,6 +717,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
surchargeTotal = surchargeTotal.setScale(2, rm);
result.setSurchargeAmount(surchargeTotal);
result.setPayableTotal(freightAmount.add(registrationFee).add(surchargeTotal).setScale(2, rm));
result.setIncomeTotal(result.getPayableTotal());
result.setFeeDetails(lines);
}
@@ -839,6 +866,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
quote.setFreightAmount(result.getFreightAmount());
quote.setSurchargeAmount(result.getSurchargeAmount());
quote.setPayableTotal(result.getPayableTotal());
quote.setProductAmount(result.getProductAmount());
quote.setRedlineAmount(result.getRedlineAmount());
quote.setCostAmount(result.getCostAmount() != null ? result.getCostAmount() : result.getProductAmount());
quote.setPayTotal(result.getPayTotal() != null ? result.getPayTotal() : result.getProductAmount());
quote.setCurrency(result.getCurrency());
quote.setRoundMode(result.getRoundMode());
try
@@ -901,7 +932,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
{
quoteFeeMapper.insertBatch(fees);
}
quoteMapper.updateQuoteAmounts(quoteId, result.getSurchargeAmount(), result.getPayableTotal());
BigDecimal cost = result.getCostAmount() != null ? result.getCostAmount() : result.getProductAmount();
BigDecimal pay = result.getPayTotal() != null ? result.getPayTotal() : cost;
quoteMapper.updateQuoteAmounts(quoteId, result.getSurchargeAmount(), result.getPayableTotal(),
result.getProductAmount(), cost, pay);
}
private List<PrcQuoteFee> toFeeRows(String quoteId, TmsPriceQuoteResult result)
@@ -962,6 +996,11 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setFreightAmount(quote.getFreightAmount());
r.setSurchargeAmount(quote.getSurchargeAmount());
r.setPayableTotal(quote.getPayableTotal());
r.setProductAmount(quote.getProductAmount());
r.setRedlineAmount(quote.getRedlineAmount());
r.setCostAmount(quote.getCostAmount());
r.setPayTotal(quote.getPayTotal());
r.setIncomeTotal(quote.getPayableTotal());
r.setCurrency(normalizeCurrency(quote.getCurrency()));
r.setRoundMode(quote.getRoundMode());
List<TmsPriceQuoteFeeLine> lines = new ArrayList<TmsPriceQuoteFeeLine>();
@@ -1013,9 +1052,15 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
to.setFreightAmount(from.getFreightAmount());
to.setSurchargeAmount(from.getSurchargeAmount());
to.setPayableTotal(from.getPayableTotal());
to.setProductAmount(from.getProductAmount());
to.setRedlineAmount(from.getRedlineAmount());
to.setCostAmount(from.getCostAmount());
to.setIncomeTotal(from.getIncomeTotal());
to.setPayTotal(from.getPayTotal());
to.setCurrency(from.getCurrency());
to.setRoundMode(from.getRoundMode());
to.setFeeDetails(from.getFeeDetails());
to.setPayFeeDetails(from.getPayFeeDetails());
}
private String buildQuoteKey(TmsPriceQuoteRequest request, BigDecimal weight, Date bizDate)