From 6256daf29f4c44944fa71a7dbd211b06bf1a495d Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期一, 25 五月 2026 09:46:20 +0800
Subject: [PATCH] feat: 报工投入用“投入重量/数量”

---
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |  914 +++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 722 insertions(+), 192 deletions(-)

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 9d466ad..e5b5843 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -1,10 +1,12 @@
 package com.ruoyi.production.service.impl;
 
+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.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -13,12 +15,14 @@
 import com.ruoyi.basic.mapper.ProductModelMapper;
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
-import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
-import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
 import com.ruoyi.procurementrecord.utils.StockUtils;
-import com.ruoyi.production.controller.ProductWorkOrderController;
 import com.ruoyi.production.dto.ProductStructureDto;
 import com.ruoyi.production.dto.ProductionProductMainDto;
 import com.ruoyi.production.mapper.*;
@@ -26,300 +30,826 @@
 import com.ruoyi.production.service.ProductionProductMainService;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
 import com.ruoyi.quality.mapper.*;
 import com.ruoyi.quality.pojo.*;
-import io.swagger.models.auth.In;
+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;
-import com.ruoyi.production.mapper.ProductionProductMainMapper;
-import oshi.driver.mac.net.NetStat;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 @Service
 @AllArgsConstructor
 @Transactional(rollbackFor = Exception.class)
 public class ProductionProductMainServiceImpl extends ServiceImpl<ProductionProductMainMapper, ProductionProductMain> implements ProductionProductMainService {
 
-    private final ProcurementRecordOutMapper procurementRecordOutMapper;
+    private static final String PROCESS_VOLTAGE_SORT = "鐢靛帇鍒嗛��";
+    private static final String PROCESS_OPTICAL_INSPECTION = "鍏夋澶栬";
+    private static final String PROCESS_PACKAGING = "鍖呰";
+    private static final String INPUT_WEIGHT_PARAMETER = "鎶曞叆閲嶉噺/鏁伴噺";
+    private static final String INPUT_WEIGHT_FIELD = "inputWeight";
+    private static final Object PRODUCT_MAIN_NO_LOCK = new Object();
+
+    private IQualityInspectService qualityInspectService;
     private ProductionProductMainMapper productionProductMainMapper;
-
-    private ProductWorkOrderController productWorkOrderController;
-
     private ProductWorkOrderMapper productWorkOrderMapper;
-
     private ProductProcessRouteItemMapper productProcessRouteItemMapper;
     private SysUserMapper userMapper;
-
     private ProductionProductOutputMapper productionProductOutputMapper;
-
-    private ProcessRouteItemMapper processRouteItemMapper;
-
     private ProductModelMapper productModelMapper;
-
     private QualityInspectMapper qualityInspectMapper;
-
+    private QualityUnqualifiedMapper qualityUnqualifiedMapper;
     private ProductProcessMapper productProcessMapper;
     private ProductProcessRouteMapper productProcessRouteMapper;
-
     private ProductMapper productMapper;
-
-
     private QualityTestStandardParamMapper qualityTestStandardParamMapper;
-    private QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
     private QualityTestStandardMapper qualityTestStandardMapper;
-
     private QualityInspectParamMapper qualityInspectParamMapper;
-
     private ProductStructureMapper productStructureMapper;
-
     private ProductionProductInputMapper productionProductInputMapper;
-
     private ProductOrderMapper productOrderMapper;
-
     private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
-
     private StockUtils stockUtils;
-
+    private FinishedProductStockDimensionResolver finishedProductStockDimensionResolver;
+    private ISysNoticeService sysNoticeService;
 
     @Override
     public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) {
         return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
     }
 
