2026-08-10 035e28134ff1ace5d7117007dd72cd18c68f14ff
refactor(crm): 重构回款审批流程为状态更新模式

- 移除 BPM 审批流程相关代码和监听器
- 将 submitContract 方法重命名为 updateReceivableStatus 并修改参数
- 添加 status 参数用于直接更新回款状态
- 修改审计状态逻辑从 BPM 审批结果转换为直接状态设置
- 更新错误码常量定义
- 调整回款创建后的初始状态设置
- 优化状态更新的安全性检查机制
已修改15个文件
已添加1个文件
已删除2个文件
525 ■■■■ 文件已修改
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInvoiceMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/finance/listener/ErpFinancePaymentStatusListener.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceService.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInvoiceServiceImpl.java 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInvoiceStatusListener.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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);
    }
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()) // å¿…须传递
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, "回款计划不存在");
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);
    /**
     * èŽ·å¾—å›žæ¬¾
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) {
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableStatusListener.java
ÎļþÒÑɾ³ý
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)
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;
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);
    }
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;
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())
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, "收款单不存在");
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);
    /**
     * åˆ é™¤ä»˜æ¬¾å•
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. éåŽ†åˆ é™¤ï¼Œå¹¶è®°å½•æ“ä½œæ—¥å¿—
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审批事件,paymentId={}, status={}", paymentId, event.getStatus());
        financePaymentService.updateFinancePaymentAuditStatus(paymentId, event.getStatus());
    }
}
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);
    /**
     * æ›´æ–°æ¥ç¥¨å®¡æ‰¹ç»“果(BPM å›žè°ƒï¼‰
     *
     * @param id ç¼–号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updatePurchaseInvoiceAuditStatus(Long id, Integer bpmResult);
    void updatePurchaseInvoiceStatus(Long id, Integer status);
    /**
     * èŽ·å¾—æ¥ç¥¨
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()));
        // 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);
    }
    @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. æ ¹æ®å®¡æ‰¹ç»“果更新状态
        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) {
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInvoiceStatusListener.java
ÎļþÒÑɾ³ý