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<Long> 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<Boolean> 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<Boolean> deleteReimbursement(Long reimbursementId) {
|
reimbursementService.deleteReimbursement(reimbursementId);
|
return success(true);
|
}
|
|
@Override
|
public CommonResult<FinanceReimbursementRespDTO> getReimbursement(Long reimbursementId) {
|
// 报销单不存在时返回 null,由调用方决定如何处理(课题侧据此容忍被直接删除的报销单)
|
FinanceReimbursementDO reimbursement = reimbursementService.getReimbursement(reimbursementId);
|
return success(BeanUtils.toBean(reimbursement, FinanceReimbursementRespDTO.class));
|
}
|
|
// ==================== 内部方法 ====================
|
|
/**
|
* 解析报销审批分类下可用的流程定义 key;分类下没有已发布的流程时抛出可执行的精确提示
|
*/
|
private String resolveApproveProcessDefinitionKey() {
|
List<Map<String, Object>> 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<Long> 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;
|
}
|
|
}
|