Browse Source

feat(hr): 新增管理人员职位津贴业务逻辑

- 新增 ManagerAllowanceService 类,实现职位津贴的生成与失效逻辑
- 在多个操作插件中集成职位津贴的生成与失效调用
- 新增职位津贴配置相关常量定义
- 优化任职经历查询方法,支持按员工ID分组获取最新记录
- 完善绩效管理表单中的职位津贴查询条件过滤
- 修复部分插件中缺少的导入依赖及字段查询问题
wyc 1 week ago
parent
commit
b76976620e
13 changed files with 862 additions and 9 deletions
  1. 40 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/hrpi/helper/EmpPosOrgRelHelper.java
  2. 655 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/ManagerAllowanceService.java
  3. 12 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java
  4. 5 3
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java
  5. 4 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/adjust/NewDynamicAdjustmentOperationPlugIn.java
  6. 9 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualAdjustmentOperationPlugin.java
  7. 9 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualEffectiveOpPlugin.java
  8. 9 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualSetinActiveOpPlugin.java
  9. 9 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/file/PersonPosFileDeleteOpPlugin.java
  10. 2 1
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/BaseInitialOperationPlugIn.java
  11. 7 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/NewHireInitialOperationPlugIn.java
  12. 7 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/ServingInitialOperationPlugIn.java
  13. 94 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/other/ManagerAllowanceSaveOp.java

+ 40 - 1
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/hrpi/helper/EmpPosOrgRelHelper.java

@@ -6,12 +6,15 @@ import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
-import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 任职经历Helper类
@@ -56,6 +59,41 @@ public class EmpPosOrgRelHelper {
         return dynamicObjects.length > 0 ? dynamicObjects[0] : null;
     }
 
