Commit dc695e3cdab93073ec4f2459a87b9d63395815ef

Authored by puhui999
2 parents 462a4ee8 5fbb4236

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

Showing 29 changed files with 465 additions and 313 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
@@ -14,5 +14,6 @@ public interface DictTypeConstants { @@ -14,5 +14,6 @@ public interface DictTypeConstants {
14 String CRM_PRODUCT_UNIT = "crm_product_unit"; // CRM 产品单位 14 String CRM_PRODUCT_UNIT = "crm_product_unit"; // CRM 产品单位
15 String CRM_PRODUCT_STATUS = "crm_product_status"; // CRM 产品状态 15 String CRM_PRODUCT_STATUS = "crm_product_status"; // CRM 产品状态
16 String CRM_FOLLOW_UP_TYPE = "crm_follow_up_type"; // CRM 跟进方式 16 String CRM_FOLLOW_UP_TYPE = "crm_follow_up_type"; // CRM 跟进方式
  17 + String CRM_RECEIVABLE_RETURN_TYPE = "crm_receivable_return_type"; // CRM 回款方式
17 18
18 } 19 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -28,7 +28,6 @@ public interface ErrorCodeConstants { @@ -28,7 +28,6 @@ public interface ErrorCodeConstants {
28 28
29 // ========== 联系人管理 1-020-003-000 ========== 29 // ========== 联系人管理 1-020-003-000 ==========
30 ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在"); 30 ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
31 - ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在");  
32 ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除"); 31 ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
33 ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, "更新联系人负责人失败"); 32 ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, "更新联系人负责人失败");
34 33
@@ -38,10 +37,13 @@ public interface ErrorCodeConstants { @@ -38,10 +37,13 @@ public interface ErrorCodeConstants {
38 ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, "删除回款失败,原因: 被回款计划所使用,不允许删除"); 37 ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, "删除回款失败,原因: 被回款计划所使用,不允许删除");
39 ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, "回款提交审核失败,原因:回款没处在未提交状态"); 38 ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, "回款提交审核失败,原因:回款没处在未提交状态");
40 ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态"); 39 ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态");
  40 + ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试");
  41 + ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态");
41 42
42 // ========== 回款计划 1-020-005-000 ========== 43 // ========== 回款计划 1-020-005-000 ==========
43 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在"); 44 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
44 - ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:{}"); 45 + ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:已经有对应的还款");
  46 + ErrorCode RECEIVABLE_PLAN_EXISTS_RECEIVABLE = new ErrorCode(1_020_006_001, "回款计划已经有对应的回款,不能使用");
45 47
46 // ========== 客户管理 1_020_006_000 ========== 48 // ========== 客户管理 1_020_006_000 ==========
47 ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在"); 49 ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
@@ -64,10 +66,8 @@ public interface ErrorCodeConstants { @@ -64,10 +66,8 @@ public interface ErrorCodeConstants {
64 // ========== 权限管理 1_020_007_000 ========== 66 // ========== 权限管理 1_020_007_000 ==========
65 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); 67 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
66 ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限"); 68 ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
67 - ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");  
68 ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人"); 69 ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人");
69 ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, "删除数据权限失败,原因:批量删除权限的时候,只能属于同一个 bizId 下"); 70 ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, "删除数据权限失败,原因:批量删除权限的时候,只能属于同一个 bizId 下");
70 - ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数据权限失败,原因:存在负责人");  
71 ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限"); 71 ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限");
72 ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人"); 72 ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人");
73 ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限"); 73 ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限");
@@ -93,7 +93,6 @@ public interface ErrorCodeConstants { @@ -93,7 +93,6 @@ public interface ErrorCodeConstants {
93 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_010_003, "商机状态不存在"); 93 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_010_003, "商机状态不存在");
94 94
95 // ========== 客户公海规则设置 1_020_012_000 ========== 95 // ========== 客户公海规则设置 1_020_012_000 ==========
96 - ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海配置不存在或未启用");  
97 ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在"); 96 ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在");
98 97
99 // ========== 跟进记录 1_020_013_000 ========== 98 // ========== 跟进记录 1_020_013_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,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 9 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
@@ -24,6 +25,7 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; @@ -24,6 +25,7 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 25 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
25 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 26 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
26 import cn.iocoder.yudao.module.crm.service.product.CrmProductService; 27 import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
  28 +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
27 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 29 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
28 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 30 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
29 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 31 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -39,6 +41,7 @@ import org.springframework.validation.annotation.Validated; @@ -39,6 +41,7 @@ import org.springframework.validation.annotation.Validated;
39 import org.springframework.web.bind.annotation.*; 41 import org.springframework.web.bind.annotation.*;
40 42
41 import java.io.IOException; 43 import java.io.IOException;
  44 +import java.math.BigDecimal;
42 import java.util.Collections; 45 import java.util.Collections;
43 import java.util.List; 46 import java.util.List;
44 import java.util.Map; 47 import java.util.Map;
@@ -67,6 +70,8 @@ public class CrmContractController { @@ -67,6 +70,8 @@ public class CrmContractController {
67 private CrmBusinessService businessService; 70 private CrmBusinessService businessService;
68 @Resource 71 @Resource
69 private CrmProductService productService; 72 private CrmProductService productService;
  73 + @Resource
  74 + private CrmReceivableService receivableService;
70 75
71 @Resource 76 @Resource
72 private AdminUserApi adminUserApi; 77 private AdminUserApi adminUserApi;
@@ -174,12 +179,6 @@ public class CrmContractController { @@ -174,12 +179,6 @@ public class CrmContractController {
174 return success(true); 179 return success(true);
175 } 180 }
176 181
177 - /**  
178 - * 构建详细的合同结果  
179 - *  
180 - * @param contractList 原始合同信息  
181 - * @return 细的合同结果  
182 - */  
183 private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) { 182 private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) {
184 if (CollUtil.isEmpty(contractList)) { 183 if (CollUtil.isEmpty(contractList)) {
185 return Collections.emptyList(); 184 return Collections.emptyList();
@@ -197,6 +196,9 @@ public class CrmContractController { @@ -197,6 +196,9 @@ public class CrmContractController {
197 // 1.4 获取商机 196 // 1.4 获取商机
198 Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap( 197 Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap(
199 convertSet(contractList, CrmContractDO::getBusinessId)); 198 convertSet(contractList, CrmContractDO::getBusinessId));
  199 + // 1.5 获得已回款金额
  200 + Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(
  201 + convertSet(contractList, CrmContractDO::getId));
200 // 2. 拼接数据 202 // 2. 拼接数据
201 return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> { 203 return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> {
202 // 2.1 设置客户信息 204 // 2.1 设置客户信息
@@ -212,6 +214,8 @@ public class CrmContractController { @@ -212,6 +214,8 @@ public class CrmContractController {
212 findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName())); 214 findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName()));
213 // 2.4 设置商机信息 215 // 2.4 设置商机信息
214 findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName())); 216 findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName()));
  217 + // 2.5 设置已回款金额
  218 + contractVO.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO));
215 }); 219 });
216 } 220 }
217 221
@@ -229,13 +233,24 @@ public class CrmContractController { @@ -229,13 +233,24 @@ public class CrmContractController {
229 return success(contractService.getRemindContractCount(getLoginUserId())); 233 return success(contractService.getRemindContractCount(getLoginUserId()));
230 } 234 }
231 235
232 - @GetMapping("/list-all-simple-by-customer")  
233 - @Operation(summary = "获得合同精简列表", description = "只包含有读权限的客户的合同,主要用于前端的下拉选项") 236 + @GetMapping("/simple-list")
  237 + @Operation(summary = "获得合同精简列表", description = "只包含的合同,主要用于前端的下拉选项")
234 @Parameter(name = "customerId", description = "客户编号", required = true) 238 @Parameter(name = "customerId", description = "客户编号", required = true)
235 @PreAuthorize("@ss.hasPermission('crm:contract:query')") 239 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
236 - public CommonResult<List<CrmContractRespVO>> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId) {  
237 - PageResult<CrmContractDO> result = contractService.getContractPageByCustomerId(new CrmContractPageReqVO().setCustomerId(customerId));  
238 - return success(BeanUtils.toBean(result.getList(), CrmContractRespVO.class)); 240 + public CommonResult<List<CrmContractRespVO>> getContractSimpleList(@RequestParam("customerId") Long customerId) {
  241 + CrmContractPageReqVO pageReqVO = new CrmContractPageReqVO().setCustomerId(customerId);
  242 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页
  243 + PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageReqVO);
  244 + if (CollUtil.isEmpty(pageResult.getList())) {
  245 + return success(Collections.emptyList());
  246 + }
  247 + // 拼接数据
  248 + Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId(
  249 + convertSet(pageResult.getList(), CrmContractDO::getId));
  250 + return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段
  251 + .setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus())
  252 + .setTotalPrice(contract.getTotalPrice())
  253 + .setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO))));
