From 035e28134ff1ace5d7117007dd72cd18c68f14ff Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 10 八月 2026 17:55:44 +0800
Subject: [PATCH] refactor(crm): 重构回款审批流程为状态更新模式

---
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java         |    9 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java                                    |   10 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java                 |   66 +-----
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java        |   21 +
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java |    3 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java                     |   16 -
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java      |   12 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java                    |    6 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java                                    |    2 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java                  |   14 -
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java                    |   22 ++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/listener/ErpFinancePaymentStatusListener.java    |   47 +++++
 /dev/null                                                                                                                   |   43 ----
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java                  |    6 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java                |  139 +++++++++++++-
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java              |   72 +------
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java                 |    6 
 17 files changed, 285 insertions(+), 209 deletions(-)

diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
index e3c6ba8..57c8fc1 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -176,11 +176,12 @@
         });
     }
 
-    @PutMapping("/submit")
-    @Operation(summary = "鎻愪氦鍥炴瀹℃壒")
+    @PutMapping("/update-status")
+    @Operation(summary = "鏇存柊鍥炴鐨勭姸鎬侊紙瀹℃牳/鍙嶅鏍革級")
     @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
-    public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
-        receivableService.submitReceivable(id, getLoginUserId());
+    public CommonResult<Boolean> updateReceivableStatus(@RequestParam("id") Long id,
+                                                        @RequestParam("status") Integer status) {
+        receivableService.updateReceivableStatus(id, status);
         return success(true);
     }
 
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
index 496afea..fa39454 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -13,6 +13,7 @@
 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
 import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.math.BigDecimal;
@@ -35,6 +36,11 @@
         return selectOne(CrmReceivableDO::getNo, no);
     }
 
+    default int updateByIdAndStatus(Long id, Integer status, CrmReceivableDO updateObj) {
+        return update(updateObj, new LambdaUpdateWrapper<CrmReceivableDO>()
+                .eq(CrmReceivableDO::getId, id).eq(CrmReceivableDO::getAuditStatus, status));
+    }
+
     default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()
                 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 蹇呴』浼犻��
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
index d514bbe..59f92fa 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -47,6 +47,8 @@
     ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "鍒涘缓鍥炴澶辫触锛屽師鍥狅細鍚堝悓涓嶆槸瀹℃牳閫氳繃鐘舵��");
     ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "鍒涘缓鍥炴澶辫触锛屽師鍥狅細鍥炴閲戦瓒呭嚭鍚堝悓閲戦锛岀洰鍓嶅墿浣欏彲閫�锛歿} 鍏�");
     ErrorCode RECEIVABLE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_020_004_008, "鍒犻櫎鍥炴澶辫触锛屽師鍥狅細鍥炴瀹℃壒宸查�氳繃");
+    ErrorCode RECEIVABLE_APPROVE_FAIL = new ErrorCode(1_020_004_009, "瀹℃牳澶辫触锛屽彧鏈夋湭瀹℃牳鐨勫洖娆炬墠鑳藉鏍�");
+    ErrorCode RECEIVABLE_PROCESS_FAIL = new ErrorCode(1_020_004_010, "鍙嶅鏍稿け璐ワ紝鍙湁宸插鏍哥殑鍥炴鎵嶈兘鍙嶅鏍�");
 
     // ========== 鍥炴璁″垝 1-020-005-000 ==========
     ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "鍥炴璁″垝涓嶅瓨鍦�");
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
index 1713c29..55ee80c 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
@@ -37,12 +37,12 @@
     void updateReceivable(@Valid CrmReceivableSaveReqVO updateReqVO);
 
     /**
-     * 鏇存柊鍥炴娴佺▼瀹℃壒缁撴灉
+     * 鏇存柊鍥炴鐨勭姸鎬侊紙瀹℃牳/鍙嶅鏍革級
      *
-     * @param id        鍥炴缂栧彿
-     * @param bpmResult BPM 瀹℃壒缁撴灉
+     * @param id     缂栧彿
+     * @param status 鐘舵��
      */
-    void updateReceivableAuditStatus(Long id, Integer bpmResult);
+    void updateReceivableStatus(Long id, Integer status);
 
     /**
      * 鍒犻櫎鍥炴
@@ -50,14 +50,6 @@
      * @param id 缂栧彿
      */
     void deleteReceivable(Long id);
-
-    /**
-     * 鍙戣捣鍥炴瀹℃壒娴佺▼
-     *
-     * @param id     鍥炴缂栧彿
-     * @param userId 鐢ㄦ埛缂栧彿
-     */
-    void submitReceivable(Long id, Long userId);
 
     /**
      * 鑾峰緱鍥炴
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
index f655977..02dad68 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -8,9 +8,6 @@
 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.bpm.api.task.BpmProcessInstanceApi;
-import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -34,7 +31,6 @@
 import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -46,7 +42,6 @@
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
-import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
 
 /**
  * CRM 鍥炴 Service 瀹炵幇绫�
@@ -55,13 +50,7 @@
  */
 @Service
 @Validated
