diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/materialBaseInfo/service/MaterialBaseInfoResolveService.java b/mhd_oms/src/main/java/com/mhd/oms/domain/materialBaseInfo/service/MaterialBaseInfoResolveService.java index 510ca34b6..02b37e095 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/materialBaseInfo/service/MaterialBaseInfoResolveService.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/materialBaseInfo/service/MaterialBaseInfoResolveService.java @@ -5,6 +5,7 @@ import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMateria import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper; import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationStockInOrderMapper; import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -13,6 +14,7 @@ import org.springframework.stereotype.Service; * 解决客户端推单把 materialInventoryId 传进 materialBaseInfoId 字段、或漏传导致明细存脏ID的问题。 * 三级解析:1.按baseId直查 → 2.按库存ID反查纠正 → 3.按物料编码+组织兜底 */ +@Slf4j @Service public class MaterialBaseInfoResolveService { @@ -30,11 +32,23 @@ public class MaterialBaseInfoResolveService { * @return 解析成功返回物料信息(调用方需用 po.getMaterialBaseInfoId() 回填纠正后的ID);全部失败返回 null */ public MaterialBaseInfoPO resolve(Long materialBaseInfoId, Long materialInventoryId, String materialCode, Long organizationId) { - // 1. 优先按 baseId 直查(有效ID不受影响) + // 1. 优先按 baseId 直查(有效ID不受影响)。 + // 防张冠李戴:客户端曾把库存ID传进 materialBaseInfoId,若该库存ID恰好撞上另一条有效档案的主键 + // (如库存1201撞上编码=名称的脏档案"水箱"),按ID查到的档案编码会与明细编码对不上。 + // 此时 baseId 不可信,继续走库存反查/编码兜底;两级都落空时才退回按ID查到的档案 + MaterialBaseInfoPO byId = null; if (materialBaseInfoId != null && materialBaseInfoId > 0) { MaterialBaseInfoPO po = reservationStockInOrderMapper.getinfo(materialBaseInfoId); if (po != null) { - return po; + if (StringUtils.isNotBlank(materialCode) && po.getMaterialCode() != null + && !po.getMaterialCode().trim().equals(materialCode.trim())) { + log.warn("物料ID疑似误传:baseInfoId={} 按主键查到档案[{}({})],与明细编码[{}]不一致," + + "转库存反查/编码兜底(客户端可能把库存ID传进了materialBaseInfoId)", + materialBaseInfoId, po.getMaterialCode(), po.getMaterialName(), materialCode); + byId = po; + } else { + return po; + } } } // 2. baseId 无效但带了库存ID → 反查库存表拿正确 baseInfoId(修复"库存ID传进baseId"场景) @@ -50,8 +64,12 @@ public class MaterialBaseInfoResolveService { } // 3. 兜底:按物料编码+组织查(修复 baseId=0 漏传场景) if (StringUtils.isNotBlank(materialCode) && organizationId != null) { - return reservationStockInOrderMapper.getByCode(materialCode.trim(), organizationId); + MaterialBaseInfoPO po = reservationStockInOrderMapper.getByCode(materialCode.trim(), organizationId); + if (po != null) { + return po; + } } - return null; + // 编码不一致已证明 baseId 不可信,但库存反查/编码兜底都落空时,退回按ID查到的档案,保持原有兜底行为 + return byId; } } diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java index 9d7774ba5..21ed397d6 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java @@ -297,16 +297,39 @@ public class ReservationStockInOrderImpl extends ServiceImpl problems = new ArrayList<>(); - if (!missingMaterials.isEmpty()) { - problems.add("物料[" + String.join("、", missingMaterials) + "]不存在,请先维护物料信息"); + // ====== 第三步:物料/客户/库存三类校验全部完成后统一报错(不再分阶段各自中断) ====== + List problems = new ArrayList<>(); + if (!missingMaterials.isEmpty()) { + problems.add("物料[" + String.join("、", missingMaterials) + "]不存在,请先维护物料信息"); + } + if (!missingCustomers.isEmpty()) { + problems.add("客户[" + String.join("、", missingCustomers) + "]不存在,请先维护客户信息"); + } + + // 库存预检:只对已存在的物料比较批次库存(不存在的物料问题已收集),不足的同样收集 + if (!productDetailList.isEmpty()) { + Set insufficientMaterials = new HashSet<>(); + for (ProductDetail productDetail : productDetailList) { + // materialCache 的 key 是 trim 过的编码,取用时同样 trim,避免 Excel 编号带空格导致校验被静默跳过 + String productCode = productDetail.getProductCode() == null ? "" : productDetail.getProductCode().trim(); + MaterialBaseInfoPO materialBaseInfoPO = materialCache.get(productCode); + if (materialBaseInfoPO == null) { + continue; // 物料不存在的问题已收集 + } + BigDecimal requireQty = new BigDecimal(productDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP); + List inventoryList = stockInOrderMapper.getkcId( + materialBaseInfoPO.getMaterialBaseInfoId(), requireQty, warehouseId); + if (inventoryList == null || inventoryList.isEmpty()) { + insufficientMaterials.add(productCode); + } } - if (!missingCustomers.isEmpty()) { - problems.add("客户[" + String.join("、", missingCustomers) + "]不存在,请先维护客户信息"); + if (!insufficientMaterials.isEmpty()) { + problems.add("物料[" + String.join("、", insufficientMaterials) + "]" + + (warehouseId != null && warehouseId > 0 ? "在当前仓库" : "") + "批次库存数量少于出库数量"); } - throw new Exception(String.join(";", problems) + ",维护完成后重新导入"); + } + if (!problems.isEmpty()) { + throw new Exception(String.join(";", problems) + ",请处理后重新导入"); } // ====== 后续处理(编号均已存在):货主归属校验、建单落库 ====== @@ -332,28 +355,6 @@ public class ReservationStockInOrderImpl extends ServiceImpl insufficientMaterials = new HashSet<>(); - for (ProductDetail productDetail : productDetailList) { - MaterialBaseInfoPO materialBaseInfoPO = materialCache.get(productDetail.getProductCode()); - if (materialBaseInfoPO == null) { - continue; // 存在性已在前面统一校验 - } - BigDecimal requireQty = new BigDecimal(productDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP); - List inventoryList = stockInOrderMapper.getkcId( - materialBaseInfoPO.getMaterialBaseInfoId(), requireQty, warehouseId); - if (inventoryList == null || inventoryList.isEmpty()) { - insufficientMaterials.add(productDetail.getProductCode()); - } - } - if (!insufficientMaterials.isEmpty()) { - throw new Exception("物料[" + String.join("、", insufficientMaterials) + "]" - + (warehouseId != null && warehouseId > 0 ? "在当前仓库" : "") + "批次库存数量少于出库数量!"); - } - } - ReservationStockOutOrderDO reservationStockOutOrderDO = new ReservationStockOutOrderDO(); // 生成出库业务单号 String orderNumber = reservationStockOutOrderApplicationService.generateReserveOrderNumber(); @@ -407,8 +408,9 @@ public class ReservationStockInOrderImpl extends ServiceImpl + 客户编码只认 USER.USER_MEMBER_CODE(客户编码); + 2026-09-03 起不再放行 USER_SHIPPER.CUSTOMER_NC_CODE(NC客户代码),避免会员/NC编码撞车导致不存在的客户编号误通过 --> select - b.* + b.*, a.status, a.issue_status from reserve_stock_out_order a left join reserve_out_material_detail b on a.OUT_ORDER_NUMBER = b.OUT_ORDER_NUMBER