From afdf7cc772baee20d5afc94d4ec3e3d691c3ed12 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期五, 24 四月 2026 14:15:55 +0800
Subject: [PATCH] 库存管理按照原材料和成品来区分

---
 src/main/java/com/ruoyi/stock/service/StockInventoryService.java            |    2 
 src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java                    |   39 ++++
 src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java           |   24 +
 src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java              |    1 
 src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java |  105 ++++++++++-
 src/main/resources/mapper/stock/StockInventoryMapper.xml                    |  201 ++++++++++++++++++++-
 src/main/java/com/ruoyi/stock/controller/StockInventoryController.java      |    9 +
 src/main/java/com/ruoyi/stock/pojo/StockUninventory.java                    |    2 
 src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java   |  139 +++++++++++----
 9 files changed, 450 insertions(+), 72 deletions(-)

diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
index 27ad046..9617d46 100644
--- a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -13,6 +13,7 @@
 import com.ruoyi.stock.service.StockInventoryService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -46,6 +47,14 @@
         return R.ok(stockInventoryDtoIPage);
     }
 
+
+    @GetMapping("/pageListCombinedStockInventory")
+    @Operation(summary = "鍒嗛〉鏌ヨ鑱斿悎搴撳瓨鍒楄〃")
+    public R pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto) {
+        IPage<StockInventoryDto> stockInventoryDtoIPage = stockInventoryService.pageListCombinedStockInventory(page, stockInventoryDto);
+        return R.ok(stockInventoryDtoIPage);
+    }
+
     @PostMapping("/addstockInventory")
     @PreAuthorize("@ss.hasPermi('add:stockInventory')")
     @ApiOperation("鏂板搴撳瓨")
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
index ec8f8b7..27ea5f8 100644
--- a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
+++ b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -2,6 +2,7 @@
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.stock.pojo.StockInventory;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
@@ -43,4 +44,40 @@
 
     //浜у搧id
     private Long productId;
-}
+
+    @Schema(description = "椤堕儴鐖朵骇鍝乮d")
+    private Long topParentProductId;
+
+    @Schema(description = "搴撳瓨绫诲瀷锛歲ualified(鍚堟牸)銆乽nqualified(涓嶅悎鏍�)")
+    private String stockType;
+
+    @Schema(description = "鍚堟牸搴撳瓨鏁伴噺")
+    private BigDecimal qualifiedQuantity;
+
+    @Schema(description = "涓嶅悎鏍煎簱瀛樻暟閲�")
+    private BigDecimal unQualifiedQuantity;
+
+    @Schema(description = "鍚堟牸搴撳瓨鍐荤粨鏁伴噺")
+    private BigDecimal qualifiedLockedQuantity;
+
+    @Schema(description = "涓嶅悎鏍煎簱瀛樺喕缁撴暟閲�")
+    private BigDecimal unQualifiedLockedQuantity;
+
+    @Schema(description = "鍚堟牸搴撳瓨鏈喕缁撴暟閲�")
+    private BigDecimal qualifiedUnLockedQuantity;
+
+    @Schema(description = "涓嶅悎鏍煎簱瀛樻湭鍐荤粨鏁伴噺")
+    private BigDecimal unQualifiedUnLockedQuantity;
+
+    @Schema(description = "鍚堟牸搴撳瓨ID")
+    private Long qualifiedId;
+
+    @Schema(description = "涓嶅悎鏍煎簱瀛業D")
+    private Long unQualifiedId;
+
+    @Schema(description = "鍚堟牸搴撳瓨鎵瑰彿")
+    private String qualifiedBatchNo;
+
+    @Schema(description = "涓嶅悎鏍煎簱瀛樻壒鍙�")
+    private String unQualifiedBatchNo;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
index b8b2bc4..93249c1 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
@@ -7,9 +7,6 @@
 
 @Data
 public class StockInventoryExportData {
-
-
-
     @Excel(name = "浜у搧鍚嶇О")
     private String productName;
 
@@ -22,14 +19,26 @@
     @Excel(name = "鏂欏彿")
     private String materialCode;
 
-    @Excel(name = "搴撳瓨鏁伴噺")
-    private BigDecimal qualitity;
+    @Excel(name = "鍚堟牸搴撳瓨鎵瑰彿")
+    private String qualifiedBatchNo;
+
+    @Excel(name = "涓嶅悎鏍煎簱瀛樻壒鍙�")
+    private String unQualifiedBatchNo;
+
+    @Excel(name = "鍚堟牸搴撳瓨鏁伴噺")
+    private BigDecimal qualifiedQuantity;
+
+    @Excel(name = "涓嶅悎鏍煎簱瀛樻暟閲�")
+    private BigDecimal unQualifiedQuantity;
 
     @Excel(name = "棰勮鏁伴噺")
     private BigDecimal warnNum;
 
-    @Excel(name = "鍐荤粨鏁伴噺")
-    private BigDecimal lockedQuantity;
+    @Excel(name = "鍚堟牸鍐荤粨鏁伴噺")
+    private BigDecimal qualifiedLockedQuantity;
+
+    @Excel(name = "涓嶅悎鏍煎喕缁撴暟閲�")
+    private BigDecimal unQualifiedLockedQuantity;
 
     @Excel(name = "澶囨敞")
     private String remark;
@@ -39,4 +48,5 @@
 //    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 //    private LocalDateTime updateTime;
 
+
 }
