Prechádzať zdrojové kódy

feat(ijp): 添加内部人力资源市场三类人员记录功能

- 新增TalentTypeEnum枚举类定义人员类别(A优秀员工/B富余员工/C绩效末等员工)
- 在DateUtil工具类中添加addYears方法用于日期年份计算
- 在IntJobPostConstant常量类中添加市场管理相关字段常量定义
- 实现IntJobPostAuditedOpPlugin插件完成三类人员记录的审批通过逻辑
- 添加人员验证器确保只有在职员工才能进入人力资源市场
- 实现历史记录处理包括备份旧值和设置退出市场状态
- 添加轨迹管理功能记录员工状态变化过程
- 优化服务月数计算逻辑修正ChronoUnit.MONTHS计算方式
- 修改薪酬标准表单插件支持父级年度薪酬分类数据查询
wyc 4 dní pred
rodič
commit
b572937b8e

+ 60 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/enums/ijp/TalentTypeEnum.java

@@ -0,0 +1,60 @@
+package nckd.jxccl.base.common.enums.ijp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* 【内部人力资源市场】-人员类别
+* @author W.Y.C
+* @date 2025/7/14 14:25
+* @version 1.0
+*/
+public enum TalentTypeEnum {
+
+    /*优秀员工	A
+    富余员工	B
+    绩效末等员工	C*/
+    /*优秀员工*/
+    EXCELLENT_EMPLOYEE("A", "优秀员工"),
+    /*富余员工*/
+    RICH_EMPLOYEE("B", "富余员工"),
+    /*绩效末等员工*/
+    PERFORMANCE_LAST_EMPLOYEE("C", "绩效末等员工");
+    private final String code;
+    private final String name;
+
+    TalentTypeEnum(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    private static final Map<String, TalentTypeEnum> CODE_MAP = new HashMap<>();
+
+    static {
+        for (TalentTypeEnum value : TalentTypeEnum.values()) {
+            CODE_MAP.put(value.code, value);
+        }
+    }
+
+    /**
+     * 根据编码获取枚举
+     * @param code 编码
+     * @return: nckd.jxccl.base.common.enums.AppraisalResultEnum
+     * @author W.Y.C
+     * @date: 2025/09/11 14:39
+     */
+    public static TalentTypeEnum getByCode(String code) {
+        if (code == null) {
+            return null;
+        }
+        return CODE_MAP.get(code);
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 11 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/DateUtil.java

@@ -156,6 +156,17 @@ public class DateUtil {
         return dateTime.plusYears(years);
     }
 
+    /**
+     * 加年
+     *
+     * @param date 原始日期时间
+     * @param years    要加的年数(可为负数)
+     * @return 计算后的日期时间
+     */
+    public static Date addYears(Date date, int years) {
+        return toDate(addYears(toLocalDateTime(date), years));
+    }
+
     /**
      * 减年
      *

+ 58 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/common/IntJobPostConstant.java

@@ -32,4 +32,62 @@ public class IntJobPostConstant extends FormConstant {
     public static final String NCKD_ISJOG = "nckd_isjog";
     /** 获取员工 */
     public static final String FETCHEMP_OP = "fetchemp";
+
+
+
+
+    /** 市场管理-实体标识 */
+    public static final String INTJOBPOSTRECORD_ENTITYID = "nckd_intjobpostrecord";
+    /** 员工状态 */
+    public static final String NCKD_STATE = "nckd_state";
+    /** 进入市场时间 */
+    public static final String NCKD_BEGINDATE = "nckd_begindate";
+    /** 转待岗培训时间 */
+    public static final String NCKD_TOTWODATE = "nckd_totwodate";
+    /** 转待岗时间 */
+    public static final String NCKD_TOTHREEDATE = "nckd_tothreedate";
+    /** 退出市场时间 */
+    public static final String NCKD_TOFOURDATE = "nckd_tofourdate";
+    /** 预计退出市场时间 */
+    public static final String NCKD_PREDICTTOFOURDATE = "nckd_predicttofourdate";
+    /** 三类人员记录分录 */
+    public static final String NCKD_INTJOBPOSTENTRY = "nckd_intjobpostentry";
+    /** 原退出市场时间 */
+    public static final String NCKD_OLDTOFOURDATE = "nckd_oldtofourdate";
+    /** 退出市场原因 */
+    public static final String NCKD_BACKUP = "nckd_backup";
+    /** 员工状态 */
+    public static final String NCKD_OLDSTATE = "nckd_oldstate";
+    /** 操作来源 */
+    public static final String NCKD_HOWFROM = "nckd_howfrom";
+    /** 竞聘时间 */
+    public static final String NCKD_COMPDATE = "nckd_compdate";
+
+
+    /** 内部人力资源市场变动记录-实体标识 */
+    public static final String INTJOBPOSTLOCUS_ENTITYID = "nckd_intjobpostlocus";
+    /** 开始时间 */
+    public static final String NCKD_STARTDATE = "nckd_startdate";
+    /** 结束时间 */
+    public static final String NCKD_ENDDATE = "nckd_enddate";
+    /** 内部人力资源市场记录 */
+    public static final String NCKD_INTJOBPOSTRECORD = "nckd_intjobpostrecord";
+    /** 原结束时间 */
+    public static final String NCKD_OLDENDDAY = "nckd_oldendday";
+    /** 历史退出市场时间 */
+    public static final String NCKD_HISTOFOURDATE = "nckd_histofourdate";
+    /** 历史转待岗时间 */
+    public static final String NCKD_HISTOTHREEDATE = "nckd_histothreedate";
+    /** 历史转待岗培训时间 */
+    public static final String NCKD_HISTOTWODATE = "nckd_histotwodate";
+    /** 员工类别 */
+    public static final String NCKD_HISTALENTTYPE = "nckd_histalenttype";
+    /** 员工状态 */
+    public static final String NCKD_HISSTATE = "nckd_hisstate";
+    /** 历史退出市场原因 */
+    public static final String NCKD_HISBACKUP = "nckd_hisbackup";
+    /** 部门(冗余) */
+    public static final String NCKD_ADMINORG = "nckd_adminorg";
+    /** 是否最新记录(人员) */
+    public static final String NCKD_ISCURRENTNEWEST = "nckd_iscurrentnewest";
 }

+ 233 - 1
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/operate/IntJobPostAuditedOpPlugin.java

@@ -1,11 +1,42 @@
 package nckd.jxccl.hr.ijp.plugin.operate;
 
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.ijp.TalentTypeEnum;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+import nckd.jxccl.base.hrpi.helper.EmpPosOrgRelHelper;
+import nckd.jxccl.hr.ijp.common.IntJobPostConstant;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
 * 内部人力资源市场-三类人员记录生成-审批通过
@@ -16,12 +47,151 @@ import nckd.jxccl.base.common.constant.FormConstant;
 */
 public class IntJobPostAuditedOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
 
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        e.addValidator(new AbstractValidator(){
+            @Override
+            public void validate() {
+                List<Long> personIds = new ArrayList<>();
+                for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                    DynamicObject data = rowDataEntity.getDataEntity();
+                    DynamicObject person = data.getDynamicObject(PositionStructureConstant.NCKD_PERSON);
+                    if(person == null){
+                        this.addFatalErrorMessage(rowDataEntity,"请选择人员");
+                    }else{
+                        long personId = person.getLong(FormConstant.ID_KEY);
+                        personIds.add(personId);
+                    }
+                }
+                Map<Long, DynamicObject> empPosOrgRelMap = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployeesMap(personIds);
+                for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                    DynamicObject data = rowDataEntity.getDataEntity();
+                    DynamicObject person = data.getDynamicObject(PositionStructureConstant.NCKD_PERSON);
+                    if(person != null){
+                        long personId = person.getLong(FormConstant.ID_KEY);
+                        if(empPosOrgRelMap.get(personId) == null){
+                            this.addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】没有在岗状态的任职经历",person.getString(FormConstant.NAME_KEY)));
+                        }
+                    }
+                }
+            }
+        });
+    }
+
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
 
