From 4bca60653b3f14448c496618cc4cef5862cfe898 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 08 五月 2026 14:52:03 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro

---
 src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java      |   11 +-
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java |  243 ++++++++++++++++++++++++++++++++++++++----------
 src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java           |    4 
 src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java            |    5 
 4 files changed, 204 insertions(+), 59 deletions(-)

diff --git a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
index 367710b..01c4d7e 100644
--- a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
@@ -23,8 +23,9 @@
     SALE_SHIP_STOCK_OUT("13", "閿�鍞�-鍙戣揣鍑哄簱"),
     RETURN_HE_IN("14", "閿�鍞��璐�-鍚堟牸鍏ュ簱"),
     RETURN_UNSTOCK_IN("15", "閿�鍞��璐�-涓嶅悎鏍煎叆搴�"),
-    PICK_RETURN_IN("20", "閿�鍞��璐�-鍚堟牸鍏ュ簱"),
-    PURCHASE_RETURN_STOCK_OUT("21", "閲囪喘閫�璐�");
+    PICK_RETURN_IN("20", "棰嗘枡閫�鏂�-鍚堟牸鍏ュ簱"),
+    PURCHASE_RETURN_STOCK_OUT("21", "閲囪喘閫�璐�"),
+    FEED_RETURN_IN("22", "鐢熶骇閫�鏂�-鍚堟牸鍏ュ簱");
 
 
 
diff --git a/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
index 5e8fe53..4fe1f6e 100644
--- a/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
@@ -9,7 +9,9 @@
     PRODUCTION_REPORT_STOCK_OUT("3", "鐢熶骇鎶ュ伐-鍑哄簱"),
     SALE_STOCK_OUT("8", "閿�鍞�-鍑哄簱"),
     PURCHASE_RETURN_STOCK_OUT("9", "閲囪喘閫�璐�"),
-    SALE_SHIP_STOCK_OUT("13", "閿�鍞�-鍙戣揣鍑哄簱");
+    SALE_SHIP_STOCK_OUT("13", "閿�鍞�-鍙戣揣鍑哄簱"),
+    PICK_STOCK_OUT("14", "鐢熶骇棰嗘枡鍑哄簱"),
+    FEED_STOCK_OUT("15", "鐢熶骇琛ユ枡鍑哄簱");
 
     private final String code;
     private final String value;
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
index 478ecd5..598ae6b 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -3,7 +3,9 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.enums.ReviewStatusEnum;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.production.bean.dto.ProductionOrderPickDto;
@@ -18,8 +20,12 @@
 import com.ruoyi.production.service.ProductionOrderPickService;
 import com.ruoyi.stock.dto.StockInventoryDto;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
 import com.ruoyi.stock.pojo.StockInventory;
+import com.ruoyi.stock.pojo.StockOutRecord;
 import com.ruoyi.stock.service.StockInventoryService;
+import com.ruoyi.stock.service.StockInRecordService;
+import com.ruoyi.stock.service.StockOutRecordService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -39,12 +45,18 @@
 
     private static final byte PICK_TYPE_NORMAL = 1;
     private static final byte PICK_TYPE_FEEDING = 2;
+    private static final String PICK_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.PICK_STOCK_OUT.getCode();
+    private static final String FEED_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.FEED_STOCK_OUT.getCode();
+    private static final String PICK_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode();
+    private static final String FEED_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode();
 
     private final ProductionOrderMapper productionOrderMapper;
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper;
     private final StockInventoryMapper stockInventoryMapper;
     private final StockInventoryService stockInventoryService;
+    private final StockInRecordService stockInRecordService;
+    private final StockOutRecordService stockOutRecordService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -52,8 +64,9 @@
         // 棰嗘枡鏂板鎬绘祦绋嬶細
         // 1) 瑙f瀽鍓嶇琛屾暟鎹苟閫愯鍚堝苟鍙傛暟锛�
         // 2) 鏍¢獙鍙傛暟涓庢壒娆★紱
