diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java index 22bb0b426..eee2b32d9 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java @@ -14,5 +14,6 @@ public interface DictTypeConstants { String CRM_PRODUCT_UNIT = "crm_product_unit"; // CRM 产品单位 String CRM_PRODUCT_STATUS = "crm_product_status"; // CRM 产品状态 String CRM_FOLLOW_UP_TYPE = "crm_follow_up_type"; // CRM 跟进方式 + String CRM_RECEIVABLE_RETURN_TYPE = "crm_receivable_return_type"; // CRM 回款方式 } diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index b9c3898ce..caf0db5d4 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/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_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 ========== diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java index f04c62c68..d0f30db23 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; @@ -24,6 +25,7 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 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.product.CrmProductService; +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -39,6 +41,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.math.BigDecimal; import java.util.Collections; import java.util.List; import java.util.Map; @@ -67,6 +70,8 @@ public class CrmContractController { private CrmBusinessService businessService; @Resource private CrmProductService productService; + @Resource + private CrmReceivableService receivableService; @Resource private AdminUserApi adminUserApi; @@ -174,12 +179,6 @@ public class CrmContractController { return success(true); } - /** - * 构建详细的合同结果 - * - * @param contractList 原始合同信息 - * @return 细的合同结果 - */ private List buildContractDetailList(List contractList) { if (CollUtil.isEmpty(contractList)) { return Collections.emptyList(); @@ -197,6 +196,9 @@ public class CrmContractController { // 1.4 获取商机 Map businessMap = businessService.getBusinessMap( convertSet(contractList, CrmContractDO::getBusinessId)); + // 1.5 获得已回款金额 + Map receivablePriceMap = receivableService.getReceivablePriceMapByContractId( + convertSet(contractList, CrmContractDO::getId)); // 2. 拼接数据 return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> { // 2.1 设置客户信息 @@ -212,6 +214,8 @@ public class CrmContractController { findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName())); // 2.4 设置商机信息 findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName())); + // 2.5 设置已回款金额 + contractVO.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO)); }); } @@ -229,13 +233,24 @@ public class CrmContractController { return success(contractService.getRemindContractCount(getLoginUserId())); } - @GetMapping("/list-all-simple-by-customer") - @Operation(summary = "获得合同精简列表", description = "只包含有读权限的客户的合同,主要用于前端的下拉选项") + @GetMapping("/simple-list") + @Operation(summary = "获得合同精简列表", description = "只包含的合同,主要用于前端的下拉选项") @Parameter(name = "customerId", description = "客户编号", required = true) @PreAuthorize("@ss.hasPermission('crm:contract:query')") - public CommonResult> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId) { - PageResult result = contractService.getContractPageByCustomerId(new CrmContractPageReqVO().setCustomerId(customerId)); - return success(BeanUtils.toBean(result.getList(), CrmContractRespVO.class)); + public CommonResult> getContractSimpleList(@RequestParam("customerId") Long customerId) { + CrmContractPageReqVO pageReqVO = new CrmContractPageReqVO().setCustomerId(customerId); + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); // 不分页 + PageResult pageResult = contractService.getContractPageByCustomerId(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(Collections.emptyList()); + } + // 拼接数据 + Map receivablePriceMap = receivableService.getReceivablePriceMapByContractId( + convertSet(pageResult.getList(), CrmContractDO::getId)); + return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // 只返回 id、name 等精简字段 + .setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus()) + .setTotalPrice(contract.getTotalPrice()) + .setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO)))); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java index 8f5469eb1..a01bc110b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java @@ -88,6 +88,10 @@ public class CrmContractRespVO { @ExcelProperty("合同金额") private BigDecimal totalPrice; + @Schema(description = "已回款金额", example = "5617") + @ExcelProperty("已回款金额") + private BigDecimal totalReceivablePrice; + @Schema(description = "客户签约人编号", example = "18546") private Long signContactId; @Schema(description = "客户签约人", example = "小豆") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java index e048d85d8..a94a32cf5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO; +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogRespVO; import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; @@ -54,11 +54,11 @@ public class CrmOperateLogController { @GetMapping("/page") @Operation(summary = "获得操作日志") @PreAuthorize("@ss.hasPermission('crm:operate-log:query')") - public CommonResult> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { + public CommonResult> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释 reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); - return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogV2RespVO.class)); + return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogRespVO.class)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java similarity index 93% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java index b3405428f..8e458a8a0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java @@ -6,10 +6,10 @@ import lombok.Data; import java.time.LocalDateTime; -@Schema(description = "管理后台 - CRM 跟进 Response VO") +@Schema(description = "管理后台 - CRM 操作日志 Response VO") @Data @ExcelIgnoreUnannotated -public class CrmOperateLogV2RespVO { +public class CrmOperateLogRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") private Long id; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java index 8afe2df79..d98ea41aa 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java @@ -4,20 +4,23 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO; -import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; 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.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -31,14 +34,15 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -57,6 +61,8 @@ public class CrmReceivableController { @Resource private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; @PostMapping("/create") @Operation(summary = "创建回款") @@ -88,7 +94,14 @@ public class CrmReceivableController { @PreAuthorize("@ss.hasPermission('crm:receivable:query')") public CommonResult getReceivable(@RequestParam("id") Long id) { CrmReceivableDO receivable = receivableService.getReceivable(id); - return success(BeanUtils.toBean(receivable, CrmReceivableRespVO.class)); + return success(buildReceivableDetail(receivable)); + } + + private CrmReceivableRespVO buildReceivableDetail(CrmReceivableDO receivable) { + if (receivable == null) { + return null; + } + return buildReceivableDetailList(Collections.singletonList(receivable)).get(0); } @GetMapping("/page") @@ -96,7 +109,7 @@ public class CrmReceivableController { @PreAuthorize("@ss.hasPermission('crm:receivable:query')") public CommonResult> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) { PageResult pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId()); - return success(buildReceivableDetailPage(pageResult)); + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal())); } @GetMapping("/page-by-customer") @@ -104,7 +117,7 @@ public class CrmReceivableController { public CommonResult> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) { Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); PageResult pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO); - return success(buildReceivableDetailPage(pageResult)); + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal())); } @GetMapping("/export-excel") @@ -114,36 +127,43 @@ public class CrmReceivableController { public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PAGE_SIZE_NONE); - PageResult pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId()); + List list = receivableService.getReceivablePage(exportReqVO, getLoginUserId()).getList(); // 导出 Excel ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class, - buildReceivableDetailPage(pageResult).getList()); + buildReceivableDetailList(list)); } - /** - * 构建详细的回款分页结果 - * - * @param pageResult 简单的回款分页结果 - * @return 详细的回款分页结果 - */ - private PageResult buildReceivableDetailPage(PageResult pageResult) { - List receivableList = pageResult.getList(); + private List buildReceivableDetailList(List receivableList) { if (CollUtil.isEmpty(receivableList)) { - return PageResult.empty(pageResult.getTotal()); + return Collections.emptyList(); } - // 1. 获取客户列表 - List customerList = customerService.getCustomerList( + // 1.1 获取客户列表 + Map customerMap = customerService.getCustomerMap( convertSet(receivableList, CrmReceivableDO::getCustomerId)); - // 2. 获取创建人、负责人列表 + // 1.2 获取创建人、负责人列表 Map userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); - // 3. 获得合同列表 - List contractList = contractService.getContractList( + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + // 1.3 获得合同列表 + Map contractMap = contractService.getContractMap( convertSet(receivableList, CrmReceivableDO::getContractId)); - return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList); + // 2. 拼接结果 + return BeanUtils.toBean(receivableList, CrmReceivableRespVO.class, (receivableVO) -> { + // 2.1 拼接客户名称 + findAndThen(customerMap, receivableVO.getCustomerId(), customer -> receivableVO.setCustomerName(customer.getName())); + // 2.2 拼接负责人、创建人名称 + MapUtils.findAndThen(userMap, NumberUtils.parseLong(receivableVO.getCreator()), + user -> receivableVO.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, receivableVO.getOwnerUserId(), user -> { + receivableVO.setOwnerUserName(user.getNickname()); + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> receivableVO.setOwnerUserDeptName(dept.getName())); + }); + // 2.3 拼接合同信息 + findAndThen(contractMap, receivableVO.getContractId(), contract -> + receivableVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class))); + }); } - @GetMapping("/check-receivables-count") @Operation(summary = "获得待审核回款数量") @PreAuthorize("@ss.hasPermission('crm:receivable:query')") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java index 18948c342..e2d3e0dd8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO; -import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; 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; @@ -34,14 +34,15 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -93,7 +94,14 @@ public class CrmReceivablePlanController { @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") public CommonResult getReceivablePlan(@RequestParam("id") Long id) { CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id); - return success(BeanUtils.toBean(receivablePlan, CrmReceivablePlanRespVO.class)); + return success(buildReceivablePlanDetail(receivablePlan)); + } + + private CrmReceivablePlanRespVO buildReceivablePlanDetail(CrmReceivablePlanDO receivablePlan) { + if (receivablePlan == null) { + return null; + } + return buildReceivableDetailList(Collections.singletonList(receivablePlan)).get(0); } @GetMapping("/page") @@ -101,7 +109,7 @@ public class CrmReceivablePlanController { @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") public CommonResult> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) { PageResult pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId()); - return success(convertDetailReceivablePlanPage(pageResult)); + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal())); } @GetMapping("/page-by-customer") @@ -109,10 +117,9 @@ public class CrmReceivablePlanController { public CommonResult> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) { Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); PageResult pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO); - return success(convertDetailReceivablePlanPage(pageResult)); + return success(new PageResult<>(buildReceivableDetailList(pageResult.getList()), pageResult.getTotal())); } - // TODO 芋艿:后面在优化导出 @GetMapping("/export-excel") @Operation(summary = "导出回款计划 Excel") @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')") @@ -120,39 +127,42 @@ public class CrmReceivablePlanController { public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PAGE_SIZE_NONE); - PageResult pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()); + List list = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()).getList(); // 导出 Excel ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class, - convertDetailReceivablePlanPage(pageResult).getList()); + buildReceivableDetailList(list)); } - /** - * 构建详细的回款计划分页结果 - * - * @param pageResult 简单的回款计划分页结果 - * @return 详细的回款计划分页结果 - */ - private PageResult convertDetailReceivablePlanPage(PageResult pageResult) { - List receivablePlanList = pageResult.getList(); + private List buildReceivableDetailList(List receivablePlanList) { if (CollUtil.isEmpty(receivablePlanList)) { - return PageResult.empty(pageResult.getTotal()); + return Collections.emptyList(); } - // 1. 获取客户列表 - List customerList = customerService.getCustomerList( + // 1.1 获取客户 Map + Map customerMap = customerService.getCustomerMap( convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId)); - // 2. 获取创建人、负责人列表 + // 1.2 获取创建人、负责人列表 Map userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); - // 3. 获得合同列表 - List contractList = contractService.getContractList( + // 1.3 获得合同 Map + Map contractMap = contractService.getContractMap( convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId)); - // 4. 获得还款列表 - List receivableList = receivableService.getReceivableList( + // 1.4 获得回款 Map + Map receivableMap = receivableService.getReceivableMap( convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId)); - return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList); + // 2. 拼接数据 + return BeanUtils.toBean(receivablePlanList, CrmReceivablePlanRespVO.class, (receivablePlanVO) -> { + // 2.1 拼接客户信息 + findAndThen(customerMap, receivablePlanVO.getCustomerId(), customer -> receivablePlanVO.setCustomerName(customer.getName())); + // 2.2 拼接用户信息 + findAndThen(userMap, receivablePlanVO.getOwnerUserId(), user -> receivablePlanVO.setOwnerUserName(user.getNickname())); + findAndThen(userMap, Long.parseLong(receivablePlanVO.getCreator()), user -> receivablePlanVO.setCreatorName(user.getNickname())); + // 2.3 拼接合同信息 + findAndThen(contractMap, receivablePlanVO.getContractId(), contract -> receivablePlanVO.setContractNo(contract.getNo())); + // 2.4 拼接回款信息 + receivablePlanVO.setReceivable(BeanUtils.toBean(receivableMap.get(receivablePlanVO.getReceivableId()), CrmReceivableRespVO.class)); + }); } - @GetMapping("/remind-receivable-plan-count") @Operation(summary = "获得待回款提醒数量") @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") @@ -160,18 +170,21 @@ public class CrmReceivablePlanController { return success(receivablePlanService.getRemindReceivablePlanCount(getLoginUserId())); } - @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> getListAllSimpleByCustomer(@RequestParam("customerId") Long customerId, - @RequestParam("contractId") Long contractId) { - PageResult result = receivablePlanService.getReceivablePlanPageByCustomerId( - new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId)); - return success(BeanUtils.toBean(result.getList(), CrmReceivablePlanRespVO.class)); + public CommonResult> getReceivablePlanSimpleList(@RequestParam("customerId") Long customerId, + @RequestParam("contractId") Long contractId) { + CrmReceivablePlanPageReqVO pageReqVO = new CrmReceivablePlanPageReqVO().setCustomerId(customerId).setContractId(contractId); + pageReqVO.setPageNo(PAGE_SIZE_NONE); + PageResult 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()))); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java index 9b503db40..1c58766e1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -35,17 +37,16 @@ public class CrmReceivablePlanRespVO { @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") private LocalDateTime returnTime; - @Schema(description = "回款方式", example = "1") - private Integer returnType; // 来自 Receivable 的 returnType 字段 + @Schema(description = "计划回款方式", example = "1") + private Integer returnType; @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") private BigDecimal price; @Schema(description = "回款编号", example = "19852") private Long receivableId; - - @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean finishStatus; + @Schema(description = "回款信息") + private CrmReceivableRespVO receivable; @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer remindDays; @@ -57,12 +58,18 @@ public class CrmReceivablePlanRespVO { private String remark; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") private LocalDateTime createTime; - @Schema(description = "创建人", example = "25682") - private String creator; + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + private LocalDateTime updateTime; - @Schema(description = "创建人名字", example = "test") + @Schema(description = "创建人", example = "1024") + @ExcelProperty("创建人") + private String creator; + @Schema(description = "创建人名字", example = "芋道源码") + @ExcelProperty("创建人名字") private String creatorName; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java index 51b688b02..470734c65 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanSaveReqVO.java @@ -14,9 +14,8 @@ public class CrmReceivablePlanSaveReqVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Long id; - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "客户编号不能为空") - private Long customerId; + @Schema(description = "客户编号", hidden = true, example = "2") + private Long customerId; // 该字段不通过前端传递,而是 contractId 查询出来设置进去 @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @NotNull(message = "合同编号不能为空") @@ -37,10 +36,6 @@ public class CrmReceivablePlanSaveReqVO { @NotNull(message = "计划回款金额不能为空") private BigDecimal price; - @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "提醒日期不能为空") - private LocalDateTime remindTime; - @Schema(description = "提前几天提醒", example = "1") private Integer remindDays; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java index 13ae884b1..a9fcb1b8b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -31,12 +33,30 @@ public class CrmReceivableRespVO { @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Long customerId; + @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + private String customerName; @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Long contractId; + @Schema(description = "合同信息") + private CrmContractRespVO contract; - @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @Schema(description = "负责人的用户编号", example = "25682") + @ExcelProperty("负责人的用户编号") private Long ownerUserId; + @Schema(description = "负责人名字", example = "25682") + @ExcelProperty("负责人名字") + private String ownerUserName; + @Schema(description = "负责人部门") + @ExcelProperty("负责人部门") + private String ownerUserDeptName; + + @Schema(description = "工作流编号", example = "1043") + @ExcelProperty("工作流编号") + private String processInstanceId; + + @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer auditStatus; @Schema(description = "备注", example = "备注") private String remark; @@ -44,21 +64,11 @@ public class CrmReceivableRespVO { @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; - @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") - private String customerName; - - @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer auditStatus; - - @Schema(description = "合同编号", example = "Q110") - private String contractNo; - - @Schema(description = "负责人", example = "test") - private String ownerUserName; + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; @Schema(description = "创建人", example = "25682") private String creator; - @Schema(description = "创建人名字", example = "test") private String creatorName; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java index fcd39b411..8ab85daeb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableSaveReqVO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.*; +import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -16,37 +18,41 @@ 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") + @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME) + @NotNull(message = "负责人编号不能为空") + private Long ownerUserId; - @Schema(description = "回款计划编号", example = "1024") - private Long planId; // 不是通过回款计划创建的回款没有回款计划编号 + @Schema(description = "客户编号", example = "2") + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME) + private Long customerId; // 该字段不通过前端传递,而是 contractId 查询出来设置进去 + + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @DiffLogField(name = "合同", function = CrmContractParseFunction.NAME) + @NotNull(message = "合同编号不能为空") + private Long contractId; + + @Schema(description = "回款计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @DiffLogField(name = "合同", function = CrmReceivablePlanParseFunction.NAME) + private Long planId; @Schema(description = "回款方式", example = "2") + @DiffLogField(name = "回款方式", function = CrmReceivableReturnTypeParseFunction.NAME) @InEnum(CrmReceivableReturnTypeEnum.class) private Integer returnType; @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") + @DiffLogField(name = "回款金额") @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 = "回款日期不能为空") + @DiffLogField(name = "回款日期") 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 = "备注") + @DiffLogField(name = "备注") private String remark; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java deleted file mode 100644 index d53d14fda..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.crm.convert.receivable; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; -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.system.api.user.dto.AdminUserRespDTO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; - -/** - * 回款 Convert - * - * @author 赤焰 - */ -@Mapper -public interface CrmReceivableConvert { - - CrmReceivableConvert INSTANCE = Mappers.getMapper(CrmReceivableConvert.class); - - default PageResult convertPage(PageResult pageResult, Map userMap, - List customerList, List contractList) { - PageResult voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class); - // 拼接关联字段 - Map customerMap = convertMap(customerList, CrmCustomerDO::getId); - Map contractMap = convertMap(contractList, CrmContractDO::getId); - voPageResult.getList().forEach(receivable -> { - findAndThen(userMap, receivable.getOwnerUserId(), user -> receivable.setOwnerUserName(user.getNickname())); - findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname())); - findAndThen(customerMap, receivable.getCustomerId(), customer -> receivable.setCustomerName(customer.getName())); - findAndThen(contractMap, receivable.getContractId(), contract -> receivable.setContractNo(contract.getNo())); - }); - return voPageResult; - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java deleted file mode 100644 index bdd422e00..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.crm.convert.receivable; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; -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.system.api.user.dto.AdminUserRespDTO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; - -/** - * 回款计划 Convert - * - * @author 芋道源码 - */ -@Mapper -public interface CrmReceivablePlanConvert { - - CrmReceivablePlanConvert INSTANCE = Mappers.getMapper(CrmReceivablePlanConvert.class); - - default PageResult convertPage(PageResult pageResult, Map userMap, - List customerList, List contractList, - List receivableList) { - PageResult voPageResult = BeanUtils.toBean(pageResult, CrmReceivablePlanRespVO.class); - // 拼接关联字段 - Map customerMap = convertMap(customerList, CrmCustomerDO::getId); - Map contractMap = convertMap(contractList, CrmContractDO::getId); - Map receivableMap = convertMap(receivableList, CrmReceivableDO::getId); - voPageResult.getList().forEach(receivablePlan -> { - findAndThen(userMap, receivablePlan.getOwnerUserId(), user -> receivablePlan.setOwnerUserName(user.getNickname())); - findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname())); - findAndThen(customerMap, receivablePlan.getCustomerId(), customer -> receivablePlan.setCustomerName(customer.getName())); - findAndThen(contractMap, receivablePlan.getContractId(), contract -> receivablePlan.setContractNo(contract.getNo())); - findAndThen(receivableMap, receivablePlan.getReceivableId(), receivable -> receivablePlan.setReturnType(receivable.getReturnType())); - }); - return voPageResult; - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java index 21b976975..269cac6cc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java +++ b/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; /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java index d0e6a3e66..5ddefc559 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java @@ -61,7 +61,7 @@ public class CrmReceivablePlanDO extends BaseDO { */ private LocalDateTime returnTime; /** - * 回款类型 + * 计划回款类型 * * 枚举 {@link CrmReceivableReturnTypeEnum} */ @@ -75,10 +75,6 @@ public class CrmReceivablePlanDO extends BaseDO { * 回款编号,关联 {@link CrmReceivableDO#getId()} */ private Long receivableId; - /** - * 完成状态 - */ - private Boolean finishStatus; /** * 提前几天提醒 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java deleted file mode 100644 index f27442cdf..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 回款 - */ -package cn.iocoder.yudao.module.crm.dal.dataobject.receivable; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java index a97e7ece2..d19e93bfb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -11,10 +12,16 @@ 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.common.CrmSceneTypeEnum; import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; +import java.math.BigDecimal; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * 回款 Mapper @@ -24,6 +31,10 @@ import java.util.List; @Mapper public interface CrmReceivableMapper extends BaseMapperX { + default CrmReceivableDO selectByNo(String no) { + return selectOne(CrmReceivableDO::getNo, no); + } + default PageResult selectPageByCustomerId(CrmReceivablePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 @@ -67,4 +78,18 @@ public interface CrmReceivableMapper extends BaseMapperX { return selectCount(query); } + default Map selectReceivablePriceMapByContractId(Collection contractIds) { + if (CollUtil.isEmpty(contractIds)) { + return Collections.emptyMap(); + } + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("contract_id, SUM(price) AS total_price") + .eq("audit_status", CrmAuditStatusEnum.APPROVE.getStatus()) + .groupBy("contract_id") + .in("contract_id", contractIds)); + // 获得金额 + return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price")); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java index 32c3d7cf1..f066d24ad 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java @@ -25,6 +25,13 @@ import java.util.Objects; @Mapper public interface CrmReceivablePlanMapper extends BaseMapperX { + default CrmReceivablePlanDO selectMaxPeriodByContractId(Long contractId) { + return selectOne(new MPJLambdaWrapperX() + .eq(CrmReceivablePlanDO::getContractId, contractId) + .orderByDesc(CrmReceivablePlanDO::getPeriod) + .last("LIMIT 1")); + } + default PageResult selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); if (Objects.nonNull(reqVO.getContractNo())) { // 根据合同编号检索 @@ -47,7 +54,6 @@ public interface CrmReceivablePlanMapper extends BaseMapperX on.like(CrmContractDO::getNo, pageReqVO.getContractNo()) .eq(CrmContractDO::getId, CrmReceivablePlanDO::getContractId)); @@ -67,9 +73,7 @@ public interface CrmReceivablePlanMapper extends BaseMapperX getContractList(Collection ids); + /** + * 获得合同 Map + * + * @param ids 编号 + * @return 合同 Map + */ + default Map getContractMap(Collection ids) { + return convertMap(getContractList(ids), CrmContractDO::getId); + } + /** * 获得合同分页 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index 4c8f8eca5..298b2f167 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -323,6 +323,11 @@ public class CrmContractServiceImpl implements CrmContractService { return contractMapper.selectById(id); } + @Override + public CrmContractDO validateContract(Long id) { + return validateContractExists(id); + } + @Override public List getContractList(Collection ids) { if (CollUtil.isEmpty(ids)) { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java index 7f5854b44..70fe18a53 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java +++ b/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); /** * 删除回款计划 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java index 35748d85d..f80703773 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java @@ -2,20 +2,17 @@ package cn.iocoder.yudao.module.crm.service.receivable; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO; 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.CrmReceivablePlanDO; import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; 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; @@ -33,7 +30,8 @@ import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_NOT_EXISTS; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_UPDATE_FAIL; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; /** @@ -54,8 +52,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @Resource private CrmContractService contractService; @Resource - private CrmCustomerService customerService; - @Resource private CrmPermissionService permissionService; @Resource @@ -66,15 +62,16 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}", success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS) public Long createReceivablePlan(CrmReceivablePlanSaveReqVO createReqVO) { - // 1.1 校验关联数据是否存在 + // 1. 校验关联数据是否存在 validateRelationDataExists(createReqVO); - // 1.2 查验关联合同回款数量 - Long count = receivableService.getReceivableCountByContractId(createReqVO.getContractId()); - int period = (int) (count + 1); - // 2. 插入还款计划 - CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class) - .setPeriod(period).setFinishStatus(false); + // 2. 插入回款计划 + CrmReceivablePlanDO maxPeriodReceivablePlan = receivablePlanMapper.selectMaxPeriodByContractId(createReqVO.getContractId()); + int period = maxPeriodReceivablePlan == null ? 1 : maxPeriodReceivablePlan.getPeriod() + 1; + CrmReceivablePlanDO receivablePlan = BeanUtils.toBean(createReqVO, CrmReceivablePlanDO.class).setPeriod(period); + if (createReqVO.getReturnTime() != null && createReqVO.getRemindDays() != null) { + receivablePlan.setRemindTime(createReqVO.getReturnTime().minusDays(createReqVO.getRemindDays())); + } receivablePlanMapper.insert(receivablePlan); // 3. 创建数据权限 @@ -93,15 +90,21 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) public void updateReceivablePlan(CrmReceivablePlanSaveReqVO updateReqVO) { - // 1. 校验存在 + updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null); // 防止修改这些字段 + // 1.1 校验存在 validateRelationDataExists(updateReqVO); + // 1.2 校验关联数据是否存在 CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId()); - if (Objects.nonNull(oldReceivablePlan.getReceivableId())) { // 如果已经有对应的还款,则不允许编辑; - throw exception(RECEIVABLE_PLAN_UPDATE_FAIL, "已经有对应的还款"); + // 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())); + } receivablePlanMapper.updateById(updateObj); // 3. 记录操作日志上下文 @@ -109,24 +112,24 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); } + private void validateRelationDataExists(CrmReceivablePlanSaveReqVO reqVO) { + // 校验负责人存在 + if (reqVO.getOwnerUserId() != null) { + adminUserApi.validateUser(reqVO.getOwnerUserId()); + } + // 校验合同存在 + if (reqVO.getContractId() != null) { + CrmContractDO contract = contractService.getContract(reqVO.getContractId()); + reqVO.setCustomerId(contract.getCustomerId()); + } + } + @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)); - } - - private void validateRelationDataExists(CrmReceivablePlanSaveReqVO 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); - } + receivablePlanMapper.updateById(new CrmReceivablePlanDO().setId(id).setReceivableId(receivableId)); } @Override @@ -135,13 +138,15 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void deleteReceivablePlan(Long id) { - // 校验存在 + // 1. 校验存在 CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id); - // 删除 + + // 2. 删除 receivablePlanMapper.deleteById(id); - // 删除数据权限 + // 3. 删除数据权限 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); - // 记录操作日志上下文 + + // 4. 记录操作日志上下文 LogRecordContext.putVariable("receivablePlan", receivablePlan); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java index 8de653a7f..ba221b75b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -7,8 +7,12 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; import jakarta.validation.Valid; +import java.math.BigDecimal; import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * CRM 回款 Service 接口 @@ -71,6 +75,16 @@ public interface CrmReceivableService { */ List getReceivableList(Collection ids); + /** + * 获得回款 Map + * + * @param ids 编号 + * @return 回款 Map + */ + default Map getReceivableMap(Collection ids) { + return convertMap(getReceivableList(ids), CrmReceivableDO::getId); + } + /** * 获得回款分页 * @@ -101,11 +115,11 @@ public interface CrmReceivableService { Long getCheckReceivablesCount(Long userId); /** - * 根据合同编号,获取合同关联的回款数量 + * 获得合同已回款金额 Map * - * @param contractId 合同编号 - * @return 数量 + * @param contractIds 合同编号 + * @return 回款金额 Map */ - Long getReceivableCountByContractId(Long contractId); + Map getReceivablePriceMapByContractId(Collection contractIds); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index fb807c699..6497db950 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/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; @@ -36,9 +35,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; import java.util.Collection; import java.util.List; -import java.util.Objects; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; @@ -56,17 +56,18 @@ import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmRes public class CrmReceivableServiceImpl implements CrmReceivableService { /** - * BPM 回款审批流程标识 + * BPM 合同审批流程标识 */ - public static final String RECEIVABLE_APPROVE = "receivable-approve"; + public static final String BPM_PROCESS_DEFINITION_KEY = "crm-receivable-audit"; @Resource private CrmReceivableMapper receivableMapper; @Resource - private CrmContractService contractService; + private CrmNoRedisDAO noRedisDAO; + @Resource - private CrmCustomerService customerService; + private CrmContractService contractService; @Resource @Lazy // 延迟加载,避免循环依赖 private CrmReceivablePlanService receivablePlanService; @@ -78,48 +79,63 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @Resource private BpmProcessInstanceApi bpmProcessInstanceApi; + // TODO @puhui999:操作日志没记录上 @Override @Transactional(rollbackFor = Exception.class) @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); + if (reqVO.getOwnerUserId() != null) { + adminUserApi.validateUser(reqVO.getOwnerUserId()); // 校验负责人存在 } - CrmCustomerDO customer = customerService.getCustomer(reqVO.getCustomerId()); - if (ObjectUtil.isNull(customer)) { - throw exception(CUSTOMER_NOT_EXISTS); + 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()); // 设置客户编号 } - if (Objects.isNull(reqVO.getPlanId())) { // 没有回款计划编号则不校验 - return; + 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); + } } - CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(reqVO.getPlanId()); - if (ObjectUtil.isNull(receivablePlan)) { - throw exception(RECEIVABLE_PLAN_NOT_EXISTS); - } - } + // TODO @puhui999:操作日志没记录上 @Override @Transactional(rollbackFor = Exception.class) @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", @@ -127,6 +143,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 +152,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 +177,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 所使用,则不允许删除 - if (Objects.nonNull(receivable.getPlanId()) && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) { + // 1.2 如果被 CrmReceivablePlanDO 所使用,则不允许删除 + if (receivable.getPlanId() != null && 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); } @@ -197,7 +212,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { // 2. 创建回款审批流程实例 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() - .setProcessDefinitionKey(RECEIVABLE_APPROVE).setBusinessKey(String.valueOf(id))); + .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))); // 3. 更新回款工作流编号 receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId) @@ -246,8 +261,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { } @Override - public Long getReceivableCountByContractId(Long contractId) { - return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId); + public Map getReceivablePriceMapByContractId(Collection contractIds) { + return receivableMapper.selectReceivablePriceMapByContractId(contractIds); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java index 769fc35ad..8c4f3e974 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/listener/CrmReceivableResultListener.java @@ -20,7 +20,7 @@ public class CrmReceivableResultListener extends BpmProcessInstanceResultEventLi @Override public String getProcessDefinitionKey() { - return CrmReceivableServiceImpl.RECEIVABLE_APPROVE; + return CrmReceivableServiceImpl.BPM_PROCESS_DEFINITION_KEY; } @Override