+    /**
+     * 根据多个员工ID查询最新的任职经历信息
+     *
+     * @param employeeIds 员工ID集合
+     * @return 员工的任职经历信息数组
+     * @author W.Y.C
+     * @date: 2025/11/04 16:02
+     */
+    public static Map<Long, DynamicObject> queryEmpPosOrgRelByEmployeesMap(Collection<Long> employeeIds) {
+        DynamicObject[] dynamicObjects = queryEmpPosOrgRelByEmployees(employeeIds, null);
+        // 按员工ID分组,并取每个员工的最新记录(按开始日期)
+        Map<Long, List<DynamicObject>> grouped = new HashMap<>();
+        for (DynamicObject obj : dynamicObjects) {
+            Long empId = obj.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+            grouped.computeIfAbsent(empId, k -> new ArrayList<>()).add(obj);
+        }
+
+        // 从每组中选取开始日期最新的记录
+        Map<Long, DynamicObject> result = new HashMap<>();
+        for (Map.Entry<Long, List<DynamicObject>> entry : grouped.entrySet()) {
+            List<DynamicObject> records = entry.getValue();
+            DynamicObject latestRecord = records.stream()
+                    .max((o1, o2) -> {
+                        Date date1 = o1.getDate(FormConstant.STARTDATE);
+                        Date date2 = o2.getDate(FormConstant.STARTDATE);
+                        return date1.compareTo(date2);
+                    })
+                    .orElse(null);
+            result.put(entry.getKey(), latestRecord);
+        }
+
+        return result;
+
+    }
+
     /**
      * 根据多个员工ID查询最新的任职经历信息
      *
@@ -117,6 +155,7 @@ public class EmpPosOrgRelHelper {
         return QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
                 .add(FormConstant.IS_PRIMARY)
+                .add(FormConstant.STARTDATE)
                 .addIdNumberName(FormConstant.EMPLOYEE_KEY)
                 .addIdNumberName(FormConstant.CHGACTION)
                 .addIdNumberName(FormConstant.POSTYPE)

+ 655 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/ManagerAllowanceService.java

@@ -0,0 +1,655 @@
+package nckd.jxccl.hr.psms.business;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+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 nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * 管理人员津贴业务逻辑
+ *
+ * @author W.Y.C
+ * @date 2025/12/6 16:27
+ * @version 1.0
+ */
+public class ManagerAllowanceService {
+
+
+    /**
+     * 生成职位津贴,根据职位津贴配置的条件
+     *
+     * @param data 职位档案数据列表
+     */
+    public static void generateMgrPostAllow(List<DynamicObject> data) {
+        if (data == null || data.isEmpty()) {
+            return;
+        }
+
+        // 获取必要的ID列表
+        DataIds dataIds = extractDataIds(data);
+
+        // 加载必要的数据
+        LoadedData loadedData = loadRequiredData(dataIds);
+
+        List<DynamicObject> addOrExpireMgrPostAllowList = new ArrayList<>();
+
+        for (DynamicObject datum : data) {
+            long personId = datum.getLong(String.join(".", PositionStructureConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            DynamicObject person = datum.getDynamicObject(PositionStructureConstant.NCKD_PERSON);
+
+            List<DynamicObject> managerAllowanceList = loadedData.managerAllowanceMap.get(personId);
+
+            // 判断人员是否符合职位津贴条件
+            QualificationResult qualificationResult = checkQualification(
+                    personId, loadedData.mgrPostAllowConfList, loadedData.adminOrgMap,
+                    loadedData.jobLevelMap, loadedData.personPosFileArchivesMap);
+
+            if (!loadedData.mgrPostAllowConfList.isEmpty()) {
+                if (qualificationResult.isQualified) {
+                    // 符合条件
+                    if (managerAllowanceList == null || managerAllowanceList.isEmpty()) {
+                        // 符合条件但尚无津贴记录,新增职位津贴
+                        DynamicObject newEntity = EntityHelper.newEntity(PositionStructureConstant.MANAGERALLOWANCE_ENTITYID);
+                        newEntity.set(FormConstant.NCKD_PERSON, person);
+                        newEntity.set(FormConstant.NCKD_STARTDATE, qualificationResult.beginDate == null ? new Date() : qualificationResult.beginDate);
+                        newEntity.set(PositionStructureConstant.NCKD_JOBLEVELHR, qualificationResult.jobLevelObj);
+                        newEntity.set(PositionStructureConstant.NCKD_PROTITLELEVEL, qualificationResult.protitleLeveObj);
+                        newEntity.set(FormConstant.DESCRIPTION_KEY, "符合【职位津贴条件】,系统自动生成");
+                        addOrExpireMgrPostAllowList.add(newEntity);
+                    }
+                    // 如果已有津贴记录且符合条件,则不做处理
+                } else {
+                    // 不符合条件
+                    handleDisqualification(managerAllowanceList, qualificationResult.beginDate, addOrExpireMgrPostAllowList);
+                }
+            } else {
+                // 连配置都没有了,直接失效职位津贴
+                handleNoConfiguration(managerAllowanceList, addOrExpireMgrPostAllowList);
+            }
+        }
+
+        // 保存变更
+        saveChanges(addOrExpireMgrPostAllowList);
+    }
+
+    /**
+     * 失效职位津贴
+     *
+     * @param data 职位档案数据列表
+     */
+    public static void expireMgrPostAllow(List<DynamicObject> data) {
+        if (data == null || data.isEmpty()) {
+            return;
+        }
+
+        // 获取必要的ID列表
+        DataIds dataIds = extractDataIds(data);
+
+        // 加载必要的数据
+        LoadedData loadedData = loadRequiredData(dataIds);
+
+        List<DynamicObject> addOrExpireMgrPostAllowList = new ArrayList<>();
+
+        for (DynamicObject datum : data) {
+            long currentId = datum.getLong(FormConstant.ID_KEY);
+            long personId = datum.getLong(String.join(".", PositionStructureConstant.NCKD_PERSON, FormConstant.ID_KEY));
+
+            List<DynamicObject> managerAllowanceList = loadedData.managerAllowanceMap.get(personId);
+
+            // 判断人员是否符合职位津贴条件(排除当前变更的档案)
+            QualificationResult qualificationResult = checkQualificationExcludeCurrent(
+                    personId, currentId, loadedData.mgrPostAllowConfList, loadedData.adminOrgMap,
+                    loadedData.jobLevelMap, loadedData.personPosFileArchivesMap);
+
+            if (!loadedData.mgrPostAllowConfList.isEmpty()) {
+                if (!qualificationResult.isQualified) {
+                    // 其他档案不符合条件,失效职位津贴
+                    handleDisqualification(managerAllowanceList, qualificationResult.beginDate, addOrExpireMgrPostAllowList);
+                }
+                // 其他档案还是符合条件的,不处理
+            } else {
+                // 连配置都没有了,直接失效职位津贴
+                handleNoConfiguration(managerAllowanceList, addOrExpireMgrPostAllowList);
+            }
+        }
+
+        // 保存变更
+        saveChanges(addOrExpireMgrPostAllowList);
+    }
+
+    /**
+     * 提取数据中的各种ID
+     */
+    private static DataIds extractDataIds(List<DynamicObject> data) {
+        DataIds dataIds = new DataIds();
+        dataIds.adminIds = new ArrayList<>();
+        dataIds.jobLevelIds = new ArrayList<>();
+        dataIds.personIds = new ArrayList<>();
+
+        for (DynamicObject datum : data) {
+            dataIds.adminIds.add(datum.getLong(String.join(".", FormConstant.NCKD_DEP, FormConstant.ID_KEY)));
+            dataIds.jobLevelIds.add(datum.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.ID_KEY)));
+            dataIds.personIds.add(datum.getLong(String.join(".", PositionStructureConstant.NCKD_PERSON, FormConstant.ID_KEY)));
+        }
+
+        return dataIds;
+    }
+
+    /**
+     * 加载所需的各类数据
+     */
+    private static LoadedData loadRequiredData(DataIds dataIds) {
+        LoadedData loadedData = new LoadedData();
+
+        // 加载职位津贴配置
+        loadedData.mgrPostAllowConfList = findMgrPostAllowConf();
+
+        // 加载组织信息
+        loadedData.adminOrgMap = loadAndGroupAdminOrgs(dataIds.adminIds);
+
+        // 加载职级信息
+        loadedData.jobLevelMap = loadAndGroupJobLevels(dataIds.jobLevelIds);
+
+        // 加载有效的职位津贴数据
+        loadedData.managerAllowanceMap = loadAndGroupManagerAllowance(dataIds.personIds);
+
+        // 加载人员所有有效中的职位档案
+        loadedData.personPosFileArchivesMap = loadPersonPosFileArchives(dataIds.personIds);
+
+        return loadedData;
+    }
+
+    /**
+     * 检查人员是否符合职位津贴条件
+     */
+    private static QualificationResult checkQualification(long personId,
+                                                          List<DynamicObject> mgrPostAllowConfList,
+                                                          Map<Long, DynamicObject> adminOrgMap,
+                                                          Map<Long, DynamicObject> jobLevelMap,
+                                                          Map<Long, List<DynamicObject>> personPosFileArchivesMap) {
+        QualificationResult result = new QualificationResult();
+        result.isQualified = false;
+
+        List<DynamicObject> personPosFileArchives = personPosFileArchivesMap.get(personId);
+        if (personPosFileArchives != null && !personPosFileArchives.isEmpty()) {
+            for (DynamicObject personPosFileArchive : personPosFileArchives) {
+                long adminOrgId = personPosFileArchive.getLong(String.join(".", FormConstant.NCKD_DEP, FormConstant.ID_KEY));
+                long jobSeqId = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_JOBSEQHR, FormConstant.ID_KEY));
+                long jobLevel = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.ID_KEY));
+                long protitleLevelId = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_PROTITLELEVEL, FormConstant.ID_KEY));
+
+                result.protitleLeveObj = EntityHelper.newEntity(FormConstant.HBSS_PROTITLELEVEL, protitleLevelId);
+                result.beginDate = personPosFileArchive.getDate(PositionStructureConstant.NCKD_BEGINDATE);
+
+                for (DynamicObject mgrPostAllowConf : mgrPostAllowConfList) {
+                    // 序列是否符合
+                    long confJobSeqId = mgrPostAllowConf.getLong(String.join(".", PositionStructureConstant.NCKD_JOBSEQHR, FormConstant.ID_KEY));
+                    if (confJobSeqId != jobSeqId) {
+                        continue;
+                    }
+
+                    // 组织是否符合
+                    if (!isAdminOrgMatch(adminOrgId, mgrPostAllowConf, adminOrgMap)) {
+                        continue;
+                    }
+
+                    // 职称级别是否符合
+                    if (!isProtitleLevelMatch(protitleLevelId, mgrPostAllowConf)) {
+                        continue;
+                    }
+
+                    // 职级是否符合
+                    JobLevelMatchResult jobLevelMatchResult = isJobLevelMatch(jobLevel, mgrPostAllowConf, jobLevelMap);
+                    if (!jobLevelMatchResult.isMatch) {
+                        continue;
+                    }
+
+                    result.isQualified = true;
+                    result.jobLevelObj = jobLevelMatchResult.jobLevelObj;
+                    break;
+                }
+
+                if (result.isQualified) {
+                    // 有符合的就不找下一个档案了
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 检查人员是否符合职位津贴条件(排除指定的档案ID)
+     */
+    private static QualificationResult checkQualificationExcludeCurrent(long personId,
+                                                                       long excludeId,
+                                                                       List<DynamicObject> mgrPostAllowConfList,
+                                                                       Map<Long, DynamicObject> adminOrgMap,
+                                                                       Map<Long, DynamicObject> jobLevelMap,
+                                                                       Map<Long, List<DynamicObject>> personPosFileArchivesMap) {
+        QualificationResult result = new QualificationResult();
+        result.isQualified = false;
+
+        List<DynamicObject> personPosFileArchives = personPosFileArchivesMap.get(personId);
+        if (personPosFileArchives != null && !personPosFileArchives.isEmpty()) {
+            for (DynamicObject personPosFileArchive : personPosFileArchives) {
+                long id = personPosFileArchive.getLong(FormConstant.ID_KEY);
+                // 因为这条职位档案是删除/生效的,所以不能判断当前职位档案是否满足职位津贴条件
+                if (id != excludeId) {
+                    long adminOrgId = personPosFileArchive.getLong(String.join(".", FormConstant.NCKD_DEP, FormConstant.ID_KEY));
+                    long jobSeqId = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_JOBSEQHR, FormConstant.ID_KEY));
+                    long jobLevel = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.ID_KEY));
+                    long protitleLevelId = personPosFileArchive.getLong(String.join(".", PositionStructureConstant.NCKD_PROTITLELEVEL, FormConstant.ID_KEY));
+
+                    result.beginDate = personPosFileArchive.getDate(PositionStructureConstant.NCKD_BEGINDATE);
+
+                    for (DynamicObject mgrPostAllowConf : mgrPostAllowConfList) {
+                        // 序列是否符合
+                        long confJobSeqId = mgrPostAllowConf.getLong(String.join(".", PositionStructureConstant.NCKD_JOBSEQHR, FormConstant.ID_KEY));
+                        if (confJobSeqId != jobSeqId) {
+                            continue;
+                        }
+
+                        // 组织是否符合
+                        if (!isAdminOrgMatch(adminOrgId, mgrPostAllowConf, adminOrgMap)) {
+                            continue;
+                        }
+
+                        // 职称级别是否符合
+                        if (!isProtitleLevelMatch(protitleLevelId, mgrPostAllowConf)) {
+                            continue;
+                        }
+
+                        // 职级是否符合
+                        JobLevelMatchResult jobLevelMatchResult = isJobLevelMatch(jobLevel, mgrPostAllowConf, jobLevelMap);
+                        if (!jobLevelMatchResult.isMatch) {
+                            continue;
+                        }
+
+                        result.isQualified = true;
+                        result.jobLevelObj = jobLevelMatchResult.jobLevelObj;
+                        break;
+                    }
+                }
+
+                if (result.isQualified) {
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 检查组织是否匹配
+     */
+    private static boolean isAdminOrgMatch(long adminOrgId,
+                                          DynamicObject mgrPostAllowConf,
+                                          Map<Long, DynamicObject> adminOrgMap) {
+        long confAdminOrgId = mgrPostAllowConf.getLong(String.join(".", PositionStructureConstant.NCKD_ORG, FormConstant.ID_KEY));
+        DynamicObject adminOrg = adminOrgMap.get(adminOrgId);
+
+        if (adminOrg == null || confAdminOrgId <= 0) {
+            return false;
+        }
+
+        long firstOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_FIRSTORG, FormConstant.ID_KEY));
+        long secondOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_SECONDORG, FormConstant.ID_KEY));
+        long thirdOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_THIRDORG, FormConstant.ID_KEY));
+        long fourthOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_FOURTHORG, FormConstant.ID_KEY));
+        long fifthOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_FIFTHORG, FormConstant.ID_KEY));
+        long sixthOrg = adminOrg.getLong(String.join(".", FormConstant.NCKD_SIXTHORG, FormConstant.ID_KEY));
+
+        // 人员职位档案中的组织与职位津贴配置的组织是否符合
+        Set<Long> orgSet = new HashSet<>(Arrays.asList(firstOrg, secondOrg, thirdOrg, fourthOrg, fifthOrg, sixthOrg));
+        return orgSet.contains(confAdminOrgId);
+    }
+
+    /**
+     * 检查职称级别是否匹配
+     */
+    private static boolean isProtitleLevelMatch(long protitleLevelId, DynamicObject mgrPostAllowConf) {
+        DynamicObjectCollection protitleLevelColl = mgrPostAllowConf.getDynamicObjectCollection(PositionStructureConstant.NCKD_PROTITLELEVEL);
+        for (DynamicObject protitleLevel : protitleLevelColl) {
+            DynamicObject mulData = protitleLevel.getDynamicObject(FormConstant.BASEDATAID_KEY);
+            long confProtitleLevelId = mulData.getLong(FormConstant.ID_KEY);
+            if (confProtitleLevelId == protitleLevelId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 检查职级是否匹配
+     */
+    private static JobLevelMatchResult isJobLevelMatch(long jobLevel,
+                                                       DynamicObject mgrPostAllowConf,
+                                                       Map<Long, DynamicObject> jobLevelMap) {
+        JobLevelMatchResult result = new JobLevelMatchResult();
+        result.isMatch = false;
+
+        if (jobLevel > 0) {
+            DynamicObject jobLevelObj = jobLevelMap.get(jobLevel);
+            if (jobLevelObj != null) {
+                int jobLevelSeq = jobLevelObj.getInt(PositionStructureConstant.JOBLEVELSEQ);
+                DynamicObject confMinJobLevel = mgrPostAllowConf.getDynamicObject(PositionStructureConstant.NCKD_MINJOBLEVEL);
+                DynamicObject confMaxJobLevel = mgrPostAllowConf.getDynamicObject(PositionStructureConstant.NCKD_MAXJOBLEVEL);
+
+                if (confMinJobLevel != null) {
+                    int min = confMinJobLevel.getInt(PositionStructureConstant.JOBLEVELSEQ);
+                    int max = confMaxJobLevel == null ? Integer.MAX_VALUE : confMaxJobLevel.getInt(PositionStructureConstant.JOBLEVELSEQ);
+                    if (jobLevelSeq >= min && jobLevelSeq <= max) {
+                        result.isMatch = true;
+                        result.jobLevelObj = jobLevelObj;
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 处理不符合条件的情况
+     */
+    private static void handleDisqualification(List<DynamicObject> managerAllowanceList,
+                                              Date beginDate,
+                                              List<DynamicObject> addOrExpireMgrPostAllowList) {
+        if (managerAllowanceList != null) {
+            // 不符合条件,失效职位津贴
+            for (DynamicObject managerAllowance : managerAllowanceList) {
+                Date startDate = managerAllowance.getDate(FormConstant.NCKD_STARTDATE);
+                Date endDate = beginDate == null ? new Date() : beginDate;
+                if (startDate != null && beginDate != null && beginDate.before(startDate)) {
+                    // 如果beginDate在职位津贴开始日期之前,则结束使用津贴开始日期
+                    endDate = startDate;
+                }
+                managerAllowance.set(FormConstant.NCKD_ENDDATE, endDate);
+                String description = managerAllowance.getString(FormConstant.DESCRIPTION_KEY);
+                if (StringUtils.isNotEmpty(description)) {
+                    managerAllowance.set(FormConstant.DESCRIPTION_KEY, description + ";" + "不符合【职位津贴条件】,系统自动失效");
+                } else {
+                    managerAllowance.set(FormConstant.DESCRIPTION_KEY, "不符合【职位津贴条件】,系统自动失效");
+                }
+                addOrExpireMgrPostAllowList.add(managerAllowance);
+            }
+        }
+    }
+
+    /**
+     * 处理无配置的情况
+     */
+    private static void handleNoConfiguration(List<DynamicObject> managerAllowanceList,
+                                             List<DynamicObject> addOrExpireMgrPostAllowList) {
+        if (managerAllowanceList != null) {
+            // 连配置都没有了,直接失效职位津贴
+            for (DynamicObject managerAllowance : managerAllowanceList) {
+                Date endDate = new Date();
+                managerAllowance.set(FormConstant.NCKD_ENDDATE, endDate);
+                String description = managerAllowance.getString(FormConstant.DESCRIPTION_KEY);
+                if (StringUtils.isNotEmpty(description)) {
+                    managerAllowance.set(FormConstant.DESCRIPTION_KEY, description + ";" + "不符合【职位津贴条件】,系统自动失效");
+                } else {
+                    managerAllowance.set(FormConstant.DESCRIPTION_KEY, "不符合【职位津贴条件】,系统自动失效");
+                }
+                addOrExpireMgrPostAllowList.add(managerAllowance);
+            }
+        }
+    }
+
+    /**
+     * 保存变更
+     */
+    private static void saveChanges(List<DynamicObject> addOrExpireMgrPostAllowList) {
+        if (!addOrExpireMgrPostAllowList.isEmpty()) {
+            OperationResult operationResult = SaveServiceHelper.saveOperate(
+                    PositionStructureConstant.MANAGERALLOWANCE_ENTITYID,
+                    addOrExpireMgrPostAllowList.toArray(new DynamicObject[0]),
+                    OperateOption.create());
+
+            if (!operationResult.isSuccess()) {
+                StringJoiner errorMsg = new StringJoiner("\n");
+                for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
+                    errorMsg.add(error.getMessage());
+                }
+                if (!ObjectUtils.isEmpty(operationResult.getMessage())) {
+                    errorMsg.add(operationResult.getMessage());
+                }
+                throw new ValidationException("同步【职位津贴管理人员失败】,原因:" + errorMsg.toString());
+            }
+        }
+    }
+
+    /**
+     * 获取有效的职位津贴配置
+     *
+     * @return 有效的职位津贴配置列表
+     */
+    public static List<DynamicObject> findMgrPostAllowConf() {
+        // 查询职位津贴配置条件
+        QueryFieldBuilder queryField = QueryFieldBuilder.create().add(FormConstant.ID_KEY);
+        QFilter filter = QFilterCommonHelper.getStatusFilter()
+                .and(QFilterCommonHelper.getEnableFilter())
+                .and(QFilterCommonHelper.getValidDateFilter(FormConstant.NCKD_STARTDATE, FormConstant.NCKD_ENDDATE));
+        
+        DynamicObjectCollection query = QueryServiceHelper.query(
+                PositionStructureConstant.MGRPOSTALLOWCONF_ENTITYID,
+                queryField.buildSelect(),
+                new QFilter[]{filter});
+
+        // 获取所有ID
+        List<Long> ids = new ArrayList<>();
+        if (query != null && !query.isEmpty()) {
+            ids = query.stream()
+                    .map(obj -> obj.getLong(FormConstant.ID_KEY))
+                    .collect(Collectors.toList());
+        }
+
+        if (ids.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        MainEntityType mainEntityType = EntityMetadataCache.getDataEntityType(PositionStructureConstant.MGRPOSTALLOWCONF_ENTITYID);
+        DynamicObject[] load = BusinessDataServiceHelper.load(ids.toArray(new Long[0]), mainEntityType);
+
+        return Arrays.asList(load);
+    }
+
+    /**
+     * 根据ID列表加载并按ID分组行政组织信息
+     *
+     * @param adminIds 行政组织ID列表
+     * @return 按ID分组的行政组织映射
+     */
+    private static Map<Long, DynamicObject> loadAndGroupAdminOrgs(List<Long> adminIds) {
+        if (adminIds.isEmpty()) {
+            return new java.util.HashMap<>();
+        }
+
+        QueryFieldBuilder adminOrgQueryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(FormConstant.NAME_KEY)
+                .add(FormConstant.NUMBER_KEY)
+                .addIdNumberName(FormConstant.NCKD_FIRSTORG)
+                .addIdNumberName(FormConstant.NCKD_SECONDORG)
+                .addIdNumberName(FormConstant.NCKD_THIRDORG)
+                .addIdNumberName(FormConstant.NCKD_FOURTHORG)
+                .addIdNumberName(FormConstant.NCKD_FIFTHORG)
+                .addIdNumberName(FormConstant.NCKD_SIXTHORG);
+
+        // 这里要从数据库加载一下组织,不然没有1、2、3、4、5、6级组织
+        DynamicObject[] adminOrgArray = BusinessDataServiceHelper.load(
+                FormConstant.ADMINORGHR_ENTITYID,
+                adminOrgQueryFieldBuilder.buildSelect(),
+                new QFilter[]{QFilterCommonHelper.getIdInFilter(adminIds)});
+
+        return java.util.Arrays.stream(adminOrgArray)
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(FormConstant.ID_KEY),
+                        obj -> obj
+                ));
+    }
+
+    /**
+     * 根据ID列表加载并按ID分组职级信息
+     *
+     * @param jobLevelIds 职级ID列表
+     * @return 按ID分组的职级映射
+     */
+    private static Map<Long, DynamicObject> loadAndGroupJobLevels(List<Long> jobLevelIds) {
+        if (jobLevelIds.isEmpty()) {
+            return new java.util.HashMap<>();
+        }
+
+        QueryFieldBuilder jobLevelQueryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(FormConstant.NAME_KEY)
+                .add(FormConstant.NUMBER_KEY)
+                .add(FormConstant.JOBLEVELSEQ);
+
+        // 这里要从数据库加载一下职级,不然没有序号
+        DynamicObject[] jobLevelArray = BusinessDataServiceHelper.load(
+                FormConstant.HBJM_JOBLEVELHR,
+                jobLevelQueryFieldBuilder.buildSelect(),
+                new QFilter[]{QFilterCommonHelper.getIdInFilter(jobLevelIds)});
+
+        return java.util.Arrays.stream(jobLevelArray)
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(FormConstant.ID_KEY),
+                        obj -> obj
+                ));
+    }
+
+    /**
+     * 获取人员有效的职位津贴
+     *
+     * @param personIds 人员ID列表
+     * @return 按人员ID分组的职位津贴映射
+     */
+    private static Map<Long, List<DynamicObject>> loadAndGroupManagerAllowance(List<Long> personIds) {
+        if (personIds.isEmpty()) {
+            return new java.util.HashMap<>();
+        }
+
+        QueryFieldBuilder managerAllowanceQueryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY);
+
+        QFilter qFilter = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.in, personIds)
+                .and(QFilterCommonHelper.getValidDateFilter(FormConstant.NCKD_STARTDATE, FormConstant.NCKD_ENDDATE));
+
+        DynamicObjectCollection query = QueryServiceHelper.query(
+                PositionStructureConstant.MANAGERALLOWANCE_ENTITYID,
+                managerAllowanceQueryFieldBuilder.buildSelect(),
+                new QFilter[]{qFilter});
+
+        List<Long> ids = query.stream()
+                .map(obj -> obj.getLong(FormConstant.ID_KEY))
+                .collect(Collectors.toList());
+
+        if (ids.isEmpty()) {
+            return new java.util.HashMap<>();
+        }
+
+        MainEntityType mainEntityType = EntityMetadataCache.getDataEntityType(PositionStructureConstant.MANAGERALLOWANCE_ENTITYID);
+        DynamicObject[] load = BusinessDataServiceHelper.load(ids.toArray(new Long[0]), mainEntityType);
+
+        return Arrays.stream(load)
+                .collect(Collectors.groupingBy(
+                        obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
+                ));
+    }
+
+    /**
+     * 获取人员所有的职位档案
+     *
+     * @param personIds 人员ID列表
+     * @return 按人员ID分组的职位档案映射
+     */
+    private static Map<Long, List<DynamicObject>> loadPersonPosFileArchives(List<Long> personIds) {
+        if (personIds.isEmpty()) {
+            return new java.util.HashMap<>();
+        }
+
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(PositionStructureConstant.NCKD_PERSON)
+                .add(PositionStructureConstant.NCKD_BEGINDATE)
+                .add(PositionStructureConstant.NCKD_ADJUSSTATUS)
+                .add(PositionStructureConstant.NCKD_DEP)
+                .add(PositionStructureConstant.NCKD_JOBLEVELHR)
+                .add(PositionStructureConstant.NCKD_JOBSEQHR)
+                .add(PositionStructureConstant.NCKD_PROTITLELEVEL)
+                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);
+
+        QFilter qFilter = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.in, personIds)
+                .and(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode());
+
+        DynamicObject[] load = BusinessDataServiceHelper.load(
+                PositionStructureConstant.PERSONPOSFILE_ENTITYID,
+                queryFieldBuilder.buildSelect(),
+                new QFilter[]{qFilter});
+
+        // 按人员ID分组
+        return Arrays.stream(load)
+                .collect(Collectors.groupingBy(
+                        obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
+                ));
+    }
+
+    // 内部类定义
+    private static class DataIds {
+        List<Long> adminIds;
+        List<Long> jobLevelIds;
+        List<Long> personIds;
+    }
+
+    private static class LoadedData {
+        List<DynamicObject> mgrPostAllowConfList;
+        Map<Long, DynamicObject> adminOrgMap;
+        Map<Long, DynamicObject> jobLevelMap;
+        Map<Long, List<DynamicObject>> managerAllowanceMap;
+        Map<Long, List<DynamicObject>> personPosFileArchivesMap;
+    }
+
+    private static class QualificationResult {
+        boolean isQualified = false;
+        Date beginDate;
+        DynamicObject jobLevelObj;
+        DynamicObject protitleLeveObj;
+    }
+
+    private static class JobLevelMatchResult {
+        boolean isMatch = false;
+        DynamicObject jobLevelObj;
+    }
+}

