From 6d3a76f894052209cad136ec9bff6ddcd43fc4e7 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期一, 20 四月 2026 17:55:48 +0800
Subject: [PATCH] feat: 扫码时做限制,若采购台账已全部质检入库,APP扫码 入库时提示已入库。并且联动质检

---
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java |  256 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 229 insertions(+), 27 deletions(-)

diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
index ba32270..2afc5dc 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -6,7 +6,10 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
 import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.framework.web.domain.R;
 import com.ruoyi.procurementrecord.utils.StockUtils;
@@ -14,7 +17,9 @@
 import com.ruoyi.production.mapper.*;
 import com.ruoyi.production.pojo.*;
 import com.ruoyi.production.service.impl.ProductOrderServiceImpl;
+import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
 import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
+import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
 import com.ruoyi.purchase.pojo.PurchaseLedger;
 import com.ruoyi.quality.mapper.QualityInspectMapper;
 import com.ruoyi.quality.pojo.QualityInspect;
@@ -28,6 +33,9 @@
 import com.ruoyi.sales.service.ISalesLedgerProductProcessBindService;
 import com.ruoyi.sales.service.ISalesLedgerProductService;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.mapper.StockInRecordMapper;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.pojo.StockInRecord;
 import com.ruoyi.stock.pojo.StockInventory;
 import lombok.AllArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -61,6 +69,7 @@
     private SalesLedgerMapper salesLedgerMapper;
 
     private PurchaseLedgerMapper purchaseLedgerMapper;
+    private PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper;
 
     private ProductOrderMapper productOrderMapper;
 
@@ -72,6 +81,7 @@
 
     private ProcessRouteMapper processRouteMapper;
     private ProductProcessRouteMapper productProcessRouteMapper;
+    private ProductModelMapper productModelMapper;
 
     private ProductWorkOrderMapper productWorkOrderMapper;
     private ProductionProductMainMapper productionProductMainMapper;
@@ -82,6 +92,8 @@
     private ShippingInfoServiceImpl shippingInfoService;
 
     private StockUtils stockUtils;
+
+    private StockInRecordMapper stockInRecordMapper;
 
     private final ISalesLedgerProductProcessBindService salesLedgerProductProcessBindService;
 
@@ -94,7 +106,11 @@
 
     @Override
     public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
-        return salesLedgerProductMapper.selectById(id);
+        SalesLedgerProduct row = salesLedgerProductMapper.selectById(id);
+        if (row != null) {
+            row.fillRemainingQuantity();
+        }
+        return row;
     }
 
     @Override
@@ -103,50 +119,112 @@
 //        queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerProduct.getSalesLedgerId())
 //                .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType());
         List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct);
-        if (!CollectionUtils.isEmpty(salesLedgerProducts)) {
-            salesLedgerProducts.forEach(item -> {
-                // 鍙戣揣淇℃伅
-                ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
-                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
-                        .orderByDesc(ShippingInfo::getCreateTime)
-                        .last("limit 1"));
-                if (shippingInfo != null) {
-                    item.setShippingDate(shippingInfo.getShippingDate());
-                    item.setShippingCarNumber(shippingInfo.getShippingCarNumber());
-                    item.setShippingStatus(shippingInfo.getStatus());
-                    item.setExpressCompany(shippingInfo.getExpressCompany());
-                    item.setExpressNumber(shippingInfo.getExpressNumber());
-                }
-            });
-            // 寮�绁�
+        if (CollectionUtils.isEmpty(salesLedgerProducts)) {
+            return salesLedgerProducts;
+        }
+
+        salesLedgerProducts.forEach(item -> {
+            ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
+                    .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
+                    .orderByDesc(ShippingInfo::getCreateTime)
+                    .last("limit 1"));
+            if (shippingInfo != null) {
+                item.setShippingDate(shippingInfo.getShippingDate());
+                item.setShippingCarNumber(shippingInfo.getShippingCarNumber());
+                item.setShippingStatus(shippingInfo.getStatus());
+                item.setExpressCompany(shippingInfo.getExpressCompany());
+                item.setExpressNumber(shippingInfo.getExpressNumber());
+            }
+        });
+
+        if (salesLedgerProduct.getSalesLedgerId() != null) {
             InvoiceRegistrationProductDto invoiceRegistrationProductDto = new InvoiceRegistrationProductDto();
             invoiceRegistrationProductDto.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId().intValue());
             List<InvoiceRegistrationProductDto> invoiceRegistrationProductDtoList = invoiceRegistrationProductMapper.invoiceRegistrationProductList(invoiceRegistrationProductDto);
