diff --git a/src/main/java/com/gaotao/modules/inboundNotification/service/impl/InboundNotificationServiceImpl.java b/src/main/java/com/gaotao/modules/inboundNotification/service/impl/InboundNotificationServiceImpl.java index e9ce7db..c34a3dc 100644 --- a/src/main/java/com/gaotao/modules/inboundNotification/service/impl/InboundNotificationServiceImpl.java +++ b/src/main/java/com/gaotao/modules/inboundNotification/service/impl/InboundNotificationServiceImpl.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.util.*; @@ -613,6 +614,7 @@ public class InboundNotificationServiceImpl implements InboundNotificationServic @Override @Transactional public void saveInboundDetail(InboundNotificationDetailVo data) { + validateInboundDetailNotDuplicate(data); List inbounds = new ArrayList<>(); // 根据orderNo获取order_item_no 如果为空则为1 Integer maxLineNo = detailMapper.getMaxLineNo(data); @@ -633,6 +635,100 @@ public class InboundNotificationServiceImpl implements InboundNotificationServic } } + /** + * 保存入库明细前防重复:本次提交内部、以及库中已有明细都不能出现相同卷号。 + * 生产入库按卷号校验;其他入库无卷号时按物料编码校验。 + */ + private void validateInboundDetailNotDuplicate(InboundNotificationDetailVo data) { + if (data == null || !StringUtils.hasText(data.getOrderNo())) { + throw new RuntimeException("入库单号不能为空"); + } + List partList = data.getPartList(); + if (partList == null || partList.isEmpty()) { + throw new RuntimeException("请选择物料!"); + } + + LinkedHashSet requestRollNos = new LinkedHashSet<>(); + List duplicateRollNos = new ArrayList<>(); + LinkedHashSet requestPartNos = new LinkedHashSet<>(); + List duplicatePartNos = new ArrayList<>(); + for (PartInformationVo part : partList) { + if (part == null) { + continue; + } + String rollNo = part.getRollNo() == null ? "" : part.getRollNo().trim(); + if (StringUtils.hasText(rollNo)) { + if (!requestRollNos.add(rollNo)) { + if (!duplicateRollNos.contains(rollNo)) { + duplicateRollNos.add(rollNo); + } + } + continue; + } + String partNo = part.getPartNo() == null ? "" : part.getPartNo().trim(); + if (StringUtils.hasText(partNo) && !requestPartNos.add(partNo) && !duplicatePartNos.contains(partNo)) { + duplicatePartNos.add(partNo); + } + } + if (!duplicateRollNos.isEmpty()) { + throw new RuntimeException("已选物料存在重复卷号:" + String.join("、", duplicateRollNos)); + } + if (!duplicatePartNos.isEmpty()) { + throw new RuntimeException("已选物料存在重复物料编码:" + String.join("、", duplicatePartNos)); + } + + PartInformationVo firstPart = partList.get(0); + String site = StringUtils.hasText(data.getSite()) ? data.getSite() : (firstPart == null ? null : firstPart.getSite()); + String buNo = StringUtils.hasText(data.getBuNo()) ? data.getBuNo() : (firstPart == null ? null : firstPart.getBuNo()); + if (!StringUtils.hasText(site) || !StringUtils.hasText(buNo)) { + throw new RuntimeException("工厂或事业部不能为空"); + } + data.setSite(site); + data.setBuNo(buNo); + + InboundNotificationDetailVo query = new InboundNotificationDetailVo(); + query.setSite(site); + query.setBuNo(buNo); + query.setOrderNo(data.getOrderNo()); + List existingList = detailMapper.getInboundDetail(query); + if (existingList == null || existingList.isEmpty()) { + return; + } + + Set existingRollNos = new HashSet<>(); + Set existingPartNos = new HashSet<>(); + for (InboundNotificationDetailVo row : existingList) { + if (row == null) { + continue; + } + if (StringUtils.hasText(row.getRollNo())) { + existingRollNos.add(row.getRollNo().trim()); + } else if (StringUtils.hasText(row.getPartNo())) { + existingPartNos.add(row.getPartNo().trim()); + } + } + + List conflictRollNos = new ArrayList<>(); + for (String rollNo : requestRollNos) { + if (existingRollNos.contains(rollNo)) { + conflictRollNos.add(rollNo); + } + } + if (!conflictRollNos.isEmpty()) { + throw new RuntimeException("以下卷号已存在于入库明细中,请勿重复添加:" + String.join("、", conflictRollNos)); + } + + List conflictPartNos = new ArrayList<>(); + for (String partNo : requestPartNos) { + if (existingPartNos.contains(partNo)) { + conflictPartNos.add(partNo); + } + } + if (!conflictPartNos.isEmpty()) { + throw new RuntimeException("以下物料已存在于入库明细中,请勿重复添加:" + String.join("、", conflictPartNos)); + } + } + private static InboundNotificationDetailVo getInboundNotificationDetailVo(InboundNotificationDetailVo data, PartInformationVo part, Integer lineNo) { InboundNotificationDetailVo detailVo = new InboundNotificationDetailVo(); detailVo.setSite(part.getSite()); diff --git a/src/main/java/com/gaotao/modules/outboundNotification/service/impl/OutboundNotificationServiceImpl.java b/src/main/java/com/gaotao/modules/outboundNotification/service/impl/OutboundNotificationServiceImpl.java index 79a7b6f..3972341 100644 --- a/src/main/java/com/gaotao/modules/outboundNotification/service/impl/OutboundNotificationServiceImpl.java +++ b/src/main/java/com/gaotao/modules/outboundNotification/service/impl/OutboundNotificationServiceImpl.java @@ -17,10 +17,14 @@ import org.apache.shiro.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; @Service public class OutboundNotificationServiceImpl implements OutboundNotificationService { @@ -150,6 +154,7 @@ public class OutboundNotificationServiceImpl implements OutboundNotificationServ @Override @Transactional public void saveOutboundDetail(OutboundNotificationDetailVo data) { + validateOutboundDetailNotDuplicate(data); List inbounds = new ArrayList<>(); for (PartInformationVo part : data.getPartList()) { OutboundNotificationDetailVo detailVo = getInboundNotificationDetailVo(data, part); @@ -165,6 +170,115 @@ public class OutboundNotificationServiceImpl implements OutboundNotificationServ } } + /** + * 保存出库明细前防重复:按 site + 采购订单 + 行号校验。 + */ + private void validateOutboundDetailNotDuplicate(OutboundNotificationDetailVo data) { + if (data == null || !StringUtils.hasText(data.getOrderNo())) { + throw new RuntimeException("出库单号不能为空"); + } + List partList = data.getPartList(); + if (partList == null || partList.isEmpty()) { + throw new RuntimeException("请选择物料!"); + } + + PartInformationVo firstPart = partList.get(0); + String site = StringUtils.hasText(data.getSite()) ? data.getSite().trim() : (firstPart == null ? null : trimToEmpty(firstPart.getSite())); + String buNo = StringUtils.hasText(data.getBuNo()) ? data.getBuNo() : (firstPart == null ? null : firstPart.getBuNo()); + if (!StringUtils.hasText(site) || !StringUtils.hasText(buNo)) { + throw new RuntimeException("工厂或事业部不能为空"); + } + data.setSite(site); + data.setBuNo(buNo); + + LinkedHashSet requestKeys = new LinkedHashSet<>(); + List duplicateDisplays = new ArrayList<>(); + for (PartInformationVo part : partList) { + if (part == null) { + continue; + } + String key = buildOutboundDuplicateKey(site, part); + if (!StringUtils.hasText(key)) { + continue; + } + if (!requestKeys.add(key)) { + String display = buildOutboundDuplicateDisplay(part); + if (!duplicateDisplays.contains(display)) { + duplicateDisplays.add(display); + } + } + } + if (!duplicateDisplays.isEmpty()) { + throw new RuntimeException("已选物料存在重复采购订单+行号:" + String.join("、", duplicateDisplays)); + } + + OutboundNotificationDetailVo query = new OutboundNotificationDetailVo(); + query.setSite(site); + query.setBuNo(buNo); + query.setOrderNo(data.getOrderNo()); + List existingList = detailMapper.getOutboundDetail(query); + if (existingList == null || existingList.isEmpty()) { + return; + } + + Set existingKeys = new HashSet<>(); + for (OutboundNotificationDetailVo row : existingList) { + if (row == null) { + continue; + } + String key = buildOutboundDuplicateKey(site, row.getRelatedOrderNo(), row.getRelatedOrderLineNo(), row.getPartNo()); + if (StringUtils.hasText(key)) { + existingKeys.add(key); + } + } + List conflictDisplays = new ArrayList<>(); + for (PartInformationVo part : partList) { + if (part == null) { + continue; + } + String key = buildOutboundDuplicateKey(site, part); + if (StringUtils.hasText(key) && existingKeys.contains(key)) { + String display = buildOutboundDuplicateDisplay(part); + if (!conflictDisplays.contains(display)) { + conflictDisplays.add(display); + } + } + } + if (!conflictDisplays.isEmpty()) { + throw new RuntimeException("以下采购订单+行号已存在于出库明细中,请勿重复添加:" + String.join("、", conflictDisplays)); + } + } + + private String buildOutboundDuplicateKey(String site, PartInformationVo part) { + if (part == null) { + return ""; + } + String partSite = StringUtils.hasText(part.getSite()) ? part.getSite().trim() : site; + return buildOutboundDuplicateKey(partSite, part.getRelatedOrderNo(), part.getRelatedOrderLineNo(), part.getPartNo()); + } + + private String buildOutboundDuplicateKey(String site, String relatedOrderNo, String relatedOrderLineNo, String partNo) { + String orderNo = trimToEmpty(relatedOrderNo); + String lineNo = trimToEmpty(relatedOrderLineNo); + if (StringUtils.hasText(orderNo) && StringUtils.hasText(lineNo)) { + return trimToEmpty(site) + "|" + orderNo + "|" + lineNo; + } + return StringUtils.hasText(partNo) ? trimToEmpty(partNo) : ""; + } + + private String buildOutboundDuplicateDisplay(PartInformationVo part) { + String orderNo = trimToEmpty(part.getRelatedOrderNo()); + String lineNo = trimToEmpty(part.getRelatedOrderLineNo()); + if (StringUtils.hasText(orderNo) && StringUtils.hasText(lineNo)) { + return orderNo + " / " + lineNo; + } + return trimToEmpty(part.getPartNo()); + } + + private String trimToEmpty(String value) { + return value == null ? "" : value.trim(); + } + private static OutboundNotificationDetailVo getInboundNotificationDetailVo(OutboundNotificationDetailVo data, PartInformationVo part) { OutboundNotificationDetailVo detailVo = new OutboundNotificationDetailVo(); detailVo.setSite(part.getSite()); diff --git a/src/main/resources/mapper/outboundNotification/OutboundNotificationDetailMapper.xml b/src/main/resources/mapper/outboundNotification/OutboundNotificationDetailMapper.xml index 7b147cc..f5eb740 100644 --- a/src/main/resources/mapper/outboundNotification/OutboundNotificationDetailMapper.xml +++ b/src/main/resources/mapper/outboundNotification/OutboundNotificationDetailMapper.xml @@ -208,8 +208,13 @@ INNER JOIN part b WITH (NOLOCK) ON a.site = b.site AND a.part_no = b.partno LEFT JOIN inventory_stock c WITH (NOLOCK) ON a.site = c.site AND a.roll_no = c.roll_no AND (c.status IN ('在库', '冻结') OR A.hardtag_in_flag ='未入库') left join um as um on a.site = um.site and b.umid = um.umid + LEFT JOIN outbound_notification_detail d ON a.Site = d.site AND a.bu_no = d.bu_no + AND a.Order_No = d.related_order_no + AND CAST(a.item_no AS NVARCHAR(50)) = CAST(d.related_order_line_no AS NVARCHAR(50)) + AND d.order_no = #{query.orderNo} where a.site = #{query.site} and a.bu_no = #{query.buNo} - and a.Part_No not in + AND d.site IS NULL + AND CAST(ISNULL(a.Order_No, '') AS NVARCHAR(100)) + '||' + CAST(ISNULL(a.item_no, '') AS NVARCHAR(50)) not in #{item}