239 } 254 }
240 255
241 } 256 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
@@ -88,6 +88,10 @@ public class CrmContractRespVO { @@ -88,6 +88,10 @@ public class CrmContractRespVO {
88 @ExcelProperty("合同金额") 88 @ExcelProperty("合同金额")
89 private BigDecimal totalPrice; 89 private BigDecimal totalPrice;
90 90
  91 + @Schema(description = "已回款金额", example = "5617")
  92 + @ExcelProperty("已回款金额")
  93 + private BigDecimal totalReceivablePrice;
  94 +
91 @Schema(description = "客户签约人编号", example = "18546") 95 @Schema(description = "客户签约人编号", example = "18546")
92 private Long signContactId; 96 private Long signContactId;
93 @Schema(description = "客户签约人", example = "小豆") 97 @Schema(description = "客户签约人", example = "小豆")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java
@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO;
7 -import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO; 7 +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogRespVO;
8 import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; 8 import cn.iocoder.yudao.module.crm.enums.LogRecordConstants;
9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; 10 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
@@ -54,11 +54,11 @@ public class CrmOperateLogController { @@ -54,11 +54,11 @@ public class CrmOperateLogController {
54 @GetMapping("/page") 54 @GetMapping("/page")
55 @Operation(summary = "获得操作日志") 55 @Operation(summary = "获得操作日志")
56 @PreAuthorize("@ss.hasPermission('crm:operate-log:query')") 56 @PreAuthorize("@ss.hasPermission('crm:operate-log:query')")
57 - public CommonResult<PageResult<CrmOperateLogV2RespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { 57 + public CommonResult<PageResult<CrmOperateLogRespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
58 OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); 58 OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO();
59 reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释 59 reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释
60 reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); 60 reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId());
61 - return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogV2RespVO.class)); 61 + return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogRespVO.class));
62 } 62 }
63 63
64 } 64 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java
@@ -6,10 +6,10 @@ import lombok.Data; @@ -6,10 +6,10 @@ import lombok.Data;
6 6
7 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
8 8
9 -@Schema(description = "管理后台 - CRM 跟进 Response VO") 9 +@Schema(description = "管理后台 - CRM 操作日志 Response VO")
10 @Data 10 @Data
11 @ExcelIgnoreUnannotated 11 @ExcelIgnoreUnannotated
12 -public class CrmOperateLogV2RespVO { 12 +public class CrmOperateLogRespVO {
13 13
14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") 14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
15 private Long id; 15 private Long id;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -4,20 +4,23 @@ import cn.hutool.core.collection.CollUtil; @@ -4,20 +4,23 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 11 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  12 +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
12 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO; 15 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
14 -import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;  
15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 18 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
18 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 19 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 20 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
20 import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; 21 import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
  22 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  23 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
21 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 24 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
22 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 25 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
23 import io.swagger.v3.oas.annotations.Operation; 26 import io.swagger.v3.oas.annotations.Operation;
@@ -31,14 +34,15 @@ import org.springframework.validation.annotation.Validated; @@ -31,14 +34,15 @@ import org.springframework.validation.annotation.Validated;
31 import org.springframework.web.bind.annotation.*; 34 import org.springframework.web.bind.annotation.*;
32 35
33 import java.io.IOException; 36 import java.io.IOException;
  37 +import java.util.Collections;
34 import java.util.List; 38 import java.util.List;
35 import java.util.Map; 39 import java.util.Map;
36 import java.util.stream.Stream; 40 import java.util.stream.Stream;
37 41
38 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 42 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
39 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 43 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
40 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;  
41 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 44 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  45 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
42 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 46 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
43 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 47 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
44 48
@@ -57,6 +61,8 @@ public class CrmReceivableController { @@ -57,6 +61,8 @@ public class CrmReceivableController {
57 61
58 @Resource 62 @Resource
59 private AdminUserApi adminUserApi; 63 private AdminUserApi adminUserApi;
  64 + @Resource
  65 + private DeptApi deptApi;
60 66
61 @PostMapping("/create") 67 @PostMapping("/create")
62 @Operation(summary = "创建回款") 68 @Operation(summary = "创建回款")
@@ -88,7 +94,14 @@ public class CrmReceivableController { @@ -88,7 +94,14 @@ public class CrmReceivableController {
88 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 94 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
89 public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) { 95 public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) {
90 CrmReceivableDO receivable = receivableService.getReceivable(id); 96 CrmReceivableDO receivable = receivableService.getReceivable(id);
91 - return success(BeanUtils.toBean(receivable, CrmReceivableRespVO.class)); 97 + return success(buildReceivableDetail(receivable));
  98 + }
  99 +
  100 + private CrmReceivableRespVO buildReceivableDetail(CrmReceivableDO receivable) {
  101 + if (receivable == null) {
  102 + return null;
  103 + }
  104 + return buildReceivableDetailList(Collections.singletonList(receivable)).get(0);
92 } 105 }
93 106
94 @GetMapping("/page") 107 @GetMapping("/page")
@@ -96,7 +109,7 @@ public class CrmReceivableController { @@ -96,7 +109,7 @@ public class CrmReceivableController {
96 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 109 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
97 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) { 110 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {
98 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId()); 111 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());
99 - return success(buildReceivableDetailPage(pageResult)); 112 + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
100 } 113 }
101 114
102 @GetMapping("/page-by-customer") 115 @GetMapping("/page-by-customer")
@@ -104,7 +117,7 @@ public class CrmReceivableController { @@ -104,7 +117,7 @@ public class CrmReceivableController {
104 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) { 117 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {
105 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 118 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
106 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO); 119 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);
107 - return success(buildReceivableDetailPage(pageResult)); 120 + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
108 } 121 }
109 122
110 @GetMapping("/export-excel") 123 @GetMapping("/export-excel")
@@ -114,36 +127,43 @@ public class CrmReceivableController { @@ -114,36 +127,43 @@ public class CrmReceivableController {
114 public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO, 127 public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO,
115 HttpServletResponse response) throws IOException { 128 HttpServletResponse response) throws IOException {
116 exportReqVO.setPageSize(PAGE_SIZE_NONE); 129 exportReqVO.setPageSize(PAGE_SIZE_NONE);
117 - PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId()); 130 + List<CrmReceivableDO> list = receivableService.getReceivablePage(exportReqVO, getLoginUserId()).getList();
118 // 导出 Excel 131 // 导出 Excel
119 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class, 132 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class,
120 - buildReceivableDetailPage(pageResult).getList()); 133 + buildReceivableDetailList(list));
121 } 134 }
122 135
123 - /**  
124 - * 构建详细的回款分页结果  
125 - *  
126 - * @param pageResult 简单的回款分页结果  
127 - * @return 详细的回款分页结果  
128 - */  
129 - private PageResult<CrmReceivableRespVO> buildReceivableDetailPage(PageResult<CrmReceivableDO> pageResult) {  
130 - List<CrmReceivableDO> receivableList = pageResult.getList(); 136 + private List<CrmReceivableRespVO> buildReceivableDetailList(List<CrmReceivableDO> receivableList) {
131 if (CollUtil.isEmpty(receivableList)) { 137 if (CollUtil.isEmpty(receivableList)) {
132 - return PageResult.empty(pageResult.getTotal()); 138 + return Collections.emptyList();
133 } 139 }
134 - // 1. 获取客户列表  
135 - List<CrmCustomerDO> customerList = customerService.getCustomerList( 140 + // 1.1 获取客户列表
  141 + Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
136 convertSet(receivableList, CrmReceivableDO::getCustomerId)); 142 convertSet(receivableList, CrmReceivableDO::getCustomerId));
137 - // 2. 获取创建人、负责人列表 143 + // 1.2 获取创建人、负责人列表
138 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList, 144 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList,
139 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 145 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
140 - // 3. 获得合同列表  
141 - List<CrmContractDO> contractList = contractService.getContractList( 146 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
  147 + // 1.3 获得合同列表
  148 + Map<Long, CrmContractDO> contractMap = contractService.getContractMap(
142 convertSet(receivableList, CrmReceivableDO::getContractId)); 149 convertSet(receivableList, CrmReceivableDO::getContractId));
143 - return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList); 150 + // 2. 拼接结果
  151 + return BeanUtils.toBean(receivableList, CrmReceivableRespVO.class, (receivableVO) -> {
  152 + // 2.1 拼接客户名称
  153 + findAndThen(customerMap, receivableVO.getCustomerId(), customer -> receivableVO.setCustomerName(customer.getName()));
  154 + // 2.2 拼接负责人、创建人名称
  155 + MapUtils.findAndThen(userMap, NumberUtils.parseLong(receivableVO.getCreator()),
  156 + user -> receivableVO.setCreatorName(user.getNickname()));
  157 + MapUtils.findAndThen(userMap, receivableVO.getOwnerUserId(), user -> {
  158 + receivableVO.setOwnerUserName(user.getNickname());
  159 + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> receivableVO.setOwnerUserDeptName(dept.getName()));
  160 + });
  161 + // 2.3 拼接合同信息
  162 + findAndThen(contractMap, receivableVO.getContractId(), contract ->
  163 + receivableVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class)));
  164 + });