-@Slf4j
 public class CrmReceivableServiceImpl implements CrmReceivableService {
-
-    /**
-     * BPM 鍚堝悓瀹℃壒娴佺▼鏍囪瘑
-     */
-    public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit";
 
     @Resource
     private CrmReceivableMapper receivableMapper;
@@ -81,8 +70,6 @@
 
     @Resource
     private AdminUserApi adminUserApi;
-    @Resource
-    private BpmProcessInstanceApi bpmProcessInstanceApi;
     @Resource
     private StorageAttachmentApi storageAttachmentApi;
 
@@ -105,9 +92,9 @@
             throw exception(RECEIVABLE_NO_EXISTS);
         }
 
-        // 2.1 鎻掑叆鍥炴
+        // 2.1 鎻掑叆鍥炴锛堝垱寤哄悗鍗充负鏈鏍哥姸鎬侊級
         CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
-                .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
+                .setNo(no).setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus());
         receivableMapper.insert(receivable);
         // 2.2
 
@@ -240,9 +227,8 @@
         // 1.5 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃璇ュ紑绁ㄥ墿浣欏彲鍥炴閲戦
         validateReceivablePriceExceedsInvoiceRemaining(updateReqVO);
 
-        // 1.6 鍙湁鑽夌銆佸鎵逛腑锛屽彲浠ョ紪杈戯紱
-        if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
-                CrmAuditStatusEnum.PROCESS.getStatus())) {
+        // 1.6 鍙湁鏈鏍革紝鍙互缂栬緫锛�
+        if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
         }
 
@@ -266,19 +252,22 @@
     }
 
     @Override
-    public void updateReceivableAuditStatus(Long id, Integer bpmResult) {
+    @Transactional(rollbackFor = Exception.class)
+    public void updateReceivableStatus(Long id, Integer status) {
+        boolean approve = CrmAuditStatusEnum.APPROVE.getStatus().equals(status);
         // 1.1 鏍¢獙瀛樺湪
         CrmReceivableDO receivable = validateReceivableExists(id);
-        // 1.2 鍙湁瀹℃壒涓紝鍙互鏇存柊瀹℃壒缁撴灉
-        if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
-            log.error("[updateReceivableAuditStatus][receivable({}) 涓嶅浜庡鎵逛腑锛屾棤娉曟洿鏂板鎵圭粨鏋�({})]",
-                    receivable.getId(), bpmResult);
-            throw exception(RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
+        // 1.2 鏍¢獙鐘舵��
+        if (receivable.getAuditStatus().equals(status)) {
+            throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
         }
 
-        // 2. 鏇存柊鍥炴瀹℃壒鐘舵��
-        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
-        receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));
+        // 2. 鏇存柊鐘舵��
+        int updateCount = receivableMapper.updateByIdAndStatus(id, receivable.getAuditStatus(),
+                new CrmReceivableDO().setAuditStatus(status));
+        if (updateCount == 0) {
+            throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
+        }
     }
 
     @Override
@@ -306,29 +295,6 @@
         // 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
         LogRecordContext.putVariable("receivable", receivable);
         LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId()));
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
-            success = CRM_RECEIVABLE_SUBMIT_SUCCESS)
-    public void submitReceivable(Long id, Long userId) {
-        // 1. 鏍¢獙鍥炴鏄惁鍦ㄥ鎵�
-        CrmReceivableDO receivable = validateReceivableExists(id);
-        if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
-            throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
-        }
-
-        // 2. 鍒涘缓鍥炴瀹℃壒娴佺▼瀹炰緥
-        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
-                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
-
-        // 3. 鏇存柊鍥炴宸ヤ綔娴佺紪鍙�
-        receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)
-                .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
-
-        // 4. 璁板綍鏃ュ織
-        LogRecordContext.putVariable("receivableNo", receivable.getNo());
     }
 
     private CrmReceivableDO validateReceivableExists(Long id) {
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java
deleted file mode 100644
index f0ddd86..0000000
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package cn.iocoder.yudao.module.crm.service.receivable.listener;
-
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
-import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
-import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableServiceImpl;
-import jakarta.annotation.Resource;
-import org.springframework.stereotype.Component;
-
-/**
- * 鍥炴瀹℃壒鐨勭粨鏋滅殑鐩戝惉鍣ㄥ疄鐜扮被
- *
- * @author HUIHUI
- */
-@Component
-public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener {
-
-    @Resource
-    private CrmReceivableService receivableService;
-
-    @Override
-    public String getProcessDefinitionKey() {
-        return CrmReceivableServiceImpl.BPM_PROCESS_DEFINITION_KEY;
-    }
-
-    @Override
-    public void onEvent(BpmProcessInstanceStatusEvent event) {
-        receivableService.updateReceivableAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());
-    }
-
-}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
index 21fe41e..e24d92f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
@@ -43,6 +43,7 @@
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 浠樻鍗�")
 @RestController
@@ -79,15 +80,23 @@
         return success(true);
     }
 
