feat(oms):出入库导入存在性校验改为收集后统一提示(物料+客户不存在的编号一条消息带全)

调整2d1e4b59引入的校验流程(入库parseStockInExcel/出库parseExcelFile统一):
1. 第一步:物料编号存在性校验——逐编号查库,全部不存在的收集到missingMaterials,不立即报错
2. 第二步:客户编号存在性校验——逐编号查库,全部不存在的收集到missingCustomers,不立即报错
3. 第三步:两步全部完成后统一抛出提示,避免修完物料问题重导一次才暴露客户问题:
   只缺物料:"物料[A、B]不存在,请先维护物料信息,维护完成后重新导入"
   只缺客户:"客户[C]不存在,请先维护客户信息,维护完成后重新导入"
   都缺:"物料[A、B]不存在,请先维护物料信息;客户[C、D]不存在,请先维护客户信息,维护完成后重新导入"
4. 存在性全部通过后才继续多货主/多客户编号/客户与货主一致性等校验及建单;
   入库"多个客户编号"校验相应移到存在性之后,多个编号中缺哪个报哪个
5. 入库客户查询结果存入customerCache复用,一致性校验不再二次查库

漏填编号(空白行)仍即时报错,不属于存在性问题,行为不变
This commit is contained in:
rcx
2026-09-01 09:53:51 +08:00
parent a588264849
commit bb3ca932d2
@@ -259,7 +259,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
List<Map<String,Object>> shipperIds = new ArrayList<>();
Set<Long> processedShipperIds = new HashSet<>();
// ====== 物料存在性校验(落库前):查询结果为空说明库中没有该物料,收集全部缺失料号后一次性提示维护 ======
// ====== 第一步:物料编号存在性校验(落库前,先收集全部缺失编号,不立即报错) ======
Map<String, MaterialBaseInfoPO> materialCache = new LinkedHashMap<>();
List<String> missingMaterials = new ArrayList<>();
for (ProductDetail productDetail : productDetailList) {
@@ -277,21 +277,10 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
materialCache.put(productCode, materialBaseInfoPO);
}
}
if (!missingMaterials.isEmpty()) {
throw new Exception("物料[" + String.join("", missingMaterials) + "]不存在,请先维护物料信息后再导入");
}
for (MaterialBaseInfoPO materialBaseInfoPO : materialCache.values()) {
if (materialBaseInfoPO.getShipperId() != null && processedShipperIds.add(materialBaseInfoPO.getShipperId())) {
Map<String, Object> shipper = new HashMap<>();
shipper.put("shipperId", materialBaseInfoPO.getShipperId());
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
shipperIds.add(shipper);
}
}
// ====== 客户存在性校验(落库前):客户编号在库中查询结果为空说明客户不存在,提示维护后再导入 ======
// ====== 第二步:客户编号存在性校验(落库前,先收集全部缺失编号,不立即报错) ======
Map<String, UserPo> customerCache = new HashMap<>();
List<String> missingCustomers = new ArrayList<>();
for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) {
String customerCode = customerDeliveryDetail.getCustomerCode() == null ? "" : customerDeliveryDetail.getCustomerCode().trim();
if (customerCode.isEmpty()) {
@@ -302,9 +291,33 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
}
UserPo customer = stockOutOrderMapper.queryCustomerByCode(customerCode, loginUser.getUserPo().getOrganizationId());
if (customer == null || customer.getUserId() == null) {
throw new Exception("客户[" + customerCode + "]不存在,请先维护客户信息后再导入");
missingCustomers.add(customerCode);
} else {
customerCache.put(customerCode, customer);
}
}
// ====== 第三步:物料/客户存在性校验全部完成后,统一提示两类编号各自有哪些不存在 ======
if (!missingMaterials.isEmpty() || !missingCustomers.isEmpty()) {
List<String> problems = new ArrayList<>();
if (!missingMaterials.isEmpty()) {
problems.add("物料[" + String.join("", missingMaterials) + "]不存在,请先维护物料信息");
}
if (!missingCustomers.isEmpty()) {
problems.add("客户[" + String.join("", missingCustomers) + "]不存在,请先维护客户信息");
}
throw new Exception(String.join("", problems) + ",维护完成后重新导入");
}
// ====== 后续处理(编号均已存在):货主归属校验、建单落库 ======
for (MaterialBaseInfoPO materialBaseInfoPO : materialCache.values()) {
if (materialBaseInfoPO.getShipperId() != null && processedShipperIds.add(materialBaseInfoPO.getShipperId())) {
Map<String, Object> shipper = new HashMap<>();
shipper.put("shipperId", materialBaseInfoPO.getShipperId());
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
shipperIds.add(shipper);
}
customerCache.put(customerCode, customer);
}
// 货品明细不为空时,校验货主信息
@@ -506,8 +519,8 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
throw new ServiceException("未解析到产品明细数据,请检查模板中是否填写了產品編號");
}
StockInHeader stockInHeader = resultDTO.getStockInHeader();
// ====== 物料存在性校验模板只填物料编号即可,物料名称按编号自动匹配库中数据 ======
// 查询结果为空说明库中没有该物料,收集全部缺失料号后一次性提示维护
// ====== 第一步:物料编号存在性校验模板只填物料编号即可,名称按编号自动匹配 ======
// 查询结果为空说明库中没有该物料,收集全部缺失编号,不立即报错
Map<String, MaterialBaseInfoPO> materialCache = new LinkedHashMap<>();
List<String> missingMaterials = new ArrayList<>();
for (StockInProductDetail productDetail : productDetailList) {
@@ -522,9 +535,35 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
materialCache.put(productCode, materialBaseInfoPO);
}
}
if (!missingMaterials.isEmpty()) {
throw new ServiceException("物料[" + String.join("", missingMaterials) + "]不存在,请先维护物料信息后再导入");
// ====== 第二步:客户编号存在性校验:先收集全部缺失编号,不立即报错 ======
Set<String> customerCodes = new LinkedHashSet<>();
for (StockInProductDetail productDetail : productDetailList) {
if (productDetail.getCustomerCode() != null && !productDetail.getCustomerCode().trim().isEmpty()) {
customerCodes.add(productDetail.getCustomerCode().trim());
}
}
Map<String, UserPo> customerCache = new HashMap<>();
List<String> missingCustomers = new ArrayList<>();
for (String customerCode : customerCodes) {
UserPo customer = stockOutOrderMapper.queryCustomerByCode(customerCode, loginUser.getUserPo().getOrganizationId());
if (customer == null || customer.getUserId() == null) {
missingCustomers.add(customerCode);
} else {
customerCache.put(customerCode, customer);
}
}
// ====== 第三步:物料/客户存在性校验全部完成后,统一提示两类编号各自有哪些不存在 ======
if (!missingMaterials.isEmpty() || !missingCustomers.isEmpty()) {
List<String> problems = new ArrayList<>();
if (!missingMaterials.isEmpty()) {
problems.add("物料[" + String.join("", missingMaterials) + "]不存在,请先维护物料信息");
}
if (!missingCustomers.isEmpty()) {
problems.add("客户[" + String.join("", missingCustomers) + "]不存在,请先维护客户信息");
}
throw new ServiceException(String.join("", problems) + ",维护完成后重新导入");
}
// ====== 后续处理(编号均已存在):名称自动匹配、货主归属与一致性校验、建单 ======
// 明细回填库中匹配到的物料名称(自动匹配,模板中的名称列可不填/以库中为准;库中名称为空时保留模板值)
for (StockInProductDetail productDetail : productDetailList) {
String materialName = materialCache.get(productDetail.getProductCode()).getMaterialName();
@@ -532,7 +571,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
productDetail.setProductName(materialName);
}
}
// ====== 物料归属货主集合(用于客户编号一致性校验,及未填客户编号时的兜底取值) ======
// 物料归属货主集合(用于客户编号一致性校验,及未填客户编号时的兜底取值)
List<Map<String,Object>> shipperIds = new ArrayList<>();
Set<Long> processedShipperIds = new HashSet<>();
for (MaterialBaseInfoPO materialBaseInfoPO : materialCache.values()) {
@@ -554,24 +593,14 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
}
throw new ServiceException("导入的物料不属于同一货主(货主编号:" + shipperCodeStr + "),一个入库单只能属于一个货主");
}
// ====== 客户编号校验与名称自动匹配:模板填客户编号,客户名称以库中数据为准 ======
// 客户取值:模板填客户编号时取库中匹配结果(编号/名称均为自动匹配值)
UserPo customer = null;
Set<String> customerCodes = new LinkedHashSet<>();
for (StockInProductDetail productDetail : productDetailList) {
if (productDetail.getCustomerCode() != null && !productDetail.getCustomerCode().trim().isEmpty()) {
customerCodes.add(productDetail.getCustomerCode().trim());
}
}
if (customerCodes.size() > 1) {
throw new ServiceException("导入明细中存在多个客户编号(" + String.join("", customerCodes) + "),一个入库单只能属于一个客户");
}
if (!customerCodes.isEmpty()) {
String customerCode = customerCodes.iterator().next();
// 查询结果为空说明库中没有该客户,提示维护后再导入
customer = stockOutOrderMapper.queryCustomerByCode(customerCode, loginUser.getUserPo().getOrganizationId());
if (customer == null || customer.getUserId() == null) {
throw new ServiceException("客户[" + customerCode + "]不存在,请先维护客户信息后再导入");
}
customer = customerCache.get(customerCode);
// 客户编号与物料归属货主一致性校验(不一致拦截,提示两者编号;货主编码未维护时提示货主名称)
if (!shipperIds.isEmpty() && !customer.getUserId().equals(shipperIds.get(0).get("shipperId"))) {
Object materialShipperCode = shipperIds.get(0).get("shipperCode");