From 55d694aaae4e421b5e55cd941500e08f85cb5d4d Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 27 八月 2026 18:21:33 +0800
Subject: [PATCH] feat(sales): 添加框架合同订单功能并优化销售台账管理

---
 src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java |  130 ++++++++++++++++--------------------------
 1 files changed, 50 insertions(+), 80 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
index b00266b..b40885e 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -24,6 +24,10 @@
 import com.ruoyi.production.pojo.ProductionPlan;
 import com.ruoyi.production.service.ProductionOrderService;
 import com.ruoyi.production.service.ProductionPlanService;
+import com.ruoyi.sales.mapper.SalesLedgerMapper;
+import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.pojo.SalesLedger;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
@@ -50,6 +54,8 @@
     private final ProductionOrderService productionOrderService;
     private final ProductModelMapper productModelMapper;
     private final ProductMapper productMapper;
+    private final SalesLedgerMapper salesLedgerMapper;
+    private final SalesLedgerProductMapper salesLedgerProductMapper;
 
     @Override
     public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) {
@@ -61,9 +67,8 @@
      * 鍚堝苟鐢熶骇璁″垝骞朵笅鍙戠敓浜ц鍗曘��
      * 绾︽潫锛�
      * 1. 浠呭厑璁稿悓涓�浜у搧鍨嬪彿鐨勮鍒掑悎骞讹紱
-     * 2. 宸蹭笅鍙戞垨閮ㄥ垎涓嬪彂鐨勮鍒掍笉鍏佽鍐嶆鍚堝苟锛�
-     * 3. 涓嬪彂鏁伴噺涓嶈兘澶т簬鎵�閫夎鍒掑墿浣欓渶姹傛�婚噺锛�
-     * 4. 涓嬪彂鏃剁粺涓�璋冪敤 ProductionOrderService.saveProductionOrder锛岀‘淇濆悗缁伐鑹�/BOM/棰嗘枡閫昏緫涓�鑷淬��
+     * 2. 涓嬪彂鏁伴噺鍙彈杈撳叆鏁伴噺鍜屽熀纭�鏁版嵁鏍¢獙绾︽潫锛�
+     * 3. 涓嬪彂鏃剁粺涓�璋冪敤 ProductionOrderService.saveProductionOrder锛岀‘淇濆悗缁伐鑹�/BOM/棰嗘枡閫昏緫涓�鑷淬��
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -101,49 +106,18 @@
             throw new BaseException("鍚堝苟澶辫触锛屾墍閫夌敓浜ц鍒掔殑浜у搧鍨嬪彿涓嶄竴鑷�");
         }
 
-        // 浠呪�滃凡涓嬪彂鈥濊鍒掍笉鍏佽鍐嶆鍙備笌鍚堝苟涓嬪彂锛�
-        // 鈥滃緟涓嬪彂/閮ㄥ垎涓嬪彂鈥濆厑璁哥户缁笅鍙戝墿浣欐暟閲忋��
-        boolean hasFullyIssuedPlan = planLists.stream()
-                .anyMatch(item -> item.getStatus() != null
-                        && item.getStatus() == PLAN_STATUS_ISSUED);
-        if (hasFullyIssuedPlan) {
-            throw new BaseException("鍚堝苟澶辫触锛屾墍閫夌敓浜ц鍒掑瓨鍦ㄥ凡涓嬪彂鐨勬暟鎹�");
-        }
-
-        // 璁$畻鏈鍙笅鍙戠殑鍓╀綑闇�姹傛�婚噺
-        BigDecimal totalRequiredQuantity = planLists.stream()
-                .map(this::resolveRemainingQuantity)
-                .reduce(BigDecimal.ZERO, BigDecimal::add);
-        if (totalRequiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
-            throw new ServiceException("涓嬪彂澶辫触锛屾墍閫夌敓浜ц鍒掑墿浣欓渶姹傛�婚噺蹇呴』澶т簬0");
-        }
-
         // 鏍¢獙涓嬪彂鏁伴噺
         BigDecimal assignedQuantity = productionPlanDto.getTotalAssignedQuantity();
         if (assignedQuantity == null || assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
             throw new ServiceException("涓嬪彂澶辫触锛屼笅鍙戞暟閲忓繀椤诲ぇ浜�0");
         }
-        if (assignedQuantity.compareTo(totalRequiredQuantity) > 0) {
-            throw new ServiceException("涓嬪彂澶辫触锛屼笅鍙戞暟閲忎笉鑳藉ぇ浜庤鍒掑墿浣欓渶姹傛�婚噺");
-        }
 
