Commit c4ff0a103816be47c97d471267e3337eb9909c94

Authored by puhui999
2 parents dc695e3c b285548b

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

Showing 12 changed files with 76 additions and 51 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/controller/admin/receivable/CrmReceivableController.java
... ... @@ -164,13 +164,6 @@ public class CrmReceivableController {
164 164 });
165 165 }
166 166  
167   - @GetMapping("/check-receivables-count")
168   - @Operation(summary = "获得待审核回款数量")
169   - @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
170   - public CommonResult<Long> getCheckReceivablesCount() {
171   - return success(receivableService.getCheckReceivablesCount(getLoginUserId()));
172   - }
173   -
174 167 @PutMapping("/submit")
175 168 @Operation(summary = "提交回款审批")
176 169 @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
... ... @@ -179,4 +172,11 @@ public class CrmReceivableController {
179 172 return success(true);
180 173 }
181 174  
  175 + @GetMapping("/audit-count")
  176 + @Operation(summary = "获得待审核回款数量")
  177 + @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
  178 + public CommonResult<Long> getAuditReceivableCount() {
  179 + return success(receivableService.getAuditReceivableCount(getLoginUserId()));
  180 + }
  181 +
182 182 }
... ...
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 {
163 163 });
164 164 }
165 165  
166   - @GetMapping("/remind-receivable-plan-count")
167   - @Operation(summary = "获得待回款提醒数量")
168   - @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
169   - public CommonResult<Long> getRemindReceivablesCount() {
170   - return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId()));
171   - }
172   -
173 166 @GetMapping("/simple-list")
174 167 @Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项")
175 168 @Parameters({
... ... @@ -187,4 +180,11 @@ public class CrmReceivablePlanController {
187 180 .setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));
188 181 }
189 182  
  183 + @GetMapping("/remind-count")
  184 + @Operation(summary = "获得待回款提醒数量")
  185 + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
  186 + public CommonResult<Long> getReceivablePlanRemindCount() {
  187 + return success(receivablePlanService.getReceivablePlanRemindCount(getLoginUserId()));
  188 + }
  189 +
190 190 }
... ...
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&lt;CrmReceivableDO&gt; {
68 68 return selectJoinList(CrmReceivableDO.class, query);
69 69 }
70 70  
71   - default Long selectCheckReceivablesCount(Long userId) {
  71 + default Long selectCountByAudit(Long userId) {
72 72 MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
73 73 // 我负责的 + 非公海
74 74 CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
75 75 CrmReceivableDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
76   - // 未提交 or 审核不通过
77   - query.in(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.REJECT.getStatus());
  76 + // 未审核
  77 + query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.PROCESS.getStatus());
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/dal/mysql/receivable/CrmReceivablePlanMapper.java
... ... @@ -60,19 +60,16 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
60 60 }
61 61  
62 62 // Backlog: 回款提醒类型
63   - // TODO: @dhb52 需要配置 提前提醒天数
64   - int REMIND_DAYS = 20;
65 63 LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
66   - LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
67 64 if (CrmReceivablePlanPageReqVO.REMIND_TYPE_NEEDED.equals(pageReqVO.getRemindType())) { // 待回款
68   - query.isNull(CrmReceivablePlanDO::getReceivableId)
69   - .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
  65 + query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
  66 + .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday) // 已逾期
  67 + .lt(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒
70 68 } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_EXPIRED.equals(pageReqVO.getRemindType())) { // 已逾期
71   - query.isNull(CrmReceivablePlanDO::getReceivableId)
72   - .lt(CrmReceivablePlanDO::getReturnTime, endOfToday);
  69 + query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
  70 + .ge(CrmReceivablePlanDO::getReturnTime, beginOfToday); // 已逾期
73 71 } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款
74   - query.isNotNull(CrmReceivablePlanDO::getReceivableId)
75   - .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
  72 + query.isNotNull(CrmReceivablePlanDO::getReceivableId);
