|
@@ -0,0 +1,1521 @@
|
|
|
+package nckd.jxccl.hr.psms.business;
|
|
|
+
|
|
|
+import kd.bos.algo.DataSet;
|
|
|
+import kd.bos.algo.Row;
|
|
|
+import kd.bos.common.enums.EnableEnum;
|
|
|
+import kd.bos.dataentity.entity.DynamicObject;
|
|
|
+import kd.bos.dataentity.entity.DynamicObjectCollection;
|
|
|
+import kd.bos.entity.constant.StatusEnum;
|
|
|
+import kd.bos.logging.Log;
|
|
|
+import kd.bos.logging.LogFactory;
|
|
|
+import kd.bos.orm.query.QCP;
|
|
|
+import kd.bos.orm.query.QFilter;
|
|
|
+import kd.bos.servicehelper.BusinessDataServiceHelper;
|
|
|
+import kd.bos.servicehelper.QueryServiceHelper;
|
|
|
+import nckd.jxccl.base.common.constant.FormConstant;
|
|
|
+import nckd.jxccl.base.common.enums.AppraisalResultEnum;
|
|
|
+import nckd.jxccl.base.common.enums.JobSeqEnum;
|
|
|
+import nckd.jxccl.base.common.exception.ValidationException;
|
|
|
+import nckd.jxccl.base.common.utils.DateUtil;
|
|
|
+import nckd.jxccl.base.common.utils.StrFormatter;
|
|
|
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
|
|
|
+import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
|
|
|
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
|
|
|
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
|
|
|
+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.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.StringJoiner;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 职级计算器,用于计算员工的职级
|
|
|
+ * 功能:根据职称等级、技能等级、学历、年度贡献分、绩效考核结果、R排名等信息,动态计算员工应获得的职级
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date 2025/9/19 13:56
|
|
|
+ * @version 1.0
|
|
|
+ */
|
|
|
+public class JobLevelCalculatorService {
|
|
|
+
|
|
|
+ protected final static Log logger = LogFactory.getLog(JobLevelCalculatorService.class);
|
|
|
+
|
|
|
+ /** 技术序列 */
|
|
|
+ public static final String JS_JOB_SEQ = "01";
|
|
|
+ /** 职能序列 */
|
|
|
+ public static final String ZN_JOB_SEQ = "02";
|
|
|
+ /** 技能序列 */
|
|
|
+ public static final String JN_JOB_SEQ = "03";
|
|
|
+ /** 管理序列 */
|
|
|
+ public static final String GL_JOB_SEQ = "04";
|
|
|
+ /** 职称等级与技能等级映射关系*/
|
|
|
+ public static final Map<String, String> TechPostLevelNumberTojobstatusNumberMap = new HashMap<>();
|
|
|
+
|
|
|
+ static {
|
|
|
+ // 正高级---高级技师
|
|
|
+ TechPostLevelNumberTojobstatusNumberMap.put("001", "1");
|
|
|
+ // 副高级---高级技师
|
|
|
+ TechPostLevelNumberTojobstatusNumberMap.put("002", "1");
|
|
|
+ // 中级---技师
|
|
|
+ TechPostLevelNumberTojobstatusNumberMap.put("003", "2");
|
|
|
+ // 助理级---高级工
|
|
|
+ TechPostLevelNumberTojobstatusNumberMap.put("004", "3");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算员工职级(单条生成年度调记录、新建调整查询职级、新建动态调整)
|
|
|
+ * @param positionAppointment 人员最新信息(最新学历、最新聘任....)
|
|
|
+ * @return: java.lang.String
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/19 14:19
|
|
|
+ */
|
|
|
+ public static JobLevelResult calculateJobLevel(DynamicObject person, Date date, DynamicObject positionAppointment) {
|
|
|
+ //资格三要素:
|
|
|
+ // • 职称/技能等级:硬性门槛(如工程师职称、技师等级)
|
|
|
+ // • 职业生涯累计积分:量化能力总分(由学历、职称、年度贡献分累计)
|
|
|
+ // • 年度绩效考核结果(R排名):决定升降的关键绩效指标
|
|
|
+ Long personId = person.getLong(FormConstant.ID_KEY);
|
|
|
+ String personName = person.getString(FormConstant.NAME_KEY);
|
|
|
+ //对应SHR:hrjobfamilyid
|
|
|
+ long jobSeqId = positionAppointment.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY));
|
|
|
+ DynamicObject jobSeq = BusinessDataServiceHelper.loadSingle(jobSeqId, FormConstant.HBJM_JOBSEQHR);
|
|
|
+ JobLevelResult jobLevelResult = new JobLevelResult();
|
|
|
+ //对应SHR:JobGradeid
|
|
|
+ // jobLevelResult.jobLevel
|
|
|
+
|
|
|
+ // 1. 获取最近的职位档案(必须存在,否则抛错)
|
|
|
+ //对应SHR:personpositionfileInfo
|
|
|
+ DynamicObject currentPersonPosFile = PositionStructureHelper.getLatsPersonPosFileByPerson(personId);
|
|
|
+ if(currentPersonPosFile == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", person.getString(FormConstant.NAME_KEY)));
|
|
|
+ }
|
|
|
+ //对应SHR:oldHRJobFamilyid
|
|
|
+ long currentJobSeqId = currentPersonPosFile.getLong(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.ID_KEY));
|
|
|
+ //oldHRJobFamilynumber
|
|
|
+ String currentJobSeqNumber = currentPersonPosFile.getString(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.NUMBER_KEY));
|
|
|
+ DynamicObject appraisalResult = currentPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_APPRAISALRESULT);
|
|
|
+ //appraisalresultnumber
|
|
|
+ String appraisalResultNumber = appraisalResult != null ? appraisalResult.getString(FormConstant.NUMBER_KEY) : null;
|
|
|
+
|
|
|
+ // 2. 处理职位序列(如果是管理序列,则按职能序列进行变动)
|
|
|
+ //对应SHR:hrjobfamilynumber
|
|
|
+ /*jobSeq = handleJobSeq(jobSeq);
|
|
|
+ // 3. 检查是否缺少聘任条件(没有聘任的职称或技能按最低职级定)
|
|
|
+ if (isInvalidRankingConditions(proTitleLevelNumber, ocpQualLevelNumber, jobSeq.getString(FormConstant.NUMBER_KEY))) {
|
|
|
+ logger.warn("人员【{}】职位序列【{}】无聘任职称/技能,按最低职级", personName,jobSeq.getString(FormConstant.NAME_KEY));
|
|
|
+ DynamicObject lowestJobGrade = getLowestJobLevel(jobSeq);
|
|
|
+ //对应SHR:selMap.put("adjusttype","7")
|
|
|
+ jobLevelResult.adjustType = "7";
|
|
|
+ jobLevelResult.adjustMsg = "不符合三要素【无聘任职称/技能】,定最低职级";
|
|
|
+ return jobLevelResult;
|
|
|
+ }*/
|
|
|
+
|
|
|
+ // 4. 读取并校验上年度考核结果
|
|
|
+ // 对应SHR:874~911
|
|
|
+ appraisalResultNumber = validateAppraisalResultConsistency(date, personId, personName, appraisalResultNumber);
|
|
|
+
|
|
|
+
|
|
|
+ //当前总积分(变动前)
|
|
|
+ BigDecimal sumScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE);
|
|
|
+
|
|
|
+ //当前职级(变动前)
|
|
|
+ //对应SHR:JobGrade;916~919行
|
|
|
+ DynamicObject currentJobLevel = currentPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
|
|
|
+ if(currentJobLevel == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("数据异常,人员【{}】最新职位档案【档案ID:{}】没有职级。请联系管理员处理!", personName, currentPersonPosFile.getLong(FormConstant.ID_KEY)));
|
|
|
+ }
|
|
|
+ //对应SHR:lastjobgradeindex
|
|
|
+ int currentJobLevelIndex = currentJobLevel.getInt(FormConstant.INDEX_KEY);
|
|
|
+
|
|
|
+ // 5.是否首次聘任
|
|
|
+ //对应SHR:fistPR;922~926行
|
|
|
+ boolean firstPR = isFirstPR(personId);
|
|
|
+
|
|
|
+ // 6. 是否已使用过考核结果
|
|
|
+ //对应SHR:useappraisalresult;926行
|
|
|
+ boolean usedAppraisalResult = useAppraisalResult(personId,date);
|
|
|
+
|
|
|
+ // 7. 计算降级数
|
|
|
+ //对应SHR:minuspersonappraisal;930~945行
|
|
|
+ int minusPersonAppraisal = calcMinusPersonAppraisal(appraisalResultNumber, firstPR, usedAppraisalResult, jobLevelResult);
|
|
|
+
|
|
|
+ // 8. 处理学历积分
|
|
|
+ //对应SHR:DiplomaScore;947~978行
|
|
|
+ BigDecimal diplomaScore = handleDiplomaScore(currentPersonPosFile, positionAppointment);
|
|
|
+
|
|
|
+ // 9.如果是管理序列,则按职能序列进行调整
|
|
|
+ //对应SHR:hrjobfamilynumber;989~993
|
|
|
+ jobSeq = handleJobSeq(jobSeq);
|
|
|
+
|
|
|
+ // 9. 处理序列相关信息
|
|
|
+ //对应SHR:995~1013
|
|
|
+ JobFamilyInfo jobFamilyInfo = processJobFamilyInfo(jobSeq, currentPersonPosFile);
|
|
|
+
|
|
|
+ // 10. 处理职称/技能等级积分
|
|
|
+ // 对应SHR:1015~1036
|
|
|
+ JobScoreInfo jobScoreInfo = handleJobScores(jobSeq, currentPersonPosFile, positionAppointment);
|
|
|
+
|
|
|
+ // 计算总积分;累计积分池的分 + 学历 + (职称分/技能分)
|
|
|
+ // 对应SHR:allsumScore;1041行
|
|
|
+ BigDecimal allSumScore = sumScore.add(diplomaScore)
|
|
|
+ .add(jobScoreInfo.perProTitleScore)
|
|
|
+ .add(jobScoreInfo.quaLevelScore);
|
|
|
+
|
|
|
+ // 11. 获取序列对应的职级
|
|
|
+ DynamicObjectCollection jobLevelByJobSeq = getJobLevelByJobSeq(jobSeq);
|
|
|
+ //对应SHR:jobgradeMap
|
|
|
+ Map<Integer, DynamicObject> jobLevelMap = jobLevelByJobSeq.stream()
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ dynamicObject -> dynamicObject.getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ)),
|
|
|
+ dynamicObject -> dynamicObject)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 12. 根据不同情况计算职级ID
|
|
|
+ //对应SHR:JobGradeInfo;1044~1071行
|
|
|
+ DynamicObject jobLevel = calculateJobGradeId(jobSeq,jobScoreInfo,jobFamilyInfo,allSumScore,currentJobLevelIndex,minusPersonAppraisal,firstPR,jobLevelResult);
|
|
|
+ //对应SHR:newjobgradeindex
|
|
|
+ int jobLevelIndex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ // 13. 处理排名相关信息
|
|
|
+ //对应SHR:allowancerankpercent、toprankpercent、allowancerank、toprank;1073~1109行
|
|
|
+ RankingResult rankingInfo = getRankingInfo(personId, personName, date);
|
|
|
+
|
|
|
+ // 14. 处理序列转换或跨单位调动的特殊逻辑
|
|
|
+ // SHR对应:1110~1136行
|
|
|
+ if (jobFamilyInfo.isSequenceChange) {
|
|
|
+ System.out.println("序列切换,新职级号不能大于原序列职级序号");
|
|
|
+ handleSequenceChange(jobLevelIndex,currentJobLevelIndex,rankingInfo,jobLevelMap,jobLevelResult,jobSeq);
|
|
|
+ jobLevel = jobLevelResult.jobLevel;
|
|
|
+ return jobLevelResult;
|
|
|
+ } else if (jobFamilyInfo.isCrossUnitTransfer) {
|
|
|
+ System.out.println("跨单位调动且未跨序列,平移处理");
|
|
|
+ handleCrossUnitTransfer(jobLevelIndex,currentJobLevelIndex,rankingInfo,jobLevelMap,jobLevelResult,jobSeq);
|
|
|
+ jobLevel = jobLevelResult.jobLevel;
|
|
|
+ return jobLevelResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 15. 获取最近一次聘任信息
|
|
|
+ //对应SHR:PR_lastjobgradeindex;1139~1147行
|
|
|
+ int PR_lastjobgradeindex = getLastAppointmentJobGradeIndex(personId, currentJobLevelIndex,jobLevelIndex);
|
|
|
+ if (jobLevelIndex - PR_lastjobgradeindex > 1) {
|
|
|
+ jobLevelIndex = PR_lastjobgradeindex + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 16. 根据考核结果使用情况处理职级计算
|
|
|
+ //对应SHR行:1149~1180行
|
|
|
+ jobLevelIndex = handleAppraisalUsage(jobSeq,rankingInfo,jobLevelIndex,PR_lastjobgradeindex,currentJobLevelIndex,minusPersonAppraisal,firstPR,usedAppraisalResult,jobLevelResult);
|
|
|
+
|
|
|
+ // 17. 处理首次聘任情况 & 处理聘任相关限制
|
|
|
+ //对应SHR:1182~1295行
|
|
|
+ jobLevelIndex = handleAppointmentRestrictions(personId, firstPR,usedAppraisalResult,jobSeq,jobSeqId,currentJobSeqId,currentJobSeqNumber,jobScoreInfo,currentJobLevelIndex,jobLevelIndex,allSumScore,jobLevelResult,minusPersonAppraisal,currentPersonPosFile);
|
|
|
+
|
|
|
+ // 18.最终确定职级ID
|
|
|
+ //对应SHR行:1297~1320行
|
|
|
+ jobLevel = determineFinalJobGradeId(jobLevelIndex, jobScoreInfo, jobLevelMap, jobSeq, jobLevelResult, appraisalResultNumber);
|
|
|
+ jobLevelResult.jobLevel = jobLevel;
|
|
|
+
|
|
|
+ return jobLevelResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 读取并校验上年度考核结果
|
|
|
+ * 目的:如果不是新年度首次定级,则必须存在上年考核结果;若本年度已有人为创建记录,则要保证“上年考核结果在系统中的值”与上笔记录一致,避免数据不一致导致错误定级。
|
|
|
+ * @param date 日期
|
|
|
+ * @param personId 人员ID
|
|
|
+ * @param personName 人员名称
|
|
|
+ * @param appraisalResultNumber 最近应用的考核结果
|
|
|
+ * @return: java.lang.String 如果当前年度没有记录,则使用去年的考核结果
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 20:41
|
|
|
+ */
|
|
|
+ public static String validateAppraisalResultConsistency(Date date, Long personId, String personName, String appraisalResultNumber) {
|
|
|
+ //SHR原代码:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#getJobGradeid:874~911行
|
|
|
+ //对应SHR:beginyear
|
|
|
+ int year = DateUtil.getYear(date);
|
|
|
+ //对应SHR:lastyear
|
|
|
+ LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(date), 1);
|
|
|
+ //对应SHR:selpersonappraisalresultByyear()方法
|
|
|
+ DynamicObject lastYearAppraisalResult = PerformanceManagerHelper.getPerformanceResult(personId, lastYearDateTime);
|
|
|
+ //上年度考核结果;
|
|
|
+ // 对应SHR:personappraisalresultMap
|
|
|
+ String lastYearAppraisalResultNumber = lastYearAppraisalResult != null ? lastYearAppraisalResult.getString(FormConstant.NUMBER_KEY) : null;
|
|
|
+ //当前年度职位档案;
|
|
|
+ // 对应SHR:nowyearpersonpositionfileCollection
|
|
|
+ DynamicObject currentYearPersonPosFileByPerson = PositionStructureHelper.getLatsPersonPosFileByPerson(personId,new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR,QCP.equals,year));
|
|
|
+ //对应SHR:newpersonfirstrank
|
|
|
+ //对应SHR:878行
|
|
|
+ boolean newPersonFirstRank = isNewPersonFirstRank(date, personId);
|
|
|
+ // 目的:如果不是新年度首次定级,则必须存在上年考核结果;若本年度已有人为创建记录,则要保证“上年考核结果在系统中的值”与上笔记录一致,避免数据不一致导致错误定级。
|
|
|
+ // 检查是否存在去年的考核结果
|
|
|
+ //对应SHR:if (personappraisalresultMap == null || personappraisalresultMap.size() == 0 || personappraisalresultMap.get("appraisalresultnumber") == null);884行
|
|
|
+ boolean hasLastYearAppraisal = StringUtils.isNotBlank(lastYearAppraisalResultNumber);
|
|
|
+
|
|
|
+ // 检查当前年度是否已有职位档案记录
|
|
|
+ //对应SHR:nowyearpersonpositionfileCollection == null || nowyearpersonpositionfileCollection.size() == 0;888行
|
|
|
+ boolean hasCurrentYearRecord = currentYearPersonPosFileByPerson != null && currentYearPersonPosFileByPerson.getDataEntityType() != null;
|
|
|
+
|
|
|
+
|
|
|
+ // 如果不是新员工初定情况,必须有去年考核结果
|
|
|
+ if (!newPersonFirstRank && !hasLastYearAppraisal) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】缺少【{}】年考核结果", personName,lastYearDateTime.getYear()));
|
|
|
+ }
|
|
|
+ // 如果当前年度没有记录,则使用去年的考核结果
|
|
|
+ if (!hasCurrentYearRecord) {
|
|
|
+ appraisalResultNumber = hasLastYearAppraisal ? lastYearAppraisalResultNumber : StringUtils.EMPTY;
|
|
|
+ } else {
|
|
|
+ // 如果当前年度已有记录,则验证考核结果是否一致
|
|
|
+ String lastYearAppraisalNumber = hasLastYearAppraisal ? lastYearAppraisalResultNumber : null;
|
|
|
+
|
|
|
+ // 对于新员工,特殊处理"无"考核结果(无考核结果)
|
|
|
+ if (newPersonFirstRank) {
|
|
|
+ // 如果去年没有考核结果,但当前有非"无"的考核结果,则报错
|
|
|
+ if (StringUtils.isBlank(lastYearAppraisalNumber) &&
|
|
|
+ StringUtils.isNotBlank(appraisalResultNumber) &&
|
|
|
+ !AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber)) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year));
|
|
|
+ }
|
|
|
+ // 如果去年有考核结果,但与当前不一致,则报错
|
|
|
+ if (StringUtils.isNotBlank(lastYearAppraisalNumber) &&
|
|
|
+ !lastYearAppraisalNumber.equals(appraisalResultNumber)) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 对于非新员工,必须有去年考核结果且与当前一致
|
|
|
+ if (!lastYearAppraisalNumber.equals(appraisalResultNumber)) {
|
|
|
+ throw new ValidationException(StrFormatter.format("人员【{}】上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return appraisalResultNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理职位序列(如果是管理序列,则按职能序列进行调整)
|
|
|
+ * @param jobSeq 序列
|
|
|
+ * @return: java.lang.String
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/19 14:07
|
|
|
+ */
|
|
|
+ public static DynamicObject handleJobSeq(DynamicObject jobSeq) {
|
|
|
+ if(GL_JOB_SEQ.equalsIgnoreCase(jobSeq.getString(FormConstant.NUMBER_KEY))){
|
|
|
+ QFilter filter = QFilterCommonHelper.getEnableFilter().and(QFilterCommonHelper.getDataStatusFilter()).and(new QFilter(FormConstant.NUMBER_KEY, QCP.equals, JobSeqEnum.FUNCTIONAL.getCode()));
|
|
|
+ StringJoiner selectFields = new StringJoiner(",");
|
|
|
+ selectFields.add(FormConstant.ID_KEY);
|
|
|
+ selectFields.add(FormConstant.NUMBER_KEY);
|
|
|
+ selectFields.add(FormConstant.NAME_KEY);
|
|
|
+ DynamicObject[] load = BusinessDataServiceHelper.load(FormConstant.HBJM_JOBSEQHR, selectFields.toString(), new QFilter[]{filter});
|
|
|
+ if(load.length == 0){
|
|
|
+ throw new ValidationException(StrFormatter.format("管理序列转职能序列错误,未找到编码为【{}】的职能序列。请维护编码为【{}】的职能序列!", ZN_JOB_SEQ,ZN_JOB_SEQ));
|
|
|
+ }
|
|
|
+ return load[0];
|
|
|
+ }else{
|
|
|
+ //其他不转换
|
|
|
+ return jobSeq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查职称/技能等级是否有效(避免职级计算错误)
|
|
|
+ * @param proTitleLevelNumber 职称等级编码
|
|
|
+ * @param ocpQualLevel 技能等级编码
|
|
|
+ * @param jobSeqNumber 职位序列编码
|
|
|
+ * @return: boolean
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/19 15:35
|
|
|
+ */
|
|
|
+ private boolean isInvalidRankingConditions(String proTitleLevelNumber, String ocpQualLevel, String jobSeqNumber) {
|
|
|
+ // 情况1:无职称且无技能等级(或非技能序列)
|
|
|
+ if (StringUtils.isBlank(proTitleLevelNumber) && (StringUtils.isBlank(ocpQualLevel) || JobSeqEnum.SKILL.getCode().equals(jobSeqNumber))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 情况2:技能序列下无技能等级,但有职称等级且职称无对应技能等级
|
|
|
+ return JN_JOB_SEQ.equals(jobSeqNumber) &&
|
|
|
+ StringUtils.isBlank(ocpQualLevel) &&
|
|
|
+ StringUtils.isNotBlank(proTitleLevelNumber) &&
|
|
|
+ TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber) == null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取职位序列最低职级
|
|
|
+ * @param jobSeq 职位序列
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 16:52
|
|
|
+ */
|
|
|
+ public static DynamicObject getLowestJobLevel(DynamicObject jobSeq) {
|
|
|
+
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ //职位序列
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY))
|
|
|
+ //职级
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_COEFFICIENT))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE));
|
|
|
+
|
|
|
+ QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ), QCP.equals, jobSeq.getLong(FormConstant.ID_KEY))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0));
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.JOBSEQTOJOBLEVELQUERY,
|
|
|
+ selectFields.toString(),
|
|
|
+ new QFilter[]{filter},
|
|
|
+ String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ return !query.isEmpty() ? query.get(0) : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取职位序列和积分最高职级
|
|
|
+ * @param jobSeq 职位序列
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 16:52
|
|
|
+ */
|
|
|
+ public static DynamicObject getMaxJobLevel(DynamicObject jobSeq,BigDecimal score) {
|
|
|
+
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ //职位序列
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY))
|
|
|
+ //职级
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_COEFFICIENT))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE));
|
|
|
+
|
|
|
+ QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ), QCP.equals, jobSeq.getLong(FormConstant.ID_KEY))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE), QCP.less_equals, score));
|
|
|
+
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.JOBSEQTOJOBLEVELQUERY,
|
|
|
+ selectFields.toString(),
|
|
|
+ new QFilter[]{filter},
|
|
|
+ String.join(".", FormConstant.HBJM_JOBGRADESCMHR, FormConstant.JOBLEVELSEQ +" desc"));
|
|
|
+ return !query.isEmpty() ? query.get(0) : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取职位序列和积分最高职级
|
|
|
+ * @param jobSeq 职位序列
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 16:52
|
|
|
+ */
|
|
|
+ private static DynamicObject getMaxJobLevel(DynamicObject jobSeq,String proTitleLevelNumber,String ocpQualLevelNumber) {
|
|
|
+ jobSeq = handleJobSeq(jobSeq);
|
|
|
+ String jobSeqNumber = jobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+ /**
|
|
|
+ * 如果没有职称等级与技能等级,则倒取该序列最低职等 如果没有职称且是非技能序列,则倒取该序列最低职等 如果是技能序列,没有技能等级但有职称等级,但职称等级没有对应的技能等级,则取该序列最低职等
|
|
|
+ */
|
|
|
+ if (StringUtils.isBlank(proTitleLevelNumber) && (StringUtils.isBlank(ocpQualLevelNumber) || !JobSeqEnum.SKILL.getCode().equals(jobSeqNumber))) {
|
|
|
+ return getLowestJobLevel(jobSeq);
|
|
|
+ }
|
|
|
+ else if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) && StringUtils.isBlank(ocpQualLevelNumber) && StringUtils.isNotBlank(proTitleLevelNumber) && TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber) == null) {
|
|
|
+ return getLowestJobLevel(jobSeq);
|
|
|
+ }
|
|
|
+ String likename = "";
|
|
|
+ String likenumber = "";
|
|
|
+ if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber)) {
|
|
|
+ if (StringUtils.isBlank(ocpQualLevelNumber)) {
|
|
|
+ likenumber = TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber);
|
|
|
+ likename = "职称等级【" + proTitleLevelNumber + "】对应的技能等级";
|
|
|
+ System.out.println("技能序列但无技能等级,使用职称等级【" + proTitleLevelNumber + "】对应的技能等级,编码【" + likenumber + "】");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ likenumber = ocpQualLevelNumber;
|
|
|
+ likename = "技能等级";
|
|
|
+ System.out.println("技能序列使用技能等级,编码【" + likenumber + "】");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ likenumber = proTitleLevelNumber;
|
|
|
+ likename = "职称等级";
|
|
|
+ System.out.println("非技能序列使用职称等级,编码【" + likenumber + "】");
|
|
|
+ }
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ //职位序列
|
|
|
+ .add(String.join(".", FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".", FormConstant.NCKD_JOBSEQ,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".", FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY))
|
|
|
+ //职级
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_COEFFICIENT))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE));
|
|
|
+
|
|
|
+ QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ), QCP.equals, jobSeq.getLong(FormConstant.ID_KEY))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBGRADEHR, FormConstant.NCKD_JOBLEVELNUMBER), QCP.like, "%"+likenumber+"%"));
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.JOBSEQTOJOBLEVELQUERY,
|
|
|
+ selectFields.toString(),
|
|
|
+ new QFilter[]{filter},
|
|
|
+ String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ +" desc"));
|
|
|
+ return !query.isEmpty() ? query.get(0) : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取职位序列对应的职级
|
|
|
+ * @param jobSeq 职位序列
|
|
|
+ * @return: kd.bos.dataentity.entity.DynamicObject
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 16:52
|
|
|
+ */
|
|
|
+ private static DynamicObjectCollection getJobLevelByJobSeq(DynamicObject jobSeq) {
|
|
|
+
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ //职位序列
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBGRADESCMHR, FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBGRADESCMHR, FormConstant.NCKD_JOBSEQ,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBGRADESCMHR, FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY))
|
|
|
+ //职级
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_COEFFICIENT))
|
|
|
+ .add(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE));
|
|
|
+
|
|
|
+ QFilter filter = new QFilter(String.join(".", FormConstant.NCKD_JOBSEQ), QCP.equals, jobSeq.getLong(FormConstant.ID_KEY))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
|
|
|
+ .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0));
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.JOBSEQTOJOBLEVELQUERY,
|
|
|
+ selectFields.toString(),
|
|
|
+ new QFilter[]{filter},
|
|
|
+ String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ return query;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查某个人在特定年份的考核结果是否被使用
|
|
|
+ * 对应SHR:useAppraisalresult方法
|
|
|
+ * @param personId 人员ID
|
|
|
+ * @param date 日期
|
|
|
+ * @return: boolean
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 15:10
|
|
|
+ */
|
|
|
+ private static boolean useAppraisalResult(Long personId, Date date) {
|
|
|
+ //对应SHR代码:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#useAppraisalresult
|
|
|
+
|
|
|
+ //查询条件:查询某个人员职位档案非失效状态 并且 (初定 或者 类型状态为:年度调整3;职位调动4) 并且 职称分数或技能分数>0的数据
|
|
|
+ QFilter rankScore = new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.large_than, 0));
|
|
|
+ QFilter jobScore = new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.large_than, 0));
|
|
|
+ QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
|
|
|
+ .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
|
|
|
+ .and(new QFilter(PositionStructureConstant.NCKD_FIRSTRANK,QCP.equals, EnableEnum.YES.getCode()).or(new QFilter(PositionStructureConstant.NCKD_TYPESTATE,QCP.in, new String[]{"3","4"})))
|
|
|
+ .and(rankScore.or(jobScore));
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ .add(PositionStructureConstant.NCKD_EXECUTEYEAR)
|
|
|
+ .add(PositionStructureConstant.NCKD_FIRSTRANK)
|
|
|
+ .add(PositionStructureConstant.NCKD_ADJUSTTYPE)
|
|
|
+ .add(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+
|
|
|
+ int year = DateUtil.getYear(date);
|
|
|
+
|
|
|
+ try(DataSet dataSet = QueryServiceHelper.queryDataSet(JobLevelCalculatorService.class.getName()+"#useAppraisalResult", PositionStructureConstant.PERSONPOSFILE_ENTITYID,
|
|
|
+ selectFields.toString(), new QFilter[]{filer}, "nckd_executeyear desc,nckd_begindate desc")){
|
|
|
+
|
|
|
+ // 条件1:如果调整年与初定年相同,则考核结果被使用;返回true
|
|
|
+ if(dataSet
|
|
|
+ .filter(PositionStructureConstant.NCKD_FIRSTRANK + " = true and " + PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
|
|
|
+ .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false) == 1){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ //条件2:如果调整年没有任何调整记录,则考核结果未被使用。(Personpositionfiletypestate:3、4);返回false
|
|
|
+ int nowyeardo = dataSet.copy()
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
|
|
|
+ .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false);
|
|
|
+ if(nowyeardo == 0){
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ //条件3:调整年是否有升降级或首聘(adjusttype:0、2),如果调整年有升降级记录(调整类型为0-降级或2-升级),则考核结果被使用。;返回true
|
|
|
+ if(dataSet.copy()
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year +"and "+PositionStructureConstant.NCKD_ADJUSTTYPE + "in ('0','2')")
|
|
|
+ .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false) > 0){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ //条件4:最近一笔是否为首次聘任(adjusttype:3),如果最近一次调整是首次聘任(调整类型为3),则考核结果未被使用。;返回false
|
|
|
+ DataSet firstRow = dataSet.copy().topBy(1, new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"});
|
|
|
+ if (firstRow.hasNext()) {
|
|
|
+ Row row = firstRow.next();
|
|
|
+ String adjustType = row.getString(PositionStructureConstant.NCKD_ADJUSTTYPE);
|
|
|
+ if("3".equalsIgnoreCase(adjustType)){
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //条件5:调整年是否只有保级/序列变化等记录,如果调整年只有保级、序列变化、聘任下调、总分不足或无聘任的记录,则考核结果未被使用(adjusttype in ('1','4','5','6','7'));返回false
|
|
|
+ int count5 = dataSet.copy()
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year +"and "+PositionStructureConstant.NCKD_ADJUSTTYPE + "in ('1','4','5','6','7')")
|
|
|
+ .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false);
|
|
|
+ if(count5 > 0 && count5 == nowyeardo){
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //条件6:调整年聘任职级是否变化,如果调整年职级有变化(最大职级-最小职级),则考核结果被使用。返回true
|
|
|
+ try(DataSet finish = dataSet.copy()
|
|
|
+ .select("max(" + String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ) + ") - min(" + String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ) + ") as differ")
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
|
|
|
+ .groupBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR})
|
|
|
+ .finish()) {
|
|
|
+ if (finish.hasNext()) {
|
|
|
+ Row row = finish.next();
|
|
|
+ Integer differ = row.getInteger("differ");
|
|
|
+ if (differ != 0) {
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //条件7:相比往年聘任职级是否有变化,如果调整年相比往年聘任职级有变化,则考核结果被使用。返回true
|
|
|
+ //获取当年最大职级
|
|
|
+ DataSet currentYear = dataSet.copy()
|
|
|
+ .orderBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"})
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
|
|
|
+ .groupBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR}).max(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ), "maxLevel").finish();
|
|
|
+
|
|
|
+ Integer maxLevel = 0;
|
|
|
+ if (currentYear.hasNext()) {
|
|
|
+ Row row = currentYear.next();
|
|
|
+ maxLevel = row.getInteger("maxLevel");
|
|
|
+ }
|
|
|
+ //获取往年职级
|
|
|
+ DataSet pastYear = dataSet.copy()
|
|
|
+ .select(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ) +"as level")
|
|
|
+ .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "<" + year)
|
|
|
+ .topBy(1, new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"});
|
|
|
+ Integer level = 0;
|
|
|
+ if (pastYear.hasNext()) {
|
|
|
+ Row row = pastYear.next();
|
|
|
+ level = row.getInteger("level");
|
|
|
+ }
|
|
|
+ if(maxLevel - level != 0){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否首次聘任
|
|
|
+ * @param personId 人员ID
|
|
|
+ * @return: boolean
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 18:20
|
|
|
+ */
|
|
|
+ private static boolean isFirstPR(long personId) {
|
|
|
+ QFilter rankScore = new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.large_than, 0));
|
|
|
+ QFilter jobScore = new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.large_than, 0));
|
|
|
+ QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
|
|
|
+ .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
|
|
|
+ .and(rankScore.or(jobScore))
|
|
|
+ .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ .add(PositionStructureConstant.NCKD_FIRSTRANK)
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NAME_KEY));
|
|
|
+ //对应SHR:PRpersonpositionfileCollection
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.PERSONPOSFILE_ENTITYID, selectFields.toString(), new QFilter[]{filer}, "nckd_executeyear desc,nckd_begindate desc");
|
|
|
+ //如果没有符合条件的记录,或者只有一条记录且为初定且考核结果为空或为考核结果为“无”,为首次聘任
|
|
|
+ if(query.isEmpty()){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }else if(query.size() == 1){
|
|
|
+ DynamicObject dynamicObject = query.get(0);
|
|
|
+ boolean firstRank = dynamicObject.getBoolean(PositionStructureConstant.NCKD_FIRSTRANK);
|
|
|
+ long appraisalResult = dynamicObject.getLong(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.ID_KEY));
|
|
|
+ String appraisalResultNumber = dynamicObject.getString(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
|
|
|
+ //考核结果为空或为考核结果为“无”
|
|
|
+ boolean none = StringUtils.isBlank(appraisalResultNumber) || AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber);
|
|
|
+ return firstRank && none;
|
|
|
+
|
|
|
+ }
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否为新员工初定且时间在第二年内
|
|
|
+ * @param beginYear 日期
|
|
|
+ * @param personId 人员
|
|
|
+ * @return: boolean
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 19:04
|
|
|
+ */
|
|
|
+ private static boolean isNewPersonFirstRank(Date beginYear, long personId) {
|
|
|
+ DynamicObject firstRankDynamicObject = PositionStructureHelper.getFirstRank(personId);
|
|
|
+ if(firstRankDynamicObject == null){
|
|
|
+ throw new ValidationException(StrFormatter.format("未获取到人员【{}】首次初定档案", personId));
|
|
|
+ }
|
|
|
+ String typeState = firstRankDynamicObject.getString(PositionStructureConstant.NCKD_TYPESTATE);
|
|
|
+ boolean firstRank = firstRankDynamicObject.getBoolean(PositionStructureConstant.NCKD_FIRSTRANK);
|
|
|
+ int executeYear = firstRankDynamicObject.getInt(PositionStructureConstant.NCKD_EXECUTEYEAR);
|
|
|
+ int year = DateUtil.getYear(beginYear);
|
|
|
+ if(firstRank && typeState.equals("1")){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+ //新员工初定次年调整也允许没有考核结果
|
|
|
+ if(year - 1 == executeYear){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算考核结果对职级的影响
|
|
|
+ * @param appraisalResultNumber 考核结果
|
|
|
+ * @param firstPR 是否首次聘任
|
|
|
+ * @param usedAppraisalResult 是否使用考核结果
|
|
|
+ * @param jobLevelResult 职级计算结果
|
|
|
+ * @return: int
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 21:16
|
|
|
+ */
|
|
|
+ private static int calcMinusPersonAppraisal(String appraisalResultNumber, boolean firstPR,boolean usedAppraisalResult,JobLevelResult jobLevelResult) {
|
|
|
+ int minuspersonappraisal = 0;
|
|
|
+ if (!usedAppraisalResult || firstPR) {
|
|
|
+ if (AppraisalResultEnum.EXCELLENT.getCode().equals(appraisalResultNumber)) {
|
|
|
+ // 优秀 -> 升一级(动态调整阶段允许升)
|
|
|
+ minuspersonappraisal = -1;
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(appraisalResultNumber)) {
|
|
|
+ // 基本合格 -> 降一级(但动态调整阶段一般保级)
|
|
|
+ minuspersonappraisal = 1;
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(appraisalResultNumber)) {
|
|
|
+ // 不合格 -> 降两级(但动态调整阶段一般保级)
|
|
|
+ minuspersonappraisal = 2;
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ System.out.println("考核结果未被使用,所以根据考核结果确定升降" + minuspersonappraisal);
|
|
|
+ return minuspersonappraisal;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理学历积分
|
|
|
+ * 根据当前最新学历与上一次职位档案中的学历对比,计算学历积分
|
|
|
+ * 如果学历未变化,则使用原有积分;如果学历有变化,则根据积分差值调整总积分
|
|
|
+ * @param currentPersonPosFileByPerson 最近一次职位档案
|
|
|
+ * @param positionAppointment 人员最信息(最新学历)
|
|
|
+ * @return: int
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/20 22:06
|
|
|
+ */
|
|
|
+ public static BigDecimal handleDiplomaScore(DynamicObject currentPersonPosFileByPerson, DynamicObject positionAppointment) {
|
|
|
+
|
|
|
+ //获取上一笔职位档案变动记录学历信息
|
|
|
+ //对应SHR:lastDiplomaInfo
|
|
|
+ DynamicObject diploma = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
|
|
|
+ //对应SHR:lastDiplomaInfo.getId()
|
|
|
+ long diplomaId = diploma.getLong(FormConstant.ID_KEY);
|
|
|
+ //对应SHR:lastDiplomaInfo.getName()
|
|
|
+ String diplomaName = diploma.getString(FormConstant.NAME_KEY);
|
|
|
+ //对应SHR:lastDiplomaInfo.getDescription()
|
|
|
+ BigDecimal diplomaScore = diploma.getBigDecimal(FormConstant.NCKD_SCORE);
|
|
|
+ //获取上一笔职位档案变动记录学历积分
|
|
|
+ //对应SHR:lastDiplomaScore
|
|
|
+ BigDecimal lastDiplomaScore = currentPersonPosFileByPerson.getBigDecimal(PositionStructureConstant.NCKD_DIPLOMASCORE);
|
|
|
+
|
|
|
+
|
|
|
+ //对应SHR:Diploma
|
|
|
+ Long currentDiplomaId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
|
|
|
+ //对应SHR:newDiplomaInfo.getName()
|
|
|
+ String currentDiplomaName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.NAME_KEY));
|
|
|
+ //对应SHR:DiplomaScore
|
|
|
+ //人员最新学历的积分
|
|
|
+ BigDecimal currentDiplomaScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.NAME_KEY));
|
|
|
+ //对应SHR:DiplomaScore
|
|
|
+ BigDecimal resultScore = currentDiplomaScore;
|
|
|
+
|
|
|
+ logger.info("原学历:ID【{}】,名称【{}】,积分【{}】;最新学历:ID【{}】,名称【{}】,积分【{}】", diplomaId,diplomaName,diplomaScore,currentDiplomaId,currentDiplomaName,currentDiplomaScore);
|
|
|
+ if (currentDiplomaId.equals(diplomaId)) {
|
|
|
+ //学历不变,分数保持不变
|
|
|
+ resultScore = lastDiplomaScore;
|
|
|
+ logger.info("");
|
|
|
+ } else {
|
|
|
+ // 分差 = 最新学历配置分 - 上一笔学历的最新配置分
|
|
|
+ // 本次学历分 = 上一笔学历分 + 分差
|
|
|
+ BigDecimal diff = resultScore.subtract(diplomaScore);
|
|
|
+ resultScore = lastDiplomaScore.add(diff);
|
|
|
+ }
|
|
|
+ return resultScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理序列相关信息
|
|
|
+ */
|
|
|
+ private static JobFamilyInfo processJobFamilyInfo(DynamicObject newJobSeq,
|
|
|
+ DynamicObject currentPersonPosFileByPerson) {
|
|
|
+ JobFamilyInfo jobFamilyInfo = new JobFamilyInfo();
|
|
|
+
|
|
|
+ long newJonSeqId = newJobSeq.getLong(FormConstant.ID_KEY);
|
|
|
+ String newJonSeqNumber = newJobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+ // 获取原序列信息
|
|
|
+ DynamicObject currentJobSeq = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR);
|
|
|
+ //如果是管理序列,则按职能序列进行调整
|
|
|
+ //对应SHR:986~993行
|
|
|
+ currentJobSeq = handleJobSeq(currentJobSeq);
|
|
|
+ long currentJonSeqId = currentJobSeq.getLong(FormConstant.ID_KEY);
|
|
|
+ String currentJonSeqNumber = currentJobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+
|
|
|
+
|
|
|
+ // 判断是否为序列转换
|
|
|
+ //对应SHR:isadjusttype_4
|
|
|
+ jobFamilyInfo.isSequenceChange = !currentJonSeqNumber.equals(newJonSeqNumber);
|
|
|
+
|
|
|
+ // 判断是否为跨单位调动
|
|
|
+ jobFamilyInfo.isCrossUnitTransfer = false;
|
|
|
+ // TODO【待修改】 这里需要根据具体业务逻辑判断是否为跨单位调动
|
|
|
+
|
|
|
+ return jobFamilyInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理员工职称和技能等级分数
|
|
|
+ * 根据职位序列类型(技能序列/非技能序列)确定采用职称等级分还是技能等级分
|
|
|
+ * @param newJobSeq 最新职位序列
|
|
|
+ * @param currentPersonPosFileByPerson 最近一次职位档案
|
|
|
+ * @param positionAppointment 人员最信息(最新学历)
|
|
|
+ * @return: nckd.jxccl.hr.psms.business.JobLevelCalculatorService.JobScoreInfo
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/22 09:39
|
|
|
+ */
|
|
|
+ public static JobScoreInfo handleJobScores(DynamicObject newJobSeq,
|
|
|
+ DynamicObject currentPersonPosFileByPerson,DynamicObject positionAppointment) {
|
|
|
+ //对应SHR:hrjobfamilynumber
|
|
|
+ String newJobSeqNumber = newJobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+
|
|
|
+
|
|
|
+ //当前人员最新职称等级
|
|
|
+ Long newPerProTitleId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
|
|
|
+ //对应SHR:selMap.get("zgjbscore")
|
|
|
+ BigDecimal newPerProTitleScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE));
|
|
|
+ //对应SHR:selMap.get("zgjbnumber")
|
|
|
+ String newPerProTitleNumber = positionAppointment.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY));
|
|
|
+
|
|
|
+ //当前人员最新技能等级
|
|
|
+ Long newQuaLevelId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
|
|
|
+ //对应SHR:selMap.get("zyjndjscore")
|
|
|
+ BigDecimal newQuaLevelScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE));
|
|
|
+ //对应SHR:selMap.get("zyjndjnumber")
|
|
|
+ String newQuaLevelNumber = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY));
|
|
|
+
|
|
|
+
|
|
|
+ JobScoreInfo jobScoreInfo = new JobScoreInfo();
|
|
|
+ jobScoreInfo.isEndGainJobGrade = true;
|
|
|
+
|
|
|
+ // 职称等级分
|
|
|
+ jobScoreInfo.perProTitleScore = BigDecimal.ZERO;
|
|
|
+ // 技能等级分
|
|
|
+ jobScoreInfo.quaLevelScore = BigDecimal.ZERO;
|
|
|
+ jobScoreInfo.perProTitleNumber = "";
|
|
|
+ jobScoreInfo.quaLevelNumber = "";
|
|
|
+
|
|
|
+ if (!JobSeqEnum.SKILL.getCode().equals(newJobSeqNumber) && (newPerProTitleScore != null && newPerProTitleScore.compareTo(BigDecimal.ZERO) > 0)) {
|
|
|
+ // 获取当前最新职称等级分
|
|
|
+ jobScoreInfo.perProTitleScore = newPerProTitleScore;
|
|
|
+ jobScoreInfo.perProTitleId = newPerProTitleId;
|
|
|
+ jobScoreInfo.perProTitleNumber = StringUtils.isNotBlank(newPerProTitleNumber) ? newPerProTitleNumber : "";
|
|
|
+ jobScoreInfo.isEndGainJobGrade = false;
|
|
|
+ } else if (JobSeqEnum.SKILL.getCode().equals(newJobSeqNumber)) {
|
|
|
+ if (newQuaLevelScore != null && newQuaLevelScore.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ //获取当前最新技能等级分
|
|
|
+ jobScoreInfo.quaLevelScore = newQuaLevelScore;
|
|
|
+ jobScoreInfo.quaLevelId = newQuaLevelId;
|
|
|
+ jobScoreInfo.quaLevelNumber = StringUtils.isNotBlank(newQuaLevelNumber) ? newQuaLevelNumber: "";
|
|
|
+ jobScoreInfo.isEndGainJobGrade = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ System.out.println("职称等级分:::" + jobScoreInfo.perProTitleScore);
|
|
|
+ System.out.println("技能等级分:::" + jobScoreInfo.quaLevelScore);
|
|
|
+
|
|
|
+ return jobScoreInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算职级ID
|
|
|
+ */
|
|
|
+ private static DynamicObject calculateJobGradeId(DynamicObject newJobSeq, JobScoreInfo jobScoreInfo,JobFamilyInfo jobFamilyInfo,
|
|
|
+ BigDecimal allSumScore, int currentJobLevelIndex, int minusPersonAppraisal, boolean fistPR, JobLevelResult jobLevelResult){
|
|
|
+
|
|
|
+ DynamicObject proTitleLevel = BusinessDataServiceHelper.loadSingle(jobScoreInfo.perProTitleId, FormConstant.HBSS_PROTITLELEVEL);
|
|
|
+ DynamicObject ocpQualLevel = BusinessDataServiceHelper.loadSingle(jobScoreInfo.quaLevelId, FormConstant.HBSS_OCPQUALLEVEL);
|
|
|
+ JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(newJobSeq.getString(FormConstant.NUMBER_KEY));
|
|
|
+ DynamicObject jobLevel = null;
|
|
|
+ if (jobScoreInfo.isEndGainJobGrade) {
|
|
|
+ System.out.println("缺少聘任,技能序列必需聘任技能等级,其它序列必需聘任职称等级!");
|
|
|
+ //对应SHR:JobGradeid = personpositionfileUtils.endGainJobGrade(ctx, allsumScore, hrjobfamilynumber);
|
|
|
+ jobLevel = getLowestJobLevel(newJobSeq);
|
|
|
+ //对应SHR:selMap.put("adjusttype", "7");
|
|
|
+ jobLevelResult.adjustType = "7";
|
|
|
+ } else if (jobFamilyInfo.isSequenceChange) {
|
|
|
+ //对应SHR:JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, 0);
|
|
|
+ jobLevel = PositionStructureHelper.calculateLevel(jobSeqEnum, allSumScore, proTitleLevel, ocpQualLevel, null, 0, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ //序列转换
|
|
|
+ jobLevelResult.adjustType = "4";
|
|
|
+ } else if (jobFamilyInfo.isCrossUnitTransfer) {
|
|
|
+ //对应SHR:JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, 0);
|
|
|
+ jobLevel = PositionStructureHelper.calculateLevel(jobSeqEnum, allSumScore, proTitleLevel, ocpQualLevel, null, 0, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ //保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ } else if (fistPR) {
|
|
|
+ //对应SHR:JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, minuspersonappraisal);
|
|
|
+ jobLevel = PositionStructureHelper.calculateLevel(jobSeqEnum, allSumScore, proTitleLevel, ocpQualLevel, null, minusPersonAppraisal, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ } else {
|
|
|
+ //对应SHR:JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, minuspersonappraisal < 0 ? minuspersonappraisal : 0);
|
|
|
+ jobLevel = PositionStructureHelper.calculateLevel(jobSeqEnum, allSumScore, proTitleLevel, ocpQualLevel, null, minusPersonAppraisal < 0 ? minusPersonAppraisal : 0, Boolean.FALSE,Boolean.FALSE);
|
|
|
+ }
|
|
|
+ int newJobGradeIndex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
|
|
|
+ System.out.println("newjobgradeindex:::" + newJobGradeIndex);
|
|
|
+
|
|
|
+ if (minusPersonAppraisal == -1 && currentJobLevelIndex == newJobGradeIndex) {
|
|
|
+ System.out.println("总分不足");
|
|
|
+ jobLevelResult.adjustType = "6";
|
|
|
+ }
|
|
|
+
|
|
|
+ return jobLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取排名信息
|
|
|
+ * @param personId 人员ID
|
|
|
+ * @param personName 人员名称
|
|
|
+ * @param date 日期
|
|
|
+ * @return: nckd.jxccl.hr.psms.business.JobLevelCalculatorService.RankingResult
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/22 14:19
|
|
|
+ */
|
|
|
+ public static RankingResult getRankingInfo(Long personId, String personName,Date date) {
|
|
|
+ LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(date), 1);
|
|
|
+ //取上年度绩效排名
|
|
|
+ QFilter groupFilter = new QFilter(PerfRankMgmtConstant.NCKD_THEYEAR,QCP.equals,lastYearDateTime.getYear());
|
|
|
+ groupFilter.and(PerfRankMgmtConstant.BILL_STATUS_KEY,QCP.equals, StatusEnum.C.toString());
|
|
|
+ groupFilter.and(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_PERSON),QCP.equals, personId);
|
|
|
+ groupFilter.and(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING),QCP.equals,EnableEnum.YES.getCode());
|
|
|
+ //获取该员工上年度所在的排名分组
|
|
|
+ DynamicObject[] groupArray = BusinessDataServiceHelper.load(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, FormConstant.ID_KEY+","+FormConstant.NAME_KEY,
|
|
|
+ new QFilter[]{groupFilter},
|
|
|
+ FormConstant.CREATE_TIME_KEY + " desc " + FormConstant.AUDIT_DATE_KEY + " desc " + FormConstant.MODIFY_TIME_KEY + " desc");
|
|
|
+ //避免一个员工存在多个排名分组中,只取最新排名分组
|
|
|
+ Long groupId = null;
|
|
|
+ String groupName = null;
|
|
|
+ if(groupArray != null && groupArray.length > 0){
|
|
|
+ groupId = groupArray[0].getLong(FormConstant.ID_KEY);
|
|
|
+ groupName = groupArray[0].getString(FormConstant.NAME_KEY);
|
|
|
+ }
|
|
|
+ if(groupId != null && groupId > 0){
|
|
|
+ QFilter perfRankMgmtFilter = new QFilter(FormConstant.ID_KEY,QCP.equals,groupId);
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ .add(FormConstant.ID_KEY)
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_PERSON,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_TOPRANK))
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_POSTALLOWANCE))
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ALLOWANCERANK))
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_APPRAISALRESULT))
|
|
|
+ .add(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING));
|
|
|
+ //对应SHR:allowancerankpercentSQL()方法
|
|
|
+ try(DataSet dataSet = QueryServiceHelper.queryDataSet(JobLevelCalculatorService.class.getName() + "#processRankingInfo", PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID,
|
|
|
+ selectFields.toString(), new QFilter[]{perfRankMgmtFilter}, PerfRankMgmtConstant.NCKD_TOPRANK + "," + PerfRankMgmtConstant.NCKD_ALLOWANCERANK)){
|
|
|
+ //获取R排名总人数
|
|
|
+ //对应SHR:countallowancerank
|
|
|
+ int countR = dataSet.filter(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING) +" = true and "+String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_POSTALLOWANCE)+" = true")
|
|
|
+ .count(FormConstant.ID_KEY, false);
|
|
|
+ //获取总排名总人数
|
|
|
+ int count = dataSet.filter(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING) +" = true")
|
|
|
+ .count(FormConstant.ID_KEY, false);
|
|
|
+ //获取排名
|
|
|
+ DataSet topRankDataSet = dataSet.select(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_TOPRANK))
|
|
|
+ .filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY) + " = " + personId);
|
|
|
+ //对应SHR:topRank
|
|
|
+ Integer topRank = null;
|
|
|
+ if(topRankDataSet.hasNext()){
|
|
|
+ Row row = topRankDataSet.next();
|
|
|
+ topRank = row.getInteger(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_TOPRANK));
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取R排名
|
|
|
+ DataSet allowanceRankDataSet = dataSet.select(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ALLOWANCERANK))
|
|
|
+ .filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY) + " = " + personId);
|
|
|
+ //对应SHR:allowancerank
|
|
|
+ Integer allowanceRank = null;
|
|
|
+ if(allowanceRankDataSet.hasNext()){
|
|
|
+ Row row = allowanceRankDataSet.next();
|
|
|
+ allowanceRank = row.getInteger(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ALLOWANCERANK));
|
|
|
+ }
|
|
|
+ //计算公式:R排名百分比 = (个人R排名 / 分组总人数) × 100%
|
|
|
+ //对应SHR:toprankpercent
|
|
|
+ double topRankPercent = (double) topRank / count * 100;
|
|
|
+ if (topRankPercent < 0 || topRankPercent > 100) {
|
|
|
+ throw new ValidationException(StrFormatter.format("全排名百分比必须在0-100之间,请检查排名分组配置是否有误。人员【{}】-排名分组【{}】-R排名总人数【{}】-排名【{}】",personName,groupName,count,topRank));
|
|
|
+ }
|
|
|
+ //对应SHR:allowancerankpercent
|
|
|
+ double allowanceRankPercent = (double) allowanceRank / countR * 100;
|
|
|
+ if (allowanceRankPercent < 0 || allowanceRankPercent > 100) {
|
|
|
+ throw new ValidationException(StrFormatter.format("R排名百分比必须在0-100之间,请检查排名分组配置是否有误。人员【{}】-排名分组【{}】-R排名总人数【{}】-排名【{}】",personName,groupName,countR,allowanceRank));
|
|
|
+ }
|
|
|
+
|
|
|
+ RankingResult rankingResult = new RankingResult();
|
|
|
+ //获取R排名最后名次
|
|
|
+ DataSet maxAllowanceRank = dataSet.filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ISRANKING) + " = true and " + String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_POSTALLOWANCE) + " = true")
|
|
|
+ .groupBy()
|
|
|
+ .max(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ALLOWANCERANK), "maxAllowanceRank").finish();
|
|
|
+ if(maxAllowanceRank.hasNext()){
|
|
|
+ Row row = maxAllowanceRank.next();
|
|
|
+ rankingResult.maxAllowanceRank = row.getInteger("maxAllowanceRank");
|
|
|
+ }
|
|
|
+ rankingResult.topRank = topRank;
|
|
|
+ rankingResult.allowanceRank = allowanceRank;
|
|
|
+ rankingResult.count = count > 0 ? count : null;
|
|
|
+ rankingResult.countR = countR > 0 ? countR : null;
|
|
|
+ rankingResult.topRankPercent = topRankPercent;
|
|
|
+ rankingResult.allowanceRankPercent = allowanceRankPercent;
|
|
|
+ rankingResult.groupName = groupName;
|
|
|
+ return rankingResult;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ logger.warn("员工不存在排名分组");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理序列转换
|
|
|
+ * 对应SHR:PV.getnewjobgradeindexByrank
|
|
|
+ */
|
|
|
+ private static void handleSequenceChange(int jobLevelIndex,int currentJobLevelIndex,RankingResult rankingResult,Map<Integer, DynamicObject> jobLevelMap,JobLevelResult jobLevelResult,DynamicObject jobSeq){
|
|
|
+ System.out.println("序列切换,新职级号不能大于原序列职级序号");
|
|
|
+ if (jobLevelIndex > currentJobLevelIndex) {
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ }
|
|
|
+ jobLevelResult.jobLevel = jobLevelMap.get(jobLevelIndex);
|
|
|
+ // 序列转换
|
|
|
+ jobLevelResult.adjustType = "4";
|
|
|
+ getnewjobgradeindexByrank(jobLevelIndex,currentJobLevelIndex,rankingResult,jobSeq);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理跨单位调动
|
|
|
+ */
|
|
|
+ private static void handleCrossUnitTransfer(int jobLevelIndex,int currentJobLevelIndex,RankingResult rankingResult,Map<Integer, DynamicObject> jobLevelMap,JobLevelResult jobLevelResult,DynamicObject jobSeq){
|
|
|
+ System.out.println("跨单位调动且未跨序列,平移处理");
|
|
|
+ if (jobLevelIndex > currentJobLevelIndex) {
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ }
|
|
|
+ jobLevelResult.jobLevel = jobLevelMap.get(jobLevelIndex);
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ getnewjobgradeindexByrank(jobLevelIndex,currentJobLevelIndex,rankingResult,jobSeq);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对应SHR:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfilecreateViewListHandler#getnewjobgradeindexByrank
|
|
|
+ * @param jobLevelIndex
|
|
|
+ * @param currentJobLevelIndex
|
|
|
+ * @param rankingResult
|
|
|
+ * @param jobSeq
|
|
|
+ * @return: int
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/09/22 19:47
|
|
|
+ */
|
|
|
+ public static int getnewjobgradeindexByrank(int jobLevelIndex,int currentJobLevelIndex,RankingResult rankingResult,DynamicObject jobSeq){
|
|
|
+
|
|
|
+ JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeq.getString(FormConstant.NUMBER_KEY));
|
|
|
+ if(rankingResult == null || rankingResult.countR == null || rankingResult.allowanceRank == null){
|
|
|
+ rankingResult.allowanceRankMark = "无";
|
|
|
+ rankingResult.allowanceRankSel = "无";
|
|
|
+ return jobLevelIndex;
|
|
|
+ }
|
|
|
+ rankingResult.allowanceRankMark = rankingResult.allowanceRank +"/" +rankingResult.countR;
|
|
|
+ Integer allowanceRank = rankingResult.allowanceRank;
|
|
|
+ Integer countR = rankingResult.countR;
|
|
|
+ Integer maxAllowanceRank = rankingResult.maxAllowanceRank;
|
|
|
+ if(rankingResult.countR < 10 && maxAllowanceRank.equals(rankingResult.allowanceRank) && rankingResult.topRankPercent >= 70){
|
|
|
+ jobLevelIndex--;
|
|
|
+ }else{
|
|
|
+ //技术或职能序列
|
|
|
+ if (JobSeqEnum.TECHNICALS.getCode().equals(jobSeqEnum.getCode()) || JobSeqEnum.FUNCTIONAL.getCode().equals(jobSeqEnum.getCode())) {
|
|
|
+
|
|
|
+ if (currentJobLevelIndex > 9) {
|
|
|
+ // 职级>9:前60%升职,后5%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(60,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前60%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(5,countR) && SelMinallowancerank(5,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后5%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ } else if (currentJobLevelIndex > 5 && currentJobLevelIndex <= 9) {
|
|
|
+ // 职级5-9:前70%升职,后5%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(70,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前70%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(5,countR) && SelMinallowancerank(5,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后5%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ }else if (currentJobLevelIndex <= 5) {
|
|
|
+ // 职级≤5:前80%升职,后3%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(80,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前80%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(3,countR) && SelMinallowancerank(3,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后3%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else if(JobSeqEnum.SKILL.getCode().equals(jobSeqEnum.getCode())){
|
|
|
+ // 技能序列:
|
|
|
+ if (currentJobLevelIndex > 8) {
|
|
|
+ //职级>8:前60%升职,后5%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(60,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前60%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(5,countR) && SelMinallowancerank(5,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后5%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ }else if (currentJobLevelIndex > 4 && currentJobLevelIndex <= 8) {
|
|
|
+ // 职级4-8:前70%升职,后5%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(70,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前70%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(5,countR) && SelMinallowancerank(5,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后5%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ } else if (currentJobLevelIndex <= 4) {
|
|
|
+ // 职级≤4:前80%升职,后3%降职
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(80,countR)) {
|
|
|
+ // selMap.put("allowanceranksel", "前80%");
|
|
|
+ jobLevelIndex++;
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(3,countR) && SelMinallowancerank(3,countR) > 0) {
|
|
|
+ // selMap.put("allowanceranksel", "后3%");
|
|
|
+ jobLevelIndex--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex > 1) {
|
|
|
+ jobLevelIndex = currentJobLevelIndex + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (allowanceRank <= SelMAXallowancerank(60, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "前60%";
|
|
|
+ }
|
|
|
+ else if (allowanceRank <= SelMAXallowancerank(70, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "前60-70%";
|
|
|
+ }
|
|
|
+ else if (allowanceRank <= SelMAXallowancerank(80, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "前70-80%";
|
|
|
+ }
|
|
|
+ else if (allowanceRank <= SelMAXallowancerank(95, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "前80-95%";
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(3, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "后3%";
|
|
|
+ }
|
|
|
+ else if (allowanceRank >= SelMinallowancerank(5, countR)) {
|
|
|
+ rankingResult.allowanceRankSel = "后5%";
|
|
|
+ }
|
|
|
+ return jobLevelIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int SelMAXallowancerank(int percent, int countallowancerank) {
|
|
|
+ int MAXallowancerank = 0;
|
|
|
+ double MAXallowancerankDouble = Double.parseDouble(countallowancerank + "") / 100.0 * Double.parseDouble(percent + "");
|
|
|
+ MAXallowancerank = (int) Math.round(MAXallowancerankDouble);
|
|
|
+ return MAXallowancerank;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static int SelMinallowancerank(int percent, int countallowancerank) {
|
|
|
+ int Minallowancerank = 0;
|
|
|
+ double MinallowancerankDouble = Double.parseDouble(countallowancerank + "") / 100.0 * Double.parseDouble(percent + "");
|
|
|
+ Minallowancerank = countallowancerank - ((int) Math.round(MinallowancerankDouble));
|
|
|
+ return Minallowancerank;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取最近一次聘任的职级索引
|
|
|
+ */
|
|
|
+ private static int getLastAppointmentJobGradeIndex(Long personId, int currentJobLevelIndex,int jobLevelIndex){
|
|
|
+
|
|
|
+
|
|
|
+ QFilter rankScore = new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.large_than, 0));
|
|
|
+ QFilter jobScore = new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.large_than, 0));
|
|
|
+ QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
|
|
|
+ .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
|
|
|
+ .and(rankScore.or(jobScore))
|
|
|
+ .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ .add(PositionStructureConstant.NCKD_FIRSTRANK)
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NAME_KEY))
|
|
|
+
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.JOBLEVELSEQ));
|
|
|
+ //对应SHR:PRpersonpositionfileCollection
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.PERSONPOSFILE_ENTITYID, selectFields.toString(), new QFilter[]{filer}, "nckd_executeyear desc,nckd_begindate desc");
|
|
|
+ if(!query.isEmpty()){
|
|
|
+ DynamicObject personPosFile = query.get(0);
|
|
|
+ int PR_lastjobgradeindex = personPosFile.getInt(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ return PR_lastjobgradeindex;
|
|
|
+ }
|
|
|
+ return currentJobLevelIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据考核结果使用情况处理职级计算
|
|
|
+ */
|
|
|
+ private static int handleAppraisalUsage(DynamicObject jobSeq, RankingResult rankingResult, int jobLevelIndex,
|
|
|
+ int PR_lastjobgradeindex, int currentJobLevelIndex, int minuspersonappraisal, boolean fistPR, boolean usedAppraisalResult, JobLevelResult jobLevelResult){
|
|
|
+ JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeq.getString(FormConstant.NUMBER_KEY));
|
|
|
+ if (!usedAppraisalResult) {
|
|
|
+ if (minuspersonappraisal == 0) {
|
|
|
+ if (!fistPR) {
|
|
|
+ // 考核结果为保级 所以相 等
|
|
|
+ jobLevelIndex = PR_lastjobgradeindex;
|
|
|
+ }
|
|
|
+ if (fistPR && ((rankingResult.countR == null || rankingResult.countR == 0) || (rankingResult.allowanceRank == null || rankingResult.allowanceRank == 0))) {
|
|
|
+ System.out.println("首次聘任而且无R排名,保持分数算出来的职级");
|
|
|
+ } else {
|
|
|
+ System.out.println("应用R排名结果");
|
|
|
+ jobLevelIndex = getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResult, jobSeq);
|
|
|
+ }
|
|
|
+ System.out.println("newjobgradeindex:::" + jobLevelIndex);
|
|
|
+ System.out.println("PR_lastjobgradeindex:::" + PR_lastjobgradeindex);
|
|
|
+ if (currentJobLevelIndex > 1 && jobLevelIndex - PR_lastjobgradeindex == 1) {
|
|
|
+ // R排名升级
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ System.out.println("R排名升级");
|
|
|
+ } else if (jobLevelIndex == PR_lastjobgradeindex) {
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ System.out.println("R排名保级");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResult, jobSeq);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResult, jobSeq);
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ System.out.println("保级");
|
|
|
+ }
|
|
|
+ return jobLevelIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理聘任相关限制
|
|
|
+ */
|
|
|
+ private static int handleAppointmentRestrictions(long personId, boolean firstPR,boolean usedAppraisalResult,
|
|
|
+ DynamicObject jobSeq,long jobSeqId,long currentJobSeqId,String currentJobSeqNumber, JobScoreInfo jobScoreInfo,
|
|
|
+ int currentJobLevelIndex,int jobLevelIndex, BigDecimal allsumScore, JobLevelResult jobLevelResult,int minusPersonAppraisal,DynamicObject currentPersonPosFile) {
|
|
|
+ /*PersonpositionfileCollection PRpersonpositionfileCollection = PersonpositionfileFactory.getLocalInstance(ctx)
|
|
|
+ .getPersonpositionfileCollection("select *,Appraisalresult.*,JobGrade.* where person='" + personid +
|
|
|
+ "' and (disable is null or disable<>1) and ((jobstatusScore is not null and jobstatusScore >0) or (RankScore is not null and RankScore >0)) " +
|
|
|
+ "and Appraisalresult.number<>'06' order by beginDate desc, createTime desc");*/
|
|
|
+ QFilter rankScore = new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.large_than, 0));
|
|
|
+ QFilter jobScore = new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.large_than, 0));
|
|
|
+ QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
|
|
|
+ .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
|
|
|
+ .and(rankScore.or(jobScore))
|
|
|
+ .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));
|
|
|
+ StringJoiner selectFields = new StringJoiner(",")
|
|
|
+ .add(PositionStructureConstant.NCKD_FIRSTRANK)
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.ID_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.NAME_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.NUMBER_KEY))
|
|
|
+ .add(String.join(".",PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.JOBLEVELSEQ));
|
|
|
+ //对应SHR:PRpersonpositionfileCollection
|
|
|
+ DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.PERSONPOSFILE_ENTITYID, selectFields.toString(), new QFilter[]{filer}, "nckd_executeyear desc,nckd_begindate desc");
|
|
|
+ if (firstPR) {
|
|
|
+ System.out.println("前一笔为初定且考核结果为无,按首次聘任处理,所以放开限制");
|
|
|
+ // 首次聘任
|
|
|
+ jobLevelResult.adjustType = "3";
|
|
|
+ }else if (!query.isEmpty()){
|
|
|
+ long hrjobfamilyid = jobSeqId;
|
|
|
+ long oldHRJobFamilyid = currentJobSeqId;
|
|
|
+ String hrjobfamilynumber = jobSeq.getString(FormConstant.NUMBER_KEY);
|
|
|
+ String oldHRJobFamilynumber = currentJobSeqNumber;
|
|
|
+ BigDecimal currentRankScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_RANKSCORE);
|
|
|
+ BigDecimal currentJobStatusScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
|
|
|
+ if (!(currentRankScore.compareTo(BigDecimal.ZERO) > 0 || currentJobStatusScore.compareTo(BigDecimal.ZERO) > 0)) {
|
|
|
+ System.out.println("上条记录未聘任,取最近一条聘任");
|
|
|
+ /* PersonpositionfileInfo prpersonpositionfileInfo = PRpersonpositionfileCollection.get(0);
|
|
|
+ CoreBaseInfo JobGrade = (CoreBaseInfo) prpersonpositionfileInfo.get("JobGrade");*/
|
|
|
+ //对应SHR:prpersonpositionfileInfo
|
|
|
+ DynamicObject personPositionFile = query.get(0);
|
|
|
+ //对应SHR:JobGrade.getInt("index")
|
|
|
+ int index = personPositionFile.getInt(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ currentJobLevelIndex = index;
|
|
|
+ // 2021-09-15 补充判断 如果之前有过年度调整,考核结果是否已被应用
|
|
|
+ if (!usedAppraisalResult) {
|
|
|
+ //对应SHR:appraisalresultnumber
|
|
|
+ String appraisalResultNumber = personPositionFile.getString(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY));
|
|
|
+ if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(appraisalResultNumber)) {
|
|
|
+ currentJobLevelIndex -= 1;
|
|
|
+ // 降级
|
|
|
+ jobLevelResult.adjustType = "0";
|
|
|
+ } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(appraisalResultNumber)) {
|
|
|
+ currentJobLevelIndex -= 2;
|
|
|
+ // 降级
|
|
|
+ jobLevelResult.adjustType = "0";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ System.out.println("上条记录已聘任");
|
|
|
+ }
|
|
|
+
|
|
|
+ //这里为什么用ID判断又用number判断。因为ID至始至终都没有变过(如管理序列会被转换为职能序列),number可能会被转换。
|
|
|
+ // 换了序列
|
|
|
+ if (hrjobfamilyid != oldHRJobFamilyid) {
|
|
|
+ System.out.println("检查到有序列切换");
|
|
|
+ //对应SHR:if (hrjobfamilynumber.equals(oldHRJobFamilynumber)) {;1214行
|
|
|
+ if (hrjobfamilynumber.equals(oldHRJobFamilynumber)) {
|
|
|
+ System.out.println("管理序列与职能序列之间切换,不做序列切换限制---pass");
|
|
|
+ if (!usedAppraisalResult && jobLevelIndex >= currentJobLevelIndex + 1) {
|
|
|
+ // 升级
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ jobLevelIndex = currentJobLevelIndex + 1;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (StringUtils.isNotBlank(jobLevelResult.adjustType) && "6".equals(jobLevelResult.adjustType)) {
|
|
|
+ System.out.println("前面判断需要升级但总分未达到");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ }
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 序列转换
|
|
|
+ jobLevelResult.adjustType = "4";
|
|
|
+ System.out.println("序列切换,新职级号不能大于原序列职级序号");
|
|
|
+ if (jobLevelIndex > currentJobLevelIndex) {
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (usedAppraisalResult) {
|
|
|
+ System.out.println("非年度首次调整,且考核结果做过升限级,所以以原职级做为限定范围");
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ } else {
|
|
|
+ if (minusPersonAppraisal > 0 && jobLevelIndex >= currentJobLevelIndex) {
|
|
|
+ System.out.println("考核结果未使用且为降级,动态调整时按保级处理,在年度调整处理");
|
|
|
+ // 保级
|
|
|
+ jobLevelResult.adjustType = "1";
|
|
|
+ jobLevelIndex = currentJobLevelIndex;
|
|
|
+ }
|
|
|
+ else if (jobLevelIndex - currentJobLevelIndex == 1) {
|
|
|
+ // 升级
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ // 首次聘任
|
|
|
+ jobLevelResult.adjustType = "3";
|
|
|
+ System.out.println("首次聘任,所以放开限制");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确定是否超出总分最高任命职级
|
|
|
+ */
|
|
|
+ //对应SHR:String personMaxjobgradeindexstr = personpositionfileUtils.MaxGainJobGrade(ctx, allsumScore, jobFamilyInfo.processedJobFamilyNumber);
|
|
|
+ DynamicObject maxJobLevel = getMaxJobLevel(jobSeq, allsumScore);
|
|
|
+ //对应SHR:personMaxjobgradeindex
|
|
|
+ int maxJobLevelIndex = maxJobLevel.getInt(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ // 超出总分能任命最高职职级则该最高职级就是要任命的职级
|
|
|
+ if (jobLevelIndex > maxJobLevelIndex) {
|
|
|
+ // 总分不足
|
|
|
+ jobLevelResult.adjustType = "6";
|
|
|
+ jobLevelIndex = maxJobLevelIndex;
|
|
|
+ if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex == 1) {
|
|
|
+ // R排名升级
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确定是否超出该职称等级或技能等级最高任命职级
|
|
|
+ */
|
|
|
+ /*String Maxjobgradeindexstr = personpositionfileUtils.getMaxJobLevel(ctx, jobFamilyInfo.processedJobFamilyNumber,
|
|
|
+ jobScoreInfo.zgjbnumber, jobScoreInfo.zyjndjnumber);*/
|
|
|
+ DynamicObject maxJobLevel1 = getMaxJobLevel(jobSeq, jobScoreInfo.perProTitleNumber, jobScoreInfo.quaLevelNumber);
|
|
|
+ int Maxjobgradeindex = maxJobLevel1.getInt(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
|
|
|
+ // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
|
|
|
+ if (jobLevelIndex > Maxjobgradeindex) {
|
|
|
+ if (StringUtils.isBlank(jobLevelResult.adjustType)) {
|
|
|
+ // 聘任下调
|
|
|
+ jobLevelResult.adjustType = "5";
|
|
|
+ }
|
|
|
+ jobLevelIndex = Maxjobgradeindex;
|
|
|
+ if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex == 1) {
|
|
|
+ // R排名升级
|
|
|
+ jobLevelResult.adjustType = "2";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查聘任分是否减少
|
|
|
+ //对应SHR:personpositionfileInfo.getInt("jobstatusScore")
|
|
|
+ BigDecimal currentJobStatusScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
|
|
|
+ //对应SHR:personpositionfileInfo.getInt("RankScore")
|
|
|
+ BigDecimal currentRankScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_RANKSCORE);
|
|
|
+ BigDecimal currentSum = currentJobStatusScore.add(currentRankScore);
|
|
|
+ BigDecimal newSum = jobScoreInfo.perProTitleScore.add(jobScoreInfo.quaLevelScore);
|
|
|
+ System.out.println("上一 笔职称等级分+技能等级分::" + currentSum);
|
|
|
+ System.out.println("当前职称等级分+技能等级分::" + (jobScoreInfo.perProTitleScore.add(jobScoreInfo.quaLevelScore)));
|
|
|
+ if (currentSum.compareTo(newSum) > 0) {
|
|
|
+ //聘任下调
|
|
|
+ jobLevelResult.adjustType = "5";
|
|
|
+ }
|
|
|
+ //currentJobLevelIndex有变更,但是后续没有使用所以就不返回了
|
|
|
+ return jobLevelIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 最终确定职级ID
|
|
|
+ */
|
|
|
+ private static DynamicObject determineFinalJobGradeId(int jobLevelIndex,
|
|
|
+ JobScoreInfo jobScoreInfo, Map<Integer, DynamicObject> jobLevelMap,
|
|
|
+ DynamicObject jobSeq, JobLevelResult jobLevelResult,
|
|
|
+ String appraisalResultNumber){
|
|
|
+ DynamicObject jobLevel = null;
|
|
|
+
|
|
|
+ if (jobScoreInfo.isEndGainJobGrade) {
|
|
|
+ System.out.println("缺少聘任,技能序列必需聘任技能等级,其它序列必需聘任职称等级!");
|
|
|
+ jobLevel = getLowestJobLevel(jobSeq);
|
|
|
+ jobLevelResult.adjustType = "7";
|
|
|
+ } else if (jobLevelMap.get(jobLevelIndex) != null) {
|
|
|
+ jobLevel = jobLevelMap.get(jobLevelIndex);
|
|
|
+ BigDecimal newSum = jobScoreInfo.perProTitleScore.add(jobScoreInfo.quaLevelScore);
|
|
|
+ if (newSum.compareTo(BigDecimal.ZERO) <= 0 && jobLevelIndex == 1) {
|
|
|
+ jobLevelResult.adjustType = "7";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ jobLevel = getLowestJobLevel(jobSeq);
|
|
|
+ jobLevelResult.adjustType = "7";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(appraisalResultNumber) || AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber)) {
|
|
|
+ jobLevel = getLowestJobLevel(jobSeq);
|
|
|
+ jobLevelResult.adjustType = "8";
|
|
|
+ }
|
|
|
+ jobLevelResult.jobLevel = jobLevel;
|
|
|
+ return jobLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 辅助类定义
|
|
|
+ private static class JobFamilyInfo {
|
|
|
+ /** 是否为序列转换 */
|
|
|
+ //对应SHR:isadjusttype_4
|
|
|
+ boolean isSequenceChange;
|
|
|
+ /** 是否为跨单位调动*/
|
|
|
+ //对应SHR:isadjusttype_1
|
|
|
+ boolean isCrossUnitTransfer;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class JobScoreInfo {
|
|
|
+ /** 职称等级分 */
|
|
|
+ public BigDecimal perProTitleScore;
|
|
|
+ /** 技能等级分 */
|
|
|
+ public BigDecimal quaLevelScore;
|
|
|
+ /** 职称级别编码 */
|
|
|
+ Long perProTitleId;
|
|
|
+ /** 技能等级编码 */
|
|
|
+ Long quaLevelId;
|
|
|
+ /** 职称级别编码 */
|
|
|
+ String perProTitleNumber;
|
|
|
+ /** 技能等级编码 */
|
|
|
+ String quaLevelNumber;
|
|
|
+ /** 是否缺少聘任 */
|
|
|
+ boolean isEndGainJobGrade;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class RankingResult {
|
|
|
+ /** 排名 */
|
|
|
+ Integer topRank;
|
|
|
+ /** R排名 */
|
|
|
+ //对应SHR:allowancerank
|
|
|
+ public Integer allowanceRank;
|
|
|
+ /** 最后名次 */
|
|
|
+ //对应SHR:maxallowancerank
|
|
|
+ public Integer maxAllowanceRank;
|
|
|
+ /** 全排名百分比 */
|
|
|
+ Double topRankPercent;
|
|
|
+ /** R排名百分比 */
|
|
|
+ Double allowanceRankPercent;
|
|
|
+ /** 全排名人数 */
|
|
|
+ Integer count;
|
|
|
+ /** R排名人数 */
|
|
|
+ //对应SHR:countallowancerank
|
|
|
+ public Integer countR;
|
|
|
+ //排名分组名称
|
|
|
+ /** R排名人数 */
|
|
|
+ String groupName;
|
|
|
+ /** R排名名次/R排名总人数 */
|
|
|
+ //对应SHR:allowancerankmark
|
|
|
+ String allowanceRankMark;
|
|
|
+ /** R排名百分比 */
|
|
|
+ //对应SHR:allowanceranksel
|
|
|
+ String allowanceRankSel;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class JobLevelResult {
|
|
|
+ public String adjustType;
|
|
|
+ public String adjustMsg;
|
|
|
+ public DynamicObject jobLevel;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|