+ 12 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java

@@ -273,4 +273,16 @@ public class PositionStructureConstant extends FormConstant {
     /** 新建任命-实体标识 */
     public static final String NEWAPPTPOPUP_ENTITYID = "nckd_newapptpopup";
     /*-------------------------------------- 新建任命 end --------------------------------------*/
+
+
+    /*-------------------------------------- 职位津贴配置 begin --------------------------------------*/
+    /** 职位津贴配置-实体标识 */
+    public static final String MGRPOSTALLOWCONF_ENTITYID = "nckd_mgrpostallowconf";
+    /** 适用单位 */
+    public static final String NCKD_ORG = "nckd_org";
+    /** 最大职级 */
+    public static final String NCKD_MAXJOBLEVEL = "nckd_maxjoblevel";
+    /** 最小职级 */
+    public static final String NCKD_MINJOBLEVEL = "nckd_minjoblevel";
+    /*-------------------------------------- 职位津贴配置 end --------------------------------------*/
 }

+ 5 - 3
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java

@@ -47,6 +47,7 @@ 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.hrpi.helper.EmpPosOrgRelHelper;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
@@ -522,9 +523,10 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
 
         // 查询管理人员职位津贴配置
         // 获取《管理人员职位津贴》,存在则默认勾选“职位津贴”
