Преглед на файлове

Merge remote-tracking branch 'yudao/develop' into develop

puhui999 преди 1 година
родител
ревизия
c4ff0a1038
променени са 12 файла, в които са добавени 76 реда и са изтрити 51 реда
  1. 1 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  2. 7 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
  3. 7 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
  4. 11 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
  5. 11 18
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
  6. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java
  7. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
  8. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
  9. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
  10. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
  11. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
  12. 31 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

+ 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, "回款计划不存在");

+ 7 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java

@@ -164,13 +164,6 @@ public class CrmReceivableController {
         });
     }
 
-    @GetMapping("/check-receivables-count")
-    @Operation(summary = "获得待审核回款数量")
-    @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
-    public CommonResult<Long> getCheckReceivablesCount() {
-        return success(receivableService.getCheckReceivablesCount(getLoginUserId()));
-    }
-
     @PutMapping("/submit")
     @Operation(summary = "提交回款审批")
     @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
@@ -179,4 +172,11 @@ public class CrmReceivableController {
         return success(true);
     }
 
+    @GetMapping("/audit-count")
+    @Operation(summary = "获得待审核回款数量")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
+    public CommonResult<Long> getAuditReceivableCount() {
+        return success(receivableService.getAuditReceivableCount(getLoginUserId()));
+    }
+
 }

+ 7 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java

@@ -163,13 +163,6 @@ public class CrmReceivablePlanController {
         });
     }
 
-    @GetMapping("/remind-receivable-plan-count")
-    @Operation(summary = "获得待回款提醒数量")
-    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
-    public CommonResult<Long> getRemindReceivablesCount() {
-        return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId()));
-    }
-
     @GetMapping("/simple-list")
     @Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项")
     @Parameters({
@@ -187,4 +180,11 @@ public class CrmReceivablePlanController {
                 .setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));
     }
 
+    @GetMapping("/remind-count")
+    @Operation(summary = "获得待回款提醒数量")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
+    public CommonResult<Long> getReceivablePlanRemindCount() {
+        return success(receivablePlanService.getReceivablePlanRemindCount(getLoginUserId()));
+    }
+
 }

+ 11 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java

@@ -68,16 +68,22 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
         return selectJoinList(CrmReceivableDO.class, query);
     }
 
-    default Long selectCheckReceivablesCount(Long userId) {
+    default Long selectCountByAudit(Long userId) {
         MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
         // 我负责的 + 非公海
         CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
                 CrmReceivableDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
-        // 未提交 or 审核不通过
-        query.in(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.REJECT.getStatus());
+        // 未审核
+        query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());
         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));
         // 获得金额

+ 11 - 18
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java

@@ -60,19 +60,16 @@ public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO
         }
 
         // Backlog: 回款提醒类型
-        // TODO: @dhb52 需要配置 提前提醒天数
-        int REMIND_DAYS = 20;
         LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
-        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
         if (CrmReceivablePlanPageReqVO.REMIND_TYPE_NEEDED.equals(pageReqVO.getRemindType())) { // 待回款
-            query.isNull(CrmReceivablePlanDO::getReceivableId)
-                    .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
+            query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
+                    .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday) // 已逾期
+                    .lt(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒
         } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_EXPIRED.equals(pageReqVO.getRemindType())) {  // 已逾期
-            query.isNull(CrmReceivablePlanDO::getReceivableId)
-                    .lt(CrmReceivablePlanDO::getReturnTime, endOfToday);
+            query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
+                    .ge(CrmReceivablePlanDO::getReturnTime, beginOfToday); // 已逾期
         } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款
-            query.isNotNull(CrmReceivablePlanDO::getReceivableId)
-                    .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
+            query.isNotNull(CrmReceivablePlanDO::getReceivableId);
         }
         return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);
     }
@@ -86,20 +83,16 @@ public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO
         return selectJoinList(CrmReceivablePlanDO.class, query);
     }
 
-    default Long selectRemindReceivablePlanCount(Long userId) {
+    default Long selectReceivablePlanCountByRemind(Long userId) {
         MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
         // 我负责的 + 非公海
         CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
                 CrmReceivablePlanDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
-        // TODO: @dhb52 需要配置 提前提醒天数
-        int REMIND_DAYS = 20;
+        // 未回款 + 已逾期 + 今天开始提醒
         LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
-        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
-        query.isNull(CrmReceivablePlanDO::getReceivableId)
-                .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
-        // TODO return_time 小于现在;
-        // TODO 未回款
-        // TODO remind_time 大于现在;
+        query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
+                .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday) // 已逾期
+                .lt(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒
         return selectCount(query);
     }
 

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java

@@ -1,4 +1,4 @@
 /**
- * TODO 芋艿:临时占位,后续可删除
+ * 定时任务
  */
 package cn.iocoder.yudao.module.crm.job;

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java

@@ -222,7 +222,7 @@ public class CrmContractServiceImpl implements CrmContractService {
             success = CRM_CONTRACT_DELETE_SUCCESS)
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
     public void deleteContract(Long id) {
-        // TODO @合同待定:如果被 CrmReceivableDO 所使用,则不允许删除
+        // TODO @puhui999:如果被 CrmReceivableDO 所使用,则不允许删除
         // 校验存在
         CrmContractDO contract = validateContractExists(id);
         // 删除

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java

@@ -170,7 +170,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
             throw exception(CRM_PERMISSION_DELETE_FAIL);
         }
         // 校验操作人是否为负责人
-        CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.getFirst().getBizId(), userId);
+        CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.get(0).getBizId(), userId);
         if (permission == null) {
             throw exception(CRM_PERMISSION_DELETE_DENIED);
         }

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java

@@ -90,6 +90,6 @@ public interface CrmReceivablePlanService {
      * @param userId 用户编号
      * @return 提醒数量
      */
-    Long getRemindReceivablePlanCount(Long userId);
+    Long getReceivablePlanRemindCount(Long userId);
 
 }

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java

@@ -184,8 +184,8 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
     }
 
     @Override
-    public Long getRemindReceivablePlanCount(Long userId) {
-        return receivablePlanMapper.selectRemindReceivablePlanCount(userId);
+    public Long getReceivablePlanRemindCount(Long userId) {
+        return receivablePlanMapper.selectReceivablePlanCountByRemind(userId);
     }
 
 }

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java

@@ -110,9 +110,9 @@ public interface CrmReceivableService {
      * 获得待审核回款数量
      *
      * @param userId 用户编号
-     * @return 提醒数量
+     * @return 待审批数量
      */
-    Long getCheckReceivablesCount(Long userId);
+    Long getAuditReceivableCount(Long userId);
 
     /**
      * 获得合同已回款金额 Map

+ 31 - 7
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 @puhui999:审批通过时,不允许删除;
 
         // 2. 删除
         receivableMapper.deleteById(id);
@@ -256,8 +280,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     }
 
     @Override
-    public Long getCheckReceivablesCount(Long userId) {
-        return receivableMapper.selectCheckReceivablesCount(userId);
+    public Long getAuditReceivableCount(Long userId) {
+        return receivableMapper.selectCountByAudit(userId);
     }
 
     @Override