diff --git a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
index 21fffd4..f5ed7d9 100644
--- a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -29,6 +29,7 @@
 
     IPage<StockInventoryDto> pagestockInventory(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
 
+    IPage<StockInventoryDto> pageListCombinedStockInventory(Page page, @Param("ew") StockInventoryDto stockInventoryDto);
 
     int updateAddStockInventory(@Param("ew") StockInventoryDto stockInventoryDto);
 
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java b/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
index 6ed47de..a57f0c4 100644
--- a/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
+++ b/src/main/java/com/ruoyi/stock/pojo/StockUninventory.java
@@ -59,4 +59,6 @@
     @ApiModelProperty("琚鍗曢攣瀹氭暟閲�")
     private BigDecimal lockedQuantity;
 
+    @ApiModelProperty("鎵瑰彿")
+    private String batchNo;
 }
diff --git a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
index f7b1b17..1b5b479 100644
--- a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
+++ b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -25,6 +25,8 @@
 
     IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto);
 
+    IPage<StockInventoryDto> pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto);
+
     Boolean addstockInventory(StockInventoryDto stockInventoryDto);
 
     Boolean subtractStockInventory(StockInventoryDto stockInventoryDto);
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
index 72fc3fa..98ad6ca 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ruoyi.stock.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -13,6 +14,7 @@
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
@@ -22,6 +24,7 @@
 import com.ruoyi.stock.dto.StockInRecordDto;
 import com.ruoyi.stock.dto.StockInventoryDto;
 import com.ruoyi.stock.dto.StockOutRecordDto;
+import com.ruoyi.stock.dto.StockUninventoryDto;
 import com.ruoyi.stock.execl.StockInventoryExportData;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
 import com.ruoyi.stock.pojo.StockInRecord;
@@ -29,6 +32,7 @@
 import com.ruoyi.stock.service.StockInRecordService;
 import com.ruoyi.stock.service.StockInventoryService;
 import com.ruoyi.stock.service.StockOutRecordService;
+import com.ruoyi.stock.service.StockUninventoryService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -39,9 +43,7 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -70,10 +72,17 @@
     private ProductModelMapper productModelMapper;
     @Autowired
     private ProductMapper productMapper;
+    @Autowired
+    private StockUninventoryService stockUninventoryService;
 
     @Override
     public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
         return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
+    }
+
+    @Override
+    public IPage<StockInventoryDto> pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto) {
+        return stockInventoryMapper.pageListCombinedStockInventory(page, stockInventoryDto);
     }
 
     //鍏ュ簱璋冪敤-娣诲姞鍏ュ簱璁板綍
@@ -106,7 +115,8 @@
             String seqStr = String.format("%03d", newSeq);
 
             // 缁勮batchNo