+        List<Long> personIds = new ArrayList<>();
+        List<Long> entityIds = new ArrayList<>();
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            DynamicObjectCollection entryEntity = dataEntity.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+            for (DynamicObject entry : entryEntity) {
+                boolean isEnter = entry.getBoolean(IntJobPostConstant.NCKD_ISENTER);
+                if(isEnter){
+                    personIds.add(entry.getLong(IntJobPostConstant.NCKD_PERSON));
+                }
+                entityIds.add(entry.getLong(IntJobPostConstant.ID_KEY));
+            }
+        }
+        Map<Long, DynamicObject> empPosOrgRelMap = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployeesMap(personIds);
+
+        // --- A. 处理旧记录 ---
+        // 1. 查询该员工最近的一条记录(排除当前单据产生的记录,按创建时间倒序取第一条)
+        long currUserId = RequestContext.get().getCurrUserId();
+        List<DynamicObject> savedRecords = new ArrayList<>();
+        //由于轨迹需要关联本次新增的记录,需要等记录保存成功后再set内部人力资源市场记录。 以分录id作为key,记录待新增的轨迹。
+        Map<Long, DynamicObject> savedLocusMap = new HashMap<>();
+        List<DynamicObject> savedLocus = new ArrayList<>();
+        Map<Long, DynamicObject> latestRecordMap = getLatestRecordIdsByPersons(personIds, entityIds);
+        Map<Long, DynamicObject> latestLocusMap = findLatestLocusByPerson(personIds);
         for (DynamicObject dataEntity : e.getDataEntities()) {
-            DynamicObjectCollection dynamicObjectCollection = dataEntity.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+            Date auditDate = dataEntity.getDate(IntJobPostConstant.AUDIT_DATE_KEY);
+            long personId = dataEntity.getLong(String.join(".", IntJobPostConstant.NCKD_PERSON, IntJobPostConstant.ID_KEY));
+            String talentType = dataEntity.getString(IntJobPostConstant.NCKD_TALENTTYPE);
+            TalentTypeEnum talentTypeEnum = TalentTypeEnum.getByCode(talentType);
+            DynamicObjectCollection entryEntity = dataEntity.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+
+            for (DynamicObject entry : entryEntity) {
+                long id = entry.getLong(IntJobPostConstant.ID_KEY);
+                boolean isEnter = entry.getBoolean(IntJobPostConstant.NCKD_ISENTER);
+                if(isEnter){
+                    DynamicObject latestRecord = latestRecordMap.get(personId);
+                    if(latestRecord != null) {
+                        // 2. 备份旧值
+                        latestRecord.set(IntJobPostConstant.NCKD_OLDTOFOURDATE, latestRecord.get(IntJobPostConstant.NCKD_TOFOURDATE));
+                        latestRecord.set(IntJobPostConstant.NCKD_OLDSTATE, latestRecord.get(IntJobPostConstant.NCKD_STATE));
+                        //退出市场时间
+                        latestRecord.set(IntJobPostConstant.NCKD_TOFOURDATE, auditDate);
+                        //标记上一条为退出市场
+                        latestRecord.set(IntJobPostConstant.NCKD_STATE, "4");
+                        latestRecord.set(IntJobPostConstant.NCKD_ISCURRENTNEWEST, Boolean.FALSE);
+                        latestRecord.set(IntJobPostConstant.NCKD_BACKUP, StrFormatter.format("以【{}】身份新进入市场,退出原市场。",talentTypeEnum.getName()));
+                        savedRecords.add(latestRecord);
+                    }
+
+                    // --- B. 插入新记录 ---
+                    DynamicObject newRecord = EntityHelper.newEntity(IntJobPostConstant.INTJOBPOSTRECORD_ENTITYID);
+                    DynamicObject empPosOrgRel = empPosOrgRelMap.get(personId);
+                    newRecord.set(IntJobPostConstant.HRPI_EMPPOSORGREL, empPosOrgRel);
+                    newRecord.set(IntJobPostConstant.NCKD_ADMINORG,empPosOrgRel.get(IntJobPostConstant.ADMINORG));
+                    newRecord.set(IntJobPostConstant.NCKD_ISCURRENTNEWEST, Boolean.TRUE);
+                    //初始状态:进入市场
+                    newRecord.set(IntJobPostConstant.NCKD_STATE,"1");
+                    newRecord.set(IntJobPostConstant.NCKD_TALENTTYPE,dataEntity.get(IntJobPostConstant.NCKD_TALENTTYPE));
+                    newRecord.set(IntJobPostConstant.NCKD_BEGINDATE,dataEntity.get(IntJobPostConstant.NCKD_BIZDAY));
+                    newRecord.set(IntJobPostConstant.NCKD_YEAR,dataEntity.get(IntJobPostConstant.NCKD_YEAR));
+                    //分录
+                    newRecord.set(IntJobPostConstant.NCKD_INTJOBPOSTENTRY,latestRecord);
+                    newRecord.set(IntJobPostConstant.NCKD_APPRAISALRESULT,entry.get(IntJobPostConstant.NCKD_APPRAISALRESULT));
+                    newRecord.set(IntJobPostConstant.NCKD_BEFORELAST,entry.get(IntJobPostConstant.NCKD_BEFORELAST));
+                    newRecord.set(FormConstant.CREATOR_KEY,currUserId);
+                    if ("A".equals(dataEntity.getString(IntJobPostConstant.NCKD_TALENTTYPE))) {
+                        //优秀员工-预计退出时间
+                        Date date = DateUtil.addYears(auditDate, 1);
+                        newRecord.set(IntJobPostConstant.NCKD_PREDICTTOFOURDATE, date);
+                    }
+                    savedRecords.add(newRecord);
+
+                    // --- C. 处理轨迹 (Locus) ---
+                    // 1. 查询最近的轨迹
+                    DynamicObject latestLocus = latestLocusMap.get(personId);
+                    if(latestLocus != null){
+                        latestLocus.set(IntJobPostConstant.NCKD_OLDENDDAY, latestLocus.get(IntJobPostConstant.ENDDATE));
+                        latestLocus.set(IntJobPostConstant.ENDDATE, auditDate);
+                        latestLocus.set(IntJobPostConstant.NCKD_ISCURRENTNEWEST, Boolean.FALSE);
+                        savedLocus.add(latestLocus);
+                    }
+                    // 2. 插入新轨迹
+                    DynamicObject newLocus = EntityHelper.newEntity(IntJobPostConstant.INTJOBPOSTLOCUS_ENTITYID);
+                    newLocus.set(IntJobPostConstant.HRPI_EMPPOSORGREL, empPosOrgRel);
+                    newLocus.set(IntJobPostConstant.NCKD_ADMINORG,empPosOrgRel.get(IntJobPostConstant.ADMINORG));
+                    newLocus.set(IntJobPostConstant.NCKD_ISCURRENTNEWEST, Boolean.TRUE);
+                    newLocus.set(IntJobPostConstant.NCKD_INTJOBPOSTRECORD, newRecord);
+                    newLocus.set(IntJobPostConstant.NCKD_STARTDATE, dataEntity.get(IntJobPostConstant.NCKD_BIZDAY));
+                    newLocus.set(IntJobPostConstant.NCKD_TALENTTYPE, dataEntity.get(IntJobPostConstant.NCKD_TALENTTYPE));
+                    newLocus.set(IntJobPostConstant.NCKD_STATE, "1");
+                    newLocus.set(FormConstant.CREATOR_KEY,currUserId);
+                    savedLocusMap.put(id, newLocus);
+                }
+            }
+        }
+        if(!savedRecords.isEmpty()){
+            OperationResult operationResult = SaveServiceHelper.saveOperate(IntJobPostConstant.INTJOBPOSTRECORD_ENTITYID, savedRecords.toArray(new DynamicObject[0]), OperateOption.create());
+            if(operationResult.isSuccess()){
+                //新增实体
+//                OperationResult operationResult = SaveServiceHelper.saveOperate(IntJobPostConstant.INTJOBPOSTLOCUS_ENTITYID, savedRecords.toArray(new DynamicObject[0]), OperateOption.create());
+            }
+
         }