-        // 鎸夎鍒掗『搴忓垎鎽婁笅鍙戞暟閲忥紝鏀堕泦瀹為檯鍙備笌涓嬪彂鐨勮鍒� ID
-        BigDecimal remainingForOrderBind = assignedQuantity;
-        List<Long> issuedPlanIds = new ArrayList<>();
-        for (ProductionPlanDto plan : planLists) {
-            BigDecimal remainingQuantity = resolveRemainingQuantity(plan);
-            if (remainingForOrderBind.compareTo(BigDecimal.ZERO) <= 0 || remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
-                continue;
-            }
-            BigDecimal issueForThisPlan = remainingForOrderBind.min(remainingQuantity);
-            remainingForOrderBind = remainingForOrderBind.subtract(issueForThisPlan);
-            if (issueForThisPlan.compareTo(BigDecimal.ZERO) > 0) {
-                issuedPlanIds.add(plan.getId());
-            }
-        }
-        if (issuedPlanIds.isEmpty()) {
-            throw new ServiceException("涓嬪彂澶辫触锛屾棤鍙笅鍙戞暟閲�");
-        }
+        // 鏈涓嬪彂鏁伴噺涓嶅啀鎸夐渶姹傞噺闄愬埗锛屾墍鏈夐�変腑璁″垝鍏卞悓浣滀负璁㈠崟鏉ユ簮
+        List<Long> issuedPlanIds = planLists.stream()
+                .map(ProductionPlanDto::getId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
 
         // 鐢熸垚鐢熶骇璁㈠崟涓诲崟锛屽苟缁戝畾鏈涓嬪彂鍏宠仈鐨勮鍒�
         ProductionOrder productionOrder = new ProductionOrder();
@@ -155,24 +129,18 @@
             throw new ServiceException("涓嬪彂澶辫触锛岀敓浜ц鍗曚繚瀛樺け璐�");
         }
 
-        // 鍥炲啓姣忔潯璁″垝鐨勭疮璁′笅鍙戦噺鍜岀姸鎬�
+        // 鍥炲啓姣忔潯璁″垝鐨勭疮璁′笅鍙戦噺鍜岀姸鎬侊紝鍏佽绱涓嬪彂閲忚秴杩囬渶姹傞噺
         BigDecimal remainingAssignedQuantity = assignedQuantity;
         List<ProductionPlan> updates = new ArrayList<>();
         for (ProductionPlanDto plan : planLists) {
-            BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO);
-            if (requiredQuantity.compareTo(BigDecimal.ZERO) < 0) {
-                requiredQuantity = BigDecimal.ZERO;
-            }
-            BigDecimal remainingQuantity = resolveRemainingQuantity(plan);
-            BigDecimal historicalIssuedQuantity = requiredQuantity.subtract(remainingQuantity);
-            BigDecimal issuedQuantity = BigDecimal.ZERO;
-            if (remainingAssignedQuantity.compareTo(BigDecimal.ZERO) > 0 && remainingQuantity.compareTo(BigDecimal.ZERO) > 0) {
-                issuedQuantity = remainingAssignedQuantity.min(remainingQuantity);
-                remainingAssignedQuantity = remainingAssignedQuantity.subtract(issuedQuantity);
-            }
+            BigDecimal historicalIssuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO);
+            BigDecimal issuedQuantity = remainingAssignedQuantity.min(assignedQuantity);
+            remainingAssignedQuantity = remainingAssignedQuantity.subtract(issuedQuantity);
 
             BigDecimal totalIssuedQuantity = historicalIssuedQuantity.add(issuedQuantity);
-            int planStatus = resolvePlanStatus(requiredQuantity, totalIssuedQuantity);
+            int planStatus = plan.getStatus() != null && plan.getStatus() == PLAN_STATUS_ISSUED
+                    ? PLAN_STATUS_ISSUED
+                    : resolvePlanStatus(plan.getQtyRequired(), totalIssuedQuantity);
             ProductionPlan update = new ProductionPlan();
             update.setId(plan.getId());
             update.setStatus(planStatus);
