Commit 91c179029f0b017ce7e6ae7feaaf9906207d3b97

Authored by YunaiV
1 parent ba47402a

✨ CRM:完善回款 plan 新增/修改

yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
... ... @@ -41,7 +41,7 @@ public interface ErrorCodeConstants {
41 41  
42 42 // ========== 回款计划 1-020-005-000 ==========
43 43 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
44   - ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:{}");
  44 + ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:已经有对应的还款");
45 45  
46 46 // ========== 客户管理 1_020_006_000 ==========
47 47 ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 9 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
... ... @@ -229,14 +230,16 @@ public class CrmContractController {
229 230 return success(contractService.getRemindContractCount(getLoginUserId()));
230 231 }
231 232  
232   - // TODO @芋艿:需要看下;
233   - @GetMapping("/list-all-simple-by-customer")
234   - @Operation(summary = "获得合同精简列表", description = "只包含有读权限的客户的合同,主要用于前端的下拉选项")
  233 + @GetMapping("/simple-list")
  234 + @Operation(summary = "获得合同精简列表", description = "只包含的合同,主要用于前端的下拉选项")
235 235 @Parameter(name = "customerId", description = "客户编号", required = true)
236 236 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
237   - public CommonResult<List<CrmContractRespVO>> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId) {
238   - PageResult<CrmContractDO> result = contractService.getContractPageByCustomerId(new CrmContractPageReqVO().setCustomerId(customerId));
239   - return success(BeanUtils.toBean(result.getList(), CrmContractRespVO.class));
  237 + public CommonResult<List<CrmContractRespVO>> getContractSimpleList(@RequestParam("customerId") Long customerId) {
  238 + CrmContractPageReqVO pageReqVO = new CrmContractPageReqVO().setCustomerId(customerId);
  239 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页
  240 + PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageReqVO);
  241 + return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段
  242 + .setId(contract.getId()).setName(contract.getName())));
