修改产品套餐
This commit is contained in:
@@ -60,6 +60,11 @@ public interface IPrcPriceSetService
|
||||
|
||||
public List<PrcPriceSetDetail> selectDetailList(PrcPriceSetDetail detail);
|
||||
|
||||
/**
|
||||
* Load all detail rows for one product under a version (no pagination).
|
||||
*/
|
||||
public List<PrcPriceSetDetail> selectDetailListByProduct(Long psvId, String productCode);
|
||||
|
||||
/**
|
||||
* 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 列表。
|
||||
* 返回 { products: [{code,name}], countries: [{code,name}], zones: [{code,name}] }
|
||||
@@ -105,11 +110,20 @@ public interface IPrcPriceSetService
|
||||
public String submitImportDetailPricesAsync(Long priceSetId, Long psvId, MultipartFile file, String operName) throws Exception;
|
||||
|
||||
/**
|
||||
* Sync worker: parse bytes and update FIXED detail prices matched by
|
||||
* product + country + zone(if any) + weight band.
|
||||
* Sync worker: parse bytes and upsert FIXED detail prices matched by
|
||||
* product + country + zone(if any) + weight band (insert when missing).
|
||||
*/
|
||||
public String importDetailPrices(Long priceSetId, Long psvId, byte[] bytes, String filename, String operName) throws Exception;
|
||||
|
||||
/** Sync worker with optional taskId for progress reporting. */
|
||||
public String importDetailPrices(Long priceSetId, Long psvId, byte[] bytes, String filename, String operName, String taskId) throws Exception;
|
||||
|
||||
/**
|
||||
* Submit async pull of product library into version details.
|
||||
* @return taskId for polling getSaveTask
|
||||
*/
|
||||
public String submitPullFromLibraryAsync(Long priceSetId, Long psvId, boolean force, String operName);
|
||||
|
||||
/** Sync worker: replace (or skip) version details from product / product_country. */
|
||||
public String pullFromLibrary(Long priceSetId, Long psvId, boolean force, String operName, String taskId);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import com.bms.price.domain.PrcPriceSetProduct;
|
||||
import com.bms.price.domain.PrcQuote;
|
||||
import com.bms.price.domain.vo.TmsPriceQuoteRequest;
|
||||
import com.bms.price.domain.vo.TmsPriceQuoteResult;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -27,4 +29,11 @@ public interface ITmsPriceQuoteService
|
||||
List<PrcQuote> selectQuoteList(PrcQuote query);
|
||||
|
||||
TmsPriceQuoteResult getQuote(String quoteId);
|
||||
|
||||
/**
|
||||
* Products under the customer's bound effective package version (for trial dropdown).
|
||||
* @param customerCode required
|
||||
* @param bizDate optional; default today (same as-of window as quote)
|
||||
*/
|
||||
List<PrcPriceSetProduct> listBoundPackageProducts(String customerCode, Date bizDate);
|
||||
}
|
||||
|
||||
+361
-104
@@ -22,20 +22,24 @@ import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.MessageUtils;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
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.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;
|
||||
import com.bms.price.mapper.BmsProductMapper;
|
||||
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.PrcSurchargeMapper;
|
||||
@@ -90,6 +94,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
@Autowired
|
||||
private BmsProductCountryMapper productCountryMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetProductMapper setProductMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsProductMapper productMapper;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private IBmsProductService productService;
|
||||
@@ -399,9 +409,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
nv.setCreateBy(createBy);
|
||||
versionMapper.insertPrcPriceSetVersion(nv);
|
||||
|
||||
setProductMapper.copyFromVersion(sourcePsvId, nv.getPsvId());
|
||||
int detailRows = detailMapper.copyDetailsFromVersion(sourcePsvId, nv.getPsvId());
|
||||
if (detailRows > 0)
|
||||
{
|
||||
setProductMapper.bindDetailPspByPsvId(nv.getPsvId());
|
||||
versionSurchargeMapper.copySurchargesFromVersion(sourcePsvId, nv.getPsvId(), createBy);
|
||||
}
|
||||
refreshVersionStatus(set.getPriceSetId());
|
||||
@@ -428,6 +440,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
requireSet(version.getPriceSetId());
|
||||
versionSurchargeMapper.deleteByPsvId(psvId);
|
||||
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
||||
setProductMapper.deleteByPsvId(psvId);
|
||||
int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
|
||||
refreshVersionStatus(version.getPriceSetId());
|
||||
return rows;
|
||||
@@ -587,16 +600,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
if (details.isEmpty())
|
||||
{
|
||||
items.add(new PrcPriceSetCheckItem("FIXED 明细完整性", "PASS",
|
||||
"\u65e0\u5957\u9910\u660e\u7ec6\uff1a\u8ba1\u8d39\u53d6 product / product_country \u9ed8\u8ba4\u8fd0\u8d39\u4e0e\u6302\u53f7\u8d39"));
|
||||
items.add(new PrcPriceSetCheckItem("FIXED \u660e\u7ec6\u5b8c\u6574\u6027", "FAIL",
|
||||
"\u65e0\u5957\u9910\u660e\u7ec6\uff1a\u56fa\u5b9a\u4ef7\u987b\u5148\u62c9\u53d6\u4ea7\u54c1\u5217\u8868\u6216\u5bfc\u5165 Excel \u5efa\u4ef7"));
|
||||
}
|
||||
else if (incomplete > 0)
|
||||
{
|
||||
items.add(new PrcPriceSetCheckItem("FIXED 明细完整性", "FAIL", incomplete + " \u6761\u660e\u7ec6\u7f3a\u5c11\u4ea7\u54c1/\u56fd\u5bb6/\u91cd\u91cf\u6bb5"));
|
||||
items.add(new PrcPriceSetCheckItem("FIXED \u660e\u7ec6\u5b8c\u6574\u6027", "FAIL", incomplete + " \u6761\u660e\u7ec6\u7f3a\u5c11\u4ea7\u54c1/\u56fd\u5bb6/\u91cd\u91cf\u6bb5"));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.add(new PrcPriceSetCheckItem("FIXED 明细完整性", "PASS", details.size() + " \u6761\u8986\u76d6\u660e\u7ec6\uff08\u672a\u8986\u76d6\u7684\u4ea7\u54c1\u56fd\u5bb6\u91cd\u91cf\u6bb5\u4ecd\u8d70\u9ed8\u8ba4\u4ef7\uff09"));
|
||||
items.add(new PrcPriceSetCheckItem("FIXED \u660e\u7ec6\u5b8c\u6574\u6027", "PASS", details.size() + " \u6761\u5957\u9910\u9500\u552e\u4ef7\u660e\u7ec6\uff08\u4e0d\u56de\u9000 product_country\uff09"));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -673,11 +686,13 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
|
||||
if (TYPE_FIXED.equals(setType) && !details.isEmpty())
|
||||
{
|
||||
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u6709\u8986\u76d6\u660e\u7ec6\u65f6\u4f18\u5148\u53d6\u5957\u9910\u56fa\u5b9a\u4ef7\uff1b\u672a\u547d\u4e2d\u5219\u56de\u9000 product_country"));
|
||||
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO",
|
||||
"\u56fa\u5b9a\u4ef7\u4ec5\u4f7f\u7528\u5957\u9910\u660e\u7ec6\uff1b\u672a\u547d\u4e2d\u5219\u4e0d\u8ba1\u8d39\uff08\u4e0d\u56de\u9000 product_country\uff09"));
|
||||
}
|
||||
else if (TYPE_FIXED.equals(setType))
|
||||
{
|
||||
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO", "\u65e0\u5957\u9910\u660e\u7ec6\uff0c\u5168\u90e8\u8d70 product_country \u9ed8\u8ba4\u8fd0\u8d39/\u6302\u53f7\u8d39"));
|
||||
items.add(new PrcPriceSetCheckItem("\u8fd0\u8d39\u5355\u4ef7 vs \u9ed8\u8ba4\u7b49\u7ea7\u4ef7\u504f\u5dee", "INFO",
|
||||
"\u65e0\u5957\u9910\u660e\u7ec6\u65f6\u56fa\u5b9a\u4ef7\u4e0d\u8ba1\u8d39\uff0c\u8bf7\u5148\u62c9\u53d6\u6216\u5bfc\u5165\u5efa\u4ef7"));
|
||||
}
|
||||
else if (TYPE_DISCOUNT.equals(setType))
|
||||
{
|
||||
@@ -695,6 +710,19 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceSetDetail> selectDetailListByProduct(Long psvId, String productCode)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return new ArrayList<PrcPriceSetDetail>();
|
||||
}
|
||||
PrcPriceSetDetail q = new PrcPriceSetDetail();
|
||||
q.setPsvId(psvId);
|
||||
q.setProductCode(productCode.trim());
|
||||
return selectDetailList(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> selectDetailOptions(Long psvId)
|
||||
{
|
||||
@@ -797,6 +825,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
assertChildEditable(db);
|
||||
validateDetail(db, detail);
|
||||
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());
|
||||
@@ -835,6 +864,56 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String submitPullFromLibraryAsync(Long priceSetId, Long psvId, boolean force, String operName)
|
||||
{
|
||||
PrcPriceSet db = requireSet(priceSetId);
|
||||
assertChildEditable(db);
|
||||
if (psvId == null)
|
||||
{
|
||||
psvId = resolveCurrentPsvId(db);
|
||||
}
|
||||
if (psvId == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
|
||||
}
|
||||
String taskId = IdUtils.fastSimpleUUID();
|
||||
saveAsyncRunner.put(taskId, PrcPriceSetSaveTask.STATUS_PENDING, 0, "queued", priceSetId, null);
|
||||
saveAsyncRunner.runPullLibrary(taskId, priceSetId, psvId, force, operName);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String pullFromLibrary(Long priceSetId, Long psvId, boolean force, String operName, String taskId)
|
||||
{
|
||||
PrcPriceSet set = requireSet(priceSetId);
|
||||
assertChildEditable(set);
|
||||
set.setUpdateBy(operName);
|
||||
set.setCreateBy(operName);
|
||||
set.setSaveTaskId(taskId);
|
||||
set.setPullFromLibrary("Y");
|
||||
set.setForcePullLibrary(force ? "Y" : "N");
|
||||
if (psvId == null)
|
||||
{
|
||||
psvId = resolveCurrentPsvId(set);
|
||||
}
|
||||
PrcPriceSetVersion version = null;
|
||||
if (psvId != null)
|
||||
{
|
||||
version = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||
}
|
||||
if (version == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
|
||||
}
|
||||
reportImportProgress(taskId, priceSetId, 20, "pulling library");
|
||||
replaceVersionDetailsFromLibrary(set, version);
|
||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||
int count = detailMapper.countByPsvId(version.getPsvId());
|
||||
return MessageUtils.message("price.set.pull.success", Integer.valueOf(count));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String importDetailPrices(Long priceSetId, Long psvId, byte[] bytes, String filename, String operName) throws Exception
|
||||
@@ -856,19 +935,24 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
psvId = resolveCurrentPsvId(db);
|
||||
}
|
||||
if (psvId == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
|
||||
}
|
||||
reportImportProgress(taskId, priceSetId, 20, "parsing excel");
|
||||
Map<String, List<BmsProductCountry>> byProduct = productService.parseQuoteExcelByProduct(bytes, filename);
|
||||
reportImportProgress(taskId, priceSetId, 50, "matching details");
|
||||
if (byProduct == null || byProduct.isEmpty())
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noPriceRows"));
|
||||
}
|
||||
|
||||
PrcPriceSetDetail q = new PrcPriceSetDetail();
|
||||
q.setPriceSetId(priceSetId);
|
||||
if (psvId != null)
|
||||
{
|
||||
q.setPsvId(psvId);
|
||||
}
|
||||
q.setPsvId(psvId);
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(q);
|
||||
if (details == null || details.isEmpty())
|
||||
if (details == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.import.noDetails"));
|
||||
details = new ArrayList<PrcPriceSetDetail>();
|
||||
}
|
||||
Map<String, List<PrcPriceSetDetail>> detailIndex = new HashMap<String, List<PrcPriceSetDetail>>();
|
||||
for (PrcPriceSetDetail d : details)
|
||||
@@ -886,61 +970,139 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
list.add(d);
|
||||
}
|
||||
|
||||
int updated = 0;
|
||||
int unmatched = 0;
|
||||
int skippedProduct = 0;
|
||||
int inserted = 0;
|
||||
int skippedFee = 0;
|
||||
Set<Long> touched = new HashSet<Long>();
|
||||
int productTotal = byProduct.size();
|
||||
int productIndex = 0;
|
||||
for (Map.Entry<String, List<BmsProductCountry>> e : byProduct.entrySet())
|
||||
{
|
||||
productIndex++;
|
||||
String productCode = e.getKey();
|
||||
reportImportProgress(taskId, priceSetId,
|
||||
20 + (int) Math.floor(70.0 * productIndex / Math.max(productTotal, 1)),
|
||||
"importing " + productCode + " (" + productIndex + "/" + productTotal + ")");
|
||||
List<PrcPriceSetDetail> candidates = detailIndex.get(productCode);
|
||||
if (candidates == null || candidates.isEmpty())
|
||||
if (candidates == null)
|
||||
{
|
||||
skippedProduct++;
|
||||
continue;
|
||||
candidates = new ArrayList<PrcPriceSetDetail>();
|
||||
detailIndex.put(productCode, candidates);
|
||||
}
|
||||
String productName = resolveImportProductName(psvId, productCode, candidates);
|
||||
// Excel import is one of the only two paths that create set-product rows
|
||||
Long pspId = ensureSetProduct(priceSetId, psvId, productCode, productName);
|
||||
for (BmsProductCountry row : e.getValue())
|
||||
{
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BigDecimal freight = row.getFreightPrice();
|
||||
BigDecimal registration = row.getRegistrationFee();
|
||||
List<PrcPriceSetDetail> hits = matchImportDetails(candidates, row);
|
||||
if (hits.isEmpty())
|
||||
if (!hits.isEmpty())
|
||||
{
|
||||
unmatched++;
|
||||
continue;
|
||||
}
|
||||
for (PrcPriceSetDetail hit : hits)
|
||||
{
|
||||
if (hit.getPsdId() == null || touched.contains(hit.getPsdId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Empty import freight/registration: do not overwrite existing detail values.
|
||||
BigDecimal freight = row.getFreightPrice();
|
||||
BigDecimal registration = row.getRegistrationFee();
|
||||
if (freight == null && registration == null)
|
||||
{
|
||||
skippedFee++;
|
||||
continue;
|
||||
}
|
||||
detailMapper.updateDetailPrices(hit.getPsdId(), freight, registration);
|
||||
if (freight != null)
|
||||
for (PrcPriceSetDetail hit : hits)
|
||||
{
|
||||
hit.setFvValue(freight);
|
||||
if (hit.getPsdId() == null || touched.contains(hit.getPsdId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
detailMapper.updateDetailPrices(hit.getPsdId(), freight, registration);
|
||||
if (freight != null)
|
||||
{
|
||||
hit.setFvValue(freight);
|
||||
}
|
||||
if (registration != null)
|
||||
{
|
||||
hit.setFvRegistrationFee(registration);
|
||||
}
|
||||
touched.add(hit.getPsdId());
|
||||
updated++;
|
||||
}
|
||||
if (registration != null)
|
||||
{
|
||||
hit.setFvRegistrationFee(registration);
|
||||
}
|
||||
touched.add(hit.getPsdId());
|
||||
updated++;
|
||||
continue;
|
||||
}
|
||||
if (freight == null && registration == null)
|
||||
{
|
||||
skippedFee++;
|
||||
continue;
|
||||
}
|
||||
PrcPriceSetDetail created = buildDetailFromImportRow(
|
||||
priceSetId, psvId, productCode, productName, row, freight, registration);
|
||||
created.setPspId(pspId);
|
||||
detailMapper.insertPrcPriceSetDetail(created);
|
||||
candidates.add(created);
|
||||
if (created.getPsdId() != null)
|
||||
{
|
||||
touched.add(created.getPsdId());
|
||||
}
|
||||
inserted++;
|
||||
}
|
||||
}
|
||||
setProductMapper.bindDetailPspByPsvId(psvId);
|
||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||
return MessageUtils.message("price.set.import.upsertSuccess",
|
||||
Integer.valueOf(inserted), Integer.valueOf(updated), Integer.valueOf(skippedFee));
|
||||
}
|
||||
|
||||
private String resolveImportProductName(Long psvId, String productCode, List<PrcPriceSetDetail> candidates)
|
||||
{
|
||||
if (candidates != null)
|
||||
{
|
||||
for (PrcPriceSetDetail d : candidates)
|
||||
{
|
||||
if (d != null && StringUtils.isNotEmpty(d.getProductName()))
|
||||
{
|
||||
return d.getProductName();
|
||||
}
|
||||
}
|
||||
}
|
||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||
return MessageUtils.message("price.set.import.success",
|
||||
Integer.valueOf(updated), Integer.valueOf(unmatched), Integer.valueOf(skippedProduct));
|
||||
if (psvId != null && StringUtils.isNotEmpty(productCode))
|
||||
{
|
||||
PrcPriceSetProduct sp = setProductMapper.selectByPsvIdAndCode(psvId, productCode);
|
||||
if (sp != null && StringUtils.isNotEmpty(sp.getProductName()))
|
||||
{
|
||||
return sp.getProductName();
|
||||
}
|
||||
}
|
||||
BmsProduct lib = productMapper.selectBmsProductByCode(productCode);
|
||||
if (lib != null && StringUtils.isNotEmpty(lib.getProductName()))
|
||||
{
|
||||
return lib.getProductName();
|
||||
}
|
||||
return productCode;
|
||||
}
|
||||
|
||||
private PrcPriceSetDetail buildDetailFromImportRow(Long priceSetId, Long psvId, String productCode,
|
||||
String productName, BmsProductCountry row, BigDecimal freight, BigDecimal registration)
|
||||
{
|
||||
PrcPriceSetDetail d = new PrcPriceSetDetail();
|
||||
d.setPriceSetId(priceSetId);
|
||||
d.setPsvId(psvId);
|
||||
d.setProductCode(productCode);
|
||||
d.setProductName(productName);
|
||||
d.setCountryCode(row.getCountryCode());
|
||||
d.setCountryName(row.getCountryName());
|
||||
d.setZoneId(row.getZoneId());
|
||||
d.setZoneCode(row.getZoneCode());
|
||||
d.setZoneName(row.getZoneName());
|
||||
d.setWeightMin(row.getWeightMin());
|
||||
d.setWeightMax(row.getWeightMax());
|
||||
d.setWeightLabel(formatWeightLabel(row.getWeightMin(), row.getWeightMax()));
|
||||
d.setProductCountryId(row.getDetailId());
|
||||
d.setFvValue(freight);
|
||||
d.setFvRegistrationFee(registration);
|
||||
d.setRoundMode("HALF_UP");
|
||||
d.setEffectiveDate(row.getEffectiveDate());
|
||||
d.setExpireDate(row.getExpireDate());
|
||||
return d;
|
||||
}
|
||||
|
||||
private void reportImportProgress(String taskId, Long priceSetId, int percent, String message)
|
||||
@@ -1097,6 +1259,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
detail.setPsvId(old.getPsvId());
|
||||
}
|
||||
validateDetail(db, detail);
|
||||
Long pspId = lookupSetProductId(detail.getPsvId(), detail.getProductCode());
|
||||
if (pspId == null)
|
||||
{
|
||||
pspId = old.getPspId();
|
||||
}
|
||||
detail.setPspId(pspId);
|
||||
int rows = detailMapper.updatePrcPriceSetDetail(detail);
|
||||
// non-null surcharges = replace snapshot for this detail (empty list clears)
|
||||
if (detail.getSurcharges() != null)
|
||||
@@ -1112,6 +1280,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
@Override
|
||||
public int deleteDetailByIds(Long[] psdIds)
|
||||
{
|
||||
Set<String> touchKeys = new HashSet<String>();
|
||||
for (Long id : psdIds)
|
||||
{
|
||||
PrcPriceSetDetail old = detailMapper.selectPrcPriceSetDetailById(id);
|
||||
@@ -1121,8 +1290,31 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
PrcPriceSet db = requireSet(old.getPriceSetId());
|
||||
assertChildEditable(db);
|
||||
if (old.getPsvId() != null && StringUtils.isNotEmpty(old.getProductCode()))
|
||||
{
|
||||
touchKeys.add(old.getPsvId() + "|" + old.getProductCode());
|
||||
}
|
||||
}
|
||||
return detailMapper.deletePrcPriceSetDetailByIds(psdIds);
|
||||
int rows = detailMapper.deletePrcPriceSetDetailByIds(psdIds);
|
||||
for (String key : touchKeys)
|
||||
{
|
||||
int sep = key.indexOf('|');
|
||||
if (sep <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Long psvId = Long.valueOf(key.substring(0, sep));
|
||||
String productCode = key.substring(sep + 1);
|
||||
PrcPriceSetDetail q = new PrcPriceSetDetail();
|
||||
q.setPsvId(psvId);
|
||||
q.setProductCode(productCode);
|
||||
List<PrcPriceSetDetail> left = detailMapper.selectPrcPriceSetDetailList(q);
|
||||
if (left == null || left.isEmpty())
|
||||
{
|
||||
setProductMapper.deleteByPsvIdAndCode(psvId, productCode);
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1818,7 +2010,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
|
||||
private PrcPriceSet requireSet(Long priceSetId)
|
||||
{
|
||||
PrcPriceSet db = setMapper.selectPrcPriceSetById(priceSetId);
|
||||
// Header only: avoid count(detail) waiting on long pull/import locks.
|
||||
PrcPriceSet db = setMapper.selectPrcPriceSetHeaderById(priceSetId);
|
||||
if (db == null)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.notExists"));
|
||||
@@ -1906,16 +2099,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
set.setVersions(versions);
|
||||
PrcPriceSetVersion current = pickCurrentVersion(versions);
|
||||
set.setVersion(current);
|
||||
if (current != null && current.getDetails() != null)
|
||||
{
|
||||
set.setDetails(current.getDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
PrcPriceSetDetail query = new PrcPriceSetDetail();
|
||||
query.setPriceSetId(priceSetId);
|
||||
set.setDetails(detailMapper.selectPrcPriceSetDetailList(query));
|
||||
}
|
||||
// Do not load full details here (large sets). Front loads by product via /detail/byProduct.
|
||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
PrcCustomerPriceSet cquery = new PrcCustomerPriceSet();
|
||||
cquery.setPriceSetId(priceSetId);
|
||||
set.setCustomers(customerMapper.selectPrcCustomerPriceSetList(cquery));
|
||||
@@ -1923,41 +2108,13 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
|
||||
private void fillVersionChildren(PrcPriceSetVersion version)
|
||||
{
|
||||
PrcPriceSetDetail dq = new PrcPriceSetDetail();
|
||||
dq.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(dq);
|
||||
PrcPriceSetVersionSurcharge sq = new PrcPriceSetVersionSurcharge();
|
||||
sq.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetVersionSurcharge> surcharges = versionSurchargeMapper.selectPrcPriceSetVersionSurchargeList(sq);
|
||||
if (surcharges == null)
|
||||
{
|
||||
surcharges = new ArrayList<PrcPriceSetVersionSurcharge>();
|
||||
}
|
||||
version.setSurcharges(surcharges);
|
||||
Map<Long, List<PrcPriceSetVersionSurcharge>> byPsd = new HashMap<Long, List<PrcPriceSetVersionSurcharge>>();
|
||||
for (PrcPriceSetVersionSurcharge sc : surcharges)
|
||||
{
|
||||
if (sc.getPsdId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcPriceSetVersionSurcharge> list = byPsd.get(sc.getPsdId());
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<PrcPriceSetVersionSurcharge>();
|
||||
byPsd.put(sc.getPsdId(), list);
|
||||
}
|
||||
list.add(sc);
|
||||
}
|
||||
if (details != null)
|
||||
{
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
List<PrcPriceSetVersionSurcharge> list = byPsd.get(d.getPsdId());
|
||||
d.setSurcharges(list != null ? list : new ArrayList<PrcPriceSetVersionSurcharge>());
|
||||
}
|
||||
}
|
||||
version.setDetails(details);
|
||||
PrcPriceSetProduct pq = new PrcPriceSetProduct();
|
||||
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)
|
||||
version.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
version.setSurcharges(new ArrayList<PrcPriceSetVersionSurcharge>());
|
||||
}
|
||||
|
||||
private PrcPriceSetVersion pickCurrentVersion(List<PrcPriceSetVersion> versions)
|
||||
@@ -1983,14 +2140,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
// null details = leave existing rows alone; empty list = clear / replace with nothing
|
||||
boolean detailsProvided = set.getDetails() != null;
|
||||
List<PrcPriceSetDetail> details = filterDetails(set.getDetails());
|
||||
// Allow creating an empty draft version when client sends version (Step1 -> Step2).
|
||||
if (version == null && !detailsProvided && !pullLibrary)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (version == null && details.isEmpty() && !pullLibrary)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (version == null)
|
||||
{
|
||||
version = new PrcPriceSetVersion();
|
||||
@@ -2097,18 +2251,21 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
if (existing > 0 && !force)
|
||||
{
|
||||
reportSaveProgress(set, 80, "details exist, skip pull");
|
||||
PrcPriceSetDetail query = new PrcPriceSetDetail();
|
||||
query.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(query);
|
||||
set.setDetails(details);
|
||||
version.setDetails(details);
|
||||
fillVersionChildren(version);
|
||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
return;
|
||||
}
|
||||
versionSurchargeMapper.deleteByPsvId(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)
|
||||
detailMapper.deleteOrphanDetailsByPriceSetId(set.getPriceSetId());
|
||||
int productRows = setProductMapper.insertFromLibrary(set.getPriceSetId(), version.getPsvId());
|
||||
if (productRows <= 0)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.library.empty"));
|
||||
}
|
||||
reportSaveProgress(set, 45, "pulling library");
|
||||
int inserted;
|
||||
if (TYPE_DISCOUNT.equals(set.getSetType()))
|
||||
{
|
||||
@@ -2130,15 +2287,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message("price.set.library.empty"));
|
||||
}
|
||||
setProductMapper.bindDetailPspByPsvId(version.getPsvId());
|
||||
reportSaveProgress(set, 75, "saving details " + inserted + "/" + inserted);
|
||||
reportSaveProgress(set, 85, "saving surcharges");
|
||||
versionSurchargeMapper.insertFromLibraryRels(version.getPsvId(), version.getCreateBy());
|
||||
PrcPriceSetDetail query = new PrcPriceSetDetail();
|
||||
query.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetDetail> details = detailMapper.selectPrcPriceSetDetailList(query);
|
||||
set.setDetails(details);
|
||||
version.setDetails(details);
|
||||
fillVersionChildren(version);
|
||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2171,6 +2325,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
validateDetail(set, d);
|
||||
d.setPriceSetId(set.getPriceSetId());
|
||||
d.setPsvId(version.getPsvId());
|
||||
Long pspId = lookupSetProductId(version.getPsvId(), d.getProductCode());
|
||||
if (pspId != null)
|
||||
{
|
||||
d.setPspId(pspId);
|
||||
}
|
||||
if (d.getPsdId() != null && byId.containsKey(d.getPsdId()))
|
||||
{
|
||||
detailMapper.updatePrcPriceSetDetail(d);
|
||||
@@ -2213,9 +2372,107 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
||||
}
|
||||
detailMapper.deletePrcPriceSetDetailByIds(orphanIds.toArray(new Long[0]));
|
||||
}
|
||||
// drop set-product rows whose codes no longer have any detail
|
||||
Set<String> keepCodes = new HashSet<String>();
|
||||
if (details != null)
|
||||
{
|
||||
for (PrcPriceSetDetail d : details)
|
||||
{
|
||||
if (d != null && StringUtils.isNotEmpty(d.getProductCode()))
|
||||
{
|
||||
keepCodes.add(d.getProductCode().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
PrcPriceSetProduct pq = new PrcPriceSetProduct();
|
||||
pq.setPsvId(version.getPsvId());
|
||||
List<PrcPriceSetProduct> setProducts = setProductMapper.selectPrcPriceSetProductList(pq);
|
||||
if (setProducts != null)
|
||||
{
|
||||
for (PrcPriceSetProduct sp : setProducts)
|
||||
{
|
||||
if (sp == null || StringUtils.isEmpty(sp.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!keepCodes.contains(sp.getProductCode()))
|
||||
{
|
||||
setProductMapper.deleteByPsvIdAndCode(version.getPsvId(), sp.getProductCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
version.setDetails(details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup existing set-product id only (no insert).
|
||||
* Insert happens only on pull-from-library / Excel import.
|
||||
*/
|
||||
private Long lookupSetProductId(Long psvId, String productCode)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
PrcPriceSetProduct existing = setProductMapper.selectByPsvIdAndCode(psvId, productCode.trim());
|
||||
return existing == null ? null : existing.getPspId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure package-side product master row exists for this version+code; return psp_id.
|
||||
* Only called from pull-from-library / Excel import.
|
||||
*/
|
||||
private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint)
|
||||
{
|
||||
if (psvId == null || StringUtils.isEmpty(productCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String code = productCode.trim();
|
||||
PrcPriceSetProduct existing = setProductMapper.selectByPsvIdAndCode(psvId, code);
|
||||
if (existing != null)
|
||||
{
|
||||
if (StringUtils.isEmpty(existing.getProductName()) && StringUtils.isNotEmpty(productNameHint)
|
||||
&& !code.equals(productNameHint))
|
||||
{
|
||||
existing.setProductName(productNameHint);
|
||||
setProductMapper.updatePrcPriceSetProduct(existing);
|
||||
}
|
||||
return existing.getPspId();
|
||||
}
|
||||
PrcPriceSetProduct row = new PrcPriceSetProduct();
|
||||
row.setPriceSetId(priceSetId);
|
||||
row.setPsvId(psvId);
|
||||
row.setProductCode(code);
|
||||
BmsProduct lib = null;
|
||||
try
|
||||
{
|
||||
lib = productMapper.selectBmsProductByCode(code);
|
||||
}
|
||||
catch (Exception ignore)
|
||||
{
|
||||
lib = null;
|
||||
}
|
||||
if (lib != null)
|
||||
{
|
||||
row.setProductName(lib.getProductName());
|
||||
row.setServiceLevel(lib.getServiceLevel());
|
||||
row.setCargoAttr(lib.getCargoAttr());
|
||||
row.setDestRegion(lib.getDestRegion());
|
||||
row.setUpdateNote(lib.getUpdateNote());
|
||||
row.setTaxMode(StringUtils.isNotEmpty(lib.getTaxMode()) ? lib.getTaxMode() : "TAX_INCLUDED");
|
||||
row.setSourceProductId(lib.getProductId());
|
||||
row.setSourceSheet(lib.getSourceSheet());
|
||||
}
|
||||
else
|
||||
{
|
||||
row.setProductName(StringUtils.isNotEmpty(productNameHint) ? productNameHint : code);
|
||||
row.setTaxMode("TAX_INCLUDED");
|
||||
}
|
||||
setProductMapper.insertPrcPriceSetProduct(row);
|
||||
return row.getPspId();
|
||||
}
|
||||
|
||||
/** Only rewrite surcharge snapshots for rows that explicitly sent surcharges. */
|
||||
private void resnapshotSurchargesIfNeeded(PrcPriceSetVersion version, List<PrcPriceSetDetail> details)
|
||||
{
|
||||
|
||||
+99
-8
@@ -28,6 +28,7 @@ import com.bms.price.domain.BmsProductCountry;
|
||||
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;
|
||||
@@ -45,6 +46,7 @@ import com.bms.price.mapper.BmsProductMapper;
|
||||
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;
|
||||
@@ -90,6 +92,9 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
@Autowired
|
||||
private PrcPriceSetDetailMapper detailMapper;
|
||||
|
||||
@Autowired
|
||||
private PrcPriceSetProductMapper setProductMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsProductCountryMapper productCountryMapper;
|
||||
|
||||
@@ -195,6 +200,98 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return toResult(quote, quoteFeeMapper.selectByQuoteId(quoteId), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrcPriceSetProduct> listBoundPackageProducts(String customerCode, Date bizDate)
|
||||
{
|
||||
if (StringUtils.isEmpty(customerCode))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PrcCustomerPriceSet> binds = customerPriceSetMapper.selectEffectiveByCustomerCode(customerCode.trim());
|
||||
if (binds == null || binds.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Date day = bizDate != null ? bizDate : new Date();
|
||||
Date asOf = endOfDay(day);
|
||||
for (PrcCustomerPriceSet bind : binds)
|
||||
{
|
||||
if (bind == null || bind.getPriceSetId() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PrcPriceSet set = priceSetMapper.selectPrcPriceSetHeaderById(bind.getPriceSetId());
|
||||
if (set == null || !"E".equals(set.getStatus()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<PrcPriceSetProduct> products = loadProductsForPackage(set.getPriceSetId(), asOf);
|
||||
if (products != null && !products.isEmpty())
|
||||
{
|
||||
return products;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/** Prefer version active at asOf; else latest version that has set-product rows. */
|
||||
private List<PrcPriceSetProduct> loadProductsForPackage(Long priceSetId, Date asOf)
|
||||
{
|
||||
PrcPriceSetVersion active = versionMapper.selectActiveByPriceSetAt(priceSetId, asOf);
|
||||
List<PrcPriceSetProduct> rows = listProductsByPsvId(active == null ? null : active.getPsvId());
|
||||
if (rows != null && !rows.isEmpty())
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
PrcPriceSetVersion q = new PrcPriceSetVersion();
|
||||
q.setPriceSetId(priceSetId);
|
||||
List<PrcPriceSetVersion> versions = versionMapper.selectPrcPriceSetVersionList(q);
|
||||
if (versions == null || versions.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// newest seq first
|
||||
versions.sort((a, b) -> {
|
||||
int sa = a.getVersionSeq() == null ? 0 : a.getVersionSeq().intValue();
|
||||
int sb = b.getVersionSeq() == null ? 0 : b.getVersionSeq().intValue();
|
||||
return Integer.compare(sb, sa);
|
||||
});
|
||||
for (PrcPriceSetVersion v : versions)
|
||||
{
|
||||
rows = listProductsByPsvId(v == null ? null : v.getPsvId());
|
||||
if (rows != null && !rows.isEmpty())
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<PrcPriceSetProduct> listProductsByPsvId(Long psvId)
|
||||
{
|
||||
if (psvId == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
PrcPriceSetProduct q = new PrcPriceSetProduct();
|
||||
q.setPsvId(psvId);
|
||||
List<PrcPriceSetProduct> rows = setProductMapper.selectPrcPriceSetProductList(q);
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, PrcPriceSetProduct> byCode = new LinkedHashMap<String, PrcPriceSetProduct>();
|
||||
for (PrcPriceSetProduct row : rows)
|
||||
{
|
||||
if (row == null || StringUtils.isEmpty(row.getProductCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
byCode.putIfAbsent(row.getProductCode(), row);
|
||||
}
|
||||
return new ArrayList<PrcPriceSetProduct>(byCode.values());
|
||||
}
|
||||
|
||||
private TmsPriceQuoteResult doQuote(TmsPriceQuoteRequest request, Date bizDate)
|
||||
{
|
||||
String productCode = request.getProductCode().trim();
|
||||
@@ -485,14 +582,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
int detailCount = detailMapper.countByPsvId(psvId);
|
||||
if (detailCount == 0)
|
||||
{
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DEFAULT, base.getFreightPrice(), null, null,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight, null);
|
||||
fillPackage(r, bind, set);
|
||||
stampVersion(r, version, null);
|
||||
// Package fixed has no version details; use product default rates
|
||||
r.setMatchMessage(MessageUtils.message("price.quote.fixedPackageNoDetails"));
|
||||
r.setCustomerId(bind.getCustomerId());
|
||||
return r;
|
||||
// FIXED sales price is version-detail only; no fallback to product_country.
|
||||
return null;
|
||||
}
|
||||
|
||||
PrcPriceSetDetail detail = detailMapper.selectFixedMatch(
|
||||
|
||||
Reference in New Issue
Block a user