fix(wms): 入库单导入"该货主下不存在该料号"误报修复

- 根因:Excel客户名称按USER表user_name解析货主ID,同名双账号(如3519/3525)解析到非档案归属账号,带错误shipperId查物料必为空
- 料号匹配:主查询(料号+顶级组织+解析shipperId)为空时,去掉货主条件按档案shipper_name与Excel客户名称等值兜底匹配,命中后以档案货主纠正单据shipperId/shipperCode/shipperName
- 新增pickMaterialByCode挑选:料号精确等值优先于LIKE模糊命中,避免子串料号截胡
- SKU校验:档案条码barCode为空时跳过比对,档案有值时仍严格校验
- 校验失败输出诊断日志并附解析shipperId/登录topOrganizationId,区分货主错配(原因A)与组织口径(原因B)
This commit is contained in:
rcx
2026-09-11 15:14:46 +08:00
parent d69af9ef20
commit 37dd374bda
@@ -2040,21 +2040,71 @@ public class StockInOrderApplicationService {
r.getMaterialCode(), topOrganizationId, shipperId);
List<MaterialBaseInfoPO> materialList = materialBaseInfoService.queryList(queryDO);
log.info("料号查询结果,查询到 {} 条记录", materialList != null ? materialList.size() : 0);
MaterialBaseInfoPO materialBaseInfoPO = null;
if (materialList != null && !materialList.isEmpty()) {
materialBaseInfoPO = materialList.get(0);
MaterialBaseInfoPO materialBaseInfoPO = pickMaterialByCode(materialList, r.getMaterialCode(), shipperId, order.getShipperName());
// 兜底:按名称解析出的shipperId可能与物料档案shipper_id不一致(系统中存在同名货主账号,
// 按user_name精确匹配解析到了另一个同名列货主),此时去掉货主ID条件再查一次,
// 用物料档案冗余的shipper_name与Excel客户名称等值匹配
if (materialBaseInfoPO == null && StringUtils.isNotBlank(order.getShipperName())) {
MaterialBaseInfoDO fallbackDO = new MaterialBaseInfoDO();
fallbackDO.setMaterialCode(r.getMaterialCode());
if (topOrganizationId != null) {
fallbackDO.setTopOrganizationId(topOrganizationId);
}
List<MaterialBaseInfoPO> fallbackList = materialBaseInfoService.queryList(fallbackDO);
materialBaseInfoPO = pickMaterialByCode(fallbackList, r.getMaterialCode(), null, order.getShipperName());
if (materialBaseInfoPO != null) {
log.warn("导入料号校验:按解析shipperId未匹配到物料,已按档案货主名称兜底匹配。料号: [{}], 解析shipperId: {}, 档案shipperId: {}, 档案shipperName: {}",
r.getMaterialCode(), shipperId, materialBaseInfoPO.getShipperId(), materialBaseInfoPO.getShipperName());
// 以档案货主为准纠正单据货主信息,避免后续shipperParam按错误的shipperId反查覆盖货主名称
if (materialBaseInfoPO.getShipperId() != null) {
shipperId = materialBaseInfoPO.getShipperId();
order.setShipperId(shipperId);
}
if (StringUtils.isNotBlank(materialBaseInfoPO.getShipperCode())) {
order.setShipperCode(materialBaseInfoPO.getShipperCode());
}
if (StringUtils.isNotBlank(materialBaseInfoPO.getShipperName())) {
order.setShipperName(materialBaseInfoPO.getShipperName());
}
}
}
if (materialBaseInfoPO != null) {
materialBaseInfoId = materialBaseInfoPO.getMaterialBaseInfoId();
log.info("通过料号查询到物料基础信息,料号: {}, 物料ID: {}, 货主ID: {}",
log.info("通过料号查询到物料基础信息,料号: {}, 物料ID: {}, 货主ID: {}",
r.getMaterialCode(), materialBaseInfoId, shipperId);
} else {
// 如果提供了料号但查询不到(或不属于该货主),直接报错,不允许导入
String errorMsg = String.format("导入失败:该货主下不存在该料号(货主名称=%s,料号=%s)。请确保该料号已在物料管理中创建,且属于该货主",
order.getShipperName(), r.getMaterialCode());
// 诊断对照查询(仅用于日志定位,不影响流程):去掉货主ID条件再查一次,
// 用于区分两类原因:A-按名称解析出的shipperId与物料档案shipper_id不一致(货主错配);
// B-登录人topOrganizationId与物料档案top_organization_id不一致(组织口径不一致)
try {
MaterialBaseInfoDO diagDO = new MaterialBaseInfoDO();
diagDO.setMaterialCode(r.getMaterialCode());
if (topOrganizationId != null) {
diagDO.setTopOrganizationId(topOrganizationId);
}
List<MaterialBaseInfoPO> diagList = materialBaseInfoService.queryList(diagDO);
if (diagList != null && !diagList.isEmpty()) {
MaterialBaseInfoPO diagPo = diagList.get(0);
log.error("导入料号校验诊断:去掉货主条件后可查到物料,判定为货主解析错配(原因A)。料号: [{}], 解析shipperId: {}, 登录topOrganizationId: {}, 物料ID: {}, 档案shipperId: {}, 档案shipperName: {}, 档案topOrganizationId: {}",
r.getMaterialCode(), shipperId, topOrganizationId,
diagPo.getMaterialBaseInfoId(), diagPo.getShipperId(), diagPo.getShipperName(), diagPo.getTopOrganizationId());
} else {
log.error("导入料号校验诊断:去掉货主条件后仍查不到物料,判定为组织口径不一致(原因B)。料号: [{}], 解析shipperId: {}, 登录topOrganizationId: {}",
r.getMaterialCode(), shipperId, topOrganizationId);
}
} catch (Exception diagEx) {
log.warn("导入料号校验诊断查询异常,料号: [{}], 错误: {}", r.getMaterialCode(), diagEx.getMessage());
}
String errorMsg = String.format("导入失败:该货主下不存在该料号(货主名称=%s,料号=%s,解析shipperId=%s,登录topOrganizationId=%s)。请确保该料号已在物料管理中创建,且属于该货主",
order.getShipperName(), r.getMaterialCode(),
shipperId == null ? "" : shipperId, topOrganizationId == null ? "" : topOrganizationId);
log.error(errorMsg);
throw new ServiceException(errorMsg);
}
// 校验料号与规格型号/SKU码一致性:按料号查到档案后逐项比对,任何一项不符直接报对应错误
// 双方(trim后)必须完全一致:Excel留空而档案有值、或Excel填了值而档案为空,均视为不一致报错
// 规格型号trim后)双方必须完全一致:Excel留空而档案有值、或Excel填了值而档案为空,均视为不一致报错
// SKU码:档案条码(barCode)为空时跳过比对(存量档案普遍未维护条码);档案有值时仍严格比对
String excelSpec = StringUtils.trimToEmpty(r.getSpecificationModel());
String dbSpec = StringUtils.trimToEmpty(materialBaseInfoPO.getSpecificationModel());
if (!excelSpec.equals(dbSpec)) {
@@ -2064,7 +2114,7 @@ public class StockInOrderApplicationService {
}
String excelSku = StringUtils.trimToEmpty(r.getSkuCode());
String dbSku = StringUtils.trimToEmpty(materialBaseInfoPO.getBarCode());
if (!excelSku.equals(dbSku)) {
if (StringUtils.isNotEmpty(dbSku) && !excelSku.equals(dbSku)) {
throw new ServiceException(String.format("导入失败:料号[%s](商品名称:%s)SKU码与物料档案不一致(Excel:%s / 档案:%s",
r.getMaterialCode(), r.getMaterialName(),
excelSku.isEmpty() ? "" : excelSku, dbSku.isEmpty() ? "" : dbSku));
@@ -2398,6 +2448,46 @@ public class StockInOrderApplicationService {
return oid != null && loginOrganizationId.equals(oid);
}
/**
* 导入校验:从按料号(SQL为LIKE模糊)查出的物料列表中挑选匹配记录。
* 优先级:料号精确等值优先于模糊命中(避免子串料号误匹配);
* 同级内优先 shipperId 精确匹配,其次 shipperName 等值匹配,最后取排序第一条(与原 get(0) 行为一致)。
*/
private MaterialBaseInfoPO pickMaterialByCode(List<MaterialBaseInfoPO> materialList, String materialCode, Long shipperId, String shipperName) {
if (materialList == null || materialList.isEmpty()) {
return null;
}
String code = StringUtils.trimToEmpty(materialCode);
String name = StringUtils.trimToEmpty(shipperName);
MaterialBaseInfoPO exactCodeFirst = null;
MaterialBaseInfoPO anyFirst = null;
// 第一轮:料号精确等值中优先 shipperId 精确匹配
for (MaterialBaseInfoPO po : materialList) {
if (anyFirst == null) {
anyFirst = po;
}
if (StringUtils.isEmpty(code) || !code.equals(StringUtils.trimToEmpty(po.getMaterialCode()))) {
continue;
}
if (exactCodeFirst == null) {
exactCodeFirst = po;
}
if (shipperId != null && shipperId.equals(po.getShipperId())) {
return po;
}
}
// 第二轮:料号精确等值中按档案货主名称等值匹配
if (StringUtils.isNotEmpty(name)) {
for (MaterialBaseInfoPO po : materialList) {
if (StringUtils.isNotEmpty(code) && code.equals(StringUtils.trimToEmpty(po.getMaterialCode()))
&& name.equals(StringUtils.trimToEmpty(po.getShipperName()))) {
return po;
}
}
}
return exactCodeFirst != null ? exactCodeFirst : anyFirst;
}
/**
* @description 根据货主名称或编号获取货主ID(用于导入时提前获取货主ID)
* @param shipperName 货主名称