144 } 165 }
145 166
146 -  
147 @GetMapping("/check-receivables-count") 167 @GetMapping("/check-receivables-count")
148 @Operation(summary = "获得待审核回款数量") 168 @Operation(summary = "获得待审核回款数量")
149 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 169 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
@@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; @@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; 11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
12 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; 12 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO; 13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
14 -import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; 14 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
@@ -34,14 +34,15 @@ import org.springframework.validation.annotation.Validated; @@ -34,14 +34,15 @@ import org.springframework.validation.annotation.Validated;
34 import org.springframework.web.bind.annotation.*; 34 import org.springframework.web.bind.annotation.*;
35 35
36 import java.io.IOException; 36 import java.io.IOException;
  37 +import java.util.Collections;
37 import java.util.List; 38 import java.util.List;
38 import java.util.Map; 39 import java.util.Map;
39 import java.util.stream.Stream; 40 import java.util.stream.Stream;
40 41
41 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 42 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
42 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 43 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
43 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;  
44 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 44 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  45 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
45 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 46 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
46 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 47 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
47 48
@@ -93,7 +94,14 @@ public class CrmReceivablePlanController { @@ -93,7 +94,14 @@ public class CrmReceivablePlanController {
93 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") 94 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
94 public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) { 95 public CommonResult<CrmReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) {
95 CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id); 96 CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);
96 - 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);
97 } 105 }
98 106
99 @GetMapping("/page") 107 @GetMapping("/page")
@@ -101,7 +109,7 @@ public class CrmReceivablePlanController { @@ -101,7 +109,7 @@ public class CrmReceivablePlanController {
101 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") 109 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
102 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) { 110 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
103 PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId()); 111 PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId());
104 - return success(convertDetailReceivablePlanPage(pageResult)); 112 + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
105 } 113 }
106 114
107 @GetMapping("/page-by-customer") 115 @GetMapping("/page-by-customer")
@@ -109,10 +117,9 @@ public class CrmReceivablePlanController { @@ -109,10 +117,9 @@ public class CrmReceivablePlanController {
109 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) { 117 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
110 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 118 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
111 PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO); 119 PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
112 - return success(convertDetailReceivablePlanPage(pageResult)); 120 + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal()));
113 } 121 }
114 122
115 - // TODO 芋艿:后面在优化导出  
116 @GetMapping("/export-excel") 123 @GetMapping("/export-excel")
117 @Operation(summary = "导出回款计划 Excel") 124 @Operation(summary = "导出回款计划 Excel")
118 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')") 125 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')")
@@ -120,39 +127,42 @@ public class CrmReceivablePlanController { @@ -120,39 +127,42 @@ public class CrmReceivablePlanController {
120 public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO, 127 public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO,
121 HttpServletResponse response) throws IOException { 128 HttpServletResponse response) throws IOException {
122 exportReqVO.setPageSize(PAGE_SIZE_NONE); 129 exportReqVO.setPageSize(PAGE_SIZE_NONE);
123 - PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()); 130 + List<CrmReceivablePlanDO> list = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()).getList();
124 // 导出 Excel 131 // 导出 Excel
125 ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class, 132 ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class,
126 - convertDetailReceivablePlanPage(pageResult).getList()); 133 + buildReceivableDetailList(list));
127 } 134 }
128 135
129 - /**  
130 - * 构建详细的回款计划分页结果  
131 - *  
132 - * @param pageResult 简单的回款计划分页结果  
133 - * @return 详细的回款计划分页结果  
134 - */  
135 - private PageResult<CrmReceivablePlanRespVO> convertDetailReceivablePlanPage(PageResult<CrmReceivablePlanDO> pageResult) {  
136 - List<CrmReceivablePlanDO> receivablePlanList = pageResult.getList(); 136 + private List<CrmReceivablePlanRespVO> buildReceivableDetailList(List<CrmReceivablePlanDO> receivablePlanList) {
137 if (CollUtil.isEmpty(receivablePlanList)) { 137 if (CollUtil.isEmpty(receivablePlanList)) {
138 - return PageResult.empty(pageResult.getTotal()); 138 + return Collections.emptyList();
139 } 139 }
140 - // 1. 获取客户列表  
141 - List<CrmCustomerDO> customerList = customerService.getCustomerList( 140 + // 1.1 获取客户 Map
  141 + Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
142 convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId)); 142 convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId));
143 - // 2. 获取创建人、负责人列表 143 + // 1.2 获取创建人、负责人列表
144 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList, 144 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList,
145 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 145 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
146 - // 3. 获得合同列表  
147 - List<CrmContractDO> contractList = contractService.getContractList( 146 + // 1.3 获得合同 Map
  147 + Map<Long, CrmContractDO> contractMap = contractService.getContractMap(
148 convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId)); 148 convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId));
149 - // 4. 获得还款列表  
150 - List<CrmReceivableDO> receivableList = receivableService.getReceivableList( 149 + // 1.4 获得回款 Map
  150 + Map<Long, CrmReceivableDO> receivableMap = receivableService.getReceivableMap(
151 convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId)); 151 convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId));
152 - return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList); 152 + // 2. 拼接数据
  153 + return BeanUtils.toBean(receivablePlanList, CrmReceivablePlanRespVO.class, (receivablePlanVO) -> {
  154 + // 2.1 拼接客户信息
  155 + findAndThen(customerMap, receivablePlanVO.getCustomerId(), customer -> receivablePlanVO.setCustomerName(customer.getName()));
  156 + // 2.2 拼接用户信息
  157 + findAndThen(userMap, receivablePlanVO.getOwnerUserId(), user -> receivablePlanVO.setOwnerUserName(user.getNickname()));
  158 + findAndThen(userMap, Long.parseLong(receivablePlanVO.getCreator()), user -> receivablePlanVO.setCreatorName(user.getNickname()));
  159 + // 2.3 拼接合同信息
  160 + findAndThen(contractMap, receivablePlanVO.getContractId(), contract -> receivablePlanVO.setContractNo(contract.getNo()));
  161 + // 2.4 拼接回款信息
  162 + receivablePlanVO.setReceivable(BeanUtils.toBean(receivableMap.get(receivablePlanVO.getReceivableId()), CrmReceivableRespVO.class));
  163 + });
