Commit a531ba29022e5f219eff75aaa74c2c1e758d1757

Authored by YunaiV
1 parent 5fbb4236

✨ CRM:增加回款超过合同的退款金额校验

yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
... ... @@ -39,6 +39,7 @@ public interface ErrorCodeConstants {
39 39 ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态");
40 40 ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试");
41 41 ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态");
  42 + ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "创建回款失败,原因:回款金额超出合同金额,目前剩余可退:{} 元");
42 43  
43 44 // ========== 回款计划 1-020-005-000 ==========
44 45 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
... ... @@ -78,6 +78,12 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
78 78 return selectCount(query);
79 79 }
80 80  
  81 + default List<CrmReceivableDO> selectListByContractIdAndStatus(Long contractId, Collection<Integer> auditStatuses) {
  82 + return selectList(new LambdaQueryWrapperX<CrmReceivableDO>()
  83 + .eq(CrmReceivableDO::getContractId, contractId)
  84 + .in(CrmReceivableDO::getAuditStatus, auditStatuses));
  85 + }
  86 +
81 87 default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) {
82 88 if (CollUtil.isEmpty(contractIds)) {
83 89 return Collections.emptyMap();
... ... @@ -85,7 +91,8 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
85 91 // SQL sum 查询
86 92 List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()
87 93 .select("contract_id, SUM(price) AS total_price")
88   - .eq("audit_status", CrmAuditStatusEnum.APPROVE.getStatus())
  94 + .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过
  95 + CrmAuditStatusEnum.PROCESS, CrmAuditStatusEnum.APPROVE.getStatus())
89 96 .groupBy("contract_id")
90 97 .in("contract_id", contractIds));
91 98 // 获得金额
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
... ... @@ -6,6 +6,7 @@ import cn.hutool.core.lang.Assert;
6 6 import cn.hutool.core.util.ObjUtil;
7 7 import cn.hutool.core.util.ObjectUtil;
8 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
... ... @@ -36,6 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
36 37 import org.springframework.validation.annotation.Validated;
37 38  
38 39 import java.math.BigDecimal;
  40 +import java.util.Arrays;
39 41 import java.util.Collection;
40 42 import java.util.List;
41 43 import java.util.Map;
... ... @@ -85,18 +87,21 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
85 87 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
86 88 success = CRM_RECEIVABLE_CREATE_SUCCESS)
87 89 public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
88   - // 1.1 校验关联数据存在
  90 + // 1.1 校验可回款金额超过上限
  91 + validateReceivablePriceExceedsLimit(createReqVO);
  92 + // 1.2 校验关联数据存在
89 93 validateRelationDataExists(createReqVO);
90   - // 1.2 生成回款编号
  94 + // 1.3 生成回款编号
91 95 String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
92 96 if (receivableMapper.selectByNo(no) != null) {
93 97 throw exception(RECEIVABLE_NO_EXISTS);
94 98 }
95 99  
96   - // 2. 插入回款
  100 + // 2.1 插入回款
97 101 CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
98 102 .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
99 103 receivableMapper.insert(receivable);
  104 + // 2.2
100 105  
101 106 // 3. 创建数据权限
102 107 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
... ... @@ -113,6 +118,22 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
113 118 return receivable.getId();
114 119 }
115 120  
  121 + private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) {
  122 + // 1. 计算剩余可退款金额,不包括 reqVO 自身
  123 + CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
  124 + List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
  125 + Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
  126 + if (reqVO.getId() != null) {
  127 + receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));
  128 + }
  129 + BigDecimal notReceivablePrice = contract.getTotalPrice().subtract(
  130 + CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
  131 + // 2. 校验金额是否超过
  132 + if (reqVO.getPrice().compareTo(notReceivablePrice) > 0) {
  133 + throw exception(RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notReceivablePrice);
  134 + }
  135 + }
  136 +
116 137 private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
117 138 if (reqVO.getOwnerUserId() != null) {
118 139 adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
... ... @@ -144,9 +165,11 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
144 165 public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
145 166 Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
146 167 updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
147   - // 1.1 校验存在
  168 + // 1.1 校验可回款金额超过上限
  169 + validateReceivablePriceExceedsLimit(updateReqVO);
  170 + // 1.2 校验存在
148 171 CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
149   - // 1.2 只有草稿、审批中,可以编辑;
  172 + // 1.3 只有草稿、审批中,可以编辑;
150 173 if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
151 174 CrmAuditStatusEnum.PROCESS.getStatus())) {
152 175 throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
... ... @@ -189,6 +212,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
189 212 if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
190 213 throw exception(RECEIVABLE_DELETE_FAIL);
191 214 }
  215 + // TODO @芋艿:审批通过时,不允许删除;
192 216  
193 217 // 2. 删除
194 218 receivableMapper.deleteById(id);
... ...