Commit c6b133d9192ffbc270f4be1954c36036602a9714

Authored by puhui999
1 parent b3cfad93

CRM: 完善回款

Showing 19 changed files with 409 additions and 232 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -32,6 +32,10 @@ public interface ErrorCodeConstants { @@ -32,6 +32,10 @@ public interface ErrorCodeConstants {
32 32
33 // ========== 回款 1-020-004-000 ========== 33 // ========== 回款 1-020-004-000 ==========
34 ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在"); 34 ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");
  35 + ErrorCode RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_004_001, "更新回款失败,原因:禁止编辑");
  36 + ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, "删除回款失败,原因: 被回款计划所使用,不允许删除");
  37 + ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, "回款提交审核失败,原因:回款没处在未提交状态");
  38 +
35 39
36 // ========== 回款计划 1-020-005-000 ========== 40 // ========== 回款计划 1-020-005-000 ==========
37 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在"); 41 ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -140,6 +140,8 @@ public interface LogRecordConstants { @@ -140,6 +140,8 @@ public interface LogRecordConstants {
140 String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}"; 140 String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
141 String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款"; 141 String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
142 String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款"; 142 String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
  143 + String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = "提交回款审批";
  144 + String CRM_RECEIVABLE_SUBMIT_SUCCESS = "提交编号为【{{#receivableNo}}】的回款审批成功";
143 145
144 // ======================= CRM_RECEIVABLE_PLAN 回款计划 ======================= 146 // ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================
145 147
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReturnTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.receivable;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * CRM 回款方式枚举
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum CrmReturnTypeEnum implements IntArrayValuable {
  17 +
  18 + // 支票
  19 + CHECK(1, "支票"),
  20 + // 现金
  21 + CASH(2, "现金"),
  22 + // 邮政汇款
  23 + POSTAL_REMITTANCE(3, "邮政汇款"),
  24 + // 电汇
  25 + TELEGRAPHIC_TRANSFER(4, "电汇"),
  26 + // 网上转账
  27 + ONLINE_TRANSFER(5, "网上转账"),
  28 + // 支付宝
  29 + ALIPAY(6, "支付宝"),
  30 + // 微信支付
  31 + WECHAT_PAY(7, "微信支付"),
  32 + // 其他
  33 + OTHER(8, "其它");
  34 +
  35 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmReturnTypeEnum::getType).toArray();
  36 +
  37 + /**
  38 + * 类型
  39 + */
  40 + private final Integer type;
  41 + /**
  42 + * 名称
  43 + */
  44 + private final String name;
  45 +
  46 + @Override
  47 + public int[] array() {
  48 + return ARRAYS;
  49 + }
  50 +
  51 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -5,12 +5,12 @@ import cn.hutool.core.lang.Assert; @@ -5,12 +5,12 @@ 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.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;  
11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 11 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; 12 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
13 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 13 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; 14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
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;
@@ -61,14 +61,14 @@ public class CrmReceivableController { @@ -61,14 +61,14 @@ public class CrmReceivableController {
61 @PostMapping("/create") 61 @PostMapping("/create")
62 @Operation(summary = "创建回款") 62 @Operation(summary = "创建回款")
63 @PreAuthorize("@ss.hasPermission('crm:receivable:create')") 63 @PreAuthorize("@ss.hasPermission('crm:receivable:create')")
64 - public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableCreateReqVO createReqVO) {  
65 - return success(receivableService.createReceivable(createReqVO, getLoginUserId())); 64 + public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableSaveReqVO createReqVO) {
  65 + return success(receivableService.createReceivable(createReqVO));
66 } 66 }
67 67
68 @PutMapping("/update") 68 @PutMapping("/update")
69 @Operation(summary = "更新回款") 69 @Operation(summary = "更新回款")
70 @PreAuthorize("@ss.hasPermission('crm:receivable:update')") 70 @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
71 - public CommonResult<Boolean> updateReceivable(@Valid @RequestBody CrmReceivableUpdateReqVO updateReqVO) { 71 + public CommonResult<Boolean> updateReceivable(@Valid @RequestBody CrmReceivableSaveReqVO updateReqVO) {
72 receivableService.updateReceivable(updateReqVO); 72 receivableService.updateReceivable(updateReqVO);
73 return success(true); 73 return success(true);
74 } 74 }
@@ -88,7 +88,7 @@ public class CrmReceivableController { @@ -88,7 +88,7 @@ public class CrmReceivableController {
88 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 88 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
89 public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) { 89 public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) {
90 CrmReceivableDO receivable = receivableService.getReceivable(id); 90 CrmReceivableDO receivable = receivableService.getReceivable(id);
91 - return success(CrmReceivableConvert.INSTANCE.convert(receivable)); 91 + return success(BeanUtils.toBean(receivable, CrmReceivableRespVO.class));
92 } 92 }
93 93
94 @GetMapping("/page") 94 @GetMapping("/page")
@@ -152,4 +152,12 @@ public class CrmReceivableController { @@ -152,4 +152,12 @@ public class CrmReceivableController {
152 return success(receivableService.getCheckReceivablesCount(getLoginUserId())); 152 return success(receivableService.getCheckReceivablesCount(getLoginUserId()));
153 } 153 }
154 154
  155 + @PutMapping("/submit")
  156 + @Operation(summary = "提交回款审批")
  157 + @PreAuthorize("@ss.hasPermission('crm:receivable:submit')")
  158 + public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
  159 + receivableService.submitReceivable(id, getLoginUserId());
  160 + return success(true);
  161 + }
  162 +