153 } 164 }
154 165
155 -  
156 @GetMapping("/remind-receivable-plan-count") 166 @GetMapping("/remind-receivable-plan-count")
157 @Operation(summary = "获得待回款提醒数量") 167 @Operation(summary = "获得待回款提醒数量")
158 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") 168 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
@@ -160,18 +170,21 @@ public class CrmReceivablePlanController { @@ -160,18 +170,21 @@ public class CrmReceivablePlanController {
160 return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId())); 170 return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId()));
161 } 171 }
162 172
163 - @GetMapping("/list-all-simple-by-customer") 173 + @GetMapping("/simple-list")
164 @Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项") 174 @Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项")
165 @Parameters({ 175 @Parameters({
166 @Parameter(name = "customerId", description = "客户编号", required = true), 176 @Parameter(name = "customerId", description = "客户编号", required = true),
167 @Parameter(name = "contractId", description = "合同编号", required = true) 177 @Parameter(name = "contractId", description = "合同编号", required = true)
168 }) 178 })
169 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") 179 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
170 - public CommonResult<List<CrmReceivablePlanRespVO>> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId,  
171 - @RequestParam("contractId") Long contractId) {  
172 - PageResult<CrmReceivablePlanDO> result = receivablePlanService.getReceivablePlanPageByCustomerId(  
173 - new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId));  
174 - return success(BeanUtils.toBean(result.getList(), CrmReceivablePlanRespVO.class)); 180 + public CommonResult<List<CrmReceivablePlanRespVO>> getReceivablePlanSimpleList(@RequestParam("customerId") Long customerId,
  181 + @RequestParam("contractId") Long contractId) {
  182 + CrmReceivablePlanPageReqVO pageReqVO = new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId);
  183 + pageReqVO.setPageNo(PAGE_SIZE_NONE);
  184 + PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
  185 + return success(convertList(pageResult.getList(), receivablePlan -> new CrmReceivablePlanRespVO() // 只返回 id、period 等信息
  186 + .setId(receivablePlan.getId()).setPeriod(receivablePlan.getPeriod()).setReceivableId(receivablePlan.getReceivableId())
  187 + .setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));
175 } 188 }
176 189
177 } 190 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
2 2
  3 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