+
+
+
+
+
+        // --- A. 处理旧记录 ---
+        // 1. 查询该员工最近的一条记录(排除当前单据产生的记录,按创建时间倒序取第一条)
+
+
         //1、阶段1:备份原有记录
         // 条件:单据中要进入市场(CFIsenter=1)的员工 + 他们最新的记录 + 记录来源不是当前单据
         //目的:为反审批时恢复数据做准备
@@ -37,7 +207,69 @@ public class IntJobPostAuditedOpPlugin extends AbstractOperationServicePlugIn im
         //A类员工:有预计退出时间(1年后)
         //非A类员工:预计退出时间为null
 
+    }
 
+    /**
+     * 获取每个人员最新一条记录
+     * @param personIds 人员ID
+     * @param entityIds 分录ID
+     * @return: java.util.Map<java.lang.Long, java.util.List < kd.bos.dataentity.entity.DynamicObject>>
+     * @author W.Y.C
+     * @date: 2026/01/13 15:56
+     */
+    public Map<Long, DynamicObject> getLatestRecordIdsByPersons(List<Long> personIds,List<Long> entityIds) {
+        QueryFieldBuilder intJobPostRecord = QueryFieldBuilder.create().add(IntJobPostConstant.ID_KEY)
+                .add(FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY,FormConstant.ID_KEY)
+                .add(IntJobPostConstant.NCKD_ISCURRENTNEWEST)
+                .orderDesc(IntJobPostConstant.CREATE_TIME_KEY);
+        QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY), QCP.in, personIds)
+                .and(IntJobPostConstant.NCKD_INTJOBPOSTENTRY,QCP.not_in,entityIds);
+        DynamicObjectCollection records = QueryServiceHelper.query(IntJobPostConstant.INTJOBPOSTRECORD_ENTITYID, intJobPostRecord.buildSelect(), new QFilter[]{filter});
+        Set<Long> uniquePersonIds = new HashSet<>();
+        List<Long> result = new ArrayList<>();
+        for (DynamicObject record : records) {
+            Long personId = record.getLong(String.join(".",FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+            if (uniquePersonIds.add(personId)) { // 如果是新的personId,添加到结果中
+                result.add(record.getLong(IntJobPostConstant.ID_KEY));
+            }
+        }
+        MainEntityType intJobPostRecordEntityType = EntityMetadataCache.getDataEntityType(IntJobPostConstant.INTJOBPOSTRECORD_ENTITYID);
+        DynamicObject[] load = BusinessDataServiceHelper.load(result.toArray(new Long[0]), intJobPostRecordEntityType);
+        return Arrays.stream(load)
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(String.join(".", FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)),
+                        obj -> obj
+                ));
+    }
 