-        // 3) 鍏堟墸鍑忓簱瀛橈紝鍐嶈惤搴撻鏂欎富璁板綍锛�
-        // 4) 鍐欏叆棰嗘枡娴佹按锛岃褰曟暟閲忓彉鍖栬建杩广��
+        // 3) 鍏堜繚瀛橀鏂欎富璁板綍锛�
+        // 4) 鍐嶈蛋鈥滃嚭搴撶敵璇� + 瀹℃壒閫氳繃鈥濆畬鎴愬簱瀛樻墸鍑忥紱
+        // 5) 鍐欏叆棰嗘枡娴佹按锛岃褰曟暟閲忓彉鍖栬建杩广��
         List<ProductionOrderPickDto> pickItems = resolvePickItems(dto);
         // 閫愯澶勭悊棰嗘枡鏁版嵁锛岃鍙风敤浜庢嫾瑁呯簿纭殑鎶ラ敊淇℃伅銆�
         for (int i = 0; i < pickItems.size(); i++) {
@@ -66,7 +79,6 @@
             List<String> batchNoList = resolveBatchNoList(resolvedDto);
             String inventoryBatchNo = pickInventoryBatchNo(batchNoList);
             String storedBatchNo = formatBatchNoStorage(batchNoList);
-            subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo);
 
             // 淇濆瓨棰嗘枡涓昏褰曞揩鐓с��
             ProductionOrderPick orderPick = new ProductionOrderPick();
@@ -82,6 +94,9 @@
             orderPick.setReturned(false);
             // 鏂板涓昏褰曘��
             baseMapper.insert(orderPick);
+
+            // 鍏堟柊澧炲嚭搴撶敵璇凤紝鍐嶅鎵归�氳繃锛屽畬鎴愬簱瀛樻墸鍑忋��
+            subtractInventory(orderPick.getId(), resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE);
 
             // 璁板綍鏈棰嗘枡娴佹按锛坆efore=0锛宎fter=鏈棰嗘枡閲忥級銆�
             insertPickRecord(orderPick.getId(),
@@ -201,7 +216,12 @@
             }
             String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo());
             BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity());
-            addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity);
+            addInventory(existingPick.getId(), existingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE);
+            // 鍒犻櫎鍏宠仈棰嗘枡娴佹按锛岄伩鍏嶉仐鐣欐棤涓昏褰曘��
+            productionOrderPickRecordMapper.delete(
+                    Wrappers.<ProductionOrderPickRecord>lambdaQuery()
+                            .eq(ProductionOrderPickRecord::getPickId, existingPick.getId())
+            );
             int affected = baseMapper.deleteById(deleteId);
             if (affected <= 0) {
                 throw new ServiceException("鍒犻櫎棰嗘枡璁板綍澶辫触锛孖D=" + deleteId);
@@ -240,7 +260,12 @@
         for (ProductionOrderPick missingPick : missingPickList) {
             String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo());
             BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity());
