CRM-合同: 新增更新合同商品如果有关联商机的话则更新商机商品

This commit is contained in:
puhui999 2024-02-08 11:53:23 +08:00
parent 84f3b230d2
commit 9caaa96e5b
12 changed files with 163 additions and 140 deletions

View File

@ -93,6 +93,8 @@ public interface LogRecordConstants {
String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】";
String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同";
String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
String CRM_CONTRACT_SUBMIT_SUB_TYPE = "提交合同审批";
String CRM_CONTRACT_SUBMIT_SUCCESS = "提交合同【{{#contractName}}】审批成功";
// ======================= CRM_PRODUCT 产品 =======================

View File

@ -8,7 +8,10 @@ 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.*;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
@ -127,8 +130,8 @@ public class CrmContractController {
HttpServletResponse response) throws IOException {
PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());
// 导出 Excel
ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class,
BeanUtils.toBean(pageResult.getList(), CrmContractExcelVO.class));
ExcelUtils.write(response, "合同.xls", "数据", CrmContractRespVO.class,
BeanUtils.toBean(pageResult.getList(), CrmContractRespVO.class));
}
@PutMapping("/transfer")

View File

@ -1,71 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @puhui999合并到 RespVO 里哈
/**
* CRM 合同 Excel VO
*
* @author dhb52
*/
@Data
public class CrmContractExcelVO {
@ExcelProperty("合同编号")
private Long id;
@ExcelProperty("合同名称")
private String name;
@ExcelProperty("客户编号")
private Long customerId;
@ExcelProperty("商机编号")
private Long businessId;
@ExcelProperty("工作流编号")
private Long processInstanceId;
@ExcelProperty("下单日期")
private LocalDateTime orderDate;
@ExcelProperty("负责人的用户编号")
private Long ownerUserId;
@ExcelProperty("合同编号")
private String no;
@ExcelProperty("开始时间")
private LocalDateTime startTime;
@ExcelProperty("结束时间")
private LocalDateTime endTime;
@ExcelProperty("合同金额")
private Integer price;
@ExcelProperty("整单折扣")
private Integer discountPercent;
@ExcelProperty("产品总金额")
private Integer productPrice;
@ExcelProperty("联系人编号")
private Long contactId;
@ExcelProperty("公司签约人")
private Long signUserId;
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -21,7 +21,6 @@ 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;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
@ -30,7 +29,6 @@ import org.mapstruct.ap.internal.util.Collections;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDateTime;
@ -218,19 +216,13 @@ public class CrmCustomerController {
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
}
// TODO @puhui999updateSupport 要不改成前端必须传递哈哈哈代码排版看着有点乱
// TODO @puhui999加一个选择负责人允许空空就进入公海
@PostMapping("/import")
@Operation(summary = "导入客户")
@Parameters({
@Parameter(name = "file", description = "Excel 文件", required = true),
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
})
@PreAuthorize("@ss.hasPermission('system:customer:import')")
public CommonResult<CrmCustomerImportRespVO> importExcel(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport)
public CommonResult<CrmCustomerImportRespVO> importExcel(@Valid @RequestBody CrmCustomerImportReqVO importReqVO)
throws Exception {
List<CrmCustomerImportExcelVO> list = ExcelUtils.read(file, CrmCustomerImportExcelVO.class);
return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
List<CrmCustomerImportExcelVO> list = ExcelUtils.read(importReqVO.getFile(), CrmCustomerImportExcelVO.class);
return success(customerService.importCustomerList(list, importReqVO));
}
@PutMapping("/transfer")

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Schema(description = "管理后台 - 客户导入 Request VO")
@Data
@Builder
public class CrmCustomerImportReqVO {
@Schema(description = "Excel 文件", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "Excel 文件不能为空")
private MultipartFile file;
@Schema(description = "是否支持更新", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否支持更新不能为空")
private Boolean updateSupport;
@Schema(description = "负责人", example = "1")
private Long ownerUserId; // null 则客户进入公海
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import jakarta.validation.Valid;
@ -50,6 +51,21 @@ public interface CrmBusinessService {
*/
void deleteBusiness(Long id);
/**
* 商机转移
*
* @param reqVO 请求
* @param userId 用户编号
*/
void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
/**
* 更新商机关联商品
*
* @param updateProductReqBO 请求
*/
void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO);
/**
* 获得商机
*
@ -105,14 +121,6 @@ public interface CrmBusinessService {
*/
PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO);
/**
* 商机转移
*
* @param reqVO 请求
* @param userId 用户编号
*/
void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
/**
* 获取关联客户的商机数量
*

View File

@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
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.business.bo.CrmBusinessUpdateProductReqBO;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
@ -80,7 +81,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
businessMapper.insert(business);
// 1.2 插入商机关联商品
if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
List<CrmBusinessProductDO> productList = convertBusinessProductList(createReqVO, business.getId());
List<CrmBusinessProductDO> productList = convertBusinessProductList(createReqVO.getProductItems(), business.getId());
businessProductMapper.insertBatch(productList);
// 更新合同商品总金额
businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice(
@ -120,7 +121,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
businessMapper.updateById(updateObj);
// 2.2 更新商机关联商品
List<CrmBusinessProductDO> productList = convertBusinessProductList(updateReqVO, updateObj.getId());
List<CrmBusinessProductDO> productList = convertBusinessProductList(updateReqVO.getProductItems(), updateObj.getId());
updateBusinessProduct(productList, updateObj.getId());
// TODO @商机待定如果状态发生变化插入商机状态变更记录表
@ -174,15 +175,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
}
private List<CrmBusinessProductDO> convertBusinessProductList(CrmBusinessSaveReqVO reqVO, Long businessId) {
private List<CrmBusinessProductDO> convertBusinessProductList(List<CrmBusinessSaveReqVO.CrmBusinessProductItem> productItems, Long businessId) {
// 校验商品存在
Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
Set<Long> productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
List<CrmProductDO> productList = productService.getProductList(productIds);
if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
throw exception(PRODUCT_NOT_EXISTS);
}
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
return convertList(reqVO.getProductItems(), productItem -> {
return convertList(productItems, productItem -> {
CrmProductDO product = productMap.get(productItem.getId());
return BeanUtils.toBean(product, CrmBusinessProductDO.class)
.setId(null).setProductId(productItem.getId()).setBusinessId(businessId)
@ -231,6 +232,14 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
LogRecordContext.putVariable("business", business);
}
@Override
public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) {
// 更新商机关联商品
List<CrmBusinessProductDO> productList = convertBusinessProductList(
BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId());
updateBusinessProduct(productList, updateProductReqBO.getId());
}
//======================= 查询相关 =======================
@Override

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.crm.service.business.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 更新商机商品 Update Req BO
*
* @author HUIHUI
*/
@Data
public class CrmBusinessUpdateProductReqBO {
/**
* 商机编号
*/
@NotNull(message = "商机编号不能为空")
private Long id;
@NotEmpty(message = "产品列表不能为空")
private List<CrmBusinessProductItem> productItems;
@Schema(description = "产品列表")
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class CrmBusinessProductItem {
@Schema(description = "产品编号", example = "20529")
@NotNull(message = "产品编号不能为空")
private Long id;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@NotNull(message = "产品数量不能为空")
private Integer count;
@Schema(description = "产品折扣")
private Integer discountPercent;
}
}

View File

@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@ -25,6 +26,7 @@ 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.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
@ -96,7 +98,11 @@ public class CrmContractServiceImpl implements CrmContractService {
// 更新合同商品总金额
contractMapper.updateById(new CrmContractDO().setId(contract.getId()).setProductPrice(
getSumValue(productList, CrmContractProductDO::getTotalPrice, Integer::sum)));
// TODO @puhui999: 如果存在合同关联了商机则更新商机商品关联
// 如果存在合同关联了商机则更新商机商品关联
if (contract.getBusinessId() != null) {
businessService.updateBusinessProduct(new CrmBusinessUpdateProductReqBO().setId(contract.getBusinessId())
.setProductItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.CrmBusinessProductItem.class)));
}
}
// 2. 创建数据权限
@ -251,7 +257,8 @@ public class CrmContractServiceImpl implements CrmContractService {
@Override
@Transactional(rollbackFor = Exception.class)
// TODO @puhui999操作日志
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
success = CRM_CONTRACT_SUBMIT_SUCCESS)
public void submitContract(Long id, Long userId) {
// 1. 校验合同是否在审批
CrmContractDO contract = validateContractExists(id);
@ -266,15 +273,43 @@ public class CrmContractServiceImpl implements CrmContractService {
// 3. 更新合同工作流编号
contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)
.setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
// 3. 记录日志
LogRecordContext.putVariable("contractName", contract.getName());
}
@Override
public void updateContractAuditStatus(BpmResultListenerRespDTO event) {
// TODO @puhui999可能要判断下状态是否符合预期
// 判断下状态是否符合预期
if (!isEndResult(event.getResult())) {
return;
}
// 状态转换
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {
event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());
}
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {
event.setResult(CrmAuditStatusEnum.REJECT.getStatus());
}
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {
event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());
}
// 更新合同状态
contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))
.setAuditStatus(event.getResult()));
}
/**
* 判断该结果是否处于 End 最终结果
*
* @param result 结果
* @return 是否
*/
public static boolean isEndResult(Integer result) {
return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),
BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
}
//======================= 查询相关 =======================
@Override

