|
|
@@ -0,0 +1,925 @@
|
|
|
+package nckd.jxccl.hr.psms.business;
|
|
|
+
|
|
|
+import kd.bos.common.enums.EnableEnum;
|
|
|
+import kd.bos.dataentity.entity.DynamicObject;
|
|
|
+import kd.bos.entity.constant.StatusEnum;
|
|
|
+import kd.bos.orm.query.QCP;
|
|
|
+import kd.bos.orm.query.QFilter;
|
|
|
+import kd.bos.servicehelper.BusinessDataServiceHelper;
|
|
|
+import nckd.jxccl.base.common.constant.FormConstant;
|
|
|
+import nckd.jxccl.base.common.enums.AppraisalResultEnum;
|
|
|
+import nckd.jxccl.base.common.enums.psms.AdjustTypeEnum;
|
|
|
+import nckd.jxccl.base.common.enums.psms.JobSeqEnum;
|
|
|
+import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
|
|
|
+import nckd.jxccl.base.common.exception.ValidationException;
|
|
|
+import nckd.jxccl.base.common.utils.ConvertUtil;
|
|
|
+import nckd.jxccl.base.common.utils.DateUtil;
|
|
|
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
|
|
|
+import nckd.jxccl.base.common.utils.StrFormatter;
|
|
|
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
|
|
|
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
|
|
|
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.StringJoiner;
|
|
|
+
|
|
|
+/**
|
|
|
+* 年度调整服务类
|
|
|
+* @author W.Y.C
|
|
|
+* @date 2025/10/08 22:12
|
|
|
+* @version 1.0
|
|
|
+*/
|
|
|
+public class AnnualAdjustmentService {
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成年度调整记录
|
|
|
+ * @param person 员工
|
|
|
+ * @param beginDate 生效日期
|
|
|
+ * @return 成功返回 新的职位档案
|
|
|
+ * @note 对应SHR:PersonpositionfilecreateViewListHandler#addNewYear_PersonpositionfileInfo(277~815行)
|
|
|
+ */
|
|
|
+ public static DynamicObject addNewYearPersonPositionFileInfo(DynamicObject person, Date beginDate,String remark,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile) {
|
|
|
+ int executeYear = beginDate != null ? DateUtil.getYear(beginDate) : DateUtil.getYear(new Date());
|
|
|
+
|
|
|
+ // 1、 初始化上下文并加载基础数据。
|
|
|
+ // 对应SHR:291~339行
|
|
|
+ Date adjustDate = beginDate;
|
|
|
+ if (adjustDate != null) {
|
|
|
+ // 如果adjustDate不为空且为年初日期(xxxx-01-01),则转换为当年7月1日
|
|
|
+ LocalDateTime adjustDateTime = DateUtil.toLocalDateTime(adjustDate);
|
|
|
+ if (adjustDateTime.getMonthValue() == 1 && adjustDateTime.getDayOfMonth() == 1) {
|
|
|
+ //前端只选年份,这里默认为XXX-07-01
|
|
|
+ adjustDate = DateUtil.toDate(adjustDateTime.withMonth(7).withDayOfMonth(1));
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ //获取当年07-01的日期
|
|
|
+ /*LocalDate julyFirst = LocalDate.of(DateUtil.getYear(new Date()), 7, 1);
|
|
|
+ adjustDate = Date.from(julyFirst.atStartOfDay(ZoneId.systemDefault()).toInstant());*/
|
|
|
+ adjustDate = new Date();
|
|
|
+ }
|
|
|
+ String adjustDateStr = DateUtil.format(adjustDate,DateUtil.NORM_DATE_PATTERN);
|
|
|
+ AdjustmentContext ac = initAndLoad(executeYear, person, beginDate,adjustDate,positionAppointment,performanceResult,firstPersonPosFile);
|
|
|
+ ac.remark = remark;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //2、 判断是否为本年首次调整、考核是否已被使用,并根据规则决定是否使用R排名。
|
|
|
+ //对应SHR:341~391行
|
|
|
+ evaluateFirstAndAppraisalUsage(ac);
|
|
|
+
|
|
|
+ // 3、加载上一条记录并校验生效日期。
|
|
|
+ //对应SHR:393~421行
|
|
|
+ loadLastRecordAndValidateBeginDate(ac);
|
|
|
+
|
|
|
+ // 4、处理职位序列(如果是管理序列,则按职能序列进行调整)
|
|
|
+ //对应SHR:521~527行
|
|
|
+ DynamicObject convertJobSeq = JobLevelCalculatorService.handleJobSeq(ac.jobSeq);
|
|
|
+ ac.convertJobSeq = convertJobSeq;
|
|
|
+ DynamicObject convertLastJobSeq = JobLevelCalculatorService.handleJobSeq(ac.data.getLastJobSeq());
|
|
|
+ ac.data.setConvertLastJobSeq(convertLastJobSeq);
|
|
|
+
|
|
|
+ // 5、获取技能/职称分
|
|
|
+ //对应SHR:451~481行
|
|
|
+ JobLevelCalculatorService.JobScoreInfo jobScoreInfo = JobLevelCalculatorService.handleJobScores(ac.lastRecordInfo,convertJobSeq, ac.positionAppointment);
|
|
|
+ ac.data.setJobScoreInfo(jobScoreInfo);
|
|
|
+
|
|
|
+ // 6、计算学历得分并生成说明
|
|
|
+ //对应SHR:423~449行
|
|
|
+ ac.diplomaScore = JobLevelCalculatorService.handleDiplomaScore(ac.lastRecordInfo, ac.positionAppointment, jobScoreInfo);
|
|
|
+ ac.whyDiplomaScore.putAll(jobScoreInfo.whyDiplomaScore);
|
|
|
+
|
|
|
+ //---------------------------------- 调整日志 begin ----------------------------------
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("毕业时间小于等于【{}】的最高学历【{}】,学历分【{}】{}",
|
|
|
+ adjustDateStr, ConvertUtil.toStr(jobScoreInfo.diplomaName,"无"), ConvertUtil.toStr(jobScoreInfo.diplomaScore,"无"),jobScoreInfo.isDiplomaChange ? "(变化)":""));
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("聘任时间小于等于【{}】的职称【{}】,职称等级【{}】,职称等级分【{}】{}",
|
|
|
+ adjustDateStr,ConvertUtil.toStr(jobScoreInfo.rankName,"无"),ConvertUtil.toStr(jobScoreInfo.perProTitleName,"无"),ConvertUtil.toStr(jobScoreInfo.perProTitleScore,"无"),
|
|
|
+ jobScoreInfo.isPerProTitleChange ? "(变化)":""));
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("聘任时间小于等于【{}】的技能【{}】,技能等级【{}】,技能等级分【{}】{}",
|
|
|
+ adjustDateStr,ConvertUtil.toStr(jobScoreInfo.jobStatusName,"无"),ConvertUtil.toStr(jobScoreInfo.quaLevelName,"无"),ConvertUtil.toStr(jobScoreInfo.quaLevelScore,"无"),
|
|
|
+ jobScoreInfo.isQuaLeveleChange ? "(变化)":""));
|
|
|
+ //---------------------------------- 调整日志 end ----------------------------------
|
|
|
+
|
|
|
+ // 7、汇总年度积分池与综合分数。
|
|
|
+ //对应SHR:483~519行
|
|
|
+ aggregateScores(ac);
|
|
|
+
|
|
|
+
|
|
|
+ //8.确定目标职级(含有R排名和无R排名两条路径)。
|
|
|
+ //对应SHR:540~702行
|
|
|
+ DynamicObject jobLevel = decideTargetJobGrade(ac);
|
|
|
+
|
|
|
+ // 9、上年度考核结果为“无”时,取最低职级
|
|
|
+ //对应SHR:703~707行
|
|
|
+ if (AppraisalResultEnum.NONE.getCode().equals(ac.data.getAppraisalResultNumber())) {
|
|
|
+ // 考核结果为无时取最低职级
|
|
|
+ ac.adjustType = "8";
|
|
|
+ ac.adjustInt = 0;
|
|
|
+ jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
|
|
|
+ }
|
|
|
+ if(jobLevel == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
|
|
|
+ ac.personName,
|
|
|
+ ac.jobSeq.getString(FormConstant.NAME_KEY),
|
|
|
+ ac.allSumScore.toString(),
|
|
|
+ ac.data.getZgjbName(),
|
|
|
+ ac.data.getZyjndjName(),
|
|
|
+ ac.data.getAppraisalResultName()));
|
|
|
+ }
|
|
|
+
|
|
|
+ JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, beginDate == null ? new Date() : beginDate, ac.positionAppointment);
|
|
|
+ if(jobLevelResult.rankingResultInfo != null) {
|
|
|
+ ac.data.getRankingResultInfo().allowanceRankMark = jobLevelResult.rankingResultInfo.allowanceRankMark;
|
|
|
+ ac.data.getRankingResultInfo().allowanceRankSel = jobLevelResult.rankingResultInfo.allowanceRankSel;
|
|
|
+ }
|
|
|
+
|
|
|
+ //【三期需求】:计算保级原因
|
|
|
+ judgeLevelKeepReason(ac, jobLevel);
|
|
|
+
|
|
|
+ //10、构建职位档案
|
|
|
+ return buildPersonPositionFile(ac, jobLevel);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void judgeLevelKeepReason(AdjustmentContext ac, DynamicObject jobLevel) {
|
|
|
+ if(AdjustTypeEnum.KEEP_LEVEL.getCode().equals(ac.adjustType)){
|
|
|
+ if(ac.useAppraisalresult){
|
|
|
+ ac.levelKeepReason = 1;
|
|
|
+ }else{
|
|
|
+ BigDecimal allSumScore = ac.allSumScore;
|
|
|
+ int currentSeq = jobLevel.getInt(PositionStructureConstant.JOBLEVELSEQ);
|
|
|
+ //下一级所需的积分
|
|
|
+ DynamicObject nextJobLevel = ac.jobLevelByJobSeqMap.get(currentSeq + 1);
|
|
|
+ if(nextJobLevel != null) {
|
|
|
+ BigDecimal nextScore = nextJobLevel.getBigDecimal(FormConstant.NCKD_SCORE);
|
|
|
+ //如果未达到下一级积分则认定:总积分没有达到下一级最低分数要求
|
|
|
+ if (allSumScore.compareTo(nextScore) < 0) {
|
|
|
+ //allSumScore小于nextScore
|
|
|
+ if (ac.rankNoUpGrad) {
|
|
|
+ //总积分及排名均未达到
|
|
|
+ ac.levelKeepReason = 6;
|
|
|
+ } else {
|
|
|
+ //总积分没有达到下一级最低分数要求
|
|
|
+ ac.levelKeepReason = 2;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(ac.rankNoUpGrad) {
|
|
|
+ //R排名与全员绩效考核等级均未达到升级标准
|
|
|
+ ac.levelKeepReason = 5;
|
|
|
+ }else{
|
|
|
+ //判断职称等级或技能等级是否达到
|
|
|
+ //1、获取当前职称或技能等级序号
|
|
|
+ //2、获取>当前职级的所有职级的资格条件的序号,取最小序号
|
|
|
+ //3、判断当前职称或技能等级序号是否大于最小序号
|
|
|
+ //4、如果当前职级序号小于最小序号则认为未达到
|
|
|
+ //这里先不判断,因为其他所有保级情况都已涵盖。剩下的就只有职称或技能达不到了
|
|
|
+ if (JobSeqEnum.SKILL.getCode().equals(ac.jobSeq.getString(FormConstant.NUMBER_KEY))) {
|
|
|
+ ac.levelKeepReason = 4;
|
|
|
+ }else{
|
|
|
+ ac.levelKeepReason = 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ //达到职级上限
|
|
|
+ ac.levelKeepReason = 7;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化上下文并加载基础数据。
|
|
|
+ * @param executeYear 执行年份
|
|
|
+ * @param person 员工
|
|
|
+ * @param beginDate 生效日期(可以为空,为空默认为不生效状态)
|
|
|
+ * @param positionAppointment 员工最新任职信息
|
|
|
+ * @param performanceResult 上年度考核结果
|
|
|
+ * @param firstPersonPosFile 初定档案
|
|
|
+ * @return: nckd.jxccl.hr.psms.business.AnnualAdjustmentService.AdjustmentContext
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/08 21:15
|
|
|
+ */
|
|
|
+ private static AdjustmentContext initAndLoad(Integer executeYear, DynamicObject person, Date beginDate,Date adjustDate,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile){
|
|
|
+ AdjustmentContext ac = new AdjustmentContext();
|
|
|
+ ac.executeYear = executeYear;
|
|
|
+ ac.nowYear = executeYear;
|
|
|
+ ac.personId = person.getLong(FormConstant.ID_KEY);
|
|
|
+ ac.personName = person.getString(FormConstant.NAME_KEY);
|
|
|
+ ac.beginDate = beginDate;
|
|
|
+ ac.personInfo = person;
|
|
|
+
|
|
|
+ int lastYear = executeYear - 1;
|
|
|
+ LocalDateTime lastYearDateTime = LocalDateTime.of(lastYear, 1, 1, 0, 0);
|
|
|
+
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("选择的调整时间:【{}】", DateUtil.format(adjustDate,DateUtil.NORM_DATE_PATTERN)));
|
|
|
+
|
|
|
+
|
|
|
+ //对应SHR:291行;utils.YearContributeScoreBillEntryScoreSumBypersonidAndYear
|
|
|
+ //TODO 1、获取年度贡献积分
|
|
|
+ //yearscoresuma、yearscoresumb、yearscoresumc、yearscoresumd、yearscoresume、yearscoresumf、yearscoresumg、yearscoresumh、yearscoresumi、allyearscoresum、yearscoresuma
|
|
|
+
|
|
|
+ //2、获取上年度考核结果(对应SHR:PersonpositionfileUtils:884~914行)
|
|
|
+ // DynamicObject performanceResult = PerformanceManagerHelper.getPerformanceResult(ac.personId, lastYearDateTime);
|
|
|
+ if(performanceResult == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】缺少【{}】年考核结果", ac.personName,lastYear));
|
|
|
+ }
|
|
|
+ AnnualAdjustmentData data = new AnnualAdjustmentData();
|
|
|
+ data.setAppraisalResult(performanceResult);
|
|
|
+ data.setAppraisalResultNumber(performanceResult.getString(FormConstant.NUMBER_KEY));
|
|
|
+ data.setAppraisalResultName(performanceResult.getString(FormConstant.NAME_KEY));
|
|
|
+ data.setAppraisalResultId(performanceResult.getLong(FormConstant.ID_KEY));
|
|
|
+ data.setAppraisalResultScore(performanceResult.getBigDecimal(FormConstant.NCKD_SCORE));
|
|
|
+
|
|
|
+
|
|
|
+ //3.获取员工上年度年度R排名(对应SHR:PersonpositionfileUtils:915~945行)
|
|
|
+ DynamicObject lastPersonPosFile = null;
|
|
|
+ JobLevelCalculatorService.RankingResultInfo rankingInfo = JobLevelCalculatorService.getRankingInfo(ac.personId, ac.personName, beginDate == null ? new Date() : beginDate);
|
|
|
+ data.setRankingResultInfo(rankingInfo);
|
|
|
+
|
|
|
+ //4.查询上一条有效年度调整记录(对应SHR:PersonpositionfileUtils:946~997行)
|
|
|
+ DynamicObject[] personPosFileByPersonAndState = PositionStructureHelper.getPersonPosFileByPersonAndState(ac.personId,
|
|
|
+ new String[]{"3","4"},
|
|
|
+ null,null,
|
|
|
+ QueryFieldBuilder.create()
|
|
|
+ .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR,PositionStructureConstant.MODIFY_TIME_KEY,PositionStructureConstant.NCKD_BEGINDATE).buildOrderArray());
|
|
|
+ if(personPosFileByPersonAndState == null || personPosFileByPersonAndState.length < 1){
|
|
|
+ //没有年度调整记录取初定记录
|
|
|
+ if(firstPersonPosFile == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", ac.personName));
|
|
|
+ }
|
|
|
+ lastPersonPosFile = firstPersonPosFile;
|
|
|
+ data.setFirstId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
|
|
|
+ String typeState = lastPersonPosFile.getString(PositionStructureConstant.NCKD_TYPESTATE);
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一条有效职位档案为【{}】档案,初定时间:【{}】;", TypeStateEnum.getByCode(typeState).getName(), DateUtil.format(lastPersonPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE),DateUtil.NORM_DATE_PATTERN)));
|
|
|
+ }else{
|
|
|
+ lastPersonPosFile = personPosFileByPersonAndState[0];
|
|
|
+ data.setLastId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
|
|
|
+ String typeState = lastPersonPosFile.getString(PositionStructureConstant.NCKD_TYPESTATE);
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一条有效职位档案为【{}】档案,调整时间:【{}】", TypeStateEnum.getByCode(typeState).getName(),DateUtil.format(lastPersonPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE),DateUtil.NORM_DATE_PATTERN)));
|
|
|
+ }
|
|
|
+ if(lastPersonPosFile != null) {
|
|
|
+ //(firstid、lastsumscore、lastdiplomascore、lastrankscore、lastjobstatusscore、lasthrjobfamilynumber、lastjobgradefid、lastjobgradeindex)
|
|
|
+ data.setLastSumScore(lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE));
|
|
|
+
|
|
|
+ //lastdiplomascore
|
|
|
+ //lastrankscore
|
|
|
+ //lastjobstatusscore
|
|
|
+ DynamicObject jobSeq = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR);
|
|
|
+ data.setLastHrJobFamilyNumber(jobSeq.getString(FormConstant.NUMBER_KEY));
|
|
|
+ data.setLastJobSeq(jobSeq);
|
|
|
+ DynamicObject jobLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
|
|
|
+ data.setLastJobLevel(jobLevel);
|
|
|
+ data.setLastJobGradeIndex(jobLevel.getInt(FormConstant.JOBLEVELSEQ));
|
|
|
+
|
|
|
+ //---------------------------------- 调整日志 begin ----------------------------------
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位序列【{}】", jobSeq.getString(FormConstant.NAME_KEY)));
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案总积分【{}】", lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_ALLSUMSCORE)));
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案积分池积分【{}】", data.getLastSumScore()));
|
|
|
+
|
|
|
+ DynamicObject diploma = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
|
|
|
+ BigDecimal diplomaScore = lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_DIPLOMASCORE);
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案学历【{}】,学历分【{}】", diploma == null ? "无" : diploma.getString(FormConstant.NAME_KEY), diplomaScore == null ? "无" : diplomaScore));
|
|
|
+
|
|
|
+ String rankName = lastPersonPosFile.getString(PositionStructureConstant.NCKD_RANKNAME);
|
|
|
+ rankName = StringUtils.isEmpty(rankName) ? "无" : rankName;
|
|
|
+ DynamicObject proTitleLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_PROTITLELEVEL);
|
|
|
+ String ranksScore = lastPersonPosFile.getString(PositionStructureConstant.NCKD_RANKSCORE);
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案职称【{}】,职称等级【{}】,职称等级分【{}】", rankName,proTitleLevel == null ? "无" : proTitleLevel.getString(FormConstant.NAME_KEY), ranksScore == null ? "无" : ranksScore));
|
|
|
+
|
|
|
+ String jobStatusName = lastPersonPosFile.getString(PositionStructureConstant.NCKD_JOBSTATUSNAME);
|
|
|
+ jobStatusName = StringUtils.isEmpty(jobStatusName) ? "无" : jobStatusName;
|
|
|
+ DynamicObject ocpQualLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_OCPQUALLEVEL);
|
|
|
+ String jobStatusScore = lastPersonPosFile.getString(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案技能【{}】,技能等级【{}】,技能等级分【{}】", jobStatusName,ocpQualLevel == null ? "无" : ocpQualLevel.getString(FormConstant.NAME_KEY), jobStatusScore == null ? "无" : jobStatusScore));
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上一职位档案职级【{}({})】", jobLevel.getString(FormConstant.NAME_KEY),data.getLastJobGradeIndex()));
|
|
|
+ ac.whyAdjust1.add("----------------------------------------------------------");
|
|
|
+ //---------------------------------- 调整日志 end ----------------------------------
|
|
|
+ }
|
|
|
+
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上年度【{}】考核结果为【{}】;", lastYear, performanceResult.getString(FormConstant.NAME_KEY)));
|
|
|
+ if(rankingInfo == null) {
|
|
|
+ ac.whyAdjust1.add("无全排名");
|
|
|
+ }else{
|
|
|
+ ac.whyAdjust1.add(StrFormatter.format("上年度【{}】排名单元【{}】,全排名名次【{}】,全排名总人数【{}】,R排名名次【{}】,R排名总人数【{}】;",
|
|
|
+ lastYear, rankingInfo.groupName,rankingInfo.topRank,rankingInfo.allowanceRank,rankingInfo.count,rankingInfo.countR));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //5.获取员工任职信息(对应SHR:PersonpositionfileUtils:998~1009行)
|
|
|
+ // PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(ac.personId, beginDate == null ? new Date() : beginDate);
|
|
|
+ DynamicObject perEduExp = positionAppointment.getPerEduExp();
|
|
|
+ if(perEduExp != null){
|
|
|
+ data.setDiplomaId(perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY)));
|
|
|
+ data.setDiplomaScore(perEduExp.getBigDecimal(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.NCKD_SCORE)));
|
|
|
+ }
|
|
|
+ DynamicObject empPosOrgRel = positionAppointment.getEmpPosOrgRel();
|
|
|
+ if(empPosOrgRel != null){
|
|
|
+ data.setPersonDepId(empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
|
|
|
+ data.setHrJobFamilyId(empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY)));
|
|
|
+ data.setHrJobFamilyNumber(empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY)));
|
|
|
+ data.setPositionId(empPosOrgRel.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY)));
|
|
|
+ data.setPositionName(empPosOrgRel.getString(String.join(".", FormConstant.POSITION_KEY, FormConstant.NAME_KEY)));
|
|
|
+ data.setPersonName(empPosOrgRel.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.NAME_KEY)));
|
|
|
+ // data.setHrOrgUnitId();
|
|
|
+ }else{
|
|
|
+ throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", ac.personName, DateUtil.format(beginDate == null ? new Date() : beginDate,DateUtil.NORM_DATE_PATTERN)));
|
|
|
+ }
|
|
|
+ DynamicObject perProTitle = positionAppointment.getPerProTitle();
|
|
|
+ if(perProTitle != null){
|
|
|
+ data.setRankName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
|
|
|
+ data.setZgjbId(perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY)));
|
|
|
+ data.setZgjbNumber(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY)));
|
|
|
+ data.setZgjbName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
|
|
|
+ data.setZgjbScore(perProTitle.getBigDecimal(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE)));
|
|
|
+ }
|
|
|
+ DynamicObject perOcpQual = positionAppointment.getPerOcpQual();
|
|
|
+ if(perOcpQual != null){
|
|
|
+ data.setJobStatusName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
|
|
|
+ data.setZyjndjId(perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY)));
|
|
|
+ data.setZyjndjNumber(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY)));
|
|
|
+ data.setZyjndjName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
|
|
|
+ data.setZyjndjScore(perOcpQual.getBigDecimal(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE)));
|
|
|
+ }
|
|
|
+ if (data.getHrJobFamilyId() == null || data.getHrJobFamilyId() == 0) {
|
|
|
+ throw new ValidationException(StrFormatter.format("无职位序列,请检查当前人员【{}】任职的岗位【{}】是否有职位序列",ac.personName,data.getPositionName()));
|
|
|
+ }
|
|
|
+ ac.positionAppointment = positionAppointment;
|
|
|
+ long jobSeq = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
|
|
|
+ ac.jobSeq = BusinessDataServiceHelper.loadSingle(jobSeq,FormConstant.HBJM_JOBSEQHR);
|
|
|
+
|
|
|
+ ac.data = data;
|
|
|
+
|
|
|
+ // 对应SHR:307~322行
|
|
|
+ // 判断R位次存在性
|
|
|
+ if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().topRank == null) {
|
|
|
+ //如果没有排名与R排名,则按保级处理
|
|
|
+ ac.keep = true;
|
|
|
+ }
|
|
|
+ if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().allowanceRankPercent <= 0) {
|
|
|
+ ac.haveRp = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 对应SHR:323~339行
|
|
|
+ // 考核结果对应升降级逻辑
|
|
|
+ if (AppraisalResultEnum.EXCELLENT.getCode().equals(ac.data.getAppraisalResultNumber())) {
|
|
|
+ ac.minusByAppraisal = 1;
|
|
|
+ ac.whyAdjust.append("【考核结果优秀】升1级");
|
|
|
+ } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
|
|
|
+ ac.minusByAppraisal = -1;
|
|
|
+ ac.whyAdjust.append("【考核结果基本合格】降1级");
|
|
|
+ } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
|
|
|
+ ac.minusByAppraisal = -2;
|
|
|
+ ac.whyAdjust.append("【考核结果不合格】降2级");
|
|
|
+ }
|
|
|
+ return ac;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为本年首次调整、考核是否已被使用,并根据规则决定是否使用R排名。
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @return: void
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/08 22:12
|
|
|
+ */
|
|
|
+ private static void evaluateFirstAndAppraisalUsage(AdjustmentContext ac) {
|
|
|
+
|
|
|
+ DynamicObject nowYearPersonPosFile = PositionStructureHelper.getLatsPersonPosFileByPerson(ac.personId, new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR, QCP.equals, ac.nowYear));
|
|
|
+
|
|
|
+ boolean isyearfirstdo = false;
|
|
|
+ if (nowYearPersonPosFile != null) {
|
|
|
+ DynamicObject nowYearAppraisalResult = nowYearPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_APPRAISALRESULT);
|
|
|
+ String nowYearAppraisalResultNumber = nowYearAppraisalResult.getString(FormConstant.NUMBER_KEY);
|
|
|
+ String appraisalResultNumber = ac.data.getAppraisalResultNumber();
|
|
|
+ if (!StringUtils.equals(nowYearAppraisalResultNumber, appraisalResultNumber)) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】记录才能继续操作 !", ac.personName,ac.nowYear));
|
|
|
+ }
|
|
|
+ LocalDateTime nowYearDateTime = LocalDateTime.of(ac.nowYear, 1, 1, 0, 0);
|
|
|
+ StringBuilder reasons = new StringBuilder();
|
|
|
+ ac.useAppraisalresult = JobLevelCalculatorService.useAppraisalResult(ac.personId, DateUtil.toDate(nowYearDateTime),reasons);
|
|
|
+ ac.useAppraisalResultReasons = reasons;
|
|
|
+ if (ac.useAppraisalresult && ac.minusByAppraisal != 0) {
|
|
|
+ ac.whyAdjust.append(" 非").append(ac.nowYear)
|
|
|
+ .append("年度首次调整,且升降【考核结果】已被使用,不需要考虑R排名,保级处理");
|
|
|
+ ac.haveRp = false;
|
|
|
+ ac.keep = true;
|
|
|
+ }
|
|
|
+ ac.whyAdjust1.add("【非】本年度首次调整,"+reasons.toString());
|
|
|
+ } else {
|
|
|
+ ac.whyAdjust.append(" ").append(ac.nowYear).append("年度首次调整,【考核结果】未被使用");
|
|
|
+ ac.whyAdjust1.add("本年度首次调整");
|
|
|
+ isyearfirstdo = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ac.useAppraisalresult) {
|
|
|
+ ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】已被使用,保级处理");
|
|
|
+ isyearfirstdo = false;
|
|
|
+ ac.keep = true;
|
|
|
+ } else {
|
|
|
+ ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】未被使用");
|
|
|
+ ac.keep = false;
|
|
|
+ isyearfirstdo = true;
|
|
|
+ if (ac.minusByAppraisal == 0) {
|
|
|
+ ac.whyAdjust.append("【考核结果】为保级,");
|
|
|
+ Double p = Optional.ofNullable(ac.data.getRankingResultInfo())
|
|
|
+ .map(info -> info.allowanceRankPercent)
|
|
|
+ .orElse(null);
|
|
|
+ if (p == null || p <= 0) {
|
|
|
+ ac.whyAdjust.append("无R排名");
|
|
|
+ ac.haveRp = false;
|
|
|
+ } else {
|
|
|
+ ac.whyAdjust.append("使用R排名进行计算 ");
|
|
|
+ ac.haveRp = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ac.isYearFirstDo = isyearfirstdo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载上一条记录并校验生效日期。
|
|
|
+ * @param ac 上下文
|
|
|
+ * @return: void
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/08 23:13
|
|
|
+ */
|
|
|
+ private static void loadLastRecordAndValidateBeginDate(AdjustmentContext ac) {
|
|
|
+ ac.lastRecordId = ac.data.getLastId() != null && ac.data.getLastId() != 0 ? ac.data.getLastId() : ac.data.getFirstId();
|
|
|
+ if (ac.lastRecordId == null || ac.lastRecordId == 0) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,缺少职位及积分初定信息,请先完成初定,第二年再操作年度调整!", ac.personName));
|
|
|
+ }
|
|
|
+ ac.lastRecordInfo = BusinessDataServiceHelper.loadSingle(ac.lastRecordId, PositionStructureConstant.PERSONPOSFILE_ENTITYID);
|
|
|
+
|
|
|
+ if (ac.beginDate != null
|
|
|
+ && ac.lastRecordInfo != null
|
|
|
+ && ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE) != null) {
|
|
|
+ Date lastBegin = ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE);
|
|
|
+ if (ac.beginDate.compareTo(lastBegin) <= 0) {
|
|
|
+ throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为最近调整时间为【{}】,不能早于最近一次职位调整时间。", ac.personName,DateUtil.format(lastBegin,DateUtil.NORM_DATE_PATTERN)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 汇总年度积分池与综合分数。
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @return: void
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/08 23:46
|
|
|
+ */
|
|
|
+ private static void aggregateScores(AdjustmentContext ac) {
|
|
|
+ BigDecimal allyearscoresum =
|
|
|
+ ac.data.getAllYearScoreSum() == null ? BigDecimal.ZERO : ac.data.getAllYearScoreSum();
|
|
|
+ System.out.println("上年所有贡献单据分数之和" + allyearscoresum);
|
|
|
+
|
|
|
+ BigDecimal appraisalresultscore =
|
|
|
+ ac.data.getAppraisalResultScore() == null ? BigDecimal.ZERO : ac.data.getAppraisalResultScore();
|
|
|
+ System.out.println("上年年度考核得分" + appraisalresultscore);
|
|
|
+
|
|
|
+ ac.lastYearContributeScore = allyearscoresum.add(appraisalresultscore);
|
|
|
+ System.out.println("上年度贡献综合评价分" + ac.lastYearContributeScore);
|
|
|
+
|
|
|
+ ac.addYearContributeScore = ac.lastYearContributeScore.multiply(new BigDecimal("0.1"));
|
|
|
+ System.out.println("年度新增的贡献积分" + ac.addYearContributeScore);
|
|
|
+
|
|
|
+ BigDecimal lastsum = ac.data.getLastSumScore() == null ? BigDecimal.ZERO : ac.data.getLastSumScore();
|
|
|
+ System.out.println("上年累计积分池的分" + lastsum);
|
|
|
+
|
|
|
+ ac.sumScore = lastsum.add(ac.addYearContributeScore);
|
|
|
+ System.out.println("累计积分池的分" + ac.sumScore);
|
|
|
+
|
|
|
+
|
|
|
+ ac.allSumScore = ac.sumScore
|
|
|
+ .add(ac.diplomaScore)
|
|
|
+ .add(ac.data.getJobScoreInfo().perProTitleScore)
|
|
|
+ .add(ac.data.getJobScoreInfo().quaLevelScore);
|
|
|
+ System.out.println("累计总积分" + ac.allSumScore);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确定目标职级(含有R排名和无R排名两条路径)。
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @note 对应原 PersonpositionfilecreateViewListHandler:540~707 行
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/10 14:17
|
|
|
+ */
|
|
|
+ private static DynamicObject decideTargetJobGrade(AdjustmentContext ac) {
|
|
|
+
|
|
|
+ //上一条职位档案的职位序列(如果是管理序列,则转换为职能序列)
|
|
|
+ String newJobSeqNumber = ac.convertJobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+ String newLastJobSeqNumber = ac.data.getConvertLastJobSeq().getString(FormConstant.NUMBER_KEY);
|
|
|
+ if (!newJobSeqNumber.equalsIgnoreCase(newLastJobSeqNumber)) {
|
|
|
+ //序列发生变化
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,序列发生变化!请先到【员工职位档案调整】中进行操作", ac.personName));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ac.haveRp) {
|
|
|
+ return decideWithR(ac);
|
|
|
+ } else {
|
|
|
+ return decideWithoutR(ac);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 有 R 排名时的定位路径。
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/10 14:18
|
|
|
+ */
|
|
|
+ private static DynamicObject decideWithR(AdjustmentContext ac) {
|
|
|
+ //根据[职位序列][积分]和[职称等级/技能等级]获取职位序列最高职级
|
|
|
+ DynamicObject jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ if(jobLevel == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
|
|
|
+ ac.personName,
|
|
|
+ ac.jobSeq.getString(FormConstant.NAME_KEY),
|
|
|
+ ac.allSumScore.toString(),
|
|
|
+ ac.data.getZgjbName(),
|
|
|
+ ac.data.getZyjndjName(),
|
|
|
+ ac.data.getAppraisalResultName()));
|
|
|
+ }
|
|
|
+ int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
|
|
|
+
|
|
|
+ Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.convertJobSeq);
|
|
|
+ ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
|
|
|
+ int newjobgradeindex = ac.data.getLastJobGradeIndex();
|
|
|
+
|
|
|
+ // 补充根据R排名确定是否升级
|
|
|
+ if (ac.minusByAppraisal == 0) {
|
|
|
+ // 如果考评结果不需要升降级则验证R排名
|
|
|
+ if (!ac.keep) {
|
|
|
+ newjobgradeindex = JobLevelCalculatorService.getnewjobgradeindexByrank(ac.data.getLastJobGradeIndex(), newjobgradeindex, ac.data.getRankingResultInfo(), ac.convertJobSeq);
|
|
|
+ if(ac.data.getLastJobGradeIndex() == newjobgradeindex){
|
|
|
+ ac.rankNoUpGrad = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (newjobgradeindex > jobGradeindex) {
|
|
|
+ //超过职位序列最高职级则退回职位序列最高职级
|
|
|
+ newjobgradeindex = jobGradeindex;
|
|
|
+ //但超出『积分』和『职称等级、技能等级』的所能定级的最高职级,按
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 考核结果需要升降级则直接执行
|
|
|
+ newjobgradeindex = ac.data.getLastJobGradeIndex() + ac.minusByAppraisal;
|
|
|
+ if (jobGradeindex <= newjobgradeindex) {
|
|
|
+ //超过职位序列最高职级则退回职位序列最高职级
|
|
|
+ newjobgradeindex = jobGradeindex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确定是否超出总分最高任命职级
|
|
|
+ DynamicObject maxJobLevel = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore);
|
|
|
+ int personMaxjobgradeindex = maxJobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ // 超出总分能任命最高职职级则该最高职级就是要任命的职级
|
|
|
+ if (newjobgradeindex > personMaxjobgradeindex) {
|
|
|
+ newjobgradeindex = personMaxjobgradeindex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确定是否超出该职称等级或技能等级最高任命职级
|
|
|
+ DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
|
|
|
+ int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
|
|
|
+ if (newjobgradeindex > maxJobGradeIndex) {
|
|
|
+ newjobgradeindex = maxJobGradeIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((ac.data.getZgjbId() == null || ac.data.getZyjndjId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
|
|
|
+ //如果没有聘任则取最低职级
|
|
|
+ jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
|
|
|
+ if(jobLevel != null) {
|
|
|
+ newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ }
|
|
|
+ }else if(jobLevelByJobSeqMap.get(newjobgradeindex) != null){
|
|
|
+ jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
|
|
|
+ }else{
|
|
|
+ //兜底;如果没有获取到合适的职级则取最低级
|
|
|
+ jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
|
|
|
+ if(jobLevel != null) {
|
|
|
+ newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if((ac.data.getZgjbId() == null || ac.data.getZyjndjId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
|
|
|
+ //无聘任
|
|
|
+ ac.adjustType = "7";
|
|
|
+ }else if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
|
|
|
+ //保级
|
|
|
+ ac.adjustType = "1";
|
|
|
+ }else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
|
|
|
+ //升级
|
|
|
+ ac.adjustType = "2";
|
|
|
+ } else {
|
|
|
+ //降级
|
|
|
+ ac.adjustType = "0";
|
|
|
+ }
|
|
|
+ // 升降级数
|
|
|
+ ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
|
|
|
+
|
|
|
+ return jobLevel;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 无 R 排名时的定位路径。
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/10 14:18
|
|
|
+ */
|
|
|
+ private static DynamicObject decideWithoutR(AdjustmentContext ac) {
|
|
|
+ // String JobGrade = "";
|
|
|
+ DynamicObject jobLevel = null;
|
|
|
+
|
|
|
+ if (ac.minusByAppraisal == 0 && ac.keep) {
|
|
|
+ System.out.println("绩效分组排名,没有勾选享受职位津贴,保持原职级不变,没有则为最低档");
|
|
|
+ ac.whyAdjust.append("绩效分组排名,没有勾选享受职位津贴,保持原职级不变");
|
|
|
+ jobLevel = ac.data.getLastJobLevel();
|
|
|
+ ac.adjustType = "1";
|
|
|
+ if(ac.useAppraisalresult){
|
|
|
+ ac.levelKeepReason = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jobLevel == null) {
|
|
|
+ jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
|
|
|
+ ac.adjustType = "0";
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ if(jobLevel == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
|
|
|
+ ac.personName,
|
|
|
+ ac.jobSeq.getString(FormConstant.NAME_KEY),
|
|
|
+ ac.allSumScore.toString(),
|
|
|
+ ac.data.getZgjbName(),
|
|
|
+ ac.data.getZyjndjName(),
|
|
|
+ ac.data.getAppraisalResultName()));
|
|
|
+ }
|
|
|
+
|
|
|
+ int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
|
|
|
+
|
|
|
+ Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.data.getConvertLastJobSeq());
|
|
|
+ ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
|
|
|
+ int newjobgradeindex = ac.data.getLastJobGradeIndex();
|
|
|
+
|
|
|
+ if (ac.isYearFirstDo) {
|
|
|
+ newjobgradeindex += ac.minusByAppraisal;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jobGradeindex <= newjobgradeindex) {
|
|
|
+ newjobgradeindex = jobGradeindex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确定是否超出该职称等级或技能等级最高任命职级
|
|
|
+ DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
|
|
|
+ int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
|
|
|
+ if (newjobgradeindex > maxJobGradeIndex) {
|
|
|
+ newjobgradeindex = maxJobGradeIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果最后超出最低排名则按最低排名
|
|
|
+ if (jobLevelByJobSeqMap.get(newjobgradeindex) != null) {
|
|
|
+ jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
|
|
|
+ } else {
|
|
|
+ jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
|
|
|
+ if(jobLevel != null) {
|
|
|
+ newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置调整类型和调整级别数
|
|
|
+ if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
|
|
|
+ //保级
|
|
|
+ ac.adjustType = "1";
|
|
|
+ } else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
|
|
|
+ //升级
|
|
|
+ ac.adjustType = "2";
|
|
|
+ } else {
|
|
|
+ //降级
|
|
|
+ ac.adjustType = "0";
|
|
|
+ }
|
|
|
+ // 升降级数
|
|
|
+ ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
|
|
|
+ }
|
|
|
+
|
|
|
+ return jobLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建职位档案
|
|
|
+ * @param ac 上下文对象
|
|
|
+ * @param jobLevel 目标职级对象
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @note 对应原 710~803行
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/10 15:44
|
|
|
+ */
|
|
|
+ private static DynamicObject buildPersonPositionFile(AdjustmentContext ac, DynamicObject jobLevel) {
|
|
|
+
|
|
|
+ DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
|
|
|
+ PositionStructureConstant.PERSONPOSFILE_ENTITYID);
|
|
|
+
|
|
|
+ DynamicObject empPosOrgRel = ac.positionAppointment.getEmpPosOrgRel();
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_PERSON, ac.personInfo);
|
|
|
+ DynamicObject company = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
|
|
|
+ company.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.COMPANY_KEY,FormConstant.ID_KEY)));
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.USEORG_KEY, company);
|
|
|
+ DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
|
|
|
+ dep.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.ORG_KEY, dep);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.CREATEORG_KEY, dep);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_TYPESTATE, "3");
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_EXECUTEYEAR, ac.executeYear);
|
|
|
+ Long positionId = empPosOrgRel.getLong(String.join(".",FormConstant.POSITION_KEY,FormConstant.ID_KEY));
|
|
|
+ DynamicObject position = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBPM_POSITIONHR);
|
|
|
+ position.set(FormConstant.ID_KEY, positionId);
|
|
|
+ Long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
|
|
|
+ DynamicObject jobSeq = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBJM_JOBSEQHR);
|
|
|
+ jobSeq.set(FormConstant.ID_KEY, jobSeqId);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSEQHR, jobSeq);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_POSITIONHR, position);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_RANKNAME, "无".equalsIgnoreCase(ac.data.getRankName()) ? null : ac.data.getRankName());
|
|
|
+ DynamicObject proTitleLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_PROTITLELEVEL);
|
|
|
+ proTitleLevel.set(FormConstant.ID_KEY, ac.data.getZgjbId());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_PROTITLELEVEL, proTitleLevel);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSNAME, "无".equalsIgnoreCase(ac.data.getZyjndjName()) ? null : ac.data.getZyjndjName());
|
|
|
+ DynamicObject ocpQualLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_OCPQUALLEVEL);
|
|
|
+ ocpQualLevel.set(FormConstant.ID_KEY, ac.data.getZyjndjId());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_OCPQUALLEVEL, ocpQualLevel);
|
|
|
+ DynamicObject diploma = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_DIPLOMA);
|
|
|
+ diploma.set(FormConstant.ID_KEY, ac.data.getDiplomaId());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMA, diploma);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_JOBLEVELHR, jobLevel);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_LASTPERSONPOSFILE, ac.lastRecordInfo);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMASCORE, ac.diplomaScore);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_WHYDIPLOMASCORE, ac.whyDiplomaScore.toString());
|
|
|
+ if(ac.data.getJobScoreInfo() != null) {
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_RANKSCORE, ac.data.getJobScoreInfo().perProTitleScore);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSSCORE, ac.data.getJobScoreInfo().quaLevelScore);
|
|
|
+ }
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLSUMSCORE, ac.allSumScore);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_SUMSCORE,ac.sumScore);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTTYPE,ac.adjustType);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSSTATUS, EnableEnum.NO.getCode());
|
|
|
+ if(ac.beginDate != null){
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_BEGINDATE, ac.beginDate);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSSTATUS, EnableEnum.YES.getCode());
|
|
|
+ }
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTINT, ac.adjustInt);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_FIRSTRANK, EnableEnum.NO.getCode());
|
|
|
+
|
|
|
+ if(ac.data.getRankingResultInfo() != null) {
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANK, ac.data.getRankingResultInfo().topRank);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANK, ac.data.getRankingResultInfo().allowanceRank);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANKPERCENT, ac.data.getRankingResultInfo().topRankPercent);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKMARK, ac.data.getRankingResultInfo().allowanceRankMark);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKSEL, ac.data.getRankingResultInfo().allowanceRankSel);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKPCT, ac.data.getRankingResultInfo().allowanceRankPercent);
|
|
|
+ }
|
|
|
+ //上年度考核结果
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_APPRAISALRESULT, ac.data.getAppraisalResult());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_RESULTSCORE, ac.data.getAppraisalResultScore());
|
|
|
+
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_DISABLE, EnableEnum.NO.getCode());
|
|
|
+ // newPersonPosFile.set(PositionStructureConstant.NCKD_ISCURRENTNEWEST, EnableEnum.YES.getCode());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.STATUS, StatusEnum.C.toString());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMA, ac.data.getYearscoresuma());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMB, ac.data.getYearscoresumb());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMC, ac.data.getYearscoresumc());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMD, ac.data.getYearscoresumd());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUME, ac.data.getYearscoresume());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMF, ac.data.getYearscoresumf());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMG, ac.data.getYearscoresumg());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMH, ac.data.getYearscoresumh());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMI, ac.data.getYearscoresumi());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_LYRCONTRIBSCORE, ac.lastYearContributeScore);
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ALLYEARSCORESUM, ac.data.getAllYearScoreSum());
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE, ac.addYearContributeScore);
|
|
|
+ // 备注
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, ac.remark);
|
|
|
+ if(AdjustTypeEnum.KEEP_LEVEL.getCode().equals(ac.adjustType)){
|
|
|
+ //保级原因
|
|
|
+ newPersonPosFile.set(PositionStructureConstant.NCKD_LEVELKEEPREASON, ac.levelKeepReason+"");
|
|
|
+ }
|
|
|
+
|
|
|
+ return newPersonPosFile;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 内部运行时上下文。
|
|
|
+ */
|
|
|
+ private static class AdjustmentContext {
|
|
|
+ /** 执行年份 */
|
|
|
+ Integer executeYear;
|
|
|
+
|
|
|
+ /** 当前年份 */
|
|
|
+ Integer nowYear;
|
|
|
+
|
|
|
+ /** 调整生效日期 */
|
|
|
+ Date beginDate;
|
|
|
+
|
|
|
+ /** 员工ID */
|
|
|
+ Long personId;
|
|
|
+
|
|
|
+ /** 员工Name */
|
|
|
+ String personName;
|
|
|
+
|
|
|
+ /** 员工信息对象 */
|
|
|
+ DynamicObject personInfo;
|
|
|
+
|
|
|
+ /** 强类型数据对象(替代原 selMap) */
|
|
|
+ AnnualAdjustmentData data;
|
|
|
+
|
|
|
+ /** 是否有R位次(allowancerankpercent≤0则为false) */
|
|
|
+ boolean haveRp = true;
|
|
|
+
|
|
|
+ /** 是否保级(缺R位次时为true) */
|
|
|
+ boolean keep = false;
|
|
|
+
|
|
|
+ /** 本年考核是否已使用 */
|
|
|
+ boolean useAppraisalresult = false;
|
|
|
+
|
|
|
+ /** 本年考核被使用的原因*/
|
|
|
+ StringBuilder useAppraisalResultReasons;
|
|
|
+
|
|
|
+ /** 是否本年首次调整 */
|
|
|
+ boolean isYearFirstDo = false;
|
|
|
+
|
|
|
+ /** 考核升降级标记(+1升/-1降/-2降两级) */
|
|
|
+ int minusByAppraisal = 0;
|
|
|
+
|
|
|
+ /** 固定类型(默认3) */
|
|
|
+ String typestate = "3";
|
|
|
+
|
|
|
+ /** 上一条档案记录ID */
|
|
|
+ Long lastRecordId;
|
|
|
+
|
|
|
+ /** 上一条记录对象 */
|
|
|
+ DynamicObject lastRecordInfo;
|
|
|
+
|
|
|
+ /** 上年度贡献综合评价分 */
|
|
|
+ BigDecimal lastYearContributeScore = BigDecimal.ZERO;
|
|
|
+ /** 年度新增贡献积分 */
|
|
|
+ BigDecimal addYearContributeScore = BigDecimal.ZERO;
|
|
|
+ /** 当前累计积分池 */
|
|
|
+ BigDecimal sumScore = BigDecimal.ZERO;
|
|
|
+ /** 总分(池+学历+职称/技能) */
|
|
|
+ BigDecimal allSumScore = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ /** 学历得分 */
|
|
|
+ BigDecimal diplomaScore = BigDecimal.ZERO;
|
|
|
+ /** 学历得分说明 */
|
|
|
+ Map<String, String> whyDiplomaScore = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ /** 职称得分 */
|
|
|
+ BigDecimal rankScore = BigDecimal.ZERO;
|
|
|
+ /** 技能得分 */
|
|
|
+ BigDecimal jobStatusScore = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ /** 调整说明 */
|
|
|
+ StringBuffer whyAdjust = new StringBuffer();
|
|
|
+
|
|
|
+ /** 升降类型:0降/1保/2升/7固定/8无考核结果 */
|
|
|
+ String adjustType;
|
|
|
+ /** 升降数量:正数升、负数降、0保 */
|
|
|
+ Integer adjustInt;
|
|
|
+
|
|
|
+ PositionAppointmentBO positionAppointment;
|
|
|
+ /**当前任职的序列(未转换前,这里转换指的是:如果是管理序列,则按职能序列进行调整)*/
|
|
|
+ DynamicObject jobSeq;
|
|
|
+ /**当前任职的序列(转换后,这里转换指的是:如果是管理序列,则按职能序列进行调整)*/
|
|
|
+ DynamicObject convertJobSeq;
|
|
|
+
|
|
|
+ String remark;
|
|
|
+
|
|
|
+ /** 调整说明 */
|
|
|
+ StringJoiner whyAdjust1 = new StringJoiner(StrFormatter.LINE_SEPARATOR);
|
|
|
+
|
|
|
+ /**标记排名未升级*/
|
|
|
+ boolean rankNoUpGrad = false;
|
|
|
+
|
|
|
+ /***1:本自然年度内已升过级,达到升级限制;2:总积分没有达到下一级最低分数要求;3:未聘任高一级的职称;4:未聘任高一级的技能,5:R排名与全员绩效考核等级均未达到升级标准;6:总积分及排名均未达到;7:达到职级上限*/
|
|
|
+ Integer levelKeepReason;
|
|
|
+
|
|
|
+ Map<Integer, DynamicObject> jobLevelByJobSeqMap;
|
|
|
+ }
|
|
|
+}
|