From 97f048ce14db07ee41ca05f9c7010dfe132150e4 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 03 十一月 2026 11:06:59 +0800
Subject: [PATCH] feat(mes): 补生产准备检查与仓储收发存汇总接口,附 CFWJ 演示数据种子脚本

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java |   84 ++++++++++++++++++++++++++++++++++++++----
 1 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
index 17d0dc8..f32c44e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
@@ -5,6 +5,8 @@
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.transaction.vo.MesWmStockSummaryReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.transaction.vo.MesWmStockSummaryRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.transaction.vo.MesWmTransactionPageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
@@ -18,10 +20,14 @@
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
 import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
+import cn.iocoder.yudao.module.mes.service.wm.stockwarning.MesWmStockWarningService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseDefaultService;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -30,6 +36,8 @@
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.List;
+
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -56,6 +64,10 @@
     private MesWmWarehouseLocationService locationService;
     @Resource
     private MesWmWarehouseAreaService areaService;
+    @Resource
+    private MesWmWarehouseDefaultService warehouseDefaultService;
+    @Resource
+    private MesWmStockWarningService stockWarningService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -70,6 +82,10 @@
         checkQuantity(reqDTO, inbound);
         // 1.3 鍏ュ簱浜嬪姟锛氭壒娆″彿鏍¢獙 + batchId/batchCode 浜掕ˉ
         checkBatch(reqDTO, inbound);
