云
2026-10-25 d54fbc9d4f8981fa0f7f613b33a28a8cd4f764ea
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchasePlanServiceImpl.java
@@ -5,6 +5,8 @@
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanImportExcelVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanImportReqVO;
@@ -66,6 +68,11 @@
    private static final String PURCHASE_PLAN_APPROVE_BIZ_TYPE = "purchase_plan_approve";
    /**
     * 采购计划审批的 BPM 流程定义 Key
     */
    private static final String PURCHASE_PLAN_PROCESS_KEY = "purchase_plan_approve";
    /**
     * 需求来源:库存预警
     */
    public static final Integer DEMAND_SOURCE_STOCK_ALERT = 1;
@@ -89,6 +96,9 @@
    @Resource
    private ApprovalConfigApi approvalConfigApi;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Resource
    private AdminUserApi adminUserApi;
@@ -219,6 +229,11 @@
        if (purchasePlanMapper.submitAndResetAuditInfo(id, ErpPurchasePlanStatusEnum.PROCESS.getStatus()) != 1) {
            throw exception(PURCHASE_PLAN_SUBMIT_FAIL_NOT_ALLOWED);
        }
        // 5. 创建 BPM 流程实例,使审批出现在「协同办公 - 我的流程」
        bpmProcessInstanceApi.createProcessInstance(getLoginUserId(),
                new BpmProcessInstanceCreateReqDTO()
                        .setProcessDefinitionKey(PURCHASE_PLAN_PROCESS_KEY)
                        .setBusinessKey(String.valueOf(id)));
    }
    @Override
@@ -308,6 +323,34 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchasePlanAuditStatus(Long id, Integer bpmStatus, String reason) {
        // 1. BPM 审批结果映射为采购计划状态:通过=20 / 不通过=30 / 取消=40
        Integer newStatus = convertBpmResultToStatus(bpmStatus);
        if (newStatus == null) {
            return;
        }
        // 2. 仅当仍处于「审批中」时回写,避免重复覆盖
        if (purchasePlanMapper.updateAuditStatusByBpm(id, newStatus, reason) != 1) {
            log.warn("[updatePurchasePlanAuditStatus] 采购计划({}) 不处于审批中状态,忽略 BPM 结果({})", id, bpmStatus);
            return;
        }
        // 3. 审批通过且未生成申请,自动生成采购申请
        if (ErpPurchasePlanStatusEnum.APPROVE.getStatus().equals(newStatus)) {
            ErpPurchasePlanDO plan = purchasePlanMapper.selectById(id);
            if (plan != null && !Boolean.TRUE.equals(plan.getGeneratedFlag())) {
                try {
                    List<Long> requestIds = doGeneratePurchaseRequests(plan);
                    log.info("[updatePurchasePlanAuditStatus] 采购计划({}) 自动生成采购申请({})", id, requestIds);
                } catch (Exception e) {
                    log.error("[updatePurchasePlanAuditStatus] 采购计划({}) 自动生成采购申请失败", id, e);
                    // 不抛出异常,避免影响状态更新
                }
            }
        }
    }
    @Override
    public List<ErpPurchasePlanRespVO.Item> getStockAlertSuggestItems() {
        // 1. 获取启用的物料基础数据(包含默认仓库、安全/最低/最高库存)
        List<MdmItemRespDTO> items = mdmItemApi.getItemListByStatus(0).getCheckedData();