-            addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity);
+            addInventory(missingPick.getId(), missingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE);
+            // 鍒犻櫎鍏宠仈棰嗘枡娴佹按锛岄伩鍏嶉仐鐣欐棤涓昏褰曘��
+            productionOrderPickRecordMapper.delete(
+                    Wrappers.<ProductionOrderPickRecord>lambdaQuery()
+                            .eq(ProductionOrderPickRecord::getPickId, missingPick.getId())
+            );
             int affected = baseMapper.deleteById(missingPick.getId());
             if (affected <= 0) {
                 throw new ServiceException("鍒犻櫎鏈洖浼犻鏂欒褰曞け璐ワ紝ID=" + missingPick.getId());
@@ -261,11 +286,11 @@
     }
 
     private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) {
-        // 鏇存柊鍦烘櫙涓嬫柊澧炰竴鏉¢鏂欙細鎵e簱瀛� -> 鏂板涓昏褰� -> 鍐欐祦姘淬��
+        // 鏇存柊鍦烘櫙涓嬫柊澧炰竴鏉¢鏂欙細
+        // 鏂板涓昏褰� -> 鍑哄簱鐢宠骞跺鎵� -> 鍐欐祦姘淬��
         List<String> batchNoList = resolveBatchNoList(dto);
         String inventoryBatchNo = pickInventoryBatchNo(batchNoList);
         String storedBatchNo = formatBatchNoStorage(batchNoList);
-        subtractInventory(dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo);
 
         ProductionOrderPick orderPick = new ProductionOrderPick();
         orderPick.setProductionOrderId(dto.getProductionOrderId());
@@ -279,6 +304,9 @@
         orderPick.setBom(dto.getBom());
         orderPick.setReturned(false);
         baseMapper.insert(orderPick);
+
+        // 鍏堟柊澧炲嚭搴撶敵璇凤紝鍐嶅鎵归�氳繃锛屽畬鎴愬簱瀛樻墸鍑忋��
+        subtractInventory(orderPick.getId(), dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE);
 
         insertPickRecord(orderPick.getId(),
                 dto.getProductionOrderId(),
@@ -337,7 +365,7 @@
                 : formatBatchNoStorage(batchNoList);
         BigDecimal feedingQuantity = dto.getFeedingQuantity();
 
-        subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo);
+        subtractInventory(oldPick.getId(), productModelId, inventoryBatchNo, feedingQuantity, rowNo, FEED_STOCK_OUT_RECORD_TYPE);
 
         // 璁$畻琛ユ枡鍓嶅悗鏁伴噺骞跺啓琛ユ枡娴佹按銆�
         BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId());
@@ -393,19 +421,36 @@
     }
 
     private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) {
-        // 閫�鏂欐洿鏂板彧鏀逛富棰嗘枡璁板綍涓殑閫�鏂欏瓧娈典笌瀹為檯閲忋��
+        // 閫�鏂欐洿鏂帮細
+        // 1) returnQty 鎸夆�滄湰娆¢��鏂欓噺鈥濆鐞嗭紱
+        // 2) 鏈閫�鏂欓噺鍥炶ˉ鍒扳�滅敓浜ч��鏂欏叆搴撯�濓紱
+        // 3) 绱姞涓昏褰曢��鏂欐�婚噺骞堕噸绠楀疄闄呴噺銆�
+        BigDecimal oldReturnQty = defaultDecimal(oldPick.getReturnQty());
+        BigDecimal currentReturnQty = defaultDecimal(dto.getReturnQty());
+        BigDecimal totalReturnQty = oldReturnQty.add(currentReturnQty);
+        if (currentReturnQty.compareTo(BigDecimal.ZERO) > 0) {
+            String returnBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo());
+            addInventory(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE);
+        }
+
+        BigDecimal actualQty = defaultDecimal(oldPick.getQuantity())
+                .add(defaultDecimal(oldPick.getFeedingQty()))
+                .subtract(totalReturnQty);
+        if (actualQty.compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("绗�" + rowNo + "琛岄��鏂欏け璐ワ細绱閫�鏂欐暟閲忎笉鑳藉ぇ浜庡彲鐢ㄦ暟閲�");
+        }
         ProductionOrderPick updatePick = new ProductionOrderPick();
         updatePick.setId(oldPick.getId());
-        updatePick.setReturnQty(dto.getReturnQty());
-        updatePick.setActualQty(dto.getActualQty());
-        updatePick.setReturned(true);
+        updatePick.setReturnQty(totalReturnQty);
+        updatePick.setActualQty(actualQty);
+        updatePick.setReturned(totalReturnQty.compareTo(BigDecimal.ZERO) > 0);
         int affected = baseMapper.updateById(updatePick);
         if (affected <= 0) {
             throw new ServiceException("绗�" + rowNo + "琛岄��鏂欏け璐ワ細鏇存柊棰嗘枡涓昏褰曞け璐�");
         }
         oldPick.setReturnQty(updatePick.getReturnQty());
         oldPick.setActualQty(updatePick.getActualQty());