-    @PutMapping("/update-status")
-    @Operation(summary = "鏇存柊浠樻鍗曠殑鐘舵��")
-    @PreAuthorize("@ss.hasPermission('erp:finance-payment:update-status')")
-    public CommonResult<Boolean> updateFinancePaymentStatus(@RequestParam("id") Long id,
-                                                           @RequestParam("status") Integer status) {
-        financePaymentService.updateFinancePaymentStatus(id, status);
+    @PostMapping("/submit")
+    @Operation(summary = "鎻愪氦浠樻鍗曞鎵�")
+    @PreAuthorize("@ss.hasPermission('erp:finance-payment:update')")
+    public CommonResult<Boolean> submitFinancePayment(@RequestParam("id") Long id,
+                                                      @RequestParam("processDefinitionKey") String processDefinitionKey) {
+        Long userId = getLoginUserId();
+        financePaymentService.submitFinancePayment(id, processDefinitionKey, userId);
         return success(true);
     }
 
+    @GetMapping("/approve-process-list")
+    @Operation(summary = "鑾峰彇浠樻鍗曞鎵规祦绋嬪垪琛�")
+    @PreAuthorize("@ss.hasPermission('erp:finance-payment:query')")
+    public CommonResult<List<Map<String, Object>>> getApproveProcessList() {
+        return success(financePaymentService.getFinancePaymentApproveProcessDefinitionList());
+    }
+
     @DeleteMapping("/delete")
     @Operation(summary = "鍒犻櫎浠樻鍗�")
     @Parameter(name = "ids", description = "缂栧彿鏁扮粍", required = true)
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java
index 4bc732e..05d7d76 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java
@@ -26,6 +26,9 @@
     @Schema(description = "浠樻鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer status;
 
+    @Schema(description = "宸ヤ綔娴佺紪鍙�", example = "3cc7f8e6")
+    private String processInstanceId;
+
     @Schema(description = "浠樻鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime paymentTime;
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
index 8270f23..8d00f34 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
@@ -49,7 +49,6 @@
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘鏉ョエ")
 @RestController
@@ -170,11 +169,12 @@
         });
     }
 
-    @PutMapping("/submit")
-    @Operation(summary = "鎻愪氦鏉ョエ瀹℃壒")
-    @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:update')")
-    public CommonResult<Boolean> submitPurchaseInvoice(@RequestParam("id") Long id) {
-        invoiceService.submitPurchaseInvoice(id, getLoginUserId());
+    @PutMapping("/update-status")
+    @Operation(summary = "鏇存柊鏉ョエ鐨勭姸鎬侊紙瀹℃牳/鍙嶅鏍革級")
+    @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:update-status')")
+    public CommonResult<Boolean> updatePurchaseInvoiceStatus(@RequestParam("id") Long id,
+                                                             @RequestParam("status") Integer status) {
+        invoiceService.updatePurchaseInvoiceStatus(id, status);
         return success(true);
     }
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java
index 8453b75..dfdaeda 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java
@@ -42,6 +42,12 @@
      */
     private Integer status;
     /**
+     * 宸ヤ綔娴佺紪鍙�
+     *
+     * 鍏宠仈 BPM 娴佺▼瀹炰緥鐨� id
+     */
+    private String processInstanceId;
+    /**
      * 浠樻鏃堕棿
      */
     private LocalDateTime paymentTime;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java
index 2c9b681..cdbef63 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java
@@ -6,6 +6,7 @@
 import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO;
 import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO;
 import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.Collection;
@@ -23,6 +24,11 @@
         return selectOne(ErpPurchaseInvoiceDO::getNo, no);
     }
 
+    default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseInvoiceDO updateObj) {
+        return update(updateObj, new LambdaUpdateWrapper<ErpPurchaseInvoiceDO>()
+                .eq(ErpPurchaseInvoiceDO::getId, id).eq(ErpPurchaseInvoiceDO::getAuditStatus, status));
+    }
+
     default PageResult<ErpPurchaseInvoiceDO> selectPage(ErpPurchaseInvoicePageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<ErpPurchaseInvoiceDO>()
                 .likeIfPresent(ErpPurchaseInvoiceDO::getNo, reqVO.getNo())
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
index b295513..64e984f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -57,13 +57,14 @@
     // ========== ERP 閲囪喘鏉ョエ锛�1-030-105-000锛� ==========
     ErrorCode PURCHASE_INVOICE_NOT_EXISTS = new ErrorCode(1_030_105_000, "鏉ョエ涓嶅瓨鍦�");
     ErrorCode PURCHASE_INVOICE_NO_EXISTS = new ErrorCode(1_030_105_001, "鐢熸垚鏉ョエ缂栧彿澶辫触锛岃閲嶆柊鎻愪氦");
-    ErrorCode PURCHASE_INVOICE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_030_105_002, "鏉ョエ({})涓嶆槸鑽夌鐘舵�侊紝鏃犳硶淇敼");
+    ErrorCode PURCHASE_INVOICE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_030_105_002, "鏉ョエ({})涓嶆槸鏈鏍哥姸鎬侊紝鏃犳硶淇敼");
     ErrorCode PURCHASE_INVOICE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_030_105_003, "鍒犻櫎鏉ョエ澶辫触锛屽師鍥狅細鏉ョエ瀹℃壒宸查�氳繃");
