From a0b4de611e3b1986b2713c6a161cc1613ec31a5b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 10 十一月 2026 11:42:13 +0800
Subject: [PATCH] feat(mes): 采购入库简化为确认入库并自动生成入库单

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java |  285 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 268 insertions(+), 17 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
index 4346163..83d2a04 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
@@ -2,17 +2,30 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.detail.MesWmItemReceiptDetailSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper;
+import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
@@ -25,26 +38,30 @@
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
+import org.flowable.engine.repository.ProcessDefinition;
 import lombok.extern.slf4j.Slf4j;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
 /**
@@ -54,6 +71,8 @@
 @Validated
 @Slf4j
 public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
+
+    private static final String ITEM_RECEIPT_APPROVE_CATEGORY_CODE = "item_receipt_approve";
 
     @Resource
     private MesWmItemReceiptMapper itemReceiptMapper;
@@ -79,12 +98,23 @@
 
     @Resource
     private BpmProcessInstanceApi processInstanceApi;
+    @Resource
+    private BpmProcessDefinitionService bpmProcessDefinitionService;
 
     @Resource
     private ErpPurchaseOrderApi purchaseOrderApi;
 
     @Resource
     private MdmItemApi mdmItemApi;
+
+    @Resource
+    private MesWmWarehouseService warehouseService;
+    @Resource
+    private MesWmWarehouseLocationService warehouseLocationService;
+    @Resource
+    private MesWmWarehouseAreaService warehouseAreaService;
+    @Resource
+    private MesWmBatchService batchService;
 
     @Override
     public Long createItemReceipt(MesWmItemReceiptSaveReqVO createReqVO) {
@@ -220,6 +250,7 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @Deprecated
     public void finishItemReceipt(Long id) {
         // 1. 鏍¢獙瀛樺湪
         MesWmItemReceiptDO receipt = validateItemReceiptExists(id);
@@ -227,20 +258,219 @@
             throw exception(WM_ITEM_RECEIPT_STATUS_ERROR);
         }
 
-        // 2. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堝鍔犲簱瀛� + 璁板綍娴佹按锛�
+        // 2. 瀹屾垚鍏ュ簱锛堝簱瀛樹簨鍔� + 鐘舵�� + 鍒拌揣閫氱煡 + 閲囪喘璁㈠崟鍥炲啓锛�
+        completeItemReceipt(receipt);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void confirmItemReceipt(Long id, Long warehouseId, Long locationId, Long areaId) {
+        // 1. 鏍¢獙鍗曟嵁瀛樺湪
+        MesWmItemReceiptDO receipt = validateItemReceiptExists(id);
+        // 2. 鏍¢獙鐘舵�侊細鑽夌銆佹棫娴佺▼鐨勫緟涓婃灦锛堜笖涓嶅湪 BPM 瀹℃壒涓級銆佸緟鎵ц鍏ュ簱 鍧囧厑璁哥‘璁�
+        validateItemReceiptConfirmable(receipt);
+        // 3. 鏍¢獙琛�
+        List<MesWmItemReceiptLineDO> lines = itemReceiptLineService.getItemReceiptLineListByReceiptId(id);
+        if (CollUtil.isEmpty(lines)) {
+            throw exception(WM_ITEM_RECEIPT_NO_LINE);
+        }
+        // 4. 鏍¢獙鍏ュ簱浣嶇疆锛堜粨搴� / 搴撳尯 / 搴撲綅锛夌殑灞傜骇鍏崇郴
+        validateConfirmLocation(receipt.getCode(), warehouseId, locationId, areaId);
+        // 5. 閫愯鏍¢獙鍏ュ簱鏁伴噺锛屽苟涓哄惎鐢ㄦ壒娆$鐞嗕絾杩樻病鎵规鐨勭墿鏂欒ˉ榻愭壒娆�
+        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
+                convertList(lines, MesWmItemReceiptLineDO::getItemId));
+        for (MesWmItemReceiptLineDO line : lines) {
+            validateConfirmLineQuantity(receipt.getCode(), line, itemMap);
+            fillLineBatch(receipt, line, itemMap);
+        }
+        // 6. 琛ラ綈涓婃灦鏄庣粏锛氭病鏈夋槑缁嗙殑琛屾寜鈥滆鍏ュ簱鏁伴噺 + 鎵�閫変綅缃�濈敓鎴愪竴鏉★紱宸叉湁鏄庣粏鐨勪繚鎸佸師鏍�
+        createDetailsForConfirm(receipt, lines, warehouseId, locationId, areaId);
+
+        // 7. 瀹屾垚鍏ュ簱锛堝簱瀛樹簨鍔� + 鐘舵�� + 鍒拌揣閫氱煡 + 閲囪喘璁㈠崟鍥炲啓锛�
+        completeItemReceipt(receipt);
+    }
+
+    /**
+     * 瀹屾垚鍏ュ簱锛氬垱寤哄簱瀛樹簨鍔°�佹洿鏂板崟鎹笌鍏宠仈鍗曟嵁鐘舵��
+     */
+    private void completeItemReceipt(MesWmItemReceiptDO receipt) {
+        // 1. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堝鍔犲簱瀛� + 璁板綍娴佹按锛�
         createTransactionList(receipt);
 
-        // 3. 鏇存柊鍏ュ簱鍗曠姸鎬�
+        // 2. 鏇存柊鍏ュ簱鍗曠姸鎬�
         itemReceiptMapper.updateById(new MesWmItemReceiptDO()
-                .setId(id).setStatus(MesWmItemReceiptStatusEnum.FINISHED.getStatus()));
+                .setId(receipt.getId()).setStatus(MesWmItemReceiptStatusEnum.FINISHED.getStatus()));
 