-        oldPick.setReturned(true);
+        oldPick.setReturned(updatePick.getReturned());
     }
 
     private void updateExistingPick(ProductionOrderPickDto dto,
@@ -431,21 +476,30 @@
         String newStoredBatchNo = formatBatchNoStorage(newBatchNoList);
         BigDecimal newQuantity = dto.getPickQuantity();
 
-        // 鍒ゆ柇瑙勬牸+鎵规鏄惁鍙樺寲锛屽喅瀹氬簱瀛樺鐞嗙瓥鐣ャ��
+        // 鍒ゆ柇瑙勬牸+鎵规鎴栨暟閲忔槸鍚﹀彉鍖栵紝骞舵寜鍦烘櫙澶勭悊搴撳瓨锛�
+        // 1) 鍚岃鏍煎悓鎵规锛氭寜宸�煎鐞嗭紙澧為噺鎵e噺 / 鍑忛噺鍥為��锛夛紱
+        // 2) 瑙勬牸鎴栨壒娆″彉鍖栵細鍥為��鏃ч鏂欏悗鍐嶉噸鎻愭柊棰嗘枡銆�
         boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId)
                 && Objects.equals(oldBatchNo, newBatchNo);
+        boolean quantityChanged = oldQuantity.compareTo(newQuantity) != 0;
+        boolean needReissuePickRecord = !sameStockKey || quantityChanged;
         if (sameStockKey) {
-            // 瑙勬牸涓庢壒娆′笉鍙橈細鍙寜宸�煎鍑忓簱瀛樸��
-            BigDecimal delta = newQuantity.subtract(oldQuantity);
-            if (delta.compareTo(BigDecimal.ZERO) > 0) {
-                subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo);
-            } else if (delta.compareTo(BigDecimal.ZERO) < 0) {
-                addInventory(oldProductModelId, oldBatchNo, delta.abs());
+            BigDecimal deltaQuantity = newQuantity.subtract(oldQuantity);
+            if (deltaQuantity.compareTo(BigDecimal.ZERO) > 0) {
+                // 鏁伴噺澧炲姞锛屽彧鎵e噺鏂板閮ㄥ垎銆�
+                subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, deltaQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE);
+            } else if (deltaQuantity.compareTo(BigDecimal.ZERO) < 0) {
+                // 鏁伴噺鍑忓皯锛屽彧鍥為��宸�奸儴鍒嗐��
+                addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, deltaQuantity.abs(), PICK_RETURN_IN_RECORD_TYPE);
             }
         } else {
-            // 瑙勬牸鎴栨壒娆″彉鍖栵細鍏堝洖琛ユ棫搴撳瓨锛屽啀鎵e噺鏂板簱瀛樸��
-            addInventory(oldProductModelId, oldBatchNo, oldQuantity);
-            subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo);
+            // 瑙勬牸鎴栨壒娆″彉鍖栵細鍏堝叏閲忓洖閫�鏃ч鏂欙紝鍐嶅叏閲忔墸鍑忔柊棰嗘枡銆�
+            addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE);
+            subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, newQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE);
+        }
+        if (needReissuePickRecord) {
+            // 姝e父棰嗘枡娴佹按鎸夆�滄渶鏂伴鏂欓噺鈥濋噸寤猴紝閬垮厤淇濈暀鍘嗗彶鏃у�笺��
+            deleteNormalPickRecord(oldPick.getId());
         }
 
         oldPick.setProductModelId(newProductModelId);
@@ -454,6 +508,9 @@
         oldPick.setRemark(dto.getRemark());
         oldPick.setOperationName(dto.getOperationName());
         oldPick.setTechnologyOperationId(dto.getTechnologyOperationId());
+        // 鏅�氭洿鏂颁篃瑕佸悓姝ラ噸绠楀疄闄呯敤閲忥紝閬垮厤娌跨敤鏃у�笺��
+        // 瑙勫垯锛氬疄闄呯敤閲� = 棰嗘枡鏁伴噺 + 琛ユ枡鏁伴噺 - 閫�鏂欐暟閲忋��
+        oldPick.setActualQty(calculateActualQty(oldPick, oldPick.getFeedingQty()));
         if (dto.getDemandedQuantity() != null) {
             oldPick.setDemandedQuantity(dto.getDemandedQuantity());
         }
