From e0cfecb2b11b3d6ac91394e71c689a30356ead28 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期三, 20 五月 2026 09:29:28 +0800
Subject: [PATCH] fix:1.生产按照特定工序更改 2.库存按照型号,工序类别,电压进行筛选 3.销售页面展示电压,类别 4.生产入库按照电压入库

---
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java           |   32 +
 src/main/java/com/ruoyi/stock/pojo/StockInventory.java                                |    6 
 src/main/resources/mapper/production/ProductWorkOrderMapper.xml                       |    3 
 src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java                                 |   43 -
 src/main/java/com/ruoyi/stock/service/StockInventoryService.java                      |    4 
 src/main/resources/mapper/sales/SalesLedgerMapper.xml                                 |   14 
 src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java                      |   21 
 src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml                        |    2 
 src/main/java/com/ruoyi/sales/pojo/SalesLedger.java                                   |    2 
 src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java                       |   24 +
 src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java              |   76 +-
 src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java                   |   16 
 src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java                           |    2 
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java                |    2 
 src/main/java/com/ruoyi/stock/dto/FinishedProductTreeDto.java                         |   31 +
 src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java             |  381 ++++++++++-----
 src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java                              |   30 
 src/main/java/com/ruoyi/stock/pojo/StockInRecord.java                                 |    6 
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |  287 ++++++++----
 src/main/resources/mapper/stock/StockInventoryMapper.xml                              |   78 +++
 src/main/resources/mapper/stock/StockInRecordMapper.xml                               |   14 
 src/main/java/com/ruoyi/stock/controller/StockInventoryController.java                |    8 
 src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java                               |   33 
 src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java       |   16 
 src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java                     |   17 
 src/main/java/com/ruoyi/stock/support/FinishedProductStockDimensionResolver.java      |  184 ++++++++
 src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java                        |    2 
 27 files changed, 976 insertions(+), 358 deletions(-)

diff --git a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
index 3d95c67..288f721 100644
--- a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
+++ b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -103,6 +103,18 @@
         stockInventoryService.addstockInventory(stockInventoryDto);
     }
 
+    public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId,
+                         String processCategory, String voltage) {
+        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+        stockInventoryDto.setRecordId(recordId);
+        stockInventoryDto.setRecordType(String.valueOf(recordType));
+        stockInventoryDto.setQualitity(quantity);
+        stockInventoryDto.setProductModelId(productModelId);
+        stockInventoryDto.setProcessCategory(processCategory);
+        stockInventoryDto.setVoltage(voltage);
+        stockInventoryService.addstockInventory(stockInventoryDto);
+    }
+
     /**
      * 鍚堟牸鍏ュ簱锛堜笉瀹℃牳锛�
      *
@@ -120,6 +132,18 @@
         stockInventoryService.addstockInventoryNoReview(stockInventoryDto);
     }
 
+    public void addStockNoReview(Long productModelId, BigDecimal quantity, String recordType, Long recordId,
+                                 String processCategory, String voltage) {
+        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+        stockInventoryDto.setRecordId(recordId);
+        stockInventoryDto.setRecordType(String.valueOf(recordType));
+        stockInventoryDto.setQualitity(quantity);
+        stockInventoryDto.setProductModelId(productModelId);
+        stockInventoryDto.setProcessCategory(processCategory);
+        stockInventoryDto.setVoltage(voltage);
+        stockInventoryService.addstockInventoryNoReview(stockInventoryDto);
+    }
+
     /**
      * 鍚堟牸鍑哄簱
      *
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index d9c4031..c2a22d4 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -30,6 +30,7 @@
 import com.ruoyi.quality.mapper.*;
 import com.ruoyi.quality.pojo.*;
 import com.ruoyi.quality.service.IQualityInspectService;
+import com.ruoyi.stock.support.FinishedProductStockDimensionResolver;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -47,6 +48,10 @@
 @AllArgsConstructor
 @Transactional(rollbackFor = Exception.class)
 public class ProductionProductMainServiceImpl extends ServiceImpl<ProductionProductMainMapper, ProductionProductMain> implements ProductionProductMainService {
+
+    private static final String PROCESS_VOLTAGE_SORT = "鐢靛帇鍒嗛��";
+    private static final String PROCESS_OPTICAL_INSPECTION = "鍏夋澶栬";
+    private static final String PROCESS_PACKAGING = "鍖呰";
 
     private IQualityInspectService qualityInspectService;
     private ProductionProductMainMapper productionProductMainMapper;
@@ -86,6 +91,11 @@
 
     private StockUtils stockUtils;
 
+    /**
+     * 瑙f瀽鐢熶骇鎶ュ伐瀵瑰簲鐨勬垚鍝佸叆搴撶淮搴︺��
+     */
+    private FinishedProductStockDimensionResolver finishedProductStockDimensionResolver;
+
     private ISysNoticeService sysNoticeService;
 
     @Override
@@ -93,6 +103,9 @@
         return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
     }
 
+    /**
+     * 鏂板鎶ュ伐锛屽苟鏍规嵁褰撳墠宸ュ簭澶勭悊璐ㄦ鍜屽叆搴撻�昏緫銆�
+     */
     @Override
     public Boolean addProductMain(ProductionProductMainDto dto) {
         SysUser user = userMapper.selectUserById(dto.getUserId());
@@ -100,9 +113,8 @@
         //褰撳墠宸ヨ壓璺嚎瀵瑰簲鐨勫伐搴忚鎯�
         ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(dto.getProductProcessRouteItemId());
         if (productProcessRouteItem == null) {
-            throw new RuntimeException("宸ヨ壓璺嚎椤逛笉瀛樺湪");
+            throw new RuntimeException("宸ヨ壓璺嚎宸ュ簭椤逛笉瀛樺湪");
         }
-        // 鏄惁鑳芥姤宸ワ細 1. 绗竴涓伐搴忚兘鎶ュ伐 2. 涓婁竴涓伐搴忓凡鎶ュ伐 3. 涔嬪墠鐨勫伐搴忔湭琚殧绂�
         //妫�鏌ヤ笂涓�涓伐搴忔槸鍚﹀凡鎶ュ伐
         Integer currentDragSort = productProcessRouteItem.getDragSort();
         if (currentDragSort != null && currentDragSort > 1) {
@@ -111,7 +123,7 @@
                     currentDragSort
             );
             if (!isPreviousReported) {
-                throw new RuntimeException("涓婁竴涓伐搴忓皻鏈姤宸ワ紝涓嶈兘杩涜褰撳墠宸ュ簭鎶ュ伐");
+                throw new RuntimeException("涓婁竴閬撳伐搴忓皻鏈姤宸ワ紝褰撳墠宸ュ簭涓嶈兘鎶ュ伐");
             }
 
             // 鏌ヨ鎵�鏈変箣鍓嶇殑宸ュ簭锛堟帓搴忓彿灏忎簬褰撳墠宸ュ簭锛�
@@ -133,7 +145,7 @@
                 // 妫�鏌ヤ箣鍓嶇殑宸ュ簭鏄惁鏈夎闅旂鐨勪笉鍚堟牸璁板綍
                 List<QualityUnqualified> unqualifiedList = qualityUnqualifiedMapper.selectUnqualifiedByProcessNames(previousProcessNames);
                 if (CollectionUtils.isNotEmpty(unqualifiedList)) {
-                    throw new RuntimeException("涔嬪墠鐨勫伐搴忓凡琚殧绂伙紝涓嶈兘杩涜褰撳墠宸ュ簭鎶ュ伐");
+                    throw new RuntimeException("鍓嶅簭宸ュ簭瀛樺湪闅旂璁板綍锛屽綋鍓嶅伐搴忎笉鑳芥姤宸�");
                 }
             }
         }
@@ -219,13 +231,11 @@
 
             // 鏍稿績璁$畻
             BigDecimal needQty = childQty.divide(parentQty, 6, RoundingMode.HALF_UP).multiply(dto.getQuantity());
-
-
             productionProductInput.setQuantity(needQty);
-
             productionProductInput.setProductMainId(productionProductMain.getId());
             productionProductInputMapper.insert(productionProductInput);
-            stockUtils.substractStock(productStructureDto.getProductModelId(), productionProductInput.getQuantity(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId(), null);
+            stockUtils.substractStock(productStructureDto.getProductModelId(), productionProductInput.getQuantity(),
+                    StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId(), null);
         }
         /*鏂板鎶ュ伐浜у嚭琛�*/
         ProductionProductOutput productionProductOutput = new ProductionProductOutput();
@@ -241,16 +251,27 @@
 
         //鍚堟牸鏁伴噺=鎶ュ伐鏁伴噺-鎶ュ簾鏁伴噺
         BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty());
-        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-        //鍙湁鍚堟牸鏁伴噺>0鎵嶈兘澧炲姞鐩稿簲鏁版嵁
+        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(
+                Wrappers.<ProductProcessRouteItem>lambdaQuery()
+                        .eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId())
+        );
+        boolean isRouteLastProcess = productProcessRouteItem.getDragSort() == productProcessRouteItems.size();
+        ReportStockRule reportStockRule = resolveReportStockRule(productProcessRouteItem, productProcess, productProcessRouteItems);
+
+        String processCategory = null;
+        String voltage = null;
+        if (productQty.compareTo(BigDecimal.ZERO) > 0 && reportStockRule.isFinishedGoodsStockIn()) {
+            processCategory = finishedProductStockDimensionResolver.resolveProcessCategory(productionProductMain.getId());
+            voltage = finishedProductStockDimensionResolver.resolveVoltage(productionProductMain.getId());
+        }
+
         if (productQty.compareTo(BigDecimal.ZERO) > 0) {
             /*鏂板璐ㄦ*/
             if (productProcessRouteItem.getIsQuality()) {
                 //瀵瑰簲鐨勮繃绋嬫鎴栬�呭嚭鍘傛
                 int inspectType = 1;
-                String process = productProcess.getName();//宸ュ簭
-                if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                    //鏈�鍚庝竴閬撳伐搴忕敓鎴愬嚭鍘傛
+                String process = productProcess.getName();
+                if (reportStockRule.isFinishedGoodsStockIn()) {
                     inspectType = 2;
                     process = null;
                 }
@@ -260,11 +281,11 @@
                 qualityInspect.setProductName(product.getProductName());
                 qualityInspect.setModel(productModel.getModel());
                 qualityInspect.setUnit(productModel.getUnit());
-                qualityInspect.setQuantity(productQty);
+                qualityInspect.setQuantity(productionProductOutput.getQuantity());
                 qualityInspect.setProcess(process);
                 qualityInspect.setInspectState(0);
                 qualityInspect.setInspectType(inspectType);
-                qualityInspect.setDefectiveQuantity(BigDecimal.ZERO);
+                qualityInspect.setDefectiveQuantity(productionProductOutput.getScrapQty());
                 qualityInspect.setProductMainId(productionProductMain.getId());
                 qualityInspect.setProductModelId(productModel.getId());
                 qualityInspectMapper.insert(qualityInspect);
@@ -272,8 +293,10 @@
                 if (qualityTestStandard.size() > 0) {
                     qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
                     qualityInspectMapper.updateById(qualityInspect);
-                    qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
-                                    .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))//榛樿鑾峰彇鏈�鏂扮殑
+                    qualityTestStandardParamMapper.selectList(
+                                    Wrappers.<QualityTestStandardParam>lambdaQuery()
+                                            .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId())
+                            )
                             .forEach(qualityTestStandardParam -> {
                                 QualityInspectParam param = new QualityInspectParam();
                                 BeanUtils.copyProperties(qualityTestStandardParam, param);
@@ -282,39 +305,43 @@
                                 qualityInspectParamMapper.insert(param);
                             });
                 }
