| | |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | 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.BpmProcessInstanceStatusEnum; |
| | | 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; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableItemDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; |
| | | import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableItemMapper; |
| | | import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; |
| | | import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO; |
| | | import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| | | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| | | import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableBizTypeEnum; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO; |
| | | import cn.iocoder.yudao.module.mes.api.productsales.MesWmProductSalesApi; |
| | | import cn.iocoder.yudao.module.mes.api.productsales.dto.MesWmProductSalesRespDTO; |
| | | import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; |
| | | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| | | import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService; |
| | |
| | | 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; |
| | |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class CrmReceivableServiceImpl implements CrmReceivableService { |
| | | |
| | | /** |
| | | * BPM 回款审批流程标识 |
| | | * |
| | | * 对应 act_re_procdef.KEY_ = crm-receivable-audit;BPM 分类「回款审批」的编码为 1111(bpm_category.id=76) |
| | | */ |
| | | public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit"; |
| | | |
| | | @Resource |
| | | private CrmReceivableMapper receivableMapper; |
| | | @Resource |
| | | private CrmReceivableItemMapper receivableItemMapper; |
| | | |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private MesWmProductSalesApi productSalesApi; |
| | | |
| | | @Resource |
| | | private CrmNoRedisDAO noRedisDAO; |
| | |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | @Resource |
| | | private BpmProcessInstanceApi bpmProcessInstanceApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | throw exception(RECEIVABLE_NO_EXISTS); |
| | | } |
| | | |
| | | // 2.1 插入回款(创建后即为未审核状态) |
| | | // 2.1 插入回款(创建后为未提交,需显式「提交审批」才进入 BPM 审批中) |
| | | CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class) |
| | | .setNo(no).setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()); |
| | | .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus()); |
| | | receivableMapper.insert(receivable); |
| | | // 2.2 |
| | | // 2.2 保存核销明细(未传时按关联开票自动生成一条) |
| | | syncReceivableItems(receivable, createReqVO.getItems()); |
| | | |
| | | // 3. 创建数据权限 |
| | | permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()) |
| | |
| | | LogRecordContext.putVariable("receivable", receivable); |
| | | LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); |
| | | return receivable.getId(); |
| | | } |
| | | |
| | | /** |
| | | * 保存核销明细 |
| | | * |
| | | * 传 null 时:若关联开票,自动生成一条「开票」核销明细;否则不生成。 |
| | | */ |
| | | private void syncReceivableItems(CrmReceivableDO receivable, |
| | | List<CrmReceivableSaveReqVO.ReceivableItem> items) { |
| | | if (items == null) { |
| | | if (receivable.getInvoiceId() == null) { |
| | | return; |
| | | } |
| | | CrmReceivableSaveReqVO.ReceivableItem autoItem = new CrmReceivableSaveReqVO.ReceivableItem(); |
| | | autoItem.setBizType(CrmReceivableBizTypeEnum.SALE_INVOICE.getType()); |
| | | autoItem.setBizId(receivable.getInvoiceId()); |
| | | autoItem.setReceiptPrice(receivable.getPrice()); |
| | | items = List.of(autoItem); |
| | | } |
| | | // 1. 校验并解析每行关联单据 |
| | | List<ResolvedReceivableItem> resolvedItems = new ArrayList<>(items.size()); |
| | | BigDecimal itemPriceSum = BigDecimal.ZERO; |
| | | for (int i = 0; i < items.size(); i++) { |
| | | CrmReceivableSaveReqVO.ReceivableItem item = items.get(i); |
| | | int row = i + 1; |
| | | CrmReceivableBizTypeEnum bizType = item == null ? null |
| | | : CrmReceivableBizTypeEnum.valueOf(item.getBizType()); |
| | | if (bizType == null) { |
| | | throw exception(RECEIVABLE_ITEM_BIZ_TYPE_INVALID, row, |
| | | item == null ? "未填写" : item.getBizType()); |
| | | } |
| | | if (item.getReceiptPrice() == null || item.getReceiptPrice().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw exception(RECEIVABLE_ITEM_PRICE_INVALID, row, bizType.getName(), |
| | | item.getBizId(), item.getReceiptPrice() == null ? "未填写" |
| | | : item.getReceiptPrice().stripTrailingZeros().toPlainString()); |
| | | } |
| | | ResolvedReceivableItem resolved = resolveReceivableItem(row, bizType, item.getBizId()); |
| | | // 1.1 已核销金额(不含本回款单,避免编辑时把自己算进去) |
| | | BigDecimal receiptedPrice = receivableItemMapper.selectListByBiz(bizType.getType(), item.getBizId()).stream() |
| | | .filter(old -> ObjectUtil.notEqual(old.getReceivableId(), receivable.getId())) |
| | | .map(old -> old.getReceiptPrice() == null ? BigDecimal.ZERO : old.getReceiptPrice()) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal remainPrice = resolved.totalPrice.subtract(receiptedPrice); |
| | | if (item.getReceiptPrice().compareTo(remainPrice) > 0) { |
| | | throw exception(RECEIVABLE_ITEM_PRICE_EXCEEDS, row, bizType.getName(), resolved.bizNo, |
| | | item.getReceiptPrice().stripTrailingZeros().toPlainString(), |
| | | remainPrice.stripTrailingZeros().toPlainString(), |
| | | resolved.totalPrice.stripTrailingZeros().toPlainString(), |
| | | receiptedPrice.stripTrailingZeros().toPlainString()); |
| | | } |
| | | resolved.receiptedPrice = receiptedPrice; |
| | | resolved.receiptPrice = item.getReceiptPrice(); |
| | | resolved.remark = item.getRemark(); |
| | | resolvedItems.add(resolved); |
| | | itemPriceSum = itemPriceSum.add(item.getReceiptPrice()); |
| | | } |
| | | // 2. 明细合计必须等于回款金额 |
| | | if (itemPriceSum.compareTo(receivable.getPrice()) != 0) { |
| | | throw exception(RECEIVABLE_ITEM_SUM_MISMATCH, |
| | | itemPriceSum.stripTrailingZeros().toPlainString(), |
| | | receivable.getPrice().stripTrailingZeros().toPlainString()); |
| | | } |
| | | // 3. 重建明细 |
| | | receivableItemMapper.deleteByReceivableId(receivable.getId()); |
| | | for (ResolvedReceivableItem resolved : resolvedItems) { |
| | | receivableItemMapper.insert(CrmReceivableItemDO.builder() |
| | | .receivableId(receivable.getId()).bizType(resolved.bizType.getType()) |
| | | .bizId(resolved.bizId).bizNo(resolved.bizNo).totalPrice(resolved.totalPrice) |
| | | .receiptedPrice(resolved.receiptedPrice).receiptPrice(resolved.receiptPrice) |
| | | .remark(resolved.remark).build()); |
| | | } |
| | | } |
| | | |
| | | private ResolvedReceivableItem resolveReceivableItem(int row, CrmReceivableBizTypeEnum bizType, Long bizId) { |
| | | ResolvedReceivableItem resolved = new ResolvedReceivableItem(); |
| | | resolved.bizType = bizType; |
| | | resolved.bizId = bizId; |
| | | switch (bizType) { |
| | | case SALE_ORDER -> { |
| | | ErpSaleOrderRespDTO saleOrder = bizId == null ? null : saleOrderApi.getSaleOrder(bizId).getCheckedData(); |
| | | if (saleOrder == null) { |
| | | throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId); |
| | | } |
| | | resolved.bizNo = saleOrder.getNo(); |
| | | resolved.totalPrice = saleOrder.getTotalPrice() == null ? BigDecimal.ZERO : saleOrder.getTotalPrice(); |
| | | } |
| | | case SALE_OUT -> { |
| | | MesWmProductSalesRespDTO saleOut = bizId == null ? null : productSalesApi.getProductSales(bizId); |
| | | if (saleOut == null) { |
| | | throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId); |
| | | } |
| | | resolved.bizNo = saleOut.getCode(); |
| | | resolved.totalPrice = saleOut.getTotalPrice() == null ? BigDecimal.ZERO : saleOut.getTotalPrice(); |
| | | } |
| | | case SALE_INVOICE -> { |
| | | CrmInvoiceDO invoice = bizId == null ? null : invoiceService.getInvoice(bizId); |
| | | if (invoice == null) { |
| | | throw exception(RECEIVABLE_ITEM_BIZ_NOT_EXISTS, row, bizType.getName(), bizId); |
| | | } |
| | | resolved.bizNo = invoice.getNo(); |
| | | resolved.totalPrice = invoice.getPrice() == null ? BigDecimal.ZERO : invoice.getPrice(); |
| | | } |
| | | default -> throw exception(RECEIVABLE_ITEM_BIZ_TYPE_INVALID, row, bizType.getType()); |
| | | } |
| | | return resolved; |
| | | } |
| | | |
| | | private static class ResolvedReceivableItem { |
| | | private CrmReceivableBizTypeEnum bizType; |
| | | private Long bizId; |
| | | private String bizNo; |
| | | private BigDecimal totalPrice; |
| | | private BigDecimal receiptedPrice; |
| | | private BigDecimal receiptPrice; |
| | | private String remark; |
| | | } |
| | | |
| | | private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) { |
| | |
| | | * 校验关联开票合格:存在、审批通过、已上传发票附件、与所选合同一致 |
| | | */ |
| | | private void validateInvoiceForReceivable(CrmReceivableSaveReqVO reqVO) { |
| | | // 未关联开票时直接跳过(允许先回款后开票) |
| | | // 回款必须先关联已审批且已录入的开票单。 |
| | | if (reqVO.getInvoiceId() == null) { |
| | | return; |
| | | throw new ServiceException(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_EXISTS.getCode(), |
| | | "回款必须先关联开票单。请先完成开票申请、审批和发票录入,再创建回款。"); |
| | | } |
| | | // 1. 校验开票存在 |
| | | CrmInvoiceDO invoice = invoiceService.getInvoice(reqVO.getInvoiceId()); |
| | |
| | | // 2. 校验开票审批通过 |
| | | if (ObjectUtil.notEqual(invoice.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_APPROVE); |
| | | } |
| | | if (!Objects.equals(invoice.getEntryStatus(), 10)) { |
| | | throw new ServiceException(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_APPROVE.getCode(), |
| | | "关联开票单尚未录入发票。请先填写发票号码、日期并上传附件,再创建回款。"); |
| | | } |
| | | // 3. 校验开票已上传发票附件 |
| | | if (CollUtil.isEmpty(storageAttachmentApi.listAttachments( |
| | |
| | | // 1.5 校验可回款金额不超过该开票剩余可回款金额 |
| | | validateReceivablePriceExceedsInvoiceRemaining(updateReqVO); |
| | | |
| | | // 1.6 只有未审核,可以编辑; |
| | | if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { |
| | | throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED); |
| | | // 1.6 仅未提交、审核不通过可以编辑;审批中等待 BPM,审核通过需先反审核 |
| | | if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) |
| | | && ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.REJECT.getStatus())) { |
| | | throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED, |
| | | StrUtil.blankToDefault(CrmAuditStatusEnum.getNameByStatus(oldReceivable.getAuditStatus()), "未知状态")); |
| | | } |
| | | |
| | | // 2. 更新回款 |
| | | CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class); |
| | | receivableMapper.updateById(updateObj); |
| | | // 2.1 更新核销明细:传了 items 以 items 为准;未传且关联开票时按开票重建;否则保持不变 |
| | | if (updateReqVO.getItems() != null |
| | | || (updateReqVO.getInvoiceId() != null && ObjectUtil.notEqual(updateReqVO.getInvoiceId(), oldReceivable.getInvoiceId()))) { |
| | | syncReceivableItems(updateObj, updateReqVO.getItems()); |
| | | } |
| | | |
| | | // 3. 记录操作日志上下文 |
| | | updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 |
| | |
| | | |
| | | @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, String remark, Long userId) { |
| | | // 1.1 校验回款存在 |
| | | CrmReceivableDO receivable = validateReceivableExists(id); |
| | | // 1.2 仅未提交、审核不通过可以提交审批 |
| | | if (ObjectUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) |
| | | && ObjectUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.REJECT.getStatus())) { |
| | | throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT, |
| | | StrUtil.blankToDefault(CrmAuditStatusEnum.getNameByStatus(receivable.getAuditStatus()), "未知状态")); |
| | | } |
| | | |
| | | // 1.3 提交前重新校验开票、合同/开票剩余可回款金额,避免草稿期间额度被其他单据占用 |
| | | CrmReceivableSaveReqVO submitReqVO = new CrmReceivableSaveReqVO() |
| | | .setId(receivable.getId()).setCustomerId(receivable.getCustomerId()) |
| | | .setContractId(receivable.getContractId()).setInvoiceId(receivable.getInvoiceId()) |
| | | .setPrice(receivable.getPrice()); |
| | | validateInvoiceForReceivable(submitReqVO); |
| | | validateReceivablePriceExceedsLimit(submitReqVO); |
| | | validateReceivablePriceExceedsInvoiceRemaining(submitReqVO); |
| | | |
| | | // 2. 全局免审批:业务校验通过后直接执行审批通过逻辑,无需流程模型 |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId("") |
| | | .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); |
| | | updateReceivableAuditStatus(id, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); |
| | | LogRecordContext.putVariable("receivableNo", receivable.getNo()); |
| | | return; |
| | | } |
| | | |
| | | // 3. 创建 BPM 回款审批流程实例 |
| | | Map<String, Object> variables = new HashMap<>(); |
| | | if (StrUtil.isNotBlank(remark)) { |
| | | variables.put("submitRemark", remark); |
| | | } |
| | | String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY) |
| | | .setBusinessKey(String.valueOf(id)) |
| | | .setVariables(variables.isEmpty() ? null : variables)); |
| | | |
| | | // 4. 更新工作流编号 + 审批状态 |
| | | receivableMapper.updateById(new CrmReceivableDO().setId(id) |
| | | .setProcessInstanceId(processInstanceId) |
| | | .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); |
| | | |
| | | // 5. 记录操作日志上下文 |
| | | LogRecordContext.putVariable("receivableNo", receivable.getNo()); |
| | | LogRecordContext.putVariable("submitRemark", remark); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateReceivableAuditStatus(Long id, Integer bpmResult) { |
| | | // 1.1 校验存在:回款可能在审批期间被删除,此时静默忽略,避免影响 BPM 流程完成 |
| | | CrmReceivableDO receivable = receivableMapper.selectById(id); |
| | | if (receivable == null) { |
| | | log.warn("[updateReceivableAuditStatus][回款({}) 不存在,忽略 BPM 审批结果({})]", id, bpmResult); |
| | | return; |
| | | } |
| | | // 1.2 只有审批中才处理:避免流程事件重复投递、或人工反审核后迟到的流程事件导致重复回写 |
| | | if (ObjectUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { |
| | | log.warn("[updateReceivableAuditStatus][回款({}) 当前状态({}) 不是审批中,忽略 BPM 审批结果({})]", |
| | | id, receivable.getAuditStatus(), bpmResult); |
| | | return; |
| | | } |
| | | |
| | | // 2. 转换 BPM 审批结果(不复用 CrmAuditStatusUtils:其 CANCEL 分支误返回 BPM 状态 4,而非 CRM 状态 40) |
| | | Integer auditStatus; |
| | | if (BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(bpmResult)) { |
| | | auditStatus = CrmAuditStatusEnum.APPROVE.getStatus(); |
| | | } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus().equals(bpmResult)) { |
| | | auditStatus = CrmAuditStatusEnum.REJECT.getStatus(); |
| | | } else if (BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(bpmResult)) { |
| | | auditStatus = CrmAuditStatusEnum.CANCEL.getStatus(); |
| | | } else { |
| | | log.warn("[updateReceivableAuditStatus][回款({}) 的 BPM 审批结果({}) 不是终态,忽略]", id, bpmResult); |
| | | return; |
| | | } |
| | | |
| | | // 3. 条件更新,确保并发/重复事件只有一次生效 |
| | | int updateCount = receivableMapper.updateByIdAndStatus(id, CrmAuditStatusEnum.PROCESS.getStatus(), |
| | | new CrmReceivableDO().setAuditStatus(auditStatus)); |
| | | if (updateCount == 0) { |
| | | log.warn("[updateReceivableAuditStatus][回款({}) 状态已被其他操作变更,忽略 BPM 审批结果({})]", id, bpmResult); |
| | | return; |
| | | } |
| | | |
| | | // 4. 仅审批通过时回写回款计划;驳回、取消不回写金额 |
| | | if (ObjectUtil.equal(auditStatus, CrmAuditStatusEnum.APPROVE.getStatus())) { |
| | | syncReceivablePlanReceipted(receivable, true); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @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 校验状态 |
| | | // 1.2 已提交 BPM 协同审批的回款,审批结果由流程监听器驱动,禁止手工审核,避免金额双重回写 |
| | | if (StrUtil.isNotBlank(receivable.getProcessInstanceId())) { |
| | | if (approve) { |
| | | throw exception(RECEIVABLE_MANUAL_AUDIT_FAIL_BPM_PROCESSING, receivable.getProcessInstanceId()); |
| | | } |
| | | // 审批中不能反审核,否则会解除流程关联,导致流程完成后无法回写;审批通过后的反审核仍保留 |
| | | if (ObjectUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { |
| | | throw exception(RECEIVABLE_MANUAL_REVERSE_FAIL_BPM_PROCESSING, receivable.getProcessInstanceId()); |
| | | } |
| | | } |
| | | // 1.3 校验状态 |
| | | if (receivable.getAuditStatus().equals(status)) { |
| | | throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL); |
| | | } |
| | | |
| | | // 2. 更新状态 |
| | | int updateCount = receivableMapper.updateByIdAndStatus(id, receivable.getAuditStatus(), |
| | | new CrmReceivableDO().setAuditStatus(status)); |
| | | // 2. 更新状态;反审核时解除 BPM 关联,回归手工审核兼容流程 |
| | | CrmReceivableDO updateObj = new CrmReceivableDO().setAuditStatus(status); |
| | | if (!approve) { |
| | | updateObj.setProcessInstanceId(""); |
| | | } |
| | | int updateCount = receivableMapper.updateByIdAndStatus(id, receivable.getAuditStatus(), updateObj); |
| | | if (updateCount == 0) { |
| | | throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL); |
| | | } |
| | | |
| | | // 3. 回款计划回写实际回款金额与状态 |
| | | syncReceivablePlanReceipted(receivable, approve); |
| | | } |
| | | |
| | | /** |
| | | * 同步回款计划的已回款金额与状态 |
| | | * |
| | | * @param receivable 回款单 |
| | | * @param approve 是否审批通过:true 回写已回款;false 重置已回款 |
| | | */ |
| | | private void syncReceivablePlanReceipted(CrmReceivableDO receivable, boolean approve) { |
| | | if (receivable.getPlanId() == null) { |
| | | return; |
| | | } |
| | | if (approve) { |
| | | receivablePlanService.updateReceivablePlanReceipted(receivable.getPlanId(), |
| | | receivable.getPrice(), receivable.getReturnTime()); |
| | | } else { |
| | | receivablePlanService.resetReceivablePlanReceipted(receivable.getPlanId()); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // 2.1 删除回款 |
| | | receivableMapper.deleteById(id); |
| | | // 2.2 删除数据权限 |
| | | // 2.2 删除核销明细 |
| | | receivableItemMapper.deleteByReceivableId(id); |
| | | // 2.3 删除数据权限 |
| | | permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id); |
| | | |
| | | // 3. 记录操作日志上下文 |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<CrmReceivableItemDO> getReceivableItemList(Long receivableId) { |
| | | return receivableItemMapper.selectListByReceivableId(receivableId); |
| | | } |
| | | |
| | | @Override |
| | | public List<CrmReceivableItemDO> getReceivableItemListByCustomerId(Long customerId) { |
| | | List<CrmReceivableDO> receivables = receivableMapper.selectListByCustomerId(customerId); |
| | | if (CollUtil.isEmpty(receivables)) { |
| | | return ListUtil.empty(); |
| | | } |
| | | return receivableItemMapper.selectListByReceivableIds( |
| | | receivables.stream().map(CrmReceivableDO::getId).toList()); |
| | | } |
| | | |
| | | @Override |
| | | @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) |
| | | public PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) { |
| | | return receivableMapper.selectPageByCustomerId(pageReqVO); |