@@ -465,16 +522,15 @@
             throw new ServiceException("绗�" + rowNo + "琛屾洿鏂板け璐ワ細鏇存柊棰嗘枡璁板綍澶辫触");
         }
 
-        // 鍐欏叆鏇存柊娴佹按锛屼繚鐣欐湰娆℃暟閲忓彉鍖栬建杩广��
-        BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity;
-        if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) {
+        // 濡傛灉鍙戠敓棰嗘枡閲嶆彁锛岃ˉ鍐欎竴鏉℃柊鐨勬甯搁鏂欐祦姘淬��
+        if (needReissuePickRecord) {
             insertPickRecord(oldPick.getId(),
                     dto.getProductionOrderId(),
                     dto.getProductionOperationTaskId(),
                     newProductModelId,
                     newBatchNo,
-                    recordQuantity,
-                    oldQuantity,
+                    newQuantity,
+                    BigDecimal.ZERO,
                     newQuantity,
                     dto.getPickType(),
                     dto.getRemark(),
@@ -509,11 +565,28 @@
         productionOrderPickRecordMapper.insert(pickRecord);
     }
 
-    private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) {
+    private void deleteNormalPickRecord(Long pickId) {
+        // 鍒犻櫎璇ラ鏂欏崟鍘嗗彶涓婄殑鈥滄甯搁鏂欌�濇祦姘达紝淇濈暀琛ユ枡/閫�鏂欐祦姘淬��
+        if (pickId == null) {
+            return;
+        }
+        productionOrderPickRecordMapper.delete(
+                Wrappers.<ProductionOrderPickRecord>lambdaQuery()
+                        .eq(ProductionOrderPickRecord::getPickId, pickId)
+                        .eq(ProductionOrderPickRecord::getPickType, PICK_TYPE_NORMAL)
+        );
+    }
+
+    private void subtractInventory(Long recordId,
+                                   Long productModelId,
+                                   String batchNo,
+                                   BigDecimal quantity,
+                                   int rowNo,
+                                   String stockOutRecordType) {
         // 鎵e噺搴撳瓨鎬绘祦绋嬶細
         // 1) 瑙f瀽鎵规鍒楄〃锛�
         // 2) 璁$畻姣忎釜鎵规鍙敤閲忎笌鎬诲彲鐢ㄩ噺锛�
-        // 3) 鎸夋壒娆¢『搴忛�愮瑪鎵e噺锛岀洿鍒版墸瀹岀洰鏍囨暟閲忥紱
+        // 3) 鎸夋壒娆¢『搴忛�愮瑪鈥滄柊澧炲嚭搴撹褰曞苟瀹℃壒閫氳繃鈥濓紝鐩村埌鎵e畬鐩爣鏁伴噺锛�
         // 4) 浠讳竴姝ュけ璐ュ嵆鎶涢敊骞跺洖婊氫簨鍔°��
         BigDecimal deductQuantity = defaultDecimal(quantity);
         // 棰嗘枡鏁伴噺灏忎簬绛変簬0鏃讹紝涓嶉渶瑕佹墽琛屽簱瀛樻墸鍑忋��
@@ -562,14 +635,7 @@
                 continue;
             }
             BigDecimal currentDeductQuantity = remainingQuantity.min(availableQuantity);
-            StockInventoryDto stockInventoryDto = new StockInventoryDto();
-            stockInventoryDto.setProductModelId(productModelId);
-            stockInventoryDto.setBatchNo(entry.getKey());
-            stockInventoryDto.setQualitity(currentDeductQuantity);
-            int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
-            if (affected <= 0) {
-                throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細搴撳瓨鏇存柊澶辫触");
-            }
+            createAndApproveStockOutRecord(recordId, productModelId, entry.getKey(), currentDeductQuantity, rowNo, stockOutRecordType);
             remainingQuantity = remainingQuantity.subtract(currentDeductQuantity);
         }
 
@@ -578,19 +644,97 @@
         }
     }
 