+    /**
+     * 查询最近的轨迹
+     * @param personIds 人员ID
+     * @return: java.util.Map<java.lang.Long, java.util.List < kd.bos.dataentity.entity.DynamicObject>>
+     * @author W.Y.C
+     * @date: 2026/01/13 15:56
+     */
+    public Map<Long, DynamicObject> findLatestLocusByPerson(List<Long> personIds) {
+        QueryFieldBuilder intJobPostLocus = QueryFieldBuilder.create().add(IntJobPostConstant.ID_KEY)
+                .add(FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY,FormConstant.ID_KEY)
+                .add(IntJobPostConstant.NCKD_ISCURRENTNEWEST)
+                .orderDesc(IntJobPostConstant.CREATE_TIME_KEY);
+        QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY), QCP.in, personIds);
+        DynamicObjectCollection records = QueryServiceHelper.query(IntJobPostConstant.INTJOBPOSTLOCUS_ENTITYID, intJobPostLocus.buildSelect(), new QFilter[]{filter});
+        Set<Long> uniquePersonIds = new HashSet<>();
+        List<Long> result = new ArrayList<>();
+        for (DynamicObject record : records) {
+            Long personId = record.getLong(String.join(".",FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+            if (uniquePersonIds.add(personId)) { // 如果是新的personId,添加到结果中
+                result.add(record.getLong(IntJobPostConstant.ID_KEY));
+            }
+        }
+        MainEntityType intJobPostLocusEntityType = EntityMetadataCache.getDataEntityType(IntJobPostConstant.INTJOBPOSTLOCUS_ENTITYID);
+        DynamicObject[] load = BusinessDataServiceHelper.load(result.toArray(new Long[0]), intJobPostLocusEntityType);
+        return Arrays.stream(load)
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(String.join(".",FormConstant.NCKD_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)),
+                        obj -> obj
+                ));
     }
 }

