From a829dd34332bbc10c6b42ef86635ed48e34c6207 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 03 十一月 2026 10:21:50 +0800
Subject: [PATCH] fix: 原料领料批号按可用库存过滤并实现先进先出

---
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java |   97 +++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
index 1905d4a..451f26e 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -34,7 +34,9 @@
 import com.ruoyi.production.service.ProductionOrderService;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.stock.mapper.StockInRecordMapper;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
 import com.ruoyi.stock.pojo.StockInventory;
 import com.ruoyi.technology.mapper.*;
 import com.ruoyi.technology.pojo.*;
@@ -70,6 +72,7 @@
     private final QualityInspectFileMapper qualityInspectFileMapper;
     private final ProductionPlanMapper productionPlanMapper;
     private final StockInventoryMapper stockInventoryMapper;
+    private final StockInRecordMapper stockInRecordMapper;
     private final StorageAttachmentMapper storageAttachmentMapper;
     private final StorageBlobMapper storageBlobMapper;
     private final SalesLedgerMapper salesLedgerMapper;
@@ -439,9 +442,54 @@
         productionOrderBomMapper.insert(orderBom);
 
         Map<Long, Long> idMap = new HashMap<>();
-        BigDecimal lastProcessDemandedQuantity = orderQuantity;
+        // 鏋勫缓 sourceId 鈫� sourceNode 鏄犲皠 鍜� parentId 鈫� children 鏄犲皠
+        Map<Long, TechnologyBomStructure> sourceById = new HashMap<>();
+        Map<Long, List<TechnologyBomStructure>> childrenMap = new LinkedHashMap<>();
         for (TechnologyBomStructure source : structureList) {
-            // 瀛愯妭鐐� parentId 闇�瑕佹槧灏勬垚鏂板揩鐓ц妭鐐� id锛屾墠鑳戒繚鐣欏師濮� BOM 灞傜骇銆�
+            if (source == null || source.getId() == null) {
+                continue;
+            }
+            sourceById.put(source.getId(), source);
+            Long parentId = source.getParentId();
+            childrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(source);
+        }
+
+        // BFS 浠庢牴鑺傜偣寮�濮嬶紝鐖惰妭鐐瑰厛浜庡瓙鑺傜偣澶勭悊
+        Deque<TechnologyBomStructure> queue = new ArrayDeque<>();
+        List<TechnologyBomStructure> rootNodes = childrenMap.get(null);
+        if (rootNodes != null) {
+            queue.addAll(rootNodes);
+        }
+        // parentId=0 鐨勪篃瑙嗕负鏍硅妭鐐�
+        List<TechnologyBomStructure> zeroParentNodes = childrenMap.get(0L);
+        if (zeroParentNodes != null) {
+            for (TechnologyBomStructure node : zeroParentNodes) {
+                if (!queue.contains(node)) {
+                    queue.add(node);
+                }
+            }
+        }
+
+        while (!queue.isEmpty()) {
+            TechnologyBomStructure source = queue.poll();
+            if (source == null || source.getId() == null) {
+                continue;
+            }
+            // 璁$畻闇�姹傞噺 = 鐖惰妭鐐归渶姹傞噺 脳 鑷韩 unitQuantity
+            BigDecimal parentDemandedQuantity = orderQuantity;
+            Long sourceParentId = source.getParentId();
+            if (sourceParentId != null && sourceParentId != 0L) {
+                Long parentTargetId = idMap.get(sourceParentId);
+                if (parentTargetId != null) {
+                    // 鐖惰妭鐐瑰凡缁忔彃鍏ワ紝鐢ㄧ埗鑺傜偣鐨� targetId 浠庡凡鎻掑叆鍒楄〃鎵�
+                    // 杩欓噷鎴戜滑鐩存帴鐢ㄥ瓨涓嬫潵鐨勭埗鑺傜偣闇�姹傞噺
+                    TechnologyBomStructure parentSource = sourceById.get(sourceParentId);
+                    if (parentSource != null && parentSource.getDemandedQuantity() != null) {
+                        parentDemandedQuantity = parentSource.getDemandedQuantity();
+                    }
+                }
+            }
+
             ProductionBomStructure target = new ProductionBomStructure();
             target.setProductionOrderId(productionOrder.getId());
             target.setProductionOrderBomId(orderBom.getId());
@@ -449,11 +497,20 @@
             target.setProductModelId(source.getProductModelId());
             target.setTechnologyOperationId(source.getOperationId());
             target.setUnitQuantity(source.getUnitQuantity());
-            target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity()));
+            target.setDemandedQuantity(parentDemandedQuantity.multiply(defaultDecimal(source.getUnitQuantity())));
             target.setUnit(source.getUnit());
             productionBomStructureMapper.insert(target);
             idMap.put(source.getId(), target.getId());