-            }else {
-                //鐩存帴鍏ュ簱
-                //鏈�鍚庝竴閬撳伐搴忎负鎴愬搧
-                if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                    //鎴愬搧瀹℃牳
-                    stockUtils.addStock(productProcessRouteItem.getProductModelId(), productQty, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
-                }else {
-                    //鍗婃垚鍝佷笉瀹℃牳
-                    stockUtils.addStockNoReview(productProcessRouteItem.getProductModelId(), productQty, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
+            } else {
+                if (reportStockRule.shouldCreateStockIn()) {
+                    if (reportStockRule.isFinishedGoodsStockIn()) {
+                        stockUtils.addStock(productProcessRouteItem.getProductModelId(), productQty,
+                                StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId(),
+                                processCategory, voltage);
+                    } else {
+                        stockUtils.addStockNoReview(productProcessRouteItem.getProductModelId(), productQty,
+                                StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
+                    }
+                }
+                if (productionProductOutput.getScrapQty().compareTo(BigDecimal.ZERO) > 0) {
+                    stockUtils.addUnStockNoReview(productProcessRouteItem.getProductModelId(), productionProductOutput.getScrapQty(),
+                            StockInUnQualifiedRecordTypeEnum.QUALITYINSPECT_UNSTOCK_IN.getCode(), productionProductMain.getId());
                 }
             }
-            /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
+
             productWorkOrder.setCompleteQuantity(productWorkOrder.getCompleteQuantity().add(productQty));
             if (ObjectUtils.isNull(productWorkOrder.getActualStartTime())) {
-                productWorkOrder.setActualStartTime(LocalDate.now());//瀹為檯寮�濮嬫椂闂�
+                productWorkOrder.setActualStartTime(LocalDate.now());
             }
             if (productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) == 0) {
-                productWorkOrder.setActualEndTime(LocalDate.now());//瀹為檯缁撴潫鏃堕棿
+                productWorkOrder.setActualEndTime(LocalDate.now());
             }
             productWorkOrderMapper.updateById(productWorkOrder);
-            //鐢熶骇璁㈠崟
+
             if (ObjectUtils.isNull(productOrder.getStartTime())) {
-                productOrder.setStartTime(LocalDateTime.now());//寮�濮嬫椂闂�
+                productOrder.setStartTime(LocalDateTime.now());
             }
-            if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                //濡傛灉鏄渶鍚庝竴閬撳伐搴忔姤宸ヤ箣鍚庣敓浜ц鍗曞畬鎴愭暟閲�+
+            if (isRouteLastProcess) {
                 productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().add(productQty));
                 if (productOrder.getCompleteQuantity().compareTo(productOrder.getQuantity()) == 0) {
-                    productOrder.setEndTime(LocalDateTime.now());//缁撴潫鏃堕棿
+                    productOrder.setEndTime(LocalDateTime.now());
                 }
             }
             productOrderMapper.updateById(productOrder);
-            /*娣诲姞鐢熶骇鏍哥畻*/
+
             SalesLedgerProductionAccounting salesLedgerProductionAccounting = SalesLedgerProductionAccounting.builder()
                     .productMainId(productionProductMain.getId())
                     .schedulingUserId(userId)
@@ -327,40 +354,122 @@
                     .build();
             salesLedgerProductionAccountingMapper.insert(salesLedgerProductionAccounting);
         }
-        //濡傛灉鎶ュ簾鏁伴噺>0,闇�瑕佽繘鍏ユ姤搴熺殑搴撳瓨
-        if (ObjectUtils.isNotEmpty(dto.getScrapQty())) {
-            if (dto.getScrapQty().compareTo(BigDecimal.ZERO) > 0) {
-                if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()){
-                    stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId());
-                }else {
-                    stockUtils.addUnStockNoReview(productModel.getId(), dto.getScrapQty(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId());
-                }
-            }
-        }
         return true;
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鎶ュ伐鏄惁闇�瑕佸叆搴擄紝浠ュ強鏄惁鎸夋垚鍝佸叆搴撳鐞嗐��
+     */
+    private ReportStockRule resolveReportStockRule(ProductProcessRouteItem currentRouteItem,
+                                                   ProductProcess currentProcess,
+                                                   List<ProductProcessRouteItem> routeItems) {
+        boolean isRouteLastProcess = currentRouteItem.getDragSort() != null
+                && CollectionUtils.isNotEmpty(routeItems)
+                && currentRouteItem.getDragSort().equals(routeItems.size());
+        String currentProcessName = normalizeProcessName(currentProcess == null ? null : currentProcess.getName());
+        if (PROCESS_VOLTAGE_SORT.equals(currentProcessName)) {
+            return new ReportStockRule(false, false);
+        }
+
+        Map<Long, String> processNameMap = loadRouteProcessNameMap(routeItems);
+        boolean hasVoltageSort = containsProcess(routeItems, processNameMap, PROCESS_VOLTAGE_SORT);
+        boolean hasOpticalInspection = containsProcess(routeItems, processNameMap, PROCESS_OPTICAL_INSPECTION);
+        boolean hasPackaging = containsProcess(routeItems, processNameMap, PROCESS_PACKAGING);
+
+        if (hasPackaging && PROCESS_PACKAGING.equals(currentProcessName)) {
+            return new ReportStockRule(true, true);
+        }
+        if (hasPackaging && PROCESS_OPTICAL_INSPECTION.equals(currentProcessName)) {
+            return new ReportStockRule(false, false);
+        }
+        if (!hasPackaging && hasVoltageSort && hasOpticalInspection && PROCESS_OPTICAL_INSPECTION.equals(currentProcessName)) {
+            return new ReportStockRule(true, true);
+        }
+        return new ReportStockRule(true, isRouteLastProcess);
+    }
+
+    /**
+     * 鎵归噺鍔犺浇宸ヨ壓璺嚎涓殑宸ュ簭鍚嶇О銆�
+     */
+    private Map<Long, String> loadRouteProcessNameMap(List<ProductProcessRouteItem> routeItems) {
+        if (CollectionUtils.isEmpty(routeItems)) {
+            return Collections.emptyMap();
+        }
+        Set<Long> processIds = routeItems.stream()
+                .map(ProductProcessRouteItem::getProcessId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (processIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return productProcessMapper.selectBatchIds(processIds).stream()
+                .collect(Collectors.toMap(ProductProcess::getId, process -> normalizeProcessName(process.getName())));
+    }
+
+    /**
+     * 鍒ゆ柇宸ヨ壓璺嚎涓槸鍚﹀寘鍚寚瀹氬伐搴忋��
+     */
+    private boolean containsProcess(List<ProductProcessRouteItem> routeItems, Map<Long, String> processNameMap, String processName) {
+        return routeItems.stream()
+                .map(ProductProcessRouteItem::getProcessId)
+                .map(processNameMap::get)
+                .anyMatch(processName::equals);
+    }
+
+    /**
+     * 缁熶竴宸ュ簭鍚嶇О鏍煎紡锛岄伩鍏嶈鍒欏尮閰嶆椂鍙楃┖鐧藉瓧绗﹀奖鍝嶃��
+     */
+    private String normalizeProcessName(String processName) {
+        return processName == null ? "" : processName.trim();
+    }
+
+    /**
+     * 鍗曟鎶ュ伐瀵瑰簲鐨勫叆搴撹鍒欍��
+     */
+    private static final class ReportStockRule {
+        private final boolean createStockIn;
+        private final boolean finishedGoodsStockIn;
+
+        private ReportStockRule(boolean createStockIn, boolean finishedGoodsStockIn) {
+            this.createStockIn = createStockIn;
+            this.finishedGoodsStockIn = finishedGoodsStockIn;
+        }
+
+        private boolean shouldCreateStockIn() {
+            return createStockIn;
+        }
+
+        private boolean isFinishedGoodsStockIn() {
+            return finishedGoodsStockIn;
+        }
     }
 
     @Override
     public Boolean removeProductMain(Long id) {
-        //鍒ゆ柇璇ユ潯鎶ュ伐鏄惁涓嶅悎鏍煎鐞�,濡傛灉涓嶅悎鏍煎鐞嗕簡锛屽垯涓嶅厑璁稿垹闄�
-        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, id));
-        if (qualityInspects.size() > 0){
-            List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(Wrappers.<QualityUnqualified>lambdaQuery()
-                    .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList())));
-            if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState()==1) {
-                throw new ServiceException("璇ユ潯鎶ュ伐宸茬粡涓嶅悎鏍煎鐞嗕簡锛屼笉鍏佽鍒犻櫎");
+        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
+                Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, id)
+        );
+        if (qualityInspects.size() > 0) {
+            List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
+                    Wrappers.<QualityUnqualified>lambdaQuery()
+                            .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList()))
+            );
+            if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) {
+                throw new ServiceException("璇ユ姤宸ュ凡瀹屾垚涓嶅悎鏍煎鐞嗭紝涓嶈兘鍒犻櫎");
             }
         }
+
         ProductionProductMain productionProductMain = productionProductMainMapper.selectById(id);
-        //璇ユ姤宸ュ搴旂殑宸ヨ壓璺嚎璇︽儏
         ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(productionProductMain.getProductProcessRouteItemId());
-        ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList(Wrappers.<ProductionProductOutput>lambdaQuery().eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())).get(0);
-        /*鍒犻櫎鏍哥畻*/
+        ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList(
+                Wrappers.<ProductionProductOutput>lambdaQuery().eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())
+        ).get(0);
+
         salesLedgerProductionAccountingMapper.delete(
                 new LambdaQueryWrapper<SalesLedgerProductionAccounting>()
                         .eq(SalesLedgerProductionAccounting::getProductMainId, productionProductMain.getId())
         );
-        /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
+
         ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(productionProductMain.getWorkOrderId());
         if (productWorkOrder != null && productionProductOutput != null) {
             BigDecimal outputQty = productionProductOutput.getQuantity() == null ? BigDecimal.ZERO : productionProductOutput.getQuantity();
@@ -368,16 +477,19 @@
             BigDecimal completeQty = productWorkOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productWorkOrder.getCompleteQuantity();
 
             BigDecimal validQuantity = outputQty.subtract(scrapQty);
-
             productWorkOrder.setCompleteQuantity(completeQty.subtract(validQuantity));
             productWorkOrder.setActualEndTime(null);
             productWorkOrderMapper.updateById(productWorkOrder);
         } else {
             throw new ServiceException("鎿嶄綔澶辫触锛氬伐鍗曚俊鎭垨浜у嚭璁板綍涓嶅瓨鍦�");
         }
-        //鍒ゆ柇鏄惁鏄渶鍚庝竴閬撳伐搴�
-        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-        if (productProcessRouteItem.getDragSort() != null && productProcessRouteItems != null && productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
+
+        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(
+                Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId())
+        );
+        if (productProcessRouteItem.getDragSort() != null
+                && productProcessRouteItems != null
+                && productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
             ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
             if (productOrder != null) {
                 BigDecimal orderCompleteQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
@@ -392,31 +504,30 @@
                 throw new ServiceException("鍏宠仈鐨勭敓浜ц鍗曚笉瀛樺湪");
             }
         }
-        //鍒犻櫎璐ㄦ
+
         qualityInspectMapper.selectList(
                 new LambdaQueryWrapper<QualityInspect>()
                         .eq(QualityInspect::getProductMainId, productionProductMain.getId())
         ).forEach(q -> {
             qualityInspectParamMapper.delete(
                     new LambdaQueryWrapper<QualityInspectParam>()
-                            .eq(QualityInspectParam::getInspectId, q.getId()));
+                            .eq(QualityInspectParam::getInspectId, q.getId())
+            );
             qualityInspectMapper.deleteById(q.getId());
-                stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode());
+            stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode());
         });
 
-        // 鍒犻櫎浜у嚭璁板綍
-        productionProductOutputMapper.delete(new LambdaQueryWrapper<ProductionProductOutput>()
-                .eq(ProductionProductOutput::getProductMainId, productionProductMain.getId()));
-        //鍒犻櫎鎶曞叆璁板綍
-        productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
-                .eq(ProductionProductInput::getProductMainId, productionProductMain.getId()));
-        //鍒犻櫎鎶ュ簾鐨勫叆搴撹褰�
+        productionProductOutputMapper.delete(
+                new LambdaQueryWrapper<ProductionProductOutput>()
+                        .eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())
+        );
+        productionProductInputMapper.delete(
+                new LambdaQueryWrapper<ProductionProductInput>()
+                        .eq(ProductionProductInput::getProductMainId, productionProductMain.getId())
+        );
         stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
-        //鍒犻櫎涓嶉渶瑕佽川妫�鐨勫悎鏍煎叆搴�
         stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode());
-        //鍒犻櫎鎶曞叆瀵瑰簲鐨勫嚭搴撹褰�
         stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
-        // 鍒犻櫎涓昏〃
         productionProductMainMapper.deleteById(productionProductMain.getId());
         return true;
     }
@@ -429,49 +540,39 @@
     @Override
     public List<ProductionProductMainDto> getByProductWorkOrderId(Long productWorkOrderId) {
         List<ProductionProductMainDto> productionProductMainDtos = productionProductMainMapper.getByProductWorkOrderId(productWorkOrderId);
-        
         if (productionProductMainDtos == null || productionProductMainDtos.isEmpty()) {
             return productionProductMainDtos;
         }
-        
-        // 鏀堕泦鎵�鏈変骇鍝佷富璁板綍ID
+
         List<Long> productMainIds = productionProductMainDtos.stream()
                 .map(ProductionProductMainDto::getId)
                 .collect(Collectors.toList());
-        
-        // 鎵归噺鏌ヨ鎵�鏈夌浉鍏崇殑璐ㄦ璁板綍
+
         List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                 Wrappers.<QualityInspect>lambdaQuery()
                         .in(QualityInspect::getProductMainId, productMainIds)
         );
-        
+
         if (!qualityInspects.isEmpty()) {
-            // 鏀堕泦鎵�鏈夎川妫�璁板綍ID
             List<Long> inspectIds = qualityInspects.stream()
                     .map(QualityInspect::getId)
                     .collect(Collectors.toList());
-            
-            // 鎵归噺鏌ヨ鎵�鏈夌浉鍏崇殑涓嶅悎鏍煎鐞嗚褰�
+
             List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
                     Wrappers.<QualityUnqualified>lambdaQuery()
                             .in(QualityUnqualified::getInspectId, inspectIds)
             );
