Эх сурвалжийг харах

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

YunaiV 1 жил өмнө
parent
commit
2890de8103
17 өөрчлөгдсөн 101 нэмэгдсэн , 110 устгасан
  1. 3 4
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  2. 2 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
  3. 0 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
  4. 10 8
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
  5. 0 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
  6. 5 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java
  7. 14 18
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java
  8. 9 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java
  9. 0 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java
  10. 0 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java
  11. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
  12. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
  13. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
  14. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
  15. 5 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
  16. 0 8
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
  17. 46 39
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

+ 3 - 4
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -28,7 +28,6 @@ public interface ErrorCodeConstants {
 
     // ========== 联系人管理 1-020-003-000 ==========
     ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
-    ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在");
     ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
     ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, "更新联系人负责人失败");
 
@@ -38,10 +37,13 @@ public interface ErrorCodeConstants {
     ErrorCode RECEIVABLE_DELETE_FAIL = new ErrorCode(1_020_004_002, "删除回款失败,原因: 被回款计划所使用,不允许删除");
     ErrorCode RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_004_003, "回款提交审核失败,原因:回款没处在未提交状态");
     ErrorCode RECEIVABLE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_004_004, "更新回款审核状态失败,原因:回款不是审核中状态");
+    ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试");
+    ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态");
 
     // ========== 回款计划 1-020-005-000 ==========
     ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
     ErrorCode RECEIVABLE_PLAN_UPDATE_FAIL = new ErrorCode(1_020_006_000, "更想回款计划失败,原因:已经有对应的还款");
+    ErrorCode RECEIVABLE_PLAN_EXISTS_RECEIVABLE = new ErrorCode(1_020_006_001, "回款计划已经有对应的回款,不能使用");
 
     // ========== 客户管理 1_020_006_000 ==========
     ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
@@ -64,10 +66,8 @@ public interface ErrorCodeConstants {
     // ========== 权限管理 1_020_007_000 ==========
     ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
     ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
-    ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");
     ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人");
     ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, "删除数据权限失败,原因:批量删除权限的时候,只能属于同一个 bizId 下");
-    ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数据权限失败,原因:存在负责人");
     ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限");
     ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人");
     ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限");
@@ -93,7 +93,6 @@ public interface ErrorCodeConstants {
     ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_010_003, "商机状态不存在");
 
     // ========== 客户公海规则设置 1_020_012_000 ==========
-    ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海配置不存在或未启用");
     ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在");
 
     // ========== 跟进记录 1_020_013_000 ==========

+ 2 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java

@@ -175,12 +175,6 @@ public class CrmContractController {
         return success(true);
     }
 
-    /**
-     * 构建详细的合同结果
-     *
-     * @param contractList 原始合同信息
-     * @return 细的合同结果
-     */
     private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) {
         if (CollUtil.isEmpty(contractList)) {
             return Collections.emptyList();
@@ -239,7 +233,8 @@ public class CrmContractController {
         pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页
         PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageReqVO);
         return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段
-                .setId(contract.getId()).setName(contract.getName())));
+                .setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus())
+                .setTotalPrice(contract.getTotalPrice()))); // TODO @芋艿:未回款金额
     }
 
 }

+ 0 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java

@@ -158,7 +158,6 @@ public class CrmReceivableController {
         });
     }
 
-
     @GetMapping("/check-receivables-count")
     @Operation(summary = "获得待审核回款数量")
     @PreAuthorize("@ss.hasPermission('crm:receivable:query')")

+ 10 - 8
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java

@@ -146,7 +146,7 @@ public class CrmReceivablePlanController {
         // 1.3 获得合同 Map
         Map<Long, CrmContractDO> contractMap = contractService.getContractMap(
                 convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId));
-        // 1.4 获得款 Map
+        // 1.4 获得款 Map
         Map<Long, CrmReceivableDO> receivableMap = receivableService.getReceivableMap(
                 convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId));
         // 2. 拼接数据
@@ -170,19 +170,21 @@ public class CrmReceivablePlanController {
         return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId()));
     }
 
-    // TODO @芋艿:需要看下;
-    @GetMapping("/list-all-simple-by-customer")
+    @GetMapping("/simple-list")
     @Operation(summary = "获得回款计划精简列表", description = "获得回款计划精简列表,主要用于前端的下拉选项")
     @Parameters({
             @Parameter(name = "customerId", description = "客户编号", required = true),
             @Parameter(name = "contractId", description = "合同编号", required = true)
     })
     @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