+    // 鏂板鎶ュ伐锛屽苟鎸夌収鎶ュ伐涓绘祦绋嬪鐞嗘姇鍏ユ墸搴撱�佷骇鍑恒�佽川妫�銆佸叆搴撱�佸伐鍗曡鍗曡繘搴﹀拰鏍哥畻鏁版嵁
     @Override
     public Boolean addProductMain(ProductionProductMainDto dto) {
-        SysUser user = userMapper.selectUserById(dto.getUserId());
-        ProductionProductMain productionProductMain = new ProductionProductMain();
-        //褰撳墠宸ヨ壓璺嚎瀵瑰簲鐨勫伐搴忚鎯�
-        ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(dto.getProductProcessRouteItemId());
-        if (productProcessRouteItem == null) {
-            throw new RuntimeException("宸ヨ壓璺嚎椤逛笉瀛樺湪");
+        // 绗竴姝ワ細鍏堟牎楠屾姤宸ュ叆鍙傦紝閬垮厤绌哄�煎拰闈炴硶鏁伴噺杩涘叆鍚庣画娴佺▼
+        if (dto == null) {
+            throw new ServiceException("鎶ュ伐鍙傛暟涓嶈兘涓虹┖");
         }
-        //褰撳墠鍏蜂綋宸ュ簭
+        if (dto.getProductProcessRouteItemId() == null) {
+            throw new ServiceException("宸ヨ壓璺嚎宸ュ簭椤逛笉鑳戒负绌�");
+        }
+        if (dto.getWorkOrderId() == null) {
+            throw new ServiceException("鐢熶骇宸ュ崟涓嶈兘涓虹┖");
+        }
+
+        BigDecimal reportQty = dto.getQuantity();
+        BigDecimal scrapQty = dto.getScrapQty() == null ? BigDecimal.ZERO : dto.getScrapQty();
+        BigDecimal bomInputQty = dto.getInputWeight();
+        if (bomInputQty == null) {
+            bomInputQty = resolveInputWeight(dto.getOtherData());
+        }
+        if (reportQty == null || reportQty.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("鎶ュ伐鏁伴噺蹇呴』澶т簬0");
+        }
+        if (scrapQty.compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("鎶ュ簾鏁伴噺涓嶈兘灏忎簬0");
+        }
+        if (scrapQty.compareTo(reportQty) > 0) {
+            throw new ServiceException("鎶ュ簾鏁伴噺涓嶈兘澶т簬鎶ュ伐鏁伴噺");
+        }
+        if (bomInputQty == null || bomInputQty.compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("浜у搧缁撴瀯鎶曞叆鏁伴噺涓嶈兘灏忎簬0");
+        }
+
+        // 绗簩姝ワ細鍔犺浇褰撳墠宸ュ簭銆佸伐鍗曘�佸伐鑹鸿矾绾垮拰璁㈠崟鏁版嵁锛屽苟鏍¢獙鍩虹鍏宠仈鍏崇郴
+        ProductProcessRouteItem productProcessRouteItem =
+                productProcessRouteItemMapper.selectById(dto.getProductProcessRouteItemId());
+        if (productProcessRouteItem == null) {
+            throw new ServiceException("宸ヨ壓璺嚎宸ュ簭椤逛笉瀛樺湪");
+        }
+
         ProductProcess productProcess = productProcessMapper.selectById(productProcessRouteItem.getProcessId());
-        //宸ヨ壓璺嚎涓綋鍓嶅伐搴忓搴旂殑浜у嚭瑙勬牸鍨嬪彿
+        if (productProcess == null) {
+            throw new ServiceException("褰撳墠宸ュ簭涓嶅瓨鍦�");
+        }
+
         ProductModel productModel = productModelMapper.selectById(productProcessRouteItem.getProductModelId());
-        //鏌ヨ璇ョ敓浜ц鍗曞搴旂殑bom
-        ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectById(productProcessRouteItem.getProductRouteId());
-        /*鏂板鎶ュ伐涓昏〃*/
-        //鏌ヨ鏈�澶ф姤宸ョ紪鍙�
-        String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
-        QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>();
-        queryWrapper.select("MAX(product_no) as maxNo")
-                .likeRight("product_no", datePrefix);
-        List<Map<String, Object>> resultList = productionProductMainMapper.selectMaps(queryWrapper);
-        int sequenceNumber = 1;
-        if (resultList != null && !resultList.isEmpty()) {
-            Map<String, Object> result = resultList.get(0);
-            if (result != null) {
-                Object maxNoObj = result.get("maxNo");
-                if (maxNoObj != null) {
-                    String lastNo = maxNoObj.toString();
-                    System.out.println("lastNo: " + lastNo);
+        if (productModel == null) {
+            throw new ServiceException("褰撳墠宸ュ簭瀵瑰簲鐨勪骇鍝佸瀷鍙蜂笉瀛樺湪");
+        }
+
+        ProductProcessRoute productProcessRoute =
+                productProcessRouteMapper.selectById(productProcessRouteItem.getProductRouteId());
+        if (productProcessRoute == null) {
+            throw new ServiceException("宸ヨ壓璺嚎涓嶅瓨鍦�");
+        }
+
+        ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(dto.getWorkOrderId());
+        if (productWorkOrder == null) {
+            throw new ServiceException("鐢熶骇宸ュ崟涓嶅瓨鍦�");
+        }
+        if (!Objects.equals(productWorkOrder.getProductProcessRouteItemId(), productProcessRouteItem.getId())) {
+            throw new ServiceException("鐢熶骇宸ュ崟涓庡綋鍓嶅伐搴忎笉鍖归厤");
+        }
+
+        ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
+        if (productOrder == null) {
+            throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
+        }
+
+        // 绗笁姝ワ細鏍¢獙宸ュ簭娴佽浆閫昏緫锛屽繀椤绘槸鍚屼竴璁㈠崟鐨勪笂涓�閬撳伐搴忓凡鎶ュ伐涓斿墠搴忔棤鏈В闄ら殧绂昏褰�
+        Integer currentDragSort = productProcessRouteItem.getDragSort();
+        if (currentDragSort != null && currentDragSort > 1) {
+            boolean isPreviousReported = productionProductMainMapper.checkPreviousProcessReported(
+                    productWorkOrder.getId(),
+                    currentDragSort
+            );
+            if (!isPreviousReported) {
+                throw new ServiceException("涓婁竴閬撳伐搴忓皻鏈姤宸ワ紝褰撳墠宸ュ簭涓嶈兘鎶ュ伐");
+            }
+
+            ProductProcessRouteItem previousRouteItem = productProcessRouteItemMapper.selectOne(
+                    Wrappers.<ProductProcessRouteItem>lambdaQuery()
+                            .eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId())
+                            .eq(ProductProcessRouteItem::getDragSort, currentDragSort - 1)
+            );
+            if (previousRouteItem == null) {
+                throw new ServiceException("涓婁竴閬撳伐搴忎笉瀛樺湪");
+            }
+
+            ProductWorkOrder previousWorkOrder = productWorkOrderMapper.selectOne(
+                    Wrappers.<ProductWorkOrder>lambdaQuery()
+                            .eq(ProductWorkOrder::getProductOrderId, productOrder.getId())
+                            .eq(ProductWorkOrder::getProductProcessRouteItemId, previousRouteItem.getId())
+            );
+            if (previousWorkOrder == null) {
+                throw new ServiceException("涓婁竴閬撳伐搴忓伐鍗曚笉瀛樺湪");
+            }
+
+            BigDecimal currentReportedQty = BigDecimal.ZERO;
+            List<ProductionProductMain> currentMainList = productionProductMainMapper.selectList(
+                    Wrappers.<ProductionProductMain>lambdaQuery()
+                            .eq(ProductionProductMain::getWorkOrderId, productWorkOrder.getId())
+            );
+            if (CollectionUtils.isNotEmpty(currentMainList)) {
+                List<Long> currentMainIds = currentMainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+                List<ProductionProductOutput> currentOutputList = productionProductOutputMapper.selectList(
+                        Wrappers.<ProductionProductOutput>lambdaQuery()
+                                .in(ProductionProductOutput::getProductMainId, currentMainIds)
+                );
+                currentReportedQty = currentOutputList.stream()
+                        .map(ProductionProductOutput::getQuantity)
+                        .filter(Objects::nonNull)
+                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+            }
+
+            BigDecimal previousCompleteQty =
+                    previousWorkOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : previousWorkOrder.getCompleteQuantity();
+            if (currentReportedQty.add(reportQty).compareTo(previousCompleteQty) > 0) {
+                throw new ServiceException("鏈鎶ュ伐鏁伴噺瓒呰繃涓婇亾宸ュ簭鍙祦杞暟閲�");
+            }
+
+            List<ProductProcessRouteItem> previousItems = productProcessRouteItemMapper.selectList(
+                    Wrappers.<ProductProcessRouteItem>lambdaQuery()
+                            .eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId())
+                            .lt(ProductProcessRouteItem::getDragSort, currentDragSort)
+            );
+
+            if (CollectionUtils.isNotEmpty(previousItems)) {
+                List<String> previousProcessNames = new ArrayList<>();
+                for (ProductProcessRouteItem item : previousItems) {
+                    ProductProcess process = productProcessMapper.selectById(item.getProcessId());
+                    if (process != null && StringUtils.isNotBlank(process.getName())) {
+                        previousProcessNames.add(process.getName().trim());
+                    }
+                }
+
+                if (CollectionUtils.isNotEmpty(previousProcessNames)) {
+                    List<QualityUnqualified> unqualifiedList =
+                            qualityUnqualifiedMapper.selectUnqualifiedByProductOrderAndProcessNames(productOrder.getId(), previousProcessNames);
+                    if (CollectionUtils.isNotEmpty(unqualifiedList)) {
+                        throw new ServiceException("鍓嶅簭宸ュ簭瀛樺湪闅旂璁板綍锛屽綋鍓嶅伐搴忎笉鑳芥姤宸�");
+                    }
+                }
+            }
+        }
+
+        // 绗洓姝ワ細鎺у埗褰撳墠宸ュ崟鎬绘姤宸ラ噺涓嶈兘瓒呰繃璁″垝閲�
+        BigDecimal currentWorkOrderReportedQty = BigDecimal.ZERO;
+        List<ProductionProductMain> workOrderMainList = productionProductMainMapper.selectList(
+                Wrappers.<ProductionProductMain>lambdaQuery()
+                        .eq(ProductionProductMain::getWorkOrderId, productWorkOrder.getId())
+        );
+        if (CollectionUtils.isNotEmpty(workOrderMainList)) {
+            List<Long> workOrderMainIds = workOrderMainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+            List<ProductionProductOutput> workOrderOutputList = productionProductOutputMapper.selectList(
+                    Wrappers.<ProductionProductOutput>lambdaQuery()
+                            .in(ProductionProductOutput::getProductMainId, workOrderMainIds)
+            );
+            currentWorkOrderReportedQty = workOrderOutputList.stream()
+                    .map(ProductionProductOutput::getQuantity)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+        if (productWorkOrder.getPlanQuantity() != null
+                && currentWorkOrderReportedQty.add(reportQty).compareTo(productWorkOrder.getPlanQuantity()) > 0) {
+            throw new ServiceException("鏈鎶ュ伐鏁伴噺瓒呰繃宸ュ崟鍙姤鏁伴噺");
+        }
+
+        // 绗簲姝ワ細鐢熸垚鎶ュ伐鍗曞彿骞剁‘瀹氭姤宸ヤ汉淇℃伅
+        String productNo;
+        synchronized (PRODUCT_MAIN_NO_LOCK) {
+            String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
+            QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>();
+            queryWrapper.select("MAX(product_no) as maxNo")
+                    .likeRight("product_no", datePrefix);
+            List<Map<String, Object>> resultList = productionProductMainMapper.selectMaps(queryWrapper);
+
+            int sequenceNumber = 1;
+            if (CollectionUtils.isNotEmpty(resultList)) {
+                Map<String, Object> result = resultList.get(0);
+                if (result != null && result.get("maxNo") != null) {
+                    String lastNo = result.get("maxNo").toString();
                     if (lastNo.startsWith(datePrefix)) {
                         try {
-                            String seqStr = lastNo.substring(datePrefix.length());
-                            sequenceNumber = Integer.parseInt(seqStr) + 1;
+                            sequenceNumber = Integer.parseInt(lastNo.substring(datePrefix.length())) + 1;
                         } catch (NumberFormatException e) {
                             sequenceNumber = 1;
                         }
                     }
                 }
             }
+            productNo = String.format("%s%03d", datePrefix, sequenceNumber);
         }
-        String productNo = String.format("%s%03d", datePrefix, sequenceNumber);
+
+        Long userId = dto.getUserId();
+        String userName = dto.getUserName();
+        if (userId == null) {
+            userId = SecurityUtils.getLoginUser().getUserId();
+            userName = SecurityUtils.getLoginUser().getNickName();
+        } else if (StringUtils.isBlank(userName)) {
+            SysUser user = userMapper.selectUserById(userId);
+            userName = user == null ? null : user.getNickName();
+        }
+
+        // 绗叚姝ワ細鍏堝啓鎶ュ伐涓昏〃
+        ProductionProductMain productionProductMain = new ProductionProductMain();
         productionProductMain.setProductNo(productNo);
-        productionProductMain.setUserId(dto.getUserId());
-        productionProductMain.setUserName(dto.getUserName());
+        productionProductMain.setUserId(userId);
+        productionProductMain.setUserName(userName);
         productionProductMain.setProductProcessRouteItemId(dto.getProductProcessRouteItemId());
         productionProductMain.setWorkOrderId(dto.getWorkOrderId());
         productionProductMain.setStatus(0);
         productionProductMainMapper.insert(productionProductMain);
-        /*鏂板鎶ュ伐鎶曞叆琛�*/
-        List<ProductStructureDto> productStructureDtos = productStructureMapper.listBybomAndProcess(productProcessRoute.getBomId(), productProcess.getId());
-        if (productStructureDtos.size() == 0) {
-            //濡傛灉璇ュ伐搴忔病鏈変骇鍝佺粨鏋勭殑鎶曞叆鍝�,閭h繖涓姇鍏ュ搧鍜屼骇鍑哄搧鏄悓涓�涓�
+
+        // 绗竷姝ワ細鏍规嵁 BOM 鐢熸垚鎶曞叆璁板綍骞舵墸鍑忓師鏂欏簱瀛�
+        List<ProductStructureDto> productStructureDtos =
+                productStructureMapper.listBybomAndProcess(productProcessRoute.getBomId(), productProcess.getId());
+
+        if (CollectionUtils.isEmpty(productStructureDtos)) {
             ProductStructureDto productStructureDto = new ProductStructureDto();
             productStructureDto.setProductModelId(productProcessRouteItem.getProductModelId());
             productStructureDto.setUnitQuantity(BigDecimal.ONE);
             productStructureDtos.add(productStructureDto);
         }
+
+        Set<Long> parentIds = productStructureDtos.stream()
+                .map(ProductStructureDto::getParentId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Map<Long, ProductStructureDto> parentMap = new HashMap<>();
+        if (CollectionUtils.isNotEmpty(parentIds)) {
+            parentMap = productStructureMapper.selectByIds(parentIds)
+                    .stream()
+                    .collect(Collectors.toMap(ProductStructureDto::getId, Function.identity(), (a, b) -> a));
+        }
+
+        // 绗竷姝�-1锛氭姇鍏ユ暟閲忓己鍒跺彇鍓嶇浼犲叆鐨� bomInputQty
+        BigDecimal inputBaseQty = bomInputQty;
+
         for (ProductStructureDto productStructureDto : productStructureDtos) {
-            ProductModel productModel1 = productModelMapper.selectById(productStructureDto.getProductModelId());
-            Product product = productMapper.selectById(productModel1.getProductId());
-            BigDecimal stockQuantity = stockUtils.getStockQuantity(productModel1.getId()).get("stockQuantity");
-            if (!(stockQuantity.compareTo(BigDecimal.ZERO) > 0)) {
-                throw new RuntimeException(product.getProductName()+"浜у搧鐨�"+productModel1.getModel() + "鐨勮鏍煎簱瀛樹负0");
+            if (productStructureDto.getProductModelId() == null) {
+                throw new ServiceException("鎶曞叆鐗╂枡浜у搧鍨嬪彿涓嶈兘涓虹┖");
             }
-            if (stockQuantity.compareTo(productStructureDto.getUnitQuantity().multiply(dto.getQuantity())) < 0) {
-                throw new RuntimeException(product.getProductName()+"浜у搧鐨�"+productModel1.getModel() + "鐨勮鏍煎簱瀛樹笉瓒�");
+
+            BigDecimal childQty = productStructureDto.getUnitQuantity();
+            if (childQty == null || childQty.compareTo(BigDecimal.ZERO) <= 0) {
+                throw new ServiceException("鎶曞叆鐗╂枡鐢ㄩ噺蹇呴』澶т簬0");
             }
+
+            BigDecimal parentQty = BigDecimal.ONE;
+            if (productStructureDto.getParentId() != null) {
+                ProductStructureDto parent = parentMap.get(productStructureDto.getParentId());
+                if (parent != null) {
+                    parentQty = parent.getUnitQuantity();
+                }
+            }
+
+            if (parentQty == null || parentQty.compareTo(BigDecimal.ZERO) <= 0) {
+                throw new ServiceException("鐖剁骇鐗╂枡鐢ㄩ噺蹇呴』澶т簬0");
+            }
+
+            BigDecimal needQty = inputBaseQty;
+
             ProductionProductInput productionProductInput = new ProductionProductInput();
             productionProductInput.setProductModelId(productStructureDto.getProductModelId());
-            productionProductInput.setQuantity(productStructureDto.getUnitQuantity().multiply(dto.getQuantity()));
+            productionProductInput.setQuantity(needQty == null ? BigDecimal.ZERO : needQty);
             productionProductInput.setProductMainId(productionProductMain.getId());
             productionProductInputMapper.insert(productionProductInput);
-            //瀵瑰簲鐨勫簱瀛樺嚭搴�
-            DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
-            LocalDate now = LocalDate.now();
-            ProcurementRecordOut procurementRecordOut1 = procurementRecordOutMapper.selectCode(dateFormat.format(now));
-            Long aLong = procurementRecordOut1 == null ? 1L : Long.valueOf(procurementRecordOut1.getCode().split("LS" + dateFormat.format(now))[1]);
-            ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
-                    .procurementRecordStorageId(0)
-                    .code("LS" + dateFormat.format(now) + String.format("%03d", aLong + 1))
-                    .salesLedgerProductId(productionProductMain.getId())//鍏宠仈鎶ュ伐浜у嚭
-                    .inboundBatches(aLong.equals(0L) ? "绗�1鎵规" : "绗�" + (aLong + 1) + "鎵规")
-                    .inboundNum(productionProductInput.getQuantity())
-                    .type(4)
-                    .createBy(user.getNickName())
-                    .productModelId(productModel1.getId());
-            procurementRecordOutMapper.insert(procurementRecordOut.build());
+
+            stockUtils.substractStock(
+                    productStructureDto.getProductModelId(),
+                    needQty,
+                    StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(),
+                    productionProductMain.getId(),
+                    null,
+                    null
+            );
         }
-        /*鏂板鎶ュ伐浜у嚭琛�*/
+
+        // 绗叓姝ワ細鍐欎骇鍑鸿褰曞苟璁$畻鏈鍚堟牸鏁伴噺
         ProductionProductOutput productionProductOutput = new ProductionProductOutput();
         productionProductOutput.setProductMainId(productionProductMain.getId());
         productionProductOutput.setProductModelId(productProcessRouteItem.getProductModelId());
-        productionProductOutput.setQuantity(dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO);
-        productionProductOutput.setScrapQty(dto.getScrapQty() != null ? dto.getScrapQty() : BigDecimal.ZERO);
+        productionProductOutput.setQuantity(reportQty);
+        productionProductOutput.setScrapQty(scrapQty);
+        productionProductOutput.setOtherData(dto.getOtherData() == null ? "" : dto.getOtherData());
         productionProductOutputMapper.insert(productionProductOutput);
-        //鍚堟牸鏁伴噺=鎶ュ伐鏁伴噺-鎶ュ簾鏁伴噺
-        BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty());
-        //鍙湁鍚堟牸鏁伴噺>0鎵嶈兘澧炲姞鐩稿簲鏁版嵁
-        if (productQty.compareTo(BigDecimal.ZERO)>0) {
-            /*鏂板璐ㄦ*/
-            //瀵瑰簲鐨勮繃绋嬫鎴栬�呭嚭鍘傛
-            List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-            int inspectType = 1;
-            String process = productProcess.getName();//宸ュ簭
-            if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                //鏈�鍚庝竴閬撳伐搴忕敓鎴愬嚭鍘傛
-                inspectType = 2;
-                process = null;
+
+        BigDecimal qualifiedQty = reportQty.subtract(scrapQty);
+
+        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(
+                Wrappers.<ProductProcessRouteItem>lambdaQuery()
+                        .eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId())
+        );
+
+        Integer maxDragSort = productProcessRouteItems.stream()
+                .map(ProductProcessRouteItem::getDragSort)
+                .filter(Objects::nonNull)
+                .max(Integer::compareTo)
+                .orElse(null);
+
+        boolean isRouteLastProcess = Objects.equals(productProcessRouteItem.getDragSort(), maxDragSort);
+        ReportStockRule reportStockRule =
+                resolveReportStockRule(productProcessRouteItem, productProcess, productProcessRouteItems);
+
+        // 绗節姝ワ細濡傛灉鏈夊悎鏍兼暟閲忥紝鍒欐牴鎹槸鍚﹁川妫�鍐冲畾璧拌川妫�鎴栫洿鎺ュ叆搴�
+        if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
+            if (Boolean.TRUE.equals(productProcessRouteItem.getIsQuality())) {
+                Product product = productMapper.selectById(productModel.getProductId());
+                if (product == null) {
+                    throw new ServiceException("璐ㄦ浜у搧涓嶅瓨鍦�");
+                }
+
+                int inspectType = 1;
+                String process = productProcess.getName();
+
+                if (reportStockRule.finishedGoodsStockIn) {
+                    inspectType = 2;
+                    process = null;
+                }
+
+                QualityInspect qualityInspect = new QualityInspect();
+                qualityInspect.setProductId(product.getId());
+                qualityInspect.setProductName(product.getProductName());
+                qualityInspect.setModel(productModel.getModel());
+                qualityInspect.setUnit(productModel.getUnit());
+                qualityInspect.setQuantity(reportQty);
+                qualityInspect.setProcess(process);
+                qualityInspect.setInspectState(0);
+                qualityInspect.setInspectType(inspectType);
+                qualityInspect.setDefectiveQuantity(scrapQty);
+                qualityInspect.setProductMainId(productionProductMain.getId());
+                qualityInspect.setProductModelId(productModel.getId());
+                qualityInspectMapper.insert(qualityInspect);
+
+                List<QualityTestStandard> qualityTestStandardList =
+                        qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
+
+                if (CollectionUtils.isNotEmpty(qualityTestStandardList)) {
+                    QualityTestStandard qualityTestStandard = qualityTestStandardList.get(0);
+                    qualityInspect.setTestStandardId(qualityTestStandard.getId());
+                    qualityInspectMapper.updateById(qualityInspect);
+
+                    qualityTestStandardParamMapper.selectList(
+                            Wrappers.<QualityTestStandardParam>lambdaQuery()
+                                    .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.getId())
+                    ).forEach(qualityTestStandardParam -> {
+                        QualityInspectParam param = new QualityInspectParam();
+                        BeanUtils.copyProperties(qualityTestStandardParam, param);
+                        param.setId(null);
+                        param.setInspectId(qualityInspect.getId());
+                        qualityInspectParamMapper.insert(param);
+                    });
+                }
+            } else if (reportStockRule.createStockIn) {
+                //鎴愬搧鍏ュ簱闇�瑕佺數鍘嬶紝宸ュ簭绫诲埆锛堥摐锛岄摱锛�
+                if (reportStockRule.finishedGoodsStockIn) {
+                    String processCategory =
+                            finishedProductStockDimensionResolver.resolveProcessCategory(productionProductMain.getId());
+                    String voltage =
+                            finishedProductStockDimensionResolver.resolveVoltage(productionProductMain.getId());
+
+                    stockUtils.addStock(
+                            productProcessRouteItem.getProductModelId(),
+                            qualifiedQty,
+                            StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+                            productionProductMain.getId(),
+                            processCategory,
+                            voltage
+                    );
+                } else {
+                    stockUtils.addStockNoReview(
+                            productProcessRouteItem.getProductModelId(),
+                            qualifiedQty,
+                            StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+                            productionProductMain.getId()
+                    );
+                }
             }
-            Product product = productMapper.selectById(productModel.getProductId());
-            QualityInspect qualityInspect = new QualityInspect();
-            qualityInspect.setProductId(product.getId());
-            qualityInspect.setProductName(product.getProductName());
-            qualityInspect.setModel(productModel.getModel());
-            qualityInspect.setUnit(productModel.getUnit());
-            qualityInspect.setQuantity(productQty);
-            qualityInspect.setProcess(productProcess.getName());
-            qualityInspect.setInspectState(0);
-            qualityInspect.setInspectType(inspectType);
-            qualityInspect.setProductMainId(productionProductMain.getId());
-            qualityInspect.setProductModelId(productModel.getId());
-            qualityInspectMapper.insert(qualityInspect);
-            List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
-            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()))//榛樿鑾峰彇鏈�鏂扮殑
-                        .forEach(qualityTestStandardParam -> {
-                            QualityInspectParam param = new QualityInspectParam();
-                            BeanUtils.copyProperties(qualityTestStandardParam, param);
-                            param.setId(null);
-                            param.setInspectId(qualityInspect.getId());
-                            qualityInspectParamMapper.insert(param);
-                        });
-            }
-            /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
-            ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(dto.getWorkOrderId());
-            productWorkOrder.setCompleteQuantity(productQty.add(dto.getQuantity()));
+
+            // 绗崄姝ワ細鏇存柊宸ュ崟銆佽鍗曞畬鎴愯繘搴﹀拰璁′欢鏍哥畻鏁版嵁
+            BigDecimal workOrderCompleteQuantity =
+                    productWorkOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productWorkOrder.getCompleteQuantity();
+            productWorkOrder.setCompleteQuantity(workOrderCompleteQuantity.add(qualifiedQty));
+
             if (ObjectUtils.isNull(productWorkOrder.getActualStartTime())) {
-                productWorkOrder.setActualStartTime(LocalDate.now());//瀹為檯寮�濮嬫椂闂�
+                productWorkOrder.setActualStartTime(LocalDate.now());
             }
-            if (productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) == 0) {
-                productWorkOrder.setActualEndTime(LocalDate.now());//瀹為檯缁撴潫鏃堕棿
+            if (productWorkOrder.getPlanQuantity() != null
+                    && productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) >= 0) {
+                productWorkOrder.setActualEndTime(LocalDate.now());
             }
             productWorkOrderMapper.updateById(productWorkOrder);
-            //鐢熶骇璁㈠崟
-            ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
+
             if (ObjectUtils.isNull(productOrder.getStartTime())) {
-                productOrder.setStartTime(LocalDateTime.now());//寮�濮嬫椂闂�
+                productOrder.setStartTime(LocalDateTime.now());
             }
-            if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()) {
-                //濡傛灉鏄渶鍚庝竴閬撳伐搴忔姤宸ヤ箣鍚庣敓浜ц鍗曞畬鎴愭暟閲�+
-                productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().add(productQty));
-                if (productOrder.getCompleteQuantity().compareTo(productOrder.getQuantity()) == 0) {
-                    productOrder.setEndTime(LocalDateTime.now());//缁撴潫鏃堕棿
+
+            if (isRouteLastProcess) {
+                BigDecimal orderCompleteQuantity =
+                        productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
+                productOrder.setCompleteQuantity(orderCompleteQuantity.add(qualifiedQty));
+
+                if (productOrder.getQuantity() != null
+                        && productOrder.getCompleteQuantity().compareTo(productOrder.getQuantity()) >= 0) {
+                    productOrder.setEndTime(LocalDateTime.now());
                 }
             }
             productOrderMapper.updateById(productOrder);
-            /*娣诲姞鐢熶骇鏍哥畻*/
-            SalesLedgerProductionAccounting salesLedgerProductionAccounting = SalesLedgerProductionAccounting.builder()
-                    .salesLedgerWorkId(productionProductMain.getId())
-                    .salesLedgerSchedulingId(0L)
-                    .salesLedgerId(productOrder.getSalesLedgerId())
-                    .salesLedgerProductId(productOrder.getProductModelId())
-                    .schedulingUserId(user.getUserId())
-                    .schedulingUserName(user.getNickName())
-                    .finishedNum(productQty)
-                    .workHours(productProcess.getSalaryQuota())
-                    .process(productProcess.getName())
-                    .schedulingDate(LocalDate.now())
-                    .tenantId(dto.getTenantId())
-                    .build();
+
+            SalesLedgerProductionAccounting salesLedgerProductionAccounting =
+                    SalesLedgerProductionAccounting.builder()
+                            .productMainId(productionProductMain.getId())
+                            .schedulingUserId(userId)
+                            .schedulingUserName(userName)
+                            .finishedNum(qualifiedQty)
+                            .workHours(productProcess.getSalaryQuota())
+                            .process(productProcess.getName())
+                            .schedulingDate(LocalDate.now())
+                            .tenantId(dto.getTenantId())
+                            .build();
             salesLedgerProductionAccountingMapper.insert(salesLedgerProductionAccounting);
         }
-        //濡傛灉鎶ュ簾鏁伴噺>0,闇�瑕佽繘鍏ユ姤搴熺殑搴撳瓨
+
+        // 绗崄涓�姝ワ細缁熶竴澶勭悊鎶ュ簾鏁伴噺鍏ヤ笉鍚堟牸搴撳瓨锛岄伩鍏嶅湪涓绘祦绋嬩腑閲嶅鍏ュ簱
+        handleScrapStock(productModel.getId(), scrapQty, isRouteLastProcess, productionProductMain.getId());
+
         return true;
     }
 