+ 0 - 4
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/contribution/ContribBillOpPlugin.java

@@ -17,7 +17,6 @@ import kd.bos.form.MessageBoxResult;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.psms.ScoreItemEnum;
-import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
 import nckd.jxccl.hr.psms.helper.ContributionHelper;
@@ -25,11 +24,8 @@ import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 /**
  * 年度积分贡献单据保存/提交插件

+ 1 - 1
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/ExaminProjectResultListPlugin.java

@@ -70,7 +70,7 @@ import java.util.stream.Collectors;
  * @Version: 1.0
  */
 public class ExaminProjectResultListPlugin extends AbstractListPlugin {
-    private ExaminProjectResultBillList billList;
+//    private ExaminProjectResultBillList billList;
     private static final Log logger = LogFactory.getLog(ExaminProjectResultListPlugin.class);
 
     @Override

+ 4 - 3
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/mas/plugin/form/empmgt/SubCoHeadServiceListPlugin.java

@@ -371,9 +371,10 @@ public class SubCoHeadServiceListPlugin extends AbstractListPlugin implements Pl
                     }else{
                         newSubCoHeadService.set(MasConstant.NCKD_ENDDATE, DateUtil.getMaxDateAsDate());
                     }
-                    //【变动时间】的次月到当年年底的月数
-                    LocalDateTime newMonths = DateUtil.addMonths(DateUtil.toLocalDateTime(changeTime), 1);
-                    long between = DateUtil.between(newMonths, endYear, ChronoUnit.MONTHS);
+                    //【变动时间】的次月到当年年底的月数(ChronoUnit.MONTHS是从0开始的,所以不用加月份)
+//                    LocalDateTime newMonths = DateUtil.addMonths(DateUtil.toLocalDateTime(changeTime), 1);
+                    Date endChangeTime = DateUtil.endOfYear(changeTime);
+                    long between = DateUtil.between(DateUtil.toLocalDateTime(changeTime), DateUtil.toLocalDateTime(endChangeTime), ChronoUnit.MONTHS);
                     newSubCoHeadService.set(MasConstant.NCKD_SERVICEMONTHS,between);
                 }
 