-            batchNo = stockInventoryDto.getMaterialCode() + stockInventoryDto.getProductModelName() + "P" + monthFlag + seqStr;
+            ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId());
+            batchNo = stockInventoryDto.getMaterialCode() + productModel.getModel() + "P" + monthFlag + seqStr;
             stockInRecordDto.setBatchNo(batchNo);
         } else {
             stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
@@ -171,11 +181,17 @@
      */
     public void updateOrCreateStockInventory(StockInRecord stockInRecord) {
         // 鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪
-        StockInventory oldStockInventory = stockInventoryMapper.selectOne(
-                new QueryWrapper<StockInventory>().lambda()
-                        .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId())
-                        .eq(StockInventory::getBatchNo, stockInRecord.getBatchNo())
-        );
+        LambdaQueryWrapper<StockInventory> queryWrapper = new QueryWrapper<StockInventory>().lambda()
+                .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId());
+        
+        // 鏍规嵁 batchNo 鏄惁涓虹┖鏋勫缓涓嶅悓鐨勬煡璇㈡潯浠�
+        if (stockInRecord.getBatchNo() != null && !stockInRecord.getBatchNo().isEmpty()) {
+            queryWrapper.eq(StockInventory::getBatchNo, stockInRecord.getBatchNo());
+        } else {
+            queryWrapper.isNull(StockInventory::getBatchNo);
+        }
+        
+        StockInventory oldStockInventory = stockInventoryMapper.selectOne(queryWrapper);
 
         if (ObjectUtils.isEmpty(oldStockInventory)) {
             // 涓嶅瓨鍦ㄥ垯鏂板
@@ -192,8 +208,16 @@
             // 瀛樺湪鍒欐洿鏂�
             LambdaUpdateWrapper<StockInventory> updateWrapper = new LambdaUpdateWrapper<>();
             updateWrapper
-                    .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId())
-                    .eq(StockInventory::getBatchNo, stockInRecord.getBatchNo())
+                    .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId());
+            
+            // 鏍规嵁 batchNo 鏄惁涓虹┖鏋勫缓涓嶅悓鐨勬洿鏂版潯浠�
+            if (stockInRecord.getBatchNo() != null && !stockInRecord.getBatchNo().isEmpty()) {
+                updateWrapper.eq(StockInventory::getBatchNo, stockInRecord.getBatchNo());
+            } else {
+                updateWrapper.isNull(StockInventory::getBatchNo);
+            }
+            
+            updateWrapper
                     .setSql(stockInRecord.getStockInNum() != null,
                             "qualitity = qualitity + " + stockInRecord.getStockInNum())
                     .setSql(true, "version = version + 1")