-            // 缁熻寮�绁ㄧ櫥璁颁骇鍝佺殑宸插紑绁ㄦ暟/宸插紑绁ㄩ噾棰�
             if (!CollectionUtils.isEmpty(invoiceRegistrationProductDtoList)) {
                 for (SalesLedgerProduct ledgerProduct : salesLedgerProducts) {
                     BigDecimal invoiceNum = BigDecimal.ZERO;
                     BigDecimal invoiceAmount = BigDecimal.ZERO;
-                    BigDecimal noInvoiceNum = BigDecimal.ZERO;
-                    BigDecimal noInvoiceAmount = BigDecimal.ZERO;
                     for (InvoiceRegistrationProductDto registrationProductDto : invoiceRegistrationProductDtoList) {
                         if (ledgerProduct.getId().intValue() == registrationProductDto.getSalesLedgerProductId()) {
                             invoiceNum = invoiceNum.add(registrationProductDto.getInvoiceNum());
                             invoiceAmount = invoiceAmount.add(registrationProductDto.getInvoiceAmount());
                         }
                     }
-                    noInvoiceNum = ledgerProduct.getQuantity().subtract(invoiceNum);
-                    noInvoiceAmount = ledgerProduct.getTaxInclusiveTotalPrice().subtract(invoiceAmount);
+                    BigDecimal ledgerQuantity = ledgerProduct.getQuantity() == null ? BigDecimal.ZERO : ledgerProduct.getQuantity();
+                    BigDecimal ledgerTotalPrice = ledgerProduct.getTaxInclusiveTotalPrice() == null ? BigDecimal.ZERO : ledgerProduct.getTaxInclusiveTotalPrice();
                     ledgerProduct.setInvoiceNum(invoiceNum);
                     ledgerProduct.setInvoiceAmount(invoiceAmount);
-                    ledgerProduct.setNoInvoiceNum(noInvoiceNum);
-                    ledgerProduct.setNoInvoiceAmount(noInvoiceAmount);
-
-
+                    ledgerProduct.setNoInvoiceNum(ledgerQuantity.subtract(invoiceNum));
+                    ledgerProduct.setNoInvoiceAmount(ledgerTotalPrice.subtract(invoiceAmount));
                 }
             }
-
         }
