From 7bf754a7835d06f26240c4ca15bc5f83650de377 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 21 九月 2026 20:52:25 +0800
Subject: [PATCH] docs(api): 更新信托基金与代储台账及油品出库相关接口文档

---
 src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java |  316 +++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 251 insertions(+), 65 deletions(-)

diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
index 4a2f241..08960b9 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -24,10 +24,13 @@
 import com.ruoyi.outsourcing.pojo.OutsourcingOrder;
 import com.ruoyi.outsourcing.pojo.OutsourcingOrderProduct;
 import com.ruoyi.sales.dto.SalesLedgerDto;
+import com.ruoyi.sales.mapper.SalesLedgerArrivalMapper;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.mapper.StockOutRecordSalesLedgerMapper;
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.StockOutRecordSalesLedger;
 
 import java.time.LocalDateTime;
 import com.ruoyi.stock.dto.OilOutAvailableDto;
@@ -49,8 +52,11 @@
 import org.springframework.util.CollectionUtils;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -64,13 +70,16 @@
 @Service
 @RequiredArgsConstructor
 public class StockOutRecordServiceImpl extends ServiceImpl<StockOutRecordMapper, StockOutRecord> implements StockOutRecordService {
+
     /**
-     * 鍑哄簱绫诲埆锛氶攢鍞�傞攢鍞被娌瑰搧鍑哄簱鍦ㄥ鎵归�氳繃鏃舵寜鎵规鎵e噺搴撳瓨
+     * 鍑哄簱绫诲埆锛氶攢鍞�傚敮涓�鐨勫尯鍒槸閿�鍞被鍑哄簱蹇呴』閫夎溅杈嗐�佹寜杞﹁締绠椾笂娆″墿浣欐补閲忥紱
+     * 鎵e簱瀛樸�佽繘鍙戣揣鍙拌处杩欎簺瀵规墍鏈夊嚭搴撶被鍒竴瑙嗗悓浠�
      */
     private static final String OUT_CATEGORY_SALE = "閿�鍞�";
 
     /**
-     * 鍑哄簱绫诲埆锛氫唬鍌ㄣ�傚鎴锋妸鑷繁鐨勬补瀵勫瓨鍦ㄦ垜浠殑缃愰噷锛屾彁璧板彧鏄褰曚竴绗旓紝涓嶆墸搴撳瓨銆佷笉杩涘彂璐у彴璐�
+     * 鍑哄簱绫诲埆锛氫唬鍌ㄣ�傚鎴锋妸鑷繁鐨勬补瀵勫瓨鍦ㄦ垜浠殑缃愰噷銆�
+     * 鍑哄簱瑕佹眰涓庨攢鍞竴鑷达紙閫夋壒娆°�佸鎵规墸搴撳瓨銆佸垹闄ゅ綊杩樸�佽繘鍙戣揣鍙拌处锛夛紝鍙槸涓嶈姹傞�夎溅杈�
      */
     private static final String OUT_CATEGORY_DEPOSIT = "浠e偍";
 
@@ -94,6 +103,8 @@
     private final OutsourcingOrderMapper outsourcingOrderMapper;
     private final SalesLedgerMapper salesLedgerMapper;
     private final SalesLedgerProductMapper salesLedgerProductMapper;
+    private final StockOutRecordSalesLedgerMapper stockOutRecordSalesLedgerMapper;
+    private final SalesLedgerArrivalMapper salesLedgerArrivalMapper;
 
     @Override
     public IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto) {
@@ -102,26 +113,34 @@
 
     @Override
     public int add(StockOutRecordDto stockOutRecordDto) {
-        resolveSalesLedgerBinding(stockOutRecordDto);
-        if (OUT_CATEGORY_SALE.equals(stockOutRecordDto.getOutCategory())) {
-            // 閿�鍞被娌瑰搧鍑哄簱瀹℃壒閫氳繃鏃惰鎸夋壒娆℃墸鍑忓簱瀛橈紝鎵规涓嶈兘涓虹┖
-            if (StringUtils.isEmpty(stockOutRecordDto.getBatchNo())) {
-                throw new BaseException("閿�鍞被鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规");
-            }
-            resolveVehicle(stockOutRecordDto);
-            checkSaleOutAvailable(stockOutRecordDto);
-        }
+        List<StockOutRecordSalesLedger> ledgerBindings = resolveSalesLedgerBinding(stockOutRecordDto);
         LocalDateTime createTime = stockOutRecordDto.getCreateTime();
         if (createTime == null) {
             createTime = LocalDateTime.now();
         }
+        // 鍑哄簱鍗曞彿鍏堜簬鏍¢獙鐢熸垚锛屽ソ璁╂牎楠屽け璐ユ椂鐨勬姤閿欏甫涓婂嚭搴撴壒娆�
         String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches", createTime);
         stockOutRecordDto.setOutboundBatches(no);
         stockOutRecordDto.setCreateTime(createTime);
+        // 娌瑰搧鍑哄簱锛坱ype 涓虹┖锛夊鎵归�氳繃鏃朵竴寰嬫寜鎵规鎵e噺搴撳瓨锛屾壒娆′笉鑳戒负绌恒��
+        // type=0/1 鐨勫悎鏍笺�佷笉鍚堟牸鍑哄簱璧板悇鑷垎鏀紝涓嶅彈杩欓噷绾︽潫
+        if (StringUtils.isEmpty(stockOutRecordDto.getType())) {
+            if (StringUtils.isEmpty(stockOutRecordDto.getBatchNo())) {
+                throw new BaseException("娌瑰搧鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规");
+            }
+            if (OUT_CATEGORY_SALE.equals(stockOutRecordDto.getOutCategory())) {
+                // 鍙湁閿�鍞被鎸夎溅杈嗙畻娈嬫补锛屼唬鍌�/瀹㈠瓨涓嶈姹傞�夎溅杈�
+                resolveVehicle(stockOutRecordDto);
+            }
+            checkOilOutAvailable(stockOutRecordDto);
+        }
         if (StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode().equals(stockOutRecordDto.getRecordType())){
             stockOutRecordDto.setApprovalStatus(3);
         }
-        return stockOutRecordMapper.insert(stockOutRecordDto);
+        int rows = stockOutRecordMapper.insert(stockOutRecordDto);
+        // 鍑哄簱鍗� id 鐢辫嚜澧炰富閿洖濉紝鎷垮埌鍚庢墠鑳借惤缁戝畾琛�
+        saveLedgerBindings(stockOutRecordDto.getId(), ledgerBindings);
+        return rows;
     }
 
     @Override
@@ -132,53 +151,187 @@
             throw new BaseException("璇ュ嚭搴撹褰曚笉瀛樺湪,鏃犳硶鏇存柊!!!");
         }
 
