CRM-合同:新增发起合同审批

This commit is contained in:
puhui999 2024-01-28 01:50:27 +08:00
parent a877bb4731
commit 1cab3c009c
9 changed files with 85 additions and 36 deletions

View File

@ -27,6 +27,11 @@
<artifactId>yudao-module-crm-api</artifactId> <artifactId>yudao-module-crm-api</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-bpm-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 --> <!-- 业务组件 -->
<dependency> <dependency>

View File

@ -140,4 +140,12 @@ public class CrmContractController {
return success(true); return success(true);
} }
@PutMapping("/approve")
@Operation(summary = "发起合同审批流程")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> transfer(@RequestParam("id") Long id) {
contractService.handleApprove(id, getLoginUserId());
return success(true);
}
} }

View File

@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFu
import com.mzt.logapi.starter.annotation.DiffLogField; import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ -35,10 +38,6 @@ public class CrmContractSaveReqVO {
@DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME) @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME)
private Long businessId; private Long businessId;
@Schema(description = "工作流编号", example = "1043")
@DiffLogField(name = "工作流编号")
private Long processInstanceId;
@Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
@DiffLogField(name = "下单日期") @DiffLogField(name = "下单日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ -86,16 +85,30 @@ public class CrmContractSaveReqVO {
@DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME) @DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME)
private Long signUserId; private Long signUserId;
@Schema(description = "最后跟进时间")
@DiffLogField(name = "最后跟进时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactLastTime;
@Schema(description = "备注", example = "你猜") @Schema(description = "备注", example = "你猜")
@DiffLogField(name = "备注") @DiffLogField(name = "备注")
private String remark; private String remark;
// TODO @dhb52增加一个 status 字段具体有哪些值你来枚举下主要页面上有个草稿提交审核的流程可以看看然后要对接工作流这块也可以看看不确定的地方问我 @Schema(description = "产品列表")
private List<CrmContractProductItem> productItems;
@Schema(description = "商品属性")
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class CrmContractProductItem {
@Schema(description = "产品编号", example = "20529")
@NotNull(message = "产品编号不能为空")
private Long id;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@NotNull(message = "产品数量不能为空")
private Long count;
@Schema(description = "产品折扣")
private Integer discountPercent;
}
} }

View File

@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
/** /**
* 商机产品关联表 DO * 商机产品关联表 DO
* *
@ -29,14 +28,12 @@ public class CrmBusinessProductDO extends BaseDO {
*/ */
@TableId @TableId
private Long id; private Long id;
/** /**
* 商机编号 * 商机编号
* *
* 关联 {@link CrmBusinessDO#getId()} * 关联 {@link CrmBusinessDO#getId()}
*/ */
private Long businessId; private Long businessId;
/** /**
* 产品编号 * 产品编号
* *
@ -50,29 +47,27 @@ public class CrmBusinessProductDO extends BaseDO {
private Integer price; private Integer price;
/** /**
* 销售价格 * 销售价格, 单位
*/ */
private BigDecimal salesPrice; private Integer salesPrice;
/** /**
* 数量 * 数量
*/ */
private BigDecimal count; private Integer count;
// TODO @lzxhqs改成 discountPercent
/** /**
* 折扣 * 折扣
*/ */
private BigDecimal discountPercent; private Integer discountPercent;
// TODO @lzxhqs改成 totalPrice总计价格和现有项目风格一致
/** /**
* 小计折扣后价格 * 总计价格折扣后价格
*/ */
private BigDecimal totalPrice; private Integer totalPrice;
/** /**
* 单位 * 单位
*
* 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
*/ */
private String unit; private Integer unit;
} }

View File