-        QFilter managerAllowanceFilter = new QFilter(FormConstant.NCKD_STARTDATE, QCP.less_equals, date)
-                .and(FormConstant.NCKD_ENDDATE, QCP.large_equals, date)
-                .and(FormConstant.NCKD_PERSON, QCP.in, personIds);
+
+        QFilter managerAllowanceFilter = QFilterCommonHelper.getValidDateFilter(FormConstant.NCKD_STARTDATE,FormConstant.NCKD_ENDDATE)
+                .and(FormConstant.NCKD_PERSON, QCP.in, personIds)
+                .and(FormConstant.STATUS,QCP.in,Arrays.asList(StatusEnum.C.toString(),StatusEnum.B.toString()));
 
         DynamicObjectCollection managerAllowanceQuery = QueryServiceHelper.query(
                 PositionStructureConstant.MANAGERALLOWANCE_ENTITYID,

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

@@ -25,6 +25,7 @@ import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
@@ -178,6 +179,9 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
         SaveServiceHelper.save(newPersonPosFiles.toArray(new DynamicObject[0]));
         PositionFileHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
         PositionFileHelper.markAsCurrentNewest(null,personIds.toArray(new Long[0]));
+
+        //职位津贴
+        ManagerAllowanceService.generateMgrPostAllow(newPersonPosFiles);
     }
 
     private void execute(DynamicObject data, List<Long> personIds, List<DynamicObject> newPersonPosFiles) {

+ 9 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualAdjustmentOperationPlugin.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.hr.psms.plugin.operate.annualadjust;
 
+import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.ExtendedDataEntity;
@@ -19,6 +20,7 @@ import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.business.AnnualAdjustmentService;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
@@ -31,6 +33,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
 * 新建年度调整记录操作插件
@@ -179,6 +183,11 @@ public class AnnualAdjustmentOperationPlugin extends AbstractOperationServicePlu
             String adjustType = newPersonPosFile.getString(PositionStructureConstant.NCKD_ADJUSTTYPE);
             setJobLevelResult(newPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_PERSON),newPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR),AdjustTypeEnum.getByCode(adjustType));
         }