+
+        List<Long> productIds = salesLedgerProducts.stream()
+                .map(SalesLedgerProduct::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        Map<Long, BigDecimal> returnOrderGroupDtoMap = Collections.emptyMap();
+        if (!CollectionUtils.isEmpty(productIds)) {
+            List<SimpleReturnOrderGroupDto> groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds);
+            if (!CollectionUtils.isEmpty(groupListByProductIds)) {
+                returnOrderGroupDtoMap = groupListByProductIds.stream()
+                        .collect(Collectors.toMap(SimpleReturnOrderGroupDto::getSalesLedgerProductId,
+                                SimpleReturnOrderGroupDto::getSumReturnQuantity,
+                                BigDecimal::add));
+            }
+        }
+
+        List<Long> productModelIds = salesLedgerProducts.stream()
+                .map(SalesLedgerProduct::getProductModelId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        Map<Long, ProductModel> productModelMap = Collections.emptyMap();
+        if (!CollectionUtils.isEmpty(productModelIds)) {
+            List<ProductModel> productModels = productModelMapper.selectBatchIds(productModelIds);
+            if (!CollectionUtils.isEmpty(productModels)) {
+                productModelMap = productModels.stream()
+                        .collect(Collectors.toMap(ProductModel::getId, Function.identity()));
+            }
+        }
+
+        Map<Long, BigDecimal> finalReturnOrderGroupDtoMap = returnOrderGroupDtoMap;
+        Map<Long, ProductModel> finalProductModelMap = productModelMap;
+        salesLedgerProducts.forEach(item -> {
+            if (item.getFutureTickets() == null || item.getFutureTickets().compareTo(BigDecimal.ZERO) == 0) {
+                item.setFutureTickets(BigDecimal.ZERO);
+            }
+            if (item.getFutureTicketsAmount() == null || item.getFutureTicketsAmount().compareTo(BigDecimal.ZERO) == 0) {
+                item.setFutureTicketsAmount(BigDecimal.ZERO);
+            }
+            BigDecimal returnQuality = finalReturnOrderGroupDtoMap.getOrDefault(item.getId(), BigDecimal.ZERO);
+            item.setReturnQuality(returnQuality);
+            BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity();
+            BigDecimal shipped = item.getShippedQuantity() == null ? BigDecimal.ZERO : item.getShippedQuantity();
+            BigDecimal available = quantity.subtract(returnQuality).subtract(shipped);
+            item.setAvailableQuality(available.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : available);
+            item.fillRemainingQuantity();
+            if (item.getApproveStatus() == null || item.getApproveStatus() != 2) {
+                BigDecimal remainingShipped = item.getRemainingShippedQuantity() == null ? BigDecimal.ZERO : item.getRemainingShippedQuantity();
+                boolean hasOutbound = shipped.compareTo(BigDecimal.ZERO) > 0;
+                if (hasOutbound && remainingShipped.compareTo(BigDecimal.ZERO) <= 0) {
+                    item.setApproveStatus(0);
+                } else {
+                    Integer hasSufficientStock = item.getHasSufficientStock();
+                    if (hasSufficientStock != null && hasSufficientStock == 0) {
+                        item.setApproveStatus(0);
+                    } else {
+                        item.setApproveStatus(1);
+                    }
+                }
+            }
+            ProductModel productModel = finalProductModelMap.get(item.getProductModelId());
+            if (productModel != null) {
+                item.setThickness(productModel.getThickness());
+            }
+        });
         return salesLedgerProducts;
     }
 
@@ -216,14 +294,38 @@
 
         int result;
         Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
+        salesLedgerProduct.fillRemainingQuantity();
         if (salesLedgerProduct.getId() == null) {
             salesLedgerProduct.setRegisterDate(LocalDateTime.now());
             result = salesLedgerProductMapper.insert(salesLedgerProduct);
             addProductionData(salesLedgerProduct);
         } else {
+            SalesLedgerProduct dbBefore = salesLedgerProductMapper.selectById(salesLedgerProduct.getId());
+            if (dbBefore == null) {
+                throw new RuntimeException("閿�鍞骇鍝佷笉瀛樺湪锛屾棤娉曠紪杈�");
+            }
+            // 涓嶈兘鎶婅鍗曟暟閲忔敼鍒板皬浜庡凡鍙戣揣鏁伴噺锛屽惁鍒欒处瀹炰笉涓�鑷�
+            BigDecimal shipped = dbBefore.getShippedQuantity() == null ? BigDecimal.ZERO : dbBefore.getShippedQuantity();
+            BigDecimal newOrderQty = salesLedgerProduct.getQuantity() == null ? BigDecimal.ZERO : salesLedgerProduct.getQuantity();
+            if (newOrderQty.compareTo(shipped) < 0) {
+                throw new RuntimeException("缂栬緫澶辫触,璁㈠崟鏁伴噺涓嶈兘灏忎簬宸插彂璐ф暟閲�");
+            }
             //鏌ヨ鍘熸湰鐨勪骇鍝佸瀷鍙穒d
             salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity());
             result = salesLedgerProductMapper.updateById(salesLedgerProduct);