-            
-            // 鏋勫缓璐ㄦID鍒颁笉鍚堟牸澶勭悊璁板綍鐨勬槧灏�
+
             Map<Long, QualityUnqualified> inspectIdToUnqualifiedMap = qualityUnqualifieds.stream()
                     .collect(Collectors.toMap(QualityUnqualified::getInspectId, q -> q, (q1, q2) -> q1));
-            
-            // 鏋勫缓浜у搧涓籌D鍒拌川妫�璁板綍鐨勬槧灏�
+
             Map<Long, QualityInspect> productMainIdToInspectMap = qualityInspects.stream()
                     .collect(Collectors.toMap(QualityInspect::getProductMainId, q -> q, (q1, q2) -> q1));
-            
-            // 鍏宠仈澶勭悊缁撴灉鍒颁骇鍝佷富璁板綍
+
             productionProductMainDtos.forEach(p -> {
                 QualityInspect qualityInspect = productMainIdToInspectMap.get(p.getId());
                 if (qualityInspect != null) {
-                    // 涓嶈壇鏁伴噺
-                    p.setDefectiveQuantity( qualityInspect.getDefectiveQuantity());
-                    // 鍚堟牸鏁伴噺 = 鎶ュ伐鏁伴噺-鎶ュ簾鏁伴噺-涓嶈壇鏁伴噺
+                    p.setDefectiveQuantity(qualityInspect.getDefectiveQuantity());
                     p.setQualifiedQty(p.getQuantity().subtract(p.getScrapQty()).subtract(p.getDefectiveQuantity()));
                     QualityUnqualified qualityUnqualified = inspectIdToUnqualifiedMap.get(qualityInspect.getId());
                     if (qualityUnqualified != null) {
@@ -480,7 +581,7 @@
                 }
             });
         }
-        
+
         return productionProductMainDtos;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 79ae988..1103268 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -10,6 +10,7 @@
 import com.deepoove.poi.XWPFTemplate;
 import com.deepoove.poi.config.Configure;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.procurementrecord.service.ProcurementRecordService;
@@ -33,7 +34,7 @@
 import com.ruoyi.quality.service.IQualityInspectParamService;
 import com.ruoyi.quality.service.IQualityInspectService;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
-import com.ruoyi.stock.pojo.StockUninventory;
+import com.ruoyi.stock.support.FinishedProductStockDimensionResolver;
 import lombok.AllArgsConstructor;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -78,6 +79,9 @@
     private ProductOrderMapper productOrderMapper;
     @Autowired
     private ISysNoticeService sysNoticeService;