-        resolveSalesLedgerBinding(stockOutRecordDto);
-        String[] ignoreProperties = {"id", "outbound_batches"};//鎺掗櫎id灞炴��
+        List<StockOutRecordSalesLedger> ledgerBindings = resolveSalesLedgerBinding(stockOutRecordDto);
+        // 鎺掗櫎 id 灞炴�э紱recordType/recordId 鐢卞悗绔淮鎶わ紙娌瑰搧鍑哄簱鎭掍负 '1' / 0锛屾墜宸ュ彂璐у崟鍑哄簱鐢卞彂璐ф祦绋嬪啓鍏ワ級锛�
+        // 鍓嶇缂栬緫琛ㄥ崟涓嶅洖浼犱細鎶婂畠浠啿鎴� null锛岀牬鍧忓嚭搴撹褰曠殑鏉ユ簮鏍囪瘑
+        String[] ignoreProperties = {"id", "outbound_batches", "recordType", "recordId"};
         BeanUtils.copyProperties(stockOutRecordDto, stockOutRecord, ignoreProperties);
-        return stockOutRecordMapper.updateById(stockOutRecord);
+        int rows = stockOutRecordMapper.updateById(stockOutRecord);
+        rebindLedger(id, ledgerBindings);
+        return rows;
     }
 
     /**
-     * 缁戝畾閿�鍞彴璐︼細瀹㈡埛涓�寰嬩互鍙拌处涓哄噯瑕嗙洊锛岄伩鍏嶅嚭搴撹褰曚笌鍙拌处鐨勫鎴峰涓嶄笂锛涘嚭搴撶被鍒寜鍙拌处鐨勯攢鍞被鍨嬭嚜鍔ㄥ畾锛�
-     * 鍏嶅緱鍑虹幇銆岀粦浜嗗崠娌瑰彴璐﹀嵈鎸変唬鍌ㄤ笉鎵e簱瀛樸�嶈繖绫荤煕鐩炬暟鎹��
+     * 缁戝畾閿�鍞彴璐︼細涓�娆″嚭搴撳彲浠ュ垎閫佸涓鎴凤紝鎵�浠ュ彴璐︽槸涓垪琛紝姣忎釜鍙拌处鎵嬪伐濉湰娆″垎鎽婂埌鐨勬暟閲忥紝
+     * 鍚勫彴璐︽暟閲忎箣鍜屽繀椤荤瓑浜庢湰娆″嚭搴撴�婚噺銆傚鎴蜂竴寰嬩互鍙拌处涓哄噯瑕嗙洊锛屽嚭搴撶被鍒寜鍙拌处鐨勯攢鍞被鍨嬭嚜鍔ㄥ畾锛�
+     * 鍏嶅緱鍑虹幇銆岀粦浜嗕唬鍌ㄥ彴璐﹀嵈鎸夐攢鍞姹傞�夎溅杈嗐�嶈繖绫荤煕鐩炬暟鎹��
      * <ul>
-     *     <li>鍗栨补锛堝惈 ledgerType 涓虹┖锛夛細鍑哄簱绫诲埆缃�岄攢鍞�嶏紝骞舵牎楠屾墍閫夎鏍肩‘瀹炲湪璇ュ彴璐︾殑閿�鍞槑缁嗛噷</li>
-     *     <li>浠e偍锛氬嚭搴撶被鍒疆銆屼唬鍌ㄣ�嶏紝甯﹀叆鍙拌处鐨勬补搴�/鍌ㄧ綈锛岃烦杩囦骇鍝佹牎楠� 鈥斺��
-     *         浠e偍鍙拌处鏈韩灏辨槸鍗栧瓨鍌ㄤ綅缃紝娌℃湁浜у搧鏄庣粏</li>
+     *     <li>鍗栨补锛堝惈 ledgerType 涓虹┖锛夛細鍑哄簱绫诲埆缃�岄攢鍞�嶏紝閫愪釜鏍¢獙鎵�閫夎鏍肩‘瀹炲湪璇ュ彴璐︾殑閿�鍞槑缁嗛噷锛�
+     *         骞舵妸鍛戒腑鐨勬槑缁� id 钀借繘缁戝畾琛岋紙鍚屼竴鍙拌处鍚岃鏍煎琛屾椂鍥哄畾鍙� id 鏈�灏忕殑閭f潯锛�</li>
+     *     <li>浠e偍锛氬嚭搴撶被鍒疆銆屼唬鍌ㄣ�嶏紝甯﹀叆鍙拌处鐨勬补搴�/鍌ㄧ綈锛岃烦杩囥�岃鏍兼槸鍚﹀湪鍙拌处鏄庣粏閲屻�嶇殑鏍¢獙 鈥斺��
+     *         浠e偍鍙拌处鏈韩灏辨槸鍗栧瓨鍌ㄤ綅缃�佹病鏈変骇鍝佹槑缁嗭紝浣嗘墸搴撳瓨瑕佹寜瑙勬牸瀹氫綅锛岃鏍间粛蹇呴』閫夈��
+     *         浠e偍涓嶈兘涓庡叾浠栧彴璐︽贩缁戯紝涔熶笉浜х粦瀹氳锛堝畠鐨勬补鏄鎴疯嚜宸辩殑锛屼笉璧板埌璐э級</li>
      * </ul>
-     * salesLedgerId 涓虹┖鏃舵暣浣撹烦杩囷紝涓嶇粦鍙拌处鐨勫嚭搴撹涓轰笌鏀瑰姩鍓嶅畬鍏ㄤ竴鑷�
+     *
+     * @return 寰呰惤搴撶殑缁戝畾琛岋紱null 琛ㄧず鏈璇锋眰娌″甫鍙拌处淇℃伅锛岃皟鐢ㄦ柟搴斾繚鎸佸凡鏈夌粦瀹氫笉鍙�
      */