-    ErrorCode PURCHASE_INVOICE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_105_004, "鏉ョエ鎻愪氦瀹℃牳澶辫触锛屽師鍥狅細鏉ョエ娌″鍦ㄦ湭鎻愪氦鐘舵��");
     ErrorCode PURCHASE_INVOICE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_030_105_005, "鏇存柊鏉ョエ瀹℃牳鐘舵�佸け璐ワ紝鍘熷洜锛氭潵绁ㄤ笉鏄鏍镐腑鐘舵��");
     ErrorCode PURCHASE_INVOICE_CREATE_FAIL_ORDER_NOT_APPROVE = new ErrorCode(1_030_105_006, "鍒涘缓鏉ョエ澶辫触锛屽師鍥狅細閲囪喘璁㈠崟涓嶆槸瀹℃牳閫氳繃鐘舵��");
     ErrorCode PURCHASE_INVOICE_CREATE_FAIL_SUPPLIER_MISMATCH = new ErrorCode(1_030_105_007, "鍒涘缓鏉ョエ澶辫触锛屽師鍥狅細鏉ョエ渚涘簲鍟嗕笌閲囪喘璁㈠崟涓嶄竴鑷�");
     ErrorCode PURCHASE_INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_030_105_008, "鍒涘缓鏉ョエ澶辫触锛屽師鍥狅細鏉ョエ閲戦瓒呭嚭閲囪喘璁㈠崟閲戦锛岀洰鍓嶅彲寮�绁細{} 鍏�");
+    ErrorCode PURCHASE_INVOICE_APPROVE_FAIL = new ErrorCode(1_030_105_009, "瀹℃牳澶辫触锛屽彧鏈夋湭瀹℃牳鐨勬潵绁ㄦ墠鑳藉鏍�");
+    ErrorCode PURCHASE_INVOICE_PROCESS_FAIL = new ErrorCode(1_030_105_010, "鍙嶅鏍稿け璐ワ紝鍙湁宸插鏍哥殑鏉ョエ鎵嶈兘鍙嶅鏍�");
 
     // ========== ERP 閲囪喘閫�璐э紙1-030-103-000锛� ==========
     ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, "閲囪喘閫�璐у崟涓嶅瓨鍦�");
@@ -196,6 +197,11 @@
     ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_ATTACHMENT = new ErrorCode(1_030_601_008, "鍒涘缓浠樻鍗曞け璐ワ紝鍘熷洜锛氬叧鑱旂殑鏉ョエ鏈笂浼犲彂绁ㄩ檮浠�");
     ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_SUPPLIER_MISMATCH = new ErrorCode(1_030_601_009, "鍒涘缓浠樻鍗曞け璐ワ紝鍘熷洜锛氬叧鑱旂殑鏉ョエ涓庢墍閫変緵搴斿晢涓嶅尮閰�");
     ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_PRICE_EXCEEDS = new ErrorCode(1_030_601_010, "鍒涘缓浠樻鍗曞け璐ワ紝鍘熷洜锛氫粯娆鹃噾棰濊秴鍑鸿鏉ョエ鍓╀綑鍙粯閲戦锛岀洰鍓嶅彲浠橈細{} 鍏�");
+    ErrorCode FINANCE_PAYMENT_SUBMIT_FAIL_STATUS = new ErrorCode(1_030_601_011, "鎻愪氦瀹℃壒澶辫触锛屽彧鏈夋湭瀹℃牳鐘舵�佺殑浠樻鍗曟墠鑳芥彁浜�");
+    ErrorCode FINANCE_PAYMENT_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_601_012, "浠樻鍗曞鎵瑰垎绫讳笉瀛樺湪锛岃鍏堝湪BPM妯″潡鍒涘缓閲囪喘瀹℃牳娴佺▼鍒嗙被");
+    ErrorCode FINANCE_PAYMENT_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_601_013, "浠樻鍗曞鎵瑰垎绫讳笅娌℃湁鍙敤鐨勬祦绋嬫ā鍨嬶紝璇峰厛鍒涘缓骞跺彂甯冩祦绋�");
+    ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVING = new ErrorCode(1_030_601_014, "浠樻鍗�({})瀹℃壒涓紝鏃犳硶淇敼");
+    ErrorCode FINANCE_PAYMENT_DELETE_FAIL_APPROVING = new ErrorCode(1_030_601_015, "浠樻鍗�({})瀹℃壒涓紝鏃犳硶鍒犻櫎");
 
     // ========== ERP 鏀舵鍗� 1-030-602-000 ==========
     ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, "鏀舵鍗曚笉瀛樺湪");
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java
index b7c2547..a58be47 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java
@@ -35,12 +35,28 @@
     void updateFinancePayment(@Valid ErpFinancePaymentSaveReqVO updateReqVO);
 
     /**
-     * 鏇存柊浠樻鍗曠殑鐘舵��
+     * 鎻愪氦浠樻鍗曞鎵�
      *
      * @param id 缂栧彿
-     * @param status 鐘舵��
+     * @param processDefinitionKey 娴佺▼瀹氫箟鐨勭紪鍙�
+     * @param userId 鐢ㄦ埛缂栧彿
      */