+    // 鍒ゆ柇褰撳墠鎶ュ伐鏄惁闇�瑕佸叆搴擄紝浠ュ強鏄惁鎸夋垚鍝佸叆搴撳鐞�
+    private ReportStockRule resolveReportStockRule(ProductProcessRouteItem currentRouteItem,
+                                                   ProductProcess currentProcess,
+                                                   List<ProductProcessRouteItem> routeItems) {
+        Integer maxDragSort = CollectionUtils.isEmpty(routeItems)
+                ? null
+                : routeItems.stream()
+                .map(ProductProcessRouteItem::getDragSort)
+                .filter(Objects::nonNull)
+                .max(Integer::compareTo)
+                .orElse(null);
+
+        boolean isRouteLastProcess = Objects.equals(currentRouteItem.getDragSort(), maxDragSort);
+        String currentProcessName = currentProcess == null || currentProcess.getName() == null
+                ? ""
+                : currentProcess.getName().trim();
+
+        if (PROCESS_VOLTAGE_SORT.equals(currentProcessName)) {
+            return new ReportStockRule(false, false);
+        }
+
+        Set<Long> processIds = CollectionUtils.isEmpty(routeItems)
+                ? Collections.emptySet()
+                : routeItems.stream()
+                .map(ProductProcessRouteItem::getProcessId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Map<Long, String> processNameMap = new HashMap<>();
+        if (CollectionUtils.isNotEmpty(processIds)) {
+            processNameMap = productProcessMapper.selectBatchIds(processIds)
+                    .stream()
+                    .collect(Collectors.toMap(
+                            ProductProcess::getId,
+                            process -> process.getName() == null ? "" : process.getName().trim(),
+                            (a, b) -> a
+                    ));
+        }
+
+        Set<String> routeProcessNames = CollectionUtils.isEmpty(routeItems)
+                ? Collections.emptySet()
+                : routeItems.stream()
+                .map(ProductProcessRouteItem::getProcessId)
+                .map(processNameMap::get)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toSet());
+
+        boolean hasVoltageSort = routeProcessNames.contains(PROCESS_VOLTAGE_SORT);
+        boolean hasOpticalInspection = routeProcessNames.contains(PROCESS_OPTICAL_INSPECTION);
+        boolean hasPackaging = routeProcessNames.contains(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 void handleScrapStock(Long productModelId,
+                                  BigDecimal scrapQty,
+                                  boolean isRouteLastProcess,
+                                  Long productMainId) {
+        if (productModelId == null) {
+            throw new ServiceException("鎶ュ簾浜у搧鍨嬪彿涓嶈兘涓虹┖");
+        }
+
+        if (scrapQty == null || scrapQty.compareTo(BigDecimal.ZERO) <= 0) {
+            return;
+        }
+
+        if (isRouteLastProcess) {
+            stockUtils.addUnStock(
+                    productModelId,
+                    scrapQty,
+                    StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(),
+                    productMainId
+            );
+        } else {
+            stockUtils.addUnStockNoReview(
+                    productModelId,
+                    scrapQty,
+                    StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(),
+                    productMainId
+            );
+        }
+    }
+
+    // 鍗曟鎶ュ伐瀵瑰簲鐨勫叆搴撹鍒�
+    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;
+        }
+    }
+
     @Override