155 } 163 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
@@ -3,40 +3,41 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; @@ -3,40 +3,41 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
  6 +import java.math.BigDecimal;
6 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
7 8
8 @Schema(description = "管理后台 - CRM 回款计划 Response VO") 9 @Schema(description = "管理后台 - CRM 回款计划 Response VO")
9 @Data 10 @Data
10 public class CrmReceivablePlanRespVO { 11 public class CrmReceivablePlanRespVO {
11 12
12 - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153") 13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
13 private Long id; 14 private Long id;
14 15
15 - @Schema(description = "期数", example = "1")  
16 - private Integer period;  
17 -  
18 - @Schema(description = "回款计划编号", example = "19852") 16 + @Schema(description = "回款编号", example = "19852")
19 private Long receivableId; 17 private Long receivableId;
20 18
21 - @Schema(description = "计划回款金额", example = "29675")  
22 - private Integer price; 19 + @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  20 + private Integer period;
  21 +
  22 + @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  23 + private BigDecimal price;
23 24
24 - @Schema(description = "计划回款日期") 25 + @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
25 private LocalDateTime returnTime; 26 private LocalDateTime returnTime;
26 27
27 - @Schema(description = "提前几天提醒") 28 + @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
28 private Integer remindDays; 29 private Integer remindDays;
29 30
30 - @Schema(description = "提醒日期") 31 + @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
31 private LocalDateTime remindTime; 32 private LocalDateTime remindTime;
32 33
33 - @Schema(description = "客户名称", example = "18026") 34 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
34 private Long customerId; 35 private Long customerId;
35 36
36 - @Schema(description = "合同编号", example = "3473") 37 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
37 private Long contractId; 38 private Long contractId;
38 39
39 - @Schema(description = "负责人编号", example = "17828") 40 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
40 private Long ownerUserId; 41 private Long ownerUserId;
41 42
42 @Schema(description = "显示顺序") 43 @Schema(description = "显示顺序")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableBaseVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;  
2 -  
3 -import cn.iocoder.yudao.framework.common.validation.InEnum;  
4 -import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;  
5 -import io.swagger.v3.oas.annotations.media.Schema;  
6 -import lombok.Data;  
7 -import org.springframework.format.annotation.DateTimeFormat;  
8 -  
9 -import java.time.LocalDateTime;  
10 -  
11 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;  
12 -  
13 -/**  
14 - * 回款 Base VO,提供给添加、修改、详细的子 VO 使用  
15 - * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
16 - */  
17 -@Data  
18 -public class CrmReceivableBaseVO {  
19 -  
20 - @Schema(description = "回款编号",requiredMode = Schema.RequiredMode.REQUIRED, example = "31177")  
21 - private String no;  
22 -  
23 - // TODO @liuhongfeng:回款计划编号  
24 - @Schema(description = "回款计划", example = "31177")  
25 - private Long planId;  
26 -  
27 - // TODO @liuhongfeng:客户编号  
28 - @Schema(description = "客户名称", example = "4963")  
29 - private Long customerId;  
30 -  
31 - // TODO @liuhongfeng:客户编号  
32 - @Schema(description = "合同名称", example = "30305")  
33 - private Long contractId;  
34 -  
35 - // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的  
36 - @Schema(description = "审批状态", example = "1")  
37 - @InEnum(CrmAuditStatusEnum.class)  
38 - private Integer checkStatus;  
39 -  
40 - @Schema(description = "回款日期")  
41 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
42 - private LocalDateTime returnTime;  
43 -  
44 - @Schema(description = "回款方式", example = "2")  
45 - private Integer returnType;  
46 -  
47 - @Schema(description = "回款金额,单位:分", example = "31859")  
48 - private Integer price;  
49 -  
50 - // TODO @liuhongfeng:负责人编号  
51 - @Schema(description = "负责人", example = "22202")  
52 - private Long ownerUserId;  
53 -  
54 - @Schema(description = "显示顺序")  
55 - private Integer sort;  
56 -  
57 - @Schema(description = "备注", example = "备注")  
58 - private String remark;  
59 -  
60 -  
61 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableCreateReqVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;  
2 -  
3 -import lombok.*;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -  
6 -@Schema(description = "管理后台 - CRM 回款创建 Request VO")  
7 -@Data  
8 -@EqualsAndHashCode(callSuper = true)  
9 -@ToString(callSuper = true)  
10 -public class CrmReceivableCreateReqVO extends CrmReceivableBaseVO {  
11 -  
12 -}  
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 io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 -import lombok.*; 4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
5 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
6 8
7 // TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能 9 // TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能
8 @Schema(description = "管理后台 - CRM 回款 Response VO") 10 @Schema(description = "管理后台 - CRM 回款 Response VO")
9 @Data 11 @Data
10 -@EqualsAndHashCode(callSuper = true)  
11 -@ToString(callSuper = true)  
12 -public class CrmReceivableRespVO extends CrmReceivableBaseVO { 12 +public class CrmReceivableRespVO {
13 13
14 - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") 14 + @Schema(description = "编号", example = "25787")
15 private Long id; 15 private Long id;
16 16
  17 + @Schema(description = "回款编号", example = "31177")
  18 + private String no;
  19 +
  20 + @Schema(description = "回款计划编号", example = "1024")
  21 + private Long planId;
  22 +
  23 + @Schema(description = "回款方式", example = "2")
  24 + private Integer returnType;
  25 +
  26 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  27 + private BigDecimal price;
  28 +
  29 + @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  30 + private LocalDateTime returnTime;
  31 +
  32 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  33 + private Long customerId;
  34 +
  35 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  36 + private Long contractId;
  37 +
  38 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  39 + private Long ownerUserId;
  40 +
  41 + @Schema(description = "显示顺序")
  42 + private Integer sort;
  43 +
  44 + @Schema(description = "备注", example = "备注")
  45 + private String remark;
  46 +
17 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 47 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
18 private LocalDateTime createTime; 48 private LocalDateTime createTime;
19 49
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +import java.math.BigDecimal;
  10 +import java.time.LocalDateTime;
  11 +
  12 +@Schema(description = "管理后台 - CRM 回款新增/修改 Request VO")
  13 +@Data
  14 +public class CrmReceivableSaveReqVO {
  15 +
  16 + @Schema(description = "编号", example = "25787")
  17 + private Long id;
  18 +
  19 + @Schema(description = "回款编号", example = "31177")
  20 + private String no;
  21 +
  22 + @Schema(description = "回款计划编号", example = "1024")
  23 + private Long planId; // 不是通过回款计划创建的回款没有回款计划编号
  24 +
  25 + @Schema(description = "回款方式", example = "2")
  26 + @InEnum(CrmReturnTypeEnum.class)
  27 + private Integer returnType;
  28 +
  29 + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
  30 + @NotNull(message = "回款金额不能为空")
  31 + private BigDecimal price;
  32 +
  33 + @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
  34 + @NotNull(message = "计划回款日期不能为空")
  35 + private LocalDateTime returnTime;
  36 +
  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 = "显示顺序")
  50 + private Integer sort;
  51 +
  52 + @Schema(description = "备注", example = "备注")
  53 + private String remark;
  54 +
  55 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableUpdateReqVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.*;  
5 -  
6 -import jakarta.validation.constraints.*;  
7 -  
8 -@Schema(description = "管理后台 - CRM 回款更新 Request VO")  
9 -@Data  
10 -@EqualsAndHashCode(callSuper = true)  
11 -@ToString(callSuper = true)  
12 -public class CrmReceivableUpdateReqVO extends CrmReceivableBaseVO {  
13 -  
14 - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787")  
15 - @NotNull(message = "ID不能为空")  
16 - private Long id;  
17 -  
18 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
@@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.crm.convert.receivable; @@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.crm.convert.receivable;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;  
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
7 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;  
8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
@@ -28,12 +26,6 @@ public interface CrmReceivableConvert { @@ -28,12 +26,6 @@ public interface CrmReceivableConvert {
28 26
29 CrmReceivableConvert INSTANCE = Mappers.getMapper(CrmReceivableConvert.class); 27 CrmReceivableConvert INSTANCE = Mappers.getMapper(CrmReceivableConvert.class);
30 28
31 - CrmReceivableDO convert(CrmReceivableCreateReqVO bean);  
32 -  
33 - CrmReceivableDO convert(CrmReceivableUpdateReqVO bean);  
34 -  
35 - CrmReceivableRespVO convert(CrmReceivableDO bean);  
36 -  
37 default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 29 default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
38 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) { 30 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) {
39 PageResult<CrmReceivableRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class); 31 PageResult<CrmReceivableRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java
@@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.receivable; @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 4 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
  7 +import cn.iocoder.yudao.module.crm.enums.receivable.CrmReturnTypeEnum;
  8 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 9 import com.baomidou.mybatisplus.annotation.KeySequence;
6 import com.baomidou.mybatisplus.annotation.TableId; 10 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 11 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.*; 12 import lombok.*;
9 13
  14 +import java.math.BigDecimal;
10 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
11 16
12 /** 17 /**
@@ -33,64 +38,55 @@ public class CrmReceivableDO extends BaseDO { @@ -33,64 +38,55 @@ public class CrmReceivableDO extends BaseDO {
33 * 回款编号 38 * 回款编号
34 */ 39 */
35 private String no; 40 private String no;
36 - // TODO @liuhongfeng:“对应实体”,参考别的模块,关联 {@link TableField.MetaInfo#getJdbcType()}  
37 /** 41 /**
38 - * 回款计划  
39 - *  
40 - * TODO @liuhongfeng:这个字段什么时候更新,也可以写下  
41 - *  
42 - * 对应实体 {@link CrmReceivablePlanDO} 42 + * 回款计划编号,关联 {@link CrmReceivablePlanDO#getId()}
43 */ 43 */
44 private Long planId; 44 private Long planId;
45 /** 45 /**
46 - * 客户 ID  
47 - *  
48 - * 对应实体 {@link cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO} 46 + * 客户编号,关联 {@link CrmCustomerDO#getId()}
49 */ 47 */
50 private Long customerId; 48 private Long customerId;
51 /** 49 /**
52 - * 合同 ID  
53 - *  
54 - * 对应实体 {@link CrmContractDO} 50 + * 合同编号,关联 {@link CrmContractDO#getId()}
55 */ 51 */
56 private Long contractId; 52 private Long contractId;
57 /** 53 /**
58 - * 工作流编号  
59 - *  
60 - * TODO @liuhongfeng:这个字段,后续要写下关联的实体哈 54 + * 负责人编号,关联 {@link AdminUserRespDTO#getId()}
61 */ 55 */
62 - private Long processInstanceId; 56 + private Long ownerUserId;
  57 +
63 /** 58 /**
64 * 回款日期 59 * 回款日期
65 */ 60 */
66 private LocalDateTime returnTime; 61 private LocalDateTime returnTime;
67 - // TODO @liuhongfeng:少个枚举  
68 /** 62 /**
69 - * 回款方式 63 + * 回款方式,关联枚举{@link CrmReturnTypeEnum}
70 */ 64 */
71 private Integer returnType; 65 private Integer returnType;
72 /** 66 /**
73 - * 回款金额 67 + * 计划回款金额,单位:元
74 */ 68 */
75 - private Integer price;  
76 - // TODO @liuhongfeng:少关联实体;  
77 - /**  
78 - * 负责人  
79 - */  
80 - private Long ownerUserId; 69 + private BigDecimal price;
81 /** 70 /**
82 * 显示顺序 71 * 显示顺序
83 */ 72 */
84 private Integer sort; 73 private Integer sort;
85 /** 74 /**
86 - * 审核状态 75 + * 备注
  76 + */
  77 + private String remark;
  78 +
  79 + /**
  80 + * 工作流编号
87 * 81 *
88 - * 枚举 {@link cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum} 82 + * 关联 ProcessInstance 的 id 属性
89 */ 83 */
90 - private Integer auditStatus; 84 + private String processInstanceId;
91 /** 85 /**
92 - * 备注 86 + * 审批状态
  87 + *
  88 + * 枚举 {@link CrmAuditStatusEnum}
93 */ 89 */
94 - private String remark; 90 + private Integer auditStatus;
95 91
96 } 92 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -11,7 +11,6 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; @@ -11,7 +11,6 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; 11 import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; 12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; 13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
14 -import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;  
15 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
16 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; 15 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
17 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 16 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@@ -50,6 +49,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -50,6 +49,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
50 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
51 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 50 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
52 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 51 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
  52 +import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertAuditStatus;
53 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; 53 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
54 54
55 /** 55 /**
@@ -284,36 +284,12 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -284,36 +284,12 @@ public class CrmContractServiceImpl implements CrmContractService {
284 284
285 @Override 285 @Override
286 public void updateContractAuditStatus(BpmResultListenerRespDTO event) { 286 public void updateContractAuditStatus(BpmResultListenerRespDTO event) {
287 - // 判断下状态是否符合预期  
288 - if (!isEndResult(event.getResult())) {  
289 - return;  
290 - }  
291 - // 状态转换  
292 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {  
293 - event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());  
294 - }  
295 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {  
296 - event.setResult(CrmAuditStatusEnum.REJECT.getStatus());  
297 - }  
298 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {  
299 - event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());  
300 - } 287 + convertAuditStatus(event);
301 // 更新合同状态 288 // 更新合同状态
302 contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())) 289 contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))
303 .setAuditStatus(event.getResult())); 290 .setAuditStatus(event.getResult()));
304 } 291 }
305 292
306 - /**  
307 - * 判断该结果是否处于 End 最终结果  
308 - *  
309 - * @param result 结果  
310 - * @return 是否  
311 - */  
312 - public static boolean isEndResult(Integer result) {  
313 - return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),  
314 - BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());  
315 - }  
316 -  
317 //======================= 查询相关 ======================= 293 //======================= 查询相关 =======================
318 294
319 @Override 295 @Override
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
@@ -33,6 +33,14 @@ public interface CrmReceivablePlanService { @@ -33,6 +33,14 @@ public interface CrmReceivablePlanService {
33 void updateReceivablePlan(@Valid CrmReceivablePlanSaveReqVO updateReqVO); 33 void updateReceivablePlan(@Valid CrmReceivablePlanSaveReqVO updateReqVO);
34 34
35 /** 35 /**
  36 + * 更新回款计划关联的回款编号
  37 + *
  38 + * @param id 编号
  39 + * @param receivableId 回款编号
  40 + */
  41 + void updateReceivableId(Long id, Long receivableId);
  42 +
  43 + /**
36 * 删除回款计划 44 * 删除回款计划
37 * 45 *
38 * @param id 编号 46 * @param id 编号
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
@@ -66,7 +66,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -66,7 +66,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
66 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS) 66 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
67 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) { 67 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
68 // 1.1 校验关联数据是否存在 68 // 1.1 校验关联数据是否存在
69 - checkReceivablePlan(createReqVO); 69 + validateRelationDataExists(createReqVO);
70 // 1.2 查验关联合同回款数量 70 // 1.2 查验关联合同回款数量
71 Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId()); 71 Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId());
72 int period = (int) (count + 1); 72 int period = (int) (count + 1);
@@ -92,7 +92,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -92,7 +92,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
92 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 92 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
93 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) { 93 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
94 // 1. 校验存在 94 // 1. 校验存在
95 - checkReceivablePlan(updateReqVO); 95 + validateRelationDataExists(updateReqVO);
96 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId()); 96 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
97 if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑; 97 if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑;
98 throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款"); 98 throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款");
@@ -107,7 +107,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -107,7 +107,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
107 LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); 107 LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
108 } 108 }
109 109
110 - private void checkReceivablePlan(CrmReceivablePlanSaveReqVO reqVO) { 110 + @Override
  111 + public void updateReceivableId(Long id, Long receivableId) {
  112 + // 校验存在
  113 + validateReceivablePlanExists(id);
  114 + // 更新回款计划
  115 + receivablePlanMapper.updateById(new CrmReceivablePlanDO().setReceivableId(receivableId).setFinishStatus(true));
  116 + }
  117 +
  118 + private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) {
111 adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在 119 adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
112 CrmContractDO contract = contractService.getContract(reqVO.getContractId()); 120 CrmContractDO contract = contractService.getContract(reqVO.getContractId());
113 if (ObjectUtil.isNull(contract)) { // 合同不存在 121 if (ObjectUtil.isNull(contract)) { // 合同不存在
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
1 package cn.iocoder.yudao.module.crm.service.receivable; 1 package cn.iocoder.yudao.module.crm.service.receivable;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; 4 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
6 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 6 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
9 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
@@ -22,17 +22,23 @@ public interface CrmReceivableService { @@ -22,17 +22,23 @@ public interface CrmReceivableService {
22 * 创建回款 22 * 创建回款
23 * 23 *
24 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
25 - * @param userId 用户编号  
26 * @return 编号 25 * @return 编号
27 */ 26 */
28 - Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO, Long userId); 27 + Long createReceivable(@Valid CrmReceivableSaveReqVO createReqVO);
29 28
30 /** 29 /**
31 * 更新回款 30 * 更新回款
32 * 31 *
33 * @param updateReqVO 更新信息 32 * @param updateReqVO 更新信息
34 */ 33 */
35 - void updateReceivable(@Valid CrmReceivableUpdateReqVO updateReqVO); 34 + void updateReceivable(@Valid CrmReceivableSaveReqVO updateReqVO);
  35 +
  36 + /**
  37 + * 更新回款流程审批结果
  38 + *
  39 + * @param event 审批结果
  40 + */
  41 + void updateReceivableAuditStatus(BpmResultListenerRespDTO event);
36 42
37 /** 43 /**
38 * 删除回款 44 * 删除回款
@@ -42,6 +48,14 @@ public interface CrmReceivableService { @@ -42,6 +48,14 @@ public interface CrmReceivableService {
42 void deleteReceivable(Long id); 48 void deleteReceivable(Long id);
43 49
44 /** 50 /**
  51 + * 发起回款审批流程
  52 + *
  53 + * @param id 回款编号
  54 + * @param userId 用户编号
  55 + */
  56 + void submitReceivable(Long id, Long userId);
  57 +
  58 + /**
45 * 获得回款 59 * 获得回款
46 * 60 *
47 * @param id 编号 61 * @param id 编号
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -2,14 +2,17 @@ package cn.iocoder.yudao.module.crm.service.receivable; @@ -2,14 +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.lang.Assert;
  6 +import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.core.util.ObjectUtil; 7 import cn.hutool.core.util.ObjectUtil;
6 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; 10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  11 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  12 +import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
  13 +import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
10 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
11 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;  
12 -import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; 15 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 18 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
@@ -23,19 +26,23 @@ import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; @@ -23,19 +26,23 @@ import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 26 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 27 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
25 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 28 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  29 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
26 import com.mzt.logapi.context.LogRecordContext; 30 import com.mzt.logapi.context.LogRecordContext;
27 import com.mzt.logapi.service.impl.DiffParseFunction; 31 import com.mzt.logapi.service.impl.DiffParseFunction;
28 import com.mzt.logapi.starter.annotation.LogRecord; 32 import com.mzt.logapi.starter.annotation.LogRecord;
29 import jakarta.annotation.Resource; 33 import jakarta.annotation.Resource;
30 import org.springframework.stereotype.Service; 34 import org.springframework.stereotype.Service;
  35 +import org.springframework.transaction.annotation.Transactional;
31 import org.springframework.validation.annotation.Validated; 36 import org.springframework.validation.annotation.Validated;
32 37
33 import java.util.Collection; 38 import java.util.Collection;
34 import java.util.List; 39 import java.util.List;
  40 +import java.util.Objects;
35 41
36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 42 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
37 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 43 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
38 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 44 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
  45 +import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertAuditStatus;
39 46
40 /** 47 /**
41 * CRM 回款 Service 实现类 48 * CRM 回款 Service 实现类
@@ -46,6 +53,11 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; @@ -46,6 +53,11 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
46 @Validated 53 @Validated
47 public class CrmReceivableServiceImpl implements CrmReceivableService { 54 public class CrmReceivableServiceImpl implements CrmReceivableService {
48 55
  56 + /**
  57 + * BPM 回款审批流程标识
  58 + */
  59 + public static final String RECEIVABLE_APPROVE = "receivable-approve";
  60 +
49 @Resource 61 @Resource
50 private CrmReceivableMapper receivableMapper; 62 private CrmReceivableMapper receivableMapper;
51 63
@@ -57,51 +69,47 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -57,51 +69,47 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
57 private CrmReceivablePlanService receivablePlanService; 69 private CrmReceivablePlanService receivablePlanService;
58 @Resource 70 @Resource
59 private CrmPermissionService permissionService; 71 private CrmPermissionService permissionService;
  72 + @Resource
  73 + private AdminUserApi adminUserApi;
  74 + @Resource
  75 + private BpmProcessInstanceApi bpmProcessInstanceApi;
60 76
61 @Override 77 @Override
  78 + @Transactional(rollbackFor = Exception.class)
62 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", 79 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
63 success = CRM_RECEIVABLE_CREATE_SUCCESS) 80 success = CRM_RECEIVABLE_CREATE_SUCCESS)
64 - public Long createReceivable(CrmReceivableCreateReqVO createReqVO, Long userId) {  
65 - // 插入还款  
66 - CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO);  
67 - if (ObjectUtil.isNull(receivable.getAuditStatus())) {  
68 - receivable.setAuditStatus(CommonStatusEnum.ENABLE.getStatus());  
69 - }  
70 - receivable.setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());  
71 -  
72 - // TODO @liuhongfeng:一般来说,逻辑的写法,是要先检查,后操作 db;所以,你这个 check 应该放到 CrmReceivableDO receivable 之前;  
73 - checkReceivable(receivable);  
74 - 81 + public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
  82 + // 1. 校验关联数据存在
  83 + validateRelationDataExists(createReqVO);
  84 + // 2. 插入还款
  85 + CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
75 receivableMapper.insert(receivable); 86 receivableMapper.insert(receivable);
76 // 3. 创建数据权限 87 // 3. 创建数据权限
77 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()) 88 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
78 - .setBizId(receivable.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人  
79 - // TODO @liuhongfeng:需要更新关联的 plan  
80 -  
81 - // 4. 记录操作日志上下文 89 + .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  90 + // 4. 更新关联的回款计划
  91 + if (Objects.nonNull(createReqVO.getPlanId())) {
  92 + receivablePlanService.updateReceivableId(receivable.getPlanId(), receivable.getId());
  93 + }
  94 + // 5. 记录操作日志上下文
82 LogRecordContext.putVariable("receivable", receivable); 95 LogRecordContext.putVariable("receivable", receivable);
83 return receivable.getId(); 96 return receivable.getId();
84 } 97 }
85 98
86 - // TODO @liuhongfeng:这里的括号要注意排版;  
87 - private void checkReceivable(CrmReceivableDO receivable) {  
88 - // TODO @liuhongfeng:校验 no 的唯一性  
89 - // TODO @liuhongfeng:这个放在参数校验合适  
90 - if (ObjectUtil.isNull(receivable.getContractId())) {  
91 - throw exception(CONTRACT_NOT_EXISTS);  
92 - }  
93 -  
94 - CrmContractDO contract = contractService.getContract(receivable.getContractId()); 99 + private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
  100 + adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
  101 + CrmContractDO contract = contractService.getContract(reqVO.getContractId());
95 if (ObjectUtil.isNull(contract)) { 102 if (ObjectUtil.isNull(contract)) {
96 throw exception(CONTRACT_NOT_EXISTS); 103 throw exception(CONTRACT_NOT_EXISTS);
97 } 104 }
98 -  
99 - CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId()); 105 + CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());
100 if (ObjectUtil.isNull(customer)) { 106 if (ObjectUtil.isNull(customer)) {
101 throw exception(CUSTOMER_NOT_EXISTS); 107 throw exception(CUSTOMER_NOT_EXISTS);
102 } 108 }
103 -  
104 - CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(receivable.getPlanId()); 109 + if (Objects.isNull(reqVO.getPlanId())) { // 没有回款计划编号则不校验
  110 + return;
  111 + }
  112 + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
