package cn.iocoder.yudao.module.finance.api.reimbursement; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.finance.api.reimbursement.dto.FinanceReimbursementCreateReqDTO; import cn.iocoder.yudao.module.finance.api.reimbursement.dto.FinanceReimbursementRespDTO; import cn.iocoder.yudao.module.finance.api.reimbursement.dto.FinanceReimbursementUpdateReqDTO; import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.vo.FinanceReimbursementSaveReqVO; import cn.iocoder.yudao.module.finance.controller.admin.reimbursement.vo.item.FinanceReimbursementItemSaveReqVO; import cn.iocoder.yudao.module.finance.dal.dataobject.reimbursement.FinanceReimbursementDO; import cn.iocoder.yudao.module.finance.service.reimbursement.FinanceReimbursementService; import jakarta.annotation.Resource; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Collections; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*; /** * 财务报销单 API 实现类 * * 供其他模块(课题管理经费拨付)以「自动建单 + 自动提交审批」的方式落一张财务报销单, * 明细由系统固定生成 1 行(费用类型=其他),发票金额取报销总金额。 * * @author 超级管理员 */ @RestController @RequestMapping(FinanceReimbursementApi.PREFIX) @Validated public class FinanceReimbursementApiImpl implements FinanceReimbursementApi { /** 费用类型:其他(字典 finance_expense_type:1-差旅 2-办公 3-招待 4-交通 5-其他) */ private static final Integer EXPENSE_TYPE_OTHER = 5; @Resource private FinanceReimbursementService reimbursementService; @Override @Transactional(rollbackFor = Exception.class) public CommonResult createReimbursementAndSubmit(FinanceReimbursementCreateReqDTO reqDTO) { // 1. 先解析可用的报销审批流程:没有已发布流程时直接失败,避免留下无法提交的孤儿草稿单 String processDefinitionKey = resolveApproveProcessDefinitionKey(); // 2. 创建报销单(1 行明细) FinanceReimbursementSaveReqVO createReqVO = buildSaveReqVO(null, reqDTO.getTitle(), reqDTO.getTotalAmount(), reqDTO.getApplyDate(), reqDTO.getRemark(), reqDTO.getBlobIds(), reqDTO.getExpenseDescription()); Long reimbursementId = reimbursementService.createReimbursement(createReqVO); // 3. 立即提交审批 Long applicantId = reqDTO.getApplicantId() != null ? reqDTO.getApplicantId() : getLoginUserId(); reimbursementService.submitReimbursement(reimbursementId, processDefinitionKey, applicantId); return success(reimbursementId); } @Override @Transactional(rollbackFor = Exception.class) public CommonResult updateReimbursementAndResubmit(FinanceReimbursementUpdateReqDTO reqDTO) { // 1. 先解析可用的报销审批流程 String processDefinitionKey = resolveApproveProcessDefinitionKey(); // 2. 修改报销单(明细重建为 1 行) FinanceReimbursementSaveReqVO updateReqVO = buildSaveReqVO(reqDTO.getReimbursementId(), reqDTO.getTitle(), reqDTO.getTotalAmount(), reqDTO.getApplyDate(), reqDTO.getRemark(), reqDTO.getBlobIds(), reqDTO.getExpenseDescription()); reimbursementService.updateReimbursement(updateReqVO); // 3. 重新提交审批 reimbursementService.submitReimbursement(reqDTO.getReimbursementId(), processDefinitionKey, getLoginUserId()); return success(true); } @Override public CommonResult deleteReimbursement(Long reimbursementId) { reimbursementService.deleteReimbursement(reimbursementId); return success(true); } @Override public CommonResult getReimbursement(Long reimbursementId) { // 报销单不存在时返回 null,由调用方决定如何处理(课题侧据此容忍被直接删除的报销单) FinanceReimbursementDO reimbursement = reimbursementService.getReimbursement(reimbursementId); return success(BeanUtils.toBean(reimbursement, FinanceReimbursementRespDTO.class)); } // ==================== 内部方法 ==================== /** * 解析报销审批分类下可用的流程定义 key;分类下没有已发布的流程时抛出可执行的精确提示 */ private String resolveApproveProcessDefinitionKey() { List> definitionList = reimbursementService.getReimbursementApproveProcessDefinitionList(); if (CollUtil.isEmpty(definitionList)) { throw exception(REIMBURSEMENT_BPM_PROCESS_DEFINITION_NOT_EXISTS); } return String.valueOf(definitionList.get(0).get("key")); } /** 组装报销单保存 VO:明细固定 1 行,费用类型「其他」,发票金额=报销总金额 */ private FinanceReimbursementSaveReqVO buildSaveReqVO(Long id, String title, BigDecimal totalAmount, LocalDate applyDate, String remark, List blobIds, String expenseDescription) { FinanceReimbursementItemSaveReqVO item = new FinanceReimbursementItemSaveReqVO(); item.setExpenseType(EXPENSE_TYPE_OTHER); item.setInvoiceAmount(totalAmount); item.setInvoiceDate(applyDate); item.setDescription(expenseDescription); FinanceReimbursementSaveReqVO reqVO = new FinanceReimbursementSaveReqVO(); reqVO.setId(id); reqVO.setTitle(title); reqVO.setTotalAmount(totalAmount); reqVO.setApplyDate(applyDate != null ? applyDate : LocalDate.now()); reqVO.setRemark(remark); reqVO.setBlobIds(blobIds); reqVO.setItems(Collections.singletonList(item)); return reqVO; } }