+    // 瑙f瀽鎴愬搧鍏ュ簱缁村害銆�
+    @Autowired
+    private FinishedProductStockDimensionResolver finishedProductStockDimensionResolver;
 
     @Override
     public int add(QualityInspectDto qualityInspectDto) {
@@ -156,16 +160,30 @@
             qualityUnqualified.setInspectId(qualityInspect.getId());
             qualityUnqualifiedMapper.insert(qualityUnqualified);
         } else {
-            //鍚堟牸鍏ュ簱
+            // 鍚堟牸鍏ュ簱
             Integer inspectType = qualityInspect.getInspectType();
-            //鐢熶骇鎴愬搧娣诲姞瀹℃牳娴佺▼锛屽崐鎴愬搧涓嶅鏍�
-            if (inspectType != null && inspectType.equals(2)) {
-                stockUtils.addStock(qualityInspect.getProductModelId(), qualityInspect.getQuantity(),
-                        StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId());
+            boolean isFinishedProduct = inspectType != null && inspectType.equals(2); // 鎴愬搧闇�瑕佸鏍�
+            // 鏍规嵁鏄惁鎴愬搧閫夋嫨鍏ュ簱鏂瑰紡
+            String processCategory = null;
+            String voltage = null;
+            if (isFinishedProduct && qualityInspect.getProductMainId() != null) {
+                processCategory = finishedProductStockDimensionResolver.resolveProcessCategory(qualityInspect.getProductMainId());
+                voltage = finishedProductStockDimensionResolver.resolveVoltage(qualityInspect.getProductMainId());
+            }
+            if (isFinishedProduct) {
+                stockUtils.addStock(qualityInspect.getProductModelId(), productQty,
+                        StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId(),
+                        processCategory, voltage);
             } else {
-                stockUtils.addStockNoReview(qualityInspect.getProductModelId(), qualityInspect.getQuantity(),
+                stockUtils.addStockNoReview(qualityInspect.getProductModelId(), productQty,
                         StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId());
             }
+
+            // 涓嶈壇鏁伴噺>0鐨勫叆涓嶅悎鏍煎簱瀛�
+            if (qualityInspect.getDefectiveQuantity().compareTo(BigDecimal.ZERO) > 0) {
+                stockUtils.addUnStockNoReview(qualityInspect.getProductModelId(), qualityInspect.getDefectiveQuantity(),
+                        StockInUnQualifiedRecordTypeEnum.QUALITYINSPECT_UNSTOCK_IN.getCode(), qualityInspect.getId());
+            }
         }
         qualityInspect.setInspectState(1);//宸叉彁浜�
         return qualityInspectMapper.updateById(qualityInspect);
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
index a9e9c1d..bee313e 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -26,6 +26,7 @@
 import com.ruoyi.quality.pojo.QualityUnqualified;
 import com.ruoyi.quality.service.IQualityInspectService;
 import com.ruoyi.quality.service.IQualityUnqualifiedService;
+import com.ruoyi.stock.support.FinishedProductStockDimensionResolver;
 import com.ruoyi.stock.service.StockUninventoryService;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
@@ -35,7 +36,6 @@
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
-import java.util.Collections;
 import java.util.List;
 
 @AllArgsConstructor
@@ -51,6 +51,10 @@
     private ProductProcessRouteItemMapper productProcessRouteItemMapper;
     private ProductWorkOrderMapper productWorkOrderMapper;
     private StockUninventoryService stockUninventoryService;
+    /**
+     * 瑙f瀽鎴愬搧鍥炴祦鍏ュ簱缁村害銆�
+     */
+    private FinishedProductStockDimensionResolver finishedProductStockDimensionResolver;
 
     @Override
     public IPage<QualityUnqualified> qualityUnqualifiedListPage(Page page, QualityUnqualified qualityUnqualified) {
@@ -143,10 +147,12 @@
                 case "璁╂鏀捐":
                     //璋冪敤鎻愪氦鍚堟牸鐨勬帴鍙�
                     // 2 鏄垚鍝�-闇�瑕佸鏍� 1 鏄崐鎴愬搧-涓嶉渶瑕佸鏍�
-                    if (qualityInspect.getInspectType().equals(2)) {
+                    if (qualityInspect.getInspectType().equals(1)) {
                         stockUtils.addStockNoReview(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
                     } else {
-                        stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
+                        String processCategory = finishedProductStockDimensionResolver.resolveProcessCategory(qualityInspect.getProductMainId());
+                        String voltage = finishedProductStockDimensionResolver.resolveVoltage(qualityInspect.getProductMainId());
+                        stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId(), processCategory, voltage);
                     }
                     break;
                 case "闅旂":
@@ -196,7 +202,9 @@
         // 鍙栨秷闅旂 璋冪敤鎻愪氦鍚堟牸鐨勬帴鍙�
         QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
         if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType().equals(2)) {
-            stockUtils.addStock(modelId, unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.LIFT_THE_QUARANTINE.getCode(), unqualified.getId());
+            String processCategory = finishedProductStockDimensionResolver.resolveProcessCategory(qualityInspect.getProductMainId());
+            String voltage = finishedProductStockDimensionResolver.resolveVoltage(qualityInspect.getProductMainId());
+            stockUtils.addStock(modelId, unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.LIFT_THE_QUARANTINE.getCode(), unqualified.getId(), processCategory, voltage);
         } else {
             stockUtils.addStockNoReview(modelId, unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.LIFT_THE_QUARANTINE.getCode(), unqualified.getId());
         }
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
index 712595e..d61d854 100644
--- a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -155,13 +155,13 @@
     @PostMapping("/export")
     public void export(HttpServletResponse response, SalesLedgerDto salesLedgerDto) {
         Page page = new Page(-1,-1);
-        IPage<SalesLedger> salesLedgerIPage = listPage(page, salesLedgerDto);
-        ExcelUtil<SalesLedger> util = new ExcelUtil<SalesLedger>(SalesLedger.class);
+        IPage<SalesLedgerDto> salesLedgerIPage = listPage(page, salesLedgerDto);
+        ExcelUtil<SalesLedgerDto> util = new ExcelUtil<SalesLedgerDto>(SalesLedgerDto.class);
         if(salesLedgerIPage == null){
             util.exportExcel(response, new ArrayList<>(), "閿�鍞彴璐︽暟鎹�");
             return;
         }
-        List<SalesLedger> list = salesLedgerIPage.getRecords();
+        List<SalesLedgerDto> list = salesLedgerIPage.getRecords();
         util.exportExcel(response, list, "閿�鍞彴璐︽暟鎹�");
     }
 
@@ -174,8 +174,8 @@
         Page page = new Page();
         page.setCurrent(-1);
         page.setSize(-1);
-        IPage<SalesLedger> salesLedgerIPage = listPage(page, salesLedgerDto);
-        ExcelUtil<SalesLedger> util = new ExcelUtil<SalesLedger>(SalesLedger.class);
+        IPage<SalesLedgerDto> salesLedgerIPage = listPage(page, salesLedgerDto);
+        ExcelUtil<SalesLedgerDto> util = new ExcelUtil<SalesLedgerDto>(SalesLedgerDto.class);
         util.exportExcel(response, salesLedgerIPage == null ? new ArrayList<>() : salesLedgerIPage.getRecords(), "瀵煎嚭寮�绁ㄧ櫥璁板垪琛�");
     }
 
@@ -270,8 +270,8 @@
      * 鏌ヨ閿�鍞彴璐﹀垪琛�
      */
     @GetMapping("/listPage")
-    public IPage<SalesLedger> listPage(Page page, SalesLedgerDto salesLedgerDto) {
-        IPage<SalesLedger> iPage = salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto);
+    public IPage<SalesLedgerDto> listPage(Page page, SalesLedgerDto salesLedgerDto) {
+        IPage<SalesLedgerDto> iPage = salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto);
 
         //  鏌ヨ缁撴灉涓虹┖,鐩存帴杩斿洖
         if (CollectionUtils.isEmpty(iPage.getRecords())) {
@@ -362,4 +362,4 @@
     public R getSalesLedgerWithProductsLoss(Long salesLedgerId) {
         return R.ok(salesLedgerService.getSalesLedgerWithProductsLoss(salesLedgerId));
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java b/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
index d4f6460..e2d7955 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -1,37 +1,23 @@
 package com.ruoyi.sales.dto;
 
 import com.baomidou.mybatisplus.annotation.TableField;
-import com.fasterxml.jackson.annotation.JsonAlias;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.sales.pojo.CommonFile;
+import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import java.time.LocalDate;
-import java.util.Date;
 import java.util.List;
 
 @Data
-public class SalesLedgerDto {
-
-    private Long id;
-    private String salesContractNo;
-    private String customerContractNo;
-    private String projectName;
-    @JsonFormat(pattern = "yyyy-MM-dd")
-    private Date entryDate;
+@EqualsAndHashCode(callSuper = true)
+public class SalesLedgerDto extends SalesLedger {
 
     private String entryDateStart;
     private String entryDateEnd;
-
-
-    private String salesman;
-    private Long customerId;
-    private String customerName;
-    private String entryPerson;
-    private String remarks;
-    private String attachmentMaterials;
 
     @TableField(exist = false)
     @JsonFormat(pattern = "yyyy-MM-dd")
@@ -39,9 +25,9 @@
 
     @TableField(exist = false)
     private String invoiceNo;
+
     @TableField(exist = false)
     private String createUser;
-
 
     private Boolean hasChildren = false;
     private List<SalesLedgerProduct> productData;
@@ -49,18 +35,17 @@
     private List<CommonFile> SalesLedgerFiles;
 
     private Integer type;
-    @ApiModelProperty(value = "绛捐鏃ユ湡")
-    private LocalDate executionDate;
-
     private Boolean status;
 
-    @ApiModelProperty(value = "浠樻鏂瑰紡")
-    private String paymentMethod;
+    @TableField(exist = false)
+    @ApiModelProperty(value = "鍨嬪彿")
+    private String model;
 
-    @ApiModelProperty(value = "浜よ揣鏃ユ湡")
-    private LocalDate deliveryDate;
+    @TableField(exist = false)
+    @ApiModelProperty(value = "鐢靛帇")
+    private String voltage;
 
-    @ApiModelProperty(value = "鏄惁鐢熶骇")
-    @JsonAlias("isProduce")
-    private Boolean produce;
+    @TableField(exist = false)
+    @ApiModelProperty(value = "c")
+    private String qty;
 }
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
index 77faa01..92e486d 100644
--- a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
@@ -52,7 +52,7 @@
      * @param salesLedgerDto
      * @return
      */
-    IPage<SalesLedger> selectSalesLedgerListPage(Page page, @Param("salesLedgerDto") SalesLedgerDto salesLedgerDto);
+    IPage<SalesLedgerDto> selectSalesLedgerListPage(Page page, @Param("salesLedgerDto") SalesLedgerDto salesLedgerDto);
 
     /**
      * 鎸夋湀浠界粺璁¤鍗曟暟銆侀攢鍞锛堟敮鎸佷骇鍝佸ぇ绫汇�佸鎴峰悕绉扮瓫閫夛級
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java b/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
index 3e2aee3..5178ad6 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -1,6 +1,7 @@
 package com.ruoyi.sales.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAlias;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import io.swagger.annotations.ApiModelProperty;
@@ -180,6 +181,7 @@
      * 鏄惁鐢熶骇
      */
     @ApiModelProperty(value = "鏄惁鐢熶骇")
+    @JsonAlias("isProduce")
     @TableField(value = "is_produce")
     private Boolean produce;
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index 3db2a37..3e7bc29 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -606,7 +606,7 @@
     }
 
     @Override
-    public IPage<SalesLedger> selectSalesLedgerListPage(Page page, SalesLedgerDto salesLedgerDto) {
+    public IPage<SalesLedgerDto> selectSalesLedgerListPage(Page page, SalesLedgerDto salesLedgerDto) {
         return salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto);
     }
 
diff --git a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
index 9617d46..8781257 100644
--- a/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
+++ b/src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -8,6 +8,7 @@
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.stock.dto.FinishedProductTreeDto;
 import com.ruoyi.stock.dto.StockInventoryDto;
 import com.ruoyi.stock.execl.StockInventoryExportData;
 import com.ruoyi.stock.service.StockInventoryService;
@@ -47,6 +48,13 @@
         return R.ok(stockInventoryDtoIPage);
     }
 
+    @GetMapping("/finishedProductList")
+    @ApiOperation("鏌ヨ鎴愬搧搴撳瓨鏍�")
+    public R finishedProductList(StockInventoryDto stockInventoryDto) {
+        List<FinishedProductTreeDto> list = stockInventoryService.finishedProductList(stockInventoryDto);
+        return R.ok(list);
+    }
+
 
     @GetMapping("/pageListCombinedStockInventory")
     @Operation(summary = "鍒嗛〉鏌ヨ鑱斿悎搴撳瓨鍒楄〃")
diff --git a/src/main/java/com/ruoyi/stock/dto/FinishedProductTreeDto.java b/src/main/java/com/ruoyi/stock/dto/FinishedProductTreeDto.java
new file mode 100644
index 0000000..9f78801
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/dto/FinishedProductTreeDto.java
@@ -0,0 +1,31 @@
+package com.ruoyi.stock.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FinishedProductTreeDto {
+
+    private Long id;
+
+    private Long parentId;
+
+    private String productName;
+
+    private String label;
+
+    private Long productId;
+
+    private Long productModelId;
+
+    private String model;
+
+    private String materialCode;
+
+    private String processCategory;
+
+    private String voltage;
+
+    private List<FinishedProductTreeDto> children;
+}
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java b/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
index 7a31272..ede69a2 100644
--- a/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
+++ b/src/main/java/com/ruoyi/stock/dto/StockInRecordDto.java
@@ -3,40 +3,39 @@
 import com.ruoyi.stock.pojo.StockInRecord;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
-
+@EqualsAndHashCode(callSuper = true)
 @Data
 public class StockInRecordDto extends StockInRecord {
-    /**
-     * 浜у搧鍚嶇О
-     */
+
+    @Schema(description = "浜у搧鍚嶇О")
     private String productName;
-    /**
-     * 浜у搧瑙勬牸
-     */
+
+    @Schema(description = "浜у搧鍨嬪彿")
     private String model;
-    /**
-     * 浜у搧鏂欏彿
-     */
+
+    @Schema(description = "鐗╂枡缂栫爜")
     private String materialCode;
-    /**
-     * 浜у搧鍗曚綅
-     */
+
+    @Schema(description = "鍗曚綅")
     private String unit;
 
+    @Schema(description = "鏃堕棿瀛楃涓�")
     private String timeStr;
 
+    @Schema(description = "鍒涘缓浜�")
     private String createBy;
 
-    //鐜板瓨閲�
+    @Schema(description = "褰撳墠搴撳瓨")
     private String currentStock;
 
-    //璁㈠崟
+    @Schema(description = "璁㈠崟缂栧彿")
     private String npsNo;
 
-    //宸ュ崟
+    @Schema(description = "宸ュ崟缂栧彿")
     private String workOrderNo;
 
-    // 椤堕儴鐖朵骇鍝乮d
+    @Schema(description = "椤剁骇鐖剁骇浜у搧ID")
     private Long topParentProductId;
 }
diff --git a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
index 27ea5f8..40de24a 100644
--- a/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
+++ b/src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -17,21 +17,17 @@
     private String unit;
     private String materialCode;
     private String productModelName;
-
-    //鍏ュ簱绫诲瀷
     private String recordType;
-
-    //鍏ュ簱绫诲瀷瀵瑰簲鐨刬d
     private Long recordId;
 
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate reportDate;
 
-    //搴撳瓨鏈堟姤鏌ヨ瀛楁
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate startMonth;
+
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate endMonth;
@@ -39,16 +35,13 @@
     private BigDecimal totalStockIn;
     private BigDecimal totalStockOut;
     private BigDecimal currentStock;
-
-    private BigDecimal  unLockedQuantity;
-
-    //浜у搧id
+    private BigDecimal unLockedQuantity;
     private Long productId;
 
-    @Schema(description = "椤堕儴鐖朵骇鍝乮d")
+    @Schema(description = "椤剁骇鐖剁骇浜у搧ID")
     private Long topParentProductId;
 
-    @Schema(description = "搴撳瓨绫诲瀷锛歲ualified(鍚堟牸)銆乽nqualified(涓嶅悎鏍�)")
+    @Schema(description = "搴撳瓨绫诲瀷")
     private String stockType;
 
     @Schema(description = "鍚堟牸搴撳瓨鏁伴噺")
@@ -57,16 +50,16 @@
     @Schema(description = "涓嶅悎鏍煎簱瀛樻暟閲�")
     private BigDecimal unQualifiedQuantity;
 
-    @Schema(description = "鍚堟牸搴撳瓨鍐荤粨鏁伴噺")
+    @Schema(description = "鍚堟牸鍐荤粨鏁伴噺")
     private BigDecimal qualifiedLockedQuantity;
 
-    @Schema(description = "涓嶅悎鏍煎簱瀛樺喕缁撴暟閲�")
+    @Schema(description = "涓嶅悎鏍煎喕缁撴暟閲�")
     private BigDecimal unQualifiedLockedQuantity;
 
-    @Schema(description = "鍚堟牸搴撳瓨鏈喕缁撴暟閲�")
+    @Schema(description = "鍚堟牸鍙敤鏁伴噺")
     private BigDecimal qualifiedUnLockedQuantity;
 
-    @Schema(description = "涓嶅悎鏍煎簱瀛樻湭鍐荤粨鏁伴噺")
+    @Schema(description = "涓嶅悎鏍煎彲鐢ㄦ暟閲�")
     private BigDecimal unQualifiedUnLockedQuantity;
 
     @Schema(description = "鍚堟牸搴撳瓨ID")
@@ -75,9 +68,10 @@
     @Schema(description = "涓嶅悎鏍煎簱瀛業D")
     private Long unQualifiedId;
 
-    @Schema(description = "鍚堟牸搴撳瓨鎵瑰彿")
+    @Schema(description = "鍚堟牸鎵规鍙�")
     private String qualifiedBatchNo;
 
-    @Schema(description = "涓嶅悎鏍煎簱瀛樻壒鍙�")
+    @Schema(description = "涓嶅悎鏍兼壒娆″彿")
     private String unQualifiedBatchNo;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
index a1424b3..41fc669 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInRecordExportData.java
@@ -12,19 +12,32 @@
 
     @Excel(name = "鍏ュ簱鎵规")
     private String inboundBatches;
+
     @Excel(name = "浜у搧鍚嶇О")
     private String productName;
-    @Excel(name = "瑙勬牸鍨嬪彿")
+
+    @Excel(name = "鍨嬪彿")
     private String model;
-    @Excel(name = "鏂欏彿")
+
+    @Excel(name = "鐗╂枡缂栫爜")
     private String materialCode;
+
     @Excel(name = "鍗曚綅")
     private String unit;
-    @Excel(name = "鍏ュ簱鏉ユ簮")
+
+    @Excel(name = "鎴愬搧绫诲埆")
+    private String processCategory;
+
+    @Excel(name = "鐢靛帇")
+    private String voltage;
+
+    @Excel(name = "璁板綍绫诲瀷")
     private String recordType;
+
     @Excel(name = "鍏ュ簱鏁伴噺")
     private String stockInNum;
-    @Excel(name = "鍏ュ簱鏃堕棿")
+
+    @Excel(name = "鍒涘缓鏃堕棿")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
index 93249c1..128d708 100644
--- a/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
+++ b/src/main/java/com/ruoyi/stock/execl/StockInventoryExportData.java
@@ -10,14 +10,20 @@
     @Excel(name = "浜у搧鍚嶇О")
     private String productName;
 
-    @Excel(name = "瑙勬牸")
+    @Excel(name = "鍨嬪彿")
     private String model;
 
     @Excel(name = "鍗曚綅")
     private String unit;
 
-    @Excel(name = "鏂欏彿")
+    @Excel(name = "鐗╂枡缂栫爜")
     private String materialCode;
+
+    @Excel(name = "鎴愬搧绫诲埆")
+    private String processCategory;
+
+    @Excel(name = "鐢靛帇")
+    private String voltage;
 
     @Excel(name = "鍚堟牸搴撳瓨鎵瑰彿")
     private String qualifiedBatchNo;
@@ -42,11 +48,4 @@
 
     @Excel(name = "澶囨敞")
     private String remark;
-//
-//    @Excel(name = "鏈�鏂版洿鏂版椂闂�")
-//    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-//    @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 f5ed7d9..29e85d9 100644
--- a/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
+++ b/src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -52,4 +52,6 @@
     BigDecimal selectTotalByDate(@Param("now") LocalDate now);
 
     List<StockInventoryDto> selectProductList();
+
+    List<StockInventoryDto> selectFinishedProductInventoryList(@Param("ew") StockInventoryDto stockInventoryDto);
 }
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java b/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
index 2cde6c5..6651361 100644
--- a/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
+++ b/src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -78,4 +78,10 @@
 
     @ApiModelProperty("鎵瑰彿")
     private String batchNo;
+
+    @ApiModelProperty("宸ヨ壓绫诲埆")
+    private String processCategory;
+
+    @ApiModelProperty("鐢靛帇")
+    private String voltage;
 }
diff --git a/src/main/java/com/ruoyi/stock/pojo/StockInventory.java b/src/main/java/com/ruoyi/stock/pojo/StockInventory.java
index 3dfd7dd..0d1154d 100644
--- a/src/main/java/com/ruoyi/stock/pojo/StockInventory.java
+++ b/src/main/java/com/ruoyi/stock/pojo/StockInventory.java
@@ -64,6 +64,12 @@
     @ApiModelProperty("澶囨敞")
     private String remark;
 
+    @ApiModelProperty("宸ヨ壓绫诲埆")
+    private String processCategory;
+
+    @ApiModelProperty("鐢靛帇")
+    private String voltage;
+
     @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 1b5b479..ddf851c 100644
--- a/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
+++ b/src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.stock.dto.FinishedProductTreeDto;
 import com.ruoyi.stock.dto.StockInRecordDto;
 import com.ruoyi.stock.dto.StockInventoryDto;
 import com.ruoyi.stock.pojo.StockInRecord;
@@ -12,6 +13,7 @@
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 
 /**
  * <p>
@@ -25,6 +27,8 @@
 
     IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto);
 
+    List<FinishedProductTreeDto> finishedProductList(StockInventoryDto stockInventoryDto);
+
     IPage<StockInventoryDto> pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto);
 
     Boolean addstockInventory(StockInventoryDto stockInventoryDto);
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
index 4d3ca12..32e5259 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -10,12 +10,11 @@
 import com.ruoyi.common.utils.EnumUtil;
 import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.stock.dto.StockInRecordDto;
-import com.ruoyi.stock.dto.StockInventoryDto;
-import com.ruoyi.stock.dto.StockUninventoryDto;
 import com.ruoyi.stock.execl.StockInRecordExportData;
 import com.ruoyi.stock.mapper.StockInRecordMapper;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
@@ -30,6 +29,8 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.List;
 
 @Service
@@ -49,7 +50,6 @@
         return stockInRecordMapper.listPage(page, stockInRecordDto);
     }
 
-    // 鏂板鍏ュ簱
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long add(StockInRecordDto stockInRecordDto) {
@@ -57,18 +57,14 @@
         stockInRecordDto.setInboundBatches(no);
         StockInRecord stockInRecord = new StockInRecord();
         BeanUtils.copyProperties(stockInRecordDto, stockInRecord);
-
         int insertRows = stockInRecordMapper.insert(stockInRecord);
-
         Long insertId = stockInRecord.getId();
-
         return insertRows > 0 ? insertId : null;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int update(Long id, StockInRecordDto stockInRecordDto) {
-        // 鍒ゆ柇瀵硅薄鏄惁瀛樺湪
         StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
         if (stockInRecord == null) {
             throw new BaseException("璇ュ叆搴撹褰曚笉瀛樺湪,鏃犳硶鏇存柊!!!");
@@ -84,33 +80,28 @@
     public int batchDelete(List<Long> ids) {
         for (Long id : ids) {
             StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
-            if (stockInRecord.getType().equals("0")) {
-                StockInventory stockInventory = stockInventoryMapper.selectOne(
-                        new LambdaQueryWrapper<StockInventory>()
-                                .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId())
-                                .eq(StockInventory::getBatchNo, stockInRecord.getBatchNo())
-                );
+            if ("0".equals(stockInRecord.getType())) {
+                StockInventory stockInventory = findQualifiedInventory(stockInRecord);
                 if (stockInventory == null) {
-                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
-                } else {
-                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
-                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
-                    stockInRecordDto.setQualitity(stockInRecord.getStockInNum());
-                    stockInventoryMapper.updateSubtractStockInventory(stockInRecordDto);
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧锛屾棤娉曞垹闄�!!!");
                 }
-            } else if (stockInRecord.getType().equals("1")) {
+                stockInventory.setQualitity(defaultDecimal(stockInventory.getQualitity()).subtract(defaultDecimal(stockInRecord.getStockInNum())));
+                stockInventory.setVersion(stockInventory.getVersion() == null ? 1 : stockInventory.getVersion() + 1);
+                stockInventory.setUpdateTime(LocalDateTime.now());
+                stockInventoryMapper.updateById(stockInventory);
+            } else if ("1".equals(stockInRecord.getType())) {
                 StockUninventory stockUninventory = stockUninventoryMapper.selectOne(
                         new LambdaQueryWrapper<StockUninventory>()
                                 .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId())
-                                .eq(StockUninventory::getBatchNo, stockInRecord.getBatchNo()));
+                                .eq(StockUninventory::getBatchNo, stockInRecord.getBatchNo())
+                );
                 if (stockUninventory == null) {
-                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧,鏃犳硶鍒犻櫎!!!");
-                } else {
-                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
-                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
-                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
-                    stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
+                    throw new BaseException("搴撳瓨璁板綍涓病鏈夊搴旂殑浜у搧锛屾棤娉曞垹闄�!!!");
                 }
+                stockUninventory.setQualitity(defaultDecimal(stockUninventory.getQualitity()).subtract(defaultDecimal(stockInRecord.getStockInNum())));
+                stockUninventory.setVersion(stockUninventory.getVersion() == null ? 1 : stockUninventory.getVersion() + 1);
+                stockUninventory.setUpdateTime(LocalDateTime.now());
+                stockUninventoryMapper.updateById(stockUninventory);
             }
         }
         return stockInRecordMapper.deleteBatchIds(ids);
@@ -120,10 +111,14 @@
     public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
         List<StockInRecordExportData> list = stockInRecordMapper.listStockInRecordExportData(stockInRecordDto);
         for (StockInRecordExportData stockInRecordExportData : list) {
-            if (stockInRecordExportData.getType().equals("0")) {
-                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+            if ("0".equals(stockInRecordExportData.getType())) {
+                stockInRecordExportData.setRecordType(
+                        EnumUtil.fromCode(StockInQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue()
+                );
             } else {
-                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
+                stockInRecordExportData.setRecordType(
+                        EnumUtil.fromCode(StockInUnQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue()
+                );
             }
         }
         ExcelUtil<StockInRecordExportData> util = new ExcelUtil<>(StockInRecordExportData.class);
@@ -139,11 +134,30 @@
     public int updateStockInRecord(StockInRecordDto stockInRecordDto) {
         LoginUser loginUser = SecurityUtils.getLoginUser();
         try {
-            stockInventoryService.addApproveByPurchase(loginUser, stockInRecordDto,stockInRecordDto.getId());
+            stockInventoryService.addApproveByPurchase(loginUser, stockInRecordDto, stockInRecordDto.getId());
         } catch (Exception e) {
             e.printStackTrace();
         }
         stockInRecordDto.setApproveStatus(1);
         return stockInRecordMapper.updateById(stockInRecordDto);
     }
+
+    // 鍚堟牸鍏ュ簱鍥為��鎸変笌搴撳瓨鍚堝苟涓�鑷寸殑鍞竴閿煡鎵俱��
+    private StockInventory findQualifiedInventory(StockInRecord stockInRecord) {
+        LambdaQueryWrapper<StockInventory> queryWrapper = new LambdaQueryWrapper<StockInventory>()
+                .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId())
+                .orderByAsc(StockInventory::getId);
+        String processCategory = StringUtils.trimToEmpty(stockInRecord.getProcessCategory());
+        String voltage = StringUtils.trimToEmpty(stockInRecord.getVoltage());
+        if (StringUtils.isNotBlank(processCategory) || StringUtils.isNotBlank(voltage)) {
+            queryWrapper.eq(StockInventory::getProcessCategory, processCategory);
+            queryWrapper.eq(StockInventory::getVoltage, voltage);
+        }
+        List<StockInventory> inventories = stockInventoryMapper.selectList(queryWrapper);
+        return inventories.isEmpty() ? null : inventories.get(0);
+    }
+
+    private BigDecimal defaultDecimal(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
+    }
 }
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 150d2f8..5a2681f 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -2,7 +2,6 @@
 
 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;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -16,9 +15,12 @@
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
 import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.stock.dto.FinishedProductTreeDto;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import com.ruoyi.stock.dto.StockInRecordDto;
@@ -43,18 +45,15 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * <p>
- * 搴撳瓨琛� 鏈嶅姟瀹炵幇绫�
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-01-21 04:16:36
- */
 @Service
 public class StockInventoryServiceImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryService {
 
@@ -80,17 +79,54 @@
         return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
     }
 
+    /**
+     * 鏌ヨ鎴愬搧搴撳瓨鏍戙��
+     * 杩斿洖缁撴瀯娌跨敤鍩虹璧勬枡浜у搧鏍戯紝鍙跺瓙鑺傜偣琛ュ厖鎴愬搧搴撳瓨缁村害锛氬瀷鍙枫�佸伐搴忓垎绫汇�佺數鍘嬨��
+     */
+    @Override
+    public List<FinishedProductTreeDto> finishedProductList(StockInventoryDto stockInventoryDto) {
+        List<StockInventoryDto> inventoryList = stockInventoryMapper.selectFinishedProductInventoryList(stockInventoryDto);
+        if (inventoryList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        List<Product> allProducts = productMapper.selectList(null);
+        Map<Long, Product> productMap = new HashMap<>();
+        Map<Long, List<Product>> childrenMap = new HashMap<>();
+        for (Product product : allProducts) {
+            productMap.put(product.getId(), product);
+            childrenMap.computeIfAbsent(product.getParentId(), key -> new ArrayList<>()).add(product);
+        }
+
+        Map<Long, List<FinishedProductTreeDto>> leafMap = buildFinishedProductLeafMap(inventoryList);
+        Set<Long> visibleProductIds = collectVisibleProductIds(leafMap.keySet(), productMap);
+
+        List<FinishedProductTreeDto> result = new ArrayList<>();
+        for (Product rootProduct : allProducts) {
+            if (!isFinishedRoot(rootProduct)) {
+                continue;
+            }
+            FinishedProductTreeDto rootNode = buildProductNode(rootProduct);
+            rootNode.setChildren(buildFinishedChildren(rootProduct.getId(), childrenMap, leafMap, visibleProductIds));
+            if (!rootNode.getChildren().isEmpty()) {
+                result.add(rootNode);
+            }
+        }
+        return result;
+    }
+
     @Override
     public IPage<StockInventoryDto> pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto) {
         return stockInventoryMapper.pageListCombinedStockInventory(page, stockInventoryDto);
     }
 
-    //鍏ュ簱璋冪敤-娣诲姞鍏ュ簱璁板綍
+    /**
+     * 鍚堟牸鍏ュ簱锛氬厛鐢熸垚鍏ュ簱璁板綍锛屽啀璧板鎵规祦銆�
+     */
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean addstockInventory(StockInventoryDto stockInventoryDto) {
         List<StockInventory> stockInventoryList = stockInventoryMapper.selectList(null);
-        //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
         StockInRecordDto stockInRecordDto = new StockInRecordDto();
         stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
         stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
@@ -99,22 +135,17 @@
         stockInRecordDto.setRemark(stockInventoryDto.getRemark());
         stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
         stockInRecordDto.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+        stockInRecordDto.setProcessCategory(normalizeDimension(stockInventoryDto.getProcessCategory()));
+        stockInRecordDto.setVoltage(normalizeDimension(stockInventoryDto.getVoltage()));
         stockInRecordDto.setApproveStatus(0);
         stockInRecordDto.setType("0");
-        if (stockInventoryDto.getBatchNo() == null || stockInventoryDto.getBatchNo().isEmpty()) {
+        if (StringUtils.isBlank(stockInventoryDto.getBatchNo())) {
             String batchNo;
-            // 鑾峰彇褰撳墠鏈堜唤锛堜袱浣嶏級
             LocalDate now = LocalDate.now();
             String monthFlag = now.format(DateTimeFormatter.ofPattern("MM"));
-
-            // 鑾峰彇褰撳墠鏈堜唤鐨勬渶澶ф祦姘村彿
             int maxSeq = getCurrentMonthMaxSeq(stockInventoryDto, monthFlag, stockInventoryList);
-
-            // 鏂版祦姘村彿 = 鏈�澶ф祦姘村彿 + 1
             int newSeq = maxSeq + 1;
             String seqStr = String.format("%03d", newSeq);
-
-            // 缁勮batchNo
             ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId());
             batchNo = stockInventoryDto.getMaterialCode() + productModel.getModel() + "P" + monthFlag + seqStr;
             stockInRecordDto.setBatchNo(batchNo);
@@ -133,21 +164,15 @@
         return true;
     }
 
-    /**
-     * 鏌ヨ褰撳墠鏈堜唤宸插瓨鍦ㄧ殑鏈�澶ф祦姘村彿
-     */
     private static int getCurrentMonthMaxSeq(StockInventoryDto dto, String monthFlag, List<StockInventory> existingList) {
         int maxSeq = 0;
-
         String prefix = dto.getMaterialCode() + dto.getProductModelName() + "P" + monthFlag;
-
-        // 姝e垯鍖归厤锛氬墠缂� + 3浣嶆暟瀛�
         Pattern pattern = Pattern.compile(Pattern.quote(prefix) + "(\\d{3})");
-
         for (StockInventory item : existingList) {
             String batchNo = item.getBatchNo();
-            if (batchNo == null) continue;
-
+            if (batchNo == null) {
+                continue;
+            }
             Matcher matcher = pattern.matcher(batchNo);
             if (matcher.find()) {
                 int seq = Integer.parseInt(matcher.group(1));
@@ -156,10 +181,10 @@
                 }
             }
         }
-
         return maxSeq;
     }
 
+    @Override
     public void addApproveByPurchase(LoginUser loginUser, StockInRecordDto stockInRecordDto, Long id) throws Exception {
         ApproveProcessVO approveProcessVO = new ApproveProcessVO();
         approveProcessVO.setApproveType(9);
@@ -175,100 +200,77 @@
     }
 
     /**
-     * 瀹為檯鍏ュ簱
-     *
-     * @param stockInRecord
+     * 鎸夊簱瀛樺敮涓�閿悎骞跺簱瀛樸��
+     * 鎴愬搧浣跨敤 product_model_id + processCategory + voltage锛涘叾浠栧叆搴撳彧鎸� product_model_id銆�
      */
+    @Override
     public void updateOrCreateStockInventory(StockInRecord stockInRecord) {
-        // 鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪
-        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);
+        String processCategory = normalizeDimension(stockInRecord.getProcessCategory());
+        String voltage = normalizeDimension(stockInRecord.getVoltage());
+        StockInventory oldStockInventory = findInventoryForMerge(
+                stockInRecord.getProductModelId(),
+                processCategory,
+                voltage
+        );
 
         if (ObjectUtils.isEmpty(oldStockInventory)) {
-            // 涓嶅瓨鍦ㄥ垯鏂板
             StockInventory newStockInventory = new StockInventory();
             newStockInventory.setProductModelId(stockInRecord.getProductModelId());
-            newStockInventory.setQualitity(stockInRecord.getStockInNum());
+            newStockInventory.setQualitity(defaultDecimal(stockInRecord.getStockInNum()));
             newStockInventory.setVersion(1);
             newStockInventory.setRemark(stockInRecord.getRemark());
-            newStockInventory.setLockedQuantity(stockInRecord.getLockedQuantity());
+            newStockInventory.setLockedQuantity(defaultDecimal(stockInRecord.getLockedQuantity()));
             newStockInventory.setWarnNum(stockInRecord.getWarnNum());
-            newStockInventory.setBatchNo(stockInRecord.getBatchNo());
+            newStockInventory.setProcessCategory(processCategory);
+            newStockInventory.setVoltage(voltage);
+            newStockInventory.setBatchNo(StringUtils.trimToEmpty(stockInRecord.getBatchNo()));
             stockInventoryMapper.insert(newStockInventory);
-        } else {
-            // 瀛樺湪鍒欐洿鏂�
-            LambdaUpdateWrapper<StockInventory> updateWrapper = new LambdaUpdateWrapper<>();
-            updateWrapper
-                    .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")
-                    .set(stockInRecord.getRemark() != null && !stockInRecord.getRemark().isEmpty(),
-                            StockInventory::getRemark, stockInRecord.getRemark())
-                    .set(stockInRecord.getWarnNum() != null,
-                            StockInventory::getWarnNum, stockInRecord.getWarnNum())
-                    .setSql(stockInRecord.getLockedQuantity() != null,
-                            "locked_quantity = locked_quantity + " + stockInRecord.getLockedQuantity())
-                    .set(StockInventory::getUpdateTime, new Date());
-
-            stockInventoryMapper.update(null, updateWrapper);
+            return;
         }
+
+        oldStockInventory.setQualitity(defaultDecimal(oldStockInventory.getQualitity()).add(defaultDecimal(stockInRecord.getStockInNum())));
+        oldStockInventory.setVersion(oldStockInventory.getVersion() == null ? 1 : oldStockInventory.getVersion() + 1);
+        if (StringUtils.isNotBlank(stockInRecord.getRemark())) {
+            oldStockInventory.setRemark(stockInRecord.getRemark());
+        }
+        if (stockInRecord.getWarnNum() != null) {
+            oldStockInventory.setWarnNum(stockInRecord.getWarnNum());
+        }
+        if (stockInRecord.getLockedQuantity() != null) {
+            oldStockInventory.setLockedQuantity(defaultDecimal(oldStockInventory.getLockedQuantity()).add(stockInRecord.getLockedQuantity()));
+        }
+        oldStockInventory.setProcessCategory(processCategory);
+        oldStockInventory.setVoltage(voltage);
+        oldStockInventory.setUpdateTime(LocalDateTime.now());
+        stockInventoryMapper.updateById(oldStockInventory);
     }
 