+        //职位津贴(只取已生效的年度调整)
+        List<DynamicObject> collect = newPersonPosFiles.stream().filter(newPersonPosFile -> newPersonPosFile.getString(PositionStructureConstant.NCKD_ADJUSSTATUS).equalsIgnoreCase(EnableEnum.YES.getCode())).collect(Collectors.toList());
+        if(!collect.isEmpty()) {
+            ManagerAllowanceService.generateMgrPostAllow(collect);
+        }
     }
 
     private void extracted(DynamicObject data, List<DynamicObject> newPersonPosFiles, List<Long> personIds) {

+ 9 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualEffectiveOpPlugin.java

@@ -19,11 +19,13 @@ 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.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 
 import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -145,8 +147,13 @@ public class AnnualEffectiveOpPlugin extends AbstractOperationServicePlugIn impl
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
         QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
+                .add(PositionStructureConstant.NCKD_PERSON)
                 .add(PositionStructureConstant.NCKD_BEGINDATE)
                 .add(PositionStructureConstant.NCKD_ADJUSSTATUS)
+                .add(PositionStructureConstant.NCKD_DEP)
+                .add(PositionStructureConstant.NCKD_JOBLEVELHR)
+                .add(PositionStructureConstant.NCKD_JOBSEQHR)
+                .add(PositionStructureConstant.NCKD_PROTITLELEVEL)
                 .addGroup(new String[]{FormConstant.NCKD_PERSON},FormConstant.ID_KEY);
         //本次需要生效的id
         Set<Long> ids = dataMap.keySet();
@@ -171,5 +178,7 @@ public class AnnualEffectiveOpPlugin extends AbstractOperationServicePlugIn impl
         PositionFileHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
         //将当前人员最新档案(初定 或者 (年度调整 并且 已生效) 或者 (动态调整) 生效时间为最新的那一条)标记为最新的
         PositionFileHelper.markAsCurrentNewest(null,personIds.toArray(new Long[0]));
+        //职位津贴
+        ManagerAllowanceService.generateMgrPostAllow(Arrays.asList(load));
     }
 }

