feat(bpm): 新增销售业绩目标申请OA功能

- 添加销售业绩目标申请相关的API接口和实现类
- 创建销售业绩目标申请的数据库表和DO类
- 实现销售业绩目标申请的CRUD操作
- 添加销售业绩目标申请的前端页面和表单组件
This commit is contained in:
aikai 2025-03-06 17:51:33 +08:00
parent 74e439eb15
commit 7f474c33cb
77 changed files with 3058 additions and 25 deletions

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.template;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.SocialTypeEnum;
import cn.iocoder.yudao.framework.common.template.dto.ApprovalResultNotificationMessageDTO;
import cn.iocoder.yudao.framework.common.template.dto.PerformanceResultConfirmationReminderDTO;
import cn.iocoder.yudao.framework.common.template.dto.ProcessToDoReminderDTO;
import cn.iocoder.yudao.framework.common.template.vo.MsgData;
import cn.iocoder.yudao.framework.common.template.vo.SubscribeMessageReqDTO;
@ -22,6 +23,10 @@ public class WxMpMsgTemplateUtils {
* OA流程待办提醒
*/
String OA_PROCESS_TO_DO_REMINDER = "rV94N8PbUOz4EQyjpJcucPQOTrBPx2icOZ5F2KNgD40";
/**
* 绩效结果确认提醒
*/
String PERFORMANCE_RESULT_CONFIRMATION_REMINDER = "56VcBVdG__KGmYQzq2q3KUbijUlKwz6KaaK0mzfPQtc";
/**
@ -115,4 +120,33 @@ public class WxMpMsgTemplateUtils {
return message;
}
/**
* 绩效结果确认提醒
*
* @param dto
* @return
*/
public SubscribeMessageReqDTO convertPerformanceResultConfirmationReminder(PerformanceResultConfirmationReminderDTO dto) {
SubscribeMessageReqDTO message = new SubscribeMessageReqDTO();
message.setToUser(dto.getOpenId());
message.setTemplateId(PERFORMANCE_RESULT_CONFIRMATION_REMINDER);
//待办标题
MsgData processType = new MsgData();
processType.setName("thing1");
processType.setValue(dto.getUserName());
message.addData(processType);
//申请人
MsgData applicant = new MsgData();
applicant.setName("time2");
applicant.setValue(dto.getTime());
message.addData(applicant);
message.setJumpWxMaFlag(true);
message.setMiniProgramState(dto.getMiniProgramState());
message.setPage(dto.getPage());
message.setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType());
return message;
}
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.framework.common.template.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class PerformanceResultConfirmationReminderDTO {
@Schema(description = "接收者(用户)的 openid")
private String openId;
@Schema(description = "被考核人")
private String userName;
@Schema(description = "考核时间")
private String time;
@Schema(description = "跳转小程序类型")
private String miniProgramState = "formal";
@Schema(description = "小程序页面地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "pages/home/index")
private String page;
}

View File

@ -0,0 +1,322 @@
package cn.iocoder.yudao.framework.common.util.number;
import cn.hutool.core.util.StrUtil;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @Desc:
* @Author: aikai
* @Date: 2025/04/11
*/
public class BigDecimalUtil {
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 2;
private static final BigDecimal ZERO = new BigDecimal(0);
// 这个类不能实例化
private BigDecimalUtil() {
}
// 是否大于0
public static boolean isGreaterThanZero(Object value) {
if (null == value) {
value = BigDecimal.ZERO;
}
return getBigDecimal(value).compareTo(ZERO) > 0;
}
// 是否大于0
public static boolean isGreaterThanOrEqualToZero(Object value) {
if (null == value) {
value = BigDecimal.ZERO;
}
return getBigDecimal(value).compareTo(ZERO) >= 0;
}
// 是否小于0
public static boolean isLessThanZero(Object value) {
if (null == value) {
value = BigDecimal.ZERO;
}
return getBigDecimal(value).compareTo(ZERO) < 0;
}
// 是否小于等于0
public static boolean isLessThanOrEqualToZero(Object value) {
if (null == value) {
value = BigDecimal.ZERO;
}
return getBigDecimal(value).compareTo(ZERO) <= 0;
}
// 是否大于
public static boolean isGreaterThan(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).compareTo(getBigDecimal(v2)) > 0;
}
// 是否大于等于
public static boolean isGreaterThanOrEqualTo(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).compareTo(getBigDecimal(v2)) >= 0;
}
// 是否小于
public static boolean isLessThan(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).compareTo(getBigDecimal(v2)) < 0;
}
// 是否小于等于
public static boolean isLessThanOrEqualTo(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).compareTo(getBigDecimal(v2)) <= 0;
}
// 是否等于
public static boolean isEqualTo(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
BigDecimal var1 = getBigDecimal(v1);
BigDecimal var2 = getBigDecimal(v2);
return var1.compareTo(var2) == 0;
}
public static boolean isEqZero(Object v1) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
BigDecimal var1 = getBigDecimal(v1);
return var1.compareTo(BigDecimal.ZERO) == 0;
}
// 是否为空 /
public static boolean isEmpty(Object obj) {
if (null == obj) {
return true;
}
if (isEqualTo(BigDecimal.ZERO, obj)) {
return true;
}
return false;
}
public static boolean isNotEmpty(Object obj) {
if (null == obj) {
return false;
}
if (isEqualTo(BigDecimal.ZERO, obj)) {
return false;
}
return true;
}
/**
* 提供精确的加法运算
*
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static BigDecimal add(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).add(getBigDecimal(v2));
}
/**
* 提供精确的减法运算
*
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static BigDecimal sub(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).subtract(getBigDecimal(v2));
}
/**
* 提供精确的乘法运算
*
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static BigDecimal mul(Object v1, Object v2) {
if (null == v1) {
v1 = BigDecimal.ZERO;
}
if (null == v2) {
v2 = BigDecimal.ZERO;
}
return getBigDecimal(v1).multiply(getBigDecimal(v2));
}
/**
* 提供精确的乘法运算
*
* @param v1
* @param arg
* @return 多数
*/
public static BigDecimal mostMul(Object v1, Object... arg) {
if (v1 == null) {
v1 = BigDecimal.ZERO;
}
for (Object obj : arg) {
if (obj == null) {
obj = BigDecimal.ZERO;
}
v1 = getBigDecimal(v1).multiply(getBigDecimal(obj));
}
return (BigDecimal) v1;
}
/**
* 提供相对精确的除法运算当发生除不尽的情况时 精确到小数点以后10位以后的数字四舍五入
*
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static BigDecimal div(Object v1, Object v2) {
return div(getBigDecimal(v1), getBigDecimal(v2), DEF_DIV_SCALE);
}
/**
* 提供相对精确的除法运算 当发生除不尽的情况时由scale参数指定精度以后的数字四舍五入
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位
* @return 两个参数的商
*/
public static BigDecimal div(Object v1, Object v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
return getBigDecimal(v1).divide(getBigDecimal(v2), scale, BigDecimal.ROUND_HALF_UP);
}
public static BigDecimal div(Object v1, Object v2, int scale, int roundingMode) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
return getBigDecimal(v1).divide(getBigDecimal(v2), scale, roundingMode);
}
/**
* Object转BigDecimal类型
*
* @param value 要转的object类型
* @return 转成的BigDecimal类型数据
*/
public static BigDecimal getBigDecimal(Object value) {
BigDecimal ret = null;
if (value != null) {
if (value instanceof BigDecimal) {
ret = (BigDecimal) value;
} else if (value instanceof String) {
if (StrUtil.isEmpty((String) value)) {
value = "0";
}
ret = new BigDecimal((String) value);
} else if (value instanceof BigInteger) {
ret = new BigDecimal((BigInteger) value);
} else if (value instanceof Number) {
ret = new BigDecimal(value.toString());
} else if (value instanceof Integer) {
ret = new BigDecimal((Integer) value);
} else {
throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal.");
}
} else {
return BigDecimal.ZERO;
}
return ret;
}
/**
*
*
* @param i
* @param arg
* @return
*/
public static BigDecimal subs(BigDecimal i, BigDecimal... arg) {
BigDecimal difference = i;
for (BigDecimal b : arg) {
difference = difference.subtract(b);
}
return difference;
}
public static BigDecimal getZeroMax(BigDecimal bigDecimal) {
if (isLessThanZero(bigDecimal)) {
return ZERO;
}
return bigDecimal;
}
/**
*
*
* @param i
* @param arg
* @return
*/
public static BigDecimal adds(BigDecimal i, BigDecimal... arg) {
BigDecimal bigDecimal = i;
if (bigDecimal == null) {
bigDecimal = BigDecimal.ZERO;
}
for (BigDecimal b : arg) {
if (b == null) {
b = BigDecimal.ZERO;
}
bigDecimal = bigDecimal.add(b);
}
return bigDecimal;
}
}

View File