-    //鍗婃垚鍝佺洿鎺ュ叆搴�
+    /**
+     * 涓嶅鏍稿叆搴擄紝鐩存帴鍐欏叆鍏ュ簱璁板綍鍜屽簱瀛樸��
+     */
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean addstockInventoryNoReview(StockInventoryDto stockInventoryDto) {
-        //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
         StockInRecordDto stockInRecordDto = new StockInRecordDto();
         stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
         stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
         stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
         stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
+        stockInRecordDto.setRemark(stockInventoryDto.getRemark());
+        stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
+        stockInRecordDto.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+        stockInRecordDto.setProcessCategory(normalizeDimension(stockInventoryDto.getProcessCategory()));
+        stockInRecordDto.setVoltage(normalizeDimension(stockInventoryDto.getVoltage()));
+        stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
         stockInRecordDto.setType("0");
         stockInRecordService.add(stockInRecordDto);
-        //鍐嶈繘琛屾柊澧炲簱瀛樻暟閲忓簱瀛�
-        //鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪锛屼笉瀛樺湪鏂板锛屽瓨鍦ㄦ洿鏂�
-        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())
-                .eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo()));
-        if (ObjectUtils.isEmpty(oldStockInventory)) {
-            StockInventory newStockInventory = new StockInventory();
-            newStockInventory.setProductModelId(stockInventoryDto.getProductModelId());
-            newStockInventory.setQualitity(stockInventoryDto.getQualitity());
-            newStockInventory.setVersion(1);
-            newStockInventory.setRemark(stockInventoryDto.getRemark());
-            newStockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
-            newStockInventory.setWarnNum(stockInventoryDto.getWarnNum());
-            stockInventoryMapper.insert(newStockInventory);
-        } else {
-            stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
-        }
+        updateOrCreateStockInventory(toStockInRecord(stockInRecordDto));
         return true;
     }
 