-    public Boolean removeProductMain(ProductionProductMainDto dto) {
-        ProductionProductMain productionProductMain = productionProductMainMapper.selectById(dto.getId());
-        //璇ユ姤宸ュ搴旂殑宸ヨ壓璺嚎璇︽儏
+    public Boolean removeProductMain(Long id) {
+        // 鍒犻櫎鎶ュ伐鍓嶅厛妫�鏌ユ槸鍚﹀凡缁忓畬鎴愪笉鍚堟牸澶勭悊
+        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
+                Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, id)
+        );
+        if (!qualityInspects.isEmpty()) {
+            List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
+                    Wrappers.<QualityUnqualified>lambdaQuery()
+                            .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList()))
+            );
+            if (!qualityUnqualifieds.isEmpty() && 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::getSalesLedgerWorkId, productionProductMain.getId())
+                        .eq(SalesLedgerProductionAccounting::getProductMainId, productionProductMain.getId())
         );
-        /*鏇存柊宸ュ崟鍜岀敓浜ц鍗�*/
+
         ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(productionProductMain.getWorkOrderId());
-        productWorkOrder.setCompleteQuantity(productWorkOrder.getCompleteQuantity().subtract(productionProductOutput.getQuantity()));
-        productWorkOrder.setActualEndTime(null);
-        productWorkOrderMapper.updateById(productWorkOrder);
-        //鍒ゆ柇鏄惁鏄渶鍚庝竴閬撳伐搴�
-        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
-        if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()){
-            ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
-            productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().subtract(productionProductOutput.getQuantity()));
-            productOrder.setEndTime(null);
-            productOrderMapper.updateById(productOrder);
+        if (productWorkOrder != null && productionProductOutput != null) {
+            BigDecimal outputQty = productionProductOutput.getQuantity() == null ? BigDecimal.ZERO : productionProductOutput.getQuantity();
+            BigDecimal scrapQty = productionProductOutput.getScrapQty() == null ? BigDecimal.ZERO : productionProductOutput.getScrapQty();
+            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()) {
+            ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
+            if (productOrder != null) {
+                BigDecimal orderCompleteQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
+                BigDecimal totalQty = productionProductOutput.getQuantity() != null ? productionProductOutput.getQuantity() : BigDecimal.ZERO;
+                BigDecimal scrapQty = productionProductOutput.getScrapQty() != null ? productionProductOutput.getScrapQty() : BigDecimal.ZERO;
+                BigDecimal actualQualifiedQty = totalQty.subtract(scrapQty);
+                BigDecimal newCompleteQty = orderCompleteQty.subtract(actualQualifiedQty);
+                productOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
+                productOrder.setEndTime(null);
+                productOrderMapper.updateById(productOrder);
+            } else {
+                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());
         });