3 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 6 import lombok.Data;
5 7
@@ -35,17 +37,16 @@ public class CrmReceivablePlanRespVO { @@ -35,17 +37,16 @@ public class CrmReceivablePlanRespVO {
35 @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") 37 @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
36 private LocalDateTime returnTime; 38 private LocalDateTime returnTime;
37 39
38 - @Schema(description = "回款方式", example = "1")  
39 - private Integer returnType; // 来自 Receivable 的 returnType 字段 40 + @Schema(description = "计划回款方式", example = "1")
  41 + private Integer returnType;
40 42
41 @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") 43 @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
42 private BigDecimal price; 44 private BigDecimal price;
43 45
44 @Schema(description = "回款编号", example = "19852") 46 @Schema(description = "回款编号", example = "19852")
45 private Long receivableId; 47 private Long receivableId;
46 -  
47 - @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
48 - private Boolean finishStatus; 48 + @Schema(description = "回款信息")
  49 + private CrmReceivableRespVO receivable;
49 50
50 @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 51 @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
51 private Integer remindDays; 52 private Integer remindDays;
@@ -57,12 +58,18 @@ public class CrmReceivablePlanRespVO { @@ -57,12 +58,18 @@ public class CrmReceivablePlanRespVO {
57 private String remark; 58 private String remark;
58 59
59 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 60 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  61 + @ExcelProperty("创建时间")
60 private LocalDateTime createTime; 62 private LocalDateTime createTime;
61 63
62 - @Schema(description = "创建人", example = "25682")  
63 - private String creator; 64 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  65 + @ExcelProperty("更新时间")
  66 + private LocalDateTime updateTime;
64 67
65 - @Schema(description = "创建人名字", example = "test") 68 + @Schema(description = "创建人", example = "1024")
  69 + @ExcelProperty("创建人")
  70 + private String creator;
  71 + @Schema(description = "创建人名字", example = "芋道源码")
  72 + @ExcelProperty("创建人名字")
66 private String creatorName; 73 private String creatorName;
67 74
68 } 75 }
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,9 +14,8 @@ public class CrmReceivablePlanSaveReqVO {
14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 14 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
15 private Long id; 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 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 20 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
22 @NotNull(message = "合同编号不能为空") 21 @NotNull(message = "合同编号不能为空")
@@ -37,10 +36,6 @@ public class CrmReceivablePlanSaveReqVO { @@ -37,10 +36,6 @@ public class CrmReceivablePlanSaveReqVO {
37 @NotNull(message = "计划回款金额不能为空") 36 @NotNull(message = "计划回款金额不能为空")
38 private BigDecimal price; 37 private BigDecimal price;
39 38
40 - @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
41 - @NotNull(message = "提醒日期不能为空")  
42 - private LocalDateTime remindTime;  
43 -  
44 @Schema(description = "提前几天提醒", example = "1") 39 @Schema(description = "提前几天提醒", example = "1")
45 private Integer remindDays; 40 private Integer remindDays;
46 41
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
2 2
  3 +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
3 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 6 import lombok.Data;
5 7
@@ -31,12 +33,30 @@ public class CrmReceivableRespVO { @@ -31,12 +33,30 @@ public class CrmReceivableRespVO {
31 33
32 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 34 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
33 private Long customerId; 35 private Long customerId;
  36 + @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
  37 + private String customerName;
34 38
35 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 39 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
36 private Long contractId; 40 private Long contractId;
  41 + @Schema(description = "合同信息")
  42 + private CrmContractRespVO contract;
37 43
38 - @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 44 + @Schema(description = "负责人的用户编号", example = "25682")
  45 + @ExcelProperty("负责人的用户编号")
39 private Long ownerUserId; 46 private Long ownerUserId;
  47 + @Schema(description = "负责人名字", example = "25682")
  48 + @ExcelProperty("负责人名字")
  49 + private String ownerUserName;
  50 + @Schema(description = "负责人部门")
  51 + @ExcelProperty("负责人部门")
  52 + private String ownerUserDeptName;
  53 +
  54 + @Schema(description = "工作流编号", example = "1043")
  55 + @ExcelProperty("工作流编号")
  56 + private String processInstanceId;
  57 +
  58 + @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  59 + private Integer auditStatus;
40 60
41 @Schema(description = "备注", example = "备注") 61 @Schema(description = "备注", example = "备注")
42 private String remark; 62 private String remark;
@@ -44,21 +64,11 @@ public class CrmReceivableRespVO { @@ -44,21 +64,11 @@ public class CrmReceivableRespVO {
44 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 64 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
45 private LocalDateTime createTime; 65 private LocalDateTime createTime;
46 66
47 - @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")  
48 - private String customerName;  
49 -  
50 - @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")  
51 - private Integer auditStatus;  
52 -  
53 - @Schema(description = "合同编号", example = "Q110")  
54 - private String contractNo;  
55 -  
56 - @Schema(description = "负责人", example = "test")  
57 - private String ownerUserName; 67 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  68 + private LocalDateTime updateTime;
58 69
59 @Schema(description = "创建人", example = "25682") 70 @Schema(description = "创建人", example = "25682")
60 private String creator; 71 private String creator;
61 -  
62 @Schema(description = "创建人名字", example = "test") 72 @Schema(description = "创建人名字", example = "test")
63 private String creatorName; 73 private String creatorName;
64 74
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum; 4 import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum;
  5 +import cn.iocoder.yudao.module.crm.framework.operatelog.core.*;
  6 +import com.mzt.logapi.starter.annotation.DiffLogField;
5 import io.swagger.v3.oas.annotations.media.Schema; 7 import io.swagger.v3.oas.annotations.media.Schema;
6 import jakarta.validation.constraints.NotNull; 8 import jakarta.validation.constraints.NotNull;
7 import lombok.Data; 9 import lombok.Data;
@@ -16,37 +18,41 @@ public class CrmReceivableSaveReqVO { @@ -16,37 +18,41 @@ public class CrmReceivableSaveReqVO {
16 @Schema(description = "编号", example = "25787") 18 @Schema(description = "编号", example = "25787")
17 private Long id; 19 private Long id;
18 20
19 - @Schema(description = "回款编号", example = "31177")  
20 - private String no; 21 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  22 + @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
  23 + @NotNull(message = "负责人编号不能为空")
  24 + private Long ownerUserId;
21 25
22 - @Schema(description = "回款计划编号", example = "1024")  
23 - private Long planId; // 不是通过回款计划创建的回款没有回款计划编号 26 + @Schema(description = "客户编号", example = "2")
  27 + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
  28 + private Long customerId; // 该字段不通过前端传递,而是 contractId 查询出来设置进去
  29 +
  30 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  31 + @DiffLogField(name = "合同", function = CrmContractParseFunction.NAME)
  32 + @NotNull(message = "合同编号不能为空")
  33 + private Long contractId;
  34 +
  35 + @Schema(description = "回款计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  36 + @DiffLogField(name = "合同", function = CrmReceivablePlanParseFunction.NAME)
  37 + private Long planId;
24 38
25 @Schema(description = "回款方式", example = "2") 39 @Schema(description = "回款方式", example = "2")
  40 + @DiffLogField(name = "回款方式", function = CrmReceivableReturnTypeParseFunction.NAME)
26 @InEnum(CrmReceivableReturnTypeEnum.class) 41 @InEnum(CrmReceivableReturnTypeEnum.class)
27 private Integer returnType; 42 private Integer returnType;
28 43
29 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") 44 @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  45 + @DiffLogField(name = "回款金额")
30 @NotNull(message = "回款金额不能为空") 46 @NotNull(message = "回款金额不能为空")
31 private BigDecimal price; 47 private BigDecimal price;
32 48
33 - @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")  
34 - @NotNull(message = "计划回款日期不能为空") 49 + @Schema(description = "回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  50 + @NotNull(message = "回款日期不能为空")
  51 + @DiffLogField(name = "回款日期")
35 private LocalDateTime returnTime; 52 private LocalDateTime returnTime;
36 53
37 - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
38 - @NotNull(message = "客户编号不能为空")  
39 - private Long customerId;  
40 -  
41 - @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
42 - @NotNull(message = "合同编号不能为空")  
43 - private Long contractId;  
44 -  
45 - @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
46 - @NotNull(message = "负责人编号不能为空")  
47 - private Long ownerUserId;  
48 -  
49 @Schema(description = "备注", example = "备注") 54 @Schema(description = "备注", example = "备注")
  55 + @DiffLogField(name = "备注")
50 private String remark; 56 private String remark;
51 57
52 } 58 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java deleted
1 -package cn.iocoder.yudao.module.crm.convert.receivable;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;  
6 -import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;  
7 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
8 -import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;  
9 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
10 -import org.mapstruct.Mapper;  
11 -import org.mapstruct.factory.Mappers;  
12 -  
13 -import java.util.List;  
14 -import java.util.Map;  
15 -  
16 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
17 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;  
18 -  
19 -/**  
20 - * 回款 Convert  
21 - *  
22 - * @author 赤焰  
23 - */  
24 -@Mapper  
25 -public interface CrmReceivableConvert {  
26 -  
27 - CrmReceivableConvert INSTANCE = Mappers.getMapper(CrmReceivableConvert.class);  
28 -  
29 - default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap,  
30 - List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) {  
31 - PageResult<CrmReceivableRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class);  
32 - // 拼接关联字段  
33 - Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);  
34 - Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId);  
35 - voPageResult.getList().forEach(receivable -> {  
36 - findAndThen(userMap, receivable.getOwnerUserId(), user -> receivable.setOwnerUserName(user.getNickname()));  
37 - findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname()));  
38 - findAndThen(customerMap, receivable.getCustomerId(), customer -> receivable.setCustomerName(customer.getName()));  
39 - findAndThen(contractMap, receivable.getContractId(), contract -> receivable.setContractNo(contract.getNo()));  
40 - });  
41 - return voPageResult;  
42 - }  
43 -  
44 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java deleted
1 -package cn.iocoder.yudao.module.crm.convert.receivable;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;  
6 -import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;  
7 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
8 -import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;  
9 -import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;  
10 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
11 -import org.mapstruct.Mapper;  
12 -import org.mapstruct.factory.Mappers;  
13 -  
14 -import java.util.List;  
15 -import java.util.Map;  
16 -  
17 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
18 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;  
19 -  
20 -/**  
21 - * 回款计划 Convert  
22 - *  
23 - * @author 芋道源码  
24 - */  
25 -@Mapper  
26 -public interface CrmReceivablePlanConvert {  
27 -  
28 - CrmReceivablePlanConvert INSTANCE = Mappers.getMapper(CrmReceivablePlanConvert.class);  
29 -  
30 - default PageResult<CrmReceivablePlanRespVO> convertPage(PageResult<CrmReceivablePlanDO> pageResult, Map<Long, AdminUserRespDTO> userMap,  
31 - List<CrmCustomerDO> customerList, List<CrmContractDO> contractList,  
32 - List<CrmReceivableDO> receivableList) {  
33 - PageResult<CrmReceivablePlanRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivablePlanRespVO.class);  
34 - // 拼接关联字段  
35 - Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);  
36 - Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId);  
37 - Map<Long, CrmReceivableDO> receivableMap = convertMap(receivableList, CrmReceivableDO::getId);  
38 - voPageResult.getList().forEach(receivablePlan -> {  
39 - findAndThen(userMap, receivablePlan.getOwnerUserId(), user -> receivablePlan.setOwnerUserName(user.getNickname()));  
40 - findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname()));  
41 - findAndThen(customerMap, receivablePlan.getCustomerId(), customer -> receivablePlan.setCustomerName(customer.getName()));  
42 - findAndThen(contractMap, receivablePlan.getContractId(), contract -> receivablePlan.setContractNo(contract.getNo()));  
43 - findAndThen(receivableMap, receivablePlan.getReceivableId(), receivable -> receivablePlan.setReturnType(receivable.getReturnType()));  
44 - });  
45 - return voPageResult;  
46 - }  
47 -  
48 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java
@@ -39,15 +39,21 @@ public class CrmReceivableDO extends BaseDO { @@ -39,15 +39,21 @@ public class CrmReceivableDO extends BaseDO {
39 */ 39 */
40 private String no; 40 private String no;
41 /** 41 /**
42 - * 回款计划编号,关联 {@link CrmReceivablePlanDO#getId()} 42 + * 回款计划编号
  43 + *
  44 + * 关联 {@link CrmReceivablePlanDO#getId()},非必须
43 */ 45 */
44 private Long planId; 46 private Long planId;
45 /** 47 /**
46 - * 客户编号,关联 {@link CrmCustomerDO#getId()} 48 + * 客户编号
  49 + *
  50 + * 关联 {@link CrmCustomerDO#getId()}
47 */ 51 */
48 private Long customerId; 52 private Long customerId;
49 /** 53 /**
50 - * 合同编号,关联 {@link CrmContractDO#getId()} 54 + * 合同编号
  55 + *
  56 + * 关联 {@link CrmContractDO#getId()}
51 */ 57 */
52 private Long contractId; 58 private Long contractId;
53 /** 59 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java
@@ -61,7 +61,7 @@ public class CrmReceivablePlanDO extends BaseDO { @@ -61,7 +61,7 @@ public class CrmReceivablePlanDO extends BaseDO {
61 */ 61 */
62 private LocalDateTime returnTime; 62 private LocalDateTime returnTime;
63 /** 63 /**
64 - * 回款类型 64 + * 计划回款类型
65 * 65 *
66 * 枚举 {@link CrmReceivableReturnTypeEnum} 66 * 枚举 {@link CrmReceivableReturnTypeEnum}
67 */ 67 */
@@ -75,10 +75,6 @@ public class CrmReceivablePlanDO extends BaseDO { @@ -75,10 +75,6 @@ public class CrmReceivablePlanDO extends BaseDO {
75 * 回款编号,关联 {@link CrmReceivableDO#getId()} 75 * 回款编号,关联 {@link CrmReceivableDO#getId()}
76 */ 76 */
77 private Long receivableId; 77 private Long receivableId;
78 - /**  
79 - * 完成状态  
80 - */  
81 - private Boolean finishStatus;  
82 78
83 /** 79 /**
84 * 提前几天提醒 80 * 提前几天提醒
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java deleted
1 -/**  
2 - * 回款  
3 - */  
4 -package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
1 package cn.iocoder.yudao.module.crm.dal.mysql.receivable; 1 package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -11,10 +12,16 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; @@ -11,10 +12,16 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
12 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; 13 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
13 import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; 14 import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
  15 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
14 import org.apache.ibatis.annotations.Mapper; 16 import org.apache.ibatis.annotations.Mapper;
15 17
  18 +import java.math.BigDecimal;
16 import java.util.Collection; 19 import java.util.Collection;
  20 +import java.util.Collections;
17 import java.util.List; 21 import java.util.List;
  22 +import java.util.Map;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
18 25
19 /** 26 /**
20 * 回款 Mapper 27 * 回款 Mapper
@@ -24,6 +31,10 @@ import java.util.List; @@ -24,6 +31,10 @@ import java.util.List;
24 @Mapper 31 @Mapper
25 public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> { 32 public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
26 33
  34 + default CrmReceivableDO selectByNo(String no) {
  35 + return selectOne(CrmReceivableDO::getNo, no);
  36 + }
  37 +
27 default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) { 38 default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
28 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>() 39 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()
29 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 40 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
@@ -67,4 +78,18 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; { @@ -67,4 +78,18 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
67 return selectCount(query); 78 return selectCount(query);
68 } 79 }
69 80
  81 + default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) {
  82 + if (CollUtil.isEmpty(contractIds)) {
  83 + return Collections.emptyMap();
  84 + }
  85 + // SQL sum 查询
  86 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>()
  87 + .select("contract_id, SUM(price) AS total_price")
  88 + .eq("audit_status", CrmAuditStatusEnum.APPROVE.getStatus())
  89 + .groupBy("contract_id")
  90 + .in("contract_id", contractIds));
  91 + // 获得金额
  92 + return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price"));
  93 + }
  94 +
70 } 95 }
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,6 +25,13 @@ import java.util.Objects;
25 @Mapper 25 @Mapper
26 public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> { 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 default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) { 35 default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {
29 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>(); 36 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
30 if (Objects.nonNull(reqVO.getContractNo())) { // 根据合同编号检索 37 if (Objects.nonNull(reqVO.getContractNo())) { // 根据合同编号检索
@@ -47,7 +54,6 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO @@ -47,7 +54,6 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
47 .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId()) 54 .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())
48 .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId()) 55 .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId())
49 .orderByDesc(CrmReceivablePlanDO::getPeriod); 56 .orderByDesc(CrmReceivablePlanDO::getPeriod);
50 -  
51 if (Objects.nonNull(pageReqVO.getContractNo())) { // 根据合同编号检索 57 if (Objects.nonNull(pageReqVO.getContractNo())) { // 根据合同编号检索
52 query.innerJoin(CrmContractDO.class, on -> on.like(CrmContractDO::getNo, pageReqVO.getContractNo()) 58 query.innerJoin(CrmContractDO.class, on -> on.like(CrmContractDO::getNo, pageReqVO.getContractNo())
53 .eq(CrmContractDO::getId, CrmReceivablePlanDO::getContractId)); 59 .eq(CrmContractDO::getId, CrmReceivablePlanDO::getContractId));
@@ -67,9 +73,7 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO @@ -67,9 +73,7 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
67 } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款 73 } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款
68 query.isNotNull(CrmReceivablePlanDO::getReceivableId) 74 query.isNotNull(CrmReceivablePlanDO::getReceivableId)
69 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS)); 75 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
70 - ;  
71 } 76 }
72 -  
73 return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query); 77 return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);
74 } 78 }
75 79
@@ -93,6 +97,9 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO @@ -93,6 +97,9 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
93 LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); 97 LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
94 query.isNull(CrmReceivablePlanDO::getReceivableId) 98 query.isNull(CrmReceivablePlanDO::getReceivableId)
95 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS)); 99 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
  100 + // TODO return_time 小于现在;
  101 + // TODO 未回款
  102 + // TODO remind_time 大于现在;
96 return selectCount(query); 103 return selectCount(query);
97 } 104 }
98 105
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
@@ -25,7 +25,7 @@ public class CrmNoRedisDAO { @@ -25,7 +25,7 @@ public class CrmNoRedisDAO {
25 public static final String CONTRACT_NO_PREFIX = "HT"; 25 public static final String CONTRACT_NO_PREFIX = "HT";
26 26
27 /** 27 /**
28 - * 还款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO} 28 + * 回款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}
29 */ 29 */
30 public static final String RECEIVABLE_PREFIX = "HK"; 30 public static final String RECEIVABLE_PREFIX = "HK";
31 31
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmReceivablePlanParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
  5 +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService;
  6 +import com.mzt.logapi.service.IParseFunction;
  7 +import jakarta.annotation.Resource;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +/**
  12 + * CRM 回款计划的 {@link IParseFunction} 实现类
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class CrmReceivablePlanParseFunction implements IParseFunction {
  19 +
  20 + public static final String NAME = "getReceivablePlanServiceById";
  21 +
  22 + @Resource
  23 + private CrmReceivablePlanService receivablePlanService;
  24 +
  25 + @Override
  26 + public boolean executeBefore() {
  27 + return true; // 先转换值后对比
  28 + }
  29 +
  30 + @Override
  31 + public String functionName() {
  32 + return NAME;
  33 + }
  34 +
  35 + @Override
  36 + public String apply(Object value) {
  37 + if (StrUtil.isEmptyIfStr(value)) {
  38 + return "";
  39 + }
  40 + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(Long.parseLong(value.toString()));
  41 + return receivablePlan == null ? "" : receivablePlan.getPeriod().toString();
  42 + }
  43 +
  44 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmReceivableReturnTypeParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
  5 +import com.mzt.logapi.service.IParseFunction;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_RECEIVABLE_RETURN_TYPE;
  10 +
  11 +/**
  12 + * CRM 回款方式的 {@link IParseFunction} 实现类
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Slf4j
  17 +@Component
  18 +public class CrmReceivableReturnTypeParseFunction implements IParseFunction {
  19 +
  20 + public static final String NAME = "getReceivableReturnType";
  21 +
  22 + @Override
  23 + public boolean executeBefore() {
  24 + return true; // 先转换值后对比
  25 + }
  26 +
  27 + @Override
  28 + public String functionName() {
  29 + return NAME;
  30 + }
  31 +
  32 + @Override
  33 + public String apply(Object value) {
  34 + if (StrUtil.isEmptyIfStr(value)) {
  35 + return "";
  36 + }
  37 + return DictFrameworkUtils.getDictDataLabel(CRM_RECEIVABLE_RETURN_TYPE, value.toString());
  38 + }
  39 +
  40 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -13,6 +13,9 @@ import jakarta.validation.Valid; @@ -13,6 +13,9 @@ import jakarta.validation.Valid;
13 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
14 import java.util.Collection; 14 import java.util.Collection;
15 import java.util.List; 15 import java.util.List;
  16 +import java.util.Map;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
16 19
17 /** 20 /**
18 * CRM 合同 Service 接口 21 * CRM 合同 Service 接口
@@ -86,6 +89,14 @@ public interface CrmContractService { @@ -86,6 +89,14 @@ public interface CrmContractService {
86 CrmContractDO getContract(Long id); 89 CrmContractDO getContract(Long id);
87 90
88 /** 91 /**
  92 + * 校验合同是否合法
  93 + *
  94 + * @param id 编号
  95 + * @return 合同
  96 + */
  97 + CrmContractDO validateContract(Long id);
  98 +
  99 + /**
89 * 获得合同列表 100 * 获得合同列表
90 * 101 *
91 * @param ids 编号 102 * @param ids 编号
@@ -94,6 +105,16 @@ public interface CrmContractService { @@ -94,6 +105,16 @@ public interface CrmContractService {
94 List<CrmContractDO> getContractList(Collection<Long> ids); 105 List<CrmContractDO> getContractList(Collection<Long> ids);
95 106
96 /** 107 /**
  108 + * 获得合同 Map
  109 + *
  110 + * @param ids 编号
  111 + * @return 合同 Map
  112 + */
  113 + default Map<Long, CrmContractDO> getContractMap(Collection<Long> ids) {
  114 + return convertMap(getContractList(ids), CrmContractDO::getId);
  115 + }
  116 +
  117 + /**
97 * 获得合同分页 118 * 获得合同分页
98 * 119 *
99 * 数据权限:基于 {@link CrmContractDO} 读取 120 * 数据权限:基于 {@link CrmContractDO} 读取
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,6 +324,11 @@ public class CrmContractServiceImpl implements CrmContractService {
324 } 324 }
325 325
326 @Override 326 @Override
  327 + public CrmContractDO validateContract(Long id) {
  328 + return validateContractExists(id);
  329 + }
  330 +
  331 + @Override
327 public List<CrmContractDO> getContractList(Collection<Long> ids) { 332 public List<CrmContractDO> getContractList(Collection<Long> ids) {
328 if (CollUtil.isEmpty(ids)) { 333 if (CollUtil.isEmpty(ids)) {
329 return ListUtil.empty(); 334 return ListUtil.empty();
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
@@ -38,7 +38,7 @@ public interface CrmReceivablePlanService { @@ -38,7 +38,7 @@ public interface CrmReceivablePlanService {
38 * @param id 编号 38 * @param id 编号
39 * @param receivableId 回款编号 39 * @param receivableId 回款编号
40 */ 40 */
41 - void updateReceivableId(Long id, Long receivableId); 41 + void updateReceivablePlanReceivableId(Long id, Long receivableId);
42 42
43 /** 43 /**
44 * 删除回款计划 44 * 删除回款计划
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,20 +2,17 @@ package cn.iocoder.yudao.module.crm.service.receivable;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
5 -import cn.hutool.core.util.ObjectUtil;  
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
11 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
12 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
13 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; 11 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
14 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
15 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 13 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
16 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 14 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
17 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 15 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
18 -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;  
19 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
20 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
21 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -33,7 +30,8 @@ import java.util.List; @@ -33,7 +30,8 @@ import java.util.List;
33 import java.util.Objects; 30 import java.util.Objects;
34 31
35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 35 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
38 36
39 /** 37 /**
@@ -54,8 +52,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -54,8 +52,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
54 @Resource 52 @Resource
55 private CrmContractService contractService; 53 private CrmContractService contractService;
56 @Resource 54 @Resource
57 - private CrmCustomerService customerService;  
58 - @Resource  
59 private CrmPermissionService permissionService; 55 private CrmPermissionService permissionService;
60 56
61 @Resource 57 @Resource
@@ -66,15 +62,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -66,15 +62,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
66 @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}", 62 @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}",
67 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS) 63 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
68 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) { 64 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
69 - // 1.1 校验关联数据是否存在 65 + // 1. 校验关联数据是否存在
70 validateRelationDataExists(createReqVO); 66 validateRelationDataExists(createReqVO);
71 - // 1.2 查验关联合同回款数量  
72 - Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId());  
73 - int period = (int) (count + 1);  
74 67
75 - // 2. 插入还款计划  
76 - CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class)  
77 - .setPeriod(period).setFinishStatus(false); 68 + // 2. 插入回款计划
  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 receivablePlanMapper.insert(receivablePlan); 75 receivablePlanMapper.insert(receivablePlan);
79 76
80 // 3. 创建数据权限 77 // 3. 创建数据权限
@@ -93,15 +90,21 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -93,15 +90,21 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
93 success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS) 90 success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)
94 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 91 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
95 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) { 92 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
96 - // 1. 校验存在 93 + updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null); // 防止修改这些字段
  94 + // 1.1 校验存在
97 validateRelationDataExists(updateReqVO); 95 validateRelationDataExists(updateReqVO);
  96 + // 1.2 校验关联数据是否存在
98 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId()); 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 CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class); 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 receivablePlanMapper.updateById(updateObj); 108 receivablePlanMapper.updateById(updateObj);
106 109
107 // 3. 记录操作日志上下文 110 // 3. 记录操作日志上下文
@@ -109,24 +112,24 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -109,24 +112,24 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
109 LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); 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 +
112 @Override 127 @Override
113 - public void updateReceivableId(Long id, Long receivableId) { 128 + public void updateReceivablePlanReceivableId(Long id, Long receivableId) {
114 // 校验存在 129 // 校验存在
115 validateReceivablePlanExists(id); 130 validateReceivablePlanExists(id);
116 // 更新回款计划 131 // 更新回款计划
117 - receivablePlanMapper.updateById(new CrmReceivablePlanDO().setReceivableId(receivableId).setFinishStatus(true));  
118 - }  
119 -  
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 - } 132 + receivablePlanMapper.updateById(new CrmReceivablePlanDO().setId(id).setReceivableId(receivableId));
130 } 133 }
131 134
132 @Override 135 @Override
@@ -135,13 +138,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -135,13 +138,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
135 success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS) 138 success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)
136 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 139 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
137 public void deleteReceivablePlan(Long id) { 140 public void deleteReceivablePlan(Long id) {
138 - // 校验存在 141 + // 1. 校验存在
139 CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id); 142 CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);
140 - // 删除 143 +
  144 + // 2. 删除
141 receivablePlanMapper.deleteById(id); 145 receivablePlanMapper.deleteById(id);
142 - // 删除数据权限 146 + // 3. 删除数据权限
143 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); 147 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
144 - // 记录操作日志上下文 148 +
  149 + // 4. 记录操作日志上下文
145 LogRecordContext.putVariable("receivablePlan", receivablePlan); 150 LogRecordContext.putVariable("receivablePlan", receivablePlan);
146 } 151 }
147 152
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
@@ -7,8 +7,12 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; @@ -7,8 +7,12 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
8 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
9 9
  10 +import java.math.BigDecimal;
10 import java.util.Collection; 11 import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
12 16
13 /** 17 /**
14 * CRM 回款 Service 接口 18 * CRM 回款 Service 接口
@@ -72,6 +76,16 @@ public interface CrmReceivableService { @@ -72,6 +76,16 @@ public interface CrmReceivableService {
72 List<CrmReceivableDO> getReceivableList(Collection<Long> ids); 76 List<CrmReceivableDO> getReceivableList(Collection<Long> ids);
73 77
74 /** 78 /**
  79 + * 获得回款 Map
  80 + *
  81 + * @param ids 编号
  82 + * @return 回款 Map
  83 + */
  84 + default Map<Long, CrmReceivableDO> getReceivableMap(Collection<Long> ids) {
  85 + return convertMap(getReceivableList(ids), CrmReceivableDO::getId);
  86 + }
  87 +
  88 + /**
75 * 获得回款分页 89 * 获得回款分页
76 * 90 *
77 * 数据权限:基于 {@link CrmReceivableDO} 读取 91 * 数据权限:基于 {@link CrmReceivableDO} 读取
@@ -101,11 +115,11 @@ public interface CrmReceivableService { @@ -101,11 +115,11 @@ public interface CrmReceivableService {
101 Long getCheckReceivablesCount(Long userId); 115 Long getCheckReceivablesCount(Long userId);
102 116
103 /** 117 /**
104 - * 根据合同编号,获取合同关联的回款数量 118 + * 获得合同已回款金额 Map
105 * 119 *
106 - * @param contractId 合同编号  
107 - * @return 数量 120 + * @param contractIds 合同编号
  121 + * @return 回款金额 Map
108 */ 122 */
109 - Long getReceivableCountByContractId(Long contractId); 123 + Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);
110 124
111 } 125 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -13,16 +13,15 @@ import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -13,16 +13,15 @@ import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 13 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
14 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
17 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
18 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
19 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; 18 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
  19 +import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
20 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; 20 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
21 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 21 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
22 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 22 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
23 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 23 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
25 -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;  
26 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 25 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
27 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 26 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
28 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 27 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -36,9 +35,10 @@ import org.springframework.stereotype.Service; @@ -36,9 +35,10 @@ import org.springframework.stereotype.Service;
36 import org.springframework.transaction.annotation.Transactional; 35 import org.springframework.transaction.annotation.Transactional;
37 import org.springframework.validation.annotation.Validated; 36 import org.springframework.validation.annotation.Validated;
38 37
  38 +import java.math.BigDecimal;
39 import java.util.Collection; 39 import java.util.Collection;
40 import java.util.List; 40 import java.util.List;
41 -import java.util.Objects; 41 +import java.util.Map;
42 42
43 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 43 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
44 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 44 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
@@ -56,17 +56,18 @@ import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmRes @@ -56,17 +56,18 @@ import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmRes
56 public class CrmReceivableServiceImpl implements CrmReceivableService { 56 public class CrmReceivableServiceImpl implements CrmReceivableService {
57 57
58 /** 58 /**
59 - * BPM 回款审批流程标识 59 + * BPM 合同审批流程标识
60 */ 60 */
61 - public static final String RECEIVABLE_APPROVE = "receivable-approve"; 61 + public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit";
62 62
63 @Resource 63 @Resource
64 private CrmReceivableMapper receivableMapper; 64 private CrmReceivableMapper receivableMapper;
65 65
66 @Resource 66 @Resource
67 - private CrmContractService contractService; 67 + private CrmNoRedisDAO noRedisDAO;
  68 +
68 @Resource 69 @Resource
69 - private CrmCustomerService customerService; 70 + private CrmContractService contractService;
70 @Resource 71 @Resource
71 @Lazy // 延迟加载,避免循环依赖 72 @Lazy // 延迟加载,避免循环依赖
72 private CrmReceivablePlanService receivablePlanService; 73 private CrmReceivablePlanService receivablePlanService;
@@ -78,48 +79,63 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -78,48 +79,63 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
78 @Resource 79 @Resource
79 private BpmProcessInstanceApi bpmProcessInstanceApi; 80 private BpmProcessInstanceApi bpmProcessInstanceApi;
80 81
  82 + // TODO @puhui999:操作日志没记录上
81 @Override 83 @Override
82 @Transactional(rollbackFor = Exception.class) 84 @Transactional(rollbackFor = Exception.class)
83 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", 85 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
84 success = CRM_RECEIVABLE_CREATE_SUCCESS) 86 success = CRM_RECEIVABLE_CREATE_SUCCESS)
85 public Long createReceivable(CrmReceivableSaveReqVO createReqVO) { 87 public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
86 - // 1. 校验关联数据存在 88 + // 1.1 校验关联数据存在
87 validateRelationDataExists(createReqVO); 89 validateRelationDataExists(createReqVO);
88 - // 2. 插入还款  
89 - CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus()); 90 + // 1.2 生成回款编号
  91 + String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
  92 + if (receivableMapper.selectByNo(no) != null) {
  93 + throw exception(RECEIVABLE_NO_EXISTS);
  94 + }
  95 +
  96 + // 2. 插入回款
  97 + CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
  98 + .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
90 receivableMapper.insert(receivable); 99 receivableMapper.insert(receivable);
  100 +
91 // 3. 创建数据权限 101 // 3. 创建数据权限
92 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()) 102 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
93 - .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 103 + .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId())
  104 + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  105 +
94 // 4. 更新关联的回款计划 106 // 4. 更新关联的回款计划
95 - if (Objects.nonNull(createReqVO.getPlanId())) {  
96 - receivablePlanService.updateReceivableId(receivable.getPlanId(), receivable.getId()); 107 + if (createReqVO.getPlanId() != null) {
  108 + receivablePlanService.updateReceivablePlanReceivableId(receivable.getPlanId(), receivable.getId());
97 } 109 }
  110 +
98 // 5. 记录操作日志上下文 111 // 5. 记录操作日志上下文
99 LogRecordContext.putVariable("receivable", receivable); 112 LogRecordContext.putVariable("receivable", receivable);
100 return receivable.getId(); 113 return receivable.getId();
101 } 114 }
102 115
103 private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) { 116 private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
104 - adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在  
105 - CrmContractDO contract = contractService.getContract(reqVO.getContractId());  
106 - if (ObjectUtil.isNull(contract)) {  
107 - throw exception(CONTRACT_NOT_EXISTS);  
108 - }  
109 - CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());  
110 - if (ObjectUtil.isNull(customer)) {  
111 - throw exception(CUSTOMER_NOT_EXISTS); 117 + if (reqVO.getOwnerUserId() != null) {
  118 + adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
112 } 119 }
113 - if (Objects.isNull(reqVO.getPlanId())) { // 没有回款计划编号则不校验  
114 - return; 120 + if (reqVO.getContractId() != null) {
  121 + CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
  122 + if (ObjectUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
  123 + throw exception(RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE);
  124 + }
  125 + reqVO.setCustomerId(contract.getCustomerId()); // 设置客户编号
115 } 126 }
116 - CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());  
117 - if (ObjectUtil.isNull(receivablePlan)) {  
118 - throw exception(RECEIVABLE_PLAN_NOT_EXISTS); 127 + if (reqVO.getPlanId() != null) {
  128 + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
  129 + if (receivablePlan == null) {
  130 + throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
  131 + }
  132 + if (receivablePlan.getReceivableId() != null) {
  133 + throw exception(RECEIVABLE_PLAN_EXISTS_RECEIVABLE);
  134 + }
119 } 135 }
120 -  
121 } 136 }
122 137
  138 + // TODO @puhui999:操作日志没记录上
