瀏覽代碼

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

YunaiV 1 年之前
父節點
當前提交
a531ba2902

+ 1 - 0
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 {
     ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态");
     ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试");
     ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态");
+    ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "创建回款失败,原因:回款金额超出合同金额,目前剩余可退:{} 元");
 
     // ========== 回款计划 1-020-005-000 ==========
     ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");

+ 8 - 1
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> {
         return selectCount(query);
     }
 
+    default List<CrmReceivableDO> selectListByContractIdAndStatus(Long contractId, Collection<Integer> auditStatuses) {
+        return selectList(new LambdaQueryWrapperX<CrmReceivableDO>()
+                .eq(CrmReceivableDO::getContractId, contractId)
+                .in(CrmReceivableDO::getAuditStatus, auditStatuses));
+    }
+
     default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) {
         if (CollUtil.isEmpty(contractIds)) {
             return Collections.emptyMap();
@@ -85,7 +91,8 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
         // SQL sum 查询
         List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()
                 .select("contract_id, SUM(price) AS total_price")
-                .eq("audit_status", CrmAuditStatusEnum.APPROVE.getStatus())
+                .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // 草稿 + 审批中 + 审批通过
+                        CrmAuditStatusEnum.PROCESS, CrmAuditStatusEnum.APPROVE.getStatus())
                 .groupBy("contract_id")
                 .in("contract_id", contractIds));
         // 获得金额

+ 29 - 5
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;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
@@ -36,6 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -85,18 +87,21 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
             success = CRM_RECEIVABLE_CREATE_SUCCESS)
     public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
-        // 1.1 校验关联数据存在
+        // 1.1 校验可回款金额超过上限
+        validateReceivablePriceExceedsLimit(createReqVO);
+        // 1.2 校验关联数据存在
         validateRelationDataExists(createReqVO);
-        // 1.2 生成回款编号
+        // 1.3 生成回款编号
         String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
         if (receivableMapper.selectByNo(no) != null) {
             throw exception(RECEIVABLE_NO_EXISTS);
         }
 
-        // 2. 插入回款
+        // 2.1 插入回款
         CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
                 .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
         receivableMapper.insert(receivable);
+        // 2.2
 
         // 3. 创建数据权限
         permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
@@ -113,6 +118,22 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
         return receivable.getId();
     }
 
+    private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) {
+        // 1. 计算剩余可退款金额,不包括 reqVO 自身
+        CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
+        List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
+                Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
+        if (reqVO.getId() != null) {
+            receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId()));
+        }
+        BigDecimal notReceivablePrice = contract.getTotalPrice().subtract(
+                CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
+        // 2. 校验金额是否超过
+        if (reqVO.getPrice().compareTo(notReceivablePrice) > 0) {
+            throw exception(RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notReceivablePrice);
+        }
+    }
+
     private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
         if (reqVO.getOwnerUserId() != null) {
             adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
@@ -144,9 +165,11 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
         Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
         updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
-        // 1.1 校验存在
+        // 1.1 校验可回款金额超过上限
+        validateReceivablePriceExceedsLimit(updateReqVO);
+        // 1.2 校验存在
         CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
-        // 1.2 只有草稿、审批中,可以编辑;
+        // 1.3 只有草稿、审批中,可以编辑;
         if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
                 CrmAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
@@ -189,6 +212,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
         if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
             throw exception(RECEIVABLE_DELETE_FAIL);
         }
+        // TODO @芋艿:审批通过时,不允许删除;
 
         // 2. 删除
         receivableMapper.deleteById(id);