+ 4 - 3
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/mas/plugin/form/empmgt/TenurePersonListListPlugin.java

@@ -312,9 +312,10 @@ public class TenurePersonListListPlugin extends AbstractListPlugin implements Pl
                     }else{
                         newSubCoHeadService.set(MasConstant.NCKD_ENDDATE, DateUtil.getMaxDateAsDate());
                     }
-                    //【变动时间】的次月到当年年底的月数
-                    LocalDateTime newMonths = DateUtil.addMonths(DateUtil.toLocalDateTime(changeTime), 1);
-                    long between = DateUtil.between(newMonths, endYear, ChronoUnit.MONTHS);
+                    //【变动时间】的次月到当年年底的月数(ChronoUnit.MONTHS是从0开始的,所以不用加月份)
+//                    LocalDateTime newMonths = DateUtil.addMonths(DateUtil.toLocalDateTime(changeTime), 1);
+                    Date endChangeTime = DateUtil.endOfYear(changeTime);
+                    long between = DateUtil.between(DateUtil.toLocalDateTime(changeTime), DateUtil.toLocalDateTime(endChangeTime), ChronoUnit.MONTHS);
                     newSubCoHeadService.set(MasConstant.NCKD_SERVICEMONTHS,between);
                 }
 

+ 6 - 1
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/mas/plugin/form/structappr/EntleaderAnlSalStdFormPlugin.java