-    private void resolveSalesLedgerBinding(StockOutRecordDto stockOutRecordDto) {
-        if (stockOutRecordDto.getSalesLedgerId() == null) {
-            return;
+    private List<StockOutRecordSalesLedger> resolveSalesLedgerBinding(StockOutRecordDto stockOutRecordDto) {
+        List<StockOutRecordSalesLedger> ledgerList = stockOutRecordDto.getLedgerList();
+        if (ledgerList == null && stockOutRecordDto.getSalesLedgerId() != null) {
+            // 鍏煎鍙紶 salesLedgerId 鐨勬棫鍏ュ弬锛氳浣溿�岀粦涓�涓彴璐︺�佹暟閲忓氨鏄湰娆″嚭搴撴�婚噺銆�
+            StockOutRecordSalesLedger single = new StockOutRecordSalesLedger();
+            single.setSalesLedgerId(stockOutRecordDto.getSalesLedgerId());
+            single.setQuantity(stockOutRecordDto.getStockOutNum());
+            ledgerList = new ArrayList<>();
+            ledgerList.add(single);
+        } else if (ledgerList == null) {
+            return null;
         }
-        SalesLedger salesLedger = salesLedgerMapper.selectById(stockOutRecordDto.getSalesLedgerId());
-        if (salesLedger == null) {
-            throw new BaseException("鎵�閫夐攢鍞彴璐︿笉瀛樺湪,閿�鍞彴璐d:" + stockOutRecordDto.getSalesLedgerId());
+        if (ledgerList.isEmpty()) {
+            // 鏄庣‘鐨勩�屼笉缁戝彴璐︺��
+            stockOutRecordDto.setSalesLedgerId(null);
+            return ledgerList;
         }
-        stockOutRecordDto.setCustomerId(salesLedger.getCustomerId());
-        stockOutRecordDto.setCustomerName(salesLedger.getCustomerName());
-        if (LEDGER_TYPE_DEPOSIT.equals(salesLedger.getLedgerType())) {
-            stockOutRecordDto.setOutCategory(OUT_CATEGORY_DEPOSIT);
-            stockOutRecordDto.setOilDepotId(salesLedger.getOilDepotId());
-            stockOutRecordDto.setOilDepotName(salesLedger.getOilDepotName());
-            stockOutRecordDto.setTankId(salesLedger.getTankId());
-            stockOutRecordDto.setTankNo(salesLedger.getTankNo());
-            return;
+        List<SalesLedger> ledgers = new ArrayList<>(ledgerList.size());
+        for (StockOutRecordSalesLedger item : ledgerList) {
+            if (item == null || item.getSalesLedgerId() == null) {
+                throw new BaseException("缁戝畾鐨勯攢鍞彴璐︿笉鑳戒负绌�");
+            }
+            SalesLedger salesLedger = salesLedgerMapper.selectById(item.getSalesLedgerId());
+            if (salesLedger == null) {
+                throw new BaseException("鎵�閫夐攢鍞彴璐︿笉瀛樺湪,閿�鍞彴璐d:" + item.getSalesLedgerId());
+            }
+            ledgers.add(salesLedger);
+        }
+        // 棣栭」鍙拌处缁х画鍐欒繘鍑哄簱璁板綍鐨勫崟鍊煎垪锛屽嚭搴撶鐞嗗垪琛ㄣ�佸簱瀛樻姤琛ㄩ兘鎸夊畠灞曠ず
+        SalesLedger first = ledgers.get(0);
+        stockOutRecordDto.setSalesLedgerId(first.getId());
+        stockOutRecordDto.setCustomerId(first.getCustomerId());
+        stockOutRecordDto.setCustomerName(first.getCustomerName());
+
+        boolean hasDeposit = ledgers.stream().anyMatch(item -> LEDGER_TYPE_DEPOSIT.equals(item.getLedgerType()));
+        if (hasDeposit) {
+            if (ledgers.size() > 1) {
+                throw new BaseException("浠e偍鍙拌处涓嶈兘涓庡叾浠栭攢鍞彴璐︿竴璧风粦瀹�,涓�娆″嚭搴撳彧鑳界粦涓�涓唬鍌ㄥ彴璐�");
+            }
+            applyDepositLedger(stockOutRecordDto, first);
+            return new ArrayList<>();
         }
         stockOutRecordDto.setOutCategory(OUT_CATEGORY_SALE);
         if (stockOutRecordDto.getProductModelId() == null) {
-            throw new BaseException("缁戝畾閿�鍞彴璐︽椂蹇呴』閫夋嫨浜у搧瑙勬牸,閿�鍞悎鍚屽彿:" + salesLedger.getSalesContractNo());
+            throw new BaseException("缁戝畾閿�鍞彴璐︽椂蹇呴』閫夋嫨浜у搧瑙勬牸,閿�鍞悎鍚屽彿:" + first.getSalesContractNo());
         }
-        boolean belongsToLedger = salesLedgerProductMapper.selectList(
-                        Wrappers.<SalesLedgerProduct>lambdaQuery()
-                                .eq(SalesLedgerProduct::getSalesLedgerId, stockOutRecordDto.getSalesLedgerId())
-                                .eq(SalesLedgerProduct::getType, LEDGER_PRODUCT_TYPE_SALE))
-                .stream()
-                .anyMatch(item -> stockOutRecordDto.getProductModelId().equals(item.getProductModelId()));
-        if (!belongsToLedger) {
-            throw new BaseException("鎵�閫変骇鍝佷笉鍦ㄨ閿�鍞彴璐︽槑缁嗕腑,閿�鍞悎鍚屽彿:" + salesLedger.getSalesContractNo());
+        BigDecimal stockOutNum = stockOutRecordDto.getStockOutNum();
+        if (stockOutNum == null || stockOutNum.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new BaseException("璇峰~鍐欐湰娆″嚭搴撴暟閲�(鍚�)");
         }
+        BigDecimal total = BigDecimal.ZERO;
+        for (int index = 0; index < ledgerList.size(); index++) {
+            StockOutRecordSalesLedger item = ledgerList.get(index);
+            SalesLedger salesLedger = ledgers.get(index);
+            BigDecimal quantity = item.getQuantity();
+            if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
+                throw new BaseException("姣忎釜閿�鍞彴璐︾殑缁戝畾鏁伴噺蹇呴』澶т簬0,閿�鍞悎鍚屽彿:" + salesLedger.getSalesContractNo());
+            }
+            SalesLedgerProduct detail = findLedgerSaleDetail(salesLedger.getId(), stockOutRecordDto.getProductModelId());
+            if (detail == null) {
+                throw new BaseException("鎵�閫変骇鍝佷笉鍦ㄨ閿�鍞彴璐︽槑缁嗕腑,閿�鍞悎鍚屽彿:" + salesLedger.getSalesContractNo());
+            }
+            item.setSalesLedgerProductId(detail.getId());
+            item.setCustomerId(salesLedger.getCustomerId());
+            item.setCustomerName(salesLedger.getCustomerName());
+            total = total.add(quantity);
+        }
+        if (total.compareTo(stockOutNum) != 0) {
+            throw new BaseException("鍚勯攢鍞彴璐︾粦瀹氭暟閲忎箣鍜屽繀椤荤瓑浜庢湰娆″嚭搴撴暟閲�,鍑哄簱鏁伴噺:" + toPlain(stockOutNum)
+                    + ",缁戝畾鏁伴噺涔嬪拰:" + toPlain(total));
+        }
+        return ledgerList;
+    }
+
+    /**
+     * 鍚屼竴鍙拌处鍙兘鏈夊琛屽悓瑙勬牸鐨勯攢鍞槑缁嗭紝鍥哄畾鍙� id 鏈�灏忕殑閭f潯锛屼繚璇佹瘡娆$粦瀹氱殑鏄庣粏 id 绋冲畾
+     */
+    private SalesLedgerProduct findLedgerSaleDetail(Long salesLedgerId, Long productModelId) {
+        List<SalesLedgerProduct> details = salesLedgerProductMapper.selectList(
+                Wrappers.<SalesLedgerProduct>lambdaQuery()
+                        .eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)
+                        .eq(SalesLedgerProduct::getType, LEDGER_PRODUCT_TYPE_SALE)
+                        .eq(SalesLedgerProduct::getProductModelId, productModelId)
+                        .orderByAsc(SalesLedgerProduct::getId));
+        return details.isEmpty() ? null : details.get(0);
+    }
+
+    /**
+     * 浠e偍鍙拌处锛氬崠鐨勬槸瀛樺偍浣嶇疆銆佹病鏈変骇鍝佹槑缁嗭紝浣嗘墸搴撳瓨瑕佹寜瑙勬牸瀹氫綅锛岃鏍间粛蹇呴』閫�
+     */
+    private void applyDepositLedger(StockOutRecordDto stockOutRecordDto, SalesLedger salesLedger) {
+        stockOutRecordDto.setOutCategory(OUT_CATEGORY_DEPOSIT);
+        stockOutRecordDto.setOilDepotId(salesLedger.getOilDepotId());
+        stockOutRecordDto.setOilDepotName(salesLedger.getOilDepotName());
+        stockOutRecordDto.setTankId(salesLedger.getTankId());
+        stockOutRecordDto.setTankNo(salesLedger.getTankNo());
+        if (stockOutRecordDto.getProductModelId() == null) {
+            throw new BaseException("浠e偍鍑哄簱闇�瑕佹墸鍑忓簱瀛�,蹇呴』閫夋嫨浜у搧瑙勬牸,閿�鍞悎鍚屽彿:" + salesLedger.getSalesContractNo());
+        }
+    }
+
+    /**
+     * 钀藉簱缁戝畾琛屻�傛槑缁� id / 瀹㈡埛 / 鍑哄簱鍗� id 涓�寰嬬敤鍚庣鏍¢獙鏃剁畻鍑虹殑鍊硷紝鍓嶇浼犵殑琚鐩栥��
+     * 閫愭潯 insert锛氭湰琛ㄤ竴娆″嚭搴撳彧鏈夊嚑琛岋紝涓� MyBaseMapper 鐨� insertBatchSomeColumn
+     * 椤圭洰閲屾病閰嶈嚜瀹氫箟 SqlInjector锛屾柟娉曟牴鏈病娉ㄥ唽锛堣皟鐢ㄤ細鎶� Invalid bound statement锛�
+     */
+    private void saveLedgerBindings(Long stockOutRecordId, List<StockOutRecordSalesLedger> bindings) {
+        if (stockOutRecordId == null || CollectionUtils.isEmpty(bindings)) {
+            return;
+        }
+        for (StockOutRecordSalesLedger binding : bindings) {
+            binding.setId(null);
+            binding.setStockOutRecordId(stockOutRecordId);
+            stockOutRecordSalesLedgerMapper.insert(binding);
+        }
+    }
+
+    /**
+     * 閲嶆柊缁戝畾鍙拌处锛氱粦瀹氭病鍙樺氨鍘熸牱涓嶅姩锛堜繚浣忕粦瀹氳 id锛夛紱鍙樹簡鍒欏厛鎸夊嚭搴撳崟鐗╃悊鍒犳棫鍐嶉噸鎻掋��
+     * 宸茬櫥璁板埌璐ц褰曟椂涓嶅厑璁告敼 鈥斺�� 鍒拌揣璁板綍鎸傚湪銆屽彴璐γ楀嚭搴撳崟銆嶄笂锛屾敼缁戜細璁╁畠鎸囧悜涓嶅瓨鍦ㄧ殑鍒嗘憡鏁伴噺
+     */
+    private void rebindLedger(Long stockOutRecordId, List<StockOutRecordSalesLedger> bindings) {
+        if (bindings == null) {
+            return;
+        }
+        List<StockOutRecordSalesLedger> existing = stockOutRecordSalesLedgerMapper.selectList(
+                Wrappers.<StockOutRecordSalesLedger>lambdaQuery()
+                        .eq(StockOutRecordSalesLedger::getStockOutRecordId, stockOutRecordId));
+        if (sameBinding(existing, bindings)) {
+            return;
+        }
+        if (salesLedgerArrivalMapper.countByStockOutRecordId(stockOutRecordId) > 0) {
+            throw new BaseException("璇ュ嚭搴撳崟宸茬櫥璁板埌璐ц褰�,鏃犳硶淇敼缁戝畾鐨勯攢鍞彴璐�,鍑哄簱鍗昳d:" + stockOutRecordId);
+        }
+        stockOutRecordSalesLedgerMapper.delete(Wrappers.<StockOutRecordSalesLedger>lambdaQuery()
+                .eq(StockOutRecordSalesLedger::getStockOutRecordId, stockOutRecordId));
+        saveLedgerBindings(stockOutRecordId, bindings);
+    }
+
+    /**
+     * 鍙拌处涓庡垎鎽婃暟閲忔槸鍚﹀畬鍏ㄤ竴鑷达紙椤哄簭鏃犲叧锛�
+     */
+    private boolean sameBinding(List<StockOutRecordSalesLedger> existing, List<StockOutRecordSalesLedger> incoming) {
+        if (existing.size() != incoming.size()) {
+            return false;
+        }
+        Map<Long, BigDecimal> existingQuantity = new HashMap<>(existing.size());
+        for (StockOutRecordSalesLedger item : existing) {
+            existingQuantity.put(item.getSalesLedgerId(), item.getQuantity());
+        }
+        for (StockOutRecordSalesLedger item : incoming) {
+            BigDecimal quantity = existingQuantity.get(item.getSalesLedgerId());
+            if (quantity == null || quantity.compareTo(item.getQuantity()) != 0) {
+                return false;
+            }
+        }
+        return true;
     }
 
     @Override
@@ -189,9 +342,30 @@
                 accountInvoiceApplicationMapper.existsByStockOutRecordId(ids)) {
             throw new BaseException("鍑哄簱璁板綍瀛樺湪寮�绁ㄦ敹娆惧叧鑱旀暟鎹�,鏃犳硶鍒犻櫎!!!");
         }
+        // 娌瑰搧鍑哄簱鐨勫紑绁�/鏀舵鍏宠仈瀛樼殑鏄粦瀹氳 id锛堜笌鍑哄簱鍗� id 鏄袱涓嫭绔� id 绌洪棿锛夛紝蹇呴』鍗曠嫭鍒わ紝
+        // 鍚﹀垯缁戜簡鍙拌处鐨勬补鍝佸嚭搴撹寮�绁ㄥ悗杩樿兘鍒犳帀锛岀暀涓嬫寚鍚戜笉瀛樺湪缁戝畾琛岀殑鍏宠仈鏁版嵁
+        if (!CollectionUtils.isEmpty(ids)) {
+            List<Long> bindingIds = stockOutRecordSalesLedgerMapper.selectList(
+                            Wrappers.<StockOutRecordSalesLedger>lambdaQuery()
+                                    .in(StockOutRecordSalesLedger::getStockOutRecordId, ids))
+                    .stream().map(StockOutRecordSalesLedger::getId).toList();
+            if (!CollectionUtils.isEmpty(bindingIds)
+                    && (accountSalesCollectionMapper.existsByStockOutBindingId(bindingIds)
+                    || accountInvoiceApplicationMapper.existsByStockOutBindingId(bindingIds))) {
+                throw new BaseException("鍑哄簱璁板綍瀛樺湪寮�绁ㄦ敹娆惧叧鑱旀暟鎹�,鏃犳硶鍒犻櫎!!!");
+            }
+        }
         for (Long id : ids) {
             StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
-            if (stockOutRecord.getType().equals("0")) {
+            // 鍒拌揣璁板綍鎸傚湪鍑哄簱鍗曚笅锛屽垹浜嗕細鍙樻垚瀛ゅ効锛岄攢鍞彴璐︾殑宸插彂璐ф暟閲忎篃灏辫涓嶆竻浜�
+            if (salesLedgerArrivalMapper.countByStockOutRecordId(id) > 0) {
+                throw new BaseException("鍑哄簱鍗曞凡鐧昏鍒拌揣璁板綍,鏃犳硶鍒犻櫎,鍑哄簱鎵规:" + stockOutRecord.getOutboundBatches());
+            }
+            // 缁戝畾琛屾槸鍑哄簱鍗曠殑闄勫睘鏁版嵁锛屼竴骞舵竻鎺夛紝涓嶇暀鎸囧悜宸插垹鍑哄簱鍗曠殑瀛ゅ効琛�
+            stockOutRecordSalesLedgerMapper.delete(Wrappers.<StockOutRecordSalesLedger>lambdaQuery()
+                    .eq(StockOutRecordSalesLedger::getStockOutRecordId, id));
+            // type 瀵规补鍝佸嚭搴撴槸 null锛屽父閲忓啓鍦ㄥ墠闈㈤伩鍏� NPE
+            if ("0".equals(stockOutRecord.getType())) {
                 LambdaQueryWrapper<StockInventory> wrapper = new LambdaQueryWrapper<StockInventory>()
                     .eq(StockInventory::getProductModelId, stockOutRecord.getProductModelId());
                 if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) {
@@ -210,7 +384,7 @@
                     stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
                 }
             }
-            else if (stockOutRecord.getType().equals("1")) {
+            else if ("1".equals(stockOutRecord.getType())) {
                 LambdaQueryWrapper<StockUninventory> wrapper = new LambdaQueryWrapper<StockUninventory>()
                         .eq(StockUninventory::getProductModelId, stockOutRecord.getProductModelId());
                 if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) {
@@ -229,19 +403,19 @@
                     stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                 }
             }
-            else if (OUT_CATEGORY_SALE.equals(stockOutRecord.getOutCategory())) {
-                // 閿�鍞被娌瑰搧鍑哄簱鍙湁瀹℃壒閫氳繃鏃舵墠鎵h繃搴撳瓨锛屽垹闄ゅ凡閫氳繃鐨勮褰曢渶鎸夊悓鎵规褰掕繕
-                returnSaleOilOutStock(stockOutRecord);
+            else {
+                // 娌瑰搧鍑哄簱鍙湁瀹℃壒閫氳繃鏃舵墠鎵h繃搴撳瓨锛屽垹闄ゅ凡閫氳繃鐨勮褰曢渶鎸夊悓鎵规褰掕繕
+                returnOilOutStock(stockOutRecord);
             }
         }
         return stockOutRecordMapper.deleteBatchIds(ids);
     }
 
     /**
-     * 閿�鍞被娌瑰搧鍑哄簱褰掕繕搴撳瓨锛屼粎瀵瑰鎵归�氳繃锛堝嵆鎵h繃搴撳瓨锛夌殑璁板綍鐢熸晥銆�
+     * 娌瑰搧鍑哄簱褰掕繕搴撳瓨锛屼粎瀵瑰鎵归�氳繃锛堝嵆鎵h繃搴撳瓨锛夌殑璁板綍鐢熸晥銆�
      * 褰掕繕閲忓彇 deduct_quantity锛堝疄闄呮墸鍑忛噺锛夛紱鏀归�犲墠瀹℃壒閫氳繃鐨勮褰曟病鏈夎鍊硷紝鎸夊叏棰濆綊杩�
      */
-    private void returnSaleOilOutStock(StockOutRecord stockOutRecord) {
+    private void returnOilOutStock(StockOutRecord stockOutRecord) {
         if (!ReviewStatusEnum.APPROVED.getCode().equals(stockOutRecord.getApprovalStatus())
                 || StringUtils.isEmpty(stockOutRecord.getBatchNo())
                 || stockOutRecord.getStockOutNum() == null) {
@@ -330,9 +504,12 @@
                     stockUninventoryDto.setBatchNo(stockOutRecord.getBatchNo());
                     stockUninventoryDto.setQualitity(stockOutRecord.getStockOutNum());
                     stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
-                } else if (OUT_CATEGORY_SALE.equals(stockOutRecord.getOutCategory())) {
-                    // 閿�鍞被娌瑰搧鍑哄簱锛坱ype 涓虹┖锛夊鎵归�氳繃鏃舵寜鎵规鎵e噺搴撳瓨
-                    deductSaleOilOutStock(stockOutRecord);
+                } else {
+                    // 娌瑰搧鍑哄簱锛坱ype 涓虹┖锛夊鎵归�氳繃鏃舵寜鎵规鎵e噺搴撳瓨銆�
+                    // 涓嶅啀鐢熸垚鍙戣揣鍗曪紝涔熶笉鏀� record_type锛氭补鍝佸嚭搴撶殑 record_type 涓�鐩存槸銆�1銆嶏紝
+                    // 鏀规垚銆�13銆嶄細璁╁嚭搴撶鐞嗗垪琛ㄦ寜鍑哄簱绫诲瀷绛涢�夋椂鐪嬩笉鍒板畠銆傚垽鏂�屾槸涓嶆槸閿�鍞被娌瑰搧鍑哄簱銆�
+                    // 涓�寰嬩互 binding 琛紙stock_out_record_sales_ledger锛変负鍑�
+                    deductOilOutStock(stockOutRecord);
                 }
             }
             stockOutRecord.setApprovalStatus(approvalStatus);
@@ -388,19 +565,23 @@
     }
 
     /**
-     * 閿�鍞被娌瑰搧鍑哄簱鎵e噺搴撳瓨銆傚嚭搴撴暟閲忔槸鏈瑁呰溅鎬婚噺锛岃溅涓婁笂娆″墿鐨勬补鏈缁х画鐢紝
-     * 鎵�浠ュ彧浠庡簱瀛樿ˉ瓒炽�屽嚭搴撴暟閲� 鈭� 涓婃鍓╀綑娌归噺銆嶇殑宸銆�
+     * 娌瑰搧鍑哄簱鎵e噺搴撳瓨銆傚嚭搴撴暟閲忔槸鏈瑁呰溅鎬婚噺锛岃溅涓婁笂娆″墿鐨勬补鏈缁х画鐢紝
+     * 鎵�浠ュ彧浠庡簱瀛樿ˉ瓒炽�屽嚭搴撴暟閲� 鈭� 涓婃鍓╀綑娌归噺銆嶇殑宸锛涗唬鍌�/瀹㈠瓨娌¢�夎溅杈嗭紝
+     * 涓婃鍓╀綑娌归噺鎸� 0 绠楋紝鍗冲叏棰濇墸鍑忋��
      * updateSubtractStockInventory 鑷甫 qualitity &gt;= 鏁伴噺 鐨勫畧鍗紝褰卞搷琛屾暟涓� 0 鍗冲簱瀛樹笉瓒筹紝
      * 鎶涢敊鐢卞灞備簨鍔″洖婊�
      */
-    private void deductSaleOilOutStock(StockOutRecord stockOutRecord) {
+    private void deductOilOutStock(StockOutRecord stockOutRecord) {
         String suffix = ",鍑哄簱鎵规:" + stockOutRecord.getOutboundBatches();
         if (StringUtils.isEmpty(stockOutRecord.getBatchNo())) {
-            throw new BaseException("閿�鍞被鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规" + suffix);
+            throw new BaseException("娌瑰搧鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规" + suffix);
+        }
+        if (stockOutRecord.getProductModelId() == null) {
+            throw new BaseException("娌瑰搧鍑哄簱蹇呴』閫夋嫨浜у搧瑙勬牸" + suffix);
         }
         BigDecimal quantity = stockOutRecord.getStockOutNum();
         if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
-            throw new BaseException("閿�鍞被鍑哄簱鏁伴噺蹇呴』澶т簬0" + suffix);
+            throw new BaseException("娌瑰搧鍑哄簱鏁伴噺蹇呴』澶т簬0" + suffix);
         }
         BigDecimal lastResidual = getLastResidualOil(stockOutRecord.getVehicleId(), stockOutRecord.getProductModelId(), stockOutRecord.getId());
         if (lastResidual.compareTo(quantity) > 0) {
@@ -501,13 +682,17 @@
     }
 
     /**
-     * 鍑哄簱鏁伴噺鏄湰娆¤杞︽�婚噺锛屽繀椤昏惤鍦ㄣ�屼笂娆″墿浣欐补閲� ~ 涓婃鍓╀綑娌归噺 + 搴撳瓨銆嶅尯闂村唴
+     * 鍑哄簱鏁伴噺鏄湰娆¤杞︽�婚噺锛屽繀椤昏惤鍦ㄣ�屼笂娆″墿浣欐补閲� ~ 涓婃鍓╀綑娌归噺 + 搴撳瓨銆嶅尯闂村唴銆�
+     * 浠e偍/瀹㈠瓨涓嶉�夎溅杈嗭紝涓婃鍓╀綑娌归噺鎸� 0 绠楋紝閫�鍖栨垚銆屼笉瓒呰繃褰撳墠搴撳瓨銆�
      */
-    private void checkSaleOutAvailable(StockOutRecordDto stockOutRecordDto) {
+    private void checkOilOutAvailable(StockOutRecordDto stockOutRecordDto) {
         String suffix = ",鍑哄簱鎵规:" + stockOutRecordDto.getOutboundBatches();
+        if (stockOutRecordDto.getProductModelId() == null) {
+            throw new BaseException("娌瑰搧鍑哄簱蹇呴』閫夋嫨浜у搧瑙勬牸" + suffix);
+        }
         BigDecimal quantity = stockOutRecordDto.getStockOutNum();
         if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
-            throw new BaseException("閿�鍞被鍑哄簱鏁伴噺蹇呴』澶т簬0" + suffix);
+            throw new BaseException("娌瑰搧鍑哄簱鏁伴噺蹇呴』澶т簬0" + suffix);
         }
         BigDecimal lastResidual = getLastResidualOil(stockOutRecordDto.getVehicleId(), stockOutRecordDto.getProductModelId(), null);
         if (lastResidual.compareTo(quantity) > 0) {
@@ -562,9 +747,10 @@
 
     @Override
     public OilOutAvailableDto getOilOutAvailableQty(StockOutRecordDto query) {
-        if (query == null || query.getVehicleId() == null || query.getProductModelId() == null) {
-            throw new BaseException("璇峰厛閫夋嫨杞﹁締鍜屼骇鍝佽鏍�");
+        if (query == null || query.getProductModelId() == null) {
+            throw new BaseException("璇峰厛閫夋嫨浜у搧瑙勬牸");
         }
+        // 浠e偍/瀹㈠瓨涓嶉�夎溅杈嗭紝涓婃鍓╀綑鍙栦笉鍒帮紝鎸� 0 绠楋紝鍙敤閲忓氨绛変簬褰撳墠搴撳瓨
         BigDecimal lastResidualOil = getLastResidualOil(query.getVehicleId(), query.getProductModelId(), query.getId());
         BigDecimal stockQuantity = getStockQuantity(query.getProductModelId(), query.getBatchNo());
         OilOutAvailableDto result = new OilOutAvailableDto();

--
Gitblit v1.9.3