-    void updateFinancePaymentStatus(Long id, Integer status);
+    void submitFinancePayment(Long id, String processDefinitionKey, Long userId);
+
+    /**
+     * 鑾峰緱浠樻鍗曞鎵规祦绋嬪畾涔夊垪琛�
+     *
+     * @return 娴佺▼瀹氫箟鍒楄〃
+     */
+    List<Map<String, Object>> getFinancePaymentApproveProcessDefinitionList();
+
+    /**
+     * 鏇存柊浠樻鍗曠殑瀹℃壒鐘舵�侊紙鐢� BPM 娴佺▼瀹炰緥鐨勫洖璋冭Е鍙戯級
+     *
+     * @param id 缂栧彿
+     * @param bpmResult BPM 瀹℃壒缁撴灉
+     */
+    void updateFinancePaymentAuditStatus(Long id, Integer bpmResult);
 
     /**
      * 鍒犻櫎浠樻鍗�
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java
index 6be9fa0..408d6d6 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java
@@ -4,6 +4,13 @@
 import cn.hutool.core.util.ObjectUtil;
 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.dal.dataobject.definition.BpmCategoryDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;
 import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;
 import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;
@@ -20,15 +27,14 @@
 import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.repository.ProcessDefinition;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -41,7 +47,13 @@
  */
 @Service
 @Validated
+@Slf4j
 public class ErpFinancePaymentServiceImpl implements ErpFinancePaymentService {
+
+    /**
+     * BPM 浠樻鍗曞鎵瑰垎绫荤紪鐮�
+     */
+    private static final String BPM_PROCESS_DEFINITION_CATEGORY_CODE = "erp_finance_payment_approve";
 
     @Resource
     private ErpFinancePaymentMapper financePaymentMapper;
@@ -63,6 +75,13 @@
 
     @Resource
     private StorageAttachmentApi storageAttachmentApi;
+
+    @Resource
+    private BpmProcessInstanceApi processInstanceApi;
+    @Resource
+    private BpmCategoryService bpmCategoryService;
+    @Resource
+    private BpmProcessDefinitionService bpmProcessDefinitionService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -118,6 +137,9 @@
         if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) {
             throw exception(FINANCE_PAYMENT_UPDATE_FAIL_APPROVE, payment.getNo());
         }
+        if (ErpAuditStatus.APPROVING.getStatus().equals(payment.getStatus())) {
+            throw exception(FINANCE_PAYMENT_UPDATE_FAIL_APPROVING, payment.getNo());
+        }
         // 1.2 鏍¢獙渚涘簲鍟�
         supplierService.validateSupplier(updateReqVO.getSupplierId());
         // 1.3 鏍¢獙缁撶畻璐︽埛