105 if (ObjectUtil.isNull(receivablePlan)) { 113 if (ObjectUtil.isNull(receivablePlan)) {
106 throw exception(RECEIVABLE_PLAN_NOT_EXISTS); 114 throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
107 } 115 }
@@ -109,46 +117,84 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -109,46 +117,84 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
109 } 117 }
110 118
111 @Override 119 @Override
  120 + @Transactional(rollbackFor = Exception.class)
112 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", 121 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
113 success = CRM_RECEIVABLE_UPDATE_SUCCESS) 122 success = CRM_RECEIVABLE_UPDATE_SUCCESS)
114 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 123 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
115 - public void updateReceivable(CrmReceivableUpdateReqVO updateReqVO) {  
116 - // 校验存在  
117 - CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());  
118 - // TODO @liuhongfeng:只有在草稿、审核中,可以提交修改 124 + public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
  125 + Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
  126 + // 1.1 校验存在
  127 + CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
  128 + // 1.2 只有草稿、审批中,可以编辑;
  129 + if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
  130 + CrmAuditStatusEnum.PROCESS.getStatus())) {
  131 + throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
  132 + }
119 133
120 - // 更新还款  
121 - CrmReceivableDO updateObj = CrmReceivableConvert.INSTANCE.convert(updateReqVO); 134 + // 2. 更新还款
  135 + CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);