123 @Override 139 @Override
124 @Transactional(rollbackFor = Exception.class) 140 @Transactional(rollbackFor = Exception.class)
125 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", 141 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
@@ -127,6 +143,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -127,6 +143,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
127 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 143 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
128 public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) { 144 public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
129 Assert.notNull(updateReqVO.getId(), "回款编号不能为空"); 145 Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
  146 + updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
130 // 1.1 校验存在 147 // 1.1 校验存在
131 CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId()); 148 CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
132 // 1.2 只有草稿、审批中,可以编辑; 149 // 1.2 只有草稿、审批中,可以编辑;
@@ -135,7 +152,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -135,7 +152,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
135 throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED); 152 throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
136 } 153 }
137 154
138 - // 2. 更新还款 155 + // 2. 更新回款
139 CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class); 156 CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);
140 receivableMapper.updateById(updateObj); 157 receivableMapper.updateById(updateObj);
141 158
@@ -160,27 +177,25 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -160,27 +177,25 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
160 receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus)); 177 receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));
161 } 178 }
162 179
163 - // TODO @liuhongfeng:缺一个取消回款的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum  
164 -  
165 @Override 180 @Override
166 @Transactional(rollbackFor = Exception.class) 181 @Transactional(rollbackFor = Exception.class)
167 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", 182 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
168 success = CRM_RECEIVABLE_DELETE_SUCCESS) 183 success = CRM_RECEIVABLE_DELETE_SUCCESS)
169 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 184 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
170 public void deleteReceivable(Long id) { 185 public void deleteReceivable(Long id) {
171 - // 校验存在 186 + // 1.1 校验存在
172 CrmReceivableDO receivable = validateReceivableExists(id); 187 CrmReceivableDO receivable = validateReceivableExists(id);
173 - // 如果被 CrmReceivablePlanDO 所使用,则不允许删除  
174 - if (Objects.nonNull(receivable.getPlanId()) && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) { 188 + // 1.2 如果被 CrmReceivablePlanDO 所使用,则不允许删除
  189 + if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
175 throw exception(RECEIVABLE_DELETE_FAIL); 190 throw exception(RECEIVABLE_DELETE_FAIL);
176 } 191 }
177 - // 删除  
178 - receivableMapper.deleteById(id);  
179 192
180 - // 删除数据权限 193 + // 2. 删除
  194 + receivableMapper.deleteById(id);
  195 + // 3. 删除数据权限
181 permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id); 196 permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
182 197
183 - // 记录操作日志上下文 198 + // 4. 记录操作日志上下文
184 LogRecordContext.putVariable("receivable", receivable); 199 LogRecordContext.putVariable("receivable", receivable);
185 } 200 }
186 201
@@ -197,7 +212,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -197,7 +212,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
197 212
198 // 2. 创建回款审批流程实例 213 // 2. 创建回款审批流程实例
199 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() 214 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
200 - .setProcessDefinitionKey(RECEIVABLE_APPROVE).setBusinessKey(String.valueOf(id))); 215 + .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
201 216
202 // 3. 更新回款工作流编号 217 // 3. 更新回款工作流编号
203 receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId) 218 receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)
@@ -246,8 +261,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -246,8 +261,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
246 } 261 }
247 262
248 @Override 263 @Override
249 - public Long getReceivableCountByContractId(Long contractId) {  
250 - return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId); 264 + public Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds) {
  265 + return receivableMapper.selectReceivablePriceMapByContractId(contractIds);
251 } 266 }
252 267
253 } 268 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java
@@ -20,7 +20,7 @@ public class CrmReceivableResultListener extends BpmProcessInstanceResultEventLi @@ -20,7 +20,7 @@ public class CrmReceivableResultListener extends BpmProcessInstanceResultEventLi
20 20
21 @Override 21 @Override
22 public String getProcessDefinitionKey() { 22 public String getProcessDefinitionKey() {
23 - return CrmReceivableServiceImpl.RECEIVABLE_APPROVE; 23 + return CrmReceivableServiceImpl.BPM_PROCESS_DEFINITION_KEY;
24 } 24 }
25 25
26 @Override 26 @Override