-    private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) {
-        // 鍥炶ˉ搴撳瓨锛堢敤浜庡垹闄ら鏂欍�佹敼灏忛鏂欍�佸垏鎹㈡壒娆$瓑鍦烘櫙锛夈��
+    private void createAndApproveStockOutRecord(Long recordId,
+                                                Long productModelId,
+                                                String batchNo,
+                                                BigDecimal quantity,
+                                                int rowNo,
+                                                String stockOutRecordType) {
+        // 搴撳瓨鎵e噺鏀逛负涓ゆ锛�
+        // 1) 鍏堣皟鐢� addStockOutRecordOnly 鏂板寰呭鎵瑰嚭搴撹褰曪紱
+        // 2) 鍐嶈皟鐢ㄥ嚭搴撳鎵癸紝瀹℃壒鐘舵�佸浐瀹氫紶 1锛堥�氳繃锛夈��
+        try {
+            StockInventoryDto stockInventoryDto = new StockInventoryDto();
+            stockInventoryDto.setRecordId(recordId == null ? 0L : recordId);
+            stockInventoryDto.setRecordType(stockOutRecordType);
+            stockInventoryDto.setProductModelId(productModelId);
+            stockInventoryDto.setBatchNo(batchNo);
+            stockInventoryDto.setQualitity(quantity);
+            stockInventoryService.addStockOutRecordOnly(stockInventoryDto);
+
+            LambdaQueryWrapper<StockOutRecord> recordWrapper = Wrappers.<StockOutRecord>lambdaQuery()
+                    .eq(StockOutRecord::getRecordId, stockInventoryDto.getRecordId())
+                    .eq(StockOutRecord::getRecordType, stockOutRecordType)
+                    .eq(StockOutRecord::getProductModelId, productModelId)
+                    .eq(StockOutRecord::getType, "0")
+                    .orderByDesc(StockOutRecord::getId)
+                    .last("limit 1");
+            if (StringUtils.isEmpty(batchNo)) {
+                recordWrapper.isNull(StockOutRecord::getBatchNo);
+            } else {
+                recordWrapper.eq(StockOutRecord::getBatchNo, batchNo);
+            }
+            StockOutRecord stockOutRecord = stockOutRecordService.getOne(recordWrapper, false);
+            if (stockOutRecord == null || stockOutRecord.getId() == null) {
+                throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細鏈壘鍒板搴斿嚭搴撶敵璇疯褰�");
+            }
+            stockOutRecordService.batchApprove(
+                    Collections.singletonList(stockOutRecord.getId()),
+                    ReviewStatusEnum.APPROVED.getCode()
+            );
+        } catch (ServiceException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細" + ex.getMessage());
+        }
+    }
+
+    private void addInventory(Long recordId,
+                              Long productModelId,
+                              String batchNo,
+                              BigDecimal quantity,
+                              String stockInRecordType) {
+        // 鍥炶ˉ搴撳瓨鏀逛负涓ゆ锛�
+        // 1) 鍏堟柊澧炲叆搴撶敵璇凤紱
+        // 2) 鍐嶅鎵归�氳繃锛岀‘淇濆簱瀛樼珛鍒诲洖琛ョ敓鏁堛��
         BigDecimal addQuantity = defaultDecimal(quantity);
         if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) {
             return;
         }
-        StockInventoryDto stockInventoryDto = new StockInventoryDto();
-        stockInventoryDto.setProductModelId(productModelId);
-        stockInventoryDto.setBatchNo(batchNo);
-        stockInventoryDto.setQualitity(addQuantity);
-        stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode()));
-        stockInventoryDto.setRecordId(0L);
-        stockInventoryService.addStockInRecordOnly(stockInventoryDto);
+        try {
+            StockInventoryDto stockInventoryDto = new StockInventoryDto();
+            stockInventoryDto.setProductModelId(productModelId);
+            stockInventoryDto.setBatchNo(batchNo);
+            stockInventoryDto.setQualitity(addQuantity);
+            stockInventoryDto.setRecordType(stockInRecordType);
+            stockInventoryDto.setRecordId(recordId == null ? 0L : recordId);
+            stockInventoryService.addStockInRecordOnly(stockInventoryDto);
+
+            LambdaQueryWrapper<StockInRecord> recordWrapper = Wrappers.<StockInRecord>lambdaQuery()
+                    .eq(StockInRecord::getRecordId, stockInventoryDto.getRecordId())
+                    .eq(StockInRecord::getRecordType, stockInventoryDto.getRecordType())
+                    .eq(StockInRecord::getProductModelId, productModelId)
+                    .eq(StockInRecord::getType, "0")
+                    .orderByDesc(StockInRecord::getId)
+                    .last("limit 1");
+            if (StringUtils.isEmpty(batchNo)) {
+                recordWrapper.isNull(StockInRecord::getBatchNo);
+            } else {
+                recordWrapper.eq(StockInRecord::getBatchNo, batchNo);
+            }
+            StockInRecord stockInRecord = stockInRecordService.getOne(recordWrapper, false);
+            if (stockInRecord == null || stockInRecord.getId() == null) {
+                throw new ServiceException("鍥炶ˉ搴撳瓨澶辫触锛氭湭鎵惧埌瀵瑰簲鍏ュ簱鐢宠璁板綍");
+            }
+            stockInRecordService.batchApprove(
+                    Collections.singletonList(stockInRecord.getId()),
+                    ReviewStatusEnum.APPROVED.getCode()
+            );
+        } catch (ServiceException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ServiceException("鍥炶ˉ搴撳瓨澶辫触锛�" + ex.getMessage());
+        }
     }
 
     private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) {
@@ -769,7 +913,7 @@
     }
 
     private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) {
-        // 鏍¢獙閫�鏂欏弬鏁帮紙璁㈠崟銆侀鏂橧D銆侀��鏂欓噺銆佸疄闄呴噺锛夈��
+        // 鏍¢獙閫�鏂欏弬鏁帮紙璁㈠崟銆侀鏂橧D銆侀��鏂欓噺锛夈��
         if (dto.getProductionOrderId() == null) {
             throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細鐢熶骇璁㈠崟ID涓嶈兘涓虹┖");
         }
@@ -778,9 +922,6 @@
         }
         if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) {
             throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細閫�鏂欐暟閲忎笉鑳藉皬浜�0");
-        }
-        if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) {
-            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細瀹為檯鏁伴噺涓嶈兘灏忎簬0");
         }
     }
 
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
index 8c885cd..b00266b 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -101,12 +101,13 @@
             throw new BaseException("鍚堝苟澶辫触锛屾墍閫夌敓浜ц鍒掔殑浜у搧鍨嬪彿涓嶄竴鑷�");
         }
 