122 receivableMapper.updateById(updateObj); 136 receivableMapper.updateById(updateObj);
123 137
124 - // TODO @liuhongfeng:需要更新关联的 plan  
125 // 3. 记录操作日志上下文 138 // 3. 记录操作日志上下文
126 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableUpdateReqVO.class));  
127 - LogRecordContext.putVariable("receivable", oldReceivable); 139 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class));
  140 + LogRecordContext.putVariable("receivable", receivable);
128 } 141 }
129 142
130 - // TODO @liuhongfeng:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum 143 + @Override
  144 + public void updateReceivableAuditStatus(BpmResultListenerRespDTO event) {
  145 + convertAuditStatus(event);
  146 + // 更新回款审批状态
  147 + receivableMapper.updateById(new CrmReceivableDO().setId(Long.parseLong(event.getBusinessKey()))
  148 + .setAuditStatus(event.getResult()));
  149 + }
131 150
132 - // TODO @liuhongfeng:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum 151 + // TODO @liuhongfeng:缺一个取消回款的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
133 152
134 @Override 153 @Override
  154 + @Transactional(rollbackFor = Exception.class)
135 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", 155 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
136 success = CRM_RECEIVABLE_DELETE_SUCCESS) 156 success = CRM_RECEIVABLE_DELETE_SUCCESS)
137 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 157 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
138 public void deleteReceivable(Long id) { 158 public void deleteReceivable(Long id) {
139 - // TODO @liuhongfeng:如果被 CrmReceivablePlanDO 所使用,则不允许删除  
140 // 校验存在 159 // 校验存在
141 CrmReceivableDO receivable = validateReceivableExists(id); 160 CrmReceivableDO receivable = validateReceivableExists(id);
  161 + // 如果被 CrmReceivablePlanDO 所使用,则不允许删除
  162 + if (Objects.nonNull(receivable.getPlanId()) && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
  163 + throw exception(RECEIVABLE_DELETE_FAIL);
  164 + }
142 // 删除 165 // 删除
143 receivableMapper.deleteById(id); 166 receivableMapper.deleteById(id);
144 167
145 // 删除数据权限 168 // 删除数据权限
146 - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); 169 + permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
147 170
148 // 记录操作日志上下文 171 // 记录操作日志上下文
149 LogRecordContext.putVariable("receivable", receivable); 172 LogRecordContext.putVariable("receivable", receivable);
150 } 173 }
151 174
  175 + @Override
  176 + @Transactional(rollbackFor = Exception.class)
  177 + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
  178 + success = CRM_RECEIVABLE_SUBMIT_SUCCESS)
  179 + public void submitReceivable(Long id, Long userId) {
  180 + // 1. 校验回款是否在审批
  181 + CrmReceivableDO receivable = validateReceivableExists(id);
  182 + if (ObjUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
  183 + throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
  184 + }
  185 +
  186 + // 2. 创建回款审批流程实例
  187 + String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
  188 + .setProcessDefinitionKey(RECEIVABLE_APPROVE).setBusinessKey(String.valueOf(id)));
  189 +
  190 + // 3. 更新回款工作流编号
  191 + receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId)
  192 + .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
  193 +
  194 + // 4. 记录日志
  195 + LogRecordContext.putVariable("receivableNo", receivable.getNo());
  196 + }
  197 +
