diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java index de7c9e6c..9bad75c1 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java @@ -27,6 +27,7 @@ import com.bms.common.enums.BusinessType; import com.bms.common.utils.MessageUtils; import com.bms.common.utils.StringUtils; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcCustomerPriceSetBatch; import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetDetail; @@ -378,6 +379,16 @@ public class PriceSetController extends BaseController return toAjax(priceSetService.insertCustomer(customer)); } + @PreAuthorize("@ss.hasPermi('price:set:edit')") + @Log(title = "套餐客户批量绑定", businessType = BusinessType.INSERT) + @PostMapping("/customer/batch") + public AjaxResult batchAddCustomer(@RequestBody PrcCustomerPriceSetBatch batch) + { + batch.setCreateBy(getUsername()); + batch.setUpdateBy(getUsername()); + return success(priceSetService.batchInsertCustomers(batch)); + } + @PreAuthorize("@ss.hasPermi('price:set:edit')") @Log(title = "套餐客户绑定", businessType = BusinessType.UPDATE) @PutMapping("/customer") diff --git a/bms-back/bms-admin/src/main/resources/i18n/messages.properties b/bms-back/bms-admin/src/main/resources/i18n/messages.properties index ce897e9c..62b2bcf4 100644 --- a/bms-back/bms-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/bms-admin/src/main/resources/i18n/messages.properties @@ -230,3 +230,5 @@ price.bill.export.tooMany=匹配订单超过上限 {0} 条,请缩小筛选范 price.bill.export.noMatch=未查询到匹配订单,无法导出 price.bill.export.notReady=导出文件尚未生成或已失败 price.bill.export.fileMissing=导出文件不存在或路径非法 +price.set.customer.batch.empty=请选择要绑定的客户 +price.set.customer.batch.done=批量绑定完成:共 {0} 个客户,新绑定 {1} 个,更换 {2} 个,跳过 {3} 个 diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomer.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomer.java index 1d351d7e..e78df049 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomer.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomer.java @@ -47,6 +47,10 @@ public class BmsCustomer extends BaseEntity @Excel(name = "业务类型", readConverterExp = "0=直客,1=同行,2=集运") private String customerType; + /** 区域类型(字典 data_area_type) */ + @Excel(name = "区域类型", dictType = "data_area_type") + private String areaType; + /** 启用状态(0正常 1停用) */ @Excel(name = "启用状态", readConverterExp = "0=正常,1=停用") private String isEnable; diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java index 128c1f9a..43bd4133 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java @@ -13,7 +13,9 @@ import java.util.function.Function; import com.alibaba.fastjson2.JSON; import com.bms.common.core.domain.entity.SysUser; import com.bms.common.utils.DateUtils; +import com.bms.common.utils.DictUtils; import com.bms.common.utils.SecurityUtils; +import com.bms.common.utils.StringUtils; import com.bms.customer.customerinfo.domain.BmsCustomer; import com.bms.customer.customerinfo.domain.BmsCustomerAttachment; import com.bms.customer.customerinfo.domain.BmsCustomerCompany; @@ -43,6 +45,13 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService private static final ThreadLocal DATE_FMT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); + /** 区域类型走字典 data_area_type;缓存未命中时保留原编码。 */ + private static String areaTypeLabel(String value) + { + String label = DictUtils.getDictLabel("data_area_type", value); + return StringUtils.isEmpty(label) ? value : label; + } + /** 业务类型字典 */ private static final Map CUSTOMER_TYPE_MAP = Map.of( "0", "直客", "1", "同行", "2", "集运"); @@ -68,7 +77,7 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService /** * 需要记录日志的主表字段(顺序即展示顺序)。 * requireNonBlank 为 true 的字段对应 update SQL 的 <if test="x!=null and x!=''">, - * 其余动态字段仅在新值非 null 时落库;业务员/结算员/客服为无条件写入。 + * 其余动态字段仅在新值非 null 时落库;业务员/结算员/客服、区域类型为无条件写入。 */ private static final List FIELD_DEFS = List.of( new FieldDef("customerCode", "客户编码", BmsCustomer::getCustomerCode, true, Function.identity()), @@ -76,6 +85,7 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService new FieldDef("customerCodeDw", "对外代码", BmsCustomer::getCustomerCodeDw, false, Function.identity()), new FieldDef("creditCode", "统一社会信用代码", BmsCustomer::getCreditCode, false, Function.identity()), new FieldDef("customerType", "业务类型", BmsCustomer::getCustomerType, false, CUSTOMER_TYPE_MAP::get), + new FieldDef("areaType", "区域类型", BmsCustomer::getAreaType, false, BmsCustomerLogServiceImpl::areaTypeLabel), new FieldDef("isEnable", "启用状态", BmsCustomer::getIsEnable, false, ENABLE_MAP::get), new FieldDef("salesUserId", "业务员", c -> userDisplay(c.getSalesUserId(), c.getSalesUser()), false, Function.identity()), new FieldDef("ed", "资金额度(元)", BmsCustomer::getEd, false, BmsCustomerLogServiceImpl::numText), @@ -198,8 +208,8 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService { // 与 update SQL 的动态条件保持一致:未提交(null/空串)的字段本次不落库,不参与比对 Object raw = def.rawGetter.apply(newCustomer); - // 合同起止允许清空;其余动态字段未提交时不落库,也不记日志 - if (raw == null && !isContractDateField(def.field)) + // 合同起止、区域类型允许清空;其余动态字段未提交时不落库,也不记日志 + if (raw == null && !isContractDateField(def.field) && !"areaType".equals(def.field)) { continue; } diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java index 834d556c..049278e2 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java @@ -69,10 +69,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi { throw new ServiceException("所选套餐不存在"); } - if (!"E".equals(set.getStatus())) - { - throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用"); - } if (bind.getBindPriority() == null || bind.getBindPriority() < 1) { bind.setBindPriority(1); @@ -141,10 +137,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi { throw new ServiceException("所选套餐不存在"); } - if (!"E".equals(set.getStatus())) - { - throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用"); - } Integer priority = (req.getBindPriority() == null || req.getBindPriority() < 1) ? 1 : req.getBindPriority(); boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting()); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java new file mode 100644 index 00000000..55df1b50 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java @@ -0,0 +1,30 @@ +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; + +/** + * Price-set detail batch bind customers. + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PrcCustomerPriceSetBatch extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private Long priceSetId; + private Integer bindPriority; + @JsonFormat(pattern = "yyyy-MM-dd") + private Date prBegindate; + @JsonFormat(pattern = "yyyy-MM-dd") + private Date prEnddate; + private String priceSetNote; + /** Customers to bind (need customerCode; id/name optional). */ + private List customers; + /** true = move already-bound customers onto this set; false = skip them. */ + private Boolean overwriteExisting; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java index 84dc0da6..073bfc68 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/BmsProductCountryMapper.java @@ -116,6 +116,17 @@ public interface BmsProductCountryMapper @Param("weight") BigDecimal weight, @Param("bizDate") Date bizDate); + /** + * Lowest PRODUCT catalog weight band for product/country/zone (ignore weight). + * Used when charge weight is below the minimum band. + */ + public BmsProductCountry selectMinBandByProductCountry( + @Param("productCode") String productCode, + @Param("countryCode") String countryCode, + @Param("countryName") String countryName, + @Param("zoneCode") String zoneCode, + @Param("bizDate") Date bizDate); + /** Count product-country rows that have a zone for this product+country+bizDate */ public int countZonedByProductCountry( @Param("productCode") String productCode, diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java index 0ff6e2b2..78e0b458 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetDetailMapper.java @@ -71,6 +71,17 @@ public interface PrcPriceSetDetailMapper @Param("zoneCode") String zoneCode, @Param("weight") BigDecimal weight); + /** + * Lowest FIXED weight band for product/country/zone (ignore weight). + * Used when charge weight is below the minimum band. + */ + public PrcPriceSetDetail selectFixedMinBand( + @Param("psvId") Long psvId, + @Param("productCode") String productCode, + @Param("countryCode") String countryCode, + @Param("countryName") String countryName, + @Param("zoneCode") String zoneCode); + public int countZonedByPsvProductCountry( @Param("psvId") Long psvId, @Param("productCode") String productCode, diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java index 698793f6..4b10014c 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java @@ -5,6 +5,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.Map; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcCustomerPriceSetBatch; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetCheckItem; import com.bms.price.domain.PrcPriceSetDetail; @@ -92,6 +93,9 @@ public interface IPrcPriceSetService public int insertCustomer(PrcCustomerPriceSet customer); + /** Batch bind customers to a price set; returns summary message. */ + public String batchInsertCustomers(PrcCustomerPriceSetBatch batch); + public int updateCustomer(PrcCustomerPriceSet customer); public int deleteCustomerByIds(Long[] customerPricesetIds); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java index 59760922..2562f3ca 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java @@ -2432,6 +2432,128 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } } + @Override + @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class) + public String batchInsertCustomers(com.bms.price.domain.PrcCustomerPriceSetBatch batch) + { + if (batch == null || batch.getCustomers() == null || batch.getCustomers().isEmpty()) + { + throw new ServiceException(MessageUtils.message("price.set.customer.batch.empty")); + } + if (batch.getPriceSetId() == null) + { + throw new ServiceException(MessageUtils.message("price.set.customer.customerId.empty")); + } + PrcPriceSet db = requireSet(batch.getPriceSetId()); + assertChildEditable(db); + Integer priority = (batch.getBindPriority() == null || batch.getBindPriority().intValue() < 1) + ? Integer.valueOf(1) : batch.getBindPriority(); + boolean overwrite = Boolean.TRUE.equals(batch.getOverwriteExisting()); + + java.util.LinkedHashMap uniq = new java.util.LinkedHashMap(); + for (PrcCustomerPriceSet item : batch.getCustomers()) + { + if (item == null || StringUtils.isEmpty(item.getCustomerCode())) + { + continue; + } + String code = item.getCustomerCode().trim(); + if (!uniq.containsKey(code)) + { + uniq.put(code, item); + } + } + if (uniq.isEmpty()) + { + throw new ServiceException(MessageUtils.message("price.set.customer.batch.empty")); + } + + int inserted = 0; + int replaced = 0; + int skipped = 0; + java.util.List skippedNames = new java.util.ArrayList(); + for (PrcCustomerPriceSet item : uniq.values()) + { + PrcCustomerPriceSet query = new PrcCustomerPriceSet(); + query.setCustomerCode(item.getCustomerCode().trim()); + java.util.List found = customerMapper.selectPrcCustomerPriceSetList(query); + PrcCustomerPriceSet existing = (found == null || found.isEmpty()) ? null : found.get(0); + + PrcCustomerPriceSet row = new PrcCustomerPriceSet(); + row.setPriceSetId(batch.getPriceSetId()); + row.setCustomerId(item.getCustomerId()); + row.setCustomerCode(item.getCustomerCode().trim()); + row.setCustomerName(item.getCustomerName()); + row.setBindPriority(priority); + row.setPrBegindate(batch.getPrBegindate()); + row.setPrEnddate(batch.getPrEnddate()); + row.setPriceSetNote(batch.getPriceSetNote()); + row.setCreateBy(batch.getCreateBy()); + row.setUpdateBy(batch.getUpdateBy()); + + if (existing != null) + { + boolean sameSet = batch.getPriceSetId().equals(existing.getPriceSetId()); + if (sameSet) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + if (!overwrite) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + row.setCustomerPricesetId(existing.getCustomerPricesetId()); + if (row.getPrBegindate() == null) + { + row.setPrBegindate(existing.getPrBegindate()); + } + if (row.getPrEnddate() == null) + { + row.setPrEnddate(existing.getPrEnddate()); + } + validateCustomer(row, db); + customerMapper.updatePrcCustomerPriceSet(row); + replaced++; + } + else + { + validateCustomer(row, db); + try + { + customerMapper.insertPrcCustomerPriceSet(row); + } + catch (Exception e) + { + if (isCustomerUniqueViolation(e)) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + throw e; + } + inserted++; + } + } + + String msg = MessageUtils.message("price.set.customer.batch.done", + Integer.valueOf(uniq.size()), Integer.valueOf(inserted), + Integer.valueOf(replaced), Integer.valueOf(skipped)); + if (skipped > 0 && !overwrite) + { + String names = skippedNames.stream().limit(10).collect(java.util.stream.Collectors.joining(", ")); + msg = msg + " [" + names + (skippedNames.size() > 10 ? "..." : "") + "]"; + } + return msg; + } + @Override public int updateCustomer(PrcCustomerPriceSet customer) { @@ -2962,6 +3084,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { throw new ServiceException(MessageUtils.message("price.set.customer.customerId.empty")); } + // UI no longer collects bind dates: fall back to set / version / today. + // Do not reject empty begin date — always fill a value. if (customer.getPrBegindate() == null) { customer.setPrBegindate(set.getEffectiveDate()); @@ -2972,11 +3096,17 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } if (customer.getPrBegindate() == null) { - throw new ServiceException(MessageUtils.message("price.set.customer.beginDate.empty")); + customer.setPrBegindate(DateUtils.getNowDate()); } - if (customer.getPrEnddate() != null && customer.getPrBegindate().after(customer.getPrEnddate())) + if (customer.getPrEnddate() == null) { - throw new ServiceException(MessageUtils.message("price.set.customer.date.invalid")); + customer.setPrEnddate(set.getExpireDate()); + } + // Auto-filled dates may conflict (e.g. set expire already past); keep bind, clear end. + if (customer.getPrEnddate() != null && customer.getPrBegindate() != null + && customer.getPrBegindate().after(customer.getPrEnddate())) + { + customer.setPrEnddate(null); } if (customer.getBindPriority() != null && customer.getBindPriority().intValue() < 1) { diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java index 755fd7f2..08afe8e7 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -520,15 +520,36 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService else if (TYPE_DISCOUNT.equals(setType)) { // DISCOUNT still needs catalog as base rate; do not soft-fail as 产品价未匹配 for now. - if (!hasProductCatalogPrice(catalog)) + BmsProductCountry discCatalog = catalog; + BigDecimal discWeight = weight; + if (!hasProductCatalogPrice(discCatalog)) + { + // Below min catalog band: snap charge weight to lowest band min. + BigDecimal snapped = snapWeightToCatalogMinBand( + productCode, cc, cn, zone.zoneCode, weight, bizDate); + if (snapped != null) + { + discWeight = snapped; + discCatalog = softMatchCatalog( + productCode, cc, cn, zone.zoneCode, discWeight, bizDate, zone.zone); + } + } + if (!hasProductCatalogPrice(discCatalog)) { // catalogMissCode = Math.max(catalogMissCode, // diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate)); continue; } - TmsPriceQuoteResult hit = tryDiscount(bind, set, version, catalog, productCode, productName, weight, bizDate); + TmsPriceQuoteResult hit = tryDiscount(bind, set, version, discCatalog, + productCode, productName, discWeight, bizDate); if (hit != null) { + // Calc-only snap: do not persist raised min-band weight as charge_weight. + if (discWeight != null && weight != null && discWeight.compareTo(weight) != 0) + { + hit.setWeight(weight); + hit.setChargeWeight(weight); + } stampRequestMeta(request, hit); return hit; } @@ -971,6 +992,21 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService PrcPriceSetDetail detail = detailMapper.selectFixedMatch( psvId, productCode, countryCode, countryName, zoneCode, weight); + BigDecimal billWeight = weight; + boolean snappedToMinBand = false; + // Miss: if charge weight is below the lowest band, bill at that band's weight_min. + if (detail == null) + { + PrcPriceSetDetail minBand = detailMapper.selectFixedMinBand( + psvId, productCode, countryCode, countryName, zoneCode); + if (minBand != null && minBand.getWeightMin() != null + && weight.compareTo(minBand.getWeightMin()) < 0) + { + detail = minBand; + billWeight = minBand.getWeightMin().setScale(3, RoundingMode.HALF_UP); + snappedToMinBand = true; + } + } if (detail == null) { return null; @@ -979,16 +1015,59 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService { return null; } - TmsPriceQuoteResult r = buildFromPackageDetail(SRC_FIXED, detail, catalog, resolvedZone, - productCode, productName, weight, detail.getFvValue(), null, null, detail.getFvRegistrationFee(), bizDate); + BmsProductCountry useCatalog = catalog; + if (snappedToMinBand) + { + BmsProductCountry rematched = softMatchCatalog( + productCode, countryCode, countryName, zoneCode, billWeight, bizDate, resolvedZone); + if (hasProductCatalogPrice(rematched)) + { + useCatalog = rematched; + } + } + TmsPriceQuoteResult r = buildFromPackageDetail(SRC_FIXED, detail, useCatalog, resolvedZone, + productCode, productName, billWeight, detail.getFvValue(), null, null, detail.getFvRegistrationFee(), bizDate); fillPackage(r, bind, set); stampVersion(r, version, detail.getPsdId()); - r.setMatchMessage(MessageUtils.message("price.quote.hitCustomerFixedPackageDetail") - + " [" + (version.getVersionNo() == null ? ("psv=" + psvId) : version.getVersionNo()) + "]"); + String verLabel = version.getVersionNo() == null ? ("psv=" + psvId) : version.getVersionNo(); + String msg = MessageUtils.message("price.quote.hitCustomerFixedPackageDetail") + " [" + verLabel + "]"; + if (snappedToMinBand) + { + // Calc-only snap: keep original weight/chargeWeight for persist so upstream re-sync + // can replace them when the new weight is already in-band. + r.setWeight(weight); + r.setChargeWeight(weight); + msg = msg + "; billed at min band " + billWeight.toPlainString() + " (persist original weight)"; + } + r.setMatchMessage(msg); r.setCustomerId(bind.getCustomerId()); return r; } + /** + * When weight misses catalog bands but is below the lowest band, return that band's weight_min. + * Otherwise null (caller keeps original miss). + */ + private BigDecimal snapWeightToCatalogMinBand(String productCode, String countryCode, String countryName, + String zoneCode, BigDecimal weight, Date bizDate) + { + if (weight == null || StringUtils.isEmpty(productCode)) + { + return null; + } + BmsProductCountry minBand = productCountryMapper.selectMinBandByProductCountry( + productCode, countryCode, countryName, zoneCode, bizDate); + if (minBand == null || minBand.getWeightMin() == null) + { + return null; + } + if (weight.compareTo(minBand.getWeightMin()) < 0) + { + return minBand.getWeightMin().setScale(3, RoundingMode.HALF_UP); + } + return null; + } + private TmsPriceQuoteResult tryDiscount(PrcCustomerPriceSet bind, PrcPriceSet set, PrcPriceSetVersion version, BmsProductCountry base, String productCode, String productName, BigDecimal weight, Date bizDate) { diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/support/CatalogVersionSupport.java b/bms-back/bms-system/src/main/java/com/bms/price/service/support/CatalogVersionSupport.java index ad65c958..6783cd76 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/support/CatalogVersionSupport.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/support/CatalogVersionSupport.java @@ -446,8 +446,7 @@ public class CatalogVersionSupport } /** - * Catalog version that can accept preview apply / Excel attach / edit. - * Same as sales force-edit: draft, pending, and effect are writable. + * Catalog force-edit: draft, pending, effect, and expired are writable. */ public PrcPriceSetVersion requireEditableCatalogVersion(Long psvId, String priceType) { @@ -467,9 +466,10 @@ public class CatalogVersionSupport } if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()) - && !STATUS_EFFECT.equals(ver.getStatus())) + && !STATUS_EFFECT.equals(ver.getStatus()) + && !STATUS_EXPIRED.equals(ver.getStatus())) { - throw new ServiceException("仅草稿、待生效或已生效版本可以编辑"); + throw new ServiceException("仅草稿、待生效、已生效或已失效版本可以编辑"); } return ver; } diff --git a/bms-back/bms-system/src/main/resources/mapper/customer/BmsCustomerMapper.xml b/bms-back/bms-system/src/main/resources/mapper/customer/BmsCustomerMapper.xml index 8ac9eef2..fbbf8b95 100644 --- a/bms-back/bms-system/src/main/resources/mapper/customer/BmsCustomerMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/customer/BmsCustomerMapper.xml @@ -11,6 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -44,7 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select c.customer_id, c.customer_code, c.customer_name, c.customer_code_dw, c.credit_code, c.customer_type, + select c.customer_id, c.customer_code, c.customer_name, c.customer_code_dw, c.credit_code, c.customer_type, c.area_type, c.is_enable, case when su.user_id is not null then cast(su.user_id as char) else null end as sales_user_id, su.nick_name as sales_user, c.ed, c.zq, c.settlement_user_id, c.settlement_user, @@ -91,6 +92,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and c.credit_code = #{creditCode} and c.customer_type = #{customerType} + and c.area_type = #{areaType} and c.is_enable = #{isEnable} and c.sales_user_id = #{salesUserId} and su.nick_name = #{salesUser} @@ -315,6 +317,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" customer_code_dw, credit_code, customer_type, + area_type, is_enable, sales_user_id, sales_user, @@ -347,6 +350,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{customerCodeDw}, #{creditCode}, #{customerType}, + #{areaType}, #{isEnable}, #{salesUserId}, #{salesUser}, @@ -383,6 +387,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" customer_code_dw = #{customerCodeDw}, credit_code = #{creditCode}, customer_type = #{customerType}, + area_type = #{areaType}, is_enable = #{isEnable}, sales_user_id = #{salesUserId}, sales_user = #{salesUser}, diff --git a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml index 107a6826..38f5880c 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml @@ -357,6 +357,36 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" limit 1 + + + +