-    //鍑哄簱璋冪敤
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) {
-        //  鏂板鍑哄簱璁板綍
         StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
         stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId());
         stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType());
@@ -276,7 +278,8 @@
         stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
         stockOutRecordDto.setType("0");
         stockOutRecordService.add(stockOutRecordDto);
-        if (ObjectUtils.isEmpty(stockInventoryDto.getBatchNo())) {
+
+        if (StringUtils.isBlank(stockInventoryDto.getBatchNo()) && !usesDimensionIdentity(normalizeDimension(stockInventoryDto.getProcessCategory()), normalizeDimension(stockInventoryDto.getVoltage()))) {
             List<StockInventory> stockInventories = stockInventoryMapper.selectList(new QueryWrapper<StockInventory>().lambda()
                     .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())
                     .orderByAsc(StockInventory::getId));
@@ -286,14 +289,14 @@
 
             BigDecimal remainingQty = stockInventoryDto.getQualitity();
             for (StockInventory stockInventory : stockInventories) {
-                BigDecimal lockedQty = stockInventory.getLockedQuantity() == null ? BigDecimal.ZERO : stockInventory.getLockedQuantity();
-                BigDecimal availableQty = stockInventory.getQualitity().subtract(lockedQty);
+                BigDecimal lockedQty = defaultDecimal(stockInventory.getLockedQuantity());
+                BigDecimal availableQty = defaultDecimal(stockInventory.getQualitity()).subtract(lockedQty);
                 if (availableQty.compareTo(BigDecimal.ZERO) <= 0) {
                     continue;
                 }
 
                 BigDecimal deductQty = remainingQty.min(availableQty);
-                stockInventory.setQualitity(stockInventory.getQualitity().subtract(deductQty));
+                stockInventory.setQualitity(defaultDecimal(stockInventory.getQualitity()).subtract(deductQty));
                 stockInventory.setVersion(stockInventory.getVersion() == null ? 1 : stockInventory.getVersion() + 1);
                 stockInventory.setUpdateTime(LocalDateTime.now());
                 stockInventoryMapper.updateById(stockInventory);
@@ -306,55 +309,51 @@
 
             ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId());
             Product product = productMapper.selectById(productModel.getProductId());
-            throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "搴撳瓨涓嶈冻鏃犳硶鍑哄簱");
+            throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "搴撳瓨涓嶈冻锛屾棤娉曞嚭搴�");
         }
-        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())
-                .eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo()));
+
+        StockInventory oldStockInventory = findInventoryForMerge(
+                stockInventoryDto.getProductModelId(),
+                stockInventoryDto.getProcessCategory(),
+                stockInventoryDto.getVoltage()
+        );
         if (ObjectUtils.isEmpty(oldStockInventory)) {
             throw new RuntimeException("浜у搧搴撳瓨涓嶅瓨鍦�");
         }
-        BigDecimal lockedQty = oldStockInventory.getLockedQuantity();
-        if (lockedQty == null) {
-            lockedQty = BigDecimal.ZERO;
-        }
-        if (stockInventoryDto.getQualitity().compareTo(oldStockInventory.getQualitity().subtract(lockedQty)) > 0) {
-            // 鏌ヨ浜у搧瑙勬牸鍚�
+        BigDecimal lockedQty = defaultDecimal(oldStockInventory.getLockedQuantity());
+        if (stockInventoryDto.getQualitity().compareTo(defaultDecimal(oldStockInventory.getQualitity()).subtract(lockedQty)) > 0) {
             ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId());
             Product product = productMapper.selectById(productModel.getProductId());
-            throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "搴撳瓨涓嶈冻鏃犳硶鍑哄簱");
+            throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "搴撳瓨涓嶈冻锛屾棤娉曞嚭搴�");
         }
 
-        stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
+        oldStockInventory.setQualitity(defaultDecimal(oldStockInventory.getQualitity()).subtract(stockInventoryDto.getQualitity()));
+        oldStockInventory.setVersion(oldStockInventory.getVersion() == null ? 1 : oldStockInventory.getVersion() + 1);
+        oldStockInventory.setUpdateTime(LocalDateTime.now());
+        stockInventoryMapper.updateById(oldStockInventory);
         return true;
     }
 
     @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);
+            ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
             List<StockInventoryExportData> list = util.importExcel(file.getInputStream());
-
-            // 璁板綍鏈壘鍒板尮閰嶉」鐨勬暟鎹�
             List<String> unmatchedRecords = new ArrayList<>();
-            // 璁板綍澶勭悊缁撴灉
             int successCount = 0;
 
             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);
@@ -363,8 +362,9 @@
                         stockInventoryDto.setRemark(dto.getRemark());
                         stockInventoryDto.setWarnNum(dto.getWarnNum());
                         stockInventoryDto.setBatchNo(dto.getQualifiedBatchNo());
+                        stockInventoryDto.setProcessCategory(dto.getProcessCategory());
+                        stockInventoryDto.setVoltage(dto.getVoltage());
 
-                        // 楠岃瘉鍚堟牸鍐荤粨鏁伴噺
                         if (ObjectUtils.isNotEmpty(dto.getQualifiedLockedQuantity())) {
                             if (dto.getQualifiedLockedQuantity().compareTo(dto.getQualifiedQuantity()) > 0) {
                                 throw new RuntimeException("鍚堟牸鍐荤粨鏁伴噺涓嶈兘瓒呰繃鏈瀵煎叆鐨勫悎鏍煎簱瀛樻暟閲�");
@@ -379,7 +379,6 @@
                         successCount++;
                     }
 
-                    // 澶勭悊涓嶅悎鏍煎簱瀛�
                     if (dto.getUnQualifiedQuantity() != null && dto.getUnQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
                         StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                         stockUninventoryDto.setRecordId(0L);
@@ -388,7 +387,6 @@
                         stockUninventoryDto.setRemark(dto.getRemark());
                         stockUninventoryDto.setBatchNo(dto.getUnQualifiedBatchNo());
 
-                        // 楠岃瘉涓嶅悎鏍煎喕缁撴暟閲�
                         if (ObjectUtils.isNotEmpty(dto.getUnQualifiedLockedQuantity())) {
                             if (dto.getUnQualifiedLockedQuantity().compareTo(dto.getUnQualifiedQuantity()) > 0) {
                                 throw new RuntimeException("涓嶅悎鏍煎喕缁撴暟閲忎笉鑳借秴杩囨湰娆″鍏ョ殑涓嶅悎鏍煎簱瀛樻暟閲�");
@@ -403,16 +401,14 @@
                         successCount++;
                     }
                 } else {
-                    // 璁板綍鏈尮閰嶇殑浜у搧
-                    String unmatchedRecord = "浜у搧鍚嶇О锛�" + dto.getProductName() + "锛屽瀷鍙凤細" + dto.getModel();
+                    String unmatchedRecord = "浜у搧鍚嶇О锛�" + dto.getProductName() + "锛屽瀷鍙凤細" + dto.getModel() + " 鏈尮閰嶅埌搴撳瓨浜у搧";
                     unmatchedRecords.add(unmatchedRecord);
                 }
             }
 
-            // 鏋勫缓杩斿洖淇℃伅
             StringBuilder message = new StringBuilder();
             if (!unmatchedRecords.isEmpty()) {
-                message.append("瀵煎叆鎴愬姛 " + successCount + " 鏉¤褰曪紝浠ヤ笅浜у搧鏈壘鍒板尮閰嶉」锛歕n");
+                message.append("瀵煎叆鎴愬姛 ").append(successCount).append(" 鏉¤褰曪紝浠ヤ笅浜у搧鏈尮閰嶏細\n");
                 for (String record : unmatchedRecords) {
                     message.append(record).append("\n");
                 }
@@ -421,14 +417,13 @@
 
             return R.ok("瀵煎叆鎴愬姛锛屽叡澶勭悊 " + successCount + " 鏉¤褰�");
         } catch (Exception e) {
-            log.error("瀵煎叆搴撳瓨澶辫触", e);
-            return R.fail("瀵煎叆澶辫触锛�" + e.getMessage());
+            log.error("搴撳瓨瀵煎叆澶辫触", e);
+            return R.fail("搴撳瓨瀵煎叆澶辫触锛�" + e.getMessage());
         }
     }
 
     @Override
     public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
-
         List<StockInventoryExportData> list = stockInventoryMapper.listStockInventoryExportData(stockInventoryDto);
         ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
         util.exportExcel(response, list, "搴撳瓨淇℃伅");
@@ -467,4 +462,132 @@
         stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
         return this.updateById(stockInventory);
     }
