| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi; |
| | |
| | | import cn.iocoder.yudao.module.finance.enums.FinanceReimbursementStatusEnum; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.flowable.engine.repository.ProcessDefinition; |
| | |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @Resource |
| | | private ArchiveDocumentApi archiveDocumentApi; |
| | | |
| | | @Resource |
| | |
| | | // 1. 报销单号:允许用户自定义,未填时由系统生成 |
| | | String no = resolveCreateNo(createReqVO.getNo()); |
| | | |
| | | // 2.1 插入报销单 |
| | | // 2.1 申请人:支持代他人发起;未指定时默认当前登录用户(代录人由 creator 审计字段记录) |
| | | Long applicantId = createReqVO.getApplicantId() != null |
| | | ? createReqVO.getApplicantId() : getLoginUserId(); |
| | | validateApplicant(applicantId); |
| | | |
| | | // 2.2 插入报销单 |
| | | FinanceReimbursementDO reimbursement = BeanUtils.toBean(createReqVO, FinanceReimbursementDO.class, in -> in |
| | | .setNo(no) |
| | | .setStatus(FinanceReimbursementStatusEnum.DRAFT.getStatus()) |
| | | .setApplicantId(getLoginUserId()) |
| | | .setApplicantId(applicantId) |
| | | .setApplyDate(createReqVO.getApplyDate() != null ? createReqVO.getApplyDate() : LocalDate.now())); |
| | | reimbursementMapper.insert(reimbursement); |
| | | |
| | | // 2.2 插入报销明细 |
| | | // 2.3 插入报销明细 |
| | | List<FinanceReimbursementItemDO> items = buildItems(reimbursement.getId(), createReqVO.getItems()); |
| | | if (CollUtil.isNotEmpty(items)) { |
| | | reimbursementItemMapper.insertBatch(items); |
| | | } |
| | | |
| | | // 2.3 绑定发票附件 |
| | | // 2.4 绑定发票附件 |
| | | storageAttachmentApi.bindAttachments("file", REIMBURSEMENT_RECORD_TYPE, |
| | | reimbursement.getId(), createReqVO.getBlobIds()); |
| | | |
| | |
| | | |
| | | // 2.1 更新报销单 |
| | | FinanceReimbursementDO updateObj = BeanUtils.toBean(updateReqVO, FinanceReimbursementDO.class); |
| | | // 申请人:代他人发起时允许修改实际申请人;不传则保持原值 |
| | | if (updateReqVO.getApplicantId() != null) { |
| | | validateApplicant(updateReqVO.getApplicantId()); |
| | | updateObj.setApplicantId(updateReqVO.getApplicantId()); |
| | | } |
| | | // 报销单号:未填表示不变更,填了则校验后覆盖 |
| | | String customNo = StrUtil.trimToNull(updateReqVO.getNo()); |
| | | if (customNo == null || customNo.equals(reimbursement.getNo())) { |
| | |
| | | throw exception(REIMBURSEMENT_SUBMIT_FAIL_ITEM_EMPTY); |
| | | } |
| | | |
| | | // 2. 创建 BPM 流程实例 |
| | | // 2. 创建 BPM 流程实例,把实际申请人与代录人写入流程变量,供审批流展示与路由使用 |
| | | Map<String, Object> variables = buildProcessVariables(reimbursement, userId); |
| | | String processInstanceId = processInstanceApi.createProcessInstance(userId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(processDefinitionKey) |
| | | .setVariables(variables) |
| | | .setBusinessKey(String.valueOf(id))); |
| | | |
| | | // 3. 更新状态为审批中 |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验实际申请人存在且为启用状态 |
| | | */ |
| | | private void validateApplicant(Long applicantId) { |
| | | AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null; |
| | | if (applicant == null || !CommonStatusEnum.ENABLE.getStatus().equals(applicant.getStatus())) { |
| | | throw exception(REIMBURSEMENT_APPLICANT_NOT_EXISTS, applicantId); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 构建报销审批流程变量:实际申请人 + 代录(发起)人 |
| | | * |
| | | * <p>审批流中可通过 applicantName 展示实际申请人,通过 initiatorName 展示代录人, |
| | | * 并通过 isProxy 判断是否为代他人发起 |
| | | */ |
| | | private Map<String, Object> buildProcessVariables(FinanceReimbursementDO reimbursement, Long submitUserId) { |
| | | Long applicantId = reimbursement.getApplicantId(); |
| | | AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null; |
| | | AdminUserRespDTO submitter = submitUserId != null ? adminUserApi.getUser(submitUserId) : null; |
| | | Map<String, Object> variables = new HashMap<>(8); |
| | | variables.put("applicantId", applicantId); |
| | | variables.put("applicantName", applicant != null ? applicant.getNickname() : ""); |
| | | variables.put("initiatorId", submitUserId); |
| | | variables.put("initiatorName", submitter != null ? submitter.getNickname() : ""); |
| | | variables.put("isProxy", applicantId != null && !applicantId.equals(submitUserId)); |
| | | return variables; |
| | | } |
| | | |
| | | private List<FinanceReimbursementItemDO> buildItems(Long reimbursementId, |
| | | List<FinanceReimbursementItemSaveReqVO> itemVOs) { |
| | | if (CollUtil.isEmpty(itemVOs)) { |