-        // 4. 鏇存柊鍏宠仈鐨勫埌璐ч�氱煡鍗曠姸鎬�
+        // 3. 鏇存柊鍏宠仈鐨勫埌璐ч�氱煡鍗曠姸鎬�
         if (receipt.getNoticeId() != null) {
             arrivalNoticeService.finishArrivalNotice(receipt.getNoticeId());
         }
 
-        // 5. 鍥炲啓 ERP 閲囪喘璁㈠崟鐨勫叆搴撴暟閲�
+        // 4. 鍥炲啓 ERP 閲囪喘璁㈠崟鐨勫叆搴撴暟閲�
         updateErpPurchaseOrderInCount(receipt);
+    }
+
+    /**
+     * 鏍¢獙閲囪喘鍏ュ簱鍗曟槸鍚﹀厑璁哥‘璁ゅ叆搴�
+     *
+     * 鑽夌锛堟柊娴佺▼锛夈�佹棫娴佺▼鐨勫緟涓婃灦锛堟棤 BPM 瀹℃壒锛夈�佸緟鎵ц鍏ュ簱锛堟棫娴佺▼宸蹭笂鏋讹級閮藉厑璁革紱
+     * 瀹℃壒涓殑鍗曟嵁蹇呴』绛夊鎵圭粨鏉燂紝宸插畬鎴�/宸插彇娑堢殑涓嶅厑璁搞��
+     */
+    private void validateItemReceiptConfirmable(MesWmItemReceiptDO receipt) {
+        MesWmItemReceiptStatusEnum status = Arrays.stream(MesWmItemReceiptStatusEnum.values())
+                .filter(item -> ObjUtil.equal(item.getStatus(), receipt.getStatus()))
+                .findFirst().orElse(null);
+        String statusName = status != null ? status.getName() : String.valueOf(receipt.getStatus());
+
+        // 1. BPM 瀹℃壒涓殑鍗曟嵁涓嶅厑璁哥洿鎺ュ叆搴擄紝鍚﹀垯浼氱粫杩囧鎵�
+        if (ObjUtil.equal(MesWmItemReceiptStatusEnum.APPROVING.getStatus(), receipt.getStatus())
+                && StrUtil.isNotBlank(receipt.getProcessInstanceId())) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_STATUS_ERROR.getCode(),
+                    "閲囪喘鍏ュ簱鍗昜" + receipt.getCode() + "]姝e湪瀹℃壒涓紙褰撳墠鐘舵�侊細" + statusName + "锛夛紝涓嶈兘纭鍏ュ簱銆�"
+                            + "璇风瓑寰呭鎵归�氳繃鍚庡啀纭鍏ュ簱锛涜嫢瀹℃壒宸查┏鍥烇紝璇锋寜瀹℃壒鎰忚淇敼鍚庨噸鏂版彁浜ゃ��");
+        }
+        // 2. 宸插畬鎴� / 宸插彇娑堜笉鍏佽閲嶅纭
+        if (ObjUtil.equal(MesWmItemReceiptStatusEnum.FINISHED.getStatus(), receipt.getStatus())
+                || ObjUtil.equal(MesWmItemReceiptStatusEnum.CANCELED.getStatus(), receipt.getStatus())) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_STATUS_ERROR.getCode(),
+                    "閲囪喘鍏ュ簱鍗昜" + receipt.getCode() + "]褰撳墠鐘舵�佷负[" + statusName + "]锛屼笉鑳界‘璁ゅ叆搴撱��"
+                            + "鍙湁鑽夌銆佸緟涓婃灦鎴栧緟鎵ц鍏ュ簱鐨勫崟鎹彲浠ョ‘璁ゅ叆搴擄紝璇峰埛鏂板垪琛ㄧ‘璁ゅ崟鎹姸鎬併��");
+        }
+    }
+
+    /**
+     * 鏍¢獙纭鍏ュ簱鏃堕�夋嫨鐨勪粨搴� / 搴撳尯 / 搴撲綅
+     */
+    private void validateConfirmLocation(String receiptCode, Long warehouseId, Long locationId, Long areaId) {
+        if (warehouseId == null || locationId == null || areaId == null) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_REQUIRED.getCode(),
+                    "閲囪喘鍏ュ簱鍗昜" + receiptCode + "]纭鍏ュ簱鍓嶅繀椤婚�夋嫨鍏ュ簱浠撳簱銆佸簱鍖哄拰搴撲綅锛堝綋鍓�"
+                            + "浠撳簱=" + (warehouseId != null ? warehouseId : "鏈�夋嫨")
+                            + "锛屽簱鍖�=" + (locationId != null ? locationId : "鏈�夋嫨")
+                            + "锛屽簱浣�=" + (areaId != null ? areaId : "鏈�夋嫨")
+                            + "锛夈�傝鐐瑰嚮銆岀‘璁ゅ叆搴撱�嶏紝鍦ㄥ脊绐椾腑渚濇閫夋嫨浠撳簱銆佸簱鍖恒�佸簱浣嶅悗閲嶆柊鎻愪氦銆�");
+        }
+        MesWmWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
+        if (warehouse == null) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_REQUIRED.getCode(),
+                    "鎵�閫夊叆搴撲粨搴�(ID=" + warehouseId + ")涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎锛屾棤娉曠‘璁ゅ叆搴撱��"
+                            + "璇峰埛鏂伴〉闈㈠悗閲嶆柊閫夋嫨浠撳簱銆佸簱鍖恒�佸簱浣嶃��");
+        }
+        MesWmWarehouseLocationDO location = warehouseLocationService.getWarehouseLocation(locationId);
+        if (location == null) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_REQUIRED.getCode(),
+                    "鎵�閫夊叆搴撳簱鍖�(ID=" + locationId + ")涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎锛屾棤娉曠‘璁ゅ叆搴撱��"
+                            + "璇峰埛鏂伴〉闈㈠悗閲嶆柊閫夋嫨浠撳簱銆佸簱鍖恒�佸簱浣嶃��");
+        }
+        MesWmWarehouseAreaDO area = warehouseAreaService.getWarehouseArea(areaId);
+        if (area == null) {
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_REQUIRED.getCode(),
+                    "鎵�閫夊叆搴撳簱浣�(ID=" + areaId + ")涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎锛屾棤娉曠‘璁ゅ叆搴撱��"
+                            + "璇峰埛鏂伴〉闈㈠悗閲嶆柊閫夋嫨浠撳簱銆佸簱鍖恒�佸簱浣嶃��");
+        }
+        // 浠撳簱 鈫� 搴撳尯 鈫� 搴撲綅 鐨勫眰绾у繀椤讳竴鑷�
+        if (ObjUtil.notEqual(location.getWarehouseId(), warehouse.getId())) {
+            MesWmWarehouseDO locationWarehouse = warehouseService.getWarehouse(location.getWarehouseId());
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_MISMATCH.getCode(),
+                    "鍏ュ簱浠撳簱涓庡簱鍖轰笉鍖归厤锛氭墍閫変粨搴揫" + warehouse.getName() + "]锛屽簱鍖篬" + location.getName()
+                            + "]灞炰簬浠撳簱[" + (locationWarehouse != null ? locationWarehouse.getName()
+                            : "ID=" + location.getWarehouseId()) + "]銆傝閲嶆柊閫夋嫨鍚屼竴浠撳簱涓嬬殑搴撳尯銆�");
+        }
+        if (ObjUtil.notEqual(area.getLocationId(), location.getId())) {
+            MesWmWarehouseLocationDO areaLocation = warehouseLocationService.getWarehouseLocation(area.getLocationId());
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_LOCATION_MISMATCH.getCode(),
+                    "鍏ュ簱搴撳尯涓庡簱浣嶄笉鍖归厤锛氭墍閫夊簱鍖篬" + location.getName() + "]锛屽簱浣峓" + area.getName()
+                            + "]灞炰簬搴撳尯[" + (areaLocation != null ? areaLocation.getName()
+                            : "ID=" + area.getLocationId()) + "]銆傝閲嶆柊閫夋嫨鍚屼竴搴撳尯涓嬬殑搴撲綅銆�");
+        }
+    }
+
+    /**
+     * 鏍¢獙鍗曡鍏ュ簱鏁伴噺
+     */
+    private void validateConfirmLineQuantity(String receiptCode, MesWmItemReceiptLineDO line,
+                                             Map<Long, MdmItemRespDTO> itemMap) {
+        if (line.getReceivedQuantity() != null && line.getReceivedQuantity().compareTo(BigDecimal.ZERO) > 0) {
+            return;
+        }
+        MdmItemRespDTO item = itemMap.get(line.getItemId());
+        String itemText = item != null ? item.getCode() + " " + item.getName() : "ID=" + line.getItemId();
+        throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_QUANTITY_INVALID.getCode(),
+                "閲囪喘鍏ュ簱鍗昜" + receiptCode + "]鐨勭墿鏂欒[" + itemText + "]鍏ュ簱鏁伴噺涓篬"
+                        + (line.getReceivedQuantity() != null ? line.getReceivedQuantity().toPlainString() : "绌�")
+                        + "]锛屽繀椤诲ぇ浜� 0 鎵嶈兘鍏ュ簱銆傝缂栬緫璇ョ墿鏂欒锛屾牳瀵瑰苟濉啓瀹為檯鏀惰揣鏁伴噺鍚庨噸璇曘��");
+    }
+
+    /**
+     * 涓哄惎鐢ㄦ壒娆$鐞嗕絾杩樻病鏈夋壒娆$殑鐗╂枡琛岃ˉ榻愭壒娆�
+     *
+     * 鐢卞埌璐ч�氱煡鍗曠敓鎴愮殑鍏ュ簱琛屼笉浼氶鍏堢敓鎴愭壒娆★紝杩欓噷鍦ㄧ‘璁ゅ叆搴撴椂缁熶竴鐢熸垚锛�
+     * 閬垮厤搴撶閫愯鎵撳紑鐗╂枡琛屼繚瀛樹竴娆°��
+     */
+    private void fillLineBatch(MesWmItemReceiptDO receipt, MesWmItemReceiptLineDO line,
+                               Map<Long, MdmItemRespDTO> itemMap) {
+        MdmItemRespDTO item = itemMap.get(line.getItemId());
+        if (item == null || !Boolean.TRUE.equals(item.getIsBatchManaged()) || line.getBatchId() != null) {
+            return;
+        }
+        MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO();
+        batchReqVO.setItemId(line.getItemId()).setProduceDate(line.getProductionDate())
+                .setExpireDate(line.getExpireDate()).setLotNumber(line.getLotNumber())
+                .setVendorId(receipt.getVendorId()).setReceiptDate(receipt.getReceiptDate())
+                .setPurchaseOrderCode(receipt.getPurchaseOrderCode());
+        MesWmBatchDO batch;
+        try {
+            batch = batchService.getOrGenerateBatchCode(batchReqVO);
+        } catch (ServiceException ex) {
+            // 鎵规閰嶇疆瑕佹眰鐨勫瓧娈碉紙鐢熶骇鏃ユ湡銆佹湁鏁堟湡銆佺敓浜ф壒鍙风瓑锛夋病鏈夊~锛氭妸琛屼笂涓嬫枃琛ヨ繘鎻愮ず
+            throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_BATCH_GENERATE_FAILED.getCode(),
+                    "鐗╂枡[" + item.getCode() + " " + item.getName() + "]鐢熸垚鎵规澶辫触锛�" + ex.getMessage()
+                            + "銆傝缂栬緫閲囪喘鍏ュ簱鍗曢噷璇ョ墿鏂欑殑鐗╂枡琛岋紝琛ュ厖鎵规瑕佹眰鐨勫瓧娈碉紙鐢熶骇鏃ユ湡銆佹湁鏁堟湡銆佺敓浜ф壒鍙凤級鍚庨噸璇曘��");
+        }
+        if (batch == null) {
+            return;
+        }
+        line.setBatchId(batch.getId()).setBatchCode(batch.getCode());
+        itemReceiptLineService.updateItemReceiptLineBatch(line.getId(), batch.getId(), batch.getCode());
+    }
+
+    /**
+     * 纭鍏ュ簱鏃惰ˉ榻愪笂鏋舵槑缁嗭細娌℃湁鏄庣粏鐨勮鎸夎鍏ュ簱鏁伴噺鐢熸垚涓�鏉℃槑缁嗭紝宸叉湁鏄庣粏鐨勬牎楠屾暟閲忓悎璁�
+     */
+    private void createDetailsForConfirm(MesWmItemReceiptDO receipt, List<MesWmItemReceiptLineDO> lines,
+                                         Long warehouseId, Long locationId, Long areaId) {
+        for (MesWmItemReceiptLineDO line : lines) {
+            List<MesWmItemReceiptDetailDO> details = itemReceiptDetailService
+                    .getItemReceiptDetailListByLineId(line.getId());
+            if (CollUtil.isEmpty(details)) {
+                // 鏃ф祦绋嬧�滃緟鎵ц鍏ュ簱鈥濈殑鍗曟嵁搴斿綋宸茬粡鏈変笂鏋舵槑缁嗭紝缂轰簡璇存槑鏁版嵁涓嶅畬鏁�
+                if (ObjUtil.equal(MesWmItemReceiptStatusEnum.APPROVED.getStatus(), receipt.getStatus())) {
+                    throw new ServiceException(WM_ITEM_RECEIPT_CONFIRM_DETAIL_MISSING.getCode(),
+                            "閲囪喘鍏ュ簱鍗昜" + receipt.getCode() + "]澶勪簬鏃ф祦绋嬬殑銆屽緟鎵ц鍏ュ簱銆嶇姸鎬侊紝浣嗙墿鏂欒(ID="
+                                    + line.getId() + ")娌℃湁涓婃灦鏄庣粏锛屾棤娉曠‘璁ゅ叆搴撱��"
+                                    + "璇峰皢璇ュ崟鎹彇娑堝悗閲嶆柊鐢熸垚锛屾垨鑱旂郴绠$悊鍛樿ˉ榻愪笂鏋舵槑缁嗐��");
+                }
+                MesWmItemReceiptDetailSaveReqVO detailReqVO = new MesWmItemReceiptDetailSaveReqVO();
+                detailReqVO.setReceiptId(receipt.getId()).setLineId(line.getId())
+                        .setItemId(line.getItemId()).setQuantity(line.getReceivedQuantity())
+                        .setBatchId(line.getBatchId())
+                        .setWarehouseId(warehouseId).setLocationId(locationId).setAreaId(areaId);
+                itemReceiptDetailService.createItemReceiptDetail(detailReqVO);
+                continue;
+            }
+            // 宸叉湁涓婃灦鏄庣粏锛堟棫娴佺▼锛夛細鏁伴噺鍚堣蹇呴』涓庤鍏ュ簱鏁伴噺涓�鑷�
+            BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
+                    MesWmItemReceiptDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
+            if (line.getReceivedQuantity() == null || totalDetailQty.compareTo(line.getReceivedQuantity()) != 0) {
+                MdmItemRespDTO item = mdmItemApi.getItem(line.getItemId()).getCheckedData();
+                String itemText = item != null ? item.getCode() + " " + item.getName() : "ID=" + line.getItemId();
+                throw new ServiceException(WM_ITEM_RECEIPT_DETAIL_QUANTITY_MISMATCH.getCode(),
+                        "閲囪喘鍏ュ簱鍗昜" + receipt.getCode() + "]鐨勭墿鏂欒[" + itemText + "]涓婃灦鏄庣粏鏁伴噺鍚堣涓篬"
+                                + totalDetailQty.toPlainString() + "]锛屼笌琛屽叆搴撴暟閲廩"
+                                + (line.getReceivedQuantity() != null ? line.getReceivedQuantity().toPlainString() : "绌�")
+                                + "]涓嶄竴鑷达紝鏃犳硶纭鍏ュ簱銆傝鍦ㄧ墿鏂欒涓婃牳瀵瑰叆搴撴暟閲忥紝鎴栧彇娑堣鍗曟嵁鍚庢寜绠�鍖栨祦绋嬮噸鏂扮敓鎴愩��");
+            }
+        }
     }
 
     /**
@@ -424,13 +654,19 @@
 
         // 6. 閬嶅巻琛屽垱寤哄叆搴撳崟琛�
         for (MesWmArrivalNoticeLineDO noticeLine : lines) {
+            BigDecimal receivable = noticeLine.getQualifiedQuantity() != null
+                    ? noticeLine.getQualifiedQuantity() : noticeLine.getArrivalQuantity();
+            // 鏁磋宸查��渚涘簲鍟嗘椂涓嶇敓鎴愰浂鏁伴噺鍏ュ簱琛岋紱鍏朵綑琛屼粛姝e父鏀惰揣銆�
+            if (receivable == null || receivable.signum() <= 0) {
+                continue;
+            }
             MesWmItemReceiptLineDO receiptLine = new MesWmItemReceiptLineDO();
             receiptLine.setReceiptId(receipt.getId());
             receiptLine.setArrivalNoticeLineId(noticeLine.getId());
             receiptLine.setPurchaseOrderItemId(noticeLine.getPurchaseOrderItemId());
             receiptLine.setItemId(noticeLine.getItemId());
             // 浣跨敤鍚堟牸鏁伴噺锛堝鏋滄湁 IQC 鍒欐槸鍚堟牸鏁伴噺锛屽惁鍒欐槸鍒拌揣鏁伴噺锛�
-            receiptLine.setReceivedQuantity(noticeLine.getQualifiedQuantity() != null ? noticeLine.getQualifiedQuantity() : noticeLine.getArrivalQuantity());
+            receiptLine.setReceivedQuantity(receivable);
             // 鎸夐噰璐鍗曡鍗曚环蹇収閲戦锛屼緵鏉ョエ/瀵硅处浣跨敤
             fillReceiptLinePrice(receiptLine);
             itemReceiptLineService.createItemReceiptLineFromArrivalNoticeLine(receiptLine);
@@ -465,7 +701,12 @@
     @Transactional(rollbackFor = Exception.class)
     public void submitItemReceiptApproval(Long id, String processDefinitionKey) {
         // 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
-        validateItemReceiptExistsAndDraft(id);
+        MesWmItemReceiptDO receipt = validateItemReceiptExistsAndDraft(id);
+        // 瀹℃壒鍏ュ彛涓庢櫘閫氭彁浜ゅ叆鍙d繚鎸佷竴鑷达紝绌鸿鍗曟嵁涓嶈兘杩涘叆瀹℃壒銆�
+        List<MesWmItemReceiptLineDO> lines = itemReceiptLineService.getItemReceiptLineListByReceiptId(id);
+        if (CollUtil.isEmpty(lines)) {
+            throw exception(WM_ITEM_RECEIPT_NO_LINE);
+        }
 
         // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥
         Long userId = getCurrentUserId();
@@ -479,10 +720,21 @@
             return;
         }
 
+        if (StrUtil.isBlank(processDefinitionKey)) {
+            throw exception(WM_ITEM_RECEIPT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+        }
+        ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
+        if (processDefinition == null || bpmProcessDefinitionService
+                .getProcessDefinitionInfoListByCategory(ITEM_RECEIPT_APPROVE_CATEGORY_CODE).stream()
+                .noneMatch(info -> Objects.equals(info.getProcessDefinitionId(), processDefinition.getId()))) {
+            throw exception(WM_ITEM_RECEIPT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+        }
+
         String processInstanceId = processInstanceApi.createProcessInstance(userId,
                 new BpmProcessInstanceCreateReqDTO()
                         .setProcessDefinitionKey(processDefinitionKey)
-                        .setBusinessKey(String.valueOf(id)));
+                        .setBusinessKey(String.valueOf(id))
+                        .setVariables(Map.of("businessNo", receipt.getCode())));
 
         // 3. 鏇存柊鍏ュ簱鍗曠姸鎬佷负瀹℃壒涓�
         itemReceiptMapper.updateById(new MesWmItemReceiptDO()
@@ -504,8 +756,11 @@
         // 2. 鏍规嵁瀹℃壒缁撴灉鏇存柊鐘舵��
         Integer newStatus = convertBpmResultToReceiptStatus(bpmResult);
         if (newStatus != null) {
-            itemReceiptMapper.updateById(new MesWmItemReceiptDO()
-                    .setId(id).setStatus(newStatus));
+            itemReceiptMapper.update(null, new LambdaUpdateWrapper<MesWmItemReceiptDO>()
+                    .eq(MesWmItemReceiptDO::getId, id)
+                    .set(MesWmItemReceiptDO::getStatus, newStatus)
+                    .set(MesWmItemReceiptDO::getApproveTime,
+                            BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult) ? LocalDateTime.now() : null));
         }
     }
 
@@ -527,11 +782,7 @@
      * 鑾峰彇褰撳墠鐢ㄦ埛 ID
      */
     private Long getCurrentUserId() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (authentication != null && authentication.getPrincipal() instanceof Long) {
-            return (Long) authentication.getPrincipal();
-        }
-        return 1L; // 榛樿鐢ㄦ埛
+        return Objects.requireNonNull(getLoginUserId(), "閲囪喘鍏ュ簱瀹℃壒闇�瑕佺櫥褰曠敤鎴�");
     }
 
     /**

--
Gitblit v1.9.3