+ 9 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualSetinActiveOpPlugin.java

@@ -20,10 +20,12 @@ import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -122,8 +124,13 @@ public class AnnualSetinActiveOpPlugin extends AbstractOperationServicePlugIn im
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
         QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
+                .add(PositionStructureConstant.NCKD_PERSON)
                 .add(PositionStructureConstant.NCKD_BEGINDATE)
                 .add(PositionStructureConstant.NCKD_ADJUSSTATUS)
+                .add(PositionStructureConstant.NCKD_DEP)
+                .add(PositionStructureConstant.NCKD_JOBLEVELHR)
+                .add(PositionStructureConstant.NCKD_JOBSEQHR)
+                .add(PositionStructureConstant.NCKD_PROTITLELEVEL)
                 .addGroup(new String[]{FormConstant.NCKD_PERSON},FormConstant.ID_KEY);
         //本次需要生效的id
         Set<Long> ids = idMaps.keySet();
@@ -147,5 +154,7 @@ public class AnnualSetinActiveOpPlugin extends AbstractOperationServicePlugIn im
         PositionFileHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
         //将当前人员最新档案(初定 或者 (年度调整 并且 已生效) 或者 (动态调整) 生效时间为最新的那一条)标记为最新的
         PositionFileHelper.markAsCurrentNewest(null,personIds.toArray(new Long[0]));