152 private CrmReceivableDO validateReceivableExists(Long id) { 198 private CrmReceivableDO validateReceivableExists(Long id) {
153 CrmReceivableDO receivable = receivableMapper.selectById(id); 199 CrmReceivableDO receivable = receivableMapper.selectById(id);
154 if (receivable == null) { 200 if (receivable == null) {
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.receivable.listener;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;
  4 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  5 +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
  6 +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableServiceImpl;
  7 +import jakarta.annotation.Resource;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +// TODO @芋艿:后续改成支持 RPC
  11 +
  12 +/**
  13 + * 回款审批的结果的监听器实现类
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class CrmReceivableResultListener implements BpmResultListenerApi {
  19 +
  20 + @Resource
  21 + private CrmReceivableService receivableService;
  22 +
  23 + @Override
  24 + public String getProcessDefinitionKey() {
  25 + return CrmReceivableServiceImpl.RECEIVABLE_APPROVE;
  26 + }
  27 +
  28 + @Override
  29 + public void onEvent(BpmResultListenerRespDTO event) {
  30 + receivableService.updateReceivableAuditStatus(event);
  31 + }
  32 +
  33 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.util;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  5 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  6 +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
  7 +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
  8 +
  9 +/**
  10 + * CRM 流程工具类
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +public class CrmAuditStatusUtils {
  15 +
  16 + public static void convertAuditStatus(BpmResultListenerRespDTO event) {
  17 + // 判断下状态是否符合预期
  18 + if (!isEndResult(event.getResult())) {
  19 + return;
  20 + }
  21 + // 状态转换
  22 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {
  23 + event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());
  24 + }
  25 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {
  26 + event.setResult(CrmAuditStatusEnum.REJECT.getStatus());
  27 + }
  28 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {
  29 + event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());
  30 + }
  31 + }
  32 +
  33 + /**
  34 + * 判断该结果是否处于 End 最终结果
  35 + *
  36 + * @param result 结果
  37 + * @return 是否
  38 + */
  39 + public static boolean isEndResult(Integer result) {
  40 + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),
  41 + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
  42 + }
  43 +
  44 +}