-    public CommonResult<List<CrmReceivablePlanRespVO>> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId,
-                                                                                  @RequestParam("contractId") Long contractId) {
-        PageResult<CrmReceivablePlanDO> result = receivablePlanService.getReceivablePlanPageByCustomerId(
-                new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId));
-        return success(BeanUtils.toBean(result.getList(), CrmReceivablePlanRespVO.class));
+    public CommonResult<List<CrmReceivablePlanRespVO>> getReceivablePlanSimpleList(@RequestParam("customerId") Long customerId,
+                                                                                   @RequestParam("contractId") Long contractId) {
+        CrmReceivablePlanPageReqVO pageReqVO = new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId);
+        pageReqVO.setPageNo(PAGE_SIZE_NONE);
+        PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
+        return success(convertList(pageResult.getList(), receivablePlan -> new CrmReceivablePlanRespVO() // 只返回 id、period 等信息
+                .setId(receivablePlan.getId()).setPeriod(receivablePlan.getPeriod()).setReceivableId(receivablePlan.getReceivableId())
+                .setPrice(receivablePlan.getPrice()).setReturnType(receivablePlan.getReturnType())));
     }
 
 }

+ 0 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java

@@ -48,9 +48,6 @@ public class CrmReceivablePlanRespVO {
     @Schema(description = "回款信息")
     private CrmReceivableRespVO receivable;
 
-    @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
-    private Boolean finishStatus;
-
     @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer remindDays;
 

+ 5 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java

@@ -54,11 +54,14 @@ public class CrmReceivableRespVO {
     @Schema(description = "备注", example = "备注")
     private String remark;
 
+    @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    private Integer auditStatus;
+
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-    @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    private Integer auditStatus;
+    @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime updateTime;
 
     @Schema(description = "创建人", example = "25682")
     private String creator;

+ 14 - 18
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java

@@ -16,11 +16,19 @@ public class CrmReceivableSaveReqVO {
     @Schema(description = "编号", example = "25787")
     private Long id;
 
-    @Schema(description = "回款编号", example = "31177")
-    private String no;
+    @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "负责人编号不能为空")
+    private Long ownerUserId;
 
-    @Schema(description = "回款计划编号", example = "1024")
-    private Long planId; // 不是通过回款计划创建的回款没有回款计划编号
+    @Schema(description = "客户编号", example = "2")
+    private Long customerId; // 该字段不通过前端传递,而是 contractId 查询出来设置进去
+
+    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "合同编号不能为空")
+    private Long contractId;
+
+    @Schema(description = "回款计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long planId;
 
     @Schema(description = "回款方式", example = "2")
     @InEnum(CrmReceivableReturnTypeEnum.class)
@@ -30,22 +38,10 @@ public class CrmReceivableSaveReqVO {
     @NotNull(message = "回款金额不能为空")
     private BigDecimal price;
 
-    @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
-    @NotNull(message = "计划回款日期不能为空")
+    @Schema(description = "回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02")
+    @NotNull(message = "回款日期不能为空")
     private LocalDateTime returnTime;
 
-    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    @NotNull(message = "客户编号不能为空")
-    private Long customerId;
-
-    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    @NotNull(message = "合同编号不能为空")
-    private Long contractId;
-
-    @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    @NotNull(message = "负责人编号不能为空")
-    private Long ownerUserId;
-
     @Schema(description = "备注", example = "备注")
     private String remark;
 

+ 9 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java

@@ -39,15 +39,21 @@ public class CrmReceivableDO extends BaseDO {
      */
     private String no;
     /**
-     * 回款计划编号,关联 {@link CrmReceivablePlanDO#getId()}
+     * 回款计划编号
+     *
+     * 关联 {@link CrmReceivablePlanDO#getId()},非必须
      */
     private Long planId;
     /**
-     * 客户编号,关联 {@link CrmCustomerDO#getId()}
+     * 客户编号
+     *
+     * 关联 {@link CrmCustomerDO#getId()}
      */
     private Long customerId;
     /**
-     * 合同编号,关联 {@link CrmContractDO#getId()}
+     * 合同编号
+     *
+     * 关联 {@link CrmContractDO#getId()}
      */
     private Long contractId;
     /**

+ 0 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java

@@ -75,10 +75,6 @@ public class CrmReceivablePlanDO extends BaseDO {
      * 回款编号,关联 {@link CrmReceivableDO#getId()}
      */
     private Long receivableId;
-    /**
-     * 完成状态
-     */
-    private Boolean finishStatus;
 
     /**
      * 提前几天提醒

+ 0 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java

@@ -1,4 +0,0 @@
-/**
- * 回款
- */
-package cn.iocoder.yudao.module.crm.dal.dataobject.receivable;

+ 4 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java

@@ -24,6 +24,10 @@ import java.util.List;
 @Mapper
 public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
 
+    default CrmReceivableDO selectByNo(String no) {
+        return selectOne(CrmReceivableDO::getNo, no);
+    }
+
     default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()
                 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java

@@ -98,7 +98,7 @@ public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO
         query.isNull(CrmReceivablePlanDO::getReceivableId)
                 .between(CrmReceivablePlanDO::getReturnTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS));
         // TODO return_time 小于现在;
-        // TODO 未
+        // TODO 未
         // TODO remind_time 大于现在;
         return selectCount(query);
     }

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java

@@ -25,7 +25,7 @@ public class CrmNoRedisDAO {
     public static final String CONTRACT_NO_PREFIX = "HT";
 
     /**
-     * 款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}
+     * 款 {@link cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO}
      */
     public static final String RECEIVABLE_PREFIX = "HK";
 

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java

@@ -38,7 +38,7 @@ public interface CrmReceivablePlanService {
      * @param id           编号
      * @param receivableId 回款编号
      */
-    void updateReceivableId(Long id, Long receivableId);
+    void updateReceivablePlanReceivableId(Long id, Long receivableId);
 
     /**
      * 删除回款计划

+ 5 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java

@@ -65,7 +65,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
         // 1. 校验关联数据是否存在
         validateRelationDataExists(createReqVO);
 
-        // 2. 插入款计划
+        // 2. 插入款计划
         CrmReceivablePlanDO maxPeriodReceivablePlan = receivablePlanMapper.selectMaxPeriodByContractId(createReqVO.getContractId());
         int period = maxPeriodReceivablePlan == null ? 1 : maxPeriodReceivablePlan.getPeriod() + 1;
         CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setPeriod(period);
@@ -95,12 +95,12 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
         validateRelationDataExists(updateReqVO);
         // 1.2 校验关联数据是否存在
         CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
-        // 1.3 如果已经有对应的款,则不允许编辑
+        // 1.3 如果已经有对应的款,则不允许编辑
         if (Objects.nonNull(oldReceivablePlan.getReceivableId())) {
             throw exception(RECEIVABLE_PLAN_UPDATE_FAIL);
         }
 
-        // 2. 更新款计划
+        // 2. 更新款计划
         CrmReceivablePlanDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivablePlanDO.class);
         if (updateReqVO.getReturnTime() != null && updateReqVO.getRemindDays() != null) {
             updateObj.setRemindTime(updateReqVO.getReturnTime().minusDays(updateReqVO.getRemindDays()));
@@ -124,13 +124,12 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
         }
     }
 
-    // TODO @芋艿:优化下这个方法的命名
     @Override
-    public void updateReceivableId(Long id, Long receivableId) {
+    public void updateReceivablePlanReceivableId(Long id, Long receivableId) {
         // 校验存在
         validateReceivablePlanExists(id);
         // 更新回款计划
-        receivablePlanMapper.updateById(new CrmReceivablePlanDO().setReceivableId(receivableId).setFinishStatus(true));
+        receivablePlanMapper.updateById(new CrmReceivablePlanDO().setId(id).setReceivableId(receivableId));
     }
 
     @Override

+ 0 - 8
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java

@@ -113,12 +113,4 @@ public interface CrmReceivableService {
      */
     Long getCheckReceivablesCount(Long userId);
 
-    /**
-     * 根据合同编号,获取合同关联的回款数量
-     *
-     * @param contractId 合同编号
-     * @return 数量
-     */
-    Long getReceivableCountByContractId(Long contractId);
-
 }

+ 46 - 39
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java

@@ -13,16 +13,15 @@ import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
-import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
+import cn.iocoder.yudao.module.crm.dal.redis.no.CrmNoRedisDAO;
 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
-import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -55,6 +54,7 @@ import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmRes
 @Slf4j
 public class CrmReceivableServiceImpl implements CrmReceivableService {
 
+    // TODO @芋艿:改个名字
     /**
      * BPM 回款审批流程标识
      */
@@ -64,9 +64,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     private CrmReceivableMapper receivableMapper;
 
     @Resource
-    private CrmContractService contractService;
+    private CrmNoRedisDAO noRedisDAO;
+
     @Resource
-    private CrmCustomerService customerService;
+    private CrmContractService contractService;
     @Resource
     @Lazy // 延迟加载,避免循环依赖
     private CrmReceivablePlanService receivablePlanService;
@@ -83,41 +84,54 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
             success = CRM_RECEIVABLE_CREATE_SUCCESS)
     public Long createReceivable(CrmReceivableSaveReqVO createReqVO) {
-        // 1. 校验关联数据存在
+        // 1.1 校验关联数据存在
         validateRelationDataExists(createReqVO);
-        // 2. 插入还款
-        CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
+        // 1.2 生成回款编号
+        String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX);
+        if (receivableMapper.selectByNo(no) != null) {
+            throw exception(RECEIVABLE_NO_EXISTS);
+        }
+
+        // 2. 插入回款
+        CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
+                .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
         receivableMapper.insert(receivable);
+
         // 3. 创建数据权限
         permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
-                .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
+                .setBizId(receivable.getId()).setUserId(createReqVO.getOwnerUserId())
+                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
+
         // 4. 更新关联的回款计划
-        if (Objects.nonNull(createReqVO.getPlanId())) {
-            receivablePlanService.updateReceivableId(receivable.getPlanId(), receivable.getId());
+        if (createReqVO.getPlanId() != null) {
+            receivablePlanService.updateReceivablePlanReceivableId(receivable.getPlanId(), receivable.getId());
         }
+
         // 5. 记录操作日志上下文
         LogRecordContext.putVariable("receivable", receivable);
         return receivable.getId();
     }
 
     private void validateRelationDataExists(CrmReceivableSaveReqVO reqVO) {
-        adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
-        CrmContractDO contract = contractService.getContract(reqVO.getContractId());
-        if (ObjectUtil.isNull(contract)) {
-            throw exception(CONTRACT_NOT_EXISTS);
-        }
-        CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId());
-        if (ObjectUtil.isNull(customer)) {
-            throw exception(CUSTOMER_NOT_EXISTS);
+        if (reqVO.getOwnerUserId() != null) {
+            adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在
         }
-        if (Objects.isNull(reqVO.getPlanId())) { // 没有回款计划编号则不校验
-            return;
+        if (reqVO.getContractId() != null) {
+            CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
+            if (ObjectUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
+                throw exception(RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE);
+            }
+            reqVO.setCustomerId(contract.getCustomerId()); // 设置客户编号
         }
-        CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
-        if (ObjectUtil.isNull(receivablePlan)) {
-            throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
+        if (reqVO.getPlanId() != null) {
+            CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId());
+            if (receivablePlan == null) {
+                throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
+            }
+            if (receivablePlan.getReceivableId() != null) {
+                throw exception(RECEIVABLE_PLAN_EXISTS_RECEIVABLE);
+            }
         }
-
     }
 
     @Override
@@ -127,6 +141,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
     public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) {
         Assert.notNull(updateReqVO.getId(), "回款编号不能为空");
+        updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段
         // 1.1 校验存在
         CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId());
         // 1.2 只有草稿、审批中,可以编辑;
@@ -135,7 +150,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
             throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
         }
 
-        // 2. 更新
+        // 2. 更新
         CrmReceivableDO updateObj = BeanUtils.toBean(updateReqVO, CrmReceivableDO.class);
         receivableMapper.updateById(updateObj);
 
@@ -160,27 +175,25 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
         receivableMapper.updateById(new CrmReceivableDO().setId(id).setAuditStatus(auditStatus));
     }
 
-    // TODO @liuhongfeng:缺一个取消回款的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
             success = CRM_RECEIVABLE_DELETE_SUCCESS)
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
     public void deleteReceivable(Long id) {
-        // 校验存在
+        // 1.1 校验存在
         CrmReceivableDO receivable = validateReceivableExists(id);
-        // 如果被 CrmReceivablePlanDO 所使用,则不允许删除
+        // 1.2 如果被 CrmReceivablePlanDO 所使用,则不允许删除
         if (Objects.nonNull(receivable.getPlanId()) && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
             throw exception(RECEIVABLE_DELETE_FAIL);
         }
-        // 删除
-        receivableMapper.deleteById(id);
 
-        // 删除数据权限
+        // 2. 删除
+        receivableMapper.deleteById(id);
+        // 3. 删除数据权限
         permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id);
 
-        // 记录操作日志上下文
+        // 4. 记录操作日志上下文
         LogRecordContext.putVariable("receivable", receivable);
     }
 
@@ -245,10 +258,4 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
         return receivableMapper.selectCheckReceivablesCount(userId);
     }
 
-    // TODO @芋艿:不确定这个方法还要不要;
-    @Override
-    public Long getReceivableCountByContractId(Long contractId) {
-        return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId);
-    }
-
 }