-        // 宸蹭笅鍙戞垨閮ㄥ垎涓嬪彂鐨勮鍒掍笉鍏佽鍐嶆鍚堝苟
-        boolean hasIssuedPlan = planLists.stream()
+        // 浠呪�滃凡涓嬪彂鈥濊鍒掍笉鍏佽鍐嶆鍙備笌鍚堝苟涓嬪彂锛�
+        // 鈥滃緟涓嬪彂/閮ㄥ垎涓嬪彂鈥濆厑璁哥户缁笅鍙戝墿浣欐暟閲忋��
+        boolean hasFullyIssuedPlan = planLists.stream()
                 .anyMatch(item -> item.getStatus() != null
-                        && (item.getStatus() == PLAN_STATUS_PARTIAL || item.getStatus() == PLAN_STATUS_ISSUED));
-        if (hasIssuedPlan) {
-            throw new BaseException("鍚堝苟澶辫触锛屾墍閫夌敓浜ц鍒掑瓨鍦ㄥ凡涓嬪彂鎴栭儴鍒嗕笅鍙戠殑鏁版嵁");
+                        && item.getStatus() == PLAN_STATUS_ISSUED);
+        if (hasFullyIssuedPlan) {
+            throw new BaseException("鍚堝苟澶辫触锛屾墍閫夌敓浜ц鍒掑瓨鍦ㄥ凡涓嬪彂鐨勬暟鎹�");
         }
 
         // 璁$畻鏈鍙笅鍙戠殑鍓╀綑闇�姹傛�婚噺

--
Gitblit v1.9.3