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 32  
33 33 // ========== 回款 1-020-004-000 ==========
34 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 40 // ========== 回款计划 1-020-005-000 ==========
37 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 140 String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
141 141 String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
142 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 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 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
11 11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
12 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 14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
15 15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
... ... @@ -61,14 +61,14 @@ public class CrmReceivableController {
61 61 @PostMapping("/create")
62 62 @Operation(summary = "创建回款")
63 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 68 @PutMapping("/update")
69 69 @Operation(summary = "更新回款")
70 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 72 receivableService.updateReceivable(updateReqVO);
73 73 return success(true);
74 74 }
... ... @@ -88,7 +88,7 @@ public class CrmReceivableController {
88 88 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
89 89 public CommonResult<CrmReceivableRespVO> getReceivable(@RequestParam("id") Long id) {
90 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 94 @GetMapping("/page")
... ... @@ -152,4 +152,12 @@ public class CrmReceivableController {
152 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 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
  6 +import java.math.BigDecimal;
6 7 import java.time.LocalDateTime;
7 8  
8 9 @Schema(description = "管理后台 - CRM 回款计划 Response VO")
9 10 @Data
10 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 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 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 26 private LocalDateTime returnTime;
26 27  
27   - @Schema(description = "提前几天提醒")
  28 + @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
28 29 private Integer remindDays;
29 30  
30   - @Schema(description = "提醒日期")
  31 + @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
31 32 private LocalDateTime remindTime;
32 33  
33   - @Schema(description = "客户名称", example = "18026")
  34 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
34 35 private Long customerId;
35 36  
36   - @Schema(description = "合同编号", example = "3473")
  37 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
37 38 private Long contractId;
38 39  
39   - @Schema(description = "负责人编号", example = "17828")
  40 + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
40 41 private Long ownerUserId;
41 42  
42 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 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4   -import lombok.*;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
5 7 import java.time.LocalDateTime;
6 8  
7 9 // TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能
8 10 @Schema(description = "管理后台 - CRM 回款 Response VO")
9 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 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 47 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
18 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 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
6 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 6 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
9 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
10 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
... ... @@ -28,12 +26,6 @@ public interface CrmReceivableConvert {
28 26  
29 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 29 default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
38 30 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) {
39 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 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 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 9 import com.baomidou.mybatisplus.annotation.KeySequence;
6 10 import com.baomidou.mybatisplus.annotation.TableId;
7 11 import com.baomidou.mybatisplus.annotation.TableName;
8 12 import lombok.*;
9 13  
  14 +import java.math.BigDecimal;
10 15 import java.time.LocalDateTime;
11 16  
12 17 /**
... ... @@ -33,64 +38,55 @@ public class CrmReceivableDO extends BaseDO {
33 38 * 回款编号
34 39 */
35 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 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 48 private Long customerId;
51 49 /**
52   - * 合同 ID
53   - *
54   - * 对应实体 {@link CrmContractDO}
  50 + * 合同编号,关联 {@link CrmContractDO#getId()}
55 51 */
56 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 61 private LocalDateTime returnTime;
67   - // TODO @liuhongfeng:少个枚举
68 62 /**
69   - * 回款方式
  63 + * 回款方式,关联枚举{@link CrmReturnTypeEnum}
70 64 */
71 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 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 11 import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
12 12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
13 13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
14   -import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
15 14 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
16 15 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
17 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 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
51 50 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
52 51 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
  52 +import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertAuditStatus;
53 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 284  
285 285 @Override
286 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 289 contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))
303 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 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 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 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 66 success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
67 67 public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) {
68 68 // 1.1 校验关联数据是否存在
69   - checkReceivablePlan(createReqVO);
  69 + validateRelationDataExists(createReqVO);
70 70 // 1.2 查验关联合同回款数量
71 71 Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId());
72 72 int period = (int) (count + 1);
... ... @@ -92,7 +92,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
92 92 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
93 93 public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) {
94 94 // 1. 校验存在
95   - checkReceivablePlan(updateReqVO);
  95 + validateRelationDataExists(updateReqVO);
96 96 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
97 97 if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑;
98 98 throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款");
... ... @@ -107,7 +107,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
107 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 119 adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
112 120 CrmContractDO contract = contractService.getContract(reqVO.getContractId());
113 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 1 package cn.iocoder.yudao.module.crm.service.receivable;
2 2  
3 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 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 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
8 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
9 9 import jakarta.validation.Valid;
... ... @@ -22,17 +22,23 @@ public interface CrmReceivableService {
22 22 * 创建回款
23 23 *
24 24 * @param createReqVO 创建信息
25   - * @param userId 用户编号
26 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 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 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 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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.ObjUtil;
5 7 import cn.hutool.core.util.ObjectUtil;
6   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 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 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 16 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
14 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 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 26 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
24 27 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
25 28 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  29 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
26 30 import com.mzt.logapi.context.LogRecordContext;
27 31 import com.mzt.logapi.service.impl.DiffParseFunction;
28 32 import com.mzt.logapi.starter.annotation.LogRecord;
29 33 import jakarta.annotation.Resource;
30 34 import org.springframework.stereotype.Service;
  35 +import org.springframework.transaction.annotation.Transactional;
31 36 import org.springframework.validation.annotation.Validated;
32 37  
33 38 import java.util.Collection;
34 39 import java.util.List;
  40 +import java.util.Objects;
35 41  
36 42 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
37 43 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
38 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 48 * CRM 回款 Service 实现类
... ... @@ -46,6 +53,11 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
46 53 @Validated
47 54 public class CrmReceivableServiceImpl implements CrmReceivableService {
48 55  
  56 + /**
  57 + * BPM 回款审批流程标识
  58 + */
  59 + public static final String RECEIVABLE_APPROVE = "receivable-approve";
  60 +
49 61 @Resource
50 62 private CrmReceivableMapper receivableMapper;
51 63  
... ... @@ -57,51 +69,47 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
57 69 private CrmReceivablePlanService receivablePlanService;
58 70 @Resource
59 71 private CrmPermissionService permissionService;
  72 + @Resource
  73 + private AdminUserApi adminUserApi;
  74 + @Resource
  75 + private BpmProcessInstanceApi bpmProcessInstanceApi;
60 76  
61 77 @Override
  78 + @Transactional(rollbackFor = Exception.class)
62 79 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
63 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 86 receivableMapper.insert(receivable);
76 87 // 3. 创建数据权限
77 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 95 LogRecordContext.putVariable("receivable", receivable);
83 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 102 if (ObjectUtil.isNull(contract)) {
96 103 throw exception(CONTRACT_NOT_EXISTS);
97 104 }
98   -
99   - CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId());
  105 + CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());
100 106 if (ObjectUtil.isNull(customer)) {
101 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 113 if (ObjectUtil.isNull(receivablePlan)) {
106 114 throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
107 115 }
... ... @@ -109,46 +117,84 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
109 117 }
110 118  
111 119 @Override
  120 + @Transactional(rollbackFor = Exception.class)
112 121 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
113 122 success = CRM_RECEIVABLE_UPDATE_SUCCESS)
114 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 136 receivableMapper.updateById(updateObj);
123 137  
124   - // TODO @liuhongfeng:需要更新关联的 plan
125 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 153 @Override
  154 + @Transactional(rollbackFor = Exception.class)
135 155 @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
136 156 success = CRM_RECEIVABLE_DELETE_SUCCESS)
137 157 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
138 158 public void deleteReceivable(Long id) {
139   - // TODO @liuhongfeng:如果被 CrmReceivablePlanDO 所使用,则不允许删除
140 159 // 校验存在
141 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 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 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 198 private CrmReceivableDO validateReceivableExists(Long id) {
153 199 CrmReceivableDO receivable = receivableMapper.selectById(id);
154 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 +}
... ...