-        // 鍒犻櫎浜у嚭璁板綍
-        productionProductOutputMapper.delete(new LambdaQueryWrapper<ProductionProductOutput>()
-                .eq(ProductionProductOutput::getProductMainId, productionProductMain.getId()));
-        /*鍒犻櫎鎶曞叆*/
-        procurementRecordOutMapper.delete(new LambdaQueryWrapper<ProcurementRecordOut>()
-                .eq(ProcurementRecordOut::getSalesLedgerProductId, 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;
     }
+
+    @Override
+    public ArrayList<Long> listMain(List<Long> idList) {
+        return productionProductMainMapper.listMain(idList);
+    }
+
+    @Override
+    public List<ProductionProductMainDto> getByProductWorkOrderId(Long productWorkOrderId) {
+        List<ProductionProductMainDto> productionProductMainDtos = productionProductMainMapper.getByProductWorkOrderId(productWorkOrderId);
+        if (productionProductMainDtos == null || productionProductMainDtos.isEmpty()) {
+            return productionProductMainDtos;
+        }
+
+        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()) {
+            List<Long> inspectIds = qualityInspects.stream()
+                    .map(QualityInspect::getId)
+                    .collect(Collectors.toList());
+
+            List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
+                    Wrappers.<QualityUnqualified>lambdaQuery()
+                            .in(QualityUnqualified::getInspectId, inspectIds)
+            );
+
+            Map<Long, QualityUnqualified> inspectIdToUnqualifiedMap = qualityUnqualifieds.stream()
+                    .collect(Collectors.toMap(QualityUnqualified::getInspectId, q -> q, (q1, q2) -> q1));
+
+            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.setQualifiedQty(p.getQuantity().subtract(p.getScrapQty()).subtract(p.getDefectiveQuantity()));
+                    QualityUnqualified qualityUnqualified = inspectIdToUnqualifiedMap.get(qualityInspect.getId());
+                    if (qualityUnqualified != null) {
+                        p.setDealResult(qualityUnqualified.getDealResult() == null ? "" : qualityUnqualified.getDealResult());
+                    }
+                }
+            });
+        }
+
+        return productionProductMainDtos;
+    }
+
+    private BigDecimal resolveInputWeight(String otherData) {
+        if (StringUtils.isBlank(otherData)) {
+            return null;
+        }
+        Object parsed;
+        try {
+            parsed = JSON.parse(otherData);
+        } catch (Exception ex) {
+            throw new ServiceException("鎶ュ伐鍙傛暟鏍煎紡閿欒锛屾棤娉曡В鏋愭姇鍏ラ噸閲�/鏁伴噺");
+        }
+        String inputWeight = StringUtils.trim(findParameterValue(parsed, INPUT_WEIGHT_PARAMETER));
+        if (StringUtils.isBlank(inputWeight)) {
+            inputWeight = StringUtils.trim(findFieldValue(parsed, INPUT_WEIGHT_FIELD));
+        }
+        if (StringUtils.isBlank(inputWeight)) {
+            return null;
+        }
+        try {
+            return new BigDecimal(inputWeight);
+        } catch (NumberFormatException ex) {
+            throw new ServiceException("鎶ュ伐鍙傛暟涓殑鎶曞叆閲嶉噺/鏁伴噺鏍煎紡閿欒");
+        }
+    }
+
+    private String findParameterValue(Object node, String parameterItem) {
+        if (node instanceof JSONArray) {
+            JSONArray array = (JSONArray) node;
+            for (Object item : array) {
+                String value = findParameterValue(item, parameterItem);
+                if (StringUtils.isNotBlank(value)) {
+                    return value;
+                }
+            }
+            return null;
+        }
+        if (node instanceof JSONObject) {
+            JSONObject object = (JSONObject) node;
+            if (parameterItem.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 matched = findParameterValue(value, parameterItem);
+                if (StringUtils.isNotBlank(matched)) {
+                    return matched;
+                }
+            }
+        }
+        return null;
+    }
+
+    private String findFieldValue(Object node, String fieldName) {
+        if (node instanceof JSONArray) {
+            JSONArray array = (JSONArray) node;
+            for (Object item : array) {
+                String value = findFieldValue(item, fieldName);
+                if (StringUtils.isNotBlank(value)) {
+                    return value;
+                }
+            }
+            return null;
+        }
+        if (node instanceof JSONObject) {
+            JSONObject object = (JSONObject) node;
+            Object fieldValue = object.get(fieldName);
+            if (fieldValue != null) {
+                String value = StringUtils.trim(String.valueOf(fieldValue));
+                if (StringUtils.isNotBlank(value)) {
+                    return value;
+                }
+            }
+            for (Object value : object.values()) {
+                String matched = findFieldValue(value, fieldName);
+                if (StringUtils.isNotBlank(matched)) {
+                    return matched;
+                }
+            }
+        }
+        return null;
+    }
 }

--
Gitblit v1.9.3