Commit a531ba29022e5f219eff75aaa74c2c1e758d1757
1 parent
5fbb4236
✨ CRM:增加回款超过合同的退款金额校验
Showing
3 changed files
with
38 additions
and
6 deletions
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<CrmReceivableDO> { |
| 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); | ... | ... |