+
+            // 鍏佽鈥滃鍏ュ簱鈥濓細褰撹鍗曟暟閲忚涓嬭皟鏃讹紝鍙洖閫�鈥滀笅璋冨樊棰濃�濆搴旂殑鍏ュ簱鏁伴噺
+            // 渚嬶細鍘熻鍗�100锛屽凡鍏ュ簱120锛涙敼涓�50 -> 鍙洖閫�50锛屾渶缁堝凡鍏ュ簱70锛堜笉寮哄帇鍒�50锛�
+            BigDecimal oldOrderQty = dbBefore.getQuantity() == null ? BigDecimal.ZERO : dbBefore.getQuantity();
+            BigDecimal stocked = dbBefore.getStockedQuantity() == null ? BigDecimal.ZERO : dbBefore.getStockedQuantity();
+            BigDecimal reduced = oldOrderQty.subtract(newOrderQty);
+            if (reduced.compareTo(BigDecimal.ZERO) > 0) {
+                BigDecimal rollbackQty = stocked.min(reduced);
+                if (rollbackQty.compareTo(BigDecimal.ZERO) > 0) {
+                    rollbackExcessStockIn(dbBefore, rollbackQty);
+                }
+            }
+
             /*鍒犻櫎瀵瑰簲鐨勭敓浜ф暟鎹苟閲嶆柊鏂板*/
             deleteProductionData(Arrays.asList(salesLedgerProduct.getId()));
             // 鍒犻櫎鐢熶骇鏍哥畻鏁版嵁
@@ -260,8 +362,106 @@
 
             //  娓呯┖閿�鍞骇鍝佺粦瀹氱殑鍔犲伐
             salesLedgerProductProcessBindService.updateProductProcessBind(salesLedgerProduct.getSalesProductProcessList(), salesLedgerProduct.getId());
+
+            // 鏂板/缂栬緫浜у搧鍚庯紝鍒锋柊閿�鍞富鍗曞叆搴撶姸鎬侊紙閬垮厤鍥犳柊澧炴湭鍏ュ簱琛屽鑷寸姸鎬佷笉鍑嗭級
+            refreshSalesLedgerStockStatus(salesLedgerId);
         }
         return result;
