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:
+98
-8
@@ -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 货主名称
|
||||
|
||||
Reference in New Issue
Block a user