+        //职位津贴
+        ManagerAllowanceService.expireMgrPostAllow(Arrays.asList(load));
     }
 }

+ 9 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/file/PersonPosFileDeleteOpPlugin.java

@@ -17,10 +17,12 @@ import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -42,6 +44,10 @@ public class PersonPosFileDeleteOpPlugin extends AbstractOperationServicePlugIn
         e.getFieldKeys().add(PositionStructureConstant.NCKD_TYPESTATE);
         e.getFieldKeys().add(PositionStructureConstant.NCKD_ADJUSSTATUS);
         e.getFieldKeys().add(PositionStructureConstant.NCKD_LOCKSTATUS);
+        e.getFieldKeys().add(PositionStructureConstant.NCKD_DEP);
+        e.getFieldKeys().add(PositionStructureConstant.NCKD_JOBLEVELHR);
+        e.getFieldKeys().add(PositionStructureConstant.NCKD_JOBSEQHR);
+        e.getFieldKeys().add(PositionStructureConstant.NCKD_PROTITLELEVEL);
     }
 
     @Override
@@ -131,5 +137,8 @@ public class PersonPosFileDeleteOpPlugin extends AbstractOperationServicePlugIn
         PositionFileHelper.markAsNotCurrentNewest(personIds);
         //将当前人员最新档案(初定 或者 (年度调整 并且 已生效) 或者 (动态调整) 生效时间为最新的那一条)标记为最新的
         PositionFileHelper.markAsCurrentNewest(new QFilter(FormConstant.ID_KEY, QCP.not_in,ids),personIds);
+
+        //失效职位津贴
+        ManagerAllowanceService.expireMgrPostAllow(Arrays.asList(e.getDataEntities()));
     }
 }

+ 2 - 1
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/BaseInitialOperationPlugIn.java

@@ -352,7 +352,7 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
     /**
      * 创建并保存职位档案
      */