-}
\ No newline at end of file
+
+    // 鍏ュ簱鍚堝苟鍞竴閿細鎴愬搧鎸夌被鍒拰鐢靛帇锛屽叾浣欏彧鎸変骇鍝佽鏍笺��
+    private StockInventory findInventoryForMerge(Long productModelId, String processCategory, String voltage) {
+        LambdaQueryWrapper<StockInventory> queryWrapper = new LambdaQueryWrapper<StockInventory>()
+                .eq(StockInventory::getProductModelId, productModelId)
+                .orderByAsc(StockInventory::getId);
+        if (usesDimensionIdentity(processCategory, voltage)) {
+            queryWrapper.eq(StockInventory::getProcessCategory, normalizeDimension(processCategory));
+            queryWrapper.eq(StockInventory::getVoltage, normalizeDimension(voltage));
+        }
+        List<StockInventory> inventories = stockInventoryMapper.selectList(queryWrapper);
+        return inventories.isEmpty() ? null : inventories.get(0);
+    }
+
+    // 灏嗗叆搴撹褰� DTO 澶嶅埗涓烘寔涔呭寲瀹炰綋銆�
+    private StockInRecord toStockInRecord(StockInRecordDto stockInRecordDto) {
+        StockInRecord stockInRecord = new StockInRecord();
+        stockInRecord.setRecordId(stockInRecordDto.getRecordId());
+        stockInRecord.setRecordType(stockInRecordDto.getRecordType());
+        stockInRecord.setStockInNum(stockInRecordDto.getStockInNum());
+        stockInRecord.setProductModelId(stockInRecordDto.getProductModelId());
+        stockInRecord.setRemark(stockInRecordDto.getRemark());
+        stockInRecord.setType(stockInRecordDto.getType());
+        stockInRecord.setLockedQuantity(stockInRecordDto.getLockedQuantity());
+        stockInRecord.setWarnNum(stockInRecordDto.getWarnNum());
+        stockInRecord.setApproveStatus(stockInRecordDto.getApproveStatus());
+        stockInRecord.setBatchNo(stockInRecordDto.getBatchNo());
+        stockInRecord.setProcessCategory(stockInRecordDto.getProcessCategory());
+        stockInRecord.setVoltage(stockInRecordDto.getVoltage());
+        return stockInRecord;
+    }
+
+    // 鍙甯︽湁鎴愬搧缁村害锛屽氨鎸夌淮搴︿綔涓哄敮涓�閿��
+    private boolean usesDimensionIdentity(String processCategory, String voltage) {
+        return StringUtils.isNotBlank(processCategory) || StringUtils.isNotBlank(voltage);
+    }
+
+    private String normalizeDimension(String value) {
+        return StringUtils.trimToEmpty(value);
+    }
+
+    private BigDecimal defaultDecimal(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
+    }
+
+    private Map<Long, List<FinishedProductTreeDto>> buildFinishedProductLeafMap(List<StockInventoryDto> inventoryList) {
+        Map<Long, List<FinishedProductTreeDto>> leafMap = new HashMap<>();
+        for (StockInventoryDto inventory : inventoryList) {
+            FinishedProductTreeDto leafNode = new FinishedProductTreeDto();
+            leafNode.setId(inventory.getId() == null ? null : -inventory.getId());
+            leafNode.setParentId(inventory.getProductId());
+            leafNode.setProductId(inventory.getProductId());
+            leafNode.setProductModelId(inventory.getProductModelId());
+            leafNode.setProductName(inventory.getProductName());
+            leafNode.setLabel(buildLeafLabel(inventory));
+            leafNode.setModel(inventory.getModel());
+            leafNode.setMaterialCode(inventory.getMaterialCode());
+            leafNode.setProcessCategory(inventory.getProcessCategory());
+            leafNode.setVoltage(inventory.getVoltage());
+            leafNode.setChildren(new ArrayList<>());
+            leafMap.computeIfAbsent(inventory.getProductId(), key -> new ArrayList<>()).add(leafNode);
+        }
+        return leafMap;
+    }
+
+    private Set<Long> collectVisibleProductIds(Set<Long> leafProductIds, Map<Long, Product> productMap) {
+        Set<Long> visibleIds = new HashSet<>();
+        for (Long productId : leafProductIds) {
+            Long currentId = productId;
+            while (currentId != null) {
+                Product current = productMap.get(currentId);
+                if (current == null) {
+                    break;
+                }
+                visibleIds.add(currentId);
+                currentId = current.getParentId();
+            }
+        }
+        return visibleIds;
+    }
+
+    private List<FinishedProductTreeDto> buildFinishedChildren(Long parentId,
+                                                               Map<Long, List<Product>> childrenMap,
+                                                               Map<Long, List<FinishedProductTreeDto>> leafMap,
+                                                               Set<Long> visibleProductIds) {
+        List<FinishedProductTreeDto> children = new ArrayList<>();
+        List<Product> childProducts = childrenMap.getOrDefault(parentId, new ArrayList<>());
+        for (Product childProduct : childProducts) {
+            if (!visibleProductIds.contains(childProduct.getId())) {
+                continue;
+            }
+            FinishedProductTreeDto childNode = buildProductNode(childProduct);
+            List<FinishedProductTreeDto> subChildren = buildFinishedChildren(childProduct.getId(), childrenMap, leafMap, visibleProductIds);
+            subChildren.addAll(leafMap.getOrDefault(childProduct.getId(), new ArrayList<>()));
+            childNode.setChildren(subChildren);
+            children.add(childNode);
+        }
+        return children;
+    }
+
+    private FinishedProductTreeDto buildProductNode(Product product) {
+        FinishedProductTreeDto node = new FinishedProductTreeDto();
+        BeanUtils.copyProperties(product, node);
+        node.setLabel(product.getProductName());
+        node.setChildren(new ArrayList<>());
+        return node;
+    }
+
+    private boolean isFinishedRoot(Product product) {
+        return product.getParentId() == null && "鎴愬搧".equals(StringUtils.trimToEmpty(product.getProductName()));
+    }
+
+    private String buildLeafLabel(StockInventoryDto inventory) {
+        List<String> parts = new ArrayList<>();
+        if (StringUtils.isNotBlank(inventory.getProductName())) {
+            parts.add(StringUtils.trim(inventory.getProductName()));
+        }
+        if (StringUtils.isNotBlank(inventory.getModel())) {
+            parts.add(StringUtils.trim(inventory.getModel()));
+        }
+        if (StringUtils.isNotBlank(inventory.getProcessCategory())) {
+            parts.add(StringUtils.trim(inventory.getProcessCategory()));
+        }
+        if (StringUtils.isNotBlank(inventory.getVoltage())) {
+            parts.add(StringUtils.trim(inventory.getVoltage()));
+        }
+        return String.join(" / ", parts);
+    }
+}
diff --git a/src/main/java/com/ruoyi/stock/support/FinishedProductStockDimensionResolver.java b/src/main/java/com/ruoyi/stock/support/FinishedProductStockDimensionResolver.java
new file mode 100644
index 0000000..5ca047b
--- /dev/null
+++ b/src/main/java/com/ruoyi/stock/support/FinishedProductStockDimensionResolver.java
@@ -0,0 +1,184 @@
+package com.ruoyi.stock.support;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.production.mapper.ProductProcessMapper;
+import com.ruoyi.production.mapper.ProductProcessRouteItemMapper;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
+import com.ruoyi.production.mapper.ProductionProductOutputMapper;
+import com.ruoyi.production.pojo.ProductProcess;
+import com.ruoyi.production.pojo.ProductProcessRouteItem;
+import com.ruoyi.production.pojo.ProductionProductMain;
+import com.ruoyi.production.pojo.ProductionProductOutput;
+import org.springframework.stereotype.Component;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component
+public class FinishedProductStockDimensionResolver {
+
+    private static final String PROCESS_COPPER = "鍗伴摐";
+    private static final String PROCESS_SILVER = "鍗伴摱";
+    private static final String PROCESS_CATEGORY_COPPER = "閾滄瀬";
+    private static final String PROCESS_CATEGORY_SILVER = "閾舵瀬";
+    private static final String VOLTAGE_PARAMETER = "鐢靛帇";
+
+    private final ProductionProductMainMapper productionProductMainMapper;
+    private final ProductionProductOutputMapper productionProductOutputMapper;
+    private final ProductProcessRouteItemMapper productProcessRouteItemMapper;
+    private final ProductProcessMapper productProcessMapper;
+
+    public FinishedProductStockDimensionResolver(ProductionProductMainMapper productionProductMainMapper,
+                                                 ProductionProductOutputMapper productionProductOutputMapper,
+                                                 ProductProcessRouteItemMapper productProcessRouteItemMapper,
+                                                 ProductProcessMapper productProcessMapper) {
+        this.productionProductMainMapper = productionProductMainMapper;
+        this.productionProductOutputMapper = productionProductOutputMapper;
+        this.productProcessRouteItemMapper = productProcessRouteItemMapper;
+        this.productProcessMapper = productProcessMapper;
+    }
+
+    /**
+     * 瑙f瀽鎴愬搧鍏ュ簱绫诲埆銆�
+     * 褰撳伐搴忎负鍗伴摐鏃惰繑鍥為摐鏋侊紝褰撳伐搴忎负鍗伴摱鏃惰繑鍥為摱鏋併��
+     */
+    public String resolveProcessCategory(Long productMainId) {
+        ProductionProductMain productionProductMain = loadProductionMain(productMainId);
+        ProductProcessRouteItem routeItem = loadRouteItem(productionProductMain);
+        if (routeItem.getProductRouteId() == null) {
+            throw new ServiceException("宸ヨ壓璺嚎鏈粦瀹氬伐鑹烘槑缁嗭紝鏃犳硶纭畾鎴愬搧绫诲埆");
+        }
+        return resolveProcessCategoryByRouteId(routeItem.getProductRouteId());
+    }
+
+    /**
+     * 瑙f瀽鎴愬搧鍏ュ簱鐢靛帇锛屾潵婧愪簬鎶ュ伐 otherData 涓殑鈥滅數鍘嬧�濆弬鏁般��
+     */
+    public String resolveVoltage(Long productMainId) {
+        List<ProductionProductOutput> outputs = loadOutputs(productMainId);
+        for (ProductionProductOutput output : outputs) {
+            String voltage = resolveVoltageValue(output.getOtherData());
+            if (StringUtils.isNotBlank(voltage)) {
+                return voltage;
+            }
+        }
+        throw new ServiceException("璇烽厤缃弬鏁扮數鍘�");
+    }
+
+    private ProductionProductMain loadProductionMain(Long productMainId) {
+        if (productMainId == null) {
+            throw new ServiceException("鎶ュ伐ID涓嶈兘涓虹┖");
+        }
+        ProductionProductMain productionProductMain = productionProductMainMapper.selectById(productMainId);
+        if (productionProductMain == null) {
+            throw new ServiceException("鎶ュ伐璁板綍涓嶅瓨鍦紝鏃犳硶纭畾鎴愬搧搴撳瓨缁村害");
+        }
+        return productionProductMain;
+    }
+
+    private ProductProcessRouteItem loadRouteItem(ProductionProductMain productionProductMain) {
+        if (productionProductMain.getProductProcessRouteItemId() == null) {
+            throw new ServiceException("鎶ュ伐鏈叧鑱斿伐鑹鸿矾绾挎槑缁嗭紝鏃犳硶纭畾鎴愬搧搴撳瓨缁村害");
+        }
+        ProductProcessRouteItem routeItem = productProcessRouteItemMapper.selectById(productionProductMain.getProductProcessRouteItemId());
+        if (routeItem == null) {
+            throw new ServiceException("宸ヨ壓璺嚎鏄庣粏涓嶅瓨鍦紝鏃犳硶纭畾鎴愬搧搴撳瓨缁村害");
+        }
+        return routeItem;
+    }
+
+    private List<ProductionProductOutput> loadOutputs(Long productMainId) {
+        List<ProductionProductOutput> outputs = productionProductOutputMapper.selectList(
+                new LambdaQueryWrapper<ProductionProductOutput>()
+                        .eq(ProductionProductOutput::getProductMainId, productMainId)
+        );
+        if (CollectionUtils.isEmpty(outputs)) {
+            throw new ServiceException("鎶ュ伐杈撳嚭璁板綍涓嶅瓨鍦紝鏃犳硶纭畾鎴愬搧搴撳瓨缁村害");
+        }
+        return outputs;
+    }
+
+    private String resolveProcessCategoryByRouteId(Long productRouteId) {
+        List<ProductProcessRouteItem> routeItems = productProcessRouteItemMapper.selectList(
+                new LambdaQueryWrapper<ProductProcessRouteItem>()
+                        .eq(ProductProcessRouteItem::getProductRouteId, productRouteId)
+        );
+        if (CollectionUtils.isEmpty(routeItems)) {
+            throw new ServiceException("璁㈠崟宸ヨ壓璺嚎鏈厤缃伐搴忥紝鏃犳硶纭畾鎴愬搧绫诲埆");
+        }
+        Set<Long> processIds = routeItems.stream()
+                .map(ProductProcessRouteItem::getProcessId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+        if (processIds.isEmpty()) {
+            throw new ServiceException("璁㈠崟宸ヨ壓璺嚎鏈厤缃伐搴忥紝鏃犳硶纭畾鎴愬搧绫诲埆");
+        }
+        List<ProductProcess> processes = productProcessMapper.selectBatchIds(processIds);
+        Set<String> matchedCategories = processes.stream()
+                .map(ProductProcess::getName)
+                .filter(name -> PROCESS_COPPER.equals(name) || PROCESS_SILVER.equals(name))
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (matchedCategories.isEmpty()) {
+            throw new ServiceException("璁㈠崟宸ヨ壓璺嚎鏈厤缃嵃閾�/鍗伴摱宸ュ簭锛屾棤娉曠‘瀹氭垚鍝佺被鍒�");
+        }
+        if (matchedCategories.size() > 1) {
+            throw new ServiceException("璁㈠崟宸ヨ壓璺嚎鍚屾椂閰嶇疆浜嗗嵃閾滃拰鍗伴摱锛屾棤娉曠‘瀹氭垚鍝佺被鍒�");
+        }
+        String matchedProcess = matchedCategories.iterator().next();
+        if (PROCESS_COPPER.equals(matchedProcess)) {
+            return PROCESS_CATEGORY_COPPER;
+        }
+        return PROCESS_CATEGORY_SILVER;
+    }
+
+    private String resolveVoltageValue(String otherData) {
+        if (StringUtils.isBlank(otherData)) {
+            return null;
+        }
+        Object parsed;
+        try {
+            parsed = JSON.parse(otherData);
+        } catch (Exception ex) {
+            throw new ServiceException("鎶ュ伐鍙傛暟鏍煎紡閿欒锛屾棤娉曡В鏋愮數鍘�");
+        }
+        String voltage = StringUtils.trim(findVoltageValue(parsed));
+        return StringUtils.isBlank(voltage) ? null : voltage;
+    }
+
+    private String findVoltageValue(Object node) {
+        if (node instanceof JSONArray) {
+            JSONArray array = (JSONArray) node;
+            for (Object item : array) {
+                String voltage = findVoltageValue(item);
+                if (StringUtils.isNotBlank(voltage)) {
+                    return voltage;
+                }
+            }
+            return null;
+        }
+        if (node instanceof JSONObject) {
+            JSONObject object = (JSONObject) node;
+            if (VOLTAGE_PARAMETER.equals(StringUtils.trim(object.getString("parameterItem")))) {
+                String value = StringUtils.trim(object.getString("value"));
+                if (StringUtils.isNotBlank(value)) {
+                    return value;
+                }
+            }
+            for (Object value : object.values()) {
+                String voltage = findVoltageValue(value);
+                if (StringUtils.isNotBlank(voltage)) {
+                    return voltage;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/resources/mapper/production/ProductWorkOrderMapper.xml b/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
index 60aa635..9dc639f 100644
--- a/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductWorkOrderMapper.xml
@@ -58,6 +58,9 @@
             <if test="c.productOrderNpsNo != null and c.productOrderNpsNo != ''">
                 and po.nps_no like concat('%',#{c.productOrderNpsNo},'%')
             </if>
+            <if test="c.processName != null and c.processName != ''">
+                and pp.NAME like concat('%',#{c.processName},'%')
+            </if>
         order by pwo.priority asc, po.id desc, ppri.drag_sort asc
     </select>
 
diff --git a/src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml b/src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
index 2028f72..cca93fc 100644
--- a/src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
+++ b/src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
@@ -24,7 +24,7 @@
         ELSE false
         END AS method
         FROM quality_unqualified qu
-        LEFT JOIN product_model pm ON qu.model = pm.model
+        LEFT JOIN product_model pm ON qu.model = pm.model and qu.product_id = pm.product_id
         where
         1=1
         <if test="qualityUnqualified.inspectType != null ">
diff --git a/src/main/resources/mapper/sales/SalesLedgerMapper.xml b/src/main/resources/mapper/sales/SalesLedgerMapper.xml
index 8cd7de7..fe36cdc 100644
--- a/src/main/resources/mapper/sales/SalesLedgerMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -43,7 +43,7 @@
         </where>
     </select>
 
-    <select id="selectSalesLedgerListPage" resultType="com.ruoyi.sales.pojo.SalesLedger">
+    <select id="selectSalesLedgerListPage" resultType="com.ruoyi.sales.dto.SalesLedgerDto">
         SELECT T1.id,
         T1.sales_contract_no,
         T1.customer_contract_no,
@@ -68,6 +68,9 @@
         T1.is_produce AS produce,
         T1.delivery_date,
         DATEDIFF(T1.delivery_date, CURDATE()) AS delivery_days_diff,
+        product_summary.model,
+        '' AS voltage,
+        product_summary.qty,
         CASE
         WHEN shipping_status_counts.total_count = 0 THEN false
         WHEN shipping_status_counts.unshipped_count = 0 THEN true
@@ -83,6 +86,15 @@
         FROM shipping_info
         GROUP BY sales_ledger_id
         ) shipping_status_counts ON T1.id = shipping_status_counts.sales_ledger_id
+        LEFT JOIN (
+        SELECT
+        sales_ledger_id,
+        GROUP_CONCAT(IFNULL(specification_model, '') ORDER BY id SEPARATOR ',') AS model,
+        GROUP_CONCAT(IFNULL(CAST(quantity AS CHAR), '') ORDER BY id SEPARATOR ',') AS qty
+        FROM sales_ledger_product
+        WHERE type = 1
+        GROUP BY sales_ledger_id
+        ) product_summary ON T1.id = product_summary.sales_ledger_id
         <where>
             <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
                 AND  T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
diff --git a/src/main/resources/mapper/stock/StockInRecordMapper.xml b/src/main/resources/mapper/stock/StockInRecordMapper.xml
index 7b55113..5d5f6fa 100644
--- a/src/main/resources/mapper/stock/StockInRecordMapper.xml
+++ b/src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -40,6 +40,12 @@
             <if test="params.recordType != null and params.recordType != ''">
                 AND sir.record_type = #{params.recordType}
             </if>
+            <if test="params.processCategory != null and params.processCategory != ''">
+                AND sir.process_category = #{params.processCategory}
+            </if>
+            <if test="params.voltage != null and params.voltage != ''">
+                AND sir.voltage = #{params.voltage}
+            </if>
             <if test="params.topParentProductId != null and params.topParentProductId > 0">
                 AND p.id in (select id from product_tree)
             </if>
@@ -83,6 +89,12 @@
             <if test="params.recordType != null and params.recordType != ''">
                 and sir.record_type = #{params.recordType}
             </if>
+            <if test="params.processCategory != null and params.processCategory != ''">
+                and sir.process_category = #{params.processCategory}
+            </if>
+            <if test="params.voltage != null and params.voltage != ''">
+                and sir.voltage = #{params.voltage}
+            </if>
             <if test="params.topParentProductId != null and params.topParentProductId > 0">
                 and p.id in (select id from product_tree)
             </if>
@@ -113,4 +125,4 @@
         ORDER BY
             sir.id DESC
     </select>
-</mapper>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/stock/StockInventoryMapper.xml b/src/main/resources/mapper/stock/StockInventoryMapper.xml
index 9acd557..e23f2fd 100644
--- a/src/main/resources/mapper/stock/StockInventoryMapper.xml
+++ b/src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -12,6 +12,9 @@
         <result column="version" property="version"/>
         <result column="locked_quantity" property="lockedQuantity"/>
         <result column="warn_num" property="warnNum"/>
+        <result column="process_category" property="processCategory"/>
+        <result column="voltage" property="voltage"/>
+        <result column="batch_no" property="batchNo"/>
     </resultMap>
     <update id="updateAddStockInventory">
         update stock_inventory
@@ -64,6 +67,8 @@
         (si.qualitity - COALESCE(si.locked_quantity, 0)) AS un_locked_quantity,
         pm.model,
         si.remark,
+        si.process_category as processCategory,
+        si.voltage,
         pm.unit,
         pm.material_code as materialCode,
         p.product_name,
@@ -78,6 +83,12 @@
         AND (p2.product_name != '鍗婃垚鍝�' OR p2.product_name IS NULL)
         <if test="ew.productName != null and ew.productName != ''">
             AND p.product_name LIKE CONCAT('%', #{ew.productName}, '%')
+        </if>
+        <if test="ew.processCategory != null and ew.processCategory != ''">
+            AND si.process_category = #{ew.processCategory}
+        </if>
+        <if test="ew.voltage != null and ew.voltage != ''">
+            AND si.voltage = #{ew.voltage}
         </if>
     </select>
 
@@ -109,6 +120,8 @@
         MAX(version) as version,
         model,
         MAX(remark) as remark,
+        processCategory,
+        voltage,
         unit,
         product_name,
         product_id,
@@ -133,6 +146,8 @@
         (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
         pm.model,
         si.remark,
+        si.process_category as processCategory,
+        si.voltage as voltage,
         pm.unit,
         p.product_name,
         p.id as product_id,
@@ -161,6 +176,8 @@
         (su.qualitity - COALESCE(su.locked_quantity, 0)) as un_locked_quantity,
         pm.model,
         su.remark,
+        '' as processCategory,
+        '' as voltage,
         pm.unit,
         p.product_name,
         p.id as product_id,
@@ -184,7 +201,7 @@
                 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
+        group by product_model_id, model, unit, product_name, product_id, unQualifiedBatchNo, materialCode, processCategory, voltage
     </select>
 
     <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
@@ -207,6 +224,8 @@
         model,
         unit,
         product_name,
+        processCategory,
+        voltage,
         MAX(warn_num) as warn_num,
         MAX(remark) as remark,
         MAX(update_time) as update_time,
@@ -227,6 +246,8 @@
         pm.model,
         pm.unit,
         p.product_name,
+        si.process_category as processCategory,
+        si.voltage as voltage,
         p.id as product_id,
         si.batch_no as qualifiedBatchNo,
         null as unQualifiedBatchNo,
@@ -250,6 +271,8 @@
         pm.model,
         pm.unit,
         p.product_name,
+        '' as processCategory,
+        '' as voltage,
         p.id as product_id,
         null as qualifiedBatchNo,
         su.batch_no as unQualifiedBatchNo,
@@ -271,18 +294,32 @@
                 and combined.product_id in (select id from product_tree)
             </if>
         </where>
-        group by product_model_id, model, unit, product_name, qualifiedBatchNo, unQualifiedBatchNo, materialCode
+        group by product_model_id, model, unit, product_name, unQualifiedBatchNo, materialCode, processCategory, voltage
     </select>
     <select id="stockInventoryPage" resultType="com.ruoyi.stock.dto.StockInRecordDto">
         select sir.*,si.qualitity as current_stock,
         pm.model,
+        sir.process_category as processCategory,
+        sir.voltage,
         pm.unit,
         pm.material_code as materialCode,
         p.product_name,
         su.nick_name as create_by
         from
         stock_in_record sir
-        left join stock_inventory si on sir.product_model_id = si.product_model_id
+        left join stock_inventory si
+               on sir.product_model_id = si.product_model_id
+              and (
+                    (ifnull(sir.process_category, '') != '' or ifnull(sir.voltage, '') != '')
+                    and ifnull(sir.process_category, '') = ifnull(si.process_category, '')
+                    and ifnull(sir.voltage, '') = ifnull(si.voltage, '')
+                    or
+                    ifnull(sir.process_category, '') = ''
+                    and ifnull(sir.voltage, '') = ''
+                    and ifnull(si.process_category, '') = ''
+                    and ifnull(si.voltage, '') = ''
+                    and si.product_model_id = sir.product_model_id
+                  )
         left join product_model pm on sir.product_model_id = pm.id
         left join product p on pm.product_id = p.id
         left join sys_user su on sir.create_user = su.user_id
@@ -432,6 +469,8 @@
                (si.qualitity - COALESCE(si.locked_quantity, 0)) as un_locked_quantity,
                pm.model,
                si.remark,
+               si.process_category as processCategory,
+               si.voltage,
                pm.unit,
                p.product_name,
                p.id as product_id
@@ -440,4 +479,35 @@
                  left join product p on pm.product_id = p.id
     </select>
 
-</mapper>
\ No newline at end of file
+    <select id="selectFinishedProductInventoryList" resultType="com.ruoyi.stock.dto.StockInventoryDto">
+        select si.id,
+               si.product_model_id,
+               si.process_category as processCategory,
+               si.voltage,
+               pm.model,
+               pm.material_code as materialCode,
+               pm.unit,
+               p.product_name,
+               p.id as product_id
+        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
+                 left join product pp on p.parent_id = pp.id
+        <where>
+            pp.product_name = '鎴愬搧'
+            <if test="ew.productName != null and ew.productName != ''">
+                and (p.product_name like concat('%', #{ew.productName}, '%')
+                or pm.model like concat('%', #{ew.productName}, '%')
+                or pm.material_code like concat('%', #{ew.productName}, '%'))
+            </if>
+            <if test="ew.processCategory != null and ew.processCategory != ''">
+                and si.process_category = #{ew.processCategory}
+            </if>
+            <if test="ew.voltage != null and ew.voltage != ''">
+                and si.voltage = #{ew.voltage}
+            </if>
+        </where>
+        order by p.id asc, pm.id asc, si.id asc
+    </select>
+
+</mapper>

--
Gitblit v1.9.3