From 69e0f44e279f7763fa9c9a4d105f154db39ee1d8 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 23 五月 2026 11:38:02 +0800
Subject: [PATCH] feat(sales): 添加每件数量字段支持及多业务流程优化

---
 src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java   |   47 +++++++
 doc/20260522_sales_每件数量前端联调文档.md                                              |  106 +++++++++++++++++
 src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java                    |    3 
 src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java                        |    5 
 src/main/resources/mapper/sales/SalesQuotationMapper.xml                      |   27 ++--
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java |    8 +
 src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java           |   31 -----
 src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java       |   33 +++++
 src/main/resources/mapper/sales/SalesLedgerProductMapper.xml                  |    5 
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java        |   13 ++
 src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java     |   14 +
 src/main/java/com/ruoyi/sales/dto/SalesLedgerProductImportDto.java            |    6 +
 src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java          |    9 
 src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java                      |    6 -
 src/main/resources/static/销售台账导入模板.xlsx                                       |    0 
 src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java                 |    4 
 16 files changed, 247 insertions(+), 70 deletions(-)

diff --git "a/doc/20260522_sales_\346\257\217\344\273\266\346\225\260\351\207\217\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260522_sales_\346\257\217\344\273\266\346\225\260\351\207\217\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..16055f8
--- /dev/null
+++ "b/doc/20260522_sales_\346\257\217\344\273\266\346\225\260\351\207\217\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,106 @@
+# Sales 妯″潡銆屾瘡浠舵暟閲忋�嶅墠绔仈璋冩枃妗�
+
+## 1. 鍙樻洿鑼冨洿
+
+- 閿�鍞彴璐︼細閿�鍞骇鍝佹柊澧炲瓧娈� `singleQuantity`锛堟瘡浠舵暟閲忥級
+- 閿�鍞姤浠凤細鎶ヤ环浜у搧鏂板瀛楁 `singleQuantity`锛堟瘡浠舵暟閲忥級
+- 鍙戣揣/鍑哄簱鏁伴噺璁$畻锛氭寜 `鏁伴噺 * 姣忎欢鏁伴噺` 璁$畻骞惰惤搴�
+
+## 1.1 鑱旇皟鍓嶇疆锛堟暟鎹簱锛�
+
+闇�淇濊瘉浠ヤ笅涓ゅ紶琛ㄥ凡鏂板鍒� `single_quantity`锛堝缓璁粯璁ゅ�� `1`锛夛細
+
+- `sales_ledger_product.single_quantity`
+- `sales_quotation_product.single_quantity`
+
+## 2. 瀛楁瀹氫箟
+
+- 瀛楁鍚嶏細`singleQuantity`
+- 鍚箟锛氭瘡浠舵暟閲�
+- 绫诲瀷锛歚number`锛堝悗绔� `BigDecimal`锛�
+- 榛樿瑙勫垯锛氭湭浼犮�佷负 `null`銆佹垨 `<= 0` 鏃讹紝鍚庣鎸� `1` 澶勭悊
+
+## 3. 鎺ュ彛鍙樻洿娓呭崟
+
+### 3.1 閿�鍞彴璐�
+
+1. `POST /sales/ledger/addOrUpdateSalesLedger`
+   - 鍏ュ弬 `productData[]` 鏂板瀛楁 `singleQuantity`
+2. `POST /sales/product/addOrUpdateSalesLedgerProduct`
+   - 鍏ュ弬鏂板瀛楁 `singleQuantity`
+3. `GET /sales/product/list`
+   - 杩斿洖瀛楁鏂板 `singleQuantity`
+   - `noQuantity`锛堝緟鍙戣揣鏁伴噺锛夎绠楁敼涓猴細
+     - `noQuantity = quantity * singleQuantity - shippedQuantity`
+   - 鍙戣揣鐘舵�佽绠楀悓姝ュ熀浜庝笂闈㈠叕寮�
+
+### 3.2 閿�鍞姤浠�
+
+1. `POST /sales/quotation/add`
+   - 鍏ュ弬 `products[]` 鏂板瀛楁 `singleQuantity`
+2. `POST /sales/quotation/update`
+   - 鍏ュ弬 `products[]` 鏂板瀛楁 `singleQuantity`
+3. `GET /sales/quotation/list`
+   - 杩斿洖 `products[]` 鏂板瀛楁 `singleQuantity`
+
+### 3.3 鍙戣揣涓庡嚭搴�
+
+1. `POST /shippingInfo/add`
+   - 鍚庣浼氭寜閿�鍞骇鍝� `singleQuantity` 鑷姩鎹㈢畻鍙戣揣鏄庣粏鏁伴噺锛�
+   - `shipping_product_detail.quantity = 鍓嶇浼犲叆quantity * singleQuantity`
+   - 鍑哄簱璁板綍鏁伴噺涓庡彂璐ф槑缁嗕竴鑷达紙鍚屾牱涓轰箻绉悗鐨勬暟閲忥級
+
+## 4. 鍓嶇鑱旇皟瑕佹眰
+
+1. 閿�鍞彴璐︿骇鍝佽銆侀攢鍞姤浠蜂骇鍝佽鏂板銆屾瘡浠舵暟閲忋�嶈緭鍏ラ」锛屽瓧娈靛悕 `singleQuantity`
+2. `singleQuantity` 寤鸿闄愬埗涓哄ぇ浜� `0` 鐨勬暟瀛�
+3. 鍙戣揣鎻愪氦娴佺▼涓紝`batchNoDetailList[].quantity` 浼犫�滀欢鏁扳�濓紝涓嶈鍦ㄥ墠绔啀涔� `singleQuantity`
+   - 鍘熷洜锛氬悗绔凡缁熶竴鎹㈢畻锛屽墠绔啀涔樹細瀵艰嚧閲嶅鏀惧ぇ
+4. 鍒楄〃灞曠ず寰呭彂璐ф暟閲忔椂锛岀洿鎺ヤ娇鐢ㄦ帴鍙h繑鍥炵殑 `noQuantity`
+
+## 5. 鑱旇皟绀轰緥
+
+### 5.1 閿�鍞彴璐︽柊澧�/缂栬緫锛堜骇鍝佺墖娈碉級
+
+```json
+{
+  "productData": [
+    {
+      "productModelId": 101,
+      "quantity": 10,
+      "singleQuantity": 12
+    }
+  ]
+}
+```
+
+### 5.2 閿�鍞姤浠锋柊澧�/缂栬緫锛堜骇鍝佺墖娈碉級
+
+```json
+{
+  "products": [
+    {
+      "productModelId": 101,
+      "quantity": 10,
+      "singleQuantity": 12
+    }
+  ]
+}
+```
+
+### 5.3 鍙戣揣鎻愪氦锛堟槑缁嗙墖娈碉級
+
+```json
+{
+  "salesLedgerProductId": 2001,
+  "batchNoDetailList": [
+    {
+      "productModelId": 101,
+      "batchNo": "B20260522001",
+      "quantity": 3
+    }
+  ]
+}
+```
+
+璇存槑锛氳嫢璇� `salesLedgerProductId` 瀵瑰簲 `singleQuantity=12`锛屽垯瀹為檯鍙戣揣/鍑哄簱鏁伴噺涓� `36`銆�
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
index c9d7aae..66853d9 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -26,6 +26,7 @@
 import com.ruoyi.basic.utils.FileUtil;
 import com.ruoyi.common.enums.FileNameType;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
 import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.procurementrecord.utils.StockUtils;
@@ -39,9 +40,11 @@
 import com.ruoyi.purchase.pojo.PurchaseLedger;
 import com.ruoyi.sales.mapper.CommonFileMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.mapper.SalesQuotationMapper;
 import com.ruoyi.sales.mapper.ShippingInfoMapper;
 import com.ruoyi.sales.pojo.CommonFile;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.SalesQuotation;
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
 import lombok.RequiredArgsConstructor;
@@ -71,6 +74,7 @@
     private final ISysNoticeService sysNoticeService;
     private final PurchaseLedgerMapper purchaseLedgerMapper;
     private final SalesLedgerProductMapper salesLedgerProductMapper;
+    private final SalesQuotationMapper salesQuotationMapper;
     private final StockUtils stockUtils;
     private final ShippingInfoMapper shippingInfoMapper;
     private final ApproveNodeMapper approveNodeMapper;
@@ -89,7 +93,17 @@
                 .collect(Collectors.toList());
         // 鏃犲鏍镐汉閫昏緫娣诲姞
         if (CollectionUtils.isEmpty(nodeIds)) {
-            autoPassPurchaseApproveIfNoApprover(approveProcessVO); // 閲囪喘鍗曟棤瀹℃牳浜洪�昏緫
+            switch (approveProcessVO.getApproveType()){
+                case 5:
+                    autoPassPurchaseApproveIfNoApprover(approveProcessVO); // 閲囪喘鍗曟棤瀹℃牳浜洪�昏緫
+                    break;
+                case 6:
+                    autoPassSalesApproveIfNoApprover(approveProcessVO); // 閿�鍞姤浠锋棤瀹℃牳浜洪�昏緫
+                    break;
+                case 7:
+                    autoShippingApproveIfNoApprover(approveProcessVO); // 閿�鍞彂璐ф棤瀹℃牳浜洪�昏緫
+                    break;
+            }
             return;
         }
         List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds);
@@ -154,10 +168,35 @@
         }
     }
 
+    private void autoPassSalesApproveIfNoApprover(ApproveProcessVO approveProcessVO) {
+        if (!StringUtils.hasText(approveProcessVO.getApproveReason())) {
+            return;
+        }
+        salesQuotationMapper.update(null, new LambdaUpdateWrapper<SalesQuotation>()
+                .eq(SalesQuotation::getQuotationNo, approveProcessVO.getApproveReason())
+                .set(SalesQuotation::getStatus, "閫氳繃"));
+    }
+
+    private void autoShippingApproveIfNoApprover(ApproveProcessVO approveProcessVO) {
+        if (!StringUtils.hasText(approveProcessVO.getApproveReason())) {
+            return;
+        }
+        ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
+                .eq(ShippingInfo::getShippingNo, approveProcessVO.getApproveReason())
+                .last("limit 1"));
+        if(shippingInfo == null){
+            return;
+        }
+        shippingInfoMapper.update(null, new LambdaUpdateWrapper<ShippingInfo>()
+                .eq(ShippingInfo::getShippingNo, approveProcessVO.getApproveReason())
+                .set(ShippingInfo::getStatus, "瀹℃牳閫氳繃"));
+        //鏇存敼鍑哄簱瀹℃牳鐘舵�侊紙寰呯‘璁ゆ敼鎴愬緟瀹℃牳锛�
+        stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
+    }
+
     private void autoPassPurchaseApproveIfNoApprover(ApproveProcessVO approveProcessVO) {
-        if (!Objects.equals(approveProcessVO.getApproveType(), 5)
-                || !StringUtils.hasText(approveProcessVO.getApproveReason())) {
-            throw new RuntimeException("瀹℃牳鐢ㄦ埛涓嶅瓨鍦�");
+        if (!StringUtils.hasText(approveProcessVO.getApproveReason())) {
+            return;
         }
 
         purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>()
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
index 44b8563..12737f8 100644
--- a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -237,42 +237,13 @@
     @GetMapping("/listPage")
     public IPage<SalesLedgerVo> listPage(Page page, SalesLedgerDto salesLedgerDto) {
         IPage<SalesLedgerVo> iPage = salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto);
-
         //  鏌ヨ缁撴灉涓虹┖,鐩存帴杩斿洖
         if (CollectionUtils.isEmpty(iPage.getRecords())) {
             return iPage;
         }
-
-        //  鑾峰彇褰撳墠椤垫墍鏈夊彴璐﹁褰曠殑 ID 闆嗗悎
-        List<Long> salesLedgerIds = iPage.getRecords().stream().map(SalesLedger::getId).collect(Collectors.toList());
-
-
-
-
-        //  杞崲鍥炴鏁版嵁, key 涓哄彴璐D, value 涓鸿鍙拌处鐨勬�诲洖娆鹃噾棰�
-        Map<Long, BigDecimal> receiptTotals = new HashMap<>();
-
-
         for (SalesLedgerVo salesLedgerVo : iPage.getRecords()) {
-            Long ledgerId = salesLedgerVo.getId();
-            // 鍚堝悓鎬婚噾棰�
-            BigDecimal contractAmount = salesLedgerVo.getContractAmount() == null ? BigDecimal.ZERO : salesLedgerVo.getContractAmount();
-            // 寮�绁ㄦ�婚鍜屽洖娆炬�婚
-            BigDecimal receiptPaymentAmountTotal = receiptTotals.getOrDefault(ledgerId, BigDecimal.ZERO);
-
-            //  濡傛灉宸茬粡鏈夎繃寮�绁ㄦ垨鍥炴鎿嶄綔,鍒欎笉鍏佽缂栬緫
-            boolean hasReceiptOperation = receiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) > 0;
-            salesLedgerVo.setIsEdit(hasReceiptOperation);
-
-            salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, ledgerId));
+            salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedgerVo.getId()));
         }
-
-        if (ObjectUtils.isNotEmpty(salesLedgerDto.getStatus())) {
-            if (salesLedgerDto.getStatus()) {
-                iPage.setTotal(iPage.getRecords().size());
-            }
-        }
-
         return iPage;
     }
 
diff --git a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
index f707b5b..ddcc700 100644
--- a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
+++ b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -57,19 +57,18 @@
     public AjaxResult add(@RequestBody ShippingInfoDto req) throws Exception {
         LoginUser loginUser = SecurityUtils.getLoginUser();
         String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no");
+        // 娣诲姞鍙戣揣娑堟伅
+        req.setShippingNo(sh);
+        req.setStatus("寰呭鏍�");
+        boolean save = shippingInfoService.add(req);
         // 鍙戣揣瀹℃壒
         ApproveProcessVO approveProcessVO = new ApproveProcessVO();
         approveProcessVO.setApproveType(7);
         approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
         approveProcessVO.setApproveReason(sh);//鍙戣揣缂栧彿
-        approveProcessVO.setApproveUserIds(req.getApproveUserIds());
         approveProcessVO.setApproveUser(loginUser.getUserId());
         approveProcessVO.setApproveTime(LocalDate.now().toString());
         approveProcessService.addApprove(approveProcessVO);
-        // 娣诲姞鍙戣揣娑堟伅
-        req.setShippingNo(sh);
-        req.setStatus("寰呭鏍�");
-        boolean save = shippingInfoService.add(req);
         return save ? AjaxResult.success() : AjaxResult.error();
     }
 
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesLedgerProductImportDto.java b/src/main/java/com/ruoyi/sales/dto/SalesLedgerProductImportDto.java
index 9f05b60..3bf962f 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesLedgerProductImportDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesLedgerProductImportDto.java
@@ -44,6 +44,12 @@
     private BigDecimal quantity;
 
     /**
+     * 姣忎欢鏁伴噺
+     */
+    @Excel(name = "姣忎欢鏁伴噺")
+    private BigDecimal singleQuantity;
+
+    /**
      * 绋庣巼
      */
     @Excel(name = "绋庣巼")
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
index 4841ffd..0ff2ab6 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -11,10 +11,4 @@
 public class SalesQuotationDto extends SalesQuotation {
     @Schema(description = "鎶ヤ环鍟嗗搧")
     private List<SalesQuotationProduct> products;
-
-    /**
-     * 瀹℃壒浜篿d鍒楄〃
-     */
-    // 瀹℃壒浜�
-    private String approveUserIds;
 }
diff --git a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
index 43409bc..7f0ac08 100644
--- a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -18,11 +18,6 @@
 @Data
 public class ShippingInfoDto extends ShippingInfo {
 
-    /**
-     * 瀹℃壒浜篿d鍒楄〃
-     */
-    // 瀹℃壒浜�
-    private String approveUserIds;
 
     private String type; // 鍙戣揣绫诲瀷
 
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
index f4a42d6..63f5c50 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -66,6 +66,9 @@
      */
     @Excel(name = "鏁伴噺")
     private BigDecimal quantity;
+    @TableField(value = "single_quantity")
+    @Excel(name = "姣忎欢鏁伴噺")
+    private BigDecimal singleQuantity;
     @Excel(name = "鏈�浣庡簱瀛樻暟閲�")
     private BigDecimal minStock;
     /**
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java b/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
index 2e55e79..5ca58f3 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
@@ -4,6 +4,7 @@
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 @Data
@@ -30,6 +31,9 @@
     private Double unitPrice;
     @Schema(description = "鏁伴噺")
     private Integer quantity;
+    @TableField(value = "single_quantity")
+    @Schema(description = "姣忎欢鏁伴噺")
+    private BigDecimal singleQuantity;
     @Schema(description = "閲戦")
     private Double amount;
     @Schema(description = "鍒涘缓鏃堕棿")
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 19aa02f..6a86537 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -167,6 +167,7 @@
 
         int result;
         Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
+        salesLedgerProduct.setSingleQuantity(normalizeSingleQuantity(salesLedgerProduct.getSingleQuantity()));
         if (salesLedgerProduct.getId() == null) {
             salesLedgerProduct.setRegisterDate(LocalDateTime.now());
             result = salesLedgerProductMapper.insert(salesLedgerProduct);
@@ -336,6 +337,13 @@
         return R.ok();
     }
 
+    private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) {
+        if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ONE;
+        }
+        return singleQuantity;
+    }
+
     private String generateNextPlanNo(String datePrefix) {
         QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>();
         queryWrapper.likeRight("mps_no", "JH" + datePrefix);
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index fe385f1..4775de5 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -388,6 +388,7 @@
                 for (SalesLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) {
                     SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct();
                     BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
+                    salesLedgerProduct.setSingleQuantity(normalizeSingleQuantity(salesLedgerProduct.getSingleQuantity()));
                     salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                     salesLedgerProduct.setType(1);
                     // 璁$畻涓嶅惈绋庢�讳环
@@ -590,7 +591,10 @@
     public void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type) {
         // 鎸塈D鍒嗙粍锛屽尯鍒嗘柊澧炲拰鏇存柊鐨勮褰�
         Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream()
-                .peek(p -> p.setSalesLedgerId(salesLedgerId))
+                .peek(p -> {
+                    p.setSalesLedgerId(salesLedgerId);
+                    p.setSingleQuantity(normalizeSingleQuantity(p.getSingleQuantity()));
+                })
                 .collect(Collectors.partitioningBy(p -> p.getId() != null));
 
         List<SalesLedgerProduct> updateList = partitionedProducts.get(true);
@@ -620,6 +624,13 @@
         return entity;
     }
 
+    private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) {
+        if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ONE;
+        }
+        return singleQuantity;
+    }
+
     @Transactional(readOnly = true)
     public String generateSalesContractNo() {
         LocalDate currentDate = LocalDate.now();
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
index de86ad0..32359ec 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
@@ -29,6 +29,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.Collections;
 import java.util.List;
@@ -78,6 +79,7 @@
         List<SalesQuotationProduct> products = salesQuotationDto.getProducts().stream().map(product -> {
             SalesQuotationProduct salesQuotationProduct = new SalesQuotationProduct();
             BeanUtils.copyProperties(product, salesQuotationProduct);
+            salesQuotationProduct.setSingleQuantity(normalizeSingleQuantity(salesQuotationProduct.getSingleQuantity()));
             salesQuotationProduct.setSalesQuotationId(salesQuotation.getId());
             return salesQuotationProduct;
         }).collect(Collectors.toList());
@@ -87,7 +89,6 @@
         approveProcessVO.setApproveType(6);
         approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
         approveProcessVO.setApproveReason(quotationNo);
-        approveProcessVO.setApproveUserIds(salesQuotationDto.getApproveUserIds());
         approveProcessVO.setApproveUser(loginUser.getUserId());
         approveProcessVO.setApproveTime(LocalDate.now().toString());
         approveProcessVO.setPrice(salesQuotationDto.getTotalAmount());
@@ -95,7 +96,7 @@
             approveProcessService.addApprove(approveProcessVO);
         }catch (Exception e){
             log.error("SalesQuotationServiceImpl error:{}", e);
-            throw                                new RuntimeException("瀹℃壒澶辫触");
+            throw  new RuntimeException("瀹℃壒澶辫触");
         }
         return true;
     }
@@ -118,13 +119,13 @@
         List<SalesQuotationProduct> products = salesQuotationDto.getProducts().stream().map(product -> {
             SalesQuotationProduct salesQuotationProduct = new SalesQuotationProduct();
             BeanUtils.copyProperties(product, salesQuotationProduct);
+            salesQuotationProduct.setSingleQuantity(normalizeSingleQuantity(salesQuotationProduct.getSingleQuantity()));
             salesQuotationProduct.setSalesQuotationId(salesQuotation.getId());
             return salesQuotationProduct;
         }).collect(Collectors.toList());
 
         salesQuotationProductService.saveBatch(products);
         // 淇敼鎶ヤ环瀹℃壒
-        vo.setApproveUserIds(salesQuotationDto.getApproveUserIds());
         vo.setApproveType(6);
         vo.setApproveReason(salesQuotationDto.getQuotationNo());
         approveProcessService.updateApproveUser(vo);
@@ -147,5 +148,12 @@
         return true;
     }
 
+    private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) {
+        if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ONE;
+        }
+        return singleQuantity;
+    }
+
 
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index 8d16029..69a0820 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -19,6 +19,7 @@
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
 import com.ruoyi.sales.mapper.ShippingInfoMapper;
 import com.ruoyi.sales.mapper.ShippingProductDetailMapper;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.sales.pojo.ShippingProductDetail;
 import com.ruoyi.sales.service.ShippingInfoService;
@@ -27,6 +28,7 @@
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -127,6 +129,10 @@
 
     @Override
     public boolean add(ShippingInfoDto req) {
+        if (CollectionUtils.isEmpty(req.getBatchNoDetailList())) {
+            throw new RuntimeException("鍙戣揣鏄庣粏涓嶈兘涓虹┖");
+        }
+//        normalizeShippingQuantity(req.getBatchNoDetailList(), req.getSalesLedgerProductId());
         this.save(req);
         req.getBatchNoDetailList().forEach(item -> item.setShippingInfoId(req.getId()));
         shippingProductDetailMapper.insert(req.getBatchNoDetailList());
@@ -153,4 +159,31 @@
         shippingApproveDto.setShippingProductDetailDtoList(dateilByShippingNo);
         return shippingApproveDto;
     }
+
+    /**
+     * 姝e父鍖栧彂璐ф暟閲�
+     * @param shippingProductDetails
+     * @param salesLedgerProductId
+     */
+    private void normalizeShippingQuantity(List<ShippingProductDetail> shippingProductDetails, Long salesLedgerProductId) {
+        if (CollectionUtils.isEmpty(shippingProductDetails)) {
+            return;
+        }
+        BigDecimal singleQuantity = BigDecimal.ONE;
+        if (salesLedgerProductId != null) {
+            SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(salesLedgerProductId);
+            if (salesLedgerProduct != null && salesLedgerProduct.getSingleQuantity() != null
+                    && salesLedgerProduct.getSingleQuantity().compareTo(BigDecimal.ZERO) > 0) {
+                singleQuantity = salesLedgerProduct.getSingleQuantity();
+            }
+        }
+        if (singleQuantity.compareTo(BigDecimal.ONE) == 0) {
+            return;
+        }
+        for (ShippingProductDetail shippingProductDetail : shippingProductDetails) {
+            if (shippingProductDetail.getQuantity() != null) {
+                shippingProductDetail.setQuantity(shippingProductDetail.getQuantity().multiply(singleQuantity));
+            }
+        }
+    }
 }
diff --git a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
index 49569de..d824291 100644
--- a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -10,6 +10,7 @@
         T1.sales_ledger_id,
         T1.warn_num,
         T1.quantity,
+        T1.single_quantity,
         T1.min_stock,
         T1.tax_rate,
         T1.tax_inclusive_unit_price,
@@ -33,10 +34,10 @@
         WHEN (IFNULL(t2.qualitity, 0) - IFNULL(t2.locked_quantity, 0)) >0 THEN 1
         ELSE 0
         END as has_sufficient_stock,
-        (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) as no_quantity,
+        (IFNULL(T1.quantity, 0) * IFNULL(NULLIF(T1.single_quantity, 0), 1) - IFNULL(t3.shipped_quantity, 0)) as no_quantity,
         CASE
          WHEN IFNULL(t3.shipped_quantity, 0) = 0 THEN '寰呭彂璐�'
-         WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '閮ㄥ垎鍙戣揣'
+         WHEN (IFNULL(T1.quantity, 0) * IFNULL(NULLIF(T1.single_quantity, 0), 1) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '閮ㄥ垎鍙戣揣'
         ELSE '宸插彂璐�'
         END as shippingStatus,
         CASE
diff --git a/src/main/resources/mapper/sales/SalesQuotationMapper.xml b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
index 3c93850..5434341 100644
--- a/src/main/resources/mapper/sales/SalesQuotationMapper.xml
+++ b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -4,20 +4,19 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.sales.mapper.SalesQuotationMapper">
     <select id="listPage" resultType="com.ruoyi.sales.dto.SalesQuotationDto">
-        SELECT t1.*,
-               t2.approve_user_ids
+        SELECT t1.*
         FROM sales_quotation t1
-        LEFT JOIN approve_process t2 ON t1.quotation_no = t2.approve_reason and t2.approve_type = 6
-        WHERE 1=1
-          and t2.approve_deleted = 0
-        <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' ">
-            AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%')
-        </if>
-        <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' ">
-            AND t1.customer = #{salesQuotationDto.customer}
-        </if>
-        <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
-            AND t1.status = #{salesQuotationDto.status}
-        </if>
+        <where>
+            <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' ">
+                AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%')
+            </if>
+            <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' ">
+                AND t1.customer = #{salesQuotationDto.customer}
+            </if>
+            <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
+                AND t1.status = #{salesQuotationDto.status}
+            </if>
+        </where>
+
     </select>
 </mapper>
diff --git "a/src/main/resources/static/\351\224\200\345\224\256\345\217\260\350\264\246\345\257\274\345\205\245\346\250\241\346\235\277.xlsx" "b/src/main/resources/static/\351\224\200\345\224\256\345\217\260\350\264\246\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
index 0dad163..8edc4ea 100644
--- "a/src/main/resources/static/\351\224\200\345\224\256\345\217\260\350\264\246\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
+++ "b/src/main/resources/static/\351\224\200\345\224\256\345\217\260\350\264\246\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
Binary files differ

--
Gitblit v1.9.3