240 243 }
241 244  
242 245 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
... ... @@ -41,8 +41,7 @@ import java.util.stream.Stream;
41 41  
42 42 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
43 43 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
44   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
45   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  44 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
46 45 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
47 46 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
48 47 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
... ... @@ -95,7 +94,14 @@ public class CrmReceivablePlanController {
95 94 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
96 95 public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) {
97 96 CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);
98   - return success(BeanUtils.toBean(receivablePlan, CrmReceivablePlanRespVO.class));
  97 + return success(buildReceivablePlanDetail(receivablePlan));
  98 + }
  99 +
  100 + private CrmReceivablePlanRespVO buildReceivablePlanDetail(CrmReceivablePlanDO receivablePlan) {
  101 + if (receivablePlan == null) {
  102 + return null;
  103 + }
  104 + return buildReceivableDetailList(Collections.singletonList(receivablePlan)).get(0);
99 105 }
100 106  
101 107 @GetMapping("/page")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java
... ... @@ -14,9 +14,8 @@ public class CrmReceivablePlanSaveReqVO {
14 14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
15 15 private Long id;
16 16  
17   - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
18   - @NotNull(message = "客户编号不能为空")
19   - private Long customerId;
  17 + @Schema(description = "客户编号", hidden = true, example = "2")
  18 + private Long customerId; // 该字段不通过前端传递,而是 contractId 查询出来设置进去
20 19  
21 20 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
22 21 @NotNull(message = "合同编号不能为空")
... ... @@ -37,10 +36,6 @@ public class CrmReceivablePlanSaveReqVO {
37 36 @NotNull(message = "计划回款金额不能为空")
38 37 private BigDecimal price;
39 38  
40   - @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
41   - @NotNull(message = "提醒日期不能为空")
42   - private LocalDateTime remindTime;
43   -
44 39 @Schema(description = "提前几天提醒", example = "1")
45 40 private Integer remindDays;
46 41  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
... ... @@ -25,6 +25,13 @@ import java.util.Objects;
25 25 @Mapper
26 26 public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> {
27 27  
  28 + default CrmReceivablePlanDO selectMaxPeriodByContractId(Long contractId) {
  29 + return selectOne(new MPJLambdaWrapperX<CrmReceivablePlanDO>()
  30 + .eq(CrmReceivablePlanDO::getContractId, contractId)
  31 + .orderByDesc(CrmReceivablePlanDO::getPeriod)
  32 + .last("LIMIT 1"));
  33 + }
  34 +
28 35 default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {
29 36 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
30 37 if (Objects.nonNull(reqVO.getContractNo())) { // 根据合同编号检索
... ... @@ -90,6 +97,9 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
90 97 LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
91 98 query.isNull(CrmReceivablePlanDO::getReceivableId)
92 99 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
  100 + // TODO return_time 小于现在;
  101 + // TODO 未还款
  102 + // TODO remind_time 大于现在;
93 103 return selectCount(query);
94 104 }
95 105  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
... ... @@ -89,6 +89,14 @@ public interface CrmContractService {
89 89 CrmContractDO getContract(Long id);
90 90  
91 91 /**
  92 + * 校验合同是否合法
  93 + *
  94 + * @param id 编号
  95 + * @return 合同
  96 + */
  97 + CrmContractDO validateContract(Long id);
  98 +
  99 + /**
92 100 * 获得合同列表
93 101 *
94 102 * @param ids 编号
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
... ... @@ -324,6 +324,11 @@ public class CrmContractServiceImpl implements CrmContractService {
324 324 }
325 325  
326 326 @Override
  327 + public CrmContractDO validateContract(Long id) {
  328 + return validateContractExists(id);
  329 + }
  330 +
  331 + @Override
327 332 public List<CrmContractDO> getContractList(Collection<Long> ids) {
328 333 if (CollUtil.isEmpty(ids)) {
329 334 return ListUtil.empty();
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
... ... @@ -2,20 +2,17 @@ package cn.iocoder.yudao.module.crm.service.receivable;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5   -import cn.hutool.core.util.ObjectUtil;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
9 8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
10 9 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
11   -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
12 10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
13 11 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
14 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
15 13 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
16 14 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
17 15 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
18   -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
19 16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
20 17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
21 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
... ... @@ -33,7 +30,8 @@ import java.util.List;
33 30 import java.util.Objects;
34 31  
35 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36   -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  33 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_NOT_EXISTS;
  34 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_UPDATE_FAIL;
37 35 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
38 36  
39 37 /**
... ... @@ -54,8 +52,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
54 52 @Resource
55 53 private CrmContractService contractService;
56 54 @Resource
57   - private CrmCustomerService customerService;
58   - @Resource
59 55 private CrmPermissionService permissionService;
60 56  
61 57 @Resource
... ... @@ -66,15 +62,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
66 62 @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}",
67 63 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
68 64 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
69   - // 1.1 校验关联数据是否存在
  65 + // 1. 校验关联数据是否存在
70 66 validateRelationDataExists(createReqVO);
71   - // 1.2 查验关联合同回款数量
72   - Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId());
73   - int period = (int) (count + 1);
74 67  
75 68 // 2. 插入还款计划
76   - CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class)
77   - .setPeriod(period).setFinishStatus(false);
  69 + CrmReceivablePlanDO maxPeriodReceivablePlan = receivablePlanMapper.selectMaxPeriodByContractId(createReqVO.getContractId());
  70 + int period = maxPeriodReceivablePlan == null ? 1 : maxPeriodReceivablePlan.getPeriod() + 1;
  71 + CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setPeriod(period);
  72 + if (createReqVO.getReturnTime() != null && createReqVO.getRemindDays() != null) {
  73 + receivablePlan.setRemindTime(createReqVO.getReturnTime().minusDays(createReqVO.getRemindDays()));
  74 + }
78 75 receivablePlanMapper.insert(receivablePlan);
79 76  
80 77 // 3. 创建数据权限
... ... @@ -93,15 +90,21 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
93 90 success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)
94 91 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
95 92 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
96   - // 1. 校验存在
  93 + updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null); // 防止修改这些字段
  94 + // 1.1 校验存在
97 95 validateRelationDataExists(updateReqVO);
  96 + // 1.2 校验关联数据是否存在
98 97 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
99   - if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑;
100   - throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款");
  98 + // 1.3 如果已经有对应的还款,则不允许编辑
  99 + if (Objects.nonNull(oldReceivablePlan.getReceivableId())) {
  100 + throw exception(RECEIVABLE_PLAN_UPDATE_FAIL);
101 101 }
102 102  
103   - // 2. 更新
  103 + // 2. 更新还款计划
104 104 CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class);
  105 + if (updateReqVO.getReturnTime() != null && updateReqVO.getRemindDays() != null) {
  106 + updateObj.setRemindTime(updateReqVO.getReturnTime().minusDays(updateReqVO.getRemindDays()));
  107 + }
105 108 receivablePlanMapper.updateById(updateObj);
106 109  
107 110 // 3. 记录操作日志上下文
... ... @@ -109,6 +112,19 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
109 112 LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
110 113 }
111 114  
  115 + private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) {
  116 + // 校验负责人存在
  117 + if (reqVO.getOwnerUserId() != null) {
  118 + adminUserApi.validateUser(reqVO.getOwnerUserId());
  119 + }
  120 + // 校验合同存在
  121 + if (reqVO.getContractId() != null) {
  122 + CrmContractDO contract = contractService.getContract(reqVO.getContractId());
  123 + reqVO.setCustomerId(contract.getCustomerId());
  124 + }
  125 + }
  126 +
  127 + // TODO @芋艿:优化下这个方法的命名
112 128 @Override
113 129 public void updateReceivableId(Long id, Long receivableId) {
114 130 // 校验存在
... ... @@ -117,31 +133,21 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
117 133 receivablePlanMapper.updateById(new CrmReceivablePlanDO().setReceivableId(receivableId).setFinishStatus(true));
118 134 }
119 135  
120   - private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) {
121   - adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
122   - CrmContractDO contract = contractService.getContract(reqVO.getContractId());
123   - if (ObjectUtil.isNull(contract)) { // 合同不存在
124   - throw exception(CONTRACT_NOT_EXISTS);
125   - }
126   - CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());
127   - if (ObjectUtil.isNull(customer)) { // 客户不存在
128   - throw exception(CUSTOMER_NOT_EXISTS);
129   - }
130   - }
131   -
132 136 @Override
133 137 @Transactional(rollbackFor = Exception.class)
134 138 @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = "{{#id}}",
135 139 success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)
136 140 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
137 141 public void deleteReceivablePlan(Long id) {
138   - // 校验存在
  142 + // 1. 校验存在
139 143 CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);
140   - // 删除
  144 +
  145 + // 2. 删除
141 146 receivablePlanMapper.deleteById(id);
142   - // 删除数据权限
  147 + // 3. 删除数据权限
143 148 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
144   - // 记录操作日志上下文
  149 +
  150 + // 4. 记录操作日志上下文
145 151 LogRecordContext.putVariable("receivablePlan", receivablePlan);
146 152 }
147 153  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
... ... @@ -245,6 +245,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
245 245 return receivableMapper.selectCheckReceivablesCount(userId);
246 246 }
247 247  
  248 + // TODO @芋艿:不确定这个方法还要不要;
248 249 @Override
249 250 public Long getReceivableCountByContractId(Long contractId) {
250 251 return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId);
... ...