@@ -155,21 +177,105 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void updateFinancePaymentStatus(Long id, Integer status) {
-        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
-        // 1.1 鏍¢獙瀛樺湪
+    public void submitFinancePayment(Long id, String processDefinitionKey, Long userId) {
+        // 1. 鏍¢獙瀛樺湪 + 鏈鏍哥姸鎬�
         ErpFinancePaymentDO payment = validateFinancePaymentExists(id);
-        // 1.2 鏍¢獙鐘舵��
-        if (payment.getStatus().equals(status)) {
-            throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL);
+        if (!ErpAuditStatus.PROCESS.getStatus().equals(payment.getStatus())) {
+            throw exception(FINANCE_PAYMENT_SUBMIT_FAIL_STATUS);
         }
 
-        // 2. 鏇存柊鐘舵��
-        int updateCount = financePaymentMapper.updateByIdAndStatus(id, payment.getStatus(),
-                new ErpFinancePaymentDO().setStatus(status));
-        if (updateCount == 0) {
-            throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL);
+        // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥
+        String processInstanceId = processInstanceApi.createProcessInstance(userId,
+                new BpmProcessInstanceCreateReqDTO()
+                        .setProcessDefinitionKey(processDefinitionKey)
+                        .setBusinessKey(String.valueOf(id)));
+
+        // 3. 鏇存柊浠樻鍗曠姸鎬佷负瀹℃壒涓�
+        financePaymentMapper.updateById(new ErpFinancePaymentDO()
+                .setId(id)
+                .setStatus(ErpAuditStatus.APPROVING.getStatus())
+                .setProcessInstanceId(processInstanceId));
+    }
+
+    @Override
+    public List<Map<String, Object>> getFinancePaymentApproveProcessDefinitionList() {
+        // 1. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+        validateFinancePaymentApproveCategoryAndProcessDefinition();
+
+        // 2. 鑾峰彇鍒嗙被涓嬬殑娴佺▼瀹氫箟淇℃伅
+        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
+                .getProcessDefinitionInfoListByCategory(BPM_PROCESS_DEFINITION_CATEGORY_CODE);
+        if (CollUtil.isEmpty(definitionInfoList)) {
+            return Collections.emptyList();
         }
+
+        // 3. 閬嶅巻鑾峰彇娴佺▼瀹氫箟璇︽儏锛岃繃婊ゆ縺娲荤姸鎬侊紝淇濈暀鏈�鏂扮増鏈�
+        Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
+        for (BpmProcessDefinitionInfoDO info : definitionInfoList) {
+            ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId());
+            if (pd == null || pd.isSuspended()) {
+                continue;
+            }
+            ProcessDefinition existing = latestVersionMap.get(pd.getKey());
+            if (existing == null || pd.getVersion() > existing.getVersion()) {
+                latestVersionMap.put(pd.getKey(), pd);
+            }
+        }
+
+        // 4. 杩斿洖娴佺▼瀹氫箟鍒楄〃
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (ProcessDefinition pd : latestVersionMap.values()) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("id", pd.getId());
+            item.put("key", pd.getKey());
+            item.put("name", pd.getName());
+            result.add(item);
+        }
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateFinancePaymentAuditStatus(Long id, Integer bpmResult) {
+        // 1. 鏍¢獙瀛樺湪
+        ErpFinancePaymentDO payment = validateFinancePaymentExists(id);
+        if (!ErpAuditStatus.APPROVING.getStatus().equals(payment.getStatus())) {
+            log.warn("[updateFinancePaymentAuditStatus] 浠樻鍗�({}) 涓嶅浜庡鎵逛腑鐘舵��", id);
+            return;
+        }
+
+        // 2. 鏍规嵁瀹℃壒缁撴灉鏇存柊鐘舵��
+        Integer newStatus = convertBpmResultToAuditStatus(bpmResult);
+        if (newStatus != null) {
+            financePaymentMapper.updateById(new ErpFinancePaymentDO()
+                    .setId(id).setStatus(newStatus));
+        }
+    }
+
+    /**
+     * 鏍¢獙浠樻鍗曞鎵瑰垎绫诲拰娴佺▼瀹氫箟鏄惁瀛樺湪
+     */
+    private void validateFinancePaymentApproveCategoryAndProcessDefinition() {
+        // 1. 鏍¢獙鍒嗙被鏄惁瀛樺湪
+        List<BpmCategoryDO> categories = bpmCategoryService.getCategoryListByCode(
+                Collections.singletonList(BPM_PROCESS_DEFINITION_CATEGORY_CODE));
+        if (CollUtil.isEmpty(categories)) {
+            throw exception(FINANCE_PAYMENT_BPM_CATEGORY_NOT_EXISTS);
+        }
+    }
+
+    /**
+     * 杞崲 BPM 瀹℃壒缁撴灉涓哄鏍哥姸鎬�
+     */
+    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
+        if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
+            return ErpAuditStatus.APPROVE.getStatus();
+        } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
+            return ErpAuditStatus.PROCESS.getStatus();
+        } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
+            return ErpAuditStatus.CANCEL.getStatus();
+        }
+        return null;
     }
 
     private List<ErpFinancePaymentItemDO> validateFinancePaymentItems(
@@ -219,6 +325,9 @@
             if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) {
                 throw exception(FINANCE_PAYMENT_DELETE_FAIL_APPROVE, payment.getNo());
             }
