diff --git a/src/main/java/com/gaotao/modules/autoWareHourceHaian/service/impl/HaiAnNewIssureServiceImpl.java b/src/main/java/com/gaotao/modules/autoWareHourceHaian/service/impl/HaiAnNewIssureServiceImpl.java index 72f83219..c0ea18d4 100644 --- a/src/main/java/com/gaotao/modules/autoWareHourceHaian/service/impl/HaiAnNewIssureServiceImpl.java +++ b/src/main/java/com/gaotao/modules/autoWareHourceHaian/service/impl/HaiAnNewIssureServiceImpl.java @@ -34,7 +34,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -401,7 +403,7 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { /** * @author rqrq - * @Description 拉取发货单及行项目:查询用大site,IFS工厂编码(contract)作为subsite来源 - rqrq + * @Description 拉取发货单及行项目:头查询site按大site+'%'模糊;行查询按头返回的CONTRACT分别调用 - rqrq */ @Override public List haiAnGetShipmentAndLineForIssure(ShipmentIssueDto data) throws Exception { @@ -411,8 +413,11 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { if (StringUtils.isBlank(data.getOrderNo())) { throw new RuntimeException("发货单号不能为空"); } - // 调用IFS发货单头接口,关键参数site+orderNo;失败会导致状态校验与基础信息缺失 - rqrq - List shipmentRows = ifsApiIssueAndReturnService.getShipment(data.getSite(), data.getOrderNo()); + String baseSite = stripSiteFuzzySuffix(data.getSite()); + String fuzzySite = baseSite + "%"; + + // 调用IFS发货单头接口:site使用模糊条件(大site+'%');失败会导致状态校验与基础信息缺失 - rqrq + List shipmentRows = ifsApiIssueAndReturnService.getShipment(fuzzySite, data.getOrderNo()); if (shipmentRows == null || shipmentRows.isEmpty()) { return new ArrayList<>(); } @@ -424,16 +429,41 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { throw new Exception("SHIPMENT:" + data.getOrderNo() + "状态不允许发货,请检查!"); } - // 调用IFS发货单行接口,关键参数site+orderNo;失败会导致发料行为空无法保存 - rqrq - List lineRows = ifsApiIssueAndReturnService.getShipmentLineVo(data.getSite(), data.getOrderNo()); - if (lineRows == null || lineRows.isEmpty()) { + // 头可能跨多个CONTRACT,行接口必须按CONTRACT逐个拉取,不能继续用大site - rqrq + LinkedHashSet contracts = new LinkedHashSet<>(); + Map headerByContract = new LinkedHashMap<>(); + for (ShipmentVo row : validRows) { + if (row == null || StringUtils.isBlank(row.getContract())) { + continue; + } + contracts.add(row.getContract()); + headerByContract.putIfAbsent(row.getContract(), row); + } + if (contracts.isEmpty()) { + contracts.add(baseSite); + } + + List lineRows = new ArrayList<>(); + for (String contract : contracts) { + // 调用IFS发货单行接口,关键参数CONTRACT+shipmentId;失败会导致该工厂下行项目缺失 - rqrq + List partLines = ifsApiIssueAndReturnService.getShipmentLineVo(contract, data.getOrderNo()); + if (partLines != null && !partLines.isEmpty()) { + lineRows.addAll(partLines); + } + } + if (lineRows.isEmpty()) { return new ArrayList<>(); } - ShipmentVo header = validRows.get(0); + + ShipmentVo defaultHeader = validRows.get(0); List joinedRows = new ArrayList<>(); for (ShipmentLineVo lineRow : lineRows) { ShipmentAndShipmentLineVo vo = new ShipmentAndShipmentLineVo(); BeanUtils.copyProperties(lineRow, vo); + String lineContract = StringUtils.isNotBlank(lineRow.getContract()) ? lineRow.getContract() : null; + ShipmentVo header = lineContract != null && headerByContract.containsKey(lineContract) + ? headerByContract.get(lineContract) + : defaultHeader; vo.setState(header.getState()); vo.setNextStepFlow(header.getNextStepFlow()); vo.setSourceRefType(header.getSourceRefType()); @@ -449,7 +479,11 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { vo.setCreatedDate(header.getCreatedDate()); vo.setPlannedShipDate(header.getPlannedShipDate()); vo.setPlannedDeliveryDate(header.getPlannedDeliveryDate()); - vo.setSite(data.getSite()); + // 回传大site给前端主单;工厂编码落在contract供subsite使用 - rqrq + vo.setSite(baseSite); + if (StringUtils.isBlank(vo.getContract()) && StringUtils.isNotBlank(lineContract)) { + vo.setContract(lineContract); + } vo.setPartNo(vo.getInventoryPartNo()); joinedRows.add(vo); } @@ -472,8 +506,11 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { throw new RuntimeException("itemNo不能为空,无法定位发料明细"); } - // 调用IFS发货单行接口拉取最新行项目,关键参数site+soorderNo;失败会影响明细完整性 - rqrq - List lineRows = ifsApiIssueAndReturnService.getShipmentLineVo(data.getSite(), data.getSoorderNo()); + // 行接口site优先用父行subsite(CONTRACT);缺失时再回退大site - rqrq + String lineSite = StringUtils.isNotBlank(data.getSubSite()) ? data.getSubSite() : stripSiteFuzzySuffix(data.getSite()); + List lineRows = ifsApiIssueAndReturnService.getShipmentLineVo(lineSite, data.getSoorderNo()); + // 与保存前mergeShipmentRows口径一致:同料号先合并,避免明细页比库内多出行 - rqrq + List mergedLineRows = mergeShipmentLineRows(lineRows); HaiAnSOIssueNotifyHeaderData query = new HaiAnSOIssueNotifyHeaderData(); query.setSite(data.getSite()); query.setNotifyNo(data.getNotifyNo()); @@ -485,36 +522,40 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { .filter(row -> sameItemNo(row.getItemNo(), data.getItemNo())) .collect(Collectors.toList()); Map savedRowMap = new LinkedHashMap<>(); + Map savedByPartMap = new LinkedHashMap<>(); for (HaiAnSOIssueNotifyOrderMaterialListData savedRow : savedRows) { savedRowMap.put(buildMaterialKey(savedRow.getBOMItemNo(), savedRow.getComponentPartNo(), savedRow.getSubSite()), savedRow); + // 料号兜底索引:兼容BOM_item_no与IFS sourceRef3/shipmentLineNo不一致的历史数据 - rqrq + if (StringUtils.isNotBlank(savedRow.getComponentPartNo()) && !savedByPartMap.containsKey(savedRow.getComponentPartNo())) { + savedByPartMap.put(savedRow.getComponentPartNo(), savedRow); + } } List result = new ArrayList<>(); - Set ifsKeys = new HashSet<>(); - if (lineRows != null) { - for (ShipmentLineVo lineRow : lineRows) { - HaiAnSOIssueNotifyOrderMaterialListData row = new HaiAnSOIssueNotifyOrderMaterialListData(); - row.setSite(data.getSite()); - row.setNotifyNo(data.getNotifyNo()); - row.setItemNo(data.getItemNo()); - row.setBOMItemNo(resolveShipmentBomItemNo(lineRow)); - row.setComponentPartNo(lineRow.getInventoryPartNo()); - row.setComponentPartDesc(lineRow.getSourcePartDescription()); - row.setQtyRequired(lineRow.getInventoryQty()); - row.setQtyOnHand(lineRow.getInventoryQty() == null ? null : lineRow.getInventoryQty().doubleValue()); - row.setIssueType("BOM物料"); - row.setOrderType("shipment"); - row.setSubSite(StringUtils.isNotBlank(lineRow.getContract()) ? lineRow.getContract() : data.getSubSite()); - String key = buildMaterialKey(row.getBOMItemNo(), row.getComponentPartNo(), row.getSubSite()); - ifsKeys.add(key); - fillSavedMaterialFields(row, savedRowMap.get(key)); - result.add(row); + Set matchedSavedRows = Collections.newSetFromMap(new IdentityHashMap<>()); + for (ShipmentLineVo lineRow : mergedLineRows) { + HaiAnSOIssueNotifyOrderMaterialListData row = new HaiAnSOIssueNotifyOrderMaterialListData(); + row.setSite(data.getSite()); + row.setNotifyNo(data.getNotifyNo()); + row.setItemNo(data.getItemNo()); + row.setBOMItemNo(resolveShipmentBomItemNo(lineRow)); + row.setComponentPartNo(lineRow.getInventoryPartNo()); + row.setComponentPartDesc(lineRow.getSourcePartDescription()); + row.setQtyRequired(lineRow.getInventoryQty()); + row.setQtyOnHand(lineRow.getInventoryQty() == null ? null : lineRow.getInventoryQty().doubleValue()); + row.setIssueType("BOM物料"); + row.setOrderType("shipment"); + row.setSubSite(StringUtils.isNotBlank(lineRow.getContract()) ? lineRow.getContract() : data.getSubSite()); + HaiAnSOIssueNotifyOrderMaterialListData saved = findSavedShipmentMaterial(savedRowMap, savedByPartMap, lineRow, row); + if (saved != null) { + matchedSavedRows.add(saved); + fillSavedMaterialFields(row, saved); } + result.add(row); } for (HaiAnSOIssueNotifyOrderMaterialListData savedRow : savedRows) { - String key = buildMaterialKey(savedRow.getBOMItemNo(), savedRow.getComponentPartNo(), savedRow.getSubSite()); - if (!ifsKeys.contains(key)) { + if (!matchedSavedRows.contains(savedRow)) { result.add(savedRow); } } @@ -527,7 +568,8 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { } Map mergedMap = new LinkedHashMap<>(); for (ShipmentAndShipmentLineVo row : rows) { - String key = defaultString(row.getShipmentId()) + "|" + defaultString(row.getSourcePartNo()); + // 合并键加入contract,避免跨工厂同料号被错误汇总 - rqrq + String key = defaultString(row.getShipmentId()) + "|" + defaultString(row.getContract()) + "|" + defaultString(row.getSourcePartNo()); if (!mergedMap.containsKey(key)) { mergedMap.put(key, copyShipmentRow(row)); continue; @@ -571,20 +613,94 @@ public class HaiAnNewIssureServiceImpl implements HaiAnNewIssureService { return left.compareTo(right) == 0; } + /** + * @author rqrq + * @Description 发料明细BOM行号与保存链路对齐:优先shipmentLineNo(前端lineItemNo来源),再回退sourceRef3 - rqrq + */ private String resolveShipmentBomItemNo(ShipmentLineVo lineRow) { if (lineRow == null) { return ""; } + if (lineRow.getShipmentLineNo() != null) { + return String.valueOf(lineRow.getShipmentLineNo()); + } if (StringUtils.isNotBlank(lineRow.getSourceRef3())) { return lineRow.getSourceRef3(); } - return lineRow.getShipmentLineNo() == null ? "" : String.valueOf(lineRow.getShipmentLineNo()); + return ""; + } + + /** + * @author rqrq + * @Description 按库存料号合并IFS发货行,保持与保存前mergeShipmentRows一致,避免同料多行撑开明细 - rqrq + */ + private List mergeShipmentLineRows(List rows) { + if (rows == null || rows.isEmpty()) { + return new ArrayList<>(); + } + Map mergedMap = new LinkedHashMap<>(); + for (ShipmentLineVo row : rows) { + // 合并键加入contract,避免跨工厂同料号被错误汇总 - rqrq + String key = defaultString(row.getShipmentId()) + "|" + defaultString(row.getContract()) + "|" + defaultString(row.getInventoryPartNo()); + if (!mergedMap.containsKey(key)) { + ShipmentLineVo copied = new ShipmentLineVo(); + BeanUtils.copyProperties(row, copied); + mergedMap.put(key, copied); + continue; + } + ShipmentLineVo merged = mergedMap.get(key); + merged.setInventoryQty(addBigDecimal(merged.getInventoryQty(), row.getInventoryQty())); + merged.setQtyAssigned(addBigDecimal(merged.getQtyAssigned(), row.getQtyAssigned())); + } + return new ArrayList<>(mergedMap.values()); + } + + /** + * @author rqrq + * @Description 查找已保存发料行:先按BOM+料号+subsite精确匹配,再试sourceRef3键,最后按料号兜底 - rqrq + */ + private HaiAnSOIssueNotifyOrderMaterialListData findSavedShipmentMaterial( + Map savedRowMap, + Map savedByPartMap, + ShipmentLineVo lineRow, + HaiAnSOIssueNotifyOrderMaterialListData ifsRow) { + String primaryKey = buildMaterialKey(ifsRow.getBOMItemNo(), ifsRow.getComponentPartNo(), ifsRow.getSubSite()); + HaiAnSOIssueNotifyOrderMaterialListData saved = savedRowMap.get(primaryKey); + if (saved != null) { + return saved; + } + if (lineRow != null && StringUtils.isNotBlank(lineRow.getSourceRef3())) { + String sourceRefKey = buildMaterialKey(lineRow.getSourceRef3(), ifsRow.getComponentPartNo(), ifsRow.getSubSite()); + saved = savedRowMap.get(sourceRefKey); + if (saved != null) { + return saved; + } + } + if (StringUtils.isNotBlank(ifsRow.getComponentPartNo())) { + return savedByPartMap.get(ifsRow.getComponentPartNo()); + } + return null; } private String buildMaterialKey(String bomItemNo, String componentPartNo, String subSite) { return defaultString(bomItemNo) + "|" + defaultString(componentPartNo) + "|" + defaultString(subSite); } + /** + * @author rqrq + * @Description 去掉site模糊后缀'%',保证回写主单site与IFS CONTRACT调用参数干净 - rqrq + */ + private String stripSiteFuzzySuffix(String site) { + if (StringUtils.isBlank(site)) { + return site; + } + String trimmed = site.trim(); + while (trimmed.endsWith("%")) { + trimmed = trimmed.substring(0, trimmed.length() - 1); + } + return trimmed; + } + private String defaultString(String value) { return value == null ? "" : value; }