@@ -307,59 +331,100 @@
     @Override
     public R importStockInventory(MultipartFile file) {
         try {
-            // 鏌ヨ鎵�鏈夌殑浜у搧
+            // 鏌ヨ鎵�鏈夌殑浜у搧骞舵瀯寤烘槧灏勶紝鎻愰珮鏌ユ壘鏁堢巼
             List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectProduct();
+            Map<String, SalesLedgerProduct> productMap = new HashMap<>();
+            for (SalesLedgerProduct product : salesLedgerProducts) {
+                // 浣跨敤浜у搧绫诲埆鍜岃鏍煎瀷鍙蜂綔涓洪敭
+                String key = product.getProductCategory() + "|" + product.getSpecificationModel();
+                productMap.put(key, product);
+            }
 
             ExcelUtil<StockInventoryExportData> util = new ExcelUtil<StockInventoryExportData>(StockInventoryExportData.class);
             List<StockInventoryExportData> list = util.importExcel(file.getInputStream());
 
             // 璁板綍鏈壘鍒板尮閰嶉」鐨勬暟鎹�
             List<String> unmatchedRecords = new ArrayList<>();
+            // 璁板綍澶勭悊缁撴灉
+            int successCount = 0;
 
-            list.forEach(dto -> {
-                boolean matched = false;
-                for (SalesLedgerProduct item : salesLedgerProducts) {
-                    if (item.getProductCategory().equals(dto.getProductName()) &&
-                            item.getSpecificationModel().equals(dto.getModel())) {
+            for (StockInventoryExportData dto : list) {
+                // 鏋勫缓鏌ユ壘閿�
+                String key = dto.getProductName() + "|" + dto.getModel();
+                SalesLedgerProduct matchedProduct = productMap.get(key);
+
+                if (matchedProduct != null) {
+                    // 澶勭悊鍚堟牸搴撳瓨
+                    if (dto.getQualifiedQuantity() != null && dto.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
                         StockInventoryDto stockInventoryDto = new StockInventoryDto();
                         stockInventoryDto.setRecordId(0L);
                         stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode());
-                        stockInventoryDto.setQualitity(dto.getQualitity());
+                        stockInventoryDto.setQualitity(dto.getQualifiedQuantity());
                         stockInventoryDto.setRemark(dto.getRemark());
                         stockInventoryDto.setWarnNum(dto.getWarnNum());
-                        if (ObjectUtils.isNotEmpty(dto.getLockedQuantity()) && dto.getLockedQuantity().compareTo(dto.getQualitity()) > 0) {
-                            throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃鏈瀵煎叆鐨勫簱瀛樻暟閲�");
+                        stockInventoryDto.setBatchNo(dto.getQualifiedBatchNo());
+
+                        // 楠岃瘉鍚堟牸鍐荤粨鏁伴噺
+                        if (ObjectUtils.isNotEmpty(dto.getQualifiedLockedQuantity())) {
+                            if (dto.getQualifiedLockedQuantity().compareTo(dto.getQualifiedQuantity()) > 0) {
+                                throw new RuntimeException("鍚堟牸鍐荤粨鏁伴噺涓嶈兘瓒呰繃鏈瀵煎叆鐨勫悎鏍煎簱瀛樻暟閲�");
+                            }
+                            stockInventoryDto.setLockedQuantity(dto.getQualifiedLockedQuantity());
+                        } else {
+                            stockInventoryDto.setLockedQuantity(BigDecimal.ZERO);
                         }
-                        stockInventoryDto.setLockedQuantity(dto.getLockedQuantity());
-                        stockInventoryDto.setProductModelId(item.getProductModelId());
+
+                        stockInventoryDto.setProductModelId(matchedProduct.getProductModelId());
                         this.addstockInventory(stockInventoryDto);
-                        matched = true;
-                        break; // 鎵惧埌鍖归厤椤瑰悗璺冲嚭寰幆
+                        successCount++;
                     }
+
+                    // 澶勭悊涓嶅悎鏍煎簱瀛�
+                    if (dto.getUnQualifiedQuantity() != null && dto.getUnQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
+                        StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
+                        stockUninventoryDto.setRecordId(0L);
+                        stockUninventoryDto.setRecordType(StockInUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode());
+                        stockUninventoryDto.setQualitity(dto.getUnQualifiedQuantity());
+                        stockUninventoryDto.setRemark(dto.getRemark());
+                        stockUninventoryDto.setBatchNo(dto.getUnQualifiedBatchNo());
+
+                        // 楠岃瘉涓嶅悎鏍煎喕缁撴暟閲�
+                        if (ObjectUtils.isNotEmpty(dto.getUnQualifiedLockedQuantity())) {
+                            if (dto.getUnQualifiedLockedQuantity().compareTo(dto.getUnQualifiedQuantity()) > 0) {
+                                throw new RuntimeException("涓嶅悎鏍煎喕缁撴暟閲忎笉鑳借秴杩囨湰娆″鍏ョ殑涓嶅悎鏍煎簱瀛樻暟閲�");
+                            }
+                            stockUninventoryDto.setLockedQuantity(dto.getUnQualifiedLockedQuantity());
+                        } else {
+                            stockUninventoryDto.setLockedQuantity(BigDecimal.ZERO);
+                        }
+
+                        stockUninventoryDto.setProductModelId(matchedProduct.getProductModelId());
+                        stockUninventoryService.addStockUninventory(stockUninventoryDto);
+                        successCount++;
+                    }
+                } else {
+                    // 璁板綍鏈尮閰嶇殑浜у搧
+                    String unmatchedRecord = "浜у搧鍚嶇О锛�" + dto.getProductName() + "锛屽瀷鍙凤細" + dto.getModel();
+                    unmatchedRecords.add(unmatchedRecord);
                 }
-                if (!matched) {
-                    // 璁板綍鏈尮閰嶇殑鏁版嵁
-                    String unmatchedInfo = String.format("浜у搧鍚嶇О锛�%s锛岃鏍煎瀷鍙凤細%s",
-                            dto.getProductName(), dto.getModel());
-                    unmatchedRecords.add(unmatchedInfo);
-                }
-            });
+            }
+
             // 鏋勫缓杩斿洖淇℃伅
             StringBuilder message = new StringBuilder();
             if (!unmatchedRecords.isEmpty()) {
-                message.append("浠ヤ笅浜у搧鏈壘鍒板尮閰嶉」锛歕n");
+                message.append("瀵煎叆鎴愬姛 " + successCount + " 鏉¤褰曪紝浠ヤ笅浜у搧鏈壘鍒板尮閰嶉」锛歕n");
                 for (String record : unmatchedRecords) {
                     message.append(record).append("\n");
                 }
-                throw new RuntimeException(message.toString());
+                return R.ok(message.toString());
             }
+
+            return R.ok("瀵煎叆鎴愬姛锛屽叡澶勭悊 " + successCount + " 鏉¤褰�");
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("瀵煎叆搴撳瓨澶辫触", e);
             return R.fail("瀵煎叆澶辫触锛�" + e.getMessage());
         }
-        return R.ok("瀵煎叆鎴愬姛");
     }
-
 
     @Override
     public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
@@ -402,4 +467,4 @@
         stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
         return this.updateById(stockInventory);
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
index b315964..85f0e23 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ruoyi.stock.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -8,6 +9,8 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
 import com.ruoyi.approve.vo.ApproveProcessVO;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
@@ -18,6 +21,7 @@
 import com.ruoyi.stock.execl.StockUnInventoryExportData;
 import com.ruoyi.stock.mapper.StockUninventoryMapper;
 import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.pojo.StockInventory;
 import com.ruoyi.stock.pojo.StockUninventory;
 import com.ruoyi.stock.service.StockInRecordService;
 import com.ruoyi.stock.service.StockOutRecordService;
@@ -28,8 +32,11 @@
 
 import javax.servlet.http.HttpServletResponse;
 import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.Date;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * <p>
@@ -50,6 +57,8 @@
     private StockInRecordService stockInRecordService;
     @Autowired
     private ApproveProcessServiceImpl approveProcessService;
+    @Autowired
+    private ProductModelMapper productModelMapper;
 
     @Override
     public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
@@ -59,6 +68,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Integer addStockUninventory(StockUninventoryDto stockUninventoryDto) {
+        List<StockUninventory> stockUninventoryList = stockUninventoryMapper.selectList(null);
         //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
         StockInRecordDto stockInRecordDto = new StockInRecordDto();
         stockInRecordDto.setRecordId(stockUninventoryDto.getRecordId());
@@ -68,6 +78,26 @@
         stockInRecordDto.setRemark(stockUninventoryDto.getRemark());
         stockInRecordDto.setApproveStatus(0);
         stockInRecordDto.setType("1");
+        if (stockUninventoryDto.getBatchNo() == null || stockUninventoryDto.getBatchNo().isEmpty()) {
+            String batchNo;
+            // 鑾峰彇褰撳墠鏈堜唤锛堜袱浣嶏級
+            LocalDate now = LocalDate.now();
+            String monthFlag = now.format(DateTimeFormatter.ofPattern("MM"));
+
+            // 鑾峰彇褰撳墠鏈堜唤鐨勬渶澶ф祦姘村彿
+            int maxSeq = getCurrentMonthMaxSeq(stockUninventoryDto, monthFlag, stockUninventoryList);
+
+            // 鏂版祦姘村彿 = 鏈�澶ф祦姘村彿 + 1
+            int newSeq = maxSeq + 1;
+            String seqStr = String.format("%03d", newSeq);
+
+            // 缁勮batchNo
+            ProductModel productModel = productModelMapper.selectById(stockUninventoryDto.getProductModelId());
+            batchNo = stockUninventoryDto.getMaterialCode() + productModel.getModel() + "P" + monthFlag + seqStr;
+            stockInRecordDto.setBatchNo(batchNo);
+        } else {
+            stockInRecordDto.setBatchNo(stockUninventoryDto.getBatchNo());
+        }
         Long id = stockInRecordService.add(stockInRecordDto);
 
         LoginUser loginUser = SecurityUtils.getLoginUser();
@@ -80,6 +110,33 @@
         }
 
         return 1;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鏈堜唤宸插瓨鍦ㄧ殑鏈�澶ф祦姘村彿
+     */
+    private static int getCurrentMonthMaxSeq(StockUninventoryDto dto, String monthFlag, List<StockUninventory> existingList) {
+        int maxSeq = 0;
+
+        String prefix = dto.getMaterialCode() + dto.getModel() + "P" + monthFlag;
+
+        // 姝e垯鍖归厤锛氬墠缂� + 3浣嶆暟瀛�
+        Pattern pattern = Pattern.compile(Pattern.quote(prefix) + "(\\d{3})");
+
+        for (StockUninventory item : existingList) {
+            String batchNo = item.getBatchNo();
+            if (batchNo == null) continue;
+
+            Matcher matcher = pattern.matcher(batchNo);
+            if (matcher.find()) {
+                int seq = Integer.parseInt(matcher.group(1));
+                if (seq > maxSeq) {
+                    maxSeq = seq;
+                }
+            }
+        }
+
+        return maxSeq;
     }
 
     public void addApproveByPurchase(LoginUser loginUser, StockInRecordDto stockInRecordDto,Long id) throws Exception {
@@ -129,10 +186,17 @@
      */
     public void updateOrCreateStockUninventory(StockInRecord stockInRecord) {
         // 鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪
-        StockUninventory oldStockUnInventory = stockUninventoryMapper.selectOne(
-                new QueryWrapper<StockUninventory>().lambda()
-                        .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId())
-        );
+        LambdaQueryWrapper<StockUninventory> queryWrapper = new QueryWrapper<StockUninventory>().lambda()
+                .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
+
+        // 鏍规嵁 batchNo 鏄惁涓虹┖鏋勫缓涓嶅悓鐨勬煡璇㈡潯浠�
+        if (stockInRecord.getBatchNo() != null && !stockInRecord.getBatchNo().isEmpty()) {
+            queryWrapper.eq(StockUninventory::getBatchNo, stockInRecord.getBatchNo());
+        } else {
+            queryWrapper.isNull(StockUninventory::getBatchNo);
+        }
+
+        StockUninventory oldStockUnInventory = stockUninventoryMapper.selectOne(queryWrapper);
 
         if (ObjectUtils.isEmpty(oldStockUnInventory)) {
             // 涓嶅瓨鍦ㄥ垯鏂板
@@ -141,19 +205,32 @@
             newStockUnInventory.setQualitity(stockInRecord.getStockInNum());
             newStockUnInventory.setVersion(1);
             newStockUnInventory.setRemark(stockInRecord.getRemark());
+            newStockUnInventory.setLockedQuantity(stockInRecord.getLockedQuantity());
+            newStockUnInventory.setBatchNo(stockInRecord.getBatchNo());
             stockUninventoryMapper.insert(newStockUnInventory);
         } else {
+            // 瀛樺湪鍒欐洿鏂�
             LambdaUpdateWrapper<StockUninventory> updateWrapper = new LambdaUpdateWrapper<>();
-            if (stockInRecord.getStockInNum() != null) {
-                updateWrapper.setSql("qualitity = qualitity + " + stockInRecord.getStockInNum());
+            updateWrapper
+                    .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
+
+            // 鏍规嵁 batchNo 鏄惁涓虹┖鏋勫缓涓嶅悓鐨勬洿鏂版潯浠�
+            if (stockInRecord.getBatchNo() != null && !stockInRecord.getBatchNo().isEmpty()) {
+                updateWrapper.eq(StockUninventory::getBatchNo, stockInRecord.getBatchNo());
+            } else {
+                updateWrapper.isNull(StockUninventory::getBatchNo);
             }
-            updateWrapper.setSql("version = version + 1");
-            String remark = stockInRecord.getRemark();
-            if (remark != null && !remark.isEmpty()) {
-                updateWrapper.set(StockUninventory::getRemark, remark);
-            }
-            updateWrapper.set(StockUninventory::getUpdateTime, new Date());
-            updateWrapper.eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
+
+            updateWrapper
+                    .setSql(stockInRecord.getStockInNum() != null,
+                            "qualitity = qualitity + " + stockInRecord.getStockInNum())
+                    .setSql(true, "version = version + 1")
+                    .set(stockInRecord.getRemark() != null && !stockInRecord.getRemark().isEmpty(),
+                            StockUninventory::getRemark, stockInRecord.getRemark())
+                    .setSql(stockInRecord.getLockedQuantity() != null,
+                            "locked_quantity = locked_quantity + " + stockInRecord.getLockedQuantity())
+                    .set(StockUninventory::getUpdateTime, new Date());
+
             stockUninventoryMapper.update(null, updateWrapper);
         }
     }
@@ -209,4 +286,4 @@
         stockUninventory.setLockedQuantity(stockUninventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
         return this.updateById(stockUninventory);
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
index db26cc9..9acd557 100644
--- a/src/main/resources/mapper/stock/StockInventoryMapper.xml
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -80,23 +80,198 @@
             AND p.product_name LIKE CONCAT('%', #{ew.productName}, '%')
         </if>
     </select>
-    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
-        select si.qualitity,
+
+    <select id="pageListCombinedStockInventory" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+        WITH RECURSIVE product_tree AS (
+        SELECT id
+        FROM product
+        WHERE id = #{ew.topParentProductId}
+
+        UNION ALL
+
+        SELECT p.id
+        FROM product p
+        INNER JOIN product_tree pt ON p.parent_id = pt.id
+        )
+        select
+        MAX(qualifiedId) as qualifiedId,
+        MAX(unQualifiedId) as unQualifiedId,
+        SUM(qualifiedQuantity) as qualifiedQuantity,
+        SUM(unQualifiedQuantity) as unQualifiedQuantity,
+        SUM(qualifiedLockedQuantity) as qualifiedLockedQuantity,
+        SUM(unQualifiedLockedQuantity) as unQualifiedLockedQuantity,
+        SUM(qualifiedQuantity - qualifiedLockedQuantity) as qualifiedUnLockedQuantity,
+        SUM(unQualifiedQuantity - unQualifiedLockedQuantity) as unQualifiedUnLockedQuantity,
+        product_model_id,
+        MAX(create_time) as create_time,
+        MAX(update_time) as update_time,
+        MAX(warn_num) as warn_num,
+        MAX(version) as version,
+        model,
+        MAX(remark) as remark,
+        unit,
+        product_name,
+        product_id,
+        MAX(qualifiedBatchNo) as qualifiedBatchNo,
+        MAX(unQualifiedBatchNo) as unQualifiedBatchNo,
+        MAX(materialCode) as materialCode,
+        'combined' as stockType
+        from (
+        select
+        si.id as qualifiedId,
+        null as unQualifiedId,
+        si.qualitity as qualifiedQuantity,
+        0 as unQualifiedQuantity,
+        COALESCE(si.locked_quantity, 0) as locked_quantity,
+        COALESCE(si.locked_quantity, 0) as qualifiedLockedQuantity,
+        0 as unQualifiedLockedQuantity,
+        si.product_model_id,
+        si.create_time,
+        si.update_time,
+        COALESCE(si.warn_num, 0) as warn_num,
+        si.version,
+        (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
         pm.model,
-        pm.unit,
-        pm.material_code as materialCode,
-        p.product_name,
-        coalesce(si.warn_num, 0) as warn_num,
-        coalesce(si.locked_quantity, 0) as locked_quantity,
         si.remark,
-        si.update_time
+        pm.unit,
+        p.product_name,
+        p.id as product_id,
+        si.batch_no as qualifiedBatchNo,
+        null as unQualifiedBatchNo,
+        pm.material_code as materialCode
         from stock_inventory si
         left join product_model pm on si.product_model_id = pm.id
         left join product p on pm.product_id = p.id
-        where 1 = 1
-        <if test="ew.productName != null and ew.productName !=''">
-            and p.product_name like concat('%',#{ew.productName},'%')
-        </if>
+
+        union all
+
+        select
+        null as qualifiedId,
+        su.id as unQualifiedId,
+        0 as qualifiedQuantity,
+        su.qualitity as unQualifiedQuantity,
+        COALESCE(su.locked_quantity, 0) as locked_quantity,
+        0 as qualifiedLockedQuantity,
+        COALESCE(su.locked_quantity, 0) as unQualifiedLockedQuantity,
+        su.product_model_id,
+        su.create_time,
+        su.update_time,
+        0 as warn_num,
+        su.version,
+        (su.qualitity - COALESCE(su.locked_quantity, 0)) as un_locked_quantity,
+        pm.model,
+        su.remark,
+        pm.unit,
+        p.product_name,
+        p.id as product_id,
+        null as qualifiedBatchNo,
+        su.batch_no as unQualifiedBatchNo,
+        pm.material_code as materialCode
+        from stock_uninventory su
+        left join product_model pm on su.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        ) as combined
+        <where>
+            <if test="ew.productName != null and ew.productName !=''">
+                and combined.product_name in (
+                select distinct p.product_name
+                from product p
+                left join product_model pm on p.id = pm.product_id
+                where p.product_name like concat('%',#{ew.productName},'%') or pm.model like concat('%',#{ew.productName},'%')
+                )
+            </if>
+            <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
+                and combined.product_id in (select id from product_tree)
+            </if>
+        </where>
+        group by product_model_id, model, unit, product_name, product_id, qualifiedBatchNo, unQualifiedBatchNo, materialCode, product_id, qualifiedBatchNo, unQualifiedBatchNo
+    </select>
+
+    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
+        WITH RECURSIVE product_tree AS (
+        SELECT id
+        FROM product
+        WHERE id = #{ew.topParentProductId}
+
+        UNION ALL
+
+        SELECT p.id
+        FROM product p
+        INNER JOIN product_tree pt ON p.parent_id = pt.id
+        )
+        select
+        SUM(qualifiedQuantity) as qualifiedQuantity,
+        SUM(unQualifiedQuantity) as unQualifiedQuantity,
+        SUM(qualifiedLockedQuantity) as qualifiedLockedQuantity,
+        SUM(unQualifiedLockedQuantity) as unQualifiedLockedQuantity,
+        model,
+        unit,
+        product_name,
+        MAX(warn_num) as warn_num,
+        MAX(remark) as remark,
+        MAX(update_time) as update_time,
+        MAX(qualifiedBatchNo) as qualifiedBatchNo,
+        MAX(unQualifiedBatchNo) as unQualifiedBatchNo,
+        MAX(materialCode) as materialCode
+        from (
+        select
+        si.qualitity as qualifiedQuantity,
+        0 as unQualifiedQuantity,
+        COALESCE(si.locked_quantity, 0) as qualifiedLockedQuantity,
+        0 as unQualifiedLockedQuantity,
+        si.product_model_id,
+        si.create_time,
+        si.update_time,
+        COALESCE(si.warn_num, 0) as warn_num,
+        si.remark,
+        pm.model,
+        pm.unit,
+        p.product_name,
+        p.id as product_id,
+        si.batch_no as qualifiedBatchNo,
+        null as unQualifiedBatchNo,
+        pm.material_code as materialCode
+        from stock_inventory si
+        left join product_model pm on si.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+
+        union all
+
+        select
+        0 as qualifiedQuantity,
+        su.qualitity as unQualifiedQuantity,
+        0 as qualifiedLockedQuantity,
+        COALESCE(su.locked_quantity, 0) as unQualifiedLockedQuantity,
+        su.product_model_id,
+        su.create_time,
+        su.update_time,
+        0 as warn_num,
+        su.remark,
+        pm.model,
+        pm.unit,
+        p.product_name,
+        p.id as product_id,
+        null as qualifiedBatchNo,
+        su.batch_no as unQualifiedBatchNo,
+        pm.material_code as materialCode
+        from stock_uninventory su
+        left join product_model pm on su.product_model_id = pm.id
+        left join product p on pm.product_id = p.id
+        ) as combined
+        <where>
+            <if test="ew.productName != null and ew.productName !=''">
+                and combined.product_name in (
+                select distinct p.product_name
+                from product p
+                left join product_model pm on p.id = pm.product_id
+                where p.product_name like concat('%',#{ew.productName},'%') or pm.model like concat('%',#{ew.productName},'%')
+                )
+            </if>
+            <if test="ew.topParentProductId != null and ew.topParentProductId > 0">
+                and combined.product_id in (select id from product_tree)
+            </if>
+        </where>
+        group by product_model_id, model, unit, product_name, qualifiedBatchNo, unQualifiedBatchNo, materialCode
     </select>
     <select id="stockInventoryPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
         select sir.*,si.qualitity as current_stock,
@@ -265,4 +440,4 @@
                  left join product p on pm.product_id = p.id
     </select>
 
-</mapper>
+</mapper>
\ No newline at end of file

--
Gitblit v1.9.3