+            if (ErpAuditStatus.APPROVING.getStatus().equals(payment.getStatus())) {
+                throw exception(FINANCE_PAYMENT_DELETE_FAIL_APPROVING, payment.getNo());
+            }
         });
 
         // 2. 閬嶅巻鍒犻櫎锛屽苟璁板綍鎿嶄綔鏃ュ織
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/listener/ErpFinancePaymentStatusListener.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/listener/ErpFinancePaymentStatusListener.java
new file mode 100644
index 0000000..9a168e3
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/listener/ErpFinancePaymentStatusListener.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.erp.service.finance.listener;
+
+import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
+import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
+import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 浠樻鍗曞鎵圭姸鎬佺洃鍚櫒
+ *
+ * 鐩戝惉 BPM 娴佺▼瀹炰緥鐘舵�佸彉鏇翠簨浠讹紝鏇存柊浠樻鍗曠殑瀹℃壒鐘舵��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Component
+@Slf4j
+public class ErpFinancePaymentStatusListener extends BpmProcessInstanceStatusEventListener {
+
+    /**
+     * BPM 浠樻鍗曞鎵规祦绋嬪畾涔� key
+     */
+    private static final String BPM_PROCESS_DEFINITION_KEY = "erp-finance-payment-audit";
+
+    @Resource
+    private ErpFinancePaymentService financePaymentService;
+
+    @Override
+    protected String getProcessDefinitionKey() {
+        return BPM_PROCESS_DEFINITION_KEY;
+    }
+
+    @Override
+    protected void onEvent(BpmProcessInstanceStatusEvent event) {
+        Long paymentId;
+        try {
+            paymentId = Long.parseLong(event.getBusinessKey());
+        } catch (NumberFormatException e) {
+            log.warn("[onEvent] businessKey({}) 涓嶆槸鏈夋晥鐨勪粯娆惧崟缂栧彿", event.getBusinessKey());
+            return;
+        }
+        log.info("[onEvent] 鏀跺埌BPM瀹℃壒浜嬩欢锛宲aymentId={}, status={}", paymentId, event.getStatus());
+        financePaymentService.updateFinancePaymentAuditStatus(paymentId, event.getStatus());
+    }
+
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java
index 8f8b01b..9e0650a 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java
@@ -42,20 +42,12 @@
     void deletePurchaseInvoice(Long id);
 
     /**
-     * 鎻愪氦鏉ョエ瀹℃壒
+     * 鏇存柊鏉ョエ鐨勭姸鎬侊紙瀹℃牳/鍙嶅鏍革級
      *
      * @param id 缂栧彿
-     * @param userId 鐢ㄦ埛缂栧彿
+     * @param status 鐘舵��
      */
-    void submitPurchaseInvoice(Long id, Long userId);
-
-    /**
-     * 鏇存柊鏉ョエ瀹℃壒缁撴灉锛圔PM 鍥炶皟锛�
-     *
-     * @param id 缂栧彿
-     * @param bpmResult BPM 瀹℃壒缁撴灉
-     */
-    void updatePurchaseInvoiceAuditStatus(Long id, Integer bpmResult);
+    void updatePurchaseInvoiceStatus(Long id, Integer status);
 
     /**
      * 鑾峰緱鏉ョエ
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java
index b9a9a1c..19c6e7e 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java
@@ -5,9 +5,6 @@
 import cn.hutool.core.util.ObjectUtil;
 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.invoice.ErpPurchaseInvoicePageReqVO;
 import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceSaveReqVO;
 import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO;
@@ -17,7 +14,6 @@
 import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum;
 import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
 import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -37,13 +33,7 @@
  */
 @Service
 @Validated
-@Slf4j
 public class ErpPurchaseInvoiceServiceImpl implements ErpPurchaseInvoiceService {
-
-    /**
-     * BPM 鏉ョエ瀹℃壒娴佺▼鏍囪瘑
-     */
-    public static final String BPM_PROCESS_DEFINITION_KEY = "erp-purchase-invoice-audit";
 
     /**
      * 鏉ョエ闄勪欢鐨勪笟鍔¤褰曠被鍨嬶紝瀵瑰簲 {@link cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum#ERP_PURCHASE_INVOICE}
@@ -60,8 +50,6 @@
     private ErpPurchaseOrderService purchaseOrderService;
 
     @Resource
-    private BpmProcessInstanceApi bpmProcessInstanceApi;
-    @Resource
     private StorageAttachmentApi storageAttachmentApi;
 
     @Override
@@ -77,9 +65,9 @@
             throw exception(PURCHASE_INVOICE_NO_EXISTS);
         }
 
-        // 2.1 鎻掑叆鏉ョエ
+        // 2.1 鎻掑叆鏉ョエ锛堝垱寤哄悗鍗充负鏈鏍哥姸鎬侊級
         ErpPurchaseInvoiceDO invoice = BeanUtils.toBean(createReqVO, ErpPurchaseInvoiceDO.class)
-                .setNo(no).setAuditStatus(ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus());
+                .setNo(no).setAuditStatus(ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus());
         invoiceMapper.insert(invoice);
 
         // 2.2 缁戝畾鍙戠エ闄勪欢
@@ -123,8 +111,8 @@
         Assert.notNull(updateReqVO.getId(), "鏉ョエ缂栧彿涓嶈兘涓虹┖");
         // 1.1 鏍¢獙瀛樺湪
         ErpPurchaseInvoiceDO oldInvoice = validatePurchaseInvoiceExists(updateReqVO.getId());
-        // 1.2 鍙湁鑽夌鍙互缂栬緫
-        if (ObjectUtil.notEqual(oldInvoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus())) {
+        // 1.2 鍙湁鏈鏍稿彲浠ョ紪杈�
+        if (ObjectUtil.notEqual(oldInvoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(PURCHASE_INVOICE_UPDATE_FAIL_EDITING_PROHIBITED, oldInvoice.getNo());
         }
         // 1.3 鏍¢獙鍏宠仈鏁版嵁 + 鍙紑绁ㄩ噾棰�
@@ -157,51 +145,21 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void submitPurchaseInvoice(Long id, Long userId) {
-        // 1. 鏍¢獙鏉ョエ鏄惁鍦ㄥ鎵�
+    public void updatePurchaseInvoiceStatus(Long id, Integer status) {
+        boolean approve = ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus().equals(status);
+        // 1.1 鏍¢獙瀛樺湪
         ErpPurchaseInvoiceDO invoice = validatePurchaseInvoiceExists(id);
-        if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus())) {
-            throw exception(PURCHASE_INVOICE_SUBMIT_FAIL_NOT_DRAFT);
+        // 1.2 鏍¢獙鐘舵��
+        if (invoice.getAuditStatus().equals(status)) {
+            throw exception(approve ? PURCHASE_INVOICE_APPROVE_FAIL : PURCHASE_INVOICE_PROCESS_FAIL);
         }
 
-        // 1.1 閲嶆柊鏍¢獙鍙紑绁ㄩ噾棰濓紝闃叉绱鏉ョエ瓒呰繃閲囪喘璁㈠崟閲戦锛堣崏绋挎湡鍚庡彲鑳芥湁鍏朵粬鍗曡繘鍏ュ鎵逛腑锛�
-        validatePurchaseInvoicePriceExceedsLimit(new ErpPurchaseInvoiceSaveReqVO()
-                .setId(invoice.getId()).setPurchaseOrderId(invoice.getPurchaseOrderId())
-                .setSupplierId(invoice.getSupplierId()).setPrice(invoice.getPrice()));
-
-        // 2. 鍒涘缓鏉ョエ瀹℃壒娴佺▼瀹炰緥
-        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
-                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
-
-        // 3. 鏇存柊鏉ョエ宸ヤ綔娴佺紪鍙�
-        invoiceMapper.updateById(new ErpPurchaseInvoiceDO().setId(id).setProcessInstanceId(processInstanceId)
-                .setAuditStatus(ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus()));
-    }
-
-    @Override
-    public void updatePurchaseInvoiceAuditStatus(Long id, Integer bpmResult) {
-        // 1. 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
-        ErpPurchaseInvoiceDO invoice = validatePurchaseInvoiceExists(id);
-        if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus())) {
-            log.warn("[updatePurchaseInvoiceAuditStatus][鏉ョエ({}) 涓嶅浜庡鎵逛腑锛屾棤娉曟洿鏂板鎵圭粨鏋�({})]", id, bpmResult);
-            return;
+        // 2. 鏇存柊鐘舵��
+        int updateCount = invoiceMapper.updateByIdAndStatus(id, invoice.getAuditStatus(),
+                new ErpPurchaseInvoiceDO().setAuditStatus(status));
+        if (updateCount == 0) {
+            throw exception(approve ? PURCHASE_INVOICE_APPROVE_FAIL : PURCHASE_INVOICE_PROCESS_FAIL);
         }
-        // 2. 鏍规嵁瀹℃壒缁撴灉鏇存柊鐘舵��
-        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
-        if (auditStatus != null) {
-            invoiceMapper.updateById(new ErpPurchaseInvoiceDO().setId(id).setAuditStatus(auditStatus));
-        }
-    }
-
-    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
-        if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
-            return ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus();
-        } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
-            return ErpPurchaseInvoiceAuditStatusEnum.REJECT.getStatus();
-        } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
-            return ErpPurchaseInvoiceAuditStatusEnum.CANCEL.getStatus();
-        }
-        return null;
     }
 
     private ErpPurchaseInvoiceDO validatePurchaseInvoiceExists(Long id) {
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInvoiceStatusListener.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInvoiceStatusListener.java
deleted file mode 100644
index e93b790..0000000
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInvoiceStatusListener.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.module.erp.service.purchase.listener;
-
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
-import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService;
-import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceServiceImpl;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-/**
- * 閲囪喘鏉ョエ瀹℃壒鐘舵�佺洃鍚櫒
- *
- * 鐩戝惉 BPM 娴佺▼瀹炰緥鐘舵�佸彉鏇翠簨浠讹紝鏇存柊鏉ョエ鐨勫鎵圭姸鎬�
- *
- * @author 瓒呯骇绠$悊鍛�
- */
-@Component
-@Slf4j
-public class ErpPurchaseInvoiceStatusListener extends BpmProcessInstanceStatusEventListener {
-
-    @Resource
-    private ErpPurchaseInvoiceService invoiceService;
-
-    @Override
-    protected String getProcessDefinitionKey() {
-        return ErpPurchaseInvoiceServiceImpl.BPM_PROCESS_DEFINITION_KEY;
-    }
-
-    @Override
-    protected void onEvent(BpmProcessInstanceStatusEvent event) {
-        Long invoiceId;
-        try {
-            invoiceId = Long.parseLong(event.getBusinessKey());
-        } catch (NumberFormatException e) {
-            log.warn("[onEvent] businessKey({}) 涓嶆槸鏈夋晥鐨勬潵绁ㄧ紪鍙�", event.getBusinessKey());
-            return;
-        }
-        log.info("[onEvent] 鏀跺埌BPM瀹℃壒浜嬩欢锛宨nvoiceId={}, status={}", invoiceId, event.getStatus());
-        invoiceService.updatePurchaseInvoiceAuditStatus(invoiceId, event.getStatus());
-    }
-
-}

--
Gitblit v1.9.3