Refactor billing options retrieval to include data permissions and add effective date handling for product imports

This commit is contained in:
wuweihua
2026-10-09 09:27:28 +08:00
parent fea34d1d10
commit c95a9861cc
42 changed files with 984 additions and 219 deletions
@@ -23,9 +23,9 @@ public class BmsProduct extends BaseEntity
private String updateNote;
/** TAX_INCLUDED / TAX_EXCLUDED */
private String taxMode;
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date effectiveDate;
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireDate;
/** 0 pending, 1 active, 2 expired */
public static final String STATUS_PENDING = "0";
@@ -31,10 +31,10 @@ public class BmsProductCountry implements Serializable
private String sizeLimit;
private String lastMile;
private String leadTime;
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date effectiveDate;
/** null=open; closed to next import effective_date */
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireDate;
/** 0 pending, 1 active, 2 expired */
private String status;
@@ -20,6 +20,10 @@ public class PrcIncomeBillFee implements Serializable
private String feeName;
private Long scId;
private BigDecimal amount;
/** 产品价(目录价,基础运费/挂号费明细对应分摊) */
private BigDecimal productAmount;
/** 红线价(预留) */
private BigDecimal redlineAmount;
private String currency;
private String roundMode;
private String remark;
@@ -37,6 +37,8 @@ public class PrcOrderBill extends BaseEntity
private String settlementManCode;
/** 客服员 */
private String serviceMan;
/** 客服员编码 */
private String serviceManCode;
private Long customerId;
private String customerCode;
private String customerName;
@@ -33,6 +33,8 @@ public class TmsPriceBillRequest extends TmsPriceQuoteRequest
private String settlementManCode;
/** 客服员 */
private String serviceMan;
/** 客服员编码 */
private String serviceManCode;
private String customerName;
private Integer pieces;
private BigDecimal actualWeight;
@@ -16,6 +16,10 @@ public class TmsPriceQuoteFeeLine implements Serializable
/** surcharge config id, 0 for freight/registration */
private Long scId;
private BigDecimal amount;
/** 产品价(目录价分摊) */
private BigDecimal productAmount;
/** 红线价(预留) */
private BigDecimal redlineAmount;
private String currency;
private String roundMode;
private String remark;
@@ -91,6 +91,8 @@ public class TmsPriceQuoteResult implements Serializable
private String settlementManCode;
/** 客服员 */
private String serviceMan;
/** 客服员编码 */
private String serviceManCode;
private String customerCode;
private String customerName;
/** ACTIVE / MISSING / DISABLED */
@@ -20,14 +20,14 @@ public interface PrcOrderBillMapper
public List<PrcOrderBill> selectPrcOrderBillList(PrcOrderBill bill);
/** 订单号下拉搜索:按关键词模糊匹配,返回去重后的订单号(按最近创建排序,限量返回) */
public List<String> selectOrderNoOptions(@Param("keyword") String keyword, @Param("limit") int limit);
/** 订单号下拉搜索(带数据权限) */
public List<String> selectOrderNoOptions(@Param("query") PrcOrderBill query, @Param("limit") int limit);
/** 运单号下拉搜索:按关键词模糊匹配,返回去重后的运单号(按最近创建排序,限量返回) */
public List<String> selectTrackingNoOptions(@Param("keyword") String keyword, @Param("limit") int limit);
/** 运单号下拉搜索(带数据权限) */
public List<String> selectTrackingNoOptions(@Param("query") PrcOrderBill query, @Param("limit") int limit);
/** 转单号下拉搜索:按关键词模糊匹配,返回去重后的转单号(按最近创建排序,限量返回) */
public List<String> selectTransferNoOptions(@Param("keyword") String keyword, @Param("limit") int limit);
/** 转单号下拉搜索(带数据权限) */
public List<String> selectTransferNoOptions(@Param("query") PrcOrderBill query, @Param("limit") int limit);
/** 应收账单生成读取指定客户截止日期前仍存在未出账费用的订单计费快照。 */
public List<PrcOrderBill> selectPrcOrderBillByCustomerPeriod(@Param("customerId") Long customerId,
@@ -26,11 +26,12 @@ public class OracleOrderBillMapper
req.setOrderNo(firstStr(row, "ORDER_ID", "ORG_ORDER_ID", "TRANSFER_ORDER_ID"));
req.setTrackingNo(firstStr(row, "ORG_ORDER_ID", "TRACK_ID", "BARCODE_NUMBER", "CUSTOMER_TRACK_ID"));
req.setTransferNo(firstStr(row, "TRANSFER_ORDER_ID"));
req.setBelongSaler(firstStr(row, "BELONG_SALER_OLD"));
// Oracle 人员字段为中文名;编码在落库前按 sys_user.nick_name 反查
req.setBelongSaler(firstStr(row, "BELONG_SALER_OLD", "BELONG_SALER_NAME", "BELONG_SALER"));
req.setBelongSalerCode(firstStr(row, "BELONG_SALER_CODE", "SALER_CODE"));
req.setSettlementMan(firstStr(row, "SETTLEMENT_MAN_OLD"));
req.setSettlementMan(firstStr(row, "SETTLEMENT_MAN_OLD", "SETTLEMENT_MAN_NAME", "SETTLEMENT_MAN"));
req.setSettlementManCode(firstStr(row, "SETTLEMENT_MAN_CODE", "SETTLEMENT_CODE"));
req.setServiceMan(firstStr(row, "SERVICE_MAN_OLD"));
req.setServiceMan(firstStr(row, "SERVICE_MAN_OLD", "SERVICE_MAN_NAME", "SERVICE_MAN"));
req.setUpstreamRow(row);
req.setCustomerCode(firstStr(row, "CUSTOMER_CODE"));
req.setCustomerName(firstStr(row, "CUSTOMER_NAME"));
@@ -32,24 +32,24 @@ public interface IBmsProductService
public List<BmsProductCountry> selectCountryListByProductCode(String productCode, Date effectiveDate, Long productId);
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName) throws Exception;
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate) throws Exception;
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName) 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) throws Exception;
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) throws Exception;
public String submitImportCountryProductExcelAsync(MultipartFile file, String countryCode, String operName, Date effectiveDate) throws Exception;
/** Poll Redis progress for product import task. */
public BmsProductImportTask 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) throws Exception;
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) throws Exception;
public String importCountryProductExcel(byte[] bytes, String filename, String countryCode, String operName, String taskId, Date effectiveDate) throws Exception;
public java.util.Map<String, List<BmsProductCountry>> parseQuoteExcelByProduct(MultipartFile file) throws Exception;
@@ -23,14 +23,14 @@ public interface IPrcOrderBillService
List<PrcOrderBill> selectBillList(PrcOrderBill query);
/** 订单号下拉搜索选项(模糊匹配,去重限量) */
List<String> listOrderNoOptions(String keyword);
/** 订单号下拉搜索选项(模糊匹配,去重限量,带数据权限) */
List<String> listOrderNoOptions(PrcOrderBill query);
/** 运单号下拉搜索选项(模糊匹配,去重限量) */
List<String> listTrackingNoOptions(String keyword);
/** 运单号下拉搜索选项(模糊匹配,去重限量,带数据权限) */
List<String> listTrackingNoOptions(PrcOrderBill query);
/** 转单号下拉搜索选项(模糊匹配,去重限量) */
List<String> listTransferNoOptions(String keyword);
/** 转单号下拉搜索选项(模糊匹配,去重限量,带数据权限) */
List<String> listTransferNoOptions(PrcOrderBill query);
TmsPriceQuoteResult getBill(String billId);
@@ -51,6 +51,12 @@ public interface IPrcPriceSetService
*/
public Long copyVersion(Long sourcePsvId, String createBy);
/**
* Create an empty draft version under the package (no detail copy).
* @return new psvId
*/
public Long createVersion(Long priceSetId, String createBy);
/** Delete a draft version and its details / surcharge snapshots */
public int deleteVersion(Long psvId, String updateBy);
@@ -244,21 +244,25 @@ public class BmsProductServiceImpl implements IBmsProductService
}
@Override
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName) throws Exception
public String importProductExcel(MultipartFile file, boolean updateSupport, String operName, Date effectiveDate)
throws Exception
{
validateImportFile(file);
return importProductExcel(file.getBytes(), file.getOriginalFilename(), updateSupport, operName, null);
return importProductExcel(file.getBytes(), file.getOriginalFilename(), updateSupport, operName, null,
effectiveDate);
}
@Override
public String submitImportProductExcelAsync(MultipartFile file, boolean updateSupport, String operName) throws Exception
public String submitImportProductExcelAsync(MultipartFile file, boolean updateSupport, String operName,
Date effectiveDate) throws Exception
{
validateImportFile(file);
Date eff = requireImportEffectiveDate(effectiveDate);
byte[] bytes = file.getBytes();
String filename = file.getOriginalFilename();
String taskId = IdUtils.fastSimpleUUID();
importAsyncRunner.put(taskId, BmsProductImportTask.STATUS_PENDING, 0, "queued", null);
importAsyncRunner.runImportAll(taskId, bytes, filename, updateSupport, operName);
importAsyncRunner.runImportAll(taskId, bytes, filename, updateSupport, operName, eff);
return taskId;
}
@@ -273,9 +277,11 @@ public class BmsProductServiceImpl implements IBmsProductService
}
@Override
public String importProductExcel(byte[] bytes, String filename, boolean updateSupport, String operName, String taskId) throws Exception
public String importProductExcel(byte[] bytes, String filename, boolean updateSupport, String operName,
String taskId, Date effectiveDate) throws Exception
{
validateImportBytes(bytes, filename);
Date forcedEff = requireImportEffectiveDate(effectiveDate);
reportImportProgress(taskId, 10, "parsing excel");
try (InputStream is = new java.io.ByteArrayInputStream(bytes); Workbook workbook = WorkbookFactory.create(is))
@@ -293,6 +299,8 @@ public class BmsProductServiceImpl implements IBmsProductService
}
products = dedupeCatalogProducts(products);
reportImportProgress(taskId, 20, "parsing excel");
// Pre-check all products: reject whole import before any write if any is earlier than prev version
assertImportProductsEffectiveNotBeforePrev(products, forcedEff);
Map<String, BmsRegion> regionByNameCn = loadRegionByNameCn();
if (regionByNameCn.isEmpty())
@@ -319,8 +327,9 @@ public class BmsProductServiceImpl implements IBmsProductService
TransactionStatus tx = transactionManager.getTransaction(def);
try
{
product.setEffectiveDate(forcedEff);
int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions,
zoneByCountryAndCode, unmatchedCountries);
zoneByCountryAndCode, unmatchedCountries, forcedEff);
transactionManager.commit(tx);
successNum += nums[0];
skipNum += nums[1];
@@ -357,32 +366,38 @@ public class BmsProductServiceImpl implements IBmsProductService
}
@Override
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName) throws Exception
public String importCountryProductExcel(MultipartFile file, String countryCode, String operName, Date effectiveDate)
throws Exception
{
validateImportFile(file);
return importCountryProductExcel(file.getBytes(), file.getOriginalFilename(), countryCode, operName, null);
return importCountryProductExcel(file.getBytes(), file.getOriginalFilename(), countryCode, operName, null,
effectiveDate);
}
@Override
public String submitImportCountryProductExcelAsync(MultipartFile file, String countryCode, String operName) throws Exception
public String submitImportCountryProductExcelAsync(MultipartFile file, String countryCode, String operName,
Date effectiveDate) throws Exception
{
validateImportFile(file);
if (StringUtils.isEmpty(countryCode))
{
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
}
Date eff = requireImportEffectiveDate(effectiveDate);
byte[] bytes = file.getBytes();
String filename = file.getOriginalFilename();
String taskId = IdUtils.fastSimpleUUID();
importAsyncRunner.put(taskId, BmsProductImportTask.STATUS_PENDING, 0, "queued", null);
importAsyncRunner.runImportCountry(taskId, bytes, filename, countryCode, operName);
importAsyncRunner.runImportCountry(taskId, bytes, filename, countryCode, operName, eff);
return taskId;
}
@Override
public String importCountryProductExcel(byte[] bytes, String filename, String countryCode, String operName, String taskId) throws Exception
public String importCountryProductExcel(byte[] bytes, String filename, String countryCode, String operName,
String taskId, Date effectiveDate) throws Exception
{
validateImportBytes(bytes, filename);
Date forcedEff = requireImportEffectiveDate(effectiveDate);
if (StringUtils.isEmpty(countryCode))
{
throw new ServiceException(MessageUtils.message("price.product.import.countryRequired"));
@@ -410,6 +425,8 @@ public class BmsProductServiceImpl implements IBmsProductService
products = dedupeCatalogProducts(products);
Map<String, PriceBlock> priceBlocks = parseCountryPriceBlocks(workbook);
reportImportProgress(taskId, 20, "parsing excel");
// Pre-check all products: reject whole import before any write if any is earlier than prev version
assertImportProductsEffectiveNotBeforePrev(products, forcedEff);
int successNum = 0;
int skipNum = 0;
@@ -437,11 +454,12 @@ public class BmsProductServiceImpl implements IBmsProductService
}
product.setDestRegion(countryLabel);
product.setSourceSheet(block.sheetName);
product.setEffectiveDate(forcedEff);
if (StringUtils.isEmpty(product.getTaxMode()))
{
product.setTaxMode(resolveTaxMode(product.getProductName()));
}
List<BmsProductCountry> countries = stampSelectedCountry(block.rows, region, product.getEffectiveDate());
List<BmsProductCountry> countries = stampSelectedCountry(block.rows, region, forcedEff);
int[] nums = persistImportedProduct(product, countries, operName);
transactionManager.commit(tx);
successNum += nums[0];
@@ -594,7 +612,7 @@ public class BmsProductServiceImpl implements IBmsProductService
/** @return int[]{added, skipped, countryRows} */
private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName,
Map<String, BmsRegion> regionByNameCn, List<BmsRegion> euRegions,
Map<String, PrcZone> zoneByCountryAndCode, Set<String> unmatchedCountries)
Map<String, PrcZone> zoneByCountryAndCode, Set<String> unmatchedCountries, Date forcedEffectiveDate)
{
product.setSourceSheet(product.getProductName());
product.setCreateBy(operName);
@@ -614,13 +632,11 @@ public class BmsProductServiceImpl implements IBmsProductService
throw new ServiceException(MessageUtils.message("price.product.import.noCountry",
productSheet.getSheetName()));
}
Date sheetEff = resolveImportEffectiveDate(countries);
Date newEff = toDateOnly(sheetEff != null ? sheetEff : product.getEffectiveDate());
product.setEffectiveDate(newEff);
if (newEff == null)
{
throw new ServiceException(MessageUtils.message("price.product.import.effEmpty"));
}
// UI-selected effective date wins over Excel; previous open product/country rows expire at this date
Date newEff = requireImportEffectiveDate(forcedEffectiveDate != null ? forcedEffectiveDate
: product.getEffectiveDate());
applyForcedEffectiveDate(product, countries, newEff);
assertImportEffectiveNotBeforePrev(product, newEff);
BmsProduct existing = productMapper.selectBmsProductByCodeAndDate(
product.getProductCode(), newEff);
@@ -657,7 +673,7 @@ public class BmsProductServiceImpl implements IBmsProductService
* - expire all earlier open weight bands under that country
* - delete same-day open weight bands (unique key blocks insert if only expired)
* - insert Excel weight bands only (no copy / merge of old bands)
* Reject when newEff &lt; last open effective_date.
* Reject when newEff is earlier than the product's previous version effective time.
*/
private int saveCountries(BmsProduct product, List<BmsProductCountry> countries)
{
@@ -667,22 +683,16 @@ public class BmsProductServiceImpl implements IBmsProductService
}
String productCode = product.getProductCode();
Long productId = product.getProductId();
Date newEff = toDateOnly(resolveImportEffectiveDate(countries));
Date newEff = normalizeToHour(product.getEffectiveDate());
if (newEff == null)
{
newEff = toDateOnly(product.getEffectiveDate());
newEff = normalizeToHour(resolveImportEffectiveDate(countries));
}
if (newEff == null)
{
throw new ServiceException(MessageUtils.message("price.product.import.effEmpty"));
}
Date lastOpenEff = toDateOnly(countryMapper.selectMaxOpenEffectiveDate(productCode));
if (lastOpenEff != null && newEff.before(lastOpenEff))
{
throw new ServiceException(MessageUtils.message("price.product.import.effTooEarly",
formatDate(newEff), formatDate(lastOpenEff)));
throw new ServiceException(MessageUtils.message("price.product.import.effRequired"));
}
assertImportEffectiveNotBeforePrev(product, newEff);
Set<String> replacedCountries = new HashSet<>();
String rowStatus = resolveStoredStatus(newEff, null);
@@ -776,6 +786,15 @@ public class BmsProductServiceImpl implements IBmsProductService
return new SimpleDateFormat("yyyy-MM-dd").format(d);
}
private String formatDateTime(Date d)
{
if (d == null)
{
return "-";
}
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(d);
}
private Date toDateOnly(Date d)
{
if (d == null)
@@ -792,17 +811,35 @@ public class BmsProductServiceImpl implements IBmsProductService
}
}
/** 0 pending / 1 active / 2 expired from dates vs today */
/** Truncate to hour precision (mm:ss = 00). */
private Date normalizeToHour(Date d)
{
if (d == null)
{
return null;
}
try
{
String s = new SimpleDateFormat("yyyy-MM-dd HH").format(d) + ":00:00";
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(s);
}
catch (Exception e)
{
return d;
}
}
/** 0 pending / 1 active / 2 expired from datetime vs now */
private String resolveStoredStatus(Date effectiveDate, Date expireDate)
{
Date today = toDateOnly(new Date());
Date expire = toDateOnly(expireDate);
Date eff = toDateOnly(effectiveDate);
if (expire != null && today != null && !expire.after(today))
Date now = new Date();
Date expire = expireDate;
Date eff = effectiveDate;
if (expire != null && !expire.after(now))
{
return BmsProduct.STATUS_EXPIRED;
}
if (eff == null || (today != null && eff.after(today)))
if (eff == null || eff.after(now))
{
return BmsProduct.STATUS_PENDING;
}
@@ -1893,6 +1930,85 @@ public class BmsProductServiceImpl implements IBmsProductService
return out;
}
/** Require import effective time (hour precision). */
private Date requireImportEffectiveDate(Date effectiveDate)
{
Date d = normalizeToHour(effectiveDate);
if (d == null)
{
throw new ServiceException(MessageUtils.message("price.product.import.effRequired"));
}
return d;
}
/**
* Reject import when selected effective time is earlier than the product's latest previous version.
* Same effective time (re-import that version) is allowed.
*/
private void assertImportEffectiveNotBeforePrev(BmsProduct product, Date newEff)
{
if (product == null || StringUtils.isEmpty(product.getProductCode()) || newEff == null)
{
return;
}
BmsProduct latest = productMapper.selectBmsProductByCode(product.getProductCode());
if (latest == null || latest.getEffectiveDate() == null)
{
return;
}
Date prevEff = normalizeToHour(latest.getEffectiveDate());
if (prevEff == null || !newEff.before(prevEff))
{
return;
}
String code = product.getProductCode();
String name = StringUtils.isNotEmpty(product.getProductName())
? product.getProductName()
: (StringUtils.isNotEmpty(latest.getProductName()) ? latest.getProductName() : "-");
throw new ServiceException(MessageUtils.message("price.product.import.effTooEarly",
code, name, formatDateTime(newEff), formatDateTime(prevEff)));
}
/** Batch pre-check: fail whole import before any DB write. */
private void assertImportProductsEffectiveNotBeforePrev(List<BmsProduct> products, Date forcedEff)
{
Date newEff = requireImportEffectiveDate(forcedEff);
if (products == null || products.isEmpty())
{
return;
}
for (BmsProduct product : products)
{
if (product == null)
{
continue;
}
product.setEffectiveDate(newEff);
assertImportEffectiveNotBeforePrev(product, newEff);
}
}
/** Stamp product + all country detail rows with the same effective date. */
private void applyForcedEffectiveDate(BmsProduct product, List<BmsProductCountry> countries, Date newEff)
{
Date eff = requireImportEffectiveDate(newEff);
if (product != null)
{
product.setEffectiveDate(eff);
}
if (countries == null)
{
return;
}
for (BmsProductCountry c : countries)
{
if (c != null)
{
c.setEffectiveDate(eff);
}
}
}
/** @return int[]{added, skipped, countryRows} */
private int[] persistImportedProduct(BmsProduct product, List<BmsProductCountry> countries, String operName)
{
@@ -1902,16 +2018,9 @@ public class BmsProductServiceImpl implements IBmsProductService
{
product.setSourceSheet(product.getProductName());
}
Date newEff = toDateOnly(product.getEffectiveDate());
if (newEff == null)
{
newEff = toDateOnly(resolveImportEffectiveDate(countries));
}
product.setEffectiveDate(newEff);
if (newEff == null)
{
throw new ServiceException(MessageUtils.message("price.product.import.effEmpty"));
}
Date newEff = requireImportEffectiveDate(product.getEffectiveDate());
applyForcedEffectiveDate(product, countries, newEff);
assertImportEffectiveNotBeforePrev(product, newEff);
BmsProduct existing = productMapper.selectBmsProductByCodeAndDate(product.getProductCode(), newEff);
if (existing != null)
{
@@ -13,6 +13,7 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
@@ -21,6 +22,8 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.bms.ar.enums.ArIncomeBillFeeStatus;
import com.bms.ar.enums.ArOrderBillStatus;
import com.bms.common.annotation.DataScope;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.DictUtils;
import com.bms.common.utils.MessageUtils;
@@ -29,6 +32,7 @@ import com.bms.common.utils.StringUtils;
import com.bms.common.utils.sign.Md5Utils;
import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
import com.bms.system.mapper.SysUserMapper;
import com.bms.price.domain.PrcIncomeBill;
import com.bms.price.domain.PrcIncomeBillFee;
import com.bms.price.domain.PrcOrderBill;
@@ -70,6 +74,10 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
private static final String DICT_FEE_TYPE = "bms_fee_type";
private static final String FEE_TYPE_REFUND = "REFUND";
@Autowired
@Lazy
private IPrcOrderBillService self;
@Autowired
private ITmsPriceQuoteService tmsPriceQuoteService;
@@ -80,6 +88,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
@Autowired
private BmsCustomerMapper bmsCustomerMapper;
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private PrcOrderBillExtMapper orderBillExtMapper;
@@ -236,7 +247,12 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
}
}
/**
* 计费列表:按销售员/结算员字段做数据权限(仅本人/本部门等)。
* 编码口径同客户:belong_saler_code / settlement_man_code = sys_user.user_name(工号)或 user_id。
*/
@Override
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public List<PrcOrderBill> selectBillList(PrcOrderBill query)
{
query.setOrderNos(normalizeCodeList(query.getOrderNos()));
@@ -248,33 +264,39 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
}
@Override
public List<String> listOrderNoOptions(String keyword)
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public List<String> listOrderNoOptions(PrcOrderBill query)
{
if (StringUtils.isBlank(keyword))
if (query == null || query.getParams() == null
|| StringUtils.isBlank((String) query.getParams().get("optionKeyword")))
{
return new ArrayList<>();
}
return orderBillMapper.selectOrderNoOptions(keyword.trim(), 20);
return orderBillMapper.selectOrderNoOptions(query, 20);
}
@Override
public List<String> listTrackingNoOptions(String keyword)
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public List<String> listTrackingNoOptions(PrcOrderBill query)
{
if (StringUtils.isBlank(keyword))
if (query == null || query.getParams() == null
|| StringUtils.isBlank((String) query.getParams().get("optionKeyword")))
{
return new ArrayList<>();
}
return orderBillMapper.selectTrackingNoOptions(keyword.trim(), 20);
return orderBillMapper.selectTrackingNoOptions(query, 20);
}
@Override
public List<String> listTransferNoOptions(String keyword)
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "price:billing:bill")
public List<String> listTransferNoOptions(PrcOrderBill query)
{
if (StringUtils.isBlank(keyword))
if (query == null || query.getParams() == null
|| StringUtils.isBlank((String) query.getParams().get("optionKeyword")))
{
return new ArrayList<>();
}
return orderBillMapper.selectTransferNoOptions(keyword.trim(), 20);
return orderBillMapper.selectTransferNoOptions(query, 20);
}
/**
@@ -303,13 +325,17 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
{
throw new ServiceException(MessageUtils.message("price.bill.billIdRequired"));
}
PrcOrderBill bill = orderBillMapper.selectPrcOrderBillById(billId);
if (bill == null)
PrcOrderBill query = new PrcOrderBill();
query.setBillId(billId.trim());
// 走 selectBillList,复用销售员/结算员数据权限,避免越权查详情
List<PrcOrderBill> list = self.selectBillList(query);
if (list == null || list.isEmpty())
{
throw new ServiceException(MessageUtils.message("price.bill.notFound"));
}
return toResult(bill, incomeBillFeeMapper.selectByBillId(billId),
payBillFeeMapper.selectByBillId(billId));
PrcOrderBill bill = list.get(0);
return toResult(bill, incomeBillFeeMapper.selectByBillId(bill.getBillId()),
payBillFeeMapper.selectByBillId(bill.getBillId()));
}
@Override
@@ -774,6 +800,45 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
return StringUtils.isEmpty(normalized) ? null : normalized.toUpperCase();
}
/**
* 同步订单:Oracle 销售员/结算员/客服为中文名,按 sys_user.nick_name 反查。
* 名称写 nick_name,编码写 user_name(工号,供数据权限);落到 prc_order_bill / prc_income_bill。
*/
private void resolveOrderStaffFromSysUser(TmsPriceBillRequest request)
{
if (request == null)
{
return;
}
SysUser saler = findUserByChineseName(request.getBelongSaler());
if (saler != null)
{
request.setBelongSaler(saler.getNickName());
request.setBelongSalerCode(saler.getUserName());
}
SysUser settlement = findUserByChineseName(request.getSettlementMan());
if (settlement != null)
{
request.setSettlementMan(settlement.getNickName());
request.setSettlementManCode(settlement.getUserName());
}
SysUser service = findUserByChineseName(request.getServiceMan());
if (service != null)
{
request.setServiceMan(service.getNickName());
request.setServiceManCode(service.getUserName());
}
}
private SysUser findUserByChineseName(String chineseName)
{
if (StringUtils.isBlank(chineseName))
{
return null;
}
return sysUserMapper.selectUserByNickName(chineseName.trim());
}
private void persistBill(TmsPriceBillRequest request, BigDecimal weight, Date bizDate,
TmsPriceQuoteResult result)
{
@@ -804,11 +869,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
bill.setCustomerName(request.getCustomerName());
bill.setTrackingNo(request.getTrackingNo());
bill.setTransferNo(request.getTransferNo());
// Oracle 中文人员名 -> sys_user.nick_name,*_code 写 user_name(数据权限)
resolveOrderStaffFromSysUser(request);
bill.setBelongSaler(request.getBelongSaler());
bill.setBelongSalerCode(request.getBelongSalerCode());
bill.setSettlementMan(request.getSettlementMan());
bill.setSettlementManCode(request.getSettlementManCode());
bill.setServiceMan(request.getServiceMan());
bill.setServiceManCode(request.getServiceManCode());
String customerActiveStatus = result == null ? null : result.getCustomerActiveStatus();
if (StringUtils.isEmpty(customerActiveStatus))
{
@@ -1109,6 +1177,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
fee.setFeeName(line.getFeeName());
fee.setScId(line.getScId() == null ? 0L : line.getScId());
fee.setAmount(line.getAmount() == null ? BigDecimal.ZERO : line.getAmount());
fee.setProductAmount(line.getProductAmount());
fee.setRedlineAmount(line.getRedlineAmount());
fee.setCurrency(line.getCurrency());
fee.setRoundMode(line.getRoundMode());
fee.setRemark(line.getRemark());
@@ -1301,6 +1371,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
line.setFeeName(fee.getFeeName());
line.setScId(fee.getScId());
line.setAmount(fee.getAmount());
line.setProductAmount(fee.getProductAmount());
line.setRedlineAmount(fee.getRedlineAmount());
line.setCurrency(fee.getCurrency());
line.setRoundMode(fee.getRoundMode());
line.setRemark(fee.getRemark());
@@ -1342,6 +1414,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
r.setSettlementMan(bill.getSettlementMan());
r.setSettlementManCode(bill.getSettlementManCode());
r.setServiceMan(bill.getServiceMan());
r.setServiceManCode(bill.getServiceManCode());
r.setCustomerName(bill.getCustomerName());
r.setCustomerActiveStatus(bill.getCustomerActiveStatus());
r.setCreditArrearsStatus(bill.getCreditArrearsStatus());
@@ -374,6 +374,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
{
throw new ServiceException(MessageUtils.message("price.set.edit.stopped"));
}
Date prevEffective = findPrevVersionEffectiveTime(version);
if (prevEffective != null && effectiveTime.before(prevEffective))
{
throw new ServiceException(MessageUtils.message("price.set.version.effectiveTime.beforePrev"));
}
// Set schedule; refreshVersionStatus decides PENDING(3) vs EFFECT(1) and chains expire_time
version.setEffectiveTime(effectiveTime);
version.setExpireTime(null);
@@ -383,6 +388,45 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
return 1;
}
/** Previous version (lower version_seq) effective_time; null if none. */
private Date findPrevVersionEffectiveTime(PrcPriceSetVersion version)
{
if (version == null || version.getPriceSetId() == null)
{
return null;
}
int curSeq = version.getVersionSeq() == null ? 0 : version.getVersionSeq().intValue();
PrcPriceSetVersion q = new PrcPriceSetVersion();
q.setPriceSetId(version.getPriceSetId());
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
if (list == null || list.isEmpty())
{
return null;
}
PrcPriceSetVersion prev = null;
for (PrcPriceSetVersion v : list)
{
if (v == null || v.getEffectiveTime() == null)
{
continue;
}
if (version.getPsvId() != null && version.getPsvId().equals(v.getPsvId()))
{
continue;
}
int vs = v.getVersionSeq() == null ? 0 : v.getVersionSeq().intValue();
if (vs >= curSeq)
{
continue;
}
if (prev == null || vs > (prev.getVersionSeq() == null ? 0 : prev.getVersionSeq().intValue()))
{
prev = v;
}
}
return prev == null ? null : prev.getEffectiveTime();
}
@Override
@Transactional
public Long copyVersion(Long sourcePsvId, String createBy)
@@ -430,6 +474,35 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
return nv.getPsvId();
}
@Override
@Transactional
public Long createVersion(Long priceSetId, String createBy)
{
PrcPriceSet set = requireSet(priceSetId);
if (STATUS_STOP.equals(set.getStatus()))
{
throw new ServiceException(MessageUtils.message("price.set.edit.stopped"));
}
if (hasDraftVersion(set.getPriceSetId()))
{
throw new ServiceException(MessageUtils.message("price.set.version.create.draftExists"));
}
Integer maxSeq = versionMapper.selectMaxVersionSeq(set.getPriceSetId());
int seq = maxSeq == null ? 1 : maxSeq.intValue() + 1;
PrcPriceSetVersion nv = new PrcPriceSetVersion();
nv.setPriceSetId(set.getPriceSetId());
nv.setVersionSeq(Integer.valueOf(seq));
nv.setVersionNo("V" + seq);
nv.setStatus(VER_DRAFT);
nv.setEffectiveTime(null);
nv.setExpireTime(null);
nv.setPriceBuildMode(null);
nv.setCreateBy(createBy);
versionMapper.insertPrcPriceSetVersion(nv);
refreshVersionStatus(set.getPriceSetId());
return nv.getPsvId();
}
@Override
@Transactional
public int deleteVersion(Long psvId, String updateBy)
@@ -957,8 +957,12 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
r.setPayTotal(productAmount);
String currency = normalizeCurrency(r.getCurrency());
List<TmsPriceQuoteFeeLine> payLines = new ArrayList<TmsPriceQuoteFeeLine>();
payLines.add(feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, productFreight, currency, roundMode, null));
payLines.add(feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, baseReg, currency, roundMode, null));
TmsPriceQuoteFeeLine payFreight = feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, productFreight, currency, roundMode, null);
payFreight.setProductAmount(productFreight);
payLines.add(payFreight);
TmsPriceQuoteFeeLine payReg = feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, baseReg.setScale(2, rm), currency, roundMode, null);
payReg.setProductAmount(baseReg.setScale(2, rm));
payLines.add(payReg);
r.setPayFeeDetails(payLines);
}
@@ -971,8 +975,14 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
List<PrcSurchargeRel> rels = loadMatchedSurcharges(result);
List<TmsPriceQuoteFeeLine> lines = new ArrayList<TmsPriceQuoteFeeLine>();
lines.add(feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, freightAmount, currency, result.getRoundMode(), null));
lines.add(feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, registrationFee, currency, result.getRoundMode(), null));
BigDecimal productFreight = productAmountOf(result.getPayFeeDetails(), FEE_BASIC);
BigDecimal productReg = productAmountOf(result.getPayFeeDetails(), FEE_REG);
TmsPriceQuoteFeeLine basicLine = feeLine(FEE_BASIC, feeNameOf(FEE_BASIC), 0L, freightAmount, currency, result.getRoundMode(), null);
basicLine.setProductAmount(productFreight);
lines.add(basicLine);
TmsPriceQuoteFeeLine regLine = feeLine(FEE_REG, feeNameOf(FEE_REG), 0L, registrationFee, currency, result.getRoundMode(), null);
regLine.setProductAmount(productReg);
lines.add(regLine);
BigDecimal surchargeTotal = BigDecimal.ZERO;
Map<Long, TmsPriceQuoteFeeLine> surchargeLines = new LinkedHashMap<Long, TmsPriceQuoteFeeLine>();
@@ -1382,6 +1392,23 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
return Md5Utils.hash(raw);
}
/** Read catalog product amount for a fee type from payFeeDetails. */
private static BigDecimal productAmountOf(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.getProductAmount() != null ? line.getProductAmount() : line.getAmount();
}
}
return null;
}
private static TmsPriceQuoteFeeLine feeLine(String feeType, String feeName, Long scId, BigDecimal amount,
String currency, String roundMode, String remark)
{
@@ -1,5 +1,6 @@
package com.bms.price.task;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,12 +31,14 @@ public class BmsProductImportAsyncRunner
/** Background all-country (Weishi catalog) import. */
@Async("threadPoolTaskExecutor")
public void runImportAll(String taskId, byte[] bytes, String filename, boolean updateSupport, String operName)
public void runImportAll(String taskId, byte[] bytes, String filename, boolean updateSupport, String operName,
Date effectiveDate)
{
try
{
put(taskId, BmsProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
String message = productService.importProductExcel(bytes, filename, updateSupport, operName, taskId);
String message = productService.importProductExcel(bytes, filename, updateSupport, operName, taskId,
effectiveDate);
put(taskId, BmsProductImportTask.STATUS_SUCCESS, 100, message, null);
log.info("async product import done, taskId={}", taskId);
}
@@ -48,12 +51,14 @@ public class BmsProductImportAsyncRunner
/** Background single-country cost-sheet import. */
@Async("threadPoolTaskExecutor")
public void runImportCountry(String taskId, byte[] bytes, String filename, String countryCode, String operName)
public void runImportCountry(String taskId, byte[] bytes, String filename, String countryCode, String operName,
Date effectiveDate)
{
try
{
put(taskId, BmsProductImportTask.STATUS_RUNNING, 5, "parsing excel", null);
String message = productService.importCountryProductExcel(bytes, filename, countryCode, operName, taskId);
String message = productService.importCountryProductExcel(bytes, filename, countryCode, operName, taskId,
effectiveDate);
put(taskId, BmsProductImportTask.STATUS_SUCCESS, 100, message, null);
log.info("async product country import done, taskId={}, country={}", taskId, countryCode);
}
@@ -51,6 +51,14 @@ public interface SysUserMapper
*/
public SysUser selectUserByUserName(String userName);
/**
* 通过中文昵称(nick_name)精确查用户(未删除,优先启用)
*
* @param nickName 中文名称
* @return 用户对象信息
*/
public SysUser selectUserByNickName(String nickName);
/**
* 通过用户ID查询用户
*
@@ -191,8 +191,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="refreshStatusByDates">
update product_country
set status = case
when expire_date is not null and expire_date &lt;= curdate() then '2'
when effective_date is null or effective_date &gt; curdate() then '0'
when expire_date is not null and expire_date &lt;= now() then '2'
when effective_date is null or effective_date &gt; now() then '0'
else '1'
end
</update>
@@ -206,8 +206,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="refreshStatusByDates">
update product
set status = case
when expire_date is not null and expire_date &lt;= curdate() then '2'
when effective_date is null or effective_date &gt; curdate() then '0'
when expire_date is not null and expire_date &lt;= now() then '2'
when effective_date is null or effective_date &gt; now() then '0'
else '1'
end
</update>
@@ -12,6 +12,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="feeName" column="fee_name" />
<result property="scId" column="sc_id" />
<result property="amount" column="amount" />
<result property="productAmount" column="product_amount" />
<result property="redlineAmount" column="redline_amount" />
<result property="currency" column="currency" />
<result property="roundMode" column="round_mode" />
<result property="remark" column="remark" />
@@ -21,7 +23,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<select id="selectByBillId" parameterType="String" resultMap="PrcIncomeBillFeeResult">
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
from prc_income_bill_fee
where bill_id = #{billId}
@@ -29,7 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectByBillIds" resultMap="PrcIncomeBillFeeResult">
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
from prc_income_bill_fee
where bill_id in
@@ -40,7 +42,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectUnbilledByBillIds" resultMap="PrcIncomeBillFeeResult">
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
from prc_income_bill_fee
where ar_bill_status = #{arBillStatus}
@@ -52,7 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectByFeeId" parameterType="String" resultMap="PrcIncomeBillFeeResult">
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
select fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
from prc_income_bill_fee
where fee_id = #{feeId}
@@ -68,21 +70,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertPrcIncomeBillFee" parameterType="PrcIncomeBillFee">
insert into prc_income_bill_fee (
fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
) values (
#{feeId}, #{billId}, #{incomeBillId}, #{feeType}, #{feeName}, #{scId}, #{amount}, #{currency}, #{roundMode}, #{remark},
#{feeId}, #{billId}, #{incomeBillId}, #{feeType}, #{feeName}, #{scId}, #{amount}, #{productAmount}, #{redlineAmount}, #{currency}, #{roundMode}, #{remark},
#{arBillStatus}, #{manualFlag}, sysdate()
)
</insert>
<insert id="insertBatch" parameterType="java.util.List">
insert into prc_income_bill_fee (
fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, currency, round_mode, remark,
fee_id, bill_id, income_bill_id, fee_type, fee_name, sc_id, amount, product_amount, redline_amount, currency, round_mode, remark,
ar_bill_status, manual_flag, create_time
) values
<foreach collection="list" item="item" separator=",">
(#{item.feeId}, #{item.billId}, #{item.incomeBillId}, #{item.feeType}, #{item.feeName}, #{item.scId}, #{item.amount},
#{item.productAmount}, #{item.redlineAmount},
#{item.currency}, #{item.roundMode}, #{item.remark},
#{item.arBillStatus}, #{item.manualFlag}, sysdate())
</foreach>
@@ -94,6 +97,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
fee_name = #{feeName},
sc_id = #{scId},
amount = #{amount},
product_amount = #{productAmount},
redline_amount = #{redlineAmount},
currency = #{currency},
round_mode = #{roundMode},
remark = #{remark}
@@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="settlementMan" column="settlement_man" />
<result property="settlementManCode" column="settlement_man_code" />
<result property="serviceMan" column="service_man" />
<result property="serviceManCode" column="service_man_code" />
<result property="customerId" column="customer_id" />
<result property="customerCode" column="customer_code" />
<result property="customerName" column="customer_name" />
@@ -77,7 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectPrcOrderBillVo">
select bill_id, bill_key, order_no, tracking_no, transfer_no,
belong_saler, belong_saler_code, settlement_man, settlement_man_code, service_man,
belong_saler, belong_saler_code, settlement_man, settlement_man_code, service_man, service_man_code,
customer_id, customer_code, customer_name,
pieces, actual_weight, volume, volume_weight, pkg_length, pkg_width, pkg_height,
shipper_name, consignee_name, dest_postcode, warehouse_code, order_time, order_remark,
@@ -89,13 +90,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
freight_amount, surcharge_amount, income_total, pay_total, currency, ar_bill_status,
customer_active_status, credit_arrears_status, round_mode,
created_by, create_time, updated_by, update_time
from prc_order_bill
from prc_order_bill p
</sql>
<!-- 应收生成专用来源快照:income_total 只汇总当前仍未出账的费用。 -->
<sql id="selectArBillSourceVo">
select p.bill_id, p.bill_key, p.order_no, p.tracking_no, p.transfer_no,
p.belong_saler, p.belong_saler_code, p.settlement_man, p.settlement_man_code, p.service_man,
p.belong_saler, p.belong_saler_code, p.settlement_man, p.settlement_man_code, p.service_man, p.service_man_code,
p.customer_id, p.customer_code, p.customer_name,
p.pieces, p.actual_weight, p.volume, p.volume_weight, p.pkg_length, p.pkg_width, p.pkg_height,
p.shipper_name, p.consignee_name, p.dest_postcode, p.warehouse_code, p.order_time, p.order_remark,
@@ -150,119 +151,167 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where bill_id = #{billId}
</select>
<!-- 计费列表数据权限:当前用户为销售员或结算员(工号=user_name 或 user_id)可见;
@DataScope 注入 su.user_id / su.dept_id 条件;空则不过滤(管理员/全部数据权限) -->
<sql id="orderBillDataScopeFilter">
<if test="params != null and params.dataScope != null and params.dataScope != ''">
AND EXISTS (
SELECT 1 FROM sys_user su
WHERE su.del_flag = '0'
AND (
(p.belong_saler_code IS NOT NULL AND p.belong_saler_code != ''
AND (su.user_name = p.belong_saler_code COLLATE utf8mb4_general_ci
OR su.user_id = p.belong_saler_code + 0))
OR
(p.settlement_man_code IS NOT NULL AND p.settlement_man_code != ''
AND (su.user_name = p.settlement_man_code COLLATE utf8mb4_general_ci
OR su.user_id = p.settlement_man_code + 0))
)
${params.dataScope}
)
</if>
</sql>
<!-- options query: query + limit, dataScope in query.params -->
<sql id="orderBillDataScopeFilterByQuery">
<if test="query != null and query.params != null and query.params.dataScope != null and query.params.dataScope != ''">
AND EXISTS (
SELECT 1 FROM sys_user su
WHERE su.del_flag = '0'
AND (
(p.belong_saler_code IS NOT NULL AND p.belong_saler_code != ''
AND (su.user_name = p.belong_saler_code COLLATE utf8mb4_general_ci
OR su.user_id = p.belong_saler_code + 0))
OR
(p.settlement_man_code IS NOT NULL AND p.settlement_man_code != ''
AND (su.user_name = p.settlement_man_code COLLATE utf8mb4_general_ci
OR su.user_id = p.settlement_man_code + 0))
)
${query.params.dataScope}
)
</if>
</sql>
<select id="selectOrderNoOptions" resultType="String">
select order_no
from prc_order_bill
select p.order_no
from prc_order_bill p
<where>
<if test="keyword != null and keyword != ''">
order_no like concat('%', #{keyword}, '%')
<if test="query.params != null and query.params.optionKeyword != null and query.params.optionKeyword != ''">
AND p.order_no like concat('%', #{query.params.optionKeyword}, '%')
</if>
<include refid="orderBillDataScopeFilterByQuery"/>
</where>
group by order_no
order by max(create_time) desc
group by p.order_no
order by max(p.create_time) desc
limit #{limit}
</select>
<select id="selectTrackingNoOptions" resultType="String">
select tracking_no
from prc_order_bill
select p.tracking_no
from prc_order_bill p
<where>
<if test="keyword != null and keyword != ''">
tracking_no like concat('%', #{keyword}, '%')
<if test="query.params != null and query.params.optionKeyword != null and query.params.optionKeyword != ''">
AND p.tracking_no like concat('%', #{query.params.optionKeyword}, '%')
</if>
<include refid="orderBillDataScopeFilterByQuery"/>
</where>
group by tracking_no
order by max(create_time) desc
group by p.tracking_no
order by max(p.create_time) desc
limit #{limit}
</select>
<select id="selectTransferNoOptions" resultType="String">
select transfer_no
from prc_order_bill
select p.transfer_no
from prc_order_bill p
<where>
<if test="keyword != null and keyword != ''">
transfer_no like concat('%', #{keyword}, '%')
<if test="query.params != null and query.params.optionKeyword != null and query.params.optionKeyword != ''">
AND p.transfer_no like concat('%', #{query.params.optionKeyword}, '%')
</if>
<include refid="orderBillDataScopeFilterByQuery"/>
</where>
group by transfer_no
order by max(create_time) desc
group by p.transfer_no
order by max(p.create_time) desc
limit #{limit}
</select>
<select id="selectPrcOrderBillList" parameterType="PrcOrderBill" resultMap="PrcOrderBillResult">
<include refid="selectPrcOrderBillVo"/>
<where>
<if test="billId != null and billId != ''">
AND p.bill_id = #{billId}
</if>
<if test="orderNo != null and orderNo != ''">
AND order_no like concat('%', #{orderNo}, '%')
AND p.order_no like concat('%', #{orderNo}, '%')
</if>
<if test="orderNos != null and orderNos.size() > 0">
AND order_no IN
AND p.order_no IN
<foreach collection="orderNos" item="ono" open="(" separator="," close=")">
#{ono}
</foreach>
</if>
<if test="trackingNo != null and trackingNo != ''">
AND tracking_no like concat('%', #{trackingNo}, '%')
AND p.tracking_no like concat('%', #{trackingNo}, '%')
</if>
<if test="trackingNos != null and trackingNos.size() > 0">
AND tracking_no IN
AND p.tracking_no IN
<foreach collection="trackingNos" item="tno" open="(" separator="," close=")">
#{tno}
</foreach>
</if>
<if test="transferNo != null and transferNo != ''">
AND transfer_no like concat('%', #{transferNo}, '%')
AND p.transfer_no like concat('%', #{transferNo}, '%')
</if>
<if test="transferNos != null and transferNos.size() > 0">
AND transfer_no IN
AND p.transfer_no IN
<foreach collection="transferNos" item="rno" open="(" separator="," close=")">
#{rno}
</foreach>
</if>
<if test="customerActiveStatus != null and customerActiveStatus != ''">
AND customer_active_status = #{customerActiveStatus}
AND p.customer_active_status = #{customerActiveStatus}
</if>
<if test="creditArrearsStatus != null and creditArrearsStatus != ''">
AND credit_arrears_status = #{creditArrearsStatus}
AND p.credit_arrears_status = #{creditArrearsStatus}
</if>
<if test="customerCode != null and customerCode != ''">
AND customer_code like concat('%', #{customerCode}, '%')
AND p.customer_code like concat('%', #{customerCode}, '%')
</if>
<if test="customerCodes != null and customerCodes.size() > 0">
AND customer_code IN
AND p.customer_code IN
<foreach collection="customerCodes" item="ccode" open="(" separator="," close=")">
#{ccode}
</foreach>
</if>
<if test="customerName != null and customerName != ''">
AND customer_name like concat('%', #{customerName}, '%')
AND p.customer_name like concat('%', #{customerName}, '%')
</if>
<if test="productCode != null and productCode != ''">
AND product_code like concat('%', #{productCode}, '%')
AND p.product_code like concat('%', #{productCode}, '%')
</if>
<if test="productCodes != null and productCodes.size() > 0">
AND product_code IN
AND p.product_code IN
<foreach collection="productCodes" item="pcode" open="(" separator="," close=")">
#{pcode}
</foreach>
</if>
<if test="countryCode != null and countryCode != ''">
AND country_code = #{countryCode}
AND p.country_code = #{countryCode}
</if>
<if test="matchSource != null and matchSource != ''">
AND match_source = #{matchSource}
AND p.match_source = #{matchSource}
</if>
<if test="bizDate != null">
AND biz_date = #{bizDate}
AND p.biz_date = #{bizDate}
</if>
<if test="params.beginTime != null and params.beginTime != ''">
AND date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
AND date_format(p.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''">
AND date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
AND date_format(p.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
<include refid="orderBillDataScopeFilter"/>
</where>
order by create_time desc, bill_id desc
order by p.create_time desc, p.bill_id desc
</select>
<!-- rename_bill_tables.sql 已将需求文档中的 prc_bill 映射为本项目的 prc_order_bill。 -->
@@ -449,7 +498,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertPrcOrderBill" parameterType="PrcOrderBill">
insert into prc_order_bill (
bill_id, bill_key, order_no, tracking_no, transfer_no,
belong_saler, belong_saler_code, settlement_man, settlement_man_code, service_man,
belong_saler, belong_saler_code, settlement_man, settlement_man_code, service_man, service_man_code,
customer_id, customer_code, customer_name,
pieces, actual_weight, volume, volume_weight, pkg_length, pkg_width, pkg_height,
shipper_name, consignee_name, dest_postcode, warehouse_code, order_time, order_remark,
@@ -463,7 +512,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
create_time
) values (
#{billId}, #{billKey}, #{orderNo}, #{trackingNo}, #{transferNo},
#{belongSaler}, #{belongSalerCode}, #{settlementMan}, #{settlementManCode}, #{serviceMan},
#{belongSaler}, #{belongSalerCode}, #{settlementMan}, #{settlementManCode}, #{serviceMan}, #{serviceManCode},
#{customerId}, #{customerCode}, #{customerName},
#{pieces}, #{actualWeight}, #{volume}, #{volumeWeight}, #{pkgLength}, #{pkgWidth}, #{pkgHeight},
#{shipperName}, #{consigneeName}, #{destPostcode}, #{warehouseCode}, #{orderTime}, #{orderRemark},
@@ -488,6 +537,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
settlement_man = #{settlementMan},
settlement_man_code = #{settlementManCode},
service_man = #{serviceMan},
service_man_code = #{serviceManCode},
customer_id = #{customerId},
customer_code = #{customerCode},
customer_name = #{customerName},
@@ -137,6 +137,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectUserVo"/>
where u.user_name = #{userName} and u.del_flag = '0'
</select>
<select id="selectUserByNickName" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.nick_name = #{nickName} and u.del_flag = '0'
order by case when u.status = '0' then 0 else 1 end, u.user_id
limit 1
</select>
<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
<include refid="selectUserVo"/>