Merge branch 'master' of http://git.lionquexpress.com/wuweihua/BMS
This commit is contained in:
@@ -33,6 +33,8 @@ public class BmsProduct extends BaseEntity
|
||||
public static final String STATUS_EXPIRED = "2";
|
||||
private String status;
|
||||
private String sourceSheet;
|
||||
/** FK prc_price_excel_file.pef_id */
|
||||
private Long pefId;
|
||||
private Long tmsId;
|
||||
private Integer countryCount;
|
||||
private Integer surchargeCount;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** Redline catalog main table redline_product */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BmsRedlineProduct extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
private String productName;
|
||||
private String serviceLevel;
|
||||
private String cargoAttr;
|
||||
private String destRegion;
|
||||
private String updateNote;
|
||||
/** TAX_INCLUDED / TAX_EXCLUDED */
|
||||
private String taxMode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
public static final String STATUS_PENDING = "0";
|
||||
public static final String STATUS_ACTIVE = "1";
|
||||
public static final String STATUS_EXPIRED = "2";
|
||||
private String status;
|
||||
private String sourceSheet;
|
||||
/** FK prc_price_excel_file.pef_id */
|
||||
private Long pefId;
|
||||
private Long tmsId;
|
||||
private Integer countryCount;
|
||||
private Integer surchargeCount;
|
||||
private List<BmsRedlineProductCountry> countries;
|
||||
/** query alias of status (0/1/2) */
|
||||
private String effectStatus;
|
||||
/** query: active = status in (0,1); expired = status 2 */
|
||||
private String statusGroup;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/** Redline country detail table redline_product_country */
|
||||
@Data
|
||||
public class BmsRedlineProductCountry implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long detailId;
|
||||
/** FK to product.product_id */
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
private String countryName;
|
||||
private String countryCode;
|
||||
/** prc_zone.zone_id; null when product country has no zone */
|
||||
private Long zoneId;
|
||||
/** e.g. Z1; null when no zone */
|
||||
private String zoneCode;
|
||||
/** e.g. Australia(Z1); null when no zone */
|
||||
private String zoneName;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
private BigDecimal freightPrice;
|
||||
private BigDecimal registrationFee;
|
||||
private String sizeLimit;
|
||||
private String lastMile;
|
||||
private String leadTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveDate;
|
||||
/** null=open; closed to next import effective_date */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireDate;
|
||||
/** 0 pending, 1 active, 2 expired */
|
||||
private String status;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
/** query only: true = expire_date is null */
|
||||
private Boolean openOnly;
|
||||
/** query alias of status (0/1/2) */
|
||||
private String effectStatus;
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Async product Excel import progress (Redis).
|
||||
*/
|
||||
public class BmsRedlineProductImportTask implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String STATUS_PENDING = "PENDING";
|
||||
public static final String STATUS_RUNNING = "RUNNING";
|
||||
public static final String STATUS_SUCCESS = "SUCCESS";
|
||||
public static final String STATUS_FAIL = "FAIL";
|
||||
|
||||
private String taskId;
|
||||
private String status;
|
||||
private int percent;
|
||||
private String message;
|
||||
private String error;
|
||||
|
||||
public String getTaskId()
|
||||
{
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public void setTaskId(String taskId)
|
||||
{
|
||||
this.taskId = taskId;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public int getPercent()
|
||||
{
|
||||
return percent;
|
||||
}
|
||||
|
||||
public void setPercent(int percent)
|
||||
{
|
||||
this.percent = percent;
|
||||
}
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Import quote Excel attachment: prc_price_excel_file
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PrcPriceExcelFile extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String TYPE_PRODUCT = "PRODUCT";
|
||||
public static final String TYPE_REDLINE = "REDLINE";
|
||||
public static final String TYPE_SALES = "SALES";
|
||||
|
||||
private Long pefId;
|
||||
/** PRODUCT / REDLINE / SALES */
|
||||
private String excelType;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date effectiveTime;
|
||||
private String fileName;
|
||||
private String filePath;
|
||||
private Long fileSize;
|
||||
private String contentType;
|
||||
private String fileExtension;
|
||||
private String ossId;
|
||||
/** Comma-separated sheet names (optional) */
|
||||
private String sheetNames;
|
||||
}
|
||||
@@ -48,4 +48,9 @@ public class PrcPriceSet extends BaseEntity
|
||||
private String pullFromLibrary;
|
||||
/** Y: even if version already has details, delete+re-pull from library */
|
||||
private String forcePullLibrary;
|
||||
/**
|
||||
* Request-only: stamp this Excel attachment id onto prc_price_set_product.pef_id
|
||||
* when saving IMPORT details (not a column on prc_price_set).
|
||||
*/
|
||||
private Long pefId;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ public class PrcPriceSetDetail implements Serializable
|
||||
/** null = open */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expireDate;
|
||||
private List<PrcPriceSetVersionSurcharge> surcharges;
|
||||
/** SALES rels for this detail (detail_id=psd_id, product_id=psp_id) */
|
||||
private List<PrcSurchargeRel> surcharges;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
@@ -37,6 +37,8 @@ public class PrcPriceSetProduct implements Serializable
|
||||
private Long sourceProductId;
|
||||
/** 来源Excel sheet */
|
||||
private String sourceSheet;
|
||||
/** 关联导入价表Excel附件 prc_price_excel_file.pef_id */
|
||||
private Long pefId;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
@@ -34,5 +34,4 @@ public class PrcPriceSetVersion extends BaseEntity
|
||||
|
||||
private List<PrcPriceSetDetail> details;
|
||||
private List<PrcPriceSetProduct> products;
|
||||
private List<PrcPriceSetVersionSurcharge> surcharges;
|
||||
}
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package com.bms.price.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.bms.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/** Price set version surcharge snapshot prc_price_set_version_surcharge */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PrcPriceSetVersionSurcharge extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long psvsId;
|
||||
private Long psvId;
|
||||
/** source prc_surcharge.sc_id */
|
||||
private Long scId;
|
||||
/** prc_price_set_detail.psd_id */
|
||||
private Long psdId;
|
||||
/** copied from detail */
|
||||
private String productCode;
|
||||
private String scName;
|
||||
private String feeType;
|
||||
/** FIXED / WEIGHT / RATE */
|
||||
private String chargeType;
|
||||
private BigDecimal chargeValue;
|
||||
private String currency;
|
||||
private String roundMode;
|
||||
private String countryCode;
|
||||
private String scNote;
|
||||
}
|
||||
@@ -6,13 +6,12 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 附加费与产品/明细关联 prc_surcharge_rel
|
||||
* 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN product_country),
|
||||
* 便于产品列表抽屉直接渲染。
|
||||
* 附加费与产品/明细/套餐关联 prc_surcharge_rel
|
||||
* 同时携带附加费展示字段(JOIN prc_surcharge)与明细展示字段(LEFT JOIN product_country,仅 PRODUCT/REDLINE)。
|
||||
*
|
||||
* 语义:
|
||||
* detail_id 为空 = 产品级附加费(作用于该产品全部明细)
|
||||
* detail_id 有值 = 明细级附加费(作用于该产品某个 国家×重量段 明细)
|
||||
* price_type 语义:
|
||||
* PRODUCT / REDLINE : product_id=product.product_id,detail_id=product_country.detail_id(空=产品全部明细)
|
||||
* SALES : product_id=prc_price_set_product.psp_id,detail_id=prc_price_set_detail.psd_id
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -20,11 +19,24 @@ public class PrcSurchargeRel extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 价格类型:产品价 */
|
||||
public static final String PRICE_TYPE_PRODUCT = "PRODUCT";
|
||||
/** 价格类型:销售价(套餐) */
|
||||
public static final String PRICE_TYPE_SALES = "SALES";
|
||||
/** 价格类型:红线价 */
|
||||
public static final String PRICE_TYPE_REDLINE = "REDLINE";
|
||||
|
||||
private Long relId;
|
||||
private Long scId;
|
||||
/** PRODUCT/REDLINE=product_id;SALES=psp_id */
|
||||
private Long productId;
|
||||
private String productCode;
|
||||
/** PRODUCT/REDLINE=product_country.detail_id;SALES=psd_id */
|
||||
private Long detailId;
|
||||
/** PRODUCT / SALES / REDLINE */
|
||||
private String priceType;
|
||||
/** 适用国家ISO2(空=全部),rel 表字段 */
|
||||
private String countryCode;
|
||||
|
||||
/** 附加费冗余展示字段 */
|
||||
private String scName;
|
||||
@@ -34,10 +46,9 @@ public class PrcSurchargeRel extends BaseEntity
|
||||
private String currency;
|
||||
private String roundMode;
|
||||
private String ssCode;
|
||||
private String countryCode;
|
||||
private String scNote;
|
||||
|
||||
/** 明细展示字段 */
|
||||
/** 明细展示字段(PRODUCT/REDLINE LEFT JOIN product_country) */
|
||||
private String countryName;
|
||||
private BigDecimal weightMin;
|
||||
private BigDecimal weightMax;
|
||||
|
||||
@@ -58,8 +58,10 @@ public class TmsPriceQuoteResult implements Serializable
|
||||
private BigDecimal surchargeAmount;
|
||||
/** product catalog total: base freight*weight + base registration (no package surcharge) */
|
||||
private BigDecimal productAmount;
|
||||
/** redline total reserved */
|
||||
/** redline total from redline_product_country (+ REDLINE surcharges) */
|
||||
private BigDecimal redlineAmount;
|
||||
/** matched redline_product_country.detail_id for REDLINE surcharge scope */
|
||||
private Long redlineCountryId;
|
||||
/** cost total (currently = product catalog amount) */
|
||||
private BigDecimal costAmount;
|
||||
/** receivable total (order bill) */
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.BmsRedlineProductCountry;
|
||||
|
||||
/**
|
||||
* 红线价国家明细 数据层
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
public interface BmsRedlineProductCountryMapper
|
||||
{
|
||||
public List<BmsRedlineProductCountry> selectBmsRedlineProductCountryList(BmsRedlineProductCountry country);
|
||||
|
||||
public int insertBmsRedlineProductCountry(BmsRedlineProductCountry country);
|
||||
|
||||
/** 按明细行主键更新可编辑字段(重量段/运费/挂号费/尺寸/尾程/时效/生效失效日期) */
|
||||
public int updateBmsRedlineProductCountry(BmsRedlineProductCountry country);
|
||||
|
||||
/** Import overlay: update price/zone/attrs on matched country row */
|
||||
public int updateBmsRedlineProductCountryImport(BmsRedlineProductCountry country);
|
||||
|
||||
public int deleteByProductId(Long productId);
|
||||
|
||||
public int deleteByProductCode(String productCode);
|
||||
|
||||
public int deleteByProductCodeAndEffectiveDate(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("effectiveDate") Date effectiveDate);
|
||||
|
||||
/** Max open (expire_date is null) effective_date for product */
|
||||
public Date selectMaxOpenEffectiveDate(@Param("productCode") String productCode);
|
||||
|
||||
/** Close open versions: set expire_date = newEffectiveDate where effective_date < newEffectiveDate */
|
||||
public int expireOpenByProductCode(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("newEffectiveDate") Date newEffectiveDate);
|
||||
|
||||
/**
|
||||
* Close open rows for one product + country where effective_date < newEffectiveDate.
|
||||
* Match by country_code when present, otherwise country_name.
|
||||
*/
|
||||
public int expireOpenByProductCodeAndCountry(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("newEffectiveDate") Date newEffectiveDate);
|
||||
|
||||
/** Recompute status from effective_date / expire_date vs today */
|
||||
public int refreshStatusByDates();
|
||||
|
||||
/** Replace same-day version only */
|
||||
public int deleteOpenByProductCodeAndEffectiveDate(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("effectiveDate") Date effectiveDate);
|
||||
|
||||
/**
|
||||
* Delete open rows for one product + country on a given effective_date (same-day replace).
|
||||
* Needed because unique key keeps expired same-day weight bands.
|
||||
*/
|
||||
public int deleteOpenByProductCodeAndCountryOnDate(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("effectiveDate") Date effectiveDate);
|
||||
|
||||
/** Open country rows (expire_date is null) for product */
|
||||
public List<BmsRedlineProductCountry> selectOpenByProductCode(@Param("productCode") String productCode);
|
||||
|
||||
/** Copy previously open rows into a new effective_date (caller must expire first) */
|
||||
public int copyOpenCountries(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("productId") Long productId,
|
||||
@Param("effectiveDate") Date effectiveDate,
|
||||
@Param("status") String status);
|
||||
|
||||
/**
|
||||
* Match by product + country + weight + bizDate (+ optional zoneCode).
|
||||
* When zoneCode is empty, prefer rows without zone.
|
||||
* When zoneCode is set, require matching zone_code.
|
||||
*/
|
||||
public BmsRedlineProductCountry selectMatchByProductCountryWeight(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("zoneCode") String zoneCode,
|
||||
@Param("weight") BigDecimal weight,
|
||||
@Param("bizDate") Date bizDate);
|
||||
|
||||
/** Count product-country rows that have a zone for this product+country+bizDate */
|
||||
public int countZonedByProductCountry(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("countryCode") String countryCode,
|
||||
@Param("countryName") String countryName,
|
||||
@Param("bizDate") Date bizDate);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.BmsRedlineProduct;
|
||||
|
||||
/**
|
||||
* 产品主表 数据层
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
public interface BmsRedlineProductMapper
|
||||
{
|
||||
public BmsRedlineProduct selectBmsRedlineProductById(Long productId);
|
||||
|
||||
/** Latest row by product_code (effective_date desc) */
|
||||
public BmsRedlineProduct selectBmsRedlineProductByCode(String productCode);
|
||||
|
||||
/** Unique business key: product_code + effective_date */
|
||||
public BmsRedlineProduct selectBmsRedlineProductByCodeAndDate(@Param("productCode") String productCode,
|
||||
@Param("effectiveDate") Date effectiveDate);
|
||||
|
||||
/** Product version effective on bizDate */
|
||||
public BmsRedlineProduct selectEffectiveBmsRedlineProductByCode(@Param("productCode") String productCode,
|
||||
@Param("bizDate") Date bizDate);
|
||||
|
||||
public List<BmsRedlineProduct> selectBmsRedlineProductList(BmsRedlineProduct product);
|
||||
|
||||
public int insertBmsRedlineProduct(BmsRedlineProduct product);
|
||||
|
||||
public int updateBmsRedlineProduct(BmsRedlineProduct product);
|
||||
|
||||
public int deleteBmsRedlineProductByIds(Long[] productIds);
|
||||
|
||||
/** Close previous open versions: expire_date = newEffectiveDate */
|
||||
public int expireOpenByProductCode(
|
||||
@Param("productCode") String productCode,
|
||||
@Param("newEffectiveDate") Date newEffectiveDate,
|
||||
@Param("excludeId") Long excludeId);
|
||||
|
||||
/** Recompute status from effective_date / expire_date vs today */
|
||||
public int refreshStatusByDates();
|
||||
}
|
||||
@@ -47,4 +47,7 @@ public interface PrcIncomeBillFeeMapper
|
||||
public int deleteByFeeId(String feeId);
|
||||
|
||||
public int deleteByBillId(String billId);
|
||||
|
||||
/** Rebill: delete auto fees only; keep manual_flag=1 rows. */
|
||||
public int deleteAutoFeesByBillId(String billId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
|
||||
/**
|
||||
* Import quote Excel attachment mapper
|
||||
*/
|
||||
public interface PrcPriceExcelFileMapper
|
||||
{
|
||||
public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId);
|
||||
|
||||
public List<PrcPriceExcelFile> selectPrcPriceExcelFileList(PrcPriceExcelFile query);
|
||||
|
||||
/** Distinct PRODUCT excel files linked by product.pef_id for this product_code */
|
||||
public List<PrcPriceExcelFile> selectByProductCode(@Param("productCode") String productCode);
|
||||
|
||||
public List<PrcPriceExcelFile> selectByRedlineProductCode(@Param("productCode") String productCode);
|
||||
|
||||
public int insertPrcPriceExcelFile(PrcPriceExcelFile row);
|
||||
|
||||
public int updatePrcPriceExcelFile(PrcPriceExcelFile row);
|
||||
|
||||
public int deletePrcPriceExcelFileByIds(Long[] pefIds);
|
||||
}
|
||||
@@ -34,4 +34,8 @@ public interface PrcPriceSetProductMapper
|
||||
|
||||
/** Bind detail.psp_id from set product by psv + product_code. */
|
||||
public int bindDetailPspByPsvId(@Param("psvId") Long psvId);
|
||||
|
||||
/** Stamp Excel attachment id on set products under this version (optional code filter). */
|
||||
public int updatePefIdByPsvId(@Param("psvId") Long psvId, @Param("pefId") Long pefId,
|
||||
@Param("productCodes") List<String> productCodes);
|
||||
}
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PrcPriceSetVersionSurcharge;
|
||||
|
||||
/**
|
||||
* Price set version surcharge snapshot
|
||||
*/
|
||||
public interface PrcPriceSetVersionSurchargeMapper
|
||||
{
|
||||
public PrcPriceSetVersionSurcharge selectPrcPriceSetVersionSurchargeById(Long psvsId);
|
||||
|
||||
public List<PrcPriceSetVersionSurcharge> selectPrcPriceSetVersionSurchargeList(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int insertPrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int updatePrcPriceSetVersionSurcharge(PrcPriceSetVersionSurcharge surcharge);
|
||||
|
||||
public int deletePrcPriceSetVersionSurchargeByIds(Long[] psvsIds);
|
||||
|
||||
public int deleteByPsvId(Long psvId);
|
||||
|
||||
public int deleteByPsdId(Long psdId);
|
||||
|
||||
public List<PrcPriceSetVersionSurcharge> selectByPsdIds(@Param("psdIds") List<Long> psdIds);
|
||||
|
||||
/** Snapshot all applicable product-rel surcharges for details under psvId (library create). */
|
||||
public int insertFromLibraryRels(@Param("psvId") Long psvId, @Param("createBy") String createBy);
|
||||
|
||||
/** Copy surcharge snapshots from src version onto new version details (match by detail keys). */
|
||||
public int copySurchargesFromVersion(
|
||||
@Param("srcPsvId") Long srcPsvId,
|
||||
@Param("newPsvId") Long newPsvId,
|
||||
@Param("createBy") String createBy);
|
||||
}
|
||||
@@ -5,24 +5,36 @@ import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.domain.PrcSurchargeRel;
|
||||
|
||||
/**
|
||||
* 附加费与产品/明细关联 数据层
|
||||
* 附加费与产品/明细/套餐关联 数据层
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
public interface PrcSurchargeRelMapper
|
||||
{
|
||||
/**
|
||||
* 查询关联列表(JOIN 附加费 + 明细),支持按 productCode / scId 过滤
|
||||
* 查询关联列表(JOIN 附加费 + 明细),支持按 productCode / scId / priceType / detailId 过滤
|
||||
*/
|
||||
public List<PrcSurchargeRel> selectPrcSurchargeRelList(PrcSurchargeRel rel);
|
||||
|
||||
public List<PrcSurchargeRel> selectByDetailIdsAndPriceType(
|
||||
@Param("detailIds") List<Long> detailIds,
|
||||
@Param("priceType") String priceType);
|
||||
|
||||
public int insertPrcSurchargeRel(PrcSurchargeRel rel);
|
||||
|
||||
public int deletePrcSurchargeRelByIds(Long[] relIds);
|
||||
|
||||
/** 删除产品时清理其关联 */
|
||||
/** 删除产品时清理 PRODUCT/REDLINE 关联 */
|
||||
public int deleteRelByProductCodes(String[] productCodes);
|
||||
|
||||
public int deleteByDetailIdsAndPriceType(
|
||||
@Param("detailIds") List<Long> detailIds,
|
||||
@Param("priceType") String priceType);
|
||||
|
||||
public int deleteByProductIdsAndPriceType(
|
||||
@Param("productIds") List<Long> productIds,
|
||||
@Param("priceType") String priceType);
|
||||
|
||||
/** 附加费版本化编辑后,把产品关联从旧记录重指向新记录 */
|
||||
public int rebindRelScId(@Param("oldScId") Long oldScId, @Param("newScId") Long newScId, @Param("operator") String operator);
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bms.price.domain.BmsRedlineProduct;
|
||||
import com.bms.price.domain.BmsRedlineProductCountry;
|
||||
import com.bms.price.domain.BmsRedlineProductImportTask;
|
||||
|
||||
/**
|
||||
* redline product list service
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
public interface IBmsRedlineProductService
|
||||
{
|
||||
public BmsRedlineProduct selectBmsRedlineProductById(Long productId);
|
||||
|
||||
public List<BmsRedlineProduct> selectBmsRedlineProductList(BmsRedlineProduct product);
|
||||
|
||||
public int insertBmsRedlineProduct(BmsRedlineProduct product);
|
||||
|
||||
public int updateBmsRedlineProduct(BmsRedlineProduct product);
|
||||
|
||||
public int updateBmsRedlineProductCountry(BmsRedlineProductCountry country);
|
||||
|
||||
public int deleteBmsRedlineProductByIds(Long[] productIds);
|
||||
|
||||
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode);
|
||||
|
||||
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode, Date effectiveDate);
|
||||
|
||||
public List<BmsRedlineProductCountry> selectCountryListByProductCode(String productCode, Date effectiveDate, Long productId);
|
||||
|
||||
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate) throws Exception;
|
||||
|
||||
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName, Date effectiveDate) throws Exception;
|
||||
|
||||
/** Submit async all-country import; returns taskId for polling getImportTask. */
|
||||
public String submitImportProductExcelAsync(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate) throws Exception;
|
||||
|
||||
/** Submit async single-country import; returns taskId for polling getImportTask. */
|
||||
public String submitImportCountryProductExcelAsync(MultipartFile file, String countryCode, String operName, Date effectiveDate) throws Exception;
|
||||
|
||||
/** Poll Redis progress for product import task. */
|
||||
public BmsRedlineProductImportTask getImportTask(String taskId);
|
||||
|
||||
/** Sync import from bytes (used by async runner; taskId may be null). */
|
||||
public String importProductExcel(byte[] bytes, String filename, boolean updateSupport, String operName, String taskId, Date effectiveDate) throws Exception;
|
||||
|
||||
/** Sync country import from bytes (used by async runner; taskId may be null). */
|
||||
public String importCountryProductExcel(byte[] bytes, String filename, String countryCode, String operName, String taskId, Date effectiveDate) throws Exception;
|
||||
|
||||
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(MultipartFile file) throws Exception;
|
||||
|
||||
public java.util.Map<String, List<BmsRedlineProductCountry>> parseQuoteExcelByProduct(byte[] bytes, String filename) throws Exception;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
|
||||
/**
|
||||
* Import quote Excel attachment service
|
||||
*/
|
||||
public interface IPrcPriceExcelFileService
|
||||
{
|
||||
public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId);
|
||||
|
||||
/** Import Excel attachments linked to this product_code via product.pef_id */
|
||||
public List<PrcPriceExcelFile> selectByProductCode(String productCode);
|
||||
|
||||
List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode);
|
||||
|
||||
/**
|
||||
* Persist uploaded Excel under upload/price/excel and insert metadata row.
|
||||
*
|
||||
* @param excelType PRODUCT / REDLINE / SALES
|
||||
* @return inserted row with pefId
|
||||
*/
|
||||
public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes,
|
||||
String filename, String operName) throws Exception;
|
||||
}
|
||||
@@ -13,6 +13,8 @@ public interface IPrcSurchargeRelService
|
||||
/** 查询某产品已关联的附加费(含附加费与明细展示字段) */
|
||||
public List<PrcSurchargeRel> selectRelByProductCode(String productCode);
|
||||
|
||||
public List<PrcSurchargeRel> selectRelByProductCode(String productCode, String priceType);
|
||||
|
||||
public List<PrcSurchargeRel> selectPrcSurchargeRelList(PrcSurchargeRel rel);
|
||||
|
||||
public int insertPrcSurchargeRel(PrcSurchargeRel rel);
|
||||
|
||||
+164
@@ -41,7 +41,9 @@ import com.bms.price.domain.PrcZone;
|
||||
import com.bms.price.mapper.BmsProductCountryMapper;
|
||||
import com.bms.price.mapper.BmsProductMapper;
|
||||
import com.bms.price.mapper.PrcZoneMapper;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.service.IBmsProductService;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
import com.bms.price.service.IPrcSurchargeRelService;
|
||||
import com.bms.price.task.BmsProductImportAsyncRunner;
|
||||
import com.bms.price.util.ZoneCodeAbbrevs;
|
||||
@@ -94,6 +96,9 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
@Autowired
|
||||
private BmsProductImportAsyncRunner importAsyncRunner;
|
||||
|
||||
@Autowired
|
||||
private IPrcPriceExcelFileService priceExcelFileService;
|
||||
|
||||
@Override
|
||||
public BmsProduct selectBmsProductById(Long productId)
|
||||
{
|
||||
@@ -309,6 +314,13 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
List<BmsRegion> euRegions = loadEuRegions(regionByNameCn);
|
||||
Map<String, PrcZone> zoneByCountryAndCode = loadZoneByCountryAndCode();
|
||||
// Pre-check: same product cannot have duplicate country + weight band in Excel
|
||||
assertImportProductsNoDupCountryWeight(workbook, products, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode);
|
||||
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
@@ -328,6 +340,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
try
|
||||
{
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
|
||||
zoneByCountryAndCode, unmatchedCountries, forcedEff);
|
||||
transactionManager.commit(tx);
|
||||
@@ -428,6 +441,10 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
// Pre-check all products: reject whole import before any write if any is earlier than prev version
|
||||
assertImportProductsEffectiveNotBeforePrev(products, forcedEff);
|
||||
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
|
||||
int successNum = 0;
|
||||
int skipNum = 0;
|
||||
int countryNum = 0;
|
||||
@@ -455,11 +472,13 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
product.setDestRegion(countryLabel);
|
||||
product.setSourceSheet(block.sheetName);
|
||||
product.setEffectiveDate(forcedEff);
|
||||
product.setPefId(pefId);
|
||||
if (StringUtils.isEmpty(product.getTaxMode()))
|
||||
{
|
||||
product.setTaxMode(resolveTaxMode(product.getProductName()));
|
||||
}
|
||||
List<BmsProductCountry> countries = stampSelectedCountry(block.rows, region, forcedEff);
|
||||
assertNoDuplicateCountryWeightRows(product, countries);
|
||||
int[] nums = persistImportedProduct(product, countries, operName);
|
||||
transactionManager.commit(tx);
|
||||
successNum += nums[0];
|
||||
@@ -597,6 +616,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
{
|
||||
continue;
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, rows);
|
||||
String code = product.getProductCode().trim().toUpperCase(Locale.ROOT);
|
||||
out.put(code, rows);
|
||||
}
|
||||
@@ -632,6 +652,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.noCountry",
|
||||
productSheet.getSheetName()));
|
||||
}
|
||||
assertNoDuplicateCountryWeightRows(product, countries);
|
||||
// UI-selected effective date wins over Excel; previous open product/country rows expire at this date
|
||||
Date newEff = requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
|
||||
: product.getEffectiveDate());
|
||||
@@ -643,6 +664,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
@@ -666,6 +688,19 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return new int[] { 1, 0, countryRows };
|
||||
}
|
||||
|
||||
private void patchProductPefId(BmsProduct product, String operName)
|
||||
{
|
||||
if (product == null || product.getProductId() == null || product.getPefId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsProduct pefPatch = new BmsProduct();
|
||||
pefPatch.setProductId(product.getProductId());
|
||||
pefPatch.setPefId(product.getPefId());
|
||||
pefPatch.setUpdateBy(operName);
|
||||
productMapper.updateBmsProduct(pefPatch);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save country rows by effective_date.
|
||||
@@ -1988,6 +2023,134 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workbook pre-check for full import: reject before any write when a product sheet
|
||||
* has duplicate country + weight band (same zone) rows.
|
||||
*/
|
||||
private void assertImportProductsNoDupCountryWeight(Workbook workbook, List<BmsProduct> products,
|
||||
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
|
||||
Map<String, PrcZone> zoneByCountryAndCode)
|
||||
{
|
||||
if (workbook == null || products == null || products.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<String> tips = new ArrayList<String>();
|
||||
Set<String> unmatchedCountries = new HashSet<String>();
|
||||
for (BmsProduct product : products)
|
||||
{
|
||||
if (product == null || StringUtils.isEmpty(product.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Sheet productSheet = findProductSheet(workbook, product);
|
||||
if (productSheet == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<BmsProductCountry> rows = parseProductSheet(productSheet, product,
|
||||
regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries);
|
||||
String tip = buildDuplicateCountryWeightTip(product, rows);
|
||||
if (StringUtils.isNotEmpty(tip))
|
||||
{
|
||||
tips.add(tip);
|
||||
}
|
||||
}
|
||||
if (!tips.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.product.import.dupCountry",
|
||||
String.join("<br/>", tips)));
|
||||
}
|
||||
}
|
||||
|
||||
/** Fail when Excel rows for one product contain duplicate country + weight band. */
|
||||
private void assertNoDuplicateCountryWeightRows(BmsProduct product, List<BmsProductCountry> rows)
|
||||
{
|
||||
String tip = buildDuplicateCountryWeightTip(product, rows);
|
||||
if (StringUtils.isNotEmpty(tip))
|
||||
{
|
||||
throw new ServiceException(tip);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDuplicateCountryWeightTip(BmsProduct product, List<BmsProductCountry> rows)
|
||||
{
|
||||
List<String> dups = findDuplicateCountryWeightLabels(rows);
|
||||
if (dups.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String code = product == null || StringUtils.isEmpty(product.getProductCode())
|
||||
? "-" : product.getProductCode();
|
||||
String name = product == null || StringUtils.isEmpty(product.getProductName())
|
||||
? "-" : product.getProductName();
|
||||
return MessageUtils.message("price.product.import.dupCountry.item",
|
||||
code, name, String.join("\u3001", dups));
|
||||
}
|
||||
|
||||
private List<String> findDuplicateCountryWeightLabels(List<BmsProductCountry> rows)
|
||||
{
|
||||
List<String> dups = new ArrayList<String>();
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return dups;
|
||||
}
|
||||
Map<String, Integer> counts = new LinkedHashMap<String, Integer>();
|
||||
Map<String, String> labels = new LinkedHashMap<String, String>();
|
||||
for (BmsProductCountry c : rows)
|
||||
{
|
||||
if (c == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String key = countryWeightDupKey(c);
|
||||
Integer n = counts.get(key);
|
||||
counts.put(key, n == null ? 1 : n.intValue() + 1);
|
||||
if (!labels.containsKey(key))
|
||||
{
|
||||
labels.put(key, formatCountryWeightTip(c));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Integer> e : counts.entrySet())
|
||||
{
|
||||
if (e.getValue() != null && e.getValue().intValue() > 1)
|
||||
{
|
||||
dups.add(labels.get(e.getKey()));
|
||||
}
|
||||
}
|
||||
return dups;
|
||||
}
|
||||
|
||||
/** Align with uk_product_country_zone_weight_eff (country + zone + weight min/max). */
|
||||
private String countryWeightDupKey(BmsProductCountry c)
|
||||
{
|
||||
String country = StringUtils.isNotEmpty(c.getCountryCode())
|
||||
? c.getCountryCode().trim().toUpperCase(Locale.ROOT)
|
||||
: (c.getCountryName() == null ? "" : c.getCountryName().trim());
|
||||
String zone = c.getZoneCode() == null ? "" : c.getZoneCode().trim().toUpperCase(Locale.ROOT);
|
||||
return country + "|" + zone + "|" + decimalKey(c.getWeightMin()) + "|" + decimalKey(c.getWeightMax());
|
||||
}
|
||||
|
||||
private String formatCountryWeightTip(BmsProductCountry c)
|
||||
{
|
||||
String name = StringUtils.isNotEmpty(c.getCountryName()) ? c.getCountryName()
|
||||
: (StringUtils.isNotEmpty(c.getCountryCode()) ? c.getCountryCode() : "-");
|
||||
if (StringUtils.isNotEmpty(c.getZoneCode()))
|
||||
{
|
||||
name = name + "(" + c.getZoneCode() + ")";
|
||||
}
|
||||
return name + " " + decimalKey(c.getWeightMin()) + "~" + decimalKey(c.getWeightMax());
|
||||
}
|
||||
|
||||
private String decimalKey(BigDecimal v)
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return v.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
/** Stamp product + all country detail rows with the same effective date. */
|
||||
private void applyForcedEffectiveDate(BmsProduct product, List<BmsProductCountry> countries, Date newEff)
|
||||
{
|
||||
@@ -2025,6 +2188,7 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
if (existing != null)
|
||||
{
|
||||
product.setProductId(existing.getProductId());
|
||||
patchProductPefId(product, operName);
|
||||
int countryRows = saveCountries(product, countries);
|
||||
return new int[] { 0, 1, countryRows };
|
||||
}
|
||||
|
||||
+2290
File diff suppressed because it is too large
Load Diff
+84
-6
@@ -8,8 +8,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -1115,18 +1117,39 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
else
|
||||
{
|
||||
orderBillMapper.updatePrcOrderBill(bill);
|
||||
incomeBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
// Keep manually added fees (manual_flag=1) across rebill
|
||||
incomeBillFeeMapper.deleteAutoFeesByBillId(bill.getBillId());
|
||||
// Payable not in scope yet: skip prc_pay_bill_fee cleanup/write.
|
||||
// payBillFeeMapper.deleteByBillId(bill.getBillId());
|
||||
}
|
||||
PrcIncomeBill incomeBill = persistIncomeBill(bill, result);
|
||||
List<PrcIncomeBillFee> fees = toFeeRows(bill.getBillId(),
|
||||
incomeBill == null ? null : incomeBill.getIncomeBillId(), result);
|
||||
if (existed != null)
|
||||
{
|
||||
fees = filterAutoFeesAgainstManual(bill.getBillId(), fees);
|
||||
}
|
||||
if (!fees.isEmpty())
|
||||
{
|
||||
incomeBillFeeMapper.insertBatch(fees);
|
||||
}
|
||||
orderBillMapper.refreshArBillStatusByBillIds(Arrays.asList(bill.getBillId()));
|
||||
if (existed != null)
|
||||
{
|
||||
// Quote totals were written above; recalc so retained manual fees join income/credit
|
||||
TmsPriceQuoteResult refreshed = refreshBillIncomeTotals(bill);
|
||||
if (refreshed != null)
|
||||
{
|
||||
result.setSurchargeAmount(refreshed.getSurchargeAmount());
|
||||
result.setIncomeTotal(refreshed.getIncomeTotal());
|
||||
result.setPayableTotal(refreshed.getPayableTotal());
|
||||
result.setCreditArrearsStatus(refreshed.getCreditArrearsStatus());
|
||||
result.setFeeDetails(refreshed.getFeeDetails());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
orderBillMapper.refreshArBillStatusByBillIds(Arrays.asList(bill.getBillId()));
|
||||
}
|
||||
// Payable not in scope yet: do not write prc_pay_bill / prc_pay_bill_fee.
|
||||
// PrcPayBill payBill = persistPayBill(bill, result);
|
||||
// List<PrcPayBillFee> payFees = toPayFeeRows(bill.getBillId(),
|
||||
@@ -1141,6 +1164,53 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
billSearchStore.save(EsLogType.BILL, EsBizLogSupport.billBizId(request), requestJson, toResponseJson(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop auto fee lines that collide with retained manual rows on uk_bill_fee_item
|
||||
* (bill_id, fee_type, sc_id). Manual fees win on rebill.
|
||||
*/
|
||||
private List<PrcIncomeBillFee> filterAutoFeesAgainstManual(String billId, List<PrcIncomeBillFee> autoFees)
|
||||
{
|
||||
if (autoFees == null || autoFees.isEmpty() || StringUtils.isEmpty(billId))
|
||||
{
|
||||
return autoFees == null ? new ArrayList<PrcIncomeBillFee>() : autoFees;
|
||||
}
|
||||
List<PrcIncomeBillFee> existing = incomeBillFeeMapper.selectByBillId(billId);
|
||||
if (existing == null || existing.isEmpty())
|
||||
{
|
||||
return autoFees;
|
||||
}
|
||||
Set<String> manualKeys = new HashSet<String>();
|
||||
for (PrcIncomeBillFee row : existing)
|
||||
{
|
||||
if (row != null && "1".equals(row.getManualFlag()))
|
||||
{
|
||||
manualKeys.add(feeUniqueKey(row.getFeeType(), row.getScId()));
|
||||
}
|
||||
}
|
||||
if (manualKeys.isEmpty())
|
||||
{
|
||||
return autoFees;
|
||||
}
|
||||
List<PrcIncomeBillFee> out = new ArrayList<PrcIncomeBillFee>();
|
||||
for (PrcIncomeBillFee fee : autoFees)
|
||||
{
|
||||
if (fee == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!manualKeys.contains(feeUniqueKey(fee.getFeeType(), fee.getScId())))
|
||||
{
|
||||
out.add(fee);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private String feeUniqueKey(String feeType, Long scId)
|
||||
{
|
||||
return StringUtils.nvl(feeType, "") + "|" + (scId == null ? "0" : String.valueOf(scId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Occupy/release credit by income total delta (bizType=ORDER).
|
||||
* Missing/disabled customer: skip occupy, return NORMAL.
|
||||
@@ -1347,7 +1417,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
return fees;
|
||||
}
|
||||
|
||||
/** 应收主表与订单计费一对一落库;销售价=套餐计费,产品价=产品目录价;红线价预留。 */
|
||||
/** 应收主表与订单计费一对一落库;销售价=套餐计费,产品价=产品目录价,红线价=红线目录价(含 REDLINE 附加费)。 */
|
||||
private PrcIncomeBill persistIncomeBill(PrcOrderBill bill, TmsPriceQuoteResult result)
|
||||
{
|
||||
if (bill == null || StringUtils.isEmpty(bill.getBillId()))
|
||||
@@ -1364,8 +1434,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
row.setCreateBy(bill.getCreateBy());
|
||||
}
|
||||
row.setCurrency(StringUtils.isEmpty(bill.getCurrency()) ? "CNY" : bill.getCurrency());
|
||||
// 产品价=目录价;红线价预留
|
||||
// product catalog + redline from quote
|
||||
row.setProductAmount(result == null ? null : result.getProductAmount());
|
||||
row.setRedlineAmount(result == null ? null : result.getRedlineAmount());
|
||||
row.setSalesAmount(sales);
|
||||
row.setIncomeTotal(sales);
|
||||
row.setBelongSaler(bill.getBelongSaler());
|
||||
@@ -1410,7 +1481,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
return fees;
|
||||
}
|
||||
|
||||
/** 应付主表与订单计费一对一落库;成本价/应付=产品目录价,同步写入 product_amount;红线价预留。 */
|
||||
/** 应付主表与订单计费一对一落库;成本价/应付=产品目录价,同步写入 product_amount;红线价同步写入 redline_amount。 */
|
||||
private PrcPayBill persistPayBill(PrcOrderBill bill, TmsPriceQuoteResult result)
|
||||
{
|
||||
if (bill == null || StringUtils.isEmpty(bill.getBillId()))
|
||||
@@ -1427,9 +1498,10 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
row.setCreateBy(bill.getCreateBy());
|
||||
}
|
||||
row.setCurrency(StringUtils.isEmpty(bill.getCurrency()) ? "CNY" : bill.getCurrency());
|
||||
// 产品价/成本价=目录价;红线价预留
|
||||
// product/cost = catalog; redline from quote
|
||||
BigDecimal product = result == null ? null : result.getProductAmount();
|
||||
row.setProductAmount(product);
|
||||
row.setRedlineAmount(result == null ? null : result.getRedlineAmount());
|
||||
row.setCostAmount(pay);
|
||||
row.setPayTotal(pay);
|
||||
row.setBelongSaler(bill.getBelongSaler());
|
||||
@@ -1493,6 +1565,12 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
r.setBizDate(bill.getBizDate());
|
||||
r.setFeeDetails(toIncomeFeeLines(incomeFees));
|
||||
r.setPayFeeDetails(toPayFeeLines(payFees));
|
||||
PrcIncomeBill incomeBill = incomeBillMapper.selectByBillId(bill.getBillId());
|
||||
if (incomeBill != null)
|
||||
{
|
||||
r.setProductAmount(incomeBill.getProductAmount());
|
||||
r.setRedlineAmount(incomeBill.getRedlineAmount());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.bms.price.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.common.config.BmsConfig;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.file.FileUploadUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.mapper.PrcPriceExcelFileMapper;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
|
||||
/**
|
||||
* Persist import Excel bytes and metadata
|
||||
*/
|
||||
@Service
|
||||
public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService
|
||||
{
|
||||
private static final String SUB_DIR = "price/excel";
|
||||
|
||||
@Autowired
|
||||
private PrcPriceExcelFileMapper excelFileMapper;
|
||||
|
||||
@Override
|
||||
public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId)
|
||||
{
|
||||
return excelFileMapper.selectPrcPriceExcelFileById(pefId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceExcelFile> selectByProductCode(String productCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PrcPriceExcelFile> list = excelFileMapper.selectByProductCode(productCode.trim());
|
||||
return list == null ? Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceExcelFile> selectByRedlineProductCode(String productCode)
|
||||
{
|
||||
if (productCode == null || productCode.trim().isEmpty())
|
||||
{
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
List<PrcPriceExcelFile> list = excelFileMapper.selectByRedlineProductCode(productCode.trim());
|
||||
return list == null ? java.util.Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes,
|
||||
String filename, String operName) throws Exception
|
||||
{
|
||||
if (StringUtils.isEmpty(excelType))
|
||||
{
|
||||
throw new ServiceException("excelType required");
|
||||
}
|
||||
if (bytes == null || bytes.length == 0)
|
||||
{
|
||||
throw new ServiceException("excel empty");
|
||||
}
|
||||
if (effectiveTime == null)
|
||||
{
|
||||
effectiveTime = new Date();
|
||||
}
|
||||
String originalName = StringUtils.isNotEmpty(filename) ? filename : "import.xlsx";
|
||||
String ext = FilenameUtils.getExtension(originalName);
|
||||
if (StringUtils.isEmpty(ext))
|
||||
{
|
||||
ext = "xlsx";
|
||||
}
|
||||
ext = ext.toLowerCase(Locale.ROOT);
|
||||
|
||||
String uploadDir = BmsConfig.getUploadPath() + File.separator + SUB_DIR;
|
||||
String pathName = DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + "." + ext;
|
||||
File dest = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
|
||||
FileOutputStream fos = null;
|
||||
try
|
||||
{
|
||||
fos = new FileOutputStream(dest);
|
||||
fos.write(bytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.close(fos);
|
||||
}
|
||||
String storedPath = FileUploadUtils.getPathFileName(uploadDir, pathName);
|
||||
|
||||
PrcPriceExcelFile row = new PrcPriceExcelFile();
|
||||
row.setExcelType(excelType.trim().toUpperCase(Locale.ROOT));
|
||||
row.setEffectiveTime(effectiveTime);
|
||||
row.setFileName(originalName);
|
||||
row.setFilePath(storedPath);
|
||||
row.setFileSize(Long.valueOf(bytes.length));
|
||||
row.setContentType(resolveContentType(ext));
|
||||
row.setFileExtension(ext);
|
||||
row.setCreateBy(operName);
|
||||
excelFileMapper.insertPrcPriceExcelFile(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
private static String resolveContentType(String ext)
|
||||
{
|
||||
if ("xls".equals(ext))
|
||||
{
|
||||
return "application/vnd.ms-excel";
|
||||
}
|
||||
if ("xlsx".equals(ext))
|
||||
{
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+403
-154
@@ -22,17 +22,18 @@ import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.MessageUtils;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.common.utils.uuid.IdUtils;
|
||||
import com.bms.price.domain.BmsProduct;
|
||||
import com.bms.price.domain.BmsProductCountry;
|
||||
import com.bms.price.domain.PrcCustomerPriceSet;
|
||||
import com.bms.price.domain.PrcPriceExcelFile;
|
||||
import com.bms.price.domain.PrcPriceSet;
|
||||
import com.bms.price.domain.PrcPriceSetCheckItem;
|
||||
import com.bms.price.domain.PrcPriceSetDetail;
|
||||
import com.bms.price.domain.PrcPriceSetProduct;
|
||||
import com.bms.price.domain.PrcPriceSetSaveTask;
|
||||
import com.bms.price.domain.PrcPriceSetVersion;
|
||||
import com.bms.price.domain.PrcPriceSetVersionSurcharge;
|
||||
import com.bms.price.domain.PrcSurcharge;
|
||||
import com.bms.price.domain.PrcSurchargeRel;
|
||||
import com.bms.price.mapper.BmsProductCountryMapper;
|
||||
@@ -42,10 +43,10 @@ import com.bms.price.mapper.PrcPriceSetDetailMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetProductMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetVersionMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetVersionSurchargeMapper;
|
||||
import com.bms.price.mapper.PrcSurchargeMapper;
|
||||
import com.bms.price.mapper.PrcSurchargeRelMapper;
|
||||
import com.bms.price.service.IBmsProductService;
|
||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||
import com.bms.price.service.IPrcPriceSetService;
|
||||
import com.bms.price.task.PrcPriceSetSaveAsyncRunner;
|
||||
|
||||
@@ -88,9 +89,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
@Autowired
|
||||
private PrcPriceSetVersionMapper versionMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetVersionSurchargeMapper versionSurchargeMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcSurchargeRelMapper surchargeRelMapper;
|
||||
|
||||
@@ -117,6 +115,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
@Autowired
|
||||
private PrcPriceSetSaveAsyncRunner saveAsyncRunner;
|
||||
|
||||
@Autowired
|
||||
private IPrcPriceExcelFileService priceExcelFileService;
|
||||
|
||||
@Override
|
||||
public PrcPriceSet selectPrcPriceSetById(Long priceSetId)
|
||||
{
|
||||
@@ -174,10 +175,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Always replace snapshot for this batch API (null would skip in updateDetail)
|
||||
// Always replace SALES rels for this batch API (null would skip in updateDetail)
|
||||
if (detail.getSurcharges() == null)
|
||||
{
|
||||
detail.setSurcharges(new ArrayList<PrcPriceSetVersionSurcharge>());
|
||||
detail.setSurcharges(new ArrayList<PrcSurchargeRel>());
|
||||
}
|
||||
rows += updateDetail(detail);
|
||||
}
|
||||
@@ -468,7 +469,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
if (detailRows > 0)
|
||||
{
|
||||
setProductMapper.bindDetailPspByPsvId(nv.getPsvId());
|
||||
versionSurchargeMapper.copySurchargesFromVersion(sourcePsvId, nv.getPsvId(), createBy);
|
||||
copySalesSurchargeRelsFromVersion(sourcePsvId, nv.getPsvId(), createBy);
|
||||
}
|
||||
refreshVersionStatus(set.getPriceSetId());
|
||||
return nv.getPsvId();
|
||||
@@ -521,7 +522,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
throw new ServiceException(MessageUtils.message("price.set.version.delete.statusNotAllowed"));
|
||||
}
|
||||
requireSet(version.getPriceSetId());
|
||||
versionSurchargeMapper.deleteByPsvId(psvId);
|
||||
deleteSalesRelsByPsvId(psvId);
|
||||
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
||||
setProductMapper.deleteByPsvId(psvId);
|
||||
int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
|
||||
@@ -852,7 +853,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Attach version surcharge snapshots to a page of details (for drawer list). */
|
||||
/** Attach SALES surcharge rels (detail_id=psd_id) to a page of details. */
|
||||
private void fillDetailSurcharges(List<PrcPriceSetDetail> details)
|
||||
{
|
||||
if (details == null || details.isEmpty())
|
||||
@@ -871,21 +872,22 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> surcharges = versionSurchargeMapper.selectByPsdIds(psdIds);
|
||||
Map<Long, List<PrcPriceSetVersionSurcharge>> byPsd = new HashMap<Long, List<PrcPriceSetVersionSurcharge>>();
|
||||
List<PrcSurchargeRel> surcharges =
|
||||
surchargeRelMapper.selectByDetailIdsAndPriceType(psdIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
Map<Long, List<PrcSurchargeRel>> byPsd = new HashMap<Long, List<PrcSurchargeRel>>();
|
||||
if (surcharges != null)
|
||||
{
|
||||
for (PrcPriceSetVersionSurcharge sc : surcharges)
|
||||
for (PrcSurchargeRel sc : surcharges)
|
||||
{
|
||||
if (sc == null || sc.getPsdId() == null)
|
||||
if (sc == null || sc.getDetailId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> list = byPsd.get(sc.getPsdId());
|
||||
List<PrcSurchargeRel> list = byPsd.get(sc.getDetailId());
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<PrcPriceSetVersionSurcharge>();
|
||||
byPsd.put(sc.getPsdId(), list);
|
||||
list = new ArrayList<PrcSurchargeRel>();
|
||||
byPsd.put(sc.getDetailId(), list);
|
||||
}
|
||||
list.add(sc);
|
||||
}
|
||||
@@ -896,8 +898,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> list = byPsd.get(d.getPsdId());
|
||||
d.setSurcharges(list != null ? list : new ArrayList<PrcPriceSetVersionSurcharge>());
|
||||
List<PrcSurchargeRel> list = byPsd.get(d.getPsdId());
|
||||
d.setSurcharges(list != null ? list : new ArrayList<PrcSurchargeRel>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -910,9 +912,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
detail.setPsvId(resolvePsvId(detail.getPriceSetId(), detail.getPsvId()));
|
||||
detail.setPspId(lookupSetProductId(detail.getPsvId(), detail.getProductCode()));
|
||||
int rows = detailMapper.insertPrcPriceSetDetail(detail);
|
||||
PrcPriceSetVersion ver = new PrcPriceSetVersion();
|
||||
ver.setPsvId(detail.getPsvId());
|
||||
snapshotVersionSurcharges(ver, java.util.Collections.singletonList(detail));
|
||||
if (detail.getSurcharges() != null)
|
||||
{
|
||||
replaceSalesSurchargeRels(java.util.Collections.singletonList(detail));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@@ -1071,6 +1074,18 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
assertUpdateOnlyWeightBandsMatch(byProduct, detailIndex);
|
||||
}
|
||||
String operName = null;
|
||||
try
|
||||
{
|
||||
operName = SecurityUtils.getUsername();
|
||||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
operName = null;
|
||||
}
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_SALES, new Date(), bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
|
||||
Map<String, PrcPriceSetProduct> productMap = new LinkedHashMap<String, PrcPriceSetProduct>();
|
||||
int updated = 0;
|
||||
@@ -1172,9 +1187,20 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noDetailWritten"));
|
||||
}
|
||||
|
||||
if (pefId != null)
|
||||
{
|
||||
for (PrcPriceSetProduct sp : productMap.values())
|
||||
{
|
||||
if (sp != null)
|
||||
{
|
||||
sp.setPefId(pefId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
result.put("products", new ArrayList<PrcPriceSetProduct>(productMap.values()));
|
||||
result.put("details", details);
|
||||
result.put("pefId", pefId);
|
||||
if (updateOnly)
|
||||
{
|
||||
result.put("message", MessageUtils.message("price.set.import.updateSuccess",
|
||||
@@ -1291,6 +1317,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
assertUpdateOnlyWeightBandsMatch(byProduct, detailIndex);
|
||||
}
|
||||
PrcPriceSetVersion verForFile = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
Date excelEff = verForFile != null && verForFile.getEffectiveTime() != null
|
||||
? verForFile.getEffectiveTime() : new Date();
|
||||
PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel(
|
||||
PrcPriceExcelFile.TYPE_SALES, excelEff, bytes, filename, operName);
|
||||
Long pefId = excelFile != null ? excelFile.getPefId() : null;
|
||||
|
||||
int updated = 0;
|
||||
int inserted = 0;
|
||||
@@ -1321,7 +1353,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
Long pspId = null;
|
||||
if (!updateOnly)
|
||||
{
|
||||
pspId = ensureSetProduct(priceSetId, psvId, productCode, productName);
|
||||
pspId = ensureSetProduct(priceSetId, psvId, productCode, productName, pefId);
|
||||
}
|
||||
for (BmsProductCountry row : e.getValue())
|
||||
{
|
||||
@@ -1385,6 +1417,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
setProductMapper.bindDetailPspByPsvId(psvId);
|
||||
}
|
||||
if (pefId != null && (inserted > 0 || updated > 0))
|
||||
{
|
||||
List<String> codes = new ArrayList<String>(byProduct.keySet());
|
||||
setProductMapper.updatePefIdByPsvId(psvId, pefId, codes);
|
||||
}
|
||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||
if (inserted == 0 && updated == 0)
|
||||
{
|
||||
@@ -1779,13 +1816,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
detail.setPspId(pspId);
|
||||
int rows = detailMapper.updatePrcPriceSetDetail(detail);
|
||||
// non-null surcharges = replace snapshot for this detail (empty list clears)
|
||||
// non-null surcharges = replace SALES rels for this detail (empty list clears)
|
||||
if (detail.getSurcharges() != null)
|
||||
{
|
||||
versionSurchargeMapper.deleteByPsdId(detail.getPsdId());
|
||||
PrcPriceSetVersion ver = new PrcPriceSetVersion();
|
||||
ver.setPsvId(detail.getPsvId());
|
||||
snapshotVersionSurcharges(ver, java.util.Collections.singletonList(detail));
|
||||
replaceSalesSurchargeRels(java.util.Collections.singletonList(detail));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
@@ -1808,6 +1842,21 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
touchKeys.add(old.getPsvId() + "|" + old.getProductCode());
|
||||
}
|
||||
}
|
||||
if (psdIds != null && psdIds.length > 0)
|
||||
{
|
||||
List<Long> ids = new ArrayList<Long>();
|
||||
for (Long id : psdIds)
|
||||
{
|
||||
if (id != null)
|
||||
{
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty())
|
||||
{
|
||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(ids, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
}
|
||||
}
|
||||
int rows = detailMapper.deletePrcPriceSetDetailByIds(psdIds);
|
||||
for (String key : touchKeys)
|
||||
{
|
||||
@@ -1824,6 +1873,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
List<PrcPriceSetDetail> left = detailMapper.selectPrcPriceSetDetailList(q);
|
||||
if (left == null || left.isEmpty())
|
||||
{
|
||||
Long pspId = lookupSetProductId(psvId, productCode);
|
||||
if (pspId != null)
|
||||
{
|
||||
surchargeRelMapper.deleteByProductIdsAndPriceType(
|
||||
java.util.Collections.singletonList(pspId), PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
}
|
||||
setProductMapper.deleteByPsvIdAndCode(psvId, productCode);
|
||||
}
|
||||
}
|
||||
@@ -2656,9 +2711,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
pq.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetProduct> products = setProductMapper.selectPrcPriceSetProductList(pq);
|
||||
version.setProducts(products == null ? new ArrayList<PrcPriceSetProduct>() : products);
|
||||
// details + surcharge snapshots are loaded on demand (edit price / detail drawer)
|
||||
// details + SALES surcharge rels are loaded on demand (edit price / detail drawer)
|
||||
version.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
version.setSurcharges(new ArrayList<PrcPriceSetVersionSurcharge>());
|
||||
}
|
||||
|
||||
private PrcPriceSetVersion pickCurrentVersion(List<PrcPriceSetVersion> versions)
|
||||
@@ -2733,6 +2787,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
version.setExpireTime(db.getExpireTime());
|
||||
}
|
||||
// Draft + build mode changed: wipe old product/detail/surcharge before mode switch
|
||||
clearDraftVersionChildrenIfBuildModeChanged(db, version);
|
||||
versionMapper.updatePrcPriceSetVersion(version);
|
||||
}
|
||||
set.setVersion(version);
|
||||
@@ -2740,7 +2796,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
upsertVersionDetails(set, version, details);
|
||||
reportSaveProgress(set, 85, "saving surcharges");
|
||||
resnapshotSurchargesIfNeeded(version, details);
|
||||
replaceSalesSurchargesIfNeeded(details);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2749,6 +2805,36 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
refreshVersionStatus(set.getPriceSetId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Draft only: when priceBuildMode changes (PULL/IMPORT), delete this version's
|
||||
* product / detail / SALES surcharge rel rows. Same mode or non-draft: no-op.
|
||||
*/
|
||||
private void clearDraftVersionChildrenIfBuildModeChanged(PrcPriceSetVersion db, PrcPriceSetVersion incoming)
|
||||
{
|
||||
if (db == null || incoming == null || db.getPsvId() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!VER_DRAFT.equals(db.getStatus()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String newMode = StringUtils.trim(incoming.getPriceBuildMode());
|
||||
if (StringUtils.isEmpty(newMode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String oldMode = StringUtils.trim(db.getPriceBuildMode());
|
||||
if (newMode.equalsIgnoreCase(StringUtils.nvl(oldMode, "")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Long psvId = db.getPsvId();
|
||||
deleteSalesRelsByPsvId(psvId);
|
||||
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
||||
setProductMapper.deleteByPsvId(psvId);
|
||||
}
|
||||
|
||||
/** Prefer same effective_time, else draft, else first version of the set. */
|
||||
private PrcPriceSetVersion findReusableVersion(Long priceSetId, Date effectiveTime)
|
||||
{
|
||||
@@ -2780,8 +2866,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk create details + surcharge snapshots via SQL joins (no per-product HTTP roundtrips).
|
||||
* Bulk create details via SQL joins (no per-product HTTP roundtrips).
|
||||
* Idempotent: if this version already has details and forcePullLibrary!=Y, skip re-insert.
|
||||
* SALES surcharge rels are not auto-created; configure via package surcharge picker.
|
||||
*/
|
||||
private void replaceVersionDetailsFromLibrary(PrcPriceSet set, PrcPriceSetVersion version)
|
||||
{
|
||||
@@ -2795,7 +2882,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
return;
|
||||
}
|
||||
versionSurchargeMapper.deleteByPsvId(version.getPsvId());
|
||||
deleteSalesRelsByPsvId(version.getPsvId());
|
||||
detailMapper.deletePrcPriceSetDetailByPsvId(version.getPsvId());
|
||||
setProductMapper.deleteByPsvId(version.getPsvId());
|
||||
// clean legacy rows without psv_id under this set (would look like duplicates in UI)
|
||||
@@ -2829,8 +2916,6 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
setProductMapper.bindDetailPspByPsvId(version.getPsvId());
|
||||
reportSaveProgress(set, 75, "saving details " + inserted + "/" + inserted);
|
||||
reportSaveProgress(set, 85, "saving surcharges");
|
||||
versionSurchargeMapper.insertFromLibraryRels(version.getPsvId(), version.getCreateBy());
|
||||
fillVersionChildren(version);
|
||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
}
|
||||
@@ -2903,7 +2988,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
d.setPriceSetId(set.getPriceSetId());
|
||||
d.setPsvId(version.getPsvId());
|
||||
Long pspId = ensureSetProduct(set.getPriceSetId(), version.getPsvId(),
|
||||
d.getProductCode(), d.getProductName());
|
||||
d.getProductCode(), d.getProductName(), set.getPefId());
|
||||
if (pspId != null)
|
||||
{
|
||||
d.setPspId(pspId);
|
||||
@@ -2948,10 +3033,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
if (!orphanIds.isEmpty())
|
||||
{
|
||||
for (Long id : orphanIds)
|
||||
{
|
||||
versionSurchargeMapper.deleteByPsdId(id);
|
||||
}
|
||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(orphanIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
detailMapper.deletePrcPriceSetDetailByIds(orphanIds.toArray(new Long[0]));
|
||||
}
|
||||
Set<String> keepCodes = new HashSet<String>();
|
||||
@@ -3027,6 +3109,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
* Uses request-scoped cache during bulk upsert to avoid N selects on same code.
|
||||
*/
|
||||
private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint)
|
||||
{
|
||||
return ensureSetProduct(priceSetId, psvId, productCode, productNameHint, null);
|
||||
}
|
||||
|
||||
private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint,
|
||||
Long pefId)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
@@ -3037,15 +3125,32 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
Map<String, Long> cache = setProductCacheTl.get();
|
||||
if (cache != null && cache.containsKey(key))
|
||||
{
|
||||
if (pefId != null)
|
||||
{
|
||||
PrcPriceSetProduct pefPatch = new PrcPriceSetProduct();
|
||||
pefPatch.setPspId(cache.get(key));
|
||||
pefPatch.setPefId(pefId);
|
||||
setProductMapper.updatePrcPriceSetProduct(pefPatch);
|
||||
}
|
||||
return cache.get(key);
|
||||
}
|
||||
PrcPriceSetProduct existing = setProductMapper.selectByPsvIdAndCode(psvId, code);
|
||||
if (existing != null)
|
||||
{
|
||||
boolean needUpdate = false;
|
||||
if (StringUtils.isEmpty(existing.getProductName()) && StringUtils.isNotEmpty(productNameHint)
|
||||
&& !code.equals(productNameHint))
|
||||
{
|
||||
existing.setProductName(productNameHint);
|
||||
needUpdate = true;
|
||||
}
|
||||
if (pefId != null && (existing.getPefId() == null || !pefId.equals(existing.getPefId())))
|
||||
{
|
||||
existing.setPefId(pefId);
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate)
|
||||
{
|
||||
setProductMapper.updatePrcPriceSetProduct(existing);
|
||||
}
|
||||
if (cache != null)
|
||||
@@ -3058,6 +3163,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
row.setPriceSetId(priceSetId);
|
||||
row.setPsvId(psvId);
|
||||
row.setProductCode(code);
|
||||
row.setPefId(pefId);
|
||||
BmsProduct lib = null;
|
||||
try
|
||||
{
|
||||
@@ -3091,8 +3197,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return row.getPspId();
|
||||
}
|
||||
|
||||
/** Only rewrite surcharge snapshots for rows that explicitly sent surcharges. */
|
||||
private void resnapshotSurchargesIfNeeded(PrcPriceSetVersion version, List<PrcPriceSetDetail> details)
|
||||
/** Only rewrite SALES surcharge rels for rows that explicitly sent surcharges. */
|
||||
private void replaceSalesSurchargesIfNeeded(List<PrcPriceSetDetail> details)
|
||||
{
|
||||
if (details == null || details.isEmpty())
|
||||
{
|
||||
@@ -3103,153 +3209,296 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
if (d != null && d.getPsdId() != null && d.getSurcharges() != null)
|
||||
{
|
||||
versionSurchargeMapper.deleteByPsdId(d.getPsdId());
|
||||
withSc.add(d);
|
||||
}
|
||||
}
|
||||
if (!withSc.isEmpty())
|
||||
{
|
||||
snapshotVersionSurcharges(version, withSc);
|
||||
replaceSalesSurchargeRels(withSc);
|
||||
}
|
||||
}
|
||||
|
||||
private void snapshotVersionSurcharges(PrcPriceSetVersion version, List<PrcPriceSetDetail> details)
|
||||
/**
|
||||
* Replace SALES prc_surcharge_rel rows for the given package details.
|
||||
* product_id = psp_id, detail_id = psd_id, price_type = SALES.
|
||||
* Empty surcharges list clears associations for that detail.
|
||||
*/
|
||||
private void replaceSalesSurchargeRels(List<PrcPriceSetDetail> details)
|
||||
{
|
||||
if (details == null || details.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Set<String> codes = new HashSet<String>();
|
||||
Map<Long, PrcSurcharge> masterById = null;
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(d.getProductCode()))
|
||||
{
|
||||
codes.add(d.getProductCode());
|
||||
}
|
||||
}
|
||||
Map<String, List<PrcSurchargeRel>> relsByProduct = new HashMap<String, List<PrcSurchargeRel>>();
|
||||
for (String code : codes)
|
||||
{
|
||||
PrcSurchargeRel q = new PrcSurchargeRel();
|
||||
q.setProductCode(code);
|
||||
List<PrcSurchargeRel> rels = surchargeRelMapper.selectPrcSurchargeRelList(q);
|
||||
relsByProduct.put(code, rels == null ? new ArrayList<PrcSurchargeRel>() : rels);
|
||||
}
|
||||
// 勾选列表来自「价格基础 → 附加费配置」主表(prc_surcharge),不要求产品已做 rel 关联;
|
||||
// rel 里没有的勾选项,回退主表取值落快照。
|
||||
List<PrcSurcharge> surchargeMasters = java.util.Collections.emptyList();
|
||||
for (PrcPriceSetDetail d0 : details)
|
||||
{
|
||||
if (d0.getSurcharges() != null && !d0.getSurcharges().isEmpty())
|
||||
{
|
||||
surchargeMasters = surchargeMapper.selectPrcSurchargeList(new PrcSurcharge());
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> saved = new ArrayList<PrcPriceSetVersionSurcharge>();
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
List<PrcSurchargeRel> rels = relsByProduct.get(d.getProductCode());
|
||||
if (rels == null)
|
||||
if (d == null || d.getPsdId() == null || d.getSurcharges() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 前端勾选结果:明细带了 surcharges 就按勾选保存(空集合 = 一条都不要);
|
||||
// 为 null 表示调用方没传勾选(如详情页新增单条明细),沿用「全部命中即快照」。
|
||||
Set<Long> pickedScIds = null;
|
||||
if (d.getSurcharges() != null)
|
||||
Long pspId = d.getPspId();
|
||||
if (pspId == null && d.getPsvId() != null && StringUtils.isNotEmpty(d.getProductCode()))
|
||||
{
|
||||
pickedScIds = new HashSet<Long>();
|
||||
for (PrcPriceSetVersionSurcharge picked : d.getSurcharges())
|
||||
{
|
||||
if (picked != null && picked.getScId() != null)
|
||||
{
|
||||
pickedScIds.add(picked.getScId());
|
||||
}
|
||||
}
|
||||
pspId = lookupSetProductId(d.getPsvId(), d.getProductCode());
|
||||
d.setPspId(pspId);
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> rowSc = new ArrayList<PrcPriceSetVersionSurcharge>();
|
||||
if (pspId == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(
|
||||
java.util.Collections.singletonList(d.getPsdId()), PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
|
||||
List<PrcSurchargeRel> saved = new ArrayList<PrcSurchargeRel>();
|
||||
Set<Long> written = new HashSet<Long>();
|
||||
for (PrcSurchargeRel rel : rels)
|
||||
for (PrcSurchargeRel picked : d.getSurcharges())
|
||||
{
|
||||
if (rel == null || StringUtils.isEmpty(rel.getScName()) || rel.getChargeValue() == null)
|
||||
if (picked == null || picked.getScId() == null || written.contains(picked.getScId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ("of".equalsIgnoreCase(rel.getSsCode()))
|
||||
PrcSurchargeRel row = buildSalesRel(d, pspId, picked.getScId(), picked.getCountryCode());
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (pickedScIds != null && !pickedScIds.contains(rel.getScId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (rel.getDetailId() != null && d.getProductCountryId() != null
|
||||
&& !rel.getDetailId().equals(d.getProductCountryId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (rel.getDetailId() != null && d.getProductCountryId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetVersionSurcharge sc = new PrcPriceSetVersionSurcharge();
|
||||
sc.setPsvId(version.getPsvId());
|
||||
sc.setPsdId(d.getPsdId());
|
||||
sc.setScId(rel.getScId());
|
||||
sc.setProductCode(d.getProductCode());
|
||||
sc.setScName(rel.getScName());
|
||||
sc.setFeeType(StringUtils.isEmpty(rel.getFeeType()) ? "SURCHARGE" : rel.getFeeType());
|
||||
sc.setChargeType(StringUtils.isEmpty(rel.getChargeType()) ? "FIXED" : rel.getChargeType());
|
||||
sc.setChargeValue(rel.getChargeValue());
|
||||
sc.setCurrency(rel.getCurrency());
|
||||
sc.setRoundMode(rel.getRoundMode());
|
||||
sc.setCountryCode(rel.getCountryCode());
|
||||
sc.setScNote(rel.getScNote());
|
||||
sc.setCreateBy(version.getCreateBy());
|
||||
versionSurchargeMapper.insertPrcPriceSetVersionSurcharge(sc);
|
||||
saved.add(sc);
|
||||
rowSc.add(sc);
|
||||
written.add(rel.getScId());
|
||||
}
|
||||
// 勾选了但产品未关联(rel 无此 scId)的附加费:回退 prc_surcharge 主表配置
|
||||
if (pickedScIds != null)
|
||||
{
|
||||
for (PrcSurcharge m : surchargeMasters)
|
||||
{
|
||||
if (m == null || m.getScId() == null || written.contains(m.getScId())
|
||||
|| !pickedScIds.contains(m.getScId()))
|
||||
if (masterById == null)
|
||||
{
|
||||
continue;
|
||||
masterById = loadSurchargeMasterMap();
|
||||
}
|
||||
if (StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null
|
||||
PrcSurcharge m = masterById.get(picked.getScId());
|
||||
if (m == null || StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null
|
||||
|| "of".equalsIgnoreCase(m.getSsCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetVersionSurcharge sc = new PrcPriceSetVersionSurcharge();
|
||||
sc.setPsvId(version.getPsvId());
|
||||
sc.setPsdId(d.getPsdId());
|
||||
sc.setScId(m.getScId());
|
||||
sc.setProductCode(d.getProductCode());
|
||||
sc.setScName(m.getScName());
|
||||
sc.setFeeType(StringUtils.isEmpty(m.getFeeType()) ? "SURCHARGE" : m.getFeeType());
|
||||
sc.setChargeType(StringUtils.isEmpty(m.getChargeType()) ? "FIXED" : m.getChargeType());
|
||||
sc.setChargeValue(m.getChargeValue());
|
||||
sc.setCurrency(m.getCurrency());
|
||||
sc.setRoundMode(m.getRoundMode());
|
||||
sc.setCountryCode(m.getCountryCode());
|
||||
sc.setScNote(m.getScNote());
|
||||
sc.setCreateBy(version.getCreateBy());
|
||||
versionSurchargeMapper.insertPrcPriceSetVersionSurcharge(sc);
|
||||
saved.add(sc);
|
||||
rowSc.add(sc);
|
||||
written.add(m.getScId());
|
||||
row = new PrcSurchargeRel();
|
||||
row.setScId(m.getScId());
|
||||
row.setProductId(pspId);
|
||||
row.setProductCode(d.getProductCode());
|
||||
row.setDetailId(d.getPsdId());
|
||||
row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
row.setCountryCode(normalizeCountry(picked.getCountryCode() != null
|
||||
? picked.getCountryCode() : m.getCountryCode()));
|
||||
row.setScName(m.getScName());
|
||||
row.setFeeType(m.getFeeType());
|
||||
row.setChargeType(m.getChargeType());
|
||||
row.setChargeValue(m.getChargeValue());
|
||||
row.setCurrency(m.getCurrency());
|
||||
row.setRoundMode(m.getRoundMode());
|
||||
row.setSsCode(m.getSsCode());
|
||||
row.setScNote(m.getScNote());
|
||||
row.setCreateBy(SecurityUtils.getUsername());
|
||||
}
|
||||
surchargeRelMapper.insertPrcSurchargeRel(row);
|
||||
saved.add(row);
|
||||
written.add(picked.getScId());
|
||||
}
|
||||
d.setSurcharges(saved);
|
||||
}
|
||||
}
|
||||
|
||||
private PrcSurchargeRel buildSalesRel(PrcPriceSetDetail d, Long pspId, Long scId, String countryCode)
|
||||
{
|
||||
if (d == null || pspId == null || scId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
PrcSurcharge m = surchargeMapper.selectPrcSurchargeById(scId);
|
||||
if (m == null || StringUtils.isEmpty(m.getScName()) || m.getChargeValue() == null
|
||||
|| "of".equalsIgnoreCase(m.getSsCode()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
PrcSurchargeRel row = new PrcSurchargeRel();
|
||||
row.setScId(m.getScId());
|
||||
row.setProductId(pspId);
|
||||
row.setProductCode(d.getProductCode());
|
||||
row.setDetailId(d.getPsdId());
|
||||
row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
row.setCountryCode(normalizeCountry(countryCode != null ? countryCode : m.getCountryCode()));
|
||||
row.setScName(m.getScName());
|
||||
row.setFeeType(m.getFeeType());
|
||||
row.setChargeType(m.getChargeType());
|
||||
row.setChargeValue(m.getChargeValue());
|
||||
row.setCurrency(m.getCurrency());
|
||||
row.setRoundMode(m.getRoundMode());
|
||||
row.setSsCode(m.getSsCode());
|
||||
row.setScNote(m.getScNote());
|
||||
row.setCreateBy(SecurityUtils.getUsername());
|
||||
return row;
|
||||
}
|
||||
|
||||
private Map<Long, PrcSurcharge> loadSurchargeMasterMap()
|
||||
{
|
||||
Map<Long, PrcSurcharge> map = new HashMap<Long, PrcSurcharge>();
|
||||
List<PrcSurcharge> all = surchargeMapper.selectPrcSurchargeList(new PrcSurcharge());
|
||||
if (all == null)
|
||||
{
|
||||
return map;
|
||||
}
|
||||
for (PrcSurcharge m : all)
|
||||
{
|
||||
if (m != null && m.getScId() != null)
|
||||
{
|
||||
map.put(m.getScId(), m);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private String normalizeCountry(String code)
|
||||
{
|
||||
if (StringUtils.isEmpty(code))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return code.trim().toUpperCase();
|
||||
}
|
||||
|
||||
/** Delete all SALES surcharge rels under a package version (by detail + set-product ids). */
|
||||
private void deleteSalesRelsByPsvId(Long psvId)
|
||||
{
|
||||
if (psvId == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PrcPriceSetDetail dq = new PrcPriceSetDetail();
|
||||
dq.setPsvId(psvId);
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(dq);
|
||||
if (details != null && !details.isEmpty())
|
||||
{
|
||||
List<Long> psdIds = new ArrayList<Long>();
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
if (d != null && d.getPsdId() != null)
|
||||
{
|
||||
psdIds.add(d.getPsdId());
|
||||
}
|
||||
}
|
||||
d.setSurcharges(rowSc);
|
||||
if (!psdIds.isEmpty())
|
||||
{
|
||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(psdIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
}
|
||||
}
|
||||
version.setSurcharges(saved);
|
||||
PrcPriceSetProduct pq = new PrcPriceSetProduct();
|
||||
pq.setPsvId(psvId);
|
||||
List<PrcPriceSetProduct> products = setProductMapper.selectPrcPriceSetProductList(pq);
|
||||
if (products != null && !products.isEmpty())
|
||||
{
|
||||
List<Long> pspIds = new ArrayList<Long>();
|
||||
for (PrcPriceSetProduct p : products)
|
||||
{
|
||||
if (p != null && p.getPspId() != null)
|
||||
{
|
||||
pspIds.add(p.getPspId());
|
||||
}
|
||||
}
|
||||
if (!pspIds.isEmpty())
|
||||
{
|
||||
surchargeRelMapper.deleteByProductIdsAndPriceType(pspIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy SALES surcharge rels from source version details onto newly copied details
|
||||
* (match by product_code + country + zone + weight + product_country_id).
|
||||
*/
|
||||
private void copySalesSurchargeRelsFromVersion(Long srcPsvId, Long newPsvId, String createBy)
|
||||
{
|
||||
if (srcPsvId == null || newPsvId == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PrcPriceSetDetail sq = new PrcPriceSetDetail();
|
||||
sq.setPsvId(srcPsvId);
|
||||
List<PrcPriceSetDetail> srcDetails = detailMapper.selectPrcPriceSetDetailList(sq);
|
||||
PrcPriceSetDetail nq = new PrcPriceSetDetail();
|
||||
nq.setPsvId(newPsvId);
|
||||
List<PrcPriceSetDetail> newDetails = detailMapper.selectPrcPriceSetDetailList(nq);
|
||||
if (srcDetails == null || srcDetails.isEmpty() || newDetails == null || newDetails.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<Long> srcPsdIds = new ArrayList<Long>();
|
||||
for (PrcPriceSetDetail d : srcDetails)
|
||||
{
|
||||
if (d != null && d.getPsdId() != null)
|
||||
{
|
||||
srcPsdIds.add(d.getPsdId());
|
||||
}
|
||||
}
|
||||
List<PrcSurchargeRel> srcRels =
|
||||
surchargeRelMapper.selectByDetailIdsAndPriceType(srcPsdIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
if (srcRels == null || srcRels.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Map<Long, List<PrcSurchargeRel>> bySrcPsd = new HashMap<Long, List<PrcSurchargeRel>>();
|
||||
for (PrcSurchargeRel rel : srcRels)
|
||||
{
|
||||
if (rel == null || rel.getDetailId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcSurchargeRel> list = bySrcPsd.get(rel.getDetailId());
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<PrcSurchargeRel>();
|
||||
bySrcPsd.put(rel.getDetailId(), list);
|
||||
}
|
||||
list.add(rel);
|
||||
}
|
||||
Map<String, PrcPriceSetDetail> newByKey = new HashMap<String, PrcPriceSetDetail>();
|
||||
for (PrcPriceSetDetail nd : newDetails)
|
||||
{
|
||||
if (nd != null)
|
||||
{
|
||||
newByKey.put(detailMatchKey(nd), nd);
|
||||
}
|
||||
}
|
||||
for (PrcPriceSetDetail od : srcDetails)
|
||||
{
|
||||
if (od == null || od.getPsdId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetDetail nd = newByKey.get(detailMatchKey(od));
|
||||
if (nd == null || nd.getPsdId() == null || nd.getPspId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcSurchargeRel> rels = bySrcPsd.get(od.getPsdId());
|
||||
if (rels == null || rels.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (PrcSurchargeRel src : rels)
|
||||
{
|
||||
PrcSurchargeRel row = new PrcSurchargeRel();
|
||||
row.setScId(src.getScId());
|
||||
row.setProductId(nd.getPspId());
|
||||
row.setProductCode(nd.getProductCode());
|
||||
row.setDetailId(nd.getPsdId());
|
||||
row.setPriceType(PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
row.setCountryCode(normalizeCountry(src.getCountryCode()));
|
||||
row.setCreateBy(createBy);
|
||||
surchargeRelMapper.insertPrcSurchargeRel(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String detailMatchKey(PrcPriceSetDetail d)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return StringUtils.nvl(d.getProductCode(), "")
|
||||
+ "|" + StringUtils.nvl(d.getCountryCode(), "")
|
||||
+ "|" + StringUtils.nvl(d.getZoneCode(), "")
|
||||
+ "|" + StringUtils.nvl(d.getWeightLabel(), "")
|
||||
+ "|" + (d.getProductCountryId() == null ? "0" : d.getProductCountryId().toString())
|
||||
+ "|" + (d.getDiscountValue() == null ? "-1" : d.getDiscountValue().toPlainString());
|
||||
}
|
||||
|
||||
/** True when package already has a true draft (status=0). Pending(3) does not block copy. */
|
||||
|
||||
+36
-1
@@ -11,7 +11,7 @@ import com.bms.price.mapper.PrcSurchargeRelMapper;
|
||||
import com.bms.price.service.IPrcSurchargeRelService;
|
||||
|
||||
/**
|
||||
* 附加费与产品/明细关联 服务层实现
|
||||
* 附加费与产品/明细/套餐关联 服务层实现
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
@@ -23,9 +23,19 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService
|
||||
|
||||
@Override
|
||||
public List<PrcSurchargeRel> selectRelByProductCode(String productCode)
|
||||
{
|
||||
return selectRelByProductCode(productCode, PrcSurchargeRel.PRICE_TYPE_PRODUCT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcSurchargeRel> selectRelByProductCode(String productCode, String priceType)
|
||||
{
|
||||
PrcSurchargeRel query = new PrcSurchargeRel();
|
||||
query.setProductCode(productCode);
|
||||
if (StringUtils.isNotEmpty(priceType))
|
||||
{
|
||||
query.setPriceType(priceType.trim().toUpperCase());
|
||||
}
|
||||
return relMapper.selectPrcSurchargeRelList(query);
|
||||
}
|
||||
|
||||
@@ -39,6 +49,18 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService
|
||||
public int insertPrcSurchargeRel(PrcSurchargeRel rel)
|
||||
{
|
||||
validate(rel);
|
||||
if (StringUtils.isEmpty(rel.getPriceType()))
|
||||
{
|
||||
rel.setPriceType(PrcSurchargeRel.PRICE_TYPE_PRODUCT);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(rel.getCountryCode()))
|
||||
{
|
||||
rel.setCountryCode(rel.getCountryCode().trim().toUpperCase());
|
||||
}
|
||||
else
|
||||
{
|
||||
rel.setCountryCode(null);
|
||||
}
|
||||
return relMapper.insertPrcSurchargeRel(rel);
|
||||
}
|
||||
|
||||
@@ -76,5 +98,18 @@ public class PrcSurchargeRelServiceImpl implements IPrcSurchargeRelService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.surchargeRel.productCode.empty"));
|
||||
}
|
||||
String type = StringUtils.isEmpty(rel.getPriceType())
|
||||
? PrcSurchargeRel.PRICE_TYPE_PRODUCT : rel.getPriceType().trim().toUpperCase();
|
||||
if (!PrcSurchargeRel.PRICE_TYPE_PRODUCT.equals(type)
|
||||
&& !PrcSurchargeRel.PRICE_TYPE_SALES.equals(type)
|
||||
&& !PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(type))
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.surchargeRel.priceType.invalid"));
|
||||
}
|
||||
rel.setPriceType(type);
|
||||
if (PrcSurchargeRel.PRICE_TYPE_SALES.equals(type) && rel.getDetailId() == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.surchargeRel.detailId.empty"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
-41
@@ -25,12 +25,12 @@ import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
|
||||
import com.bms.price.domain.BmsProduct;
|
||||
import com.bms.price.domain.BmsProductCountry;
|
||||
import com.bms.price.domain.BmsRedlineProductCountry;
|
||||
import com.bms.price.domain.PrcCustomerPriceSet;
|
||||
import com.bms.price.domain.PrcPriceSet;
|
||||
import com.bms.price.domain.PrcPriceSetDetail;
|
||||
import com.bms.price.domain.PrcPriceSetProduct;
|
||||
import com.bms.price.domain.PrcPriceSetVersion;
|
||||
import com.bms.price.domain.PrcPriceSetVersionSurcharge;
|
||||
import com.bms.price.domain.PrcQuote;
|
||||
import com.bms.price.domain.PrcQuoteFee;
|
||||
import com.bms.price.domain.PrcSurchargeRel;
|
||||
@@ -43,12 +43,12 @@ import com.bms.price.domain.vo.TmsPriceQuoteResult;
|
||||
import com.bms.price.enums.BillCustomerActiveStatus;
|
||||
import com.bms.price.mapper.BmsProductCountryMapper;
|
||||
import com.bms.price.mapper.BmsProductMapper;
|
||||
import com.bms.price.mapper.BmsRedlineProductCountryMapper;
|
||||
import com.bms.price.mapper.PrcCustomerPriceSetMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetDetailMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetProductMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetVersionMapper;
|
||||
import com.bms.price.mapper.PrcPriceSetVersionSurchargeMapper;
|
||||
import com.bms.price.mapper.PrcQuoteFeeMapper;
|
||||
import com.bms.price.mapper.PrcQuoteMapper;
|
||||
import com.bms.price.mapper.PrcSurchargeRelMapper;
|
||||
@@ -87,9 +87,6 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
@Autowired
|
||||
private PrcPriceSetVersionMapper versionMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetVersionSurchargeMapper versionSurchargeMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetDetailMapper detailMapper;
|
||||
|
||||
@@ -99,6 +96,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
@Autowired
|
||||
private BmsProductCountryMapper productCountryMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsRedlineProductCountryMapper redlineProductCountryMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsProductMapper productMapper;
|
||||
|
||||
@@ -879,6 +879,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
r.setRoundMode(mode);
|
||||
r.setFromCache(false);
|
||||
fillProductAmountAndPay(r, catalog, weight, rm, mode);
|
||||
fillRedlineAmount(r, weight, rm);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -934,6 +935,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
r.setRoundMode(mode);
|
||||
r.setFromCache(false);
|
||||
fillProductAmountAndPay(r, base, weight, rm, mode);
|
||||
fillRedlineAmount(r, weight, rm);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -966,6 +968,50 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
r.setPayFeeDetails(payLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redline catalog amount from redline_product_country (same match keys as product catalog).
|
||||
*/
|
||||
private void fillRedlineAmount(TmsPriceQuoteResult r, BigDecimal weight, RoundingMode rm)
|
||||
{
|
||||
if (r == null || weight == null || StringUtils.isEmpty(r.getProductCode()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BmsRedlineProductCountry base = redlineProductCountryMapper.selectMatchByProductCountryWeight(
|
||||
r.getProductCode(), r.getCountryCode(), r.getCountryName(), r.getZoneCode(), weight, null);
|
||||
if (base == null || base.getFreightPrice() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (base.getDetailId() != null)
|
||||
{
|
||||
r.setRedlineCountryId(base.getDetailId());
|
||||
}
|
||||
BigDecimal baseReg = base.getRegistrationFee() == null ? BigDecimal.ZERO : base.getRegistrationFee();
|
||||
BigDecimal freight = base.getFreightPrice().multiply(weight).setScale(2, rm);
|
||||
BigDecimal regScaled = baseReg.setScale(2, rm);
|
||||
r.setRedlineAmount(freight.add(regScaled).setScale(2, rm));
|
||||
// Stash freight/reg on existing payFeeDetails for income fee-line apportionment
|
||||
if (r.getPayFeeDetails() != null)
|
||||
{
|
||||
for (TmsPriceQuoteFeeLine line : r.getPayFeeDetails())
|
||||
{
|
||||
if (line == null || line.getFeeType() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (FEE_BASIC.equals(line.getFeeType()))
|
||||
{
|
||||
line.setRedlineAmount(freight);
|
||||
}
|
||||
else if (FEE_REG.equals(line.getFeeType()))
|
||||
{
|
||||
line.setRedlineAmount(regScaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applySurchargesAndFees(TmsPriceQuoteResult result)
|
||||
{
|
||||
BigDecimal freightAmount = nvl(result.getFreightAmount());
|
||||
@@ -973,20 +1019,27 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
RoundingMode rm = resolveRoundingMode(result.getRoundMode());
|
||||
String currency = normalizeCurrency(result.getCurrency());
|
||||
|
||||
List<PrcSurchargeRel> rels = loadMatchedSurcharges(result);
|
||||
List<PrcSurchargeRel> salesRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
List<PrcSurchargeRel> productRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_PRODUCT);
|
||||
List<PrcSurchargeRel> redlineRels = loadMatchedSurcharges(result, PrcSurchargeRel.PRICE_TYPE_REDLINE);
|
||||
|
||||
List<TmsPriceQuoteFeeLine> lines = new ArrayList<TmsPriceQuoteFeeLine>();
|
||||
BigDecimal productFreight = productAmountOf(result.getPayFeeDetails(), FEE_BASIC);
|
||||
BigDecimal productReg = productAmountOf(result.getPayFeeDetails(), FEE_REG);
|
||||
BigDecimal redlineFreight = redlineAmountOf(result.getPayFeeDetails(), FEE_BASIC);
|
||||
BigDecimal redlineReg = redlineAmountOf(result.getPayFeeDetails(), FEE_REG);
|
||||
TmsPriceQuoteFeeLine basicLine = feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, freightAmount, currency, result.getRoundMode(), null);
|
||||
basicLine.setProductAmount(productFreight);
|
||||
basicLine.setRedlineAmount(redlineFreight);
|
||||
lines.add(basicLine);
|
||||
TmsPriceQuoteFeeLine regLine = feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, registrationFee, currency, result.getRoundMode(), null);
|
||||
regLine.setProductAmount(productReg);
|
||||
regLine.setRedlineAmount(redlineReg);
|
||||
lines.add(regLine);
|
||||
|
||||
BigDecimal surchargeTotal = BigDecimal.ZERO;
|
||||
Map<Long, TmsPriceQuoteFeeLine> surchargeLines = new LinkedHashMap<Long, TmsPriceQuoteFeeLine>();
|
||||
for (PrcSurchargeRel rel : rels)
|
||||
for (PrcSurchargeRel rel : salesRels)
|
||||
{
|
||||
BigDecimal itemAmount = calcSurchargeAmount(rel, result.getWeight(), freightAmount);
|
||||
if (itemAmount == null)
|
||||
@@ -1017,41 +1070,90 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
result.setPayableTotal(freightAmount.add(registrationFee).add(surchargeTotal).setScale(2, rm));
|
||||
result.setIncomeTotal(result.getPayableTotal());
|
||||
result.setFeeDetails(lines);
|
||||
|
||||
BigDecimal productExtra = sumSurchargeAmounts(productRels, result.getWeight(), freightAmount, rm);
|
||||
BigDecimal redlineExtra = sumSurchargeAmounts(redlineRels, result.getWeight(), freightAmount, rm);
|
||||
if (result.getProductAmount() != null || productExtra.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
result.setProductAmount(nvl(result.getProductAmount()).add(productExtra).setScale(2, rm));
|
||||
}
|
||||
if (result.getCostAmount() != null || productExtra.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
result.setCostAmount(nvl(result.getCostAmount()).add(productExtra).setScale(2, rm));
|
||||
}
|
||||
if (result.getPayTotal() != null || productExtra.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
result.setPayTotal(nvl(result.getPayTotal()).add(productExtra).setScale(2, rm));
|
||||
}
|
||||
if (result.getRedlineAmount() != null || redlineExtra.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
result.setRedlineAmount(nvl(result.getRedlineAmount()).add(redlineExtra).setScale(2, rm));
|
||||
}
|
||||
else if (redlineExtra.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
result.setRedlineAmount(redlineExtra);
|
||||
}
|
||||
}
|
||||
|
||||
private List<PrcSurchargeRel> loadMatchedSurcharges(TmsPriceQuoteResult result)
|
||||
private BigDecimal sumSurchargeAmounts(List<PrcSurchargeRel> rels, BigDecimal weight,
|
||||
BigDecimal freightAmount, RoundingMode rm)
|
||||
{
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
if (rels == null)
|
||||
{
|
||||
return total;
|
||||
}
|
||||
for (PrcSurchargeRel rel : rels)
|
||||
{
|
||||
BigDecimal item = calcSurchargeAmount(rel, weight, freightAmount);
|
||||
if (item != null)
|
||||
{
|
||||
total = total.add(item);
|
||||
}
|
||||
}
|
||||
return total.setScale(2, rm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load matched surcharge rels by price_type.
|
||||
* SALES: package detail_id = psd_id.
|
||||
* PRODUCT / REDLINE: product catalog associations by product_code + country/detail.
|
||||
*/
|
||||
private List<PrcSurchargeRel> loadMatchedSurcharges(TmsPriceQuoteResult result, String priceType)
|
||||
{
|
||||
List<PrcSurchargeRel> matched = new ArrayList<PrcSurchargeRel>();
|
||||
if (result == null || StringUtils.isEmpty(result.getProductCode()))
|
||||
if (result == null || StringUtils.isEmpty(priceType))
|
||||
{
|
||||
return matched;
|
||||
}
|
||||
boolean packageHit = SRC_FIXED.equals(result.getMatchSource()) || SRC_DISCOUNT.equals(result.getMatchSource());
|
||||
// Package sales use version surcharge snapshot only (empty snapshot = no surcharge).
|
||||
if (packageHit)
|
||||
if (PrcSurchargeRel.PRICE_TYPE_SALES.equals(priceType))
|
||||
{
|
||||
if (result.getPsdId() == null)
|
||||
{
|
||||
return matched;
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> snaps =
|
||||
versionSurchargeMapper.selectByPsdIds(Collections.singletonList(result.getPsdId()));
|
||||
if (snaps == null || snaps.isEmpty())
|
||||
List<PrcSurchargeRel> rels = surchargeRelMapper.selectByDetailIdsAndPriceType(
|
||||
Collections.singletonList(result.getPsdId()), PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||
if (rels == null)
|
||||
{
|
||||
return matched;
|
||||
}
|
||||
for (PrcPriceSetVersionSurcharge snap : snaps)
|
||||
for (PrcSurchargeRel rel : rels)
|
||||
{
|
||||
PrcSurchargeRel rel = toRelFromVersionSnap(snap);
|
||||
if (surchargeApplies(rel, result))
|
||||
if (surchargeApplies(rel, result, true))
|
||||
{
|
||||
matched.add(rel);
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
if (StringUtils.isEmpty(result.getProductCode()))
|
||||
{
|
||||
return matched;
|
||||
}
|
||||
PrcSurchargeRel query = new PrcSurchargeRel();
|
||||
query.setProductCode(result.getProductCode());
|
||||
query.setPriceType(priceType);
|
||||
List<PrcSurchargeRel> rels = surchargeRelMapper.selectPrcSurchargeRelList(query);
|
||||
if (rels == null)
|
||||
{
|
||||
@@ -1059,7 +1161,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
for (PrcSurchargeRel rel : rels)
|
||||
{
|
||||
if (surchargeApplies(rel, result))
|
||||
if (surchargeApplies(rel, result, false))
|
||||
{
|
||||
matched.add(rel);
|
||||
}
|
||||
@@ -1067,28 +1169,10 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return matched;
|
||||
}
|
||||
|
||||
private PrcSurchargeRel toRelFromVersionSnap(PrcPriceSetVersionSurcharge snap)
|
||||
{
|
||||
PrcSurchargeRel rel = new PrcSurchargeRel();
|
||||
if (snap == null)
|
||||
{
|
||||
return rel;
|
||||
}
|
||||
rel.setScId(snap.getScId());
|
||||
rel.setProductCode(snap.getProductCode());
|
||||
rel.setScName(snap.getScName());
|
||||
rel.setFeeType(snap.getFeeType());
|
||||
rel.setChargeType(snap.getChargeType());
|
||||
rel.setChargeValue(snap.getChargeValue());
|
||||
rel.setCurrency(snap.getCurrency());
|
||||
rel.setRoundMode(snap.getRoundMode());
|
||||
rel.setCountryCode(snap.getCountryCode());
|
||||
rel.setScNote(snap.getScNote());
|
||||
rel.setSsCode("on");
|
||||
return rel;
|
||||
}
|
||||
|
||||
private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result)
|
||||
/**
|
||||
* @param salesRel true = SALES (detail_id already = psd_id); false = PRODUCT/REDLINE detail match
|
||||
*/
|
||||
private boolean surchargeApplies(PrcSurchargeRel rel, TmsPriceQuoteResult result, boolean salesRel)
|
||||
{
|
||||
if (rel == null)
|
||||
{
|
||||
@@ -1103,10 +1187,18 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (salesRel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (rel.getDetailId() == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (PrcSurchargeRel.PRICE_TYPE_REDLINE.equals(rel.getPriceType()))
|
||||
{
|
||||
return result.getRedlineCountryId() != null && rel.getDetailId().equals(result.getRedlineCountryId());
|
||||
}
|
||||
return result.getProductCountryId() != null && rel.getDetailId().equals(result.getProductCountryId());
|
||||
}
|
||||
|
||||
@@ -1409,6 +1501,23 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Read redline catalog amount for a fee type from payFeeDetails. */
|
||||
private static BigDecimal redlineAmountOf(List<TmsPriceQuoteFeeLine> payLines, String feeType)
|
||||
{
|
||||
if (payLines == null || feeType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (TmsPriceQuoteFeeLine line : payLines)
|
||||
{
|
||||
if (line != null && feeType.equals(line.getFeeType()))
|
||||
{
|
||||
return line.getRedlineAmount();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TmsPriceQuoteFeeLine feeLine(String feeType, String feeName, Long scId, BigDecimal amount,
|
||||
String currency, String roundMode, String remark)
|
||||
{
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.bms.price.task;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.common.core.redis.RedisCache;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.price.domain.BmsRedlineProductImportTask;
|
||||
import com.bms.price.service.IBmsRedlineProductService;
|
||||
|
||||
/**
|
||||
* Async runner for product Excel import (avoids HTTP timeout on large workbooks).
|
||||
*/
|
||||
@Component
|
||||
public class BmsRedlineProductImportAsyncRunner
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BmsRedlineProductImportAsyncRunner.class);
|
||||
|
||||
public static final String CACHE_KEY_PREFIX = "price:redline:import:task:";
|
||||
public static final int TTL_MINUTES = 60;
|
||||
|
||||
@Autowired
|
||||
private IBmsRedlineProductService productService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
/** Background all-country (Weishi catalog) import. */
|
||||
@Async("threadPoolTaskExecutor")
|
||||
public void runImportAll(String taskId, byte[] bytes, String filename, boolean updateSupport, String operName,
|
||||
Date effectiveDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
|
||||
String message = productService.importProductExcel(bytes, filename, updateSupport, operName, taskId,
|
||||
effectiveDate);
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_SUCCESS, 100, message, null);
|
||||
log.info("async product import done, taskId={}", taskId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("async product import failed, taskId={}", taskId, e);
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_FAIL, 100, "failed", errorMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
/** Background single-country cost-sheet import. */
|
||||
@Async("threadPoolTaskExecutor")
|
||||
public void runImportCountry(String taskId, byte[] bytes, String filename, String countryCode, String operName,
|
||||
Date effectiveDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
|
||||
String message = productService.importCountryProductExcel(bytes, filename, countryCode, operName, taskId,
|
||||
effectiveDate);
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_SUCCESS, 100, message, null);
|
||||
log.info("async product country import done, taskId={}, country={}", taskId, countryCode);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("async product country import failed, taskId={}, country={}", taskId, countryCode, e);
|
||||
put(taskId, BmsRedlineProductImportTask.STATUS_FAIL, 100, "failed", errorMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
public void put(String taskId, String status, int percent, String message, String error)
|
||||
{
|
||||
BmsRedlineProductImportTask task = new BmsRedlineProductImportTask();
|
||||
task.setTaskId(taskId);
|
||||
task.setStatus(status);
|
||||
task.setPercent(percent);
|
||||
task.setMessage(message);
|
||||
task.setError(error);
|
||||
redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public BmsRedlineProductImportTask get(String taskId)
|
||||
{
|
||||
return redisCache.getCacheObject(CACHE_KEY_PREFIX + taskId);
|
||||
}
|
||||
|
||||
private static String errorMessage(Exception e)
|
||||
{
|
||||
String msg = e.getMessage();
|
||||
if (StringUtils.isEmpty(msg))
|
||||
{
|
||||
msg = e.getClass().getSimpleName();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user