View File

@ -1,11 +1,7 @@
package cn.iocoder.yudao.module.crm.service.contract.listener;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;
import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl;
import jakarta.annotation.Resource;
@ -30,31 +26,7 @@ public class CrmContractResultListener implements BpmResultListenerApi {
@Override
public void onEvent(BpmResultListenerRespDTO event) {
boolean currentTaskFinish = isEndResult(event.getResult());
if (!currentTaskFinish) {
return;
}
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {
event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());
}
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {
event.setResult(CrmAuditStatusEnum.REJECT.getStatus());
}
if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {
event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());
}
contractService.updateContractAuditStatus(event);
}
/**
* 判断该结果是否处于 End 最终结果
*
* @param result 结果
* @return 是否
*/
public static boolean isEndResult(Integer result) {
return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),
BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
}
}

View File

@ -110,11 +110,10 @@ public interface CrmCustomerService {
* 批量导入客户
*
* @param importCustomers 导入客户列表
* @param isUpdateSupport 是否支持更新
* @param userId 用户编号
* @param importReqVO 请求
* @return 导入结果
*/
CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId);
CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, CrmCustomerImportReqVO importReqVO);
// ==================== 公海相关操作 ====================

View File

@ -244,8 +244,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
}
@Override
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
Boolean isUpdateSupport, Long userId) {
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, CrmCustomerImportReqVO importReqVO) {
if (CollUtil.isEmpty(importCustomers)) {
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
}
@ -264,19 +263,21 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
if (existCustomer == null) {
// 1.1 插入客户信息
CrmCustomerDO customer = initCustomer(importCustomer, userId);
CrmCustomerDO customer = initCustomer(importCustomer, importReqVO.getOwnerUserId());
customerMapper.insert(customer);
respVO.getCreateCustomerNames().add(importCustomer.getName());
if (importReqVO.getOwnerUserId() != null) {
// 1.2 创建数据权限
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
.setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
.setBizId(customer.getId()).setUserId(importReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
}
// 1.3 记录操作日志
getSelf().importCustomerLog(customer, false);
return;
}
// 情况二如果存在判断是否允许更新
if (!isUpdateSupport) {
if (!importReqVO.getUpdateSupport()) {
respVO.getFailureCustomerNames().put(importCustomer.getName(),
StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName()));
return;