+        // 1.3.1 鍏ュ簱浜嬪姟鏈寚瀹氬簱鍖�/搴撲綅鏃讹紝鑷姩钀藉埌浠撳簱榛樿搴撳尯/搴撲綅锛堝鎴锋湭鍚敤搴撳尯/搴撲綅绠$悊锛�
+        if (inbound) {
+            fillDefaultLocationAndArea(reqDTO);
+        }
         // 1.4 鍏ュ簱浜嬪姟锛氬簱浣嶆贩鏀捐鍒欐牎楠�
         if (inbound) {
             materialStockService.checkAreaMixingRule(reqDTO.getAreaId(), reqDTO.getItemId(), reqDTO.getBatchId());
@@ -81,31 +97,49 @@
         MesWmMaterialStockDO materialStock;
         MesWmTransactionTypeEnum transactionType = MesWmTransactionTypeEnum.valueOf(reqDTO.getType());
         boolean isInbound = transactionType != null && transactionType.isInbound();
+        boolean forceCreateNewStock = Boolean.TRUE.equals(reqDTO.getForceCreateNewStock());
+
         if (reqDTO.getMaterialStockId() != null) {
             // 浼犲叆浜� materialStockId锛岀洿鎺ヤ娇鐢�
             materialStock = materialStockService.getMaterialStock(reqDTO.getMaterialStockId());
             if (materialStock == null) {
                 throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
             }
-            // 鏍¢獙鐗╂枡銆佹壒娆°�佷粨搴撶瓑缁村害涓�鑷达紙闃叉浼犲叆閿欒鐨� materialStockId锛�
+            // 鏍¢獙鐗╂枡銆佹壒娆°�佷粨搴撶瓑缁村害涓�鑷达紙闃叉浼犲叆閿欒鐨� materialStockId锛夈��
+            // 鑻ユ澶勪笉涓�鑷达紝閫氬父鏄搴撳瓨宸茶绉诲簱/鍚堝苟/鎷嗗垎锛岄渶閫�鍥為噸鏂版嫞璐э紝鏁呯粰鍑洪拡瀵规�ф彁绀�
             if (ObjUtil.notEqual(materialStock.getItemId(), reqDTO.getItemId())
                     || ObjUtil.notEqual(materialStock.getWarehouseId(), reqDTO.getWarehouseId())
                     || ObjUtil.notEqual(materialStock.getLocationId(), reqDTO.getLocationId())
                     || ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId())) {
-                throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
+                throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
+                        "璇ュ簱瀛樿褰曞凡鍙樺姩锛堝彲鑳借绉诲簱/鍚堝苟/鎷嗗垎锛夛紝涓庡嚭搴撴槑缁嗕笉涓�鑷达紝鏃犳硶鎵e噺锛岃閫�鍥為噸鏂版嫞璐�");
             }
         } else if (isInbound) {
-            // 鍏ュ簱浜嬪姟锛氭壘涓嶅埌鍒欒嚜鍔ㄥ垱寤哄簱瀛樿褰�
-            materialStock = materialStockService.getOrCreateMaterialStock(
-                    reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
-                    reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            // 鍏ュ簱浜嬪姟锛氭牴鎹� forceCreateNewStock 鏍囧織鍐冲畾鏄垱寤烘柊璁板綍杩樻槸鏌ユ壘/鍒涘缓
+            if (forceCreateNewStock) {
+                materialStock = materialStockService.createMaterialStock(
+                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
+                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            } else {
+                materialStock = materialStockService.getOrCreateMaterialStock(
+                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
+                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            }
         } else {
-            // 鍑哄簱浜嬪姟锛氬簱瀛樿褰曞繀椤诲凡瀛樺湪锛屼笉鍏佽鑷姩鍒涘缓
+            // 鍑哄簱浜嬪姟锛氬簱瀛樿褰曞繀椤诲凡瀛樺湪锛屼笉鍏佽鑷姩鍒涘缓锛堥櫎闈� checkFlag=false 鍏佽璐熷簱瀛橈級
             materialStock = materialStockService.getMaterialStockByCompositeKey(
                     reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
                     reqDTO.getBatchId(), reqDTO.getVendorId());
             if (materialStock == null) {
-                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
+                // checkFlag=false 鏃跺厑璁歌礋搴撳瓨锛岃嚜鍔ㄥ垱寤哄簱瀛樿褰曪紙鏁伴噺灏嗕粠 0 鎵e噺涓鸿礋鏁帮級
+                boolean allowNegative = !ObjUtil.defaultIfNull(reqDTO.getCheckFlag(), true);
+                if (allowNegative) {
+                    materialStock = materialStockService.getOrCreateMaterialStock(
+                            reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
+                            reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+                } else {
+                    throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
+                }
             }
         }
         if (materialStock == null || materialStock.getId() == null) {
@@ -116,6 +150,8 @@
         // 2.3 鏇存柊搴撳瓨鏁伴噺
         boolean checkFlag = ObjUtil.defaultIfNull(reqDTO.getCheckFlag(), true);
         materialStockService.updateMaterialStockQuantity(materialStock.getId(), reqDTO.getQuantity(), checkFlag);
+        // 2.4 搴撳瓨棰勮鏍¢獙锛氭湰娆″彉鍔ㄥ鑷村簱瀛樿法瓒婁笂涓嬮檺鏃讹紝鑷姩鍙戦�佺珯鍐呴�氱煡锛堥渶姹� 5.1鈶★級
+        stockWarningService.checkAfterStockChange(reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getQuantity());
 
         // 3. 鎻掑叆浜嬪姟娴佹按
         MesWmTransactionDO transaction = MesWmTransactionDO.builder()
@@ -143,9 +179,41 @@
         return transactionMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public PageResult<MesWmStockSummaryRespVO> getStockSummaryPage(MesWmStockSummaryReqVO reqVO) {
+        // 鏈寚瀹氭椂闂存椂榛樿缁熻杩戜笁涓湀锛氭湡鍒濅负涓や釜鏈堝墠 1 鏃� 0 鐐癸紝鏈熸湯涓哄綋鍓嶆椂闂�
+        LocalDateTime endTime = reqVO.getEndTime() != null ? reqVO.getEndTime() : LocalDateTime.now();
+        LocalDateTime startTime = reqVO.getStartTime() != null ? reqVO.getStartTime()
+                : endTime.toLocalDate().withDayOfMonth(1).minusMonths(2).atStartOfDay();
+        if (startTime.isAfter(endTime)) {
+            throw new ServiceException(400, StrUtil.format(
+                    "鏌ヨ寮�濮嬫椂闂碵{}]鏅氫簬缁撴潫鏃堕棿[{}]锛岃璋冩暣鏃堕棿鑼冨洿鍚庨噸璇�",
+                    startTime, endTime));
+        }
+        IPage<MesWmStockSummaryRespVO> page = transactionMapper.selectStockSummaryPage(
+                new Page<>(reqVO.getPageNo(), reqVO.getPageSize()),
+                reqVO.getItemId(), reqVO.getWarehouseId(), startTime, endTime);
+        return new PageResult<>(page.getRecords(), page.getTotal());
+    }
+
     // ==================== 绉佹湁鏍¢獙鏂规硶 ====================
 
     /**
+     * 鍏ュ簱浜嬪姟鏈寚瀹氬簱鍖�/搴撲綅鏃讹紝鑷姩浣跨敤浠撳簱鐨勯粯璁ゅ簱鍖�/榛樿搴撲綅
+     *
+     * 瀹㈡埛鏈惎鐢ㄥ簱鍖�/搴撲綅绠$悊锛屽墠绔笉鍐嶈鐢ㄦ埛閫夋嫨锛屽簱瀛樹粛闇�瑕佸畬鏁寸殑浠撳簱-搴撳尯-搴撲綅缁村害銆�
+     */
+    private void fillDefaultLocationAndArea(MesWmTransactionSaveReqDTO reqDTO) {
+        if (reqDTO.getWarehouseId() == null
+                || (reqDTO.getLocationId() != null && reqDTO.getAreaId() != null)) {
+            return;
+        }
+        MesWmWarehouseAreaDO defaultArea = warehouseDefaultService.getOrCreateDefaultArea(reqDTO.getWarehouseId());
+        reqDTO.setLocationId(defaultArea.getLocationId());
+        reqDTO.setAreaId(defaultArea.getId());
+    }
+
+    /**
      * 鏍¢獙鏁伴噺姝h礋鍙蜂笌浜嬪姟鏂瑰悜涓�鑷�
      */
     private void checkQuantity(MesWmTransactionSaveReqDTO reqDTO, boolean inbound) {

--
Gitblit v1.9.3