-            lastProcessDemandedQuantity = target.getDemandedQuantity();
+            // 灏嗗綋鍓嶈妭鐐圭殑 demandedQuantity 鍥炲啓鍒� source 瀵硅薄涓婏紝渚涘瓙鑺傜偣璁$畻浣跨敤
+            source.setDemandedQuantity(target.getDemandedQuantity());
+
+            // 灏嗗瓙鑺傜偣鍔犲叆闃熷垪
+            List<TechnologyBomStructure> children = childrenMap.get(source.getId());
+            if (children != null) {
+                for (TechnologyBomStructure child : children) {
+                    queue.add(child);
+                }
+            }
         }
         return orderBom;
     }
@@ -611,9 +668,6 @@
                 .collect(Collectors.toSet());
         if (productModelIds.size() > 1) {
             throw new ServiceException("鎵�閫夌敓浜ц鍒掑繀椤诲睘浜庡悓涓�浜у搧瑙勬牸");
-        }
-        if (productionPlans.stream().anyMatch(item -> item.getStatus() != null && item.getStatus() == 2)) {
-            throw new ServiceException("鎵�閫夌敓浜ц鍒掑凡涓嬪彂");
         }
         ProductionPlan firstPlan = mainPlan;
         if (productionOrder.getProductModelId() == null) {
@@ -1093,12 +1147,41 @@
             List<StockInventory> stockList = stockInventoryMapper.selectList(
                     Wrappers.<StockInventory>lambdaQuery()
                             .in(StockInventory::getProductModelId, productModelIds));
+            // 鍚勬壒娆¢娆″叆搴撴椂闂达紝鐢ㄤ簬鍏堣繘鍏堝嚭鎺掑簭锛堝巻鍙叉暟鎹鍏ユ椂搴撳瓨琛岀殑 create_time 鍙兘鐩稿悓锛�
+            Map<Long, Map<String, LocalDateTime>> firstInTimeMap = new HashMap<>();
+            List<StockInRecord> stockInRecords = stockInRecordMapper.selectList(
+                    Wrappers.<StockInRecord>lambdaQuery()
+                            .in(StockInRecord::getProductModelId, productModelIds)
+                            .isNotNull(StockInRecord::getBatchNo));
+            for (StockInRecord stockInRecord : stockInRecords) {
+                if (stockInRecord == null || stockInRecord.getProductModelId() == null
+                        || stockInRecord.getBatchNo() == null || stockInRecord.getCreateTime() == null) {
+                    continue;
+                }
+                firstInTimeMap.computeIfAbsent(stockInRecord.getProductModelId(), key -> new HashMap<>())
+                        .merge(stockInRecord.getBatchNo(), stockInRecord.getCreateTime(),
+                                (a, b) -> a.isBefore(b) ? a : b);
+            }
+            // 搴撳瓨琛屾寜鎵�灞炴壒娆$殑棣栨鍏ュ簱鏃堕棿鍗囧簭锛屼繚璇侀鏂欎笅鎷夋鏄厛杩涘厛鍑洪『搴�
+            stockList.sort(Comparator
+                    .comparing((StockInventory stockItem) -> {
+                        Map<String, LocalDateTime> modelBatchTimeMap = firstInTimeMap.get(stockItem.getProductModelId());
+                        return modelBatchTimeMap == null ? null : modelBatchTimeMap.get(stockItem.getBatchNo());
+                    }, Comparator.nullsLast(Comparator.naturalOrder()))
+                    .thenComparing(StockInventory::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder()))
+                    .thenComparing(stockItem -> stockItem.getId() == null ? Long.MAX_VALUE : stockItem.getId()));
             for (StockInventory stockItem : stockList) {
                 if (stockItem == null || stockItem.getProductModelId() == null) {
                     continue;
                 }
                 Long productModelId = stockItem.getProductModelId();
                 stockQuantityMap.merge(productModelId, defaultDecimal(stockItem.getQualitity()), BigDecimal::add);
+                // 宸叉棤鍙敤搴撳瓨锛堟暟閲忔墸瀹屾垨琚攣瀹氾級鐨勬壒娆′笉鍐嶅甫鍒伴鏂欎笅鎷夋
+                BigDecimal availableQuantity = defaultDecimal(stockItem.getQualitity())
+                        .subtract(defaultDecimal(stockItem.getLockedQuantity()));
+                if (availableQuantity.compareTo(BigDecimal.ZERO) <= 0) {
+                    continue;
+                }
                 String batchNo = stockItem.getBatchNo();
                 if (batchNo != null && !batchNo.trim().isEmpty()) {
                     stockBatchNoMap.computeIfAbsent(productModelId, key -> new LinkedHashSet<>()).add(batchNo);

--
Gitblit v1.9.3