76 73 }
77 74 return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);
78 75 }
... ... @@ -86,20 +83,16 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
86 83 return selectJoinList(CrmReceivablePlanDO.class, query);
87 84 }
88 85  
89   - default Long selectRemindReceivablePlanCount(Long userId) {
  86 + default Long selectReceivablePlanCountByRemind(Long userId) {
90 87 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
91 88 // 我负责的 + 非公海
92 89 CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
93 90 CrmReceivablePlanDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
94   - // TODO: @dhb52 需要配置 提前提醒天数
95   - int REMIND_DAYS = 20;
  91 + // 未回款 + 已逾期 + 今天开始提醒
96 92 LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
97   - LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
98   - query.isNull(CrmReceivablePlanDO::getReceivableId)
99   - .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
100   - // TODO return_time 小于现在;
101   - // TODO 未回款
102   - // TODO remind_time 大于现在;
  93 + query.isNull(CrmReceivablePlanDO::getReceivableId) // 未回款
  94 + .lt(CrmReceivablePlanDO::getReturnTime, beginOfToday) // 已逾期
  95 + .lt(CrmReceivablePlanDO::getRemindTime, beginOfToday); // 今天开始提醒
103 96 return selectCount(query);
104 97 }
105 98  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java
1 1 /**
2   - * TODO 芋艿:临时占位,后续可删除
  2 + * 定时任务
3 3 */
4 4 package cn.iocoder.yudao.module.crm.job;
5 5 \ No newline at end of file
... ...
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 {
222 222 success = CRM_CONTRACT_DELETE_SUCCESS)
223 223 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
224 224 public void deleteContract(Long id) {
225   - // TODO @合同待定:如果被 CrmReceivableDO 所使用,则不允许删除
  225 + // TODO @puhui999:如果被 CrmReceivableDO 所使用,则不允许删除
226 226 // 校验存在
227 227 CrmContractDO contract = validateContractExists(id);
228 228 // 删除
... ...
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 {
170 170 throw exception(CRM_PERMISSION_DELETE_FAIL);
171 171 }
172 172 // 校验操作人是否为负责人
173   - CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.getFirst().getBizId(), userId);
  173 + CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.get(0).getBizId(), userId);
174 174 if (permission == null) {
175 175 throw exception(CRM_PERMISSION_DELETE_DENIED);
176 176 }
... ...
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 {
90 90 * @param userId 用户编号
91 91 * @return 提醒数量
92 92 */
93   - Long getRemindReceivablePlanCount(Long userId);
  93 + Long getReceivablePlanRemindCount(Long userId);
94 94  
95 95 }
... ...
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 {
184 184 }
185 185  
186 186 @Override
187   - public Long getRemindReceivablePlanCount(Long userId) {
188   - return receivablePlanMapper.selectRemindReceivablePlanCount(userId);
  187 + public Long getReceivablePlanRemindCount(Long userId) {
  188 + return receivablePlanMapper.selectReceivablePlanCountByRemind(userId);
189 189 }
190 190  
191 191 }
... ...
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 {
110 110 * 获得待审核回款数量
111 111 *
112 112 * @param userId 用户编号
113   - * @return 提醒数量
  113 + * @return 待审批数量
114 114 */
115   - Long getCheckReceivablesCount(Long userId);
  115 + Long getAuditReceivableCount(Long userId);
116 116  
117 117 /**
118 118 * 获得合同已回款金额 Map
... ...
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 @puhui999:审批通过时,不允许删除;
192 216  
193 217 // 2. 删除
194 218 receivableMapper.deleteById(id);
... ... @@ -256,8 +280,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
256 280 }
257 281  
258 282 @Override
259   - public Long getCheckReceivablesCount(Long userId) {
260   - return receivableMapper.selectCheckReceivablesCount(userId);
  283 + public Long getAuditReceivableCount(Long userId) {
  284 + return receivableMapper.selectCountByAudit(userId);
261 285 }
262 286  
263 287 @Override
... ...