@@ -145,8 +145,13 @@ public class EntleaderAnlSalStdFormPlugin extends AbstractStructApprFormPlugin i
                         DynamicObjectCollection dbEntryColl = dbSubCorpSalary.getDynamicObjectCollection(MasConstant.NCKD_ENTRYENTITY);
                         for (DynamicObject dbEntryEntity : dbEntryColl) {
                             DynamicObject projectCategory = dbEntryEntity.getDynamicObject(MasConstant.NCKD_PROJECTCATEGORY);
+                            DynamicObject parent = null;
+                            if(projectCategory.containsProperty("parent")) {
+                                parent = projectCategory.getDynamicObject("parent");
+                            }
                             String projectCategoryNumber = projectCategory.getString(FormConstant.NUMBER_KEY);
-                            if(projectCategoryNumber.equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode())) {
+                            //只取分类为"年度薪酬"的数据
+                            if(projectCategoryNumber.equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode()) || (parent != null && (parent.getString(FormConstant.NUMBER_KEY).equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode())))) {
                                 DynamicObject entry = entryEntities.addNew();
                                 boolean deferredPayItem = dbEntryEntity.getBoolean(MasConstant.NCKD_DEFERREDPAYITEM);
                                 entry.set(MasConstant.NCKD_PROJECTCATEGORY, dbEntryEntity.getDynamicObject(MasConstant.NCKD_PROJECTCATEGORY));

+ 5 - 1
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/mas/plugin/form/structappr/SubcorpChiefSalStdFormPlugin.java

@@ -134,9 +134,13 @@ public class SubcorpChiefSalStdFormPlugin extends AbstractStructApprFormPlugin i
                     DynamicObjectCollection dbEntryColl = dbSubCorpSalary.getDynamicObjectCollection(MasConstant.NCKD_ENTRYENTITY);
                     for (DynamicObject dbEntryEntity : dbEntryColl) {
                         DynamicObject projectCategory = dbEntryEntity.getDynamicObject(MasConstant.NCKD_PROJECTCATEGORY);
+                        DynamicObject parent = null;
+                        if(projectCategory.containsProperty("parent")) {
+                            parent = projectCategory.getDynamicObject("parent");
+                        }
                         String projectCategoryNumber = projectCategory.getString(FormConstant.NUMBER_KEY);
                         //只取分类为"年度薪酬"的数据
-                        if(projectCategoryNumber.equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode())) {
+                        if(projectCategoryNumber.equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode()) || (parent != null && (parent.getString(FormConstant.NUMBER_KEY).equalsIgnoreCase(ProjectCategoryEnum.YEARLY_SALARY.getCode())))) {
                             DynamicObject entry = entryEntities.addNew();
                             boolean deferredPayItem = dbEntryEntity.getBoolean(MasConstant.NCKD_DEFERREDPAYITEM);
                             entry.set(MasConstant.NCKD_PROJECTCATEGORY, dbEntryEntity.getDynamicObject(MasConstant.NCKD_PROJECTCATEGORY));