feat(oms):入库模板导入支持模板自带业务单号,重复拦截并按R+年月日规则生成
1. 模板OrderNO解析兼容无标签新模板(E1直接为单号值) 2. 导入单号优先模板自带:先校验R+年月日+3位序号格式(含年月日有效性), 再跨reservation/reserve两表查重,重复提示"入库业务单号已存在" 3. 模板无单号时按R+年月日+当日累加生成(insertDomain不再无条件覆盖已设置单号,清理无效的仓库规则生成死代码)
This commit is contained in:
+23
-5
@@ -92,18 +92,36 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
|
||||
}
|
||||
|
||||
// OrderNO:查找包含"OrderNO"标签的下一个值
|
||||
// OrderNO:优先查找"OrderNO"标签后的值(老模板布局,标签与值相邻);
|
||||
// 新模板第1行无"OrderNO"标签,单号值直接放在单元格中(如E1),
|
||||
// 此时取第1行中除标签、入库日期以外的第一个非空字符串值
|
||||
Object orderNoObj = getValueAfterLabel(data, "OrderNO");
|
||||
if (orderNoObj == null) {
|
||||
for (Object value : data.values()) {
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String str = value.toString().trim();
|
||||
// 跳过标签单元格与已被识别为入库日期的值
|
||||
if (str.isEmpty() || str.contains("入庫日期") || str.contains("OrderNO") || str.contains("Total")) {
|
||||
continue;
|
||||
}
|
||||
if (value == stockInDateObj) {
|
||||
continue;
|
||||
}
|
||||
orderNoObj = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (orderNoObj != null) {
|
||||
String orderNo = orderNoObj.toString().trim();
|
||||
stockInHeader.setOrderNo(orderNo);
|
||||
LOGGER.debug("第1行:OrderNO解析完成,值:{}", orderNo);
|
||||
}
|
||||
|
||||
// 校验表头数据完整性
|
||||
if (stockInHeader.getStockInDate() == null || stockInHeader.getOrderNo() == null || stockInHeader.getOrderNo().isEmpty()) {
|
||||
LOGGER.warn("第1行表头数据不完整,入库日期:{},OrderNO:{}",
|
||||
stockInHeader.getStockInDate(), stockInHeader.getOrderNo());
|
||||
// 校验表头数据完整性(OrderNO允许为空:为空时由系统按规则生成业务单号)
|
||||
if (stockInHeader.getStockInDate() == null) {
|
||||
LOGGER.warn("第1行表头数据不完整,入库日期:{}", stockInHeader.getStockInDate());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
-66
@@ -485,10 +485,26 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
throw new ServiceException("导入的产品均未配置货主,请先在物料档案中维护货主信息");
|
||||
}
|
||||
ReservationStockInOrderDO reservationStockInOrderDO = new ReservationStockInOrderDO();
|
||||
// 生成业务单号
|
||||
String orderNumber = reservationStockInOrderDomainService.generateReserveOrderNumber();
|
||||
reservationStockInOrderDO.setInOrderNumber(orderNumber);
|
||||
reservationStockInOrderDO.setBusinessOrderNo(orderNumber);
|
||||
StockInHeader stockInHeader = resultDTO.getStockInHeader();
|
||||
// 业务单号:优先使用模板自带OrderNO,与系统已有入库业务单号重复时提示;
|
||||
// 模板未填单号时按 R+年月日+当日累加 规则生成(如R260717001、R260717002)
|
||||
String templateOrderNo = stockInHeader.getOrderNo() == null ? "" : stockInHeader.getOrderNo().trim();
|
||||
if (!templateOrderNo.isEmpty()) {
|
||||
// 先校验模板单号是否符合规则:R+年月日+3位及以上序号,如R260717001
|
||||
validateTemplateOrderNo(templateOrderNo);
|
||||
// 跨两张入库单表查重(与手工新增取号逻辑保持一致)
|
||||
// 注意:本类字段命名易混淆——stockInOrderMapper为reservation表,reservationStockInOrderMapper为reserve表
|
||||
boolean duplicated = stockInOrderMapper.existsByOrderNumber(templateOrderNo) > 0
|
||||
|| reservationStockInOrderMapper.countByInOrderNumber(templateOrderNo) > 0;
|
||||
if (duplicated) {
|
||||
throw new ServiceException("入库业务单号已存在:" + templateOrderNo + ",请修改模板中的OrderNO后重新导入");
|
||||
}
|
||||
}
|
||||
if (templateOrderNo.isEmpty()) {
|
||||
templateOrderNo = reservationStockInOrderDomainService.generateReserveOrderNumber();
|
||||
}
|
||||
reservationStockInOrderDO.setInOrderNumber(templateOrderNo);
|
||||
reservationStockInOrderDO.setBusinessOrderNo(templateOrderNo);
|
||||
reservationStockInOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
reservationStockInOrderDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
reservationStockInOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||
@@ -510,7 +526,6 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationStockInOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
|
||||
reservationStockInOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
|
||||
reservationStockInOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
|
||||
StockInHeader stockInHeader = resultDTO.getStockInHeader();
|
||||
reservationStockInOrderDO.setWarehouseDate(stockInHeader.getStockInDate());
|
||||
reservationStockInOrderDO.setStorageTime(stockInHeader.getStockInDate());
|
||||
reservationStockInOrderDO.setExpectTime(stockInHeader.getStockInDate());
|
||||
@@ -583,75 +598,41 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
|
||||
|
||||
/**
|
||||
* 从单号列表中提取最大序号
|
||||
* 单号格式: prefix + 数字序号(如 RK260807001 中 prefix=RK260807, 序号=001)
|
||||
* 即使中间有删单,也能保证序号递增不重复
|
||||
*
|
||||
* @param orderNumbers 匹配的单号列表
|
||||
* @param prefixLength 前缀长度(序号从该位置开始截取)
|
||||
* @return 最大序号值,列表为空时返回0
|
||||
* 校验模板自带业务单号是否符合规则:R + 年月日(yyMMdd) + 3位及以上序号
|
||||
* 示例:R260717001、R260717002
|
||||
* 格式不符或年月日部分非有效日期(如R261332001)时抛出提示
|
||||
*/
|
||||
private int extractMaxSequenceNumber(List<String> orderNumbers, int prefixLength) {
|
||||
if (orderNumbers == null || orderNumbers.isEmpty()) {
|
||||
return 0;
|
||||
private void validateTemplateOrderNo(String orderNo) {
|
||||
// R + 6位年月日 + 至少3位数字序号
|
||||
if (!orderNo.matches("R\\d{9,}")) {
|
||||
throw new ServiceException("入库业务单号格式不正确:" + orderNo
|
||||
+ ",请按照[代码R+年月日+当日累加]规则填写单号,正确格式为R+年月日+3位序号,如R260717001");
|
||||
}
|
||||
int maxSeq = 0;
|
||||
for (String orderNumber : orderNumbers) {
|
||||
if (orderNumber != null && orderNumber.length() > prefixLength) {
|
||||
try {
|
||||
String seqStr = orderNumber.substring(prefixLength);
|
||||
int seq = Integer.parseInt(seqStr);
|
||||
if (seq > maxSeq) {
|
||||
maxSeq = seq;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 序号部分非数字,跳过
|
||||
}
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("yyMMdd");
|
||||
fmt.setLenient(false);
|
||||
fmt.parse(orderNo.substring(1, 7));
|
||||
} catch (java.text.ParseException e) {
|
||||
throw new ServiceException("入库业务单号格式不正确:" + orderNo
|
||||
+ ",请按照[代码R+年月日+当日累加]规则填写单号,单号中的年月日无效,正确格式为R+年月日+3位序号,如R260717001");
|
||||
}
|
||||
return maxSeq;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertDomain(ReservationStockInOrderDO stockInOrderDO) {
|
||||
// if (stockInOrderDO.getInOrderNumber() == null) {
|
||||
|
||||
//设置入库单号
|
||||
Long warehouseId = stockInOrderDO.getWarehouseId();
|
||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
throw new ServiceException("获取仓库信息失败");
|
||||
}
|
||||
//仓库信息
|
||||
WarehouseFeignPO warehouseFeignPO = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson2.JSONObject.toJSONString(ajaxResult.get("data")), WarehouseFeignPO.class);
|
||||
String orderNoGenerateRule = warehouseFeignPO.getOrderNoGenerateRule();
|
||||
String prefix = warehouseFeignPO.getInOrderNoPrefix();
|
||||
if (prefix == null || prefix.isEmpty()) {
|
||||
prefix = "RK";
|
||||
}
|
||||
Long organizationId = stockInOrderDO.getOrganizationId();
|
||||
Long warehouseIdForCount = stockInOrderDO.getWarehouseId();
|
||||
if (orderNoGenerateRule != null && "0".equals(orderNoGenerateRule)) {
|
||||
//日期累加: 前缀 + 当前日期yymmdd + 当天最大序号+1
|
||||
String dateStr = new SimpleDateFormat("yyMMdd").format(new Date());
|
||||
String datePrefix = prefix + dateStr;
|
||||
List<String> orderNumbers = reservationStockInOrderMapper.getMaxStockInOrderByPrefixAndOrgAndWarehouse(datePrefix, organizationId, warehouseIdForCount);
|
||||
int maxSeq = extractMaxSequenceNumber(orderNumbers, datePrefix.length());
|
||||
String seq = String.format("%03d", maxSeq + 1);
|
||||
stockInOrderDO.setInOrderNumber(datePrefix + seq);
|
||||
stockInOrderDO.setBusinessOrderNo(datePrefix + seq);
|
||||
} else {
|
||||
//历史累加: 前缀 + 历史最大序号+1
|
||||
List<String> orderNumbers = reservationStockInOrderMapper.getMaxStockInOrderByPrefixAndOrgAndWarehouse(prefix, organizationId, warehouseIdForCount);
|
||||
int maxSeq = extractMaxSequenceNumber(orderNumbers, prefix.length());
|
||||
String seq = String.format("%03d", maxSeq + 1);
|
||||
stockInOrderDO.setInOrderNumber(prefix + seq);
|
||||
stockInOrderDO.setBusinessOrderNo(prefix + seq);
|
||||
}
|
||||
// 仓库信息校验(保留原有行为:仓库信息获取失败直接报错)
|
||||
Long warehouseId = stockInOrderDO.getWarehouseId();
|
||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
||||
if (!"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
throw new ServiceException("获取仓库信息失败");
|
||||
}
|
||||
// 入库单号:调用方已设置(如导入时优先使用模板自带OrderNO,且已校验不重复)则沿用,不再覆盖;
|
||||
// 未设置时按 R+年月日+当日累加 规则生成(如R260717001、R260717002),跨reservation/reserve两表取号防撞
|
||||
if (stockInOrderDO.getInOrderNumber() == null || stockInOrderDO.getInOrderNumber().trim().isEmpty()) {
|
||||
stockInOrderDO.setInOrderNumber(reservationStockInOrderDomainService.generateReserveOrderNumber());
|
||||
stockInOrderDO.setBusinessOrderNo(stockInOrderDO.getInOrderNumber());
|
||||
// }
|
||||
// //统计总计划数
|
||||
}
|
||||
stockInOrderDO.setBusinessOrderNo(stockInOrderDO.getInOrderNumber());
|
||||
//统计总计划数
|
||||
BigDecimal quantity = stockInOrderDO.getMaterialDetailList().stream().map(ReservationInMaterialDetailDO::getQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
stockInOrderDO.setQuantity(quantity);
|
||||
BigDecimal amount = stockInOrderDO.getMaterialDetailList().stream().map(ReservationInMaterialDetailDO::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
Reference in New Issue
Block a user