-    protected void createAndSavePersonPosFile(BaseInitialData data, ScoreData scoreData, 
+    protected DynamicObject createAndSavePersonPosFile(BaseInitialData data, ScoreData scoreData,
                                              BigDecimal allSumScore, BigDecimal sumScore, 
                                              DynamicObject jobLeve, String logPrefix, String typeState) {
         // 优秀生得2分
@@ -403,6 +403,7 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
 
 
         SaveServiceHelper.save(new DynamicObject[]{personPosFile});
+        return personPosFile;
     }
 
     /**

+ 7 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/NewHireInitialOperationPlugIn.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.hr.psms.plugin.operate.initial;
 
+import com.google.common.collect.Lists;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
@@ -12,6 +13,7 @@ import nckd.jxccl.base.common.exception.ValidationException;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 
 import java.math.BigDecimal;
@@ -138,6 +140,7 @@ public class NewHireInitialOperationPlugIn extends BaseInitialOperationPlugIn {
      */
     private void processNewHireInitialData() {
 
+        List<DynamicObject> savePersonPosFileList = Lists.newArrayList();
         for (BaseInitialData data : baseInitialData) {
 
             String logPrefix = StrFormatter.format("【职位体系】-新入职人员初定-人员【{}({})】",
@@ -180,14 +183,16 @@ public class NewHireInitialOperationPlugIn extends BaseInitialOperationPlugIn {
                     quaLevelNumber,data.downgradeNum,useMinLevel,!threeElementMeet);
             if(jobLeve != null) {
                 // 构建职位档案并存入数据库
-                createAndSavePersonPosFile(data, scoreData, allSumScore, sumScore, jobLeve, logPrefix, "1");
-
+                DynamicObject savePersonPosFile = createAndSavePersonPosFile(data, scoreData, allSumScore, sumScore, jobLeve, logPrefix, "1");
+                savePersonPosFileList.add(savePersonPosFile);
                 //返回定级后的职级
                 setJobLevelResult(data.person, jobLeve);
 
                 //TODO 【待修改】-职位体系-这里可能有协同,初定完成后需要将待办任务置为已完成
             }
         }
+        //职位津贴
+        ManagerAllowanceService.generateMgrPostAllow(savePersonPosFileList);
 
     }
 

+ 7 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/ServingInitialOperationPlugIn.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.hr.psms.plugin.operate.initial;
 
+import com.google.common.collect.Lists;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
@@ -12,6 +13,7 @@ import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
+import nckd.jxccl.hr.psms.business.ManagerAllowanceService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 
@@ -145,6 +147,7 @@ public class ServingInitialOperationPlugIn extends BaseInitialOperationPlugIn {
      * 处理单个在职人员初定
      */
     private void processServingInitial() {
+        List<DynamicObject> savePersonPosFileList = Lists.newArrayList();
         // 提取基本信息
         for (BaseInitialData data : baseInitialData) {
             String logPrefix = StrFormatter.format("【职位体系】-在职人员初定-人员【{}({})】",
@@ -180,12 +183,14 @@ public class ServingInitialOperationPlugIn extends BaseInitialOperationPlugIn {
 
             if(jobLeve != null) {
                 // 构建职位档案并存入数据库
-                createAndSavePersonPosFile(data, scoreData, data.allSumScore, sumScore, jobLeve, logPrefix, "2");
-
+                DynamicObject savePersonPosFile = createAndSavePersonPosFile(data, scoreData, data.allSumScore, sumScore, jobLeve, logPrefix, "2");
+                savePersonPosFileList.add(savePersonPosFile);
                 //返回定级后的职级。张三:职位级为"初级(1)"
                 setJobLevelResult(data.person, jobLeve);
             }
         }
+        //职位津贴
+        ManagerAllowanceService.generateMgrPostAllow(savePersonPosFileList);
     }
 
     @Override

+ 94 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/other/ManagerAllowanceSaveOp.java

@@ -0,0 +1,94 @@
+package nckd.jxccl.hr.psms.plugin.operate.other;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.constant.StatusEnum;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.hrpi.helper.EmpPosOrgRelHelper;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.helper.PositionFileHelper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+* 配置有职位津贴的管理人员-保存op
+* 实体标识:nckd_managerallowance
+* @author W.Y.C
+* @date 2025/12/6 14:44
+* @version 1.0
+*/
+public class ManagerAllowanceSaveOp extends AbstractOperationServicePlugIn implements Plugin {
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().addAll(this.billEntityType.getAllFields().keySet());
+    }
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        //修改和删除不校验是否是暂存状态(只能修改暂存的数据。只能删除暂存的数据。)
+        this.operateOption.setVariableValue("forceNoAudit","true");
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        String variableValue = this.getOption().getVariableValue("importtype", "false");
+        boolean isImport = !variableValue.equalsIgnoreCase("false");
+        List<Long> personIdList = new ArrayList<>();
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            long personId = dataEntity.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            personIdList.add(personId);
+
+        }
+        Map<Long, DynamicObject> empPosOrgRelMap = new HashMap<>();
+        Map<Long, DynamicObject> latestPersonPosFileMap = new HashMap<>();
+        if(!personIdList.isEmpty()){
+            empPosOrgRelMap = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployeesMap(personIdList);
+
+            DynamicObject[] newestPersonPosFileByPerson = PositionFileHelper.getNewestPersonPosFileByPerson(personIdList.toArray(new Long[0]));
+            latestPersonPosFileMap = Arrays.stream(newestPersonPosFileByPerson)
+                    .collect(Collectors.toMap(personPosFile -> personPosFile.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)), Function.identity(), BinaryOperator.maxBy(Comparator.comparing(
+                            personPosFile -> personPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE)))));
+
+        }
+
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            long personId = dataEntity.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            DynamicObject empPosOrgRel = empPosOrgRelMap.get(personId);
+            if(dataEntity.getDynamicObject(FormConstant.NCKD_DEP) == null){
+                dataEntity.set(FormConstant.NCKD_DEP, empPosOrgRel.get(FormConstant.ADMINORG));
+            }
+            if(dataEntity.getDynamicObject(PositionStructureConstant.NCKD_POSITIONHR) == null){
+                dataEntity.set(PositionStructureConstant.NCKD_POSITIONHR, empPosOrgRel.get(FormConstant.POSITION_KEY));
+            }
+            if(isImport){
+                //导入的默认未“已确认”
+                dataEntity.set(FormConstant.STATUS, StatusEnum.B.toString());
+            }
+            DynamicObject personPosFile = latestPersonPosFileMap.get(personId);
+            if(personPosFile != null){
+                if(dataEntity.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR) == null){
+                    dataEntity.set(PositionStructureConstant.NCKD_JOBLEVELHR, personPosFile.get(PositionStructureConstant.NCKD_JOBLEVELHR));
+                }
+                if(dataEntity.getDynamicObject(PositionStructureConstant.NCKD_PROTITLELEVEL) == null){
+                    dataEntity.set(PositionStructureConstant.NCKD_PROTITLELEVEL, personPosFile.get(PositionStructureConstant.NCKD_PROTITLELEVEL));
+                }
+            }
+        }
+    }
+}