@@ -189,6 +157,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean add(ProductionPlanDto dto) {
+        validateSalesSource(dto);
         // 鏂板涓荤敓浜ц鍒�
         if (StringUtils.isBlank(dto.getMpsNo())) {
             String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
@@ -211,6 +180,7 @@
             throw new ServiceException("缂栬緫澶辫触锛屾暟鎹笉鑳戒负绌�");
         }
 
+        validateSalesSource(dto);
         ProductionPlan old = getById(dto.getId());
         if (old == null) {
             throw new ServiceException("缂栬緫澶辫触锛岀敓浜ц鍒掍笉瀛樺湪");
@@ -311,7 +281,7 @@
             BeanUtils.copyProperties(dto, entity);
             entity.setProductModelId(resolveProductModelId(dto, i + 2, allModels, productNameById));
             entity.setStatus(PLAN_STATUS_WAIT);
-            entity.setSource(StringUtils.isNotEmpty(dto.getSource()) ? StringUtils.trim(dto.getSource()) : "鍐呴儴");
+            entity.setSource("鍐呴儴");
             entity.setQuantityIssued(BigDecimal.ZERO);
             entity.setCreateTime(now);
             entity.setUpdateTime(now);
@@ -357,6 +327,32 @@
 
         ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class);
         util.exportExcel(response, exportList, "涓荤敓浜ц鍒�");
+    }
+
+    private void validateSalesSource(ProductionPlanDto dto) {
+        if (dto == null) {
+            throw new ServiceException("鐢熶骇璁″垝涓嶈兘涓虹┖");
+        }
+        if (dto.getSalesLedgerId() == null && dto.getSalesLedgerProductId() == null) {
+            return;
+        }
+        if (dto.getSalesLedgerId() == null || dto.getSalesLedgerProductId() == null) {
+            throw new ServiceException("閿�鍞彴璐﹀拰閿�鍞骇鍝佸繀椤诲悓鏃跺~鍐�");
+        }
+        SalesLedgerProduct product = salesLedgerProductMapper.selectById(dto.getSalesLedgerProductId());
+        if (product == null || !Objects.equals(product.getSalesLedgerId(), dto.getSalesLedgerId())) {
+            throw new ServiceException("閿�鍞骇鍝佷笌閿�鍞彴璐︿笉鍖归厤");
+        }
+        SalesLedger ledger = salesLedgerMapper.selectById(dto.getSalesLedgerId());
+        if (ledger == null || (!Objects.equals(ledger.getContractType(), 1)
+                && !Objects.equals(ledger.getContractType(), 3))) {
+            throw new ServiceException("鐢熶骇璁″垝鍙兘鍏宠仈鏅�氶攢鍞骇鍝佹垨妗嗘灦璁㈠崟浜у搧");
+        }
+        if (dto.getProductModelId() == null) {
+            dto.setProductModelId(product.getProductModelId());
+        } else if (!Objects.equals(dto.getProductModelId(), product.getProductModelId())) {
+            throw new ServiceException("鐢熶骇璁″垝浜у搧鍨嬪彿涓庨攢鍞骇鍝佷笉涓�鑷�");
+        }
     }
 
     /**
@@ -455,32 +451,6 @@
             }
         }
         return sequence;
-    }
-
-    /**
-     * 璁$畻鐢熶骇璁″垝鐨勫墿浣欐湭涓嬪彂鏁伴噺锛堥渶姹傞噺 - 宸蹭笅鍙戦噺锛屾渶灏忎负 0锛夈��
-     */
-    private BigDecimal resolveRemainingQuantity(ProductionPlan plan) {
-        // 绌哄璞℃寜 0 澶勭悊
-        if (plan == null) {
-            return BigDecimal.ZERO;
-        }
-        // 闇�姹傞噺涓虹┖鎴栧皬浜庣瓑浜� 0锛岃涓烘棤鍓╀綑
-        BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO);
-        if (requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
-            return BigDecimal.ZERO;
-        }
-        // 宸蹭笅鍙戦噺涓虹┖鎴栧皬浜庣瓑浜� 0锛屽墿浣欏嵆闇�姹傞噺
-        BigDecimal issuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO);
-        if (issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
-            return requiredQuantity;
-        }
-        // 宸蹭笅鍙戦噺澶т簬绛変簬闇�姹傞噺锛屽墿浣欏綊闆�
-        if (issuedQuantity.compareTo(requiredQuantity) >= 0) {
-            return BigDecimal.ZERO;
-        }
-        // 姝e父鍦烘櫙杩斿洖宸��
-        return requiredQuantity.subtract(issuedQuantity);
     }
 
     /**

--
Gitblit v1.9.3