+    }
+
+    private void rollbackExcessStockIn(SalesLedgerProduct dbBefore, BigDecimal rollbackQtyTotal) {
+        if (dbBefore == null || rollbackQtyTotal == null || rollbackQtyTotal.compareTo(BigDecimal.ZERO) <= 0) {
+            return;
+        }
+        if (dbBefore.getProductModelId() == null) {
+            throw new RuntimeException("鍥為��鍏ュ簱澶辫触,浜у搧瑙勬牸鏈淮鎶�");
+        }
+        Long productId = dbBefore.getId();
+        //  鍊掑簭鍥為��锛氫紭鍏堝洖閫�鈥滄壂鐮佸叆搴撯�濓紝鍐嶅洖閫�鈥滄墜鍔ㄥ叆搴撯��
+        List<String> recordTypes = Arrays.asList(
+                StockInQualifiedRecordTypeEnum.SALE_SCAN_STOCK_IN.getCode(),
+                StockInQualifiedRecordTypeEnum.SALE_STOCK_IN.getCode()
+        );
+        List<StockInRecord> records = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
+                .eq(StockInRecord::getSalesLedgerProductId, productId)
+                .eq(StockInRecord::getType, "0")
+                .in(StockInRecord::getRecordType, recordTypes)
+                .orderByDesc(StockInRecord::getCreateTime));
+        BigDecimal remaining = rollbackQtyTotal;
+        for (StockInRecord r : records) {
+            if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
+                break;
+            }
+            BigDecimal inNum = r.getStockInNum() == null ? BigDecimal.ZERO : r.getStockInNum();
+            if (inNum.compareTo(BigDecimal.ZERO) <= 0) {
+                continue;
+            }
+            BigDecimal rollbackQty = inNum.min(remaining);
+
+            StockInventoryDto stockInventoryDto = new StockInventoryDto();
+            stockInventoryDto.setProductModelId(dbBefore.getProductModelId());
+            stockInventoryDto.setQualitity(rollbackQty);
+            int affectRows = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
+            if (affectRows <= 0) {
+                throw new RuntimeException("鍥為��鍏ュ簱澶辫触,褰撳墠搴撳瓨涓嶈冻,鏃犳硶鎵e洖瓒呭叆搴撴暟閲�");
+            }
+
+            BigDecimal newInNum = inNum.subtract(rollbackQty);
+            if (newInNum.compareTo(BigDecimal.ZERO) <= 0) {
+                stockInRecordMapper.deleteById(r.getId());
+            } else {
+                r.setStockInNum(newInNum);
+                stockInRecordMapper.updateById(r);
+            }
+            remaining = remaining.subtract(rollbackQty);
+        }
+        if (remaining.compareTo(BigDecimal.ZERO) > 0) {
+            throw new RuntimeException("鍥為��鍏ュ簱澶辫触,鏈壘鍒拌冻澶熺殑鍏ュ簱璁板綍鐢ㄤ簬鎵e洖瓒呭叆搴撴暟閲�");
+        }
+
+        //  鍥炲啓浜у搧琛屽凡鍏ュ簱鏁伴噺涓庣姸鎬�
+        SalesLedgerProduct fresh = salesLedgerProductMapper.selectById(dbBefore.getId());
+        if (fresh == null) {
+            return;
+        }
+        BigDecimal newStocked = (fresh.getStockedQuantity() == null ? BigDecimal.ZERO : fresh.getStockedQuantity()).subtract(rollbackQtyTotal);
+        if (newStocked.compareTo(BigDecimal.ZERO) < 0) {
+            newStocked = BigDecimal.ZERO;
+        }
+        BigDecimal orderQty = fresh.getQuantity() == null ? BigDecimal.ZERO : fresh.getQuantity();
+        int stockStatus;
+        if (newStocked.compareTo(BigDecimal.ZERO) <= 0) stockStatus = 0;
+        else if (orderQty.compareTo(BigDecimal.ZERO) > 0 && newStocked.compareTo(orderQty) < 0) stockStatus = 1;
+        else stockStatus = 2;
+        fresh.setStockedQuantity(newStocked);
+        fresh.setProductStockStatus(stockStatus);
+        fresh.fillRemainingQuantity();
+        salesLedgerProductMapper.updateById(fresh);
+    }
+
+    private void refreshSalesLedgerStockStatus(Long salesLedgerId) {
+        if (salesLedgerId == null) {
+            return;
+        }
+        SalesLedger ledger = salesLedgerMapper.selectById(salesLedgerId);
+        if (ledger == null) {
+            return;
+        }
+        List<SalesLedgerProduct> allProducts = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
+                .eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)
+                .eq(SalesLedgerProduct::getType, 1));
+        if (CollectionUtils.isEmpty(allProducts)) {
+            ledger.setStockStatus(0);
+            salesLedgerMapper.updateById(ledger);
+            return;
+        }
+        boolean anyInbound = allProducts.stream().anyMatch(p -> {
+            BigDecimal sq = p.getStockedQuantity();
+            return sq != null && sq.compareTo(BigDecimal.ZERO) > 0;
+        });
+        boolean allFull = allProducts.stream().allMatch(p -> Objects.equals(p.getProductStockStatus(), 2));
+        ledger.setStockStatus(allFull ? 2 : (anyInbound ? 1 : 0));
+        salesLedgerMapper.updateById(ledger);
     }
 
     /**
@@ -438,6 +638,7 @@
             } else {
                 item.setStatusName("鏈畬鎴愪粯娆�");
             }
+            item.fillRemainingQuantity();
         });
         return salesLedgerProductDtoIPage;
     }
@@ -452,6 +653,7 @@
             } else {
                 item.setStatusName("鏈畬鎴愪粯娆�");
             }
+            item.fillRemainingQuantity();
         });
         return salesLedgerProductDtoIPage;
     }

--
Gitblit v1.9.3