@ -16,7 +16,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
@ -41,4 +40,8 @@ public interface BpmOAContractApi {
@PostMapping(PREFIX + "/getList")
@Operation(summary = "获得合同列表")
CommonResult<List<BpmOAContractDTO>> getContractList(@RequestBody BpmOAContractVO respVO);
@PostMapping(PREFIX + "/updateSettlementFlagByIds")
@Operation(summary = "批量更新合同结算状态")
void updateSettlementFlagByIds(@RequestBody List<Long> contractIds);
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
@ -34,4 +35,8 @@ public interface BpmOAReceiptApi {
@Operation(summary = "获得指定用户的回款统计信息")
CommonResult<List<ReceiptStatisticsDTO>> getContractStatistics(@RequestParam("userId") List<Long> userIds,
@RequestParam(name = "createTime", required = false) LocalDateTime[] createTime);
@GetMapping(PREFIX + "/getReceiptSettlement")
@Operation(summary = "获取用户回款结算信息")
CommonResult<List<ReceiptSettlementDTO>> getReceiptSettlement(@RequestParam(name = "times", required = false) LocalDateTime[] times);
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.salesperformance.SalesPerformanceDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 销售目标")
public interface BpmOASalesPerformanceApi {
String PREFIX = ApiConstants.PREFIX + "/oa/salesPerformance";
@GetMapping(PREFIX + "/getSalesPerformance")
@Operation(summary = "根据日期获取用户销售目标申请")
CommonResult<List<SalesPerformanceDTO>> getReceiptSettlement(@RequestParam(name = "year", required = false) String year,
@RequestParam(name = "month", required = false) String month);
@GetMapping(PREFIX + "/getBySalesPerformanceId")
@Operation(summary = "根据日期获取用户销售目标申请")
CommonResult<SalesPerformanceDTO> getBySalesPerformanceId(@RequestParam(name = "salesPerformanceId", required = false) Long salesPerformanceId);
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.receipt;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ReceiptSettlementDTO {
@Schema(description = "用户编号")
private Long userId;
@Schema(description = "回款总金额")
private BigDecimal money;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.salesperformance;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class SalesPerformanceDTO {
/**
* 销售业绩目标申请id
*/
private Long id;
/**
* 申请人的用户编号
*/
private Long userId;
/**
*
*/
private String year;
/**
*
*/
private String month;
/**
* 回款目标
*/
private BigDecimal paymentTarget;
/**
* 销售目标
*/
private Integer saleTarget;
/**
* 备注
*/
private String remark;
/**
* 申请结果
*/
private Integer result;
}

View File

@ -117,6 +117,10 @@ public interface ErrorCodeConstants {
// ========== 用户组模块 1-009-011-000 ==========
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户组不存在");
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户组已被禁用");
// ========== 业绩申请模块 1-009-012-000 ==========
ErrorCode CAN_ONLY_APPLY_FOR_CURRENT_MONTH_PERFORMANCE_TARGET = new ErrorCode(1_009_012_001, "只能申请当月业绩目标");
ErrorCode THE_PROCESS_IS_BEING_APPROVED_OR_HAS_PASSED_THE_CURRENT_MONTH_AND_THE_APPLICATION_CANNOT_BE_REPEATED
= new ErrorCode(1_009_012_002, "流程审批中或已通过当月不可重复申请");
ErrorCode BPM_SYSTEM_BUG = new ErrorCode(1_009_012_001, "系统问题,请联系管理员");

View File

@ -52,4 +52,9 @@ public class BpmOAContractApiImpl implements BpmOAContractApi {
List<BpmOAContractDO> contractDO = contractService.getContractList(respVO);
return success(BeanUtils.toBean(contractDO, BpmOAContractDTO.class));
}
@Override
public void updateSettlementFlagByIds(List<Long> contractIds) {
contractService.updateSettlementFlagByIds(contractIds);
}
}

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
@ -45,4 +46,9 @@ public class BpmOAReceiptApiImpl implements BpmOAReceiptApi{
List<ReceiptStatisticsDTO> respVOS = receiptService.getReceiptStatisticsListByUserIds(userIds, createTime);
return success(BeanUtils.toBean(respVOS, ReceiptStatisticsDTO.class));
}
@Override
public CommonResult<List<ReceiptSettlementDTO>> getReceiptSettlement(LocalDateTime[] times) {
return success(receiptService.getReceiptSettlement(times));
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.salesperformance.SalesPerformanceDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOASalesPerformanceService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* Flowable 流程实例 Api 实现类
*/
@RestController
@Validated
public class BpmOASalesPerformanceApiImpl implements BpmOASalesPerformanceApi {
@Resource
private BpmOASalesPerformanceService salesPerformanceService;
@Override
public CommonResult<List<SalesPerformanceDTO>> getReceiptSettlement(String year, String month) {
List<BpmOASalesPerformanceDO> list = salesPerformanceService.list(new LambdaQueryWrapper<BpmOASalesPerformanceDO>()
.eq(BpmOASalesPerformanceDO::getYear, year)
.eq(BpmOASalesPerformanceDO::getMonth, month)
.eq(BpmOASalesPerformanceDO::getResult, 2));
return CommonResult.success(BeanUtil.copyToList(list, SalesPerformanceDTO.class));
}
@Override
public CommonResult<SalesPerformanceDTO> getBySalesPerformanceId(Long salesPerformanceId) {
BpmOASalesPerformanceDO item = salesPerformanceService.getById(salesPerformanceId);
return CommonResult.success(BeanUtil.copyProperties(item, SalesPerformanceDTO.class));
}
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformancePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformanceRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformanceSaveReqVO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOASalesPerformanceService;
@Tag(name = "管理后台 - 销售业绩目标申请OA")
@RestController
@RequestMapping("/bpm/sales-performance")
@Validated
public class SalesPerformanceController {
@Resource
private BpmOASalesPerformanceService bpmOASalesPerformanceService;
@PostMapping("/create")
@Operation(summary = "创建销售业绩目标申请OA")
@PreAuthorize("@ss.hasPermission('bpm:sales-performance:create')")
public CommonResult<Long> createSalesPerformance(@Valid @RequestBody SalesPerformanceSaveReqVO createReqVO) {
return success(bpmOASalesPerformanceService.createSalesPerformance(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新销售业绩目标申请OA")
@PreAuthorize("@ss.hasPermission('bpm:sales-performance:update')")
public CommonResult<Boolean> updateSalesPerformance(@Valid @RequestBody SalesPerformanceSaveReqVO updateReqVO) {
bpmOASalesPerformanceService.updateSalesPerformance(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除销售业绩目标申请OA")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:sales-performance:delete')")
public CommonResult<Boolean> deleteSalesPerformance(@RequestParam("id") Long id) {
bpmOASalesPerformanceService.deleteSalesPerformance(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得销售业绩目标申请OA")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:sales-performance:query')")
public CommonResult<SalesPerformanceRespVO> getSalesPerformance(@RequestParam("id") Long id) {
BpmOASalesPerformanceDO salesPerformance = bpmOASalesPerformanceService.getSalesPerformance(id);
return success(BeanUtils.toBean(salesPerformance, SalesPerformanceRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得销售业绩目标申请OA分页")
@PreAuthorize("@ss.hasPermission('bpm:sales-performance:query')")
public CommonResult<PageResult<SalesPerformanceRespVO>> getSalesPerformancePage(@Valid SalesPerformancePageReqVO pageReqVO) {
PageResult<BpmOASalesPerformanceDO> pageResult = bpmOASalesPerformanceService.getSalesPerformancePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, SalesPerformanceRespVO.class));
}
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance;
import lombok.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 销售业绩目标申请OA分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SalesPerformancePageReqVO extends PageParam {
@Schema(description = "申请人的用户编号", example = "2022")
private Long userId;
@Schema(description = "")
private String year;
@Schema(description = "")
private String month;
@Schema(description = "回款目标")
private BigDecimal paymentTarget;
@Schema(description = "销售目标")
private Integer saleTarget;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "申请结果")
private Integer result;
@Schema(description = "流程实例的编号", example = "1600")
private String processInstanceId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 销售业绩目标申请OA Response VO")
@Data
public class SalesPerformanceRespVO {
@Schema(description = "销售业绩目标申请", requiredMode = Schema.RequiredMode.REQUIRED, example = "1883")
private Long id;
@Schema(description = "申请人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022")
private Long userId;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
private String year;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
private String month;
@Schema(description = "回款目标")
private BigDecimal paymentTarget;
@Schema(description = "销售目标")
private Integer saleTarget;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "申请结果", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer result;
@Schema(description = "流程实例的编号", example = "1600")
private String processInstanceId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 销售业绩目标申请OA新增/修改 Request VO")
@Data
public class SalesPerformanceSaveReqVO {
@Schema(description = "销售业绩目标申请", requiredMode = Schema.RequiredMode.REQUIRED, example = "1883")
private Long id;
@Schema(description = "申请人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022")
private Long userId;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "年不能为空")
private String year;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "月不能为空")
private String month;
@Schema(description = "回款目标")
private BigDecimal paymentTarget;
@Schema(description = "销售目标")
private Integer saleTarget;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "申请结果", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer result;
@Schema(description = "流程实例的编号", example = "1600")
private String processInstanceId;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.bpm.convert.oa;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformanceSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 销售业绩目标申请OA Convert
*
* @author 艾楷
*/
@Mapper
public interface BpmOASalesPerformanceConvert {
BpmOASalesPerformanceConvert INSTANCE = Mappers.getMapper(BpmOASalesPerformanceConvert.class);
BpmOASalesPerformanceDO convert(SalesPerformanceSaveReqVO createReqVO);
}

View File

@ -134,6 +134,11 @@ public class BpmOAContractDO extends BaseDO {
*/
private Integer status;
/**
* 是否结算 0否 1是
*/
private Integer settlementFlag;
/**
* 审批结果
* 枚举 {@link BpmProcessInstanceResultEnum}

View File

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
import lombok.*;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 销售业绩目标申请OA DO
*
* @author 艾楷
*/
@TableName("bpm_oa_sales_performance")
@KeySequence("bpm_oa_sales_performance_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmOASalesPerformanceDO extends BaseDO {
/**
* 销售业绩目标申请
*/
@TableId
private Long id;
/**
* 申请人的用户编号
*/
private Long userId;
/**
*
*/
private String year;
/**
*
*/
private String month;
/**
* 回款目标
*/
private BigDecimal paymentTarget;
/**
* 销售目标
*/
private Integer saleTarget;
/**
* 备注
*/
private String remark;
/**
* 申请结果
*/
private Integer result;
/**
* 流程实例的编号
*/
private String processInstanceId;
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
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.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
@ -31,16 +32,16 @@ public interface BpmOAReceiptMapper extends BaseMapperX<BpmOAReceiptDO> {
query.eqIfPresent(BpmOAReceiptDO::getResult, pageReqVO.getResult());
query.apply(Objects.nonNull(pageReqVO.getCustomerName()), "c.customer_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getCustomerName());
query.apply(Objects.nonNull(pageReqVO.getContractName()), "c.contract_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getContractName());
query.apply(Objects.nonNull(pageReqVO.getCreateName()),"u.nickname Like CONCAT('%', {0}, '%')", pageReqVO.getCreateName());
query.apply(Objects.nonNull(pageReqVO.getCreateName()), "u.nickname Like CONCAT('%', {0}, '%')", pageReqVO.getCreateName());
if ("my".equals(pageReqVO.getRelation())) {
query.eq(BpmOAContractDO::getUserId, userId);
}else if ("sub".equals(pageReqVO.getRelation())){
} else if ("sub".equals(pageReqVO.getRelation())) {
query.innerJoin("(" +
"\tSELECT DISTINCT\n" +
"\t\tu.id \n" +
"\tFROM\n" +
"\t\tsystem_users u\n" +
"\t\tINNER JOIN system_dept d ON d.leader_user_id = "+ userId +"\n" +
"\t\tINNER JOIN system_dept d ON d.leader_user_id = " + userId + "\n" +
"\t\tINNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '%-', d.id, '-%' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '-', d.id, '%' ) \n" +
@ -59,4 +60,12 @@ public interface BpmOAReceiptMapper extends BaseMapperX<BpmOAReceiptDO> {
List<ReceiptStatisticsDTO> selectReceiptStatisticsListByUserIds(@Param("userIds") List<Long> userIds,
@Param("createTime") LocalDateTime[] createTime);
/**
* 获取用户结算回款信息
*
* @param times
* @return
*/
List<ReceiptSettlementDTO> getReceiptSettlement(@Param("times") LocalDateTime[] times);
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformancePageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 销售业绩目标申请OA Mapper
*
* @author 艾楷
*/
@Mapper
public interface BpmOASalesPerformanceMapper extends BaseMapperX<BpmOASalesPerformanceDO> {
default PageResult<BpmOASalesPerformanceDO> selectPage(SalesPerformancePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmOASalesPerformanceDO>()
.eqIfPresent(BpmOASalesPerformanceDO::getUserId, reqVO.getUserId())
.eqIfPresent(BpmOASalesPerformanceDO::getYear, reqVO.getYear())
.eqIfPresent(BpmOASalesPerformanceDO::getMonth, reqVO.getMonth())
.eqIfPresent(BpmOASalesPerformanceDO::getPaymentTarget, reqVO.getPaymentTarget())
.eqIfPresent(BpmOASalesPerformanceDO::getSaleTarget, reqVO.getSaleTarget())
.eqIfPresent(BpmOASalesPerformanceDO::getRemark, reqVO.getRemark())
.eqIfPresent(BpmOASalesPerformanceDO::getResult, reqVO.getResult())
.eqIfPresent(BpmOASalesPerformanceDO::getProcessInstanceId, reqVO.getProcessInstanceId())
.betweenIfPresent(BpmOASalesPerformanceDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmOASalesPerformanceDO::getId));
}
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.rpc.config;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.BusinessApi;
import cn.iocoder.yudao.module.hrm.api.crmcontract.ContractApi;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.CrmCustomerApi;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.SalesPerformanceSettlementApi;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi;
@ -42,8 +43,8 @@ import org.springframework.context.annotation.Configuration;
@EnableFeignClients(clients = {FileApi.class, RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class,
SubscribeMessageSendApi.class, SocialClientApi.class, UsersExtApi.class, AttendanceApi.class, BankApi.class, ConfigApi.class, PositionApi.class, SupplierApi.class, AssetsApi.class,
AssetsTypeApi.class, AssetReceiveApi.class, AttendanceApi.class, AttendanceGroupApi.class, WorkOvertimeApi.class, HolidayApi.class,
RentalOrderApi.class, RentalDepositRecordApi.class, ProjectApi.class, RentalItemsRecordApi.class,AdminOauthUserOtherInfoApi.class, StoreProductAttrValueApi.class, StoreProductApi.class,
ContractApi.class, BusinessApi.class, CrmCustomerApi.class, StaffApi.class, LoanApi.class, FactoryInfoApi.class
RentalOrderApi.class, RentalDepositRecordApi.class, ProjectApi.class, RentalItemsRecordApi.class, AdminOauthUserOtherInfoApi.class, StoreProductAttrValueApi.class, StoreProductApi.class,
ContractApi.class, BusinessApi.class, CrmCustomerApi.class, StaffApi.class, LoanApi.class, FactoryInfoApi.class, SalesPerformanceSettlementApi.class
})
public class RpcConfiguration {
}

View File

@ -120,4 +120,11 @@ public interface BpmOAContractService {
* @return 合同列表
*/
List<BpmOAContractDO> getListByCustomerId(Long customerId);
/**
* 批量更新合同结算状态
*
* @param contractIds
*/
void updateSettlementFlagByIds(List<Long> contractIds);
}

View File

@ -30,6 +30,7 @@ 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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
@ -332,6 +333,12 @@ public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAC
.eq(BpmOAContractDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult()));
}
@Override
public void updateSettlementFlagByIds(List<Long> contractIds) {
contractMapper.update(new BpmOAContractDO().setSettlementFlag(1), new LambdaQueryWrapper<BpmOAContractDO>()
.in(BpmOAContractDO::getId, contractIds));
}
public static BigDecimal calculatePercentageChange(Object today, Object yesterday) {
// 转换为 BigDecimal 进行高精度计算

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
@ -42,6 +43,7 @@ public interface BpmOAReceiptService {
/**
* 获得指定的回款申请
*
* @param processInstanceId 流程实例编号
* @return 退款申请
*/
@ -49,6 +51,7 @@ public interface BpmOAReceiptService {
/**
* 获得回款申请分页列表
*
* @param pageReqVO 分页信息
* @return 分页列表
*/
@ -56,6 +59,7 @@ public interface BpmOAReceiptService {
/**
* 获得回款统计信息
*
* @param userId 用户编号
* @param relation 查询类型
* @return 回款统计信息
@ -64,6 +68,7 @@ public interface BpmOAReceiptService {
/**
* 获得回款列表
*
* @param respVO 参数信息
* @return 回款信息列表
*/
@ -71,9 +76,18 @@ public interface BpmOAReceiptService {
/**
* 获得指定用户回款统计信息
*
* @param userIds 用户编号
* @param createTime 创建时间
* @return 回款统计信息
*/
List<ReceiptStatisticsDTO> getReceiptStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime);
/**
* 获取用户结算回款信息
*
* @param times
* @return
*/
List<ReceiptSettlementDTO> getReceiptSettlement(LocalDateTime[] times);
}

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
@ -25,6 +26,7 @@ import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -160,6 +162,11 @@ public class BpmOAReceiptServiceImpl extends BpmOABaseService implements BpmOARe
return receiptMapper.selectReceiptStatisticsListByUserIds(userIds, createTime);
}
@Override
public List<ReceiptSettlementDTO> getReceiptSettlement(LocalDateTime[] times) {
return receiptMapper.getReceiptSettlement(times);
}
public static BigDecimal calculatePercentageChange(Object today, Object yesterday) {
// 转换为 BigDecimal 进行高精度计算

View File

@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformancePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformanceSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid;
/**
* 销售业绩目标申请OA Service 接口
*
* @author 艾楷
*/
public interface BpmOASalesPerformanceService extends IService<BpmOASalesPerformanceDO> {
/**
* 创建销售业绩目标申请OA
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSalesPerformance(@Valid SalesPerformanceSaveReqVO createReqVO);
/**
* 更新销售业绩目标申请OA
*
* @param updateReqVO 更新信息
*/
void updateSalesPerformance(@Valid SalesPerformanceSaveReqVO updateReqVO);
/**
* 删除销售业绩目标申请OA
*
* @param id 编号
*/
void deleteSalesPerformance(Long id);
/**
* 获得销售业绩目标申请OA
*
* @param id 编号
* @return 销售业绩目标申请OA
*/
BpmOASalesPerformanceDO getSalesPerformance(Long id);
/**
* 获得销售业绩目标申请OA分页
*
* @param pageReqVO 分页查询
* @return 销售业绩目标申请OA分页
*/
PageResult<BpmOASalesPerformanceDO> getSalesPerformancePage(SalesPerformancePageReqVO pageReqVO);
/**
* 更新销售业绩目标申请的状态
*
* @param processInstanceId 流程实例id
* @param id
* @param result 结果
*/
void updateSalesPerformanceResult(String processInstanceId, Long id, Integer result);
}

View File

@ -0,0 +1,134 @@
package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.bpm.controller.admin.oa.vo.salesperformance.SalesPerformancePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.salesperformance.SalesPerformanceSaveReqVO;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOASalesPerformanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASalesPerformanceDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOASalesPerformanceMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.SalesPerformanceSettlementApi;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.dto.SalesPerformanceSettlementDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CAN_ONLY_APPLY_FOR_CURRENT_MONTH_PERFORMANCE_TARGET;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.THE_PROCESS_IS_BEING_APPROVED_OR_HAS_PASSED_THE_CURRENT_MONTH_AND_THE_APPLICATION_CANNOT_BE_REPEATED;
/**
* 销售业绩目标申请OA Service 实现类
*
* @author 艾楷
*/
@Service
@Validated
public class BpmOASalesPerformanceServiceImpl extends ServiceImpl<BpmOASalesPerformanceMapper, BpmOASalesPerformanceDO> implements BpmOASalesPerformanceService {
@Resource
private BpmOASalesPerformanceMapper bpmOASalesPerformanceMapper;
@Resource
private BpmProcessInstanceApi processInstanceApi;
@Resource
private BpmHistoryProcessInstanceService historyProcessInstanceService;
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private SalesPerformanceSettlementApi salesPerformanceSettlementApi;
/**
* OA 销售业绩目标申请OA对应的流程定义 KEY
*/
public static final String PROCESS_KEY = "oa_sales_performance";
@Override
public Long createSalesPerformance(SalesPerformanceSaveReqVO createReqVO) {
// 判断是否是当月的目标
LocalDateTime now = LocalDateTime.now();
String year = String.valueOf(now.getYear());
String month = String.format("%02d", now.getMonthValue());
Long userId = getLoginUserId();
if (!year.equals(createReqVO.getYear()) || !month.equals(createReqVO.getMonth())) {
throw exception(CAN_ONLY_APPLY_FOR_CURRENT_MONTH_PERFORMANCE_TARGET);
}
// 判断是否已经存在
Long count = bpmOASalesPerformanceMapper.selectCount(new LambdaQueryWrapper<BpmOASalesPerformanceDO>()
.eq(BpmOASalesPerformanceDO::getUserId, userId)
.eq(BpmOASalesPerformanceDO::getYear, year)
.eq(BpmOASalesPerformanceDO::getMonth, month)
.in(BpmOASalesPerformanceDO::getResult, BpmProcessInstanceResultEnum.PROCESS.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult()));
if (count > 0L) {
throw exception(THE_PROCESS_IS_BEING_APPROVED_OR_HAS_PASSED_THE_CURRENT_MONTH_AND_THE_APPLICATION_CANNOT_BE_REPEATED);
}
BpmOASalesPerformanceDO bpmOASalesPerformanceDO = BpmOASalesPerformanceConvert.INSTANCE.convert(createReqVO).setUserId(userId)
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
bpmOASalesPerformanceMapper.insert(bpmOASalesPerformanceDO);
// 发起 BPM 流程
String processInstanceId = processInstanceApi.createProcessInstance(userId,
new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
.setBusinessKey(String.valueOf(bpmOASalesPerformanceDO.getId()))).getCheckedData();
bpmOASalesPerformanceMapper.updateById(bpmOASalesPerformanceDO.setProcessInstanceId(processInstanceId));
// 判断是否为重新发起的流程
if (createReqVO.getProcessInstanceId() != null && createReqVO.getResult() == 3) {
historyProcessInstanceService.createHistoryProcessInstance(processInstanceId, createReqVO.getProcessInstanceId());
}
// 返回
return bpmOASalesPerformanceDO.getId();
}
@Override
public void updateSalesPerformance(SalesPerformanceSaveReqVO updateReqVO) {
// 更新
BpmOASalesPerformanceDO updateObj = BeanUtils.toBean(updateReqVO, BpmOASalesPerformanceDO.class);
bpmOASalesPerformanceMapper.updateById(updateObj);
}
@Override
public void deleteSalesPerformance(Long id) {
// 删除
bpmOASalesPerformanceMapper.deleteById(id);
}
@Override
public BpmOASalesPerformanceDO getSalesPerformance(Long id) {
return bpmOASalesPerformanceMapper.selectById(id);
}
@Override
public PageResult<BpmOASalesPerformanceDO> getSalesPerformancePage(SalesPerformancePageReqVO pageReqVO) {
return bpmOASalesPerformanceMapper.selectPage(pageReqVO);
}
@Override
public void updateSalesPerformanceResult(String processInstanceId, Long id, Integer result) {
//审核通过 最后节点
if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {
ProcessInstance instance = processInstanceService.getProcessInstance(processInstanceId);
if (instance.isEnded()) {
// -- 拿到当前的id set到表中目标表中
// 当前用户
BpmOASalesPerformanceDO bpmOASalesPerformanceDO = bpmOASalesPerformanceMapper.selectById(id);
salesPerformanceSettlementApi.update(new SalesPerformanceSettlementDTO().setSalesPerformanceId(id)
.setYear(bpmOASalesPerformanceDO.getYear())
.setMonth(bpmOASalesPerformanceDO.getMonth())
.setUserId(bpmOASalesPerformanceDO.getUserId()));
}
}
bpmOASalesPerformanceMapper.updateById(new BpmOASalesPerformanceDO().setId(id).setResult(result));
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.service.oa.listener;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventListener;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOASalesPerformanceService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOASalesPerformanceServiceImpl;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* OA 销售业绩目标申请OA
*
* @author 符溶馨
*/
@Component
public class BpmOASalesPerformanceResultListener extends BpmProcessInstanceResultEventListener {
@Resource
private BpmOASalesPerformanceService salesPerformanceService;
@Override
protected String getProcessDefinitionKey() {
return BpmOASalesPerformanceServiceImpl.PROCESS_KEY;
}
@Override
protected void onEvent(BpmProcessInstanceResultEvent event) {
salesPerformanceService.updateSalesPerformanceResult(event.getId(), Long.parseLong(event.getBusinessKey()), event.getResult());
}
}

View File

@ -59,4 +59,24 @@
</if>
GROUP BY user_id
</select>
<select id="getReceiptSettlement"
resultType="cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO">
SELECT
user_id AS userId,
SUM(money) AS money
FROM
bpm_oa_receipt
WHERE
deleted = 0
AND result = 2
<if test="times != null and times.length > 0">
<if test="times[0] != null">
and create_time &gt;= #{times[0]}
</if>
<if test="times[1] != null">
and create_time &lt;= #{times[1]}
</if>
</if>
GROUP BY user_id
</select>
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOASalesPerformanceMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.hrm.api.crmcontract.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CrmContractProductSettlementDTO {
@Schema(description = "crm申请用户id")
private Long userId;
@Schema(description = "合同编号")
private Long contractId;
@Schema(description = "产品编号")
private Long productId;
@Schema(description = "产品单价")
private BigDecimal price;
@Schema(description = "数量")
private Integer nums;
@Schema(description = "折扣")
private BigDecimal discount;
@Schema(description = "小计")
private BigDecimal subtotal;
@Schema(description = "备注")
private String remarks;
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.hrm.api.salesperformancesettlement;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.dto.CrmCustomerDTO;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.dto.SalesPerformanceSettlementDTO;
import cn.iocoder.yudao.module.hrm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 销售业绩目标")
public interface SalesPerformanceSettlementApi {
String PREFIX = ApiConstants.PREFIX + "/crm/salesPerformanceSettlement";
@PostMapping(PREFIX + "/update")
@Operation(summary = "更新")
CommonResult<Boolean> update(@RequestBody SalesPerformanceSettlementDTO salesPerformanceSettlementDTO);
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class SalesPerformanceSettlementDTO {
@Schema(description = "用户id")
private Long userId;
@Schema(description = "")
private String year;
@Schema(description = "")
private String month;
@Schema(description = "销售业绩目标申请ID", example = "14373")
private Long salesPerformanceId;
}

View File

@ -14,4 +14,5 @@ public interface ErrorCodeConstants {
ErrorCode CONTRACT_RECEIVABLES_NOT_EXISTS = new ErrorCode(200007, "回款不存在");
ErrorCode INVOICE_NOT_EXISTS = new ErrorCode(200008, "发票不存在");
ErrorCode ACHIEVEMENT_NOT_EXISTS = new ErrorCode(200009, "业绩目标不存在");
ErrorCode NOT_EDITABLE_UNTIL_SALES_TARGET_HAS_BEEN_APPLIED = new ErrorCode(200010, "未申请销售目标前不可编辑");
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractProduct
import cn.iocoder.yudao.module.crm.service.crmcontract.CrmContractService;
import cn.iocoder.yudao.module.hrm.api.crmcontract.ContractApi;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductDTO;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.crm.api.salesperformancesettlement;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO;
import cn.iocoder.yudao.module.crm.service.salesperformancesettlement.SalesPerformanceSettlementService;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.SalesPerformanceSettlementApi;
import cn.iocoder.yudao.module.hrm.api.salesperformancesettlement.dto.SalesPerformanceSettlementDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Flowable CRM Api 实现类
*/
@RestController
@Validated
public class SalesPerformanceSettlementApImpl implements SalesPerformanceSettlementApi {
@Resource
private SalesPerformanceSettlementService salesPerformanceSettlementService;
@Override
public CommonResult<Boolean> update(SalesPerformanceSettlementDTO salesPerformanceSettlementDTO) {
salesPerformanceSettlementService.update(new SalesPerformanceSettlementDO().setSalesPerformanceId(salesPerformanceSettlementDTO.getSalesPerformanceId()),
new LambdaQueryWrapper<SalesPerformanceSettlementDO>().eq(SalesPerformanceSettlementDO::getYear, salesPerformanceSettlementDTO.getYear())
.eq(SalesPerformanceSettlementDO::getMonth, salesPerformanceSettlementDTO.getMonth())
.eq(SalesPerformanceSettlementDO::getUserId, salesPerformanceSettlementDTO.getUserId()));
return CommonResult.success(true);
}
}

View File

@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment.SalesPerformanceAssessmentDO;
import cn.iocoder.yudao.module.crm.service.salesperformanceassessment.SalesPerformanceAssessmentService;
@Tag(name = "管理后台 - 绩效考核设置")
@RestController
@RequestMapping("/crm/sales-performance-assessment")
@Validated
public class SalesPerformanceAssessmentController {
@Resource
private SalesPerformanceAssessmentService salesPerformanceAssessmentService;
@PostMapping("/create")
@Operation(summary = "创建绩效考核设置")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-assessment:create')")
public CommonResult<Long> createSalesPerformanceAssessment(@Valid @RequestBody SalesPerformanceAssessmentSaveReqVO createReqVO) {
return success(salesPerformanceAssessmentService.createSalesPerformanceAssessment(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新绩效考核设置")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-assessment:update')")
public CommonResult<Boolean> updateSalesPerformanceAssessment(@Valid @RequestBody SalesPerformanceAssessmentSaveReqVO updateReqVO) {
salesPerformanceAssessmentService.updateSalesPerformanceAssessment(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除绩效考核设置")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('crm:sales-performance-assessment:delete')")
public CommonResult<Boolean> deleteSalesPerformanceAssessment(@RequestParam("id") Long id) {
salesPerformanceAssessmentService.deleteSalesPerformanceAssessment(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得绩效考核设置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-assessment:query')")
public CommonResult<SalesPerformanceAssessmentRespVO> getSalesPerformanceAssessment(@RequestParam("id") Long id) {
SalesPerformanceAssessmentDO salesPerformanceAssessment = salesPerformanceAssessmentService.getSalesPerformanceAssessment(id);
return success(BeanUtils.toBean(salesPerformanceAssessment, SalesPerformanceAssessmentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得绩效考核设置分页")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-assessment:query')")
public CommonResult<PageResult<SalesPerformanceAssessmentRespVO>> getSalesPerformanceAssessmentPage(@Valid SalesPerformanceAssessmentPageReqVO pageReqVO) {
PageResult<SalesPerformanceAssessmentDO> pageResult = salesPerformanceAssessmentService.getSalesPerformanceAssessmentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, SalesPerformanceAssessmentRespVO.class));
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 绩效考核设置分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SalesPerformanceAssessmentPageReqVO extends PageParam {
@Schema(description = "计算表达式 例如 10<=n&&n<=20")
private String calculationExpression;
@Schema(description = "核发比例%")
private BigDecimal proportion;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 绩效考核设置 Response VO")
@Data
@ExcelIgnoreUnannotated
public class SalesPerformanceAssessmentRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "3321")
@ExcelProperty("id")
private Long id;
@Schema(description = "计算表达式 例如 10<=n&&n<=20")
@ExcelProperty("计算表达式 例如 10<=n&&n<=20")
private String calculationExpression;
@Schema(description = "核发比例%")
@ExcelProperty("核发比例%")
private BigDecimal proportion;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 绩效考核设置新增/修改 Request VO")
@Data
public class SalesPerformanceAssessmentSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "3321")
private Long id;
@Schema(description = "计算表达式 例如 10<=n&&n<=20")
private String calculationExpression;
@Schema(description = "核发比例%")
private BigDecimal proportion;
}

View File

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement;
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.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.salesperformancesettlement.vo.SalesPerformanceSettlementPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.userlivetree.UserLiveTreeDO;
import cn.iocoder.yudao.module.crm.service.salesperformancesettlement.SalesPerformanceSettlementService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 销售业绩结算记录")
@RestController
@RequestMapping("/crm/sales-performance-settlement")
@Validated
public class SalesPerformanceSettlementController {
@Resource
private SalesPerformanceSettlementService salesPerformanceSettlementService;
@PutMapping("/update")
@Operation(summary = "更新销售业绩结算记录")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-settlement:update')")
public CommonResult<Boolean> updateSalesPerformanceSettlement(@Valid @RequestBody SalesPerformanceSettlementSaveReqVO updateReqVO) {
salesPerformanceSettlementService.updateSalesPerformanceSettlement(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除销售业绩结算记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('crm:sales-performance-settlement:delete')")
public CommonResult<Boolean> deleteSalesPerformanceSettlement(@RequestParam("id") Long id) {
salesPerformanceSettlementService.deleteSalesPerformanceSettlement(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得销售业绩结算记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-settlement:query')")
public CommonResult<SalesPerformanceSettlementRespVO> getSalesPerformanceSettlement(@RequestParam("id") Long id) {
SalesPerformanceSettlementDO salesPerformanceSettlement = salesPerformanceSettlementService.getSalesPerformanceSettlement(id);
return success(BeanUtils.toBean(salesPerformanceSettlement, SalesPerformanceSettlementRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得销售业绩结算记录分页")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-settlement:query')")
public CommonResult<PageResult<SalesPerformanceSettlementRespVO>> getSalesPerformanceSettlementPage(@Valid SalesPerformanceSettlementPageReqVO pageReqVO) {
PageResult<SalesPerformanceSettlementDO> pageResult = salesPerformanceSettlementService.getSalesPerformanceSettlementPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, SalesPerformanceSettlementRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出销售业绩结算记录 Excel")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-settlement:export')")
@OperateLog(type = EXPORT)
public void exportSalesPerformanceSettlementExcel(@Valid SalesPerformanceSettlementPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<SalesPerformanceSettlementDO> list = salesPerformanceSettlementService.getSalesPerformanceSettlementPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "销售业绩结算记录.xls", "数据", SalesPerformanceSettlementRespVO.class,
BeanUtils.toBean(list, SalesPerformanceSettlementRespVO.class));
}
@GetMapping("/test")
@Operation(summary = "测试")
@PermitAll
public CommonResult<Boolean> test() {
salesPerformanceSettlementService.settlement();
return success(true);
}
}

View File

@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 销售业绩结算记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SalesPerformanceSettlementPageReqVO extends PageParam {
@Schema(description = "用户id", example = "27486")
private Long userId;
@Schema(description = "销售业绩目标申请ID", example = "14373")
private Long salesPerformanceId;
@Schema(description = "")
private String year;
@Schema(description = "")
private String month;
@Schema(description = "实际回款额")
private BigDecimal actualPayment;
@Schema(description = "实际销售额")
private Integer actualSale;
@Schema(description = "评分")
private BigDecimal score;
@Schema(description = "销售任务%")
private BigDecimal saleTask;
@Schema(description = "回款任务%")
private BigDecimal paymentTask;
@Schema(description = "评分任务%")
private BigDecimal scoreTask;
@Schema(description = "总分")
private BigDecimal totalScore;
@Schema(description = "核发比例")
private BigDecimal proportionOfIssuance;
@Schema(description = "是否确认 0否 1是")
private Integer confirmFlag;
@Schema(description = "是否可以确认 0否 1是")
private Integer canConfirmFlag;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "用户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "27486")
private String nickname;
}

View File

@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 销售业绩结算记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class SalesPerformanceSettlementRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23686")
@ExcelProperty("id")
private Long id;
@Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "27486")
@ExcelProperty("用户id")
private Long userId;
@Schema(description = "用户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "27486")
@ExcelProperty("用户名称")
private String nickname;
@Schema(description = "销售业绩目标申请ID", example = "14373")
@ExcelProperty("销售业绩目标申请ID")
private Long salesPerformanceId;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("")
private String year;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("")
private String month;
@Schema(description = "实际回款额")
private BigDecimal actualPayment;
@Schema(description = "实际销售额")
private Integer actualSale;
@Schema(description = "评分")
private BigDecimal score;
@Schema(description = "销售任务%")
private BigDecimal saleTask;
@Schema(description = "回款任务%")
private BigDecimal paymentTask;
@Schema(description = "评分任务%")
private BigDecimal scoreTask;
@Schema(description = "总分")
private BigDecimal totalScore;
@Schema(description = "核发比例")
private BigDecimal proportionOfIssuance;
@Schema(description = "是否确认 0否 1是")
private Integer confirmFlag;
@Schema(description = "是否可以确认 0否 1是")
private Integer canConfirmFlag;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 销售业绩结算记录新增/修改 Request VO")
@Data
public class SalesPerformanceSettlementSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23686")
private Long id;
@Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "27486")
@NotNull(message = "用户id不能为空")
private Long userId;
@Schema(description = "销售业绩目标申请ID", example = "14373")
private Long salesPerformanceId;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "年不能为空")
private String year;
@Schema(description = "", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "月不能为空")
private String month;
@Schema(description = "实际回款额")
private BigDecimal actualPayment;
@Schema(description = "实际销售额")
private Integer actualSale;
@Schema(description = "评分")
private BigDecimal score;
@Schema(description = "销售任务%")
private BigDecimal saleTask;
@Schema(description = "回款任务%")
private BigDecimal paymentTask;
@Schema(description = "评分任务%")
private BigDecimal scoreTask;
@Schema(description = "总分")
private BigDecimal totalScore;
@Schema(description = "核发比例")
private BigDecimal proportionOfIssuance;
@Schema(description = "是否确认 0否 1是")
private Integer confirmFlag;
@Schema(description = "是否可以确认 0否 1是")
private Integer canConfirmFlag;
}

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo.SalesPerformanceWeightRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo.SalesPerformanceWeightSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight.SalesPerformanceWeightDO;
import cn.iocoder.yudao.module.crm.service.salesperformanceweight.SalesPerformanceWeightService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 销售业绩权重设置")
@RestController
@RequestMapping("/crm/sales-performance-weight")
@Validated
public class SalesPerformanceWeightController {
@Resource
private SalesPerformanceWeightService salesPerformanceWeightService;
@PutMapping("/saveOrEdit")
@Operation(summary = "新增/修改销售业绩权重设置")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-weight:update')")
public CommonResult<Boolean> saveOrEdit(@Valid @RequestBody SalesPerformanceWeightSaveReqVO updateReqVO) {
salesPerformanceWeightService.saveOrEdit(updateReqVO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得销售业绩权重设置")
@PreAuthorize("@ss.hasPermission('crm:sales-performance-weight:query')")
public CommonResult<SalesPerformanceWeightRespVO> getSalesPerformanceWeight() {
SalesPerformanceWeightDO salesPerformanceWeight = salesPerformanceWeightService.getSalesPerformanceWeight();
return success(BeanUtils.toBean(salesPerformanceWeight, SalesPerformanceWeightRespVO.class));
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 销售业绩权重设置分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SalesPerformanceWeightPageReqVO extends PageParam {
@Schema(description = "销售权重%")
private BigDecimal saleWeight;
@Schema(description = "回款权重%")
private BigDecimal paymentWeight;
@Schema(description = "评分权重%")
private BigDecimal scoreWeight;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 销售业绩权重设置 Response VO")
@Data
@ExcelIgnoreUnannotated
public class SalesPerformanceWeightRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2375")
@ExcelProperty("id")
private Long id;
@Schema(description = "销售权重%")
@ExcelProperty("销售权重%")
private BigDecimal saleWeight;
@Schema(description = "回款权重%")
@ExcelProperty("回款权重%")
private BigDecimal paymentWeight;
@Schema(description = "评分权重%")
@ExcelProperty("评分权重%")
private BigDecimal scoreWeight;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 销售业绩权重设置新增/修改 Request VO")
@Data
public class SalesPerformanceWeightSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2375")
private Long id;
@Schema(description = "销售权重%")
private BigDecimal saleWeight;
@Schema(description = "回款权重%")
private BigDecimal paymentWeight;
@Schema(description = "评分权重%")
private BigDecimal scoreWeight;
}

View File

@ -21,6 +21,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
@ -81,6 +82,15 @@ public class UserLiveTreeController {
return success(BeanUtils.toBean(userLiveTree, UserLiveTreeRespVO.class));
}
@GetMapping("/getItemByPid")
@Operation(summary = "获取用户直接下级")
@Parameter(name = "pid", description = "编号", required = true, example = "1024")
public CommonResult<List<UserLiveTreeRespVO>> getItemByPid(@RequestParam("pid") Long pid) {
List<UserLiveTreeDO> userLiveTree = userLiveTreeService.getItemByPid(pid);
return success(BeanUtils.toBean(userLiveTree, UserLiveTreeRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得crm用户结构树分页")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:query')")
@ -101,5 +111,4 @@ public class UserLiveTreeController {
ExcelUtils.write(response, "crm用户结构树.xls", "数据", UserLiveTreeRespVO.class,
BeanUtils.toBean(list, UserLiveTreeRespVO.class));
}
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment;
import lombok.*;
import java.util.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 绩效考核设置 DO
*
* @author 艾楷
*/
@TableName("crm_sales_performance_assessment")
@KeySequence("crm_sales_performance_assessment_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalesPerformanceAssessmentDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 计算表达式 例如 10<=n&&n<=20
*/
private String calculationExpression;
/**
* 核发比例%
*/
private BigDecimal proportion;
}

View File

@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
* 销售业绩结算记录 DO
*
* @author 艾楷
*/
@TableName("crm_sales_performance_settlement")
@KeySequence("crm_sales_performance_settlement_seq")
// 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalesPerformanceSettlementDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 用户id
*/
private Long userId;
/**
* 销售业绩目标申请ID
*/
private Long salesPerformanceId;
/**
*
*/
private String year;
/**
*
*/
private String month;
/**
* 实际回款额
*/
private BigDecimal actualPayment;
/**
* 实际销售额
*/
private Integer actualSale;
/**
* 评分
*/
private BigDecimal score;
/**
* 销售任务%
*/
private BigDecimal saleTask;
/**
* 回款任务%
*/
private BigDecimal paymentTask;
/**
* 评分任务%
*/
private BigDecimal scoreTask;
/**
* 总分
*/
private BigDecimal totalScore;
/**
* 核发比例
*/
private BigDecimal proportionOfIssuance;
/**
* 是否确认 0否 1是
*/
private Integer confirmFlag;
/**
* 是否可以确认 0否 1是
*/
private Integer canConfirmFlag;
/**
* 昵称
*/
@TableField(exist = false)
private String nickname;
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight;
import lombok.*;
import java.util.*;
import java.math.BigDecimal;
import java.math.BigDecimal;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 销售业绩权重设置 DO
*
* @author 艾楷
*/
@TableName("crm_sales_performance_weight")
@KeySequence("crm_sales_performance_weight_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalesPerformanceWeightDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 销售权重%
*/
private BigDecimal saleWeight;
/**
* 回款权重%
*/
private BigDecimal paymentWeight;
/**
* 评分权重%
*/
private BigDecimal scoreWeight;
}

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.UserProductCountVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
@ -58,9 +59,17 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
/**
* 获取用户合同产品数量
*
* @param userIds
* @param createTime
* @return
*/
List<UserProductCountVO> getProductCount(@Param("userIds") List<Long> userIds, @Param("createTime") LocalDateTime[] createTime);
/**
* 获取合同结算信息
*
* @return
*/
List<CrmContractProductSettlementDTO> getContractProductSettlement();
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceassessment;
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;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo.SalesPerformanceAssessmentPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment.SalesPerformanceAssessmentDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 绩效考核设置 Mapper
*
* @author 艾楷
*/
@Mapper
public interface SalesPerformanceAssessmentMapper extends BaseMapperX<SalesPerformanceAssessmentDO> {
default PageResult<SalesPerformanceAssessmentDO> selectPage(SalesPerformanceAssessmentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SalesPerformanceAssessmentDO>()
.eqIfPresent(SalesPerformanceAssessmentDO::getCalculationExpression, reqVO.getCalculationExpression())
.eqIfPresent(SalesPerformanceAssessmentDO::getProportion, reqVO.getProportion())
.betweenIfPresent(SalesPerformanceAssessmentDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SalesPerformanceAssessmentDO::getId));
}
}

View File

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.crm.dal.mysql.salesperformancesettlement;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.*;
import org.apache.ibatis.annotations.Param;
/**
* 销售业绩结算记录 Mapper
*
* @author 艾楷
*/
@Mapper
public interface SalesPerformanceSettlementMapper extends BaseMapperX<SalesPerformanceSettlementDO> {
default PageResult<SalesPerformanceSettlementDO> selectPage(SalesPerformanceSettlementPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SalesPerformanceSettlementDO>()
.eqIfPresent(SalesPerformanceSettlementDO::getUserId, reqVO.getUserId())
.eqIfPresent(SalesPerformanceSettlementDO::getSalesPerformanceId, reqVO.getSalesPerformanceId())
.eqIfPresent(SalesPerformanceSettlementDO::getYear, reqVO.getYear())
.eqIfPresent(SalesPerformanceSettlementDO::getMonth, reqVO.getMonth())
.eqIfPresent(SalesPerformanceSettlementDO::getActualPayment, reqVO.getActualPayment())
.eqIfPresent(SalesPerformanceSettlementDO::getActualSale, reqVO.getActualSale())
.eqIfPresent(SalesPerformanceSettlementDO::getScore, reqVO.getScore())
.eqIfPresent(SalesPerformanceSettlementDO::getTotalScore, reqVO.getTotalScore())
.eqIfPresent(SalesPerformanceSettlementDO::getConfirmFlag, reqVO.getConfirmFlag())
.betweenIfPresent(SalesPerformanceSettlementDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SalesPerformanceSettlementDO::getId));
}
/**
*
* @param vo
* @param page
* @return
*/
IPage<SalesPerformanceSettlementDO> getSalesPerformanceSettlementPage(@Param("vo") SalesPerformanceSettlementPageReqVO vo, @Param("page") Page page);
/**
*
* @param id
* @return
*/
SalesPerformanceSettlementDO getSalesPerformanceSettlement(@Param("id") Long id);
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceweight;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight.SalesPerformanceWeightDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo.*;
/**
* 销售业绩权重设置 Mapper
*
* @author 艾楷
*/
@Mapper
public interface SalesPerformanceWeightMapper extends BaseMapperX<SalesPerformanceWeightDO> {
default PageResult<SalesPerformanceWeightDO> selectPage(SalesPerformanceWeightPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SalesPerformanceWeightDO>()
.eqIfPresent(SalesPerformanceWeightDO::getSaleWeight, reqVO.getSaleWeight())
.eqIfPresent(SalesPerformanceWeightDO::getPaymentWeight, reqVO.getPaymentWeight())
.eqIfPresent(SalesPerformanceWeightDO::getScoreWeight, reqVO.getScoreWeight())
.betweenIfPresent(SalesPerformanceWeightDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SalesPerformanceWeightDO::getId));
}
}

View File

@ -2,19 +2,22 @@ package cn.iocoder.yudao.module.crm.framework.rpc.config;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAReceiptApi;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOASalesPerformanceApi;
import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.StoreProductAttrValueApi;
import cn.iocoder.yudao.module.system.api.auth.AdminOauthUserOtherInfoApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
import cn.iocoder.yudao.module.system.api.notice.NoticeApi;
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
import cn.iocoder.yudao.module.system.api.subscribe.SubscribeMessageSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = { DeptApi.class, DictDataApi.class, AdminUserApi.class, StoreProductApi.class, StoreProductAttrValueApi.class, SmsSendApi.class, MailSendApi.class, NoticeApi.class,
BpmOAContractApi.class, BpmOAReceiptApi.class})
@EnableFeignClients(clients = {DeptApi.class, DictDataApi.class, AdminUserApi.class, StoreProductApi.class, StoreProductAttrValueApi.class, SmsSendApi.class, MailSendApi.class, NoticeApi.class,
BpmOAContractApi.class, BpmOAReceiptApi.class, BpmOASalesPerformanceApi.class, AdminOauthUserOtherInfoApi.class, SubscribeMessageSendApi.class})
public class RpcConfiguration {
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.crm.job.salesperformance;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.crm.service.salesperformancesettlement.SalesPerformanceSettlementService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class GenerateSalesPerformanceJob {
// TODO: 2024/11/04 - 每个月最后一天晚上11点半 执行一次
@Resource
private SalesPerformanceSettlementService salesPerformanceSettlementService;
@XxlJob("generateSalesPerformanceJob")
@TenantJob // --- 这个注解 会将租户列表拉出来 完了后逐个租户执行 定时任务需要注意
public ReturnT<String> execute() {
log.info("开始 生产下一个月的销售业绩");
salesPerformanceSettlementService.generateSalesPerformanceJob();
log.info("结束 生产下一个月的销售业绩");
return ReturnT.SUCCESS;
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.crm.job.salesperformance;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.crm.service.salesperformancesettlement.SalesPerformanceSettlementService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Component
@Slf4j
public class SalesPerformanceJob {
// TODO: 2024/11/04 - 每个月月初 凌晨0点01分结算上个月的数据
@Resource
private SalesPerformanceSettlementService salesPerformanceSettlementService;
@XxlJob("salesPerformanceJob")
@TenantJob // --- 这个注解 会将租户列表拉出来 完了后逐个租户执行 定时任务需要注意
public ReturnT<String> execute() throws Exception {
log.info("开始 销售业绩结算");
salesPerformanceSettlementService.settlement();
log.info("结束 销售业绩结算");
return ReturnT.SUCCESS;
}
}

View File

@ -6,12 +6,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractPa
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractProductDO;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.UserProductCountVO;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 合同 Service 接口
@ -98,4 +98,11 @@ public interface CrmContractService {
* @return
*/
List<UserProductCountVO> getProductCount(List<Long> userIds, LocalDateTime[] createTime);
/**
* 获取合同结算信息
*
* @return
*/
List<CrmContractProductSettlementDTO> getContractProductSettlement();
}

View File

@ -33,6 +33,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.crmflow.CrmFlowMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmflow.CrmFlowStepMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmflowlog.CrmFlowLogMapper;
import cn.iocoder.yudao.module.crm.service.userlivetree.UserLiveTreeService;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO;
import cn.iocoder.yudao.module.hrm.enums.ContractStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum;
import cn.iocoder.yudao.module.hrm.enums.RelationEnum;
@ -401,6 +402,11 @@ public class CrmContractServiceImpl implements CrmContractService {
return contractMapper.getProductCount(userIds, createTime);
}
@Override
public List<CrmContractProductSettlementDTO> getContractProductSettlement() {
return contractMapper.getContractProductSettlement();
}
private void createContractProductList(Long contractId, List<CrmContractProductDO> list) {
List<StoreProductAttrValueApiVO> storeProductAttrValueList = new ArrayList<>();
if (CollUtil.isNotEmpty(list)) {

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.crm.service.salesperformanceassessment;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo.SalesPerformanceAssessmentPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo.SalesPerformanceAssessmentSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment.SalesPerformanceAssessmentDO;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid;
/**
* 绩效考核设置 Service 接口
*
* @author 艾楷
*/
public interface SalesPerformanceAssessmentService extends IService<SalesPerformanceAssessmentDO> {
/**
* 创建绩效考核设置
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSalesPerformanceAssessment(@Valid SalesPerformanceAssessmentSaveReqVO createReqVO);
/**
* 更新绩效考核设置
*
* @param updateReqVO 更新信息
*/
void updateSalesPerformanceAssessment(@Valid SalesPerformanceAssessmentSaveReqVO updateReqVO);
/**
* 删除绩效考核设置
*
* @param id 编号
*/
void deleteSalesPerformanceAssessment(Long id);
/**
* 获得绩效考核设置
*
* @param id 编号
* @return 绩效考核设置
*/
SalesPerformanceAssessmentDO getSalesPerformanceAssessment(Long id);
/**
* 获得绩效考核设置分页
*
* @param pageReqVO 分页查询
* @return 绩效考核设置分页
*/
PageResult<SalesPerformanceAssessmentDO> getSalesPerformanceAssessmentPage(SalesPerformanceAssessmentPageReqVO pageReqVO);
}

View File

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.crm.service.salesperformanceassessment;
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.salesperformanceassessment.vo.SalesPerformanceAssessmentPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceassessment.vo.SalesPerformanceAssessmentSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment.SalesPerformanceAssessmentDO;
import cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceassessment.SalesPerformanceAssessmentMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 绩效考核设置 Service 实现类
*
* @author 艾楷
*/
@Service
@Validated
public class SalesPerformanceAssessmentServiceImpl extends ServiceImpl<SalesPerformanceAssessmentMapper, SalesPerformanceAssessmentDO> implements SalesPerformanceAssessmentService {
@Resource
private SalesPerformanceAssessmentMapper salesPerformanceAssessmentMapper;
@Override
public Long createSalesPerformanceAssessment(SalesPerformanceAssessmentSaveReqVO createReqVO) {
// 插入
SalesPerformanceAssessmentDO salesPerformanceAssessment = BeanUtils.toBean(createReqVO, SalesPerformanceAssessmentDO.class);
salesPerformanceAssessmentMapper.insert(salesPerformanceAssessment);
// 返回
return salesPerformanceAssessment.getId();
}
@Override
public void updateSalesPerformanceAssessment(SalesPerformanceAssessmentSaveReqVO updateReqVO) {
// 更新
SalesPerformanceAssessmentDO updateObj = BeanUtils.toBean(updateReqVO, SalesPerformanceAssessmentDO.class);
salesPerformanceAssessmentMapper.updateById(updateObj);
}
@Override
public void deleteSalesPerformanceAssessment(Long id) {
// 删除
salesPerformanceAssessmentMapper.deleteById(id);
}
@Override
public SalesPerformanceAssessmentDO getSalesPerformanceAssessment(Long id) {
return salesPerformanceAssessmentMapper.selectById(id);
}
@Override
public PageResult<SalesPerformanceAssessmentDO> getSalesPerformanceAssessmentPage(SalesPerformanceAssessmentPageReqVO pageReqVO) {
return salesPerformanceAssessmentMapper.selectPage(pageReqVO);
}
}

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.crm.service.salesperformancesettlement;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.Valid;
import java.time.LocalDateTime;
/**
* 销售业绩结算记录 Service 接口
*
* @author 艾楷
*/
public interface SalesPerformanceSettlementService extends IService<SalesPerformanceSettlementDO> {
/**
* 创建 - 这里用户crm新增用户
*
* @param now
* @param userId
*/
void createSalesPerformanceSettlement(LocalDateTime now, Long userId);
/**
* 更新销售业绩结算记录
*
* @param updateReqVO 更新信息
*/
void updateSalesPerformanceSettlement(@Valid SalesPerformanceSettlementSaveReqVO updateReqVO);
/**
* 删除销售业绩结算记录
*
* @param id 编号
*/
void deleteSalesPerformanceSettlement(Long id);
/**
* 获得销售业绩结算记录
*
* @param id 编号
* @return 销售业绩结算记录
*/
SalesPerformanceSettlementDO getSalesPerformanceSettlement(Long id);
/**
* 获得销售业绩结算记录分页
*
* @param pageReqVO 分页查询
* @return 销售业绩结算记录分页
*/
PageResult<SalesPerformanceSettlementDO> getSalesPerformanceSettlementPage(SalesPerformanceSettlementPageReqVO pageReqVO);
/**
* 每月结算
*/
void settlement();
/**
* 生成下一个月的业绩
*/
void generateSalesPerformanceJob();
}

View File

@ -0,0 +1,348 @@
package cn.iocoder.yudao.module.crm.service.salesperformancesettlement;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.SocialTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.template.WxMpMsgTemplateUtils;
import cn.iocoder.yudao.framework.common.template.dto.PerformanceResultConfirmationReminderDTO;
import cn.iocoder.yudao.framework.common.template.vo.SubscribeMessageReqDTO;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.number.BigDecimalUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAReceiptApi;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOASalesPerformanceApi;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptSettlementDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.salesperformance.SalesPerformanceDTO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformancesettlement.vo.SalesPerformanceSettlementSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.userlivetree.vo.UserLiveTreeListVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceassessment.SalesPerformanceAssessmentDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight.SalesPerformanceWeightDO;
import cn.iocoder.yudao.module.crm.dal.mysql.salesperformancesettlement.SalesPerformanceSettlementMapper;
import cn.iocoder.yudao.module.crm.service.crmcontract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.salesperformanceassessment.SalesPerformanceAssessmentService;
import cn.iocoder.yudao.module.crm.service.salesperformanceweight.SalesPerformanceWeightService;
import cn.iocoder.yudao.module.crm.service.userlivetree.UserLiveTreeService;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO;
import cn.iocoder.yudao.module.system.api.auth.AdminOauthUserOtherInfoApi;
import cn.iocoder.yudao.module.system.api.auth.dto.AdminOauthUserOtherInfoApiDTO;
import cn.iocoder.yudao.module.system.api.auth.vo.AdminOauthUserOtherInfoApiVO;
import cn.iocoder.yudao.module.system.api.subscribe.SubscribeMessageSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.ognl.Ognl;
import org.apache.ibatis.ognl.OgnlException;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.NOT_EDITABLE_UNTIL_SALES_TARGET_HAS_BEEN_APPLIED;
/**
* 销售业绩结算记录 Service 实现类
*
* @author 艾楷
*/
@Service
@Validated
public class SalesPerformanceSettlementServiceImpl extends ServiceImpl<SalesPerformanceSettlementMapper, SalesPerformanceSettlementDO> implements SalesPerformanceSettlementService {
@Resource
private SalesPerformanceSettlementMapper salesPerformanceSettlementMapper;
@Resource
private UserLiveTreeService userLiveTreeService;
@Resource
private CrmContractService crmContractService;
@Resource
private BpmOAReceiptApi receiptApi;
@Resource
private SalesPerformanceWeightService salesPerformanceWeightService;
@Resource
private BpmOASalesPerformanceApi salesPerformanceApi;
@Resource
private SalesPerformanceAssessmentService salesPerformanceAssessmentService;
@Resource
private AdminOauthUserOtherInfoApi adminOauthUserOtherInfoApi;
@Resource
private SubscribeMessageSendApi subscribeMessageSendApi;
@Resource
private AdminUserApi adminUserApi;
@Resource
private BpmOAContractApi bpmOAContractApi;
//百分比
private static final BigDecimal PERCENTAGE = new BigDecimal("0.01");
//十分比率
private static final BigDecimal TEN_PERCENT = new BigDecimal("10");
@Override
public void createSalesPerformanceSettlement(LocalDateTime now, Long userId) {
// 插入
SalesPerformanceSettlementDO salesPerformanceSettlementDO = new SalesPerformanceSettlementDO();
salesPerformanceSettlementDO.setUserId(userId);
salesPerformanceSettlementDO.setYear(String.valueOf(now.getYear()));
// 月份补0
salesPerformanceSettlementDO.setMonth(String.format("%02d", now.getMonthValue()));
salesPerformanceSettlementMapper.insert(salesPerformanceSettlementDO);
}
@Override
public void updateSalesPerformanceSettlement(SalesPerformanceSettlementSaveReqVO updateReqVO) {
SalesPerformanceSettlementDO salesPerformanceSettlementDO = salesPerformanceSettlementMapper.selectById(updateReqVO.getId());
// 更新
SalesPerformanceSettlementDO updateObj = BeanUtils.toBean(updateReqVO, SalesPerformanceSettlementDO.class);
// 如果更新的是 /实际回款额 /实际销售额 /评分 重新计算 对应的比例以及总分和核发比例
boolean changeFlag = false;
//获取当月目标审核通过的
SalesPerformanceDTO salesPerformanceDTO = null;
//获取权重
SalesPerformanceWeightDO salesPerformanceWeight = salesPerformanceWeightService.getSalesPerformanceWeight();
if (salesPerformanceSettlementDO.getSalesPerformanceId() != null) {
salesPerformanceDTO = salesPerformanceApi.getBySalesPerformanceId(salesPerformanceSettlementDO.getSalesPerformanceId()).getCheckedData();
}
if (!BigDecimalUtil.isEqualTo(updateObj.getActualPayment(), salesPerformanceSettlementDO.getActualPayment())) {
if (salesPerformanceDTO == null) {
// 未申请销售目标前不可编辑
throw exception(NOT_EDITABLE_UNTIL_SALES_TARGET_HAS_BEEN_APPLIED);
}
BigDecimal saleScore = BigDecimal.valueOf(updateObj.getActualSale())
.divide(BigDecimal.valueOf(salesPerformanceDTO.getSaleTarget()), 2, RoundingMode.HALF_UP);
saleScore = (saleScore.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getSaleWeight() :
salesPerformanceWeight.getSaleWeight().multiply(saleScore);
// 销售任务最终得分
updateObj.setSaleTask(saleScore);
changeFlag = true;
}
if (updateObj.getActualSale() != null && updateObj.getActualSale().equals(salesPerformanceSettlementDO.getActualSale())) {
if (salesPerformanceDTO == null) {
// 未申请销售目标前不可编辑
throw exception(NOT_EDITABLE_UNTIL_SALES_TARGET_HAS_BEEN_APPLIED);
}
BigDecimal paymentScore = updateObj.getActualPayment()
.divide(salesPerformanceDTO.getPaymentTarget(), 2, RoundingMode.HALF_UP);
paymentScore = (paymentScore.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getPaymentWeight() :
salesPerformanceWeight.getPaymentWeight().multiply(paymentScore);
// 回款任务最终得分
updateObj.setPaymentTask(paymentScore);
changeFlag = true;
}
if (!BigDecimalUtil.isEqualTo(updateObj.getScore(), salesPerformanceSettlementDO.getScore())) {
BigDecimal score = new BigDecimal(String.valueOf(updateObj.getScore()))
.divide(TEN_PERCENT, 2, RoundingMode.HALF_UP);
score = (score.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getScoreWeight() :
salesPerformanceWeight.getScoreWeight().multiply(score);
// 回款任务最终得分
updateObj.setScoreTask(score);
changeFlag = true;
}
if (changeFlag) {
// 重新计算 -
updateObj.setTotalScore(BigDecimalUtil.adds(updateObj.getSaleTask(), updateObj.getPaymentTask(), updateObj.getScoreTask()));
// -- 根据总分再计算核发比例
List<SalesPerformanceAssessmentDO> salesPerformanceAssessmentDOS = salesPerformanceAssessmentService.list(new LambdaQueryWrapper<SalesPerformanceAssessmentDO>()
.orderByDesc(SalesPerformanceAssessmentDO::getProportion));
for (SalesPerformanceAssessmentDO salesPerformanceAssessmentDO : salesPerformanceAssessmentDOS) {
if (calculation(salesPerformanceAssessmentDO.getCalculationExpression(), updateObj.getTotalScore())) {
updateObj.setProportionOfIssuance(salesPerformanceAssessmentDO.getProportion());
break;
}
}
}
salesPerformanceSettlementMapper.updateById(updateObj);
}
@Override
public void deleteSalesPerformanceSettlement(Long id) {
// 删除
salesPerformanceSettlementMapper.deleteById(id);
}
@Override
public SalesPerformanceSettlementDO getSalesPerformanceSettlement(Long id) {
return salesPerformanceSettlementMapper.getSalesPerformanceSettlement(id);
}
@Override
public PageResult<SalesPerformanceSettlementDO> getSalesPerformanceSettlementPage(SalesPerformanceSettlementPageReqVO pageReqVO) {
IPage<SalesPerformanceSettlementDO> pageResult = salesPerformanceSettlementMapper.getSalesPerformanceSettlementPage(pageReqVO, MyBatisUtils.buildPage(pageReqVO));
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
}
@Override
public void settlement() {
// LocalDateTime now = LocalDateTime.now().minusMonths(1L);
LocalDateTime now = LocalDateTime.now();
String year = String.valueOf(now.getYear());
String month = String.format("%02d", now.getMonthValue());
// 获取CRM用户一下
// List<UserLiveTreeListVO> userLiveList = userLiveTreeService.getUserLiveList();
List<UserLiveTreeListVO> userLiveList = Arrays.asList(new UserLiveTreeListVO().setUserId(152L));
// 计算当前月所有销售的 销售额 - 销售台数
// -- 合同产品数量 后续可能需要根据不同的产品来定目标 - 所以这里 我们预判下 - 将合同对应的projectId也查询出来 好方便后续做产品区分
List<CrmContractProductSettlementDTO> contractProductSettlementDTOList = crmContractService.getContractProductSettlement();
contractProductSettlementDTOList = CollUtil.isEmpty(contractProductSettlementDTOList) ? Collections.emptyList() : contractProductSettlementDTOList;
//根据用户id分组 并且合并数量
Map<Long, Integer> productMap = contractProductSettlementDTOList.stream().collect(Collectors.groupingBy(CrmContractProductSettlementDTO::getUserId,
Collectors.summingInt(CrmContractProductSettlementDTO::getNums)));
// -- 回款申请 / - 回了多少就是多少咯 - 这个没啥业务判断当前月回了多少款即可
LocalDateTime beginTime = LocalDateTimeUtils.beginOfMonth(now);
LocalDateTime endTime = LocalDateTimeUtils.endOfMonth(now);
LocalDateTime[] times = {beginTime, endTime};
List<ReceiptSettlementDTO> receiptSettlementList = receiptApi.getReceiptSettlement(times).getCheckedData();
receiptSettlementList = CollUtil.isEmpty(receiptSettlementList) ? Collections.emptyList() : receiptSettlementList;
Map<Long, BigDecimal> receiptSettlementMap = receiptSettlementList.stream()
.collect(Collectors.toMap(ReceiptSettlementDTO::getUserId, ReceiptSettlementDTO::getMoney));
List<SalesPerformanceSettlementDO> salesPerformanceSettlementDOS = salesPerformanceSettlementMapper.selectList(new LambdaQueryWrapperX<SalesPerformanceSettlementDO>()
.eq(SalesPerformanceSettlementDO::getYear, year)
.eq(SalesPerformanceSettlementDO::getMonth, month));
Map<Long, SalesPerformanceSettlementDO> map = salesPerformanceSettlementDOS.stream().collect(Collectors.toMap(SalesPerformanceSettlementDO::getUserId, Function.identity()));
List<SalesPerformanceSettlementDO> list = new ArrayList<>();
//获取当月目标审核通过的
List<SalesPerformanceDTO> salesPerformanceDTOS = salesPerformanceApi.getReceiptSettlement(year, month).getCheckedData();
Map<Long, SalesPerformanceDTO> salesPerformanceMap = salesPerformanceDTOS.stream().collect(Collectors.toMap(SalesPerformanceDTO::getUserId, Function.identity()));
//获取权重
SalesPerformanceWeightDO salesPerformanceWeight = salesPerformanceWeightService.getSalesPerformanceWeight();
//获取绩效考核设置
for (UserLiveTreeListVO vo : userLiveList) {
SalesPerformanceSettlementDO salesPerformanceSettlementDO = map.get(vo.getUserId());
if (salesPerformanceSettlementDO == null) {
salesPerformanceSettlementDO = new SalesPerformanceSettlementDO();
}
salesPerformanceSettlementDO.setUserId(vo.getUserId());
salesPerformanceSettlementDO.setYear(String.valueOf(now.getYear()));
salesPerformanceSettlementDO.setMonth(String.format("%02d", now.getMonthValue()));
salesPerformanceSettlementDO.setActualSale(productMap.getOrDefault(vo.getUserId(), 0));
salesPerformanceSettlementDO.setActualPayment(receiptSettlementMap.getOrDefault(vo.getUserId(), BigDecimal.ZERO));
SalesPerformanceDTO salesPerformanceDTO = salesPerformanceMap.get(vo.getUserId());
// 计算得分
if (salesPerformanceWeight != null) {
// -- 如果实际/目标>0则拿到全额的权重
// ----------- 销售任务得分计算 -----------
BigDecimal saleScore = BigDecimal.valueOf(salesPerformanceSettlementDO.getActualSale())
.divide(BigDecimal.valueOf(salesPerformanceDTO.getSaleTarget()), 2, RoundingMode.HALF_UP);
saleScore = (saleScore.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getSaleWeight() :
salesPerformanceWeight.getSaleWeight().multiply(saleScore);
// 销售任务最终得分
salesPerformanceSettlementDO.setSaleTask(saleScore);
// ----------- 回款任务得分计算 -----------
BigDecimal paymentScore = salesPerformanceSettlementDO.getActualPayment()
.divide(salesPerformanceDTO.getPaymentTarget(), 2, RoundingMode.HALF_UP);
paymentScore = (paymentScore.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getPaymentWeight() :
salesPerformanceWeight.getPaymentWeight().multiply(paymentScore);
// 回款任务最终得分
salesPerformanceSettlementDO.setPaymentTask(paymentScore);
// ----------- 评分任务得分计算 10分制 -----------
BigDecimal score = BigDecimal.ZERO;
if (salesPerformanceSettlementDO.getScore() != null) {
score = new BigDecimal(String.valueOf(salesPerformanceSettlementDO.getScore()))
.divide(TEN_PERCENT, 2, RoundingMode.HALF_UP);
score = (score.compareTo(BigDecimal.ONE) >= 0) ? salesPerformanceWeight.getScoreWeight() :
salesPerformanceWeight.getScoreWeight().multiply(score);
// 回款任务最终得分
salesPerformanceSettlementDO.setScoreTask(score);
}
salesPerformanceSettlementDO.setTotalScore(saleScore.add(paymentScore).add(score));
// -- 根据总分再计算核发比例
List<SalesPerformanceAssessmentDO> salesPerformanceAssessmentDOS = salesPerformanceAssessmentService.list(new LambdaQueryWrapper<SalesPerformanceAssessmentDO>()
.orderByDesc(SalesPerformanceAssessmentDO::getProportion));
for (SalesPerformanceAssessmentDO salesPerformanceAssessmentDO : salesPerformanceAssessmentDOS) {
if (this.calculation(salesPerformanceAssessmentDO.getCalculationExpression(), salesPerformanceSettlementDO.getTotalScore())) {
salesPerformanceSettlementDO.setProportionOfIssuance(salesPerformanceAssessmentDO.getProportion());
break;
}
}
salesPerformanceSettlementDO.setCanConfirmFlag(1);
}
list.add(salesPerformanceSettlementDO);
}
if (CollUtil.isNotEmpty(list)) {
salesPerformanceSettlementMapper.insertOrUpdateBatch(list);
}
List<Long> userIds = new ArrayList<>(salesPerformanceMap.keySet());
//获取用户列表
List<AdminUserRespDTO> adminUserRespDTOList = adminUserApi.getUserList(userIds).getCheckedData();
Map<Long, AdminUserRespDTO> userMap = adminUserRespDTOList.stream().collect(Collectors.toMap(AdminUserRespDTO::getId, Function.identity()));
List<AdminOauthUserOtherInfoApiVO> adminOauthUserOtherInfoApiVOS = adminOauthUserOtherInfoApi.getOpenIdByCondition(
new AdminOauthUserOtherInfoApiDTO().setUserIds(userIds)
.setSocialType(SocialTypeEnum.WECHAT_MP.getType())).getCheckedData();
// - 结算完毕 - 通知所有填写申请到crm用户确认
for (AdminOauthUserOtherInfoApiVO adminOauthUserOtherInfoApiVO : adminOauthUserOtherInfoApiVOS) {
SubscribeMessageReqDTO dto = new WxMpMsgTemplateUtils().convertPerformanceResultConfirmationReminder(new PerformanceResultConfirmationReminderDTO()
.setOpenId(adminOauthUserOtherInfoApiVO.getOpenId())
.setUserName(userMap.get(adminOauthUserOtherInfoApiVO.getUserId()).getNickname())
.setTime(year + "" + month + "")
.setMiniProgramState("formal")
.setPage("pages/components/pages/performanceSet/detail" + "?id=" + map.get(adminOauthUserOtherInfoApiVO.getUserId()).getId()));
subscribeMessageSendApi.sendMpMsg(dto);
}
// 计算完成后 需要修改合同的结算状态 - 待结算 - 已结算
if (CollUtil.isNotEmpty(contractProductSettlementDTOList)) {
List<Long> contractIds = contractProductSettlementDTOList.stream().map(CrmContractProductSettlementDTO::getContractId).collect(Collectors.toList());
bpmOAContractApi.updateSettlementFlagByIds(contractIds);
}
}
@Override
public void generateSalesPerformanceJob() {
LocalDateTime now = LocalDateTime.now().plusMonths(1L);
List<UserLiveTreeListVO> userLiveList = userLiveTreeService.getUserLiveList();
List<SalesPerformanceSettlementDO> list = new ArrayList<>();
for (UserLiveTreeListVO vo : userLiveList) {
SalesPerformanceSettlementDO salesPerformanceSettlementDO = new SalesPerformanceSettlementDO();
salesPerformanceSettlementDO.setUserId(vo.getUserId());
salesPerformanceSettlementDO.setYear(String.valueOf(now.getYear()));
salesPerformanceSettlementDO.setMonth(String.format("%02d", now.getMonthValue()));
list.add(salesPerformanceSettlementDO);
}
if (CollUtil.isNotEmpty(list)) {
salesPerformanceSettlementMapper.insertBatch(list);
}
}
/**
* 表达式 10<=n && n<=20
*
* @param originalExpr
* @param n
* @return
*/
public static boolean calculation(String originalExpr, BigDecimal n) {
boolean result = false;
try {
// 执行OGNL表达式
result = evaluateOgnlExpression(originalExpr, n);
} catch (IllegalArgumentException | OgnlException e) {
e.printStackTrace();
}
return result;
}
public static boolean evaluateOgnlExpression(String ognlExpr, BigDecimal n) throws OgnlException {
Map<String, Object> context = new HashMap<>();
context.put("n", n);
Object result = Ognl.getValue(ognlExpr, context);
return (Boolean) result;
}
}

View File

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.crm.service.salesperformanceweight;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight.SalesPerformanceWeightDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 销售业绩权重设置 Service 接口
*
* @author 艾楷
*/
public interface SalesPerformanceWeightService {
/**
* 创建销售业绩权重设置
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSalesPerformanceWeight(@Valid SalesPerformanceWeightSaveReqVO createReqVO);
/**
* 更新销售业绩权重设置
*
* @param updateReqVO 更新信息
*/
void updateSalesPerformanceWeight(@Valid SalesPerformanceWeightSaveReqVO updateReqVO);
/**
* 删除销售业绩权重设置
*
* @param id 编号
*/
void deleteSalesPerformanceWeight(Long id);
/**
* 获得销售业绩权重设置
*
* @return 销售业绩权重设置
*/
SalesPerformanceWeightDO getSalesPerformanceWeight();
/**
* 获得销售业绩权重设置分页
*
* @param pageReqVO 分页查询
* @return 销售业绩权重设置分页
*/
PageResult<SalesPerformanceWeightDO> getSalesPerformanceWeightPage(SalesPerformanceWeightPageReqVO pageReqVO);
/**
* 新增修改销售业绩权重设置
* @param updateReqVO
*/
void saveOrEdit(@Valid SalesPerformanceWeightSaveReqVO updateReqVO);
}

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.crm.service.salesperformanceweight;
import cn.hutool.core.collection.CollUtil;
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.salesperformanceweight.vo.SalesPerformanceWeightPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.salesperformanceweight.vo.SalesPerformanceWeightSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.salesperformanceweight.SalesPerformanceWeightDO;
import cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceweight.SalesPerformanceWeightMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
/**
* 销售业绩权重设置 Service 实现类
*
* @author 艾楷
*/
@Service
@Validated
public class SalesPerformanceWeightServiceImpl implements SalesPerformanceWeightService {
@Resource
private SalesPerformanceWeightMapper salesPerformanceWeightMapper;
@Override
public Long createSalesPerformanceWeight(SalesPerformanceWeightSaveReqVO createReqVO) {
// 插入
SalesPerformanceWeightDO salesPerformanceWeight = BeanUtils.toBean(createReqVO, SalesPerformanceWeightDO.class);
salesPerformanceWeightMapper.insert(salesPerformanceWeight);
// 返回
return salesPerformanceWeight.getId();
}
@Override
public void updateSalesPerformanceWeight(SalesPerformanceWeightSaveReqVO updateReqVO) {
// 更新
SalesPerformanceWeightDO updateObj = BeanUtils.toBean(updateReqVO, SalesPerformanceWeightDO.class);
salesPerformanceWeightMapper.updateById(updateObj);
}
@Override
public void deleteSalesPerformanceWeight(Long id) {
// 删除
salesPerformanceWeightMapper.deleteById(id);
}
@Override
public SalesPerformanceWeightDO getSalesPerformanceWeight() {
List<SalesPerformanceWeightDO> list = salesPerformanceWeightMapper.selectList();
if (CollUtil.isNotEmpty(list)) {
return list.get(0);
}
return new SalesPerformanceWeightDO();
}
@Override
public PageResult<SalesPerformanceWeightDO> getSalesPerformanceWeightPage(SalesPerformanceWeightPageReqVO pageReqVO) {
return salesPerformanceWeightMapper.selectPage(pageReqVO);
}
@Override
public void saveOrEdit(SalesPerformanceWeightSaveReqVO createReqVO) {
SalesPerformanceWeightDO salesPerformanceWeight = BeanUtils.toBean(createReqVO, SalesPerformanceWeightDO.class);
salesPerformanceWeightMapper.insertOrUpdate(salesPerformanceWeight);
}
}

View File

@ -71,7 +71,16 @@ public interface UserLiveTreeService {
/**
* 获取所有用户列表
*
* @return 用户列表
*/
List<UserLiveTreeListVO> getUserLiveList();
/**
* 获取用户的直接下级
*
* @param pid
* @return
*/
List<UserLiveTreeDO> getItemByPid(Long pid);
}

View File

@ -9,12 +9,17 @@ import cn.iocoder.yudao.module.crm.controller.admin.userlivetree.vo.UserLiveTree
import cn.iocoder.yudao.module.crm.controller.admin.userlivetree.vo.UserLiveTreeSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.userlivetree.UserLiveTreeDO;
import cn.iocoder.yudao.module.crm.dal.mysql.userlivetree.UserLiveTreeMapper;
import cn.iocoder.yudao.module.crm.service.salesperformancesettlement.SalesPerformanceSettlementService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -34,13 +39,17 @@ public class UserLiveTreeServiceImpl implements UserLiveTreeService {
@Resource
private AdminUserApi userApi;
@Resource
@Lazy
private SalesPerformanceSettlementService salesPerformanceSettlementService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createUserLiveTree(UserLiveTreeSaveReqVO createReqVO) {
// 插入
UserLiveTreeDO userLiveTree = BeanUtils.toBean(createReqVO, UserLiveTreeDO.class);
userLiveTreeMapper.insert(userLiveTree);
salesPerformanceSettlementService.createSalesPerformanceSettlement(LocalDateTime.now(), userLiveTree.getUserId());
// 返回
return userLiveTree.getId();
}
@ -100,13 +109,13 @@ public class UserLiveTreeServiceImpl implements UserLiveTreeService {
}
private void recursionGetId(List<UserLiveTreeListVO> list, List<Long> ids) {
if (CollectionUtil.isEmpty(list)){
if (CollectionUtil.isEmpty(list)) {
return;
}
List<UserLiveTreeListVO> nextList = new ArrayList<>();
for (UserLiveTreeListVO userLiveTreeListVO : list) {
ids.add(userLiveTreeListVO.getUserId());
if (CollectionUtil.isNotEmpty(userLiveTreeListVO.getItems())){
if (CollectionUtil.isNotEmpty(userLiveTreeListVO.getItems())) {
nextList.addAll(userLiveTreeListVO.getItems());
}
}
@ -118,8 +127,13 @@ public class UserLiveTreeServiceImpl implements UserLiveTreeService {
// -- 这里将所有的数据拉出来 -
return userLiveTreeMapper.userLiveTreeList();
}
@Override
public List<UserLiveTreeDO> getItemByPid(Long pid) {
return userLiveTreeMapper.selectList(new LambdaQueryWrapper<UserLiveTreeDO>()
.eq(UserLiveTreeDO::getPid, pid));
}
public List<UserLiveTreeListVO> getUserLiveTreeList(){
public List<UserLiveTreeListVO> getUserLiveTreeList() {
// -- 这里将所有的数据拉出来 -
List<UserLiveTreeListVO> userLiveTreeDOS = userLiveTreeMapper.userLiveTreeList();
// 1. 按父ID分组获取每个父节点下的子节点列表

View File

@ -85,10 +85,16 @@ logging:
--- #################### 定时任务相关配置 ####################
xxl:
job:
enabled: false # 是否开启调度中心,默认为 true 开启
enabled: true # 是否开启调度中心,默认为 true 开启
admin:
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
executor:
appname: ${spring.application.name} # 执行器 AppName
ip: # 执行器IP [选填]默认为空表示自动获取IP多网卡时可手动设置指定IP该IP不会绑定Host仅作为通讯实用地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"
port: 6670 # ### 执行器端口号 [选填]小于等于0则自动获取默认端口为9999单机部署多个执行器时注意要配置不同执行器端口
logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
#accessToken: default_token
logretentiondays: 30 # 执行器日志文件保存天数 [选填] 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项

View File

@ -98,7 +98,7 @@
<where>
a.deleted = 0
and b.deleted = 0
<if test="userIds != null and userIds.length > 0">
<if test="userIds != null and userIds.size() > 0">
AND a.owner_user_id in
<foreach collection="userIds" item="id" index="index" open="(" close=")" separator=",">
#{id}
@ -114,4 +114,24 @@
</if>
</where>
</select>
<select id="getContractProductSettlement"
resultType="cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductSettlementDTO">
SELECT
b.user_id AS userId,
b.id AS contractId,
a.product_id AS productId,
a.price AS price,
a.nums AS nums,
a.discount AS discount,
a.subtotal AS subtotal
FROM
crm_contract_product AS a
LEFT JOIN bpm_oa_contract AS b ON a.contract_id = b.id
WHERE
a.deleted = 0
and b.deleted = 0
and b.result = 2
and b.return_money &gt; 0
and b.settlement_flag = 0
</select>
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceassessment.SalesPerformanceAssessmentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.salesperformancesettlement.SalesPerformanceSettlementMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
<select id="getSalesPerformanceSettlementPage"
resultType="cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO">
select
a.*,
b.nickname
from crm_sales_performance_settlement as a
left join system_users as b on a.user_id = b.id
<where>
a.deleted = 0
and b.deleted = 0
<if test="vo.userId != null">
and a.user_id = #{vo.userId}
</if>
<if test="vo.year != null and vo.year != ''">
and a.year = #{vo.year}
</if>
<if test="vo.month != null and vo.month != ''">
and a.month = #{vo.month}
</if>
<if test="vo.nickname != null and vo.nickname != ''">
and b.nickname like concat('%', #{vo.nickname}, '%')
</if>
</where>
</select>
<select id="getSalesPerformanceSettlement"
resultType="cn.iocoder.yudao.module.crm.dal.dataobject.salesperformancesettlement.SalesPerformanceSettlementDO">
select
a.*,
b.nickname
from crm_sales_performance_settlement as a
left join system_users as b on a.user_id = b.id
<where>
a.deleted = 0
and b.deleted = 0
and a.id = #{id}
</where>
</select>
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.salesperformanceweight.SalesPerformanceWeightMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -207,7 +207,7 @@
</foreach>
</if>
<if test="filterCrmUserFlag != null and filterCrmUserFlag == 1">
and not exists (SELECT user_id from crm_user_live_tree where a.id = user_id)
and not exists (SELECT user_id from crm_user_live_tree where a.id = user_id and deleted = 0)
</if>
</where>
</select>