@ -45,7 +45,7 @@ public class CrmContractDO extends BaseDO {
/** /**
* 工作流编号 * 工作流编号
*/ */
private Long processInstanceId; private String processInstanceId;
/** /**
* 下单日期 * 下单日期
*/ */

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.product; package cn.iocoder.yudao.module.crm.dal.dataobject.product;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
@ -38,7 +39,7 @@ public class CrmProductDO extends BaseDO {
/** /**
* 单位 * 单位
* *
* 字典 {@link cn.iocoder.yudao.module.crm.enums.DictTypeConstants#CRM_PRODUCT_UNIT} * 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
*/ */
private Integer unit; private Integer unit;
/** /**

View File

@ -41,7 +41,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) { default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
// 拼接数据权限的查询条件 // 拼接数据权限的查询条件
CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTRACT.getType(),
CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
// 拼接自身的查询条件 // 拼接自身的查询条件
mpjLambdaWrapperX.selectAll(CrmContractDO.class) mpjLambdaWrapperX.selectAll(CrmContractDO.class)
@ -56,7 +56,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) { default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) {
MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>(); MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
// 构建数据权限连表条件 // 构建数据权限连表条件
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId);
// 拼接自身的查询条件 // 拼接自身的查询条件
query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId); query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId);
return selectJoinList(CrmContractDO.class, query); return selectJoinList(CrmContractDO.class, query);

View File

@ -57,6 +57,14 @@ public interface CrmContractService {
*/ */
void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO); void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO);
/**
* 发起合同审批流程
*
* @param id 合同编号
* @param userId 用户编号
*/
void handleApprove(Long id, Long userId);
/** /**
* 获得合同 * 获得合同
* *
@ -111,9 +119,11 @@ public interface CrmContractService {
Long getContractCountByCustomerId(Long customerId); Long getContractCountByCustomerId(Long customerId);
/** /**
* 根据商机ID获取关联客户的合同数量 TODO @lzxhqs1方法注释和参数注释之间要有空行2中英文之间有空格更清晰例如说 商机 ID * 根据商机ID获取关联客户的合同数量
* @param businessId 商机ID *
* @param businessId 商机编号
* @return 数量 * @return 数量
*/ */
Long selectCountByBusinessId(Long businessId); Long selectCountByBusinessId(Long businessId);
} }

View File

@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 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.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 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.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
@ -40,28 +43,33 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
@Validated @Validated
public class CrmContractServiceImpl implements CrmContractService { public class CrmContractServiceImpl implements CrmContractService {
public static final String CONTRACT_APPROVE = "contract-approve"; // 合同审批流程标识
@Resource @Resource
private CrmContractMapper contractMapper; private CrmContractMapper contractMapper;
@Resource @Resource
private CrmPermissionService crmPermissionService; private CrmPermissionService crmPermissionService;
@Resource
private CrmBusinessProductService businessProductService;
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}", @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
success = CRM_CONTRACT_CREATE_SUCCESS) success = CRM_CONTRACT_CREATE_SUCCESS)
public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) { public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null);
// TODO @合同待定插入合同商品需要搞个 BusinessProductDO // TODO @合同待定插入合同商品需要搞个 BusinessProductDO
// 插入合同 // 插入合同
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class); CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
contractMapper.insert(contract); contractMapper.insert(contract);
// 创建数据权限 // 创建数据权限
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
.setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId()) .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 4. 记录操作日志上下文 // 4. 记录操作日志上下文
LogRecordContext.putVariable("contract", contract); LogRecordContext.putVariable("contract", contract);
return contract.getId(); return contract.getId();
@ -141,6 +149,16 @@ public class CrmContractServiceImpl implements CrmContractService {
contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId())); contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId()));
} }
@Override
public void handleApprove(Long id, Long userId) {
// 创建合同审批流程实例
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
.setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id)));
// 更新合同工作流编号
contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId));
}
//======================= 查询相关 ======================= //======================= 查询相关 =======================
@Override @Override
@ -182,6 +200,5 @@ public class CrmContractServiceImpl implements CrmContractService {
public Long selectCountByBusinessId(Long businessId) { public Long selectCountByBusinessId(Long businessId) {
return contractMapper.selectCountByBusinessId(businessId); return contractMapper.selectCountByBusinessId(businessId);
} }
// TODO @合同待定需要新增一个 ContractConfigDO 合同配置重点是到期提醒 // TODO @合同待定需要新增一个 ContractConfigDO 合同配置重点是到期提醒
} }