Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master'

jtd před 6 dny
rodič
revize
35b13c4a5c
47 změnil soubory, kde provedl 3531 přidání a 551 odebrání
  1. 25 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  2. 14 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/QueryConstant.java
  3. 0 13
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/SystemQueryConstant.java
  4. 101 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/plugins/LoadMultiOrgByEmpPosOrgRelListPlugin.java
  5. 10 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/DateUtil.java
  6. 21 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/hrpi/helper/EmpPosOrgRelHelper.java
  7. 22 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PerfRankMgmtConstant.java
  8. 1 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java
  9. 214 20
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java
  10. 8 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/adjust/NewDynamicAdjustmentOperationPlugIn.java
  11. 212 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/PerfRankMgmtSaveOpPlugin.java
  12. 126 47
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/validate/PerfRankMgmtSaveValidate.java
  13. 8 1
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportReportListDataPlugin.java
  14. 14 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/common/PerfManagerFormConstant.java
  15. 266 60
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/helper/PerfManagerHelper.java
  16. 34 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfmanagerQueryListPlugin.java
  17. 264 63
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/BatchEvalCycleFormPlugin.java
  18. 0 14
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/PerfManagerFormPlugin.java
  19. 48 31
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/PerfManagerListPlugin.java
  20. 39 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailFormPlugin.java
  21. 230 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailReportListDataPlugin.java
  22. 35 16
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/CycleGenerateOpPlugin.java
  23. 3 3
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerDeleteOpPlugin.java
  24. 439 111
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerSaveOpPlugin.java
  25. 186 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/result/UpdateResultOpPlugin.java
  26. 16 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordBizExtRegister.java
  27. 104 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordBizSaveHandler.java
  28. 156 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/datacomparison/DataComparisonPushService.java
  29. 10 10
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/datacomparison/DataComparisonQueryService.java
  30. 7 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/common/constant/SitConstant.java
  31. 218 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/coordination/SinSurEmpChgCoordBatchEditPluginEx.java
  32. 99 7
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/datacomparison/DetailCompareBillEdit.java
  33. 1 1
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/SinsurTemplateEdit.java
  34. 13 3
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/SitItemSelectAddItemPlugin.java
  35. 25 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/helper/SinsurTemplateHelper.java
  36. 41 10
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/helper/SitItemSelectAddItemServiceHelper.java
  37. 27 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/opplugin/web/coordination/SinsurEmpConfirmOpEx.java
  38. 9 3
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/utils/ReportUtils.java
  39. 27 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/validate/web/SinsurEmpCoordBillValidator.java
  40. 14 21
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hcdm/business/annualincome/AnnualIncomeService.java
  41. 77 13
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hcdm/formplugin/annualincome/SalAnnualIncomeBillEdit.java
  42. 23 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hpdi/business/coordination/api/SwcCoordBizExtRegister.java
  43. 56 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hpdi/business/coordination/api/SwcCoordBizSaveHandler.java
  44. 4 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hsbs/formplugin/web/basedata/salarygroup/PerSalaryGroupEdit.java
  45. 9 4
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/business/CalResultCoverSalaryItemExtPlugin.java
  46. 184 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/plugin/form/PendingSalaryAdjListPlugin.java
  47. 91 91
      code/wtc/nckd-jxccl-wtc/src/main/java/nckd/jxccl/wtc/task/SyncPunchCardTask.java

+ 25 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -8,6 +8,11 @@ package nckd.jxccl.base.common.constant;
  */
 public class FormConstant {
 
+    /** 数迁参数 */
+    public static final String HR_INVOKER_PARAM_INVOKER = "hrInvokerParam#invoker";
+    public static final String DATA_MIGRATION = "dataMigration";
+
+
     //====================================== 标品实体标识(需要小写) ======================================
     /**学历-实体标识*/
     public static final String HBSS_DIPLOMA = "hbss_diploma";
@@ -101,6 +106,8 @@ public class FormConstant {
     public static final String CHKINCLUDECHILD = "chkincludechild";
     /** 单据体 */
     public static final String NCKD_ENTRYENTITY = "nckd_entryentity";
+    /** 向导控件 */
+    public static final String NCKD_WIZARDAP = "nckd_wizardap";
 
     //====================================== 通用字段 ======================================
     /** BOID标识 */
@@ -264,5 +271,23 @@ public class FormConstant {
     public static final String NCKD_ISCURRENTNEWEST = "nckd_iscurrentnewest";
     /** 部门*/
     public static final String NCKD_DEP = "nckd_dep";
+    /** 任职经历 */
+    public static final String NCKD_EMPPOSORGREL = "nckd_empposorgrel";
+
+    /** 一级组织 */
+    public static final String NCKD_FIRSTORG = "nckd_firstorg";
+    /** 二级组织 */
+    public static final String NCKD_SECONDORG = "nckd_secondorg";
+    /** 三级组织 */
+    public static final String NCKD_THIRDORG = "nckd_thirdorg";
+    /** 四级组织 */
+    public static final String NCKD_FOURTHORG = "nckd_fourthorg";
+    /** 五级组织 */
+    public static final String NCKD_FIFTHORG = "nckd_fifthorg";
+    /** 六级组织 */
+    public static final String NCKD_SIXTHORG = "nckd_sixthorg";
+    /** 工作性质 */
+    public static final String NCKD_WORKNATURE = "nckd_worknature";
+
 
 }

+ 14 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/QueryConstant.java

@@ -0,0 +1,14 @@
+package nckd.jxccl.base.common.constant;
+
+/**
+ * TODO
+ *
+ * @author W.Y.C
+ * @version 1.0
+ * @date 2025/11/19 17:29
+ */
+public class QueryConstant {
+
+    /**人员列表*/
+    public static final String PERSON_QUERY = "personquery";
+}

+ 0 - 13
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/SystemQueryConstant.java

@@ -1,13 +0,0 @@
-package nckd.jxccl.base.common.constant;
-
-/**
- * 系统内置常量
- * @author W.Y.C
- * @date 2025/10/20 16:05
- * @version 1.0
- */
-public class SystemQueryConstant {
-
-    /**人员列表*/
-    public static final String HSPM_ASSIGNMENTQUERY = "hspm_assignmentquery";
-}

+ 101 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/plugins/LoadMultiOrgByEmpPosOrgRelListPlugin.java

@@ -0,0 +1,101 @@
+package nckd.jxccl.base.common.plugins;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.datamodel.events.PackageDataEvent;
+import kd.bos.form.IPageCache;
+import kd.bos.form.events.BeforeCreateListDataProviderArgs;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.mvc.list.ListDataProvider;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 根据任职经历加载多级组织通用插件
+ * 实体有任职经历需要在列表显示出1~6级组织
+ * 列表需要有动态字段:nckd_firstorg、nckd_secondorg、nckd_thirdorg、nckd_fourthorg、nckd_fifthorg、nckd_sixthorg
+ * @author W.Y.C
+ * @date 2025/11/4 13:07
+ * @version 1.0
+ */
+public class LoadMultiOrgByEmpPosOrgRelListPlugin extends AbstractListPlugin implements Plugin {
+
+    Map<Long, DynamicObject> map = null;
+    @Override
+    public void packageData(PackageDataEvent e) {
+        if(!adminOrgIds.isEmpty() && map == null) {
+            QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                    .add(FormConstant.ID_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);
+            QFilter qFilter = new QFilter(FormConstant.ID_KEY, QCP.in, adminOrgIds);
+            DynamicObjectCollection adminOrgColl = QueryServiceHelper.query(FormConstant.ADMINORGHR_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter});
+            map = adminOrgColl.stream()
+                    .collect(Collectors.toMap(
+                            obj -> obj.getLong(FormConstant.ID_KEY),
+                            obj -> obj
+                    ));
+        }
+        if(map != null) {
+            String colKey = e.getColKey();
+            if (e.getRowData().containsProperty(FormConstant.NCKD_EMPPOSORGREL)) {
+                DynamicObject adminOrg = e.getRowData().getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.ADMINORG);
+                if (adminOrg != null) {
+                    DynamicObject org = map.get(adminOrg.getLong(FormConstant.ID_KEY));
+                    if (FormConstant.NCKD_FIRSTORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_FIRSTORG, FormConstant.NAME_KEY)));
+                    } else if (FormConstant.NCKD_SECONDORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_SECONDORG, FormConstant.NAME_KEY)));
+                    } else if (FormConstant.NCKD_THIRDORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_THIRDORG, FormConstant.NAME_KEY)));
+                    } else if (FormConstant.NCKD_FOURTHORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_FOURTHORG, FormConstant.NAME_KEY)));
+                    } else if (FormConstant.NCKD_FIFTHORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_FIFTHORG, FormConstant.NAME_KEY)));
+                    } else if (FormConstant.NCKD_SIXTHORG.equalsIgnoreCase(colKey)) {
+                        e.setFormatValue(org.getString(String.join(".", FormConstant.NCKD_SIXTHORG, FormConstant.NAME_KEY)));
+                    }
+                }
+            }
+        }
+
+
+    }
+
+    Set<Long> adminOrgIds = new HashSet<>();
+    @Override
+    public void beforeCreateListDataProvider(BeforeCreateListDataProviderArgs args) {
+        args.setListDataProvider(new ListDataProvider() {
+            @Override
+            public DynamicObjectCollection getData(int start, int size) {
+                DynamicObjectCollection rows = super.getData(start, size);
+                IPageCache pageCache = getView().getPageCache();
+                for (DynamicObject row : rows) {
+                    if(row.containsProperty(FormConstant.NCKD_EMPPOSORGREL)){
+                        DynamicObject empPosOrgRel = row.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL);
+                        if(empPosOrgRel != null){
+                            DynamicObject adminOrg = empPosOrgRel.getDynamicObject(FormConstant.ADMINORG);
+                            if(adminOrg != null) {
+                                adminOrgIds.add(adminOrg.getLong(FormConstant.ID_KEY));
+                            }
+                        }
+                    }
+                }
+                return rows;
+            }
+        });
+    }
+}

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

@@ -57,6 +57,16 @@ public class DateUtil {
         return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
     }
 
+    /**
+     * 转换为Date对象
+     *
+     * @param localDate LocalDate
+     * @return 对应的Date对象
+     */
+    public static Date toDate(LocalDate localDate) {
+        return  Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+    }
+
     /**
      * 转换为LocalDateTime对象
      *

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

@@ -20,8 +20,28 @@ import java.util.Collection;
  * @date 2025/11/4 15:33
  * @version 1.0
  */
-public class EmpPosOrgRelHelper implements Plugin {
+public class EmpPosOrgRelHelper {
 
+    //根据id查询任职经历
+    public static DynamicObject[] queryEmpPosOrgRelById(Collection<Long> ids) {
+        // 构建基础查询条件
+        QFilter queryFilter = new QFilter(FormConstant.ID_KEY, QCP.in, ids);
+
+        // 构建查询字段和排序规则
+        QueryFieldBuilder queryFieldBuilder = selectProperties();
+        queryFieldBuilder.addIdNumberName(FormConstant.POSITION_KEY,FormConstant.NCKD_WORKNATURE);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FIRSTORG);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_SECONDORG);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_THIRDORG);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FOURTHORG);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FIFTHORG);
+        queryFieldBuilder.addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_SIXTHORG);
+        String selectProperties = queryFieldBuilder.buildSelect();
+        String order = selectProperties().buildOrder();
+
+        // 执行查询并返回结果
+        return BusinessDataServiceHelper.load(FormConstant.HRPI_EMPPOSORGREL, selectProperties, new QFilter[]{queryFilter}, order);
+    }
     /**
      * 根据员工ID查询最新的任职经历信息
      *

+ 22 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PerfRankMgmtConstant.java

@@ -48,6 +48,8 @@ public class PerfRankMgmtConstant extends FormConstant {
     public static final String NCKD_GETRANKLIST = "nckd_getranklist";
     /** 获取排名名单-OP */
     public static final String GETRANKLIST_OP = "getranklist";
+    /** 步骤 */
+    public static final String NCKD_STEP = "nckd_step";
 
 
 
@@ -78,4 +80,24 @@ public class PerfRankMgmtConstant extends FormConstant {
     /** 降级百分比阈值 */
     public static final String NCKD_DOWNGRADETHRESHOLD = "nckd_downgradethreshold";
     /*-------------------------------------- 升保降级条件配置 begin --------------------------------------*/
+
+
+    /*-------------------------------------- 考核周期(人员考评) begin --------------------------------------*/
+    /** 周期开始年份 */
+    public static final String NCKD_BEGINYEAR = "nckd_beginyear";
+    /** 周期结束年份 */
+    public static final String NCKD_ENDYEAR = "nckd_endyear";
+    /** 人员考评管理实体名称 */
+    public static final String PERFMANAGER_ENTITYID = "nckd_perfmanager";
+    /** 人员考评管理分录实体名称 */
+    public static final String NCKD_PERFMANAGERENTRY = "nckd_perfmanagerentry";
+    /** 分录-考核年份 */
+    public static final String NCKD_APPRAISALYEAR = "nckd_appraisalyear";
+    /** 分录-是否来源全排名 */
+    public static final String NCKD_ISALLRANKSOURCE = "nckd_isallranksource";
+    /** 实际周期结束年份 */
+    public static final String NCKD_ACTENDYEAR = "nckd_actendyear";
+    /** 周期状态 */
+    public static final String NCKD_THESTATUS = "nckd_thestatus";
+    /*-------------------------------------- 考核周期(人员考评) end --------------------------------------*/
 }

+ 1 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java

@@ -160,8 +160,7 @@ public class PositionStructureConstant extends FormConstant {
     public static final String NCKD_JOBSEQ = "nckd_jobseq";
     /** 优秀生 */
     public static final String NCKD_EXCELLENT = "nckd_excellent";
-    /** 任职经历 */
-    public static final String NCKD_EMPPOSORGREL = "nckd_empposorgrel";
+
     /*-------------------------------------- 在职人员初定(弹窗) end --------------------------------------*/
 
 

+ 214 - 20
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfrankMgmtFormPlugin.java → code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java

@@ -11,47 +11,47 @@ import kd.bos.entity.QueryEntityType;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.datamodel.IDataModel;
 import kd.bos.entity.datamodel.RowDataEntity;
-import kd.bos.entity.datamodel.events.AfterAddRowEventArgs;
-import kd.bos.entity.datamodel.events.BeforeImportEntryEventArgs;
-import kd.bos.entity.datamodel.events.ChangeData;
-import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.entity.datamodel.events.*;
 import kd.bos.ext.hr.service.query.QueryEntityHelper;
 import kd.bos.form.ConfirmCallBackListener;
 import kd.bos.form.ConfirmTypes;
 import kd.bos.form.IClientViewProxy;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.MessageBoxResult;
+import kd.bos.form.container.Wizard;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.control.Steps;
+import kd.bos.form.control.events.StepEvent;
+import kd.bos.form.control.events.WizardStepsListener;
 import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.operate.FormOperate;
+import kd.bos.form.plugin.AbstractFormPlugin;
 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.plugin.sample.dynamicform.pcform.form.template.ConfirmCallBack;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
-import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
-import nckd.jxccl.base.common.constant.SystemQueryConstant;
+import nckd.jxccl.base.common.constant.QueryConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.enums.psms.JobSeqEnum;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.hrpi.helper.EmpPosOrgRelHelper;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
+import org.apache.commons.lang3.StringUtils;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -62,17 +62,31 @@ import java.util.stream.Stream;
 * @date 2025/10/20 15:11
 * @version 1.0
 */
-public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
+public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements WizardStepsListener {
 
-    private static final Log logger = LogFactory.getLog(PerfrankMgmtFormPlugin.class);
+    private static final Log logger = LogFactory.getLog(PerfRankMgmtFormPlugin.class);
 
     private final static String CALLBACK_ID = "interrupt_confirm";
 
+    @Override
+    public void registerListener(EventObject e) {
+        Wizard wizard = this.getControl(FormConstant.NCKD_WIZARDAP);
+        wizard.addWizardStepsListener(this);
+    }
+
+
     @Override
     public void afterBindData(EventObject e) {
         sortEntry();
         // 该单据不需要审批,默认为暂存状态
         this.getModel().setValue(FormConstant.STATUS, StatusEnum.A.toString());
+        String step = ConvertUtil.toStr(this.getModel().getValue(PerfRankMgmtConstant.NCKD_STEP));
+        if("1".equals(step)){
+            setStepStatus(0,Steps.FINISH);
+            setStepStatus(1,Steps.PROCESS);
+
+            importResultStep();
+        }
     }
 
     @Override
@@ -193,12 +207,98 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
                     );
                 }
             }
-        }else if(Arrays.asList(FormConstant.SAVE_OP, FormConstant.SUBMIT_OP).contains(itemKey)){
+        } else if(Arrays.asList(FormConstant.SAVE_OP, FormConstant.SUBMIT_OP).contains(itemKey)){
             //保存或提交先计算一遍
             // calcRankCount();
+        } else if(FormConstant.DELETEENTRY_OP.equalsIgnoreCase(itemKey)){
+            //校验人员考评,若考评周期已结束,则禁止删除。
+            EntryGrid entryGrid = getView().getControl(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+            if (entryGrid != null) {
+                Integer theYear = ConvertUtil.toInt(this.getModel().getValue(PerfRankMgmtConstant.NCKD_THEYEAR));
+                LocalDate startDate = LocalDate.of(theYear, 1, 1);
+                // 获取选中行索引数组
+                int[] selectedRows = entryGrid.getSelectRows();
+                List<Long> personIds = new ArrayList<>();
+                if (selectedRows.length > 0) {
+                    for (int rowIndex : selectedRows) {
+                        DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                        DynamicObject rowData = entryEntity.get(rowIndex);
+                        DynamicObject person = rowData.getDynamicObject(FormConstant.NCKD_PERSON);
+                        long personId = person.getLong(FormConstant.ID_KEY);
+                        personIds.add(personId);
+                    }
+
+                    if(!personIds.isEmpty()){
+                        DynamicObject[] byDate = getByDate(DateUtil.toDate(startDate), personIds);
+                        for (DynamicObject dynamicObject : byDate) {
+                            String theStatus = dynamicObject.getString(PerfRankMgmtConstant.NCKD_THESTATUS);
+                            DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
+                            if(!EnableEnum.YES.getCode().equals(theStatus)){
+                                DynamicObjectCollection entryList = dynamicObject.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFMANAGERENTRY);
+                                for (DynamicObject object : entryList) {
+                                    LocalDateTime appraisalYear = DateUtil.toLocalDateTime(object.getDate(PerfRankMgmtConstant.NCKD_APPRAISALYEAR));
+                                    int year = appraisalYear.getYear();
+                                    if(year == theYear){
+                                        this.getView().showTipNotification(StrFormatter.format("所选人员【{}】考评周期【{}】,不能删除",person.getString(FormConstant.NAME_KEY),"2".equals(theStatus) ? "已锁定":"已结束"));
+                                        args.setCancel(true);
+                                    }
+                                }
+
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 
+
+    /**
+     * 根据日期查询范围内的考核周期
+     * 查询逻辑说明:
+     * 1. 开始时间条件:考核周期的开始年份(NCKD_BEGINYEAR)必须小于等于指定日期
+     * 2. 结束时间条件:优先使用实际结束年份(NCKD_ACTENDYEAR),如果为空则使用计划结束年份(NCKD_ENDYEAR)
+     * 3. 结束时间必须大于等于指定日期
+     * 4. 即查找满足 "NCKD_BEGINYEAR <= date <= (NCKD_ACTENDYEAR为空时取NCKD_ENDYEAR)" 条件的考核周期
+     *
+     * @param date      指定查询日期
+     * @param personIds 人员ID集合
+     * @return 符合条件的考核周期数组
+     * @author W.Y.C
+     * @date 2025/11/4 17:06
+     */
+    public static DynamicObject[] getByDate(Date date, Collection<Long> personIds) {
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .addIdNumberName(FormConstant.NCKD_EMPPOSORGREL)
+                .addIdNumberName(FormConstant.NCKD_PERSON)
+                .add(PerfRankMgmtConstant.NCKD_THESTATUS)
+                .addGroup(new String[]{PerfRankMgmtConstant.NCKD_PERFMANAGERENTRY},
+                        PerfRankMgmtConstant.NCKD_APPRAISALRESULT,
+                        PerfRankMgmtConstant.NCKD_APPRAISALYEAR,
+                        PerfRankMgmtConstant.NCKD_ISALLRANKSOURCE);
+
+        // 构造查询条件:日期在[NCKD_BEGINYEAR, NCKD_ENDYEAR]范围内
+        // 考虑到NCKD_ACTENDYEAR可能为空,优先使用NCKD_ACTENDYEAR作为结束时间
+        QFilter filter = new QFilter(PerfRankMgmtConstant.NCKD_BEGINYEAR, QCP.less_equals, date)
+                .and(new QFilter(PerfRankMgmtConstant.NCKD_PERSON, QCP.in, personIds));
+
+        // 添加结束时间判断逻辑
+        QFilter endDateFilter = new QFilter(PerfRankMgmtConstant.NCKD_ACTENDYEAR, QCP.large_equals, date)
+                .or(new QFilter(PerfRankMgmtConstant.NCKD_ACTENDYEAR, QCP.is_null, null)
+                        .and(new QFilter(PerfRankMgmtConstant.NCKD_ENDYEAR, QCP.large_equals, date)));
+
+        filter.and(endDateFilter);
+
+        return BusinessDataServiceHelper.load(PerfRankMgmtConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+    }
+
+    @Override
+    public void beforeDeleteRow(BeforeDeleteRowEventArgs e) {
+        super.beforeDeleteRow(e);
+    }
+
     @Override
     public void confirmCallBack(MessageBoxClosedEvent evt) {
         super.confirmCallBack(evt);
@@ -249,6 +349,7 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
                     //在职人员
                     qFilter.and(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_HIRED), QCP.equals, EnableEnum.YES.getCode());
                     QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                            .addGroup(new String[]{FormConstant.HRPI_EMPPOSORGREL}, FormConstant.ID_KEY)
                             //用工关系状态.编码(1005_S	在职-试用中、1010_S	在职、1020_S	离职、1030_S	已退休),istrial:是否试用,ishired:是否在职
                             .addGroup(new String[]{FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS}, FormConstant.NUMBER_KEY, FormConstant.NAME_KEY, FormConstant.IS_TRIAL, FormConstant.IS_HIRED)
                             //任职状态分类(1010_S:在岗,1030_S:不在岗)
@@ -260,7 +361,7 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
                             .addGroup(new String[]{FormConstant.EMPLOYEE_KEY}, FormConstant.ID_KEY, FormConstant.NAME_KEY, FormConstant.EMP_NUMBER_KEY);
 
                     // -------------------------------- 1、查询组织下的在职人员 --------------------------------
-                    QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(SystemQueryConstant.HSPM_ASSIGNMENTQUERY);
+                    QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(QueryConstant.PERSON_QUERY);
                     DynamicObjectCollection personList = QueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder());
                     //添加分录
                     DynamicObjectCollection entryEntityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
@@ -279,18 +380,38 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
                         Map<Long, Boolean> entitledToAllowance = isEntitledToAllowance(personIds, now);
 
                         // -------------------------------- 4、加载数据到分录 --------------------------------
+                        //预加载任职经历
+                        List<Long> ids = personList.stream()
+                                .map(person -> person.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY)))
+                                .collect(Collectors.toList());
+                        DynamicObject[] empPosOrgRelArray = EmpPosOrgRelHelper.queryEmpPosOrgRelById(ids);
+                        Map<Long, DynamicObject> empPosOrgRelMap = Arrays.stream(empPosOrgRelArray)
+                                .filter(Objects::nonNull)
+                                .collect(Collectors.toMap(
+                                        obj -> obj.getLong(FormConstant.ID_KEY),
+                                        obj -> obj,
+                                        (existing, replacement) -> existing
+                                ));
                         for (DynamicObject person : personList) {
                             DynamicObject entryCol = entryEntityCols.addNew();
                             long personId = person.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+                            long empPosOrgRelId = person.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY));
                             String personName = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.NAME_KEY));
                             String personNumber = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.EMP_NUMBER_KEY));
 
+                            DynamicObject empPosOrgRel = empPosOrgRelMap.get(empPosOrgRelId);
+                            entryCol.set(FormConstant.NCKD_EMPPOSORGREL, empPosOrgRel);
+
+
                             DynamicObjectType type = EntityMetadataCache.getDataEntityType(FormConstant.HRPI_EMPLOYEE);
                             DynamicObject personObj = (DynamicObject) type.createInstance();
                             personObj.set(FormConstant.ID_KEY, personId);
                             personObj.set(FormConstant.NAME_KEY, personName);
                             personObj.set(FormConstant.EMP_NUMBER_KEY, personNumber);
                             entryCol.set(FormConstant.NCKD_PERSON, personObj);
+
+
+
                             //是否试用
                             boolean isTrial = person.getBoolean(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_TRIAL));
                             //入职日期
@@ -312,13 +433,27 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
                             entryCol.set(PerfRankMgmtConstant.NCKD_POSTALLOWANCE, hasAllowance);
 
                         }
+                        this.getModel().updateEntryCache(entryEntityCols);
+                        this.getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
                         this.getView().showSuccessNotification("名单获取完成");
                     } else {
                         this.getView().showTipNotification("未获取到人员");
                     }
                 }
+            /* }*/
+        }else if(PerfRankMgmtConstant.SAVE_OP.equalsIgnoreCase(itemKey)){
+            if(afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess()){
+                String wizadap = this.getView().getPageCache().get(FormConstant.NCKD_WIZARDAP);
+                if(StringUtils.isBlank(wizadap) || "0".equals(wizadap)){
+                    setStepStatus(0,Steps.FINISH);
+                    setStepStatus(1,Steps.PROCESS);
+                    //控制显示隐藏
+                    importResultStep();
+                }else {
+                    this.getView().showSuccessNotification("同步考核周期成功。");
+                }
             }
-       /* }*/
+        }
         if(Arrays.asList(FormConstant.DELETEENTRY_OP, PerfRankMgmtConstant.GETRANKLIST_OP).contains(itemKey)){
             sortEntry();
             calcRankCount();
@@ -326,6 +461,26 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
         // this.getView().setStatus(OperationStatus.EDIT);
     }
 
+    private void importResultStep() {
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_STEP,1);
+        this.getView().setVisible(false, FormConstant.NUMBER_KEY, PerfRankMgmtConstant.NCKD_GETRANKLIST);
+        this.getView().setVisible(true, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6");
+        DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+        for (int i = 0; i < entryEntity.size(); i++) {
+            this.getView().setEnable(Boolean.FALSE, i, FormConstant.NCKD_EMPPOSORGREL);
+        }
+    }
+
+    private void generatePersonList() {
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_STEP,0);
+        this.getView().setVisible(true, FormConstant.NUMBER_KEY, PerfRankMgmtConstant.NCKD_GETRANKLIST);
+        this.getView().setVisible(false, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6");
+        DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+        for (int i = 0; i < entryEntity.size(); i++) {
+            this.getView().setEnable(Boolean.TRUE, i, FormConstant.NCKD_EMPPOSORGREL);
+        }
+    }
+
     /**
      * 判断人员是否享受职位津贴
      * @param personIds 人员ID列表
@@ -528,4 +683,43 @@ public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
         //优秀人数
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_EXCELLENTS, excellentCount);
     }
+
+
+    /**
+     * 设置步骤状态
+     * @param step 步骤索引(从0开始)
+     * @param stepStatus 步骤状态 {@link Steps}
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/06/14 20:12
+     */
+    private void setStepStatus(int step,String stepStatus) {
+        Wizard wizard = this.getControl(FormConstant.NCKD_WIZARDAP);
+        Map<String, Object> currentStepMap = new HashMap<>();
+        currentStepMap.put("currentStep", step);
+        currentStepMap.put("currentStatus", stepStatus);
+        wizard.setWizardCurrentStep(currentStepMap);
+        this.getView().getPageCache().put(FormConstant.NCKD_WIZARDAP, step+"");
+    }
+
+    @Override
+    public void update(StepEvent stepEvent) {
+        Wizard wizard = this.getControl(FormConstant.NCKD_WIZARDAP);
+        if(stepEvent.getValue() == 0){
+            generatePersonList();
+            setStepStatus(0, Steps.PROCESS);
+        }else{
+            //校验有没有保存
+            DynamicObject dataEntity = this.getModel().getDataEntity();
+            Object pkValue = dataEntity.getPkValue();
+            if(pkValue == null || pkValue.toString().equals("0")){
+                this.getView().showTipNotification("请先生成名单并保存后再进行下一步!");
+            }else {
+                importResultStep();
+                setStepStatus(1, Steps.PROCESS);
+            }
+        }
+        this.getView().updateView(PerfRankMgmtConstant.NCKD_STEP);
+        this.getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+    }
 }

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

@@ -9,6 +9,7 @@ import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
 import kd.bos.entity.validate.AbstractValidator;
 import kd.bos.logging.Log;
@@ -148,10 +149,15 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
     }
 
     @Override
-    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
         if(!this.getOperationResult().isSuccess()){
-            return;
+            e.setCancel(true);
         }
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+
         logger.info("【职位体系】-动态调整-开始");
         List<Long> personIds = new ArrayList<>(e.getDataEntities().length);
         List<DynamicObject> newPersonPosFiles = new ArrayList<>(e.getDataEntities().length);

+ 212 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/PerfRankMgmtSaveOpPlugin.java

@@ -1,10 +1,40 @@
 package nckd.jxccl.hr.psms.plugin.operate.performance;
 
+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.entity.MainEntityType;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
 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.plugin.args.EndOperationTransactionArgs;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+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.exception.ValidationException;
+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.plugin.operate.performance.validate.PerfRankMgmtSaveValidate;
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
 * 年度绩效排名管理-保存插件
@@ -14,15 +44,195 @@ import nckd.jxccl.hr.psms.plugin.operate.performance.validate.PerfRankMgmtSaveVa
 */
 public class PerfRankMgmtSaveOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
 
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().addAll(this.billEntityType.getAllFields().keySet());
+    }
+
     @Override
     public void onAddValidators(AddValidatorsEventArgs e) {
         e.addValidator(new PerfRankMgmtSaveValidate());
     }
 
+    private Map<Long,List<Long>> dbPersonMap = new HashMap<>();
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        String invoker = (String)this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        boolean dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker);
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+
+            //事务开始前先查询数据库中的排名名单,找出哪些是此次被删除的人员
+            QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                    .addIdNumberName(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,FormConstant.NCKD_PERSON);
+            QFilter qFilter = new QFilter(FormConstant.ID_KEY, QCP.equals, dataEntity.getLong(FormConstant.ID_KEY));
+            DynamicObjectCollection dbEntryList = QueryServiceHelper.query(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter});
+
+            // 获取数据库中存在的人员ID列表
+            List<Long> dbPersonIds = dbEntryList.stream()
+                    .map(dbEntry -> dbEntry.getLong(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,FormConstant.NCKD_PERSON, FormConstant.ID_KEY)))
+                    .collect(Collectors.toList());
+            dbPersonMap.put(dataEntity.getLong(FormConstant.ID_KEY), dbPersonIds);
+
+            List<Long> personids = new ArrayList<>();
+            DynamicObjectCollection entryList = dataEntity.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+            for (DynamicObject entry : entryList) {
+                DynamicObject person = entry.getDynamicObject(FormConstant.NCKD_PERSON);
+                DynamicObject empPosOrgRel = entry.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL);
+                if(person == null && empPosOrgRel != null){
+                    entry.set(FormConstant.NCKD_PERSON, empPosOrgRel.getDynamicObject(FormConstant.EMPLOYEE_KEY));
+                }
+                person = entry.getDynamicObject(FormConstant.NCKD_PERSON);
+                if(person != null) {
+                    personids.add(person.getLong(FormConstant.ID_KEY));
+                }
+            }
+            if(dataMigration){
+                //数据迁移逻辑
+                DynamicObject[] dbEmpPosOrgRelList = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployees(personids);
+                for (DynamicObject entry : entryList) {
+                    DynamicObject person = entry.getDynamicObject(FormConstant.NCKD_PERSON);
+                    DynamicObject empPosOrgRel = entry.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL);
+                    if(empPosOrgRel == null){
+                        long personId = person.getLong(FormConstant.ID_KEY);
+                        for (DynamicObject ebEmpPosOrgRel : dbEmpPosOrgRelList) {
+                            long employeeId = ebEmpPosOrgRel.getDynamicObject(FormConstant.EMPLOYEE_KEY).getLong(FormConstant.ID_KEY);
+                            if(Objects.equals(personId, employeeId)){
+                                entry.set(FormConstant.NCKD_EMPPOSORGREL, ebEmpPosOrgRel);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    }
 
     @Override
     public void endOperationTransaction(EndOperationTransactionArgs e) {
-        //TODO 【待修改】-职位体系-全排名同步到考核周期
-        super.endOperationTransaction(e);
+        //-------------------------------- 同步人员考评 begin --------------------------------
+        //先查询人员对应考核周期
+        for (DynamicObject data : e.getDataEntities()) {
+            int theYear = data.getInt(PerfRankMgmtConstant.NCKD_THEYEAR);
+            int step = data.getInt(PerfRankMgmtConstant.NCKD_STEP);
+            //当步骤为1时同步考核周期结果
+            if(step == 1) {
+                DynamicObjectCollection entryList = data.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+
+                List<Long> personIds = entryList.stream()
+                        .map(entry -> entry.getDynamicObject(FormConstant.NCKD_PERSON))
+                        .filter(Objects::nonNull)
+                        .map(person -> person.getLong(FormConstant.ID_KEY))
+                        .filter(id -> id > 0)
+                        .collect(Collectors.toList());
+
+                Map<Long, DynamicObject> personAppraisalMap = entryList.stream()
+                        .filter(entry -> Objects.nonNull(entry.getDynamicObject(FormConstant.NCKD_PERSON)))
+                        .filter(entry -> Objects.nonNull(entry.getDynamicObject(PerfRankMgmtConstant.NCKD_APPRAISALRESULT)))
+                        .collect(Collectors.toMap(
+                                entry -> entry.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY),
+                                entry -> entry.getDynamicObject(PerfRankMgmtConstant.NCKD_APPRAISALRESULT)
+                        ));
+
+
+                List<Long> dbPersonIds = dbPersonMap.get(data.getLong(FormConstant.ID_KEY));
+                // 找出在数据库中存在但在当前列表中不存在的人员ID(即被删除的人员)
+                List<Long> deletedPersonIds = dbPersonIds.stream()
+                        .filter(dbPersonId -> !personIds.contains(dbPersonId))
+                        .collect(Collectors.toList());
+                if(!deletedPersonIds.isEmpty()){
+                    personIds.addAll(deletedPersonIds);
+                }
+                //获取考核周期
+                List<Long> perfManagerIds = findPerfManager(theYear, personIds);
+                QFilter filter = QFilterCommonHelper.getIdInFilter(perfManagerIds);
+                MainEntityType dataEntityType = MetadataServiceHelper.getDataEntityType(PerfRankMgmtConstant.PERFMANAGER_ENTITYID);
+                DynamicObject[] perfManagerArray = BusinessDataServiceHelper.load(perfManagerIds.toArray(new Long[0]), dataEntityType);
+                LocalDate appraisalYearLocalDate = LocalDate.of(theYear, 1, 1);
+                Date appraisalYearDate = DateUtil.toDate(appraisalYearLocalDate);
+
+                for (DynamicObject perfManager : perfManagerArray) {
+                    DynamicObject person = perfManager.getDynamicObject(FormConstant.NCKD_PERSON);
+                    long personId = person.getLong(FormConstant.ID_KEY);
+                    if(deletedPersonIds.contains(personId)){
+                        //此次删除的人员同步删除人员考评的考核结果
+                        DynamicObjectCollection perfManagerEntryColl = perfManager.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFMANAGERENTRY);
+                        Iterator<DynamicObject> iterator = perfManagerEntryColl.iterator();
+                        while (iterator.hasNext()) {
+                            DynamicObject perfManagerEntry = iterator.next();
+                            LocalDateTime appraisalYear = DateUtil.toLocalDateTime(perfManagerEntry.getDate(PerfRankMgmtConstant.NCKD_APPRAISALYEAR));
+                            int year = appraisalYear.getYear();
+                            if(year == theYear){
+                                iterator.remove();
+                            }
+                        }
+                    }else{
+                        DynamicObjectCollection perfManagerEntryColl = perfManager.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFMANAGERENTRY);
+                        boolean isExist = false;
+                        for (DynamicObject perfManagerEntry : perfManagerEntryColl) {
+                            LocalDateTime appraisalYear = DateUtil.toLocalDateTime(perfManagerEntry.getDate(PerfRankMgmtConstant.NCKD_APPRAISALYEAR));
+                            int year = appraisalYear.getYear();
+                            if(year == theYear){
+                                perfManagerEntry.set(PerfRankMgmtConstant.NCKD_APPRAISALRESULT, personAppraisalMap.get(personId));
+                                isExist = true;
+                                break;
+                            }
+                        }
+                        if(!isExist){
+                            DynamicObject perfManagerEntry = perfManagerEntryColl.addNew();
+                            perfManagerEntry.set(PerfRankMgmtConstant.NCKD_APPRAISALYEAR,appraisalYearDate);
+                            perfManagerEntry.set(PerfRankMgmtConstant.NCKD_APPRAISALRESULT, personAppraisalMap.get(personId));
+                            perfManagerEntry.set(PerfRankMgmtConstant.NCKD_ISALLRANKSOURCE, EnableEnum.YES.getCode());
+                        }
+                    }
+
+                }
+                OperateOption option = OperateOption.create();
+                option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE+"");
+                //告诉OP此次操作类型
+                option.setVariableValue("isUpdate", Boolean.TRUE+"");
+                OperationResult operationResult = SaveServiceHelper.saveOperate(FormConstant.SAVE_OP, PerfRankMgmtConstant.PERFMANAGER_ENTITYID, perfManagerArray, option);
+                if (!operationResult.isSuccess()) {
+                    StringJoiner errorMsgJoiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+                    for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
+                        errorMsgJoiner.add(error.getMessage());
+                    }
+                    String errorMsg = errorMsgJoiner.toString();
+                    if(StringUtils.isBlank(errorMsg)){
+                        errorMsg = operationResult.getMessage();
+                    }
+                    throw new ValidationException("同步考核周期失败,原因:"+errorMsg);
+                }
+            }
+        }
+        //-------------------------------- 同步人员考评 end --------------------------------
+    }
+
+
+
+
+
+    /**
+     * 根据人员和年度查询人员考核周期
+     * @param theYear 年份
+     * @param personIds 人员ID
+     * @return: java.util.List<java.lang.Long>
+     * @author W.Y.C
+     * @date: 2025/11/12 22:10
+     */
+    private List<Long> findPerfManager(int theYear,List<Long> personIds) {
+        //根据年度校验人员只能在同一个组
+        LocalDate startDate = LocalDate.of(theYear, 1, 1);
+        QFilter filter = new QFilter(PerfRankMgmtConstant.NCKD_PERSON, QCP.in, personIds);
+        filter.and(PerfRankMgmtConstant.NCKD_BEGINYEAR,QCP.less_equals,startDate);
+        filter.and(PerfRankMgmtConstant.NCKD_ENDYEAR,QCP.large_equals,startDate);
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY);
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        List<Long> ids = query.stream()
+                .map(obj -> obj.getLong(FormConstant.ID_KEY))
+                .collect(Collectors.toList());
+        return ids;
     }
 }

+ 126 - 47
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/validate/PerfRankMgmtSaveValidate.java

@@ -4,6 +4,7 @@ import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.QueryServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
@@ -11,12 +12,12 @@ import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 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.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -24,6 +25,8 @@ 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;
 
 /**
 * 绩效排名管理保存验证插件
@@ -36,39 +39,53 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
     private final Map<String, BigDecimal> appraisalResultRatioMap = new HashMap<>();
     @Override
     public void validate() {
-        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, PerfRankMgmtConstant.NCKD_RATIO)
-                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT}, FormConstant.NUMBER_KEY);
-        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), null);
-
-        query.forEach(dynamicObject -> {
-            String key = dynamicObject.getString(String.join(".",FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
-            BigDecimal value = dynamicObject.getBigDecimal(String.join(".",FormConstant.NCKD_ENTRYENTITY, PerfRankMgmtConstant.NCKD_RATIO));
-            appraisalResultRatioMap.put(key, value);
-        });
-        for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
-            if(appraisalResultRatioMap.isEmpty()){
-                this.addMessage(rowDataEntity, "没有配置【排名考核结果比例】");
-                return;
-            }
-            DynamicObject data = rowDataEntity.getDataEntity();
-            // 年份校验
-            if (validateYear(data, rowDataEntity)) {
-                // 获取明细数据并验证是否为空
-                DynamicObjectCollection entries = data.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
-                if (entries.isEmpty()) {
-                    String operateKey = this.getOperateKey();
-                    if(!operateKey.equals(FormConstant.SAVE_OP)) {
-                        this.addMessage(rowDataEntity, "请添加排名名单");
-                        continue;
-                    }
+        String invoker = (String)this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        boolean dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker);
+        if(!dataMigration) {
+            QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                    .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, PerfRankMgmtConstant.NCKD_RATIO)
+                    .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT}, FormConstant.NUMBER_KEY);
+            DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), null);
+
+            query.forEach(dynamicObject -> {
+                String key = dynamicObject.getString(String.join(".", FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
+                BigDecimal value = dynamicObject.getBigDecimal(String.join(".", FormConstant.NCKD_ENTRYENTITY, PerfRankMgmtConstant.NCKD_RATIO));
+                appraisalResultRatioMap.put(key, value);
+            });
+            for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                if (appraisalResultRatioMap.isEmpty()) {
+                    this.addFatalErrorMessage(rowDataEntity, "没有配置【排名考核结果比例】");
+                    return;
                 }
+                DynamicObject data = rowDataEntity.getDataEntity();
+                // 年份校验
+                if (validateYear(data, rowDataEntity)) {
+                    // 获取明细数据并验证是否为空
+                    DynamicObjectCollection entries = data.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                    if (entries.isEmpty()) {
+                        String operateKey = this.getOperateKey();
+                        // if (!operateKey.equals(FormConstant.SAVE_OP)) {
+                            this.addFatalErrorMessage(rowDataEntity, "请添加排名名单");
+                            // continue;
+                        // }
+                    }
+                    int step = data.getInt(PerfRankMgmtConstant.NCKD_STEP);
 
-                // 验证明细数据
-                ValidationContext context = validateEntries(entries, rowDataEntity);
+                    //当步骤为导入考核结果时需校验明细数据
 
-                // 验证统计数据一致性
-                validateDataConsistency(data, context, rowDataEntity);
+                    // 验证明细数据
+                    ValidationContext context = validateEntries(entries, rowDataEntity);
+
+                    // 校验人员是否在其他分组中排名
+                    validateRepetition(data, rowDataEntity, context);
+
+                    // 校验人员是否有对应考核周期
+                    validatePerfManager(data,entries,rowDataEntity, context);
+                    if(step == 1) {
+                        // 验证统计数据一致性
+                        validateDataConsistency(data, context, rowDataEntity);
+                    }
+                }
             }
         }
     }
@@ -128,7 +145,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
 
         if (difference > allowedDifference) {
 
-            this.addMessage(rowDataEntity,
+            this.addFatalErrorMessage(rowDataEntity,
                     StrFormatter.format("{}人数与配置比例不符,应为{}人,实际为{}人",
                             levelName, expectedCount,actualCount));
         }
@@ -147,7 +164,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
         int theYear = data.getInt(PerfRankMgmtConstant.NCKD_THEYEAR);
         int currentYear = DateUtil.now().getYear();
         if (theYear < 1900 || theYear > currentYear) {
-            this.addMessage(rowDataEntity, "年份不合法或超过当前年份");
+            this.addFatalErrorMessage(rowDataEntity, "年份不合法或超过当前年份");
             return false;
         }
         return true;
@@ -169,10 +186,66 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
             DynamicObject entry = entries.get(i);
             validateEntry(entry, rowDataEntity, i + 1, personIds, context);
         }
+        context.personIds = personIds;
+
+        return context;
+    }
 
+
+    private void validateRepetition(DynamicObject data, ExtendedDataEntity rowDataEntity, ValidationContext context) {
         //根据年度校验人员只能在同一个组
+        long id = data.getLong(FormConstant.ID_KEY);
+        int theYear = data.getInt(PerfRankMgmtConstant.NCKD_THEYEAR);
+        QFilter filter = new QFilter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON), QCP.in, context.personIds);
+        if(id > 0){
+            filter.and(FormConstant.ID_KEY, QCP.not_equals, id);
+        }
+        filter.and(PerfRankMgmtConstant.NCKD_THEYEAR,QCP.equals,theYear);
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.NAME_KEY)
+                .add(PerfRankMgmtConstant.NCKD_THEYEAR)
+                .addIdNumberName(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON);
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        StringJoiner msgJoiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+        for (DynamicObject dynamicObject : query) {
+            String name = dynamicObject.getString(FormConstant.NAME_KEY);
+            String personName = dynamicObject.getString(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON,FormConstant.NAME_KEY));
+            msgJoiner.add(StrFormatter.format("人员【{}】已在排名单元【{}】中排名;", personName,name));
+        }
+        if(msgJoiner.length() > 0){
+            this.addFatalErrorMessage(rowDataEntity, msgJoiner.toString());
+        }
+    }
 
-        return context;
+
+
+    private void validatePerfManager(DynamicObject data,DynamicObjectCollection entries, ExtendedDataEntity rowDataEntity, ValidationContext context) {
+        //根据年度校验人员只能在同一个组
+        int theYear = data.getInt(PerfRankMgmtConstant.NCKD_THEYEAR);
+        LocalDate startDate = LocalDate.of(theYear, 1, 1);
+        QFilter filter = new QFilter(PerfRankMgmtConstant.NCKD_PERSON, QCP.in, context.personIds);
+        filter.and(PerfRankMgmtConstant.NCKD_BEGINYEAR,QCP.less_equals,startDate);
+        filter.and(PerfRankMgmtConstant.NCKD_ENDYEAR,QCP.large_equals,startDate);
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(PerfRankMgmtConstant.NCKD_PERSON);
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        Map<Long, DynamicObject> personMap = query.stream()
+                .filter(obj -> obj.getLong(String.join(".", PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY)) > 0)
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(String.join(".", PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY)),
+                        obj -> obj
+                ));
+        StringJoiner msgJoiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+        for (DynamicObject entry : entries) {
+            long person = entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY);
+            if (!personMap.containsKey(person)) {
+                String personName = entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERSON).getString(FormConstant.NAME_KEY);
+                msgJoiner.add(StrFormatter.format("人员【{}】没有【{}】年度的考核周期;", personName,theYear));
+            }
+        }
+        if(msgJoiner.length() > 0){
+            this.addFatalErrorMessage(rowDataEntity, msgJoiner.toString());
+        }
     }
 
     /**
@@ -189,14 +262,19 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
      */
     private void validateEntry(DynamicObject entry, ExtendedDataEntity rowDataEntity,
                                int rowIndex, Set<Long> personIds, ValidationContext context) {
+        String invoker = (String)this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        boolean dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker);
+        DynamicObject empPosOrgRel = entry.getDynamicObject(PerfRankMgmtConstant.NCKD_EMPPOSORGREL);
         DynamicObject person = entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERSON);
-        if (person == null) {
-            this.addMessage(rowDataEntity, StrFormatter.format("第{}行,请选择人员\n", rowIndex));
+        if(!dataMigration && empPosOrgRel == null){
+            this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("第{}行,请选择人员\n", rowIndex));
+        }else if (person == null) {
+            this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("第{}行,请选择人员\n", rowIndex));
         } else {
             // 人员重复校验
             long personId = person.getLong(FormConstant.ID_KEY);
             if (personIds.contains(personId)) {
-                this.addMessage(rowDataEntity, StrFormatter.format("第{}行,人员【{}】重复;\n", rowIndex, person.getString(FormConstant.NAME_KEY)));
+                this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("第{}行,人员【{}】重复;\n", rowIndex, person.getString(FormConstant.NAME_KEY)));
             } else {
                 personIds.add(personId);
             }
@@ -279,19 +357,19 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
         int excellents = data.getInt(PerfRankMgmtConstant.NCKD_EXCELLENTS);
 
         if (topRanks != context.totalRankCount) {
-            this.addMessage(rowDataEntity, "总排名人数与实际录入数据不一致;\n");
+            this.addFatalErrorMessage(rowDataEntity, "总排名人数与实际录入数据不一致;\n");
         }
         if (allowanceRanks != context.allowanceRankCount) {
-            this.addMessage(rowDataEntity, "R排名人数与实际录入数据不一致;\n");
+            this.addFatalErrorMessage(rowDataEntity, "R排名人数与实际录入数据不一致;\n");
         }
         if (fails != context.failCount) {
-            this.addMessage(rowDataEntity, "不合格人数与实际录入数据不一致;\n");
+            this.addFatalErrorMessage(rowDataEntity, "不合格人数与实际录入数据不一致;\n");
         }
         if (basics != context.basicCount) {
-            this.addMessage(rowDataEntity, "基本人数与实际录入数据不一致;\n");
+            this.addFatalErrorMessage(rowDataEntity, "基本人数与实际录入数据不一致;\n");
         }
         if (excellents != context.excellentCount) {
-            this.addMessage(rowDataEntity, "优秀人数与实际录入数据不一致;\n");
+            this.addFatalErrorMessage(rowDataEntity, "优秀人数与实际录入数据不一致;\n");
         }
     }
 
@@ -343,14 +421,14 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
                 // 检查排名字段是否在有效范围内
                 int topRank = entry.getInt(PerfRankMgmtConstant.NCKD_TOPRANK);
                 if (topRank < 1 || topRank > totalRankCount) {
-                    this.addMessage(rowDataEntity,
+                    this.addFatalErrorMessage(rowDataEntity,
                             StrFormatter.format("第{}行,排名{}超出有效范围[1-{}]\n", i + 1, topRank, totalRankCount));
                 }
                 if (postAllowance) {
                     // 检查R排名字段是否在有效范围内
                     int allowanceRank = entry.getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
                     if (allowanceRank < 1 || allowanceRank > totalRankCount) {
-                        this.addMessage(rowDataEntity,
+                        this.addFatalErrorMessage(rowDataEntity,
                                 StrFormatter.format("第{}行,R排名{}超出有效范围[1-{}]\n", i + 1, allowanceRank, totalRankCount));
                     }
                 }
@@ -378,7 +456,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
                                        Map<Integer, Integer> rankCountMap) {
         int rankValue = entry.getInt(rankField);
         if (rankValue < 1) {
-            this.addMessage(rowDataEntity, StrFormatter.format("人员【{}】已勾选{},{}数不能小于1或为空",
+            this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("人员【{}】已勾选{},{}数不能小于1或为空",
                     personName, type, rankType));
         } else {
             rankCountMap.put(rankValue, rankCountMap.getOrDefault(rankValue, 0) + 1);
@@ -431,7 +509,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
             errorMsg.append("✅ 合规示例:[1,1,1,4] - 排名1重复3次,结束位置应为1+3-1=3,3不在列表中\n");
             errorMsg.append("【解决建议】\n");
             errorMsg.append("请调整重复排名的数值或移除冲突的排名,确保连续序列结束位置未被占用;\n\n");
-            this.addMessage(rowDataEntity, errorMsg.toString());
+            this.addFatalErrorMessage(rowDataEntity, errorMsg.toString());
         }
     }
 
@@ -511,7 +589,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
                     detailMessage.append(StrFormatter.format("{}【{}】出现{}次;", rankType, rank, count));
                 }
                 detailMessage.append("\n");
-                this.addMessage(rowDataEntity, detailMessage.toString());
+                this.addFatalErrorMessage(rowDataEntity, detailMessage.toString());
             }
         }
     }
@@ -533,6 +611,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
         int basicCount = 0;
         int excellentCount = 0;
         int qualifiedCount = 0;
+        Set<Long> personIds = new HashSet<>();
         Set<Integer> rankSet = new HashSet<>();  // 记录已出现的排名
         Set<Integer> duplicateRanks = new HashSet<>();  // 记录普通排名中重复的排名
         Set<Integer> duplicateRRanks = new HashSet<>();  // 记录R排名中重复的排名

+ 8 - 1
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportReportListDataPlugin.java

@@ -86,7 +86,14 @@ public class UnAdjustedReportReportListDataPlugin extends AbstractReportListData
                 .add(FormConstant.ADMINORG)
                 .addIdNumberName(FormConstant.POSITION_KEY)
                 .addGroup(new String[]{FormConstant.HBPM_POSITIONHR}, PositionStructureConstant.NCKD_JOBSEQ)
-                .addGroup(new String[]{FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ}, FormConstant.NUMBER_KEY);
+                .addGroup(new String[]{FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ}, FormConstant.NUMBER_KEY)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FIRSTORG)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_SECONDORG)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_THIRDORG)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FOURTHORG)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_FIFTHORG)
+                .addIdNumberName(FormConstant.ADMINORG,FormConstant.NCKD_SIXTHORG);
+
     }
 
     /**

+ 14 - 2
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/common/PerfManagerFormConstant.java

@@ -14,6 +14,8 @@ public class PerfManagerFormConstant extends FormConstant {
     public static final String PERFMANAGER_ENTITYID = "nckd_perfmanager";
     /** 周期开始年份 */
     public static final String NCKD_BEGINYEAR = "nckd_beginyear";
+    /** 周期结束年份 */
+    public static final String NCKD_ENDYEAR = "nckd_endyear";
     /** 实际周期结束年份 */
     public static final String NCKD_ACTENDYEAR = "nckd_actendyear";
     /** 人员考评向导页面标识*/
@@ -26,8 +28,7 @@ public class PerfManagerFormConstant extends FormConstant {
     public static final String NCKD_THIRDYEARRESULT = "nckd_thirdyearresult";
     /** 调薪说明 */
     public static final String NCKD_WAGEEXPLAIN = "nckd_wageexplain";
-    /** 周期结束年份 */
-    public static final String NCKD_ENDYEAR = "nckd_endyear";
+
     /** 周期状态 */
     public static final String NCKD_THESTATUS = "nckd_thestatus";
     /** 锁定人 */
@@ -52,4 +53,15 @@ public class PerfManagerFormConstant extends FormConstant {
 
     /** 批量新增考核周期(弹窗)-实体标识 */
     public static final String BATCHEVALCYCLE_ENTITYID = "nckd_batchevalcycle";
+
+    /*-------------------------------------- 年度全排名 begin --------------------------------------*/
+    /** 年度 */
+    public static final String NCKD_THEYEAR = "nckd_theyear";
+    /** 年度绩效排名管理-实体标识 */
+    public static final String NCKD_PERFRANKMGMT_ENTITYID = "nckd_perfrankmgmt";
+    /** 单据体 */
+    public static final String NCKD_PERFRANKMGMTENTRY = "nckd_perfrankmgmtentry";
+    /** 是否参与排名 */
+    public static final String NCKD_ISRANKING = "nckd_isranking";
+    /*-------------------------------------- 年度全排名 end --------------------------------------*/
 }

+ 266 - 60
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/helper/PerfManagerHelper.java

@@ -2,12 +2,19 @@ package nckd.jxccl.opmc.pm.helper;
 
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.MainEntityType;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.AppraisalResultEnum;
+import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
 import java.util.ArrayList;
@@ -18,98 +25,245 @@ import java.util.List;
 import java.util.Map;
 
 /**
-* 考核周期帮助类
-* @author W.Y.C
-* @date 2025/11/4 17:06
-* @version 1.0
-*/
+ * 考核周期帮助类
+ *
+ * @author W.Y.C
+ * @date 2025/11/4 17:06
+ * @version 1.0
+ */
 public class PerfManagerHelper {
 
+
+    /**
+     * 根据人员和日期查询全排名结果
+     * @param date 日期
+     * @param personIds 人员id
+     * @return: java.util.Map<java.lang.Long, nckd.jxccl.base.common.enums.AppraisalResultEnum>
+     * @author W.Y.C
+     * @date: 2025/11/15 22:40
+     */
+    public static Map<Long, AppraisalResultEnum> getPerfRankResult(Date date, Collection<Long> personIds) {
+
+        QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_THEYEAR, QCP.equals, DateUtil.getYear(date))
+                .and(String.join(".", PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, FormConstant.NCKD_PERSON), QCP.in, personIds)
+                .and(String.join(".", PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, PerfManagerFormConstant.NCKD_ISRANKING), QCP.equals, EnableEnum.YES.getCode());
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT)
+                .addIdNumberName(PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, PerfManagerFormConstant.NCKD_PERSON);
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfManagerFormConstant.NCKD_PERFRANKMGMT_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        Map<Long, AppraisalResultEnum> resultMap = new HashMap<>();
+        for (DynamicObject obj : query) {
+            long personId = obj.getLong(String.join(".", PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, PerfManagerFormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            String resultNumber = obj.getString(String.join(".", PerfManagerFormConstant.NCKD_PERFRANKMGMTENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
+            AppraisalResultEnum resultEnum = AppraisalResultEnum.getByCode(resultNumber);
+            if (resultEnum != null) {
+                resultMap.put(personId, resultEnum);
+            }
+
+        }
+
+        return resultMap;
+    }
+
+
+    /**
+     * 根据日期查询范围内的考核周期
+     * 查询逻辑说明:
+     * 1. 开始时间条件:考核周期的开始年份(NCKD_BEGINYEAR)必须小于等于指定日期
+     * 2. 结束时间条件:优先使用实际结束年份(NCKD_ACTENDYEAR),如果为空则使用计划结束年份(NCKD_ENDYEAR)
+     * 3. 结束时间必须大于等于指定日期
+     * 4. 即查找满足 "NCKD_BEGINYEAR <= date <= (NCKD_ACTENDYEAR为空时取NCKD_ENDYEAR)" 条件的考核周期
+     *
+     * @param date      指定查询日期
+     * @param personIds 人员ID集合
+     * @return 符合条件的考核周期数组
+     * @author W.Y.C
+     * @date 2025/11/4 17:06
+     */
+    public static DynamicObject[] getByDate(Date date, Collection<Long> personIds) {
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder();
+
+        // 构造查询条件:日期在[NCKD_BEGINYEAR, NCKD_ENDYEAR]范围内
+        // 考虑到NCKD_ACTENDYEAR可能为空,优先使用NCKD_ACTENDYEAR作为结束时间
+        QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, date)
+                .and(new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.in, personIds));
+
+        // 添加结束时间判断逻辑
+        QFilter endDateFilter = new QFilter(PerfManagerFormConstant.NCKD_ACTENDYEAR, QCP.large_equals, date)
+                .or(new QFilter(PerfManagerFormConstant.NCKD_ACTENDYEAR, QCP.is_null, null)
+                        .and(new QFilter(PerfManagerFormConstant.NCKD_ENDYEAR, QCP.large_equals, date)));
+
+        filter.and(endDateFilter);
+
+        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+    }
+
+    /**
+     * 获取当前人员所有考核周期
+     *
+     * @param personIds   人员ID集合
+     * @param otherFilter 其他过滤条件
+     * @return 符合条件的考核周期数组
+     * @author W.Y.C
+     * @date 2025/11/13 22:16
+     */
+    public static DynamicObject[] getByPersonId(Collection<Long> personIds, QFilter otherFilter) {
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder()
+                .orderDesc(PerfManagerFormConstant.NCKD_BEGINYEAR);
+
+        QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.in, personIds);
+        if (otherFilter != null) {
+            filter.and(otherFilter);
+        }
+        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+    }
+
+    /**
+     * 查询开始时间之前的考核周期
+     *
+     * @param beginYear   开始年份
+     * @param personIds   人员ID集合
+     * @param otherFilter 其他过滤条件
+     * @return 符合条件的考核周期数组
+     * @author W.Y.C
+     * @date 2025/11/13 22:16
+     */
+    public static DynamicObject[] getBeforeBeginYear(Date beginYear, Collection<Long> personIds, QFilter otherFilter) {
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder();
+
+        QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, beginYear)
+                .and(new QFilter(PerfManagerFormConstant.NCKD_THESTATUS, QCP.equals, EnableEnum.YES.getCode()))
+                .and(new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.in, personIds));
+        if (otherFilter != null) {
+            filter.and(otherFilter);
+        }
+
+        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+    }
+    
+    /**
+     * 批量查询不同人员在各自指定日期前的考核周期
+     *
+     * @param personDateMap 人员ID与日期的映射关系
+     * @param otherFilter   其他过滤条件
+     * @return 符合条件的考核周期数组
+     */
+    public static DynamicObject[] getBeforeBeginYear(Map<Long, Date> personDateMap, QFilter otherFilter) {
+        if (personDateMap == null || personDateMap.isEmpty()) {
+            return new DynamicObject[0];
+        }
+
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder();
+
+        // 构建OR条件查询不同人员的不同日期
+        QFilter orFilter = null;
+        for (Map.Entry<Long, Date> entry : personDateMap.entrySet()) {
+            Long personId = entry.getKey();
+            Date beginYear = entry.getValue();
+            
+            // 为每个人员构建独立的查询条件
+            QFilter personFilter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, beginYear)
+                    .and(new QFilter(PerfManagerFormConstant.NCKD_THESTATUS, QCP.equals, EnableEnum.YES.getCode()))
+                    .and(new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.equals, personId));
+            
+            // 使用OR连接所有人员的条件
+            if (orFilter == null) {
+                orFilter = personFilter;
+            } else {
+                orFilter = orFilter.or(personFilter);
+            }
+        }
+
+        QFilter finalFilter = orFilter;
+        if (otherFilter != null) {
+            finalFilter.and(otherFilter);
+        }
+
+        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{finalFilter});
+    }
+
     /**
      * 根据人员获取当前最新的考核周期
+     *
      * @param personIds 人员ID
-     * @return: kd.bos.dataentity.entity.DynamicObject[]
+     * @return 符合条件的考核周期数组
      * @author W.Y.C
-     * @date: 2025/11/04 17:31
+     * @date 2025/11/04 17:31
      */
     public static DynamicObject[] getNewestPerfManagerByPerson(Collection<Long> personIds) {
-        return getNewestPerfManagerByPerson(personIds,null);
+        return getNewestPerfManagerByPerson(personIds, null);
     }
 
+    /**
+     * 根据ID获取考核周期
+     *
+     * @param ids ID集合
+     * @return 考核周期数组
+     */
+    public static DynamicObject[] getById(Collection<Long> ids) {
+        QFilter filter = QFilterCommonHelper.getIdInFilter(ids);
+        MainEntityType dataEntityType = MetadataServiceHelper.getDataEntityType(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
+
+        return BusinessDataServiceHelper.load(ids.toArray(new Long[0]), dataEntityType);
+    }
 
     /**
-     * 根据人员获取当前最新的考核周期(“是否当前最新记录 = true” 并且  “周期状态 = 1”)
-     * @param personIds 人员ID
+     * 根据人员获取当前最新的考核周期("是否当前最新记录 = true" 并且 "周期状态 = 1")
+     *
+     * @param personIds   人员ID
      * @param otherFilter 其他过滤条件
-     * @return: kd.bos.dataentity.entity.DynamicObject[]
+     * @return 符合条件的考核周期数组
      * @author W.Y.C
-     * @date: 2025/11/04 17:30
+     * @date 2025/11/04 17:30
      */
-    public static DynamicObject[] getNewestPerfManagerByPerson(Collection<Long> personIds,QFilter otherFilter) {
-        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                .add(FormConstant.ID_KEY)
-                .addIdNumberName(FormConstant.NCKD_PERSON)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_SECONDYEARRESULT)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_THIRDYEARRESULT)
-                .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
-                .add(PerfManagerFormConstant.NCKD_ENDYEAR)
-                .add(PerfManagerFormConstant.NCKD_ACTENDYEAR)
-                .add(PerfManagerFormConstant.NCKD_WHYEND)
-                .add(PerfManagerFormConstant.NCKD_THESTATUS)
-                .add(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST)
-                .addIdNumberName(new String[]{PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALRESULT})
-                .addIdNumberName(new String[]{PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALYEAR});
+    public static DynamicObject[] getNewestPerfManagerByPerson(Collection<Long> personIds, QFilter otherFilter) {
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder();
 
         QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST, QCP.equals, EnableEnum.YES.getCode())
                 .and(new QFilter(PerfManagerFormConstant.NCKD_THESTATUS, QCP.equals, EnableEnum.YES.getCode()))
                 .and(new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.in, personIds));
-        if(otherFilter != null) {
+        if (otherFilter != null) {
             filter.and(otherFilter);
         }
 
-        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        return BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter});
     }
 
     /**
-     * 根据人员获取当前最新的考核周期(根据“周期开始时间”)
-     * @param personIds 人员ID
+     * 根据人员获取当前最新的考核周期(根据"周期开始时间")
+     *
+     * @param personIds   人员ID
      * @param otherFilter 其他过滤条件
-     * @return: kd.bos.dataentity.entity.DynamicObject[]
+     * @return 符合条件的考核周期列表
      * @author W.Y.C
-     * @date: 2025/11/04 17:30
+     * @date 2025/11/04 17:30
      */
-    public static List<DynamicObject> getNewestPerfManagerByPersonAndBeginYear(Collection<Long> personIds,QFilter otherFilter) {
-        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                .addIdNumberName()
-                .addIdNumberName(FormConstant.NCKD_PERSON)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_SECONDYEARRESULT)
-                .addIdNumberName(PerfManagerFormConstant.NCKD_THIRDYEARRESULT)
-                .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
-                .add(PerfManagerFormConstant.NCKD_ENDYEAR)
-                .add(PerfManagerFormConstant.NCKD_ACTENDYEAR)
-                .add(PerfManagerFormConstant.NCKD_WHYEND)
-                .add(PerfManagerFormConstant.NCKD_THESTATUS)
-                .add(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST)
+    public static List<DynamicObject> getNewestPerfManagerByPersonAndBeginYear(Collection<Long> personIds, QFilter otherFilter) {
+        QueryFieldBuilder queryFieldBuilder = createDefaultQueryFieldBuilder()
                 .add(PerfManagerFormConstant.CREATE_TIME_KEY)
-                .addIdNumberName(new String[]{PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALRESULT})
-                .addIdNumberName(new String[]{PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALYEAR})
                 .orderDesc(PerfManagerFormConstant.NCKD_BEGINYEAR);
 
         QFilter filter = new QFilter(PerfManagerFormConstant.NCKD_PERSON, QCP.in, personIds);
-        if(otherFilter != null) {
+        if (otherFilter != null) {
             filter.and(otherFilter);
         }
-        DynamicObject[] load = BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter}, queryFieldBuilder.buildOrder());
 
-        //只取每个人员最新记录(先按NCKD_BEGINYEAR,再按CREATE_TIME_KEY)
+        DynamicObject[] load = BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryFieldBuilder.buildSelect(), new QFilter[]{filter}, queryFieldBuilder.buildOrder());
+
+        // 只取每个人员最新记录(先按NCKD_BEGINYEAR,再按CREATE_TIME_KEY)
         Map<Long, DynamicObject> latestRecordMap = new HashMap<>();
         for (DynamicObject dynamicObject : load) {
             DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
             Date beginDate = dynamicObject.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
             Date createTime = dynamicObject.getDate(PerfManagerFormConstant.CREATE_TIME_KEY);
             long personId = person.getLong(FormConstant.ID_KEY);
+
             // 如果map中没有该personId的记录,或者当前记录的beginDate更晚,
             // 或者beginDate相同但createTime更晚,则更新记录
             if (!latestRecordMap.containsKey(personId)) {
@@ -129,36 +283,61 @@ public class PerfManagerHelper {
         return new ArrayList<>(latestRecordMap.values());
     }
 
-
     /**
      * 根据人员将所有IsCurrentNewest更新为非最新的
+     *
      * @param personId 人员ID(可传多个)
-     * @return: void
      * @author W.Y.C
-     * @date: 2025/09/28 21:18
+     * @date 2025/09/28 21:18
      */
     public static void markAsNotCurrentNewest(Long... personId) {
         QFilter filter = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.in, personId);
         QueryFieldBuilder queryField = QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
                 .add(FormConstant.NCKD_ISCURRENTNEWEST);
-        DynamicObject[] personPosFileColl = BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,queryField.buildSelect(), new QFilter[]{filter});
+        DynamicObject[] personPosFileColl = BusinessDataServiceHelper.load(PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                queryField.buildSelect(), new QFilter[]{filter});
         for (DynamicObject personPosFile : personPosFileColl) {
             personPosFile.set(FormConstant.NCKD_ISCURRENTNEWEST, Boolean.FALSE);
         }
         SaveServiceHelper.update(personPosFileColl);
     }
 
-    /**
-     * 将人员最新考核周期标记为最新的
+    /***
+     * 将人员最新考核周期标记为最新的并修改状态为正常
      * @param personIds 人员ID(可传多个)
      * @param otherFilter 其他条件
      * @return: void
      * @author W.Y.C
-     * @date: 2025/09/28 21:18
+     * @date: 2025/11/19 18:07
      */
-    public static void markAsCurrentNewest(Collection personIds,QFilter otherFilter) {
-        List<DynamicObject> perfManagerColl = getNewestPerfManagerByPersonAndBeginYear(personIds,otherFilter);
+    public static void markAsCurrentNewestAndUpdateStatus(Collection personIds, QFilter otherFilter) {
+        List<DynamicObject> perfManagerColl = getNewestPerfManagerByPersonAndBeginYear(personIds, otherFilter);
+        DynamicObject[] updatePerfManagerColl = new DynamicObject[perfManagerColl.size()];
+        for (int i = 0; i < perfManagerColl.size(); i++) {
+            DynamicObject dynamicObject = perfManagerColl.get(i);
+            dynamicObject.set(FormConstant.NCKD_ISCURRENTNEWEST, Boolean.TRUE);
+
+            String theStatus = dynamicObject.getString(PerfManagerFormConstant.NCKD_THESTATUS);
+            if(theStatus.equalsIgnoreCase("3")){
+                //结束状态标记为正常
+                dynamicObject.set(PerfManagerFormConstant.NCKD_THESTATUS, EnableEnum.YES.getCode());
+            }
+            updatePerfManagerColl[i] = dynamicObject;
+        }
+        SaveServiceHelper.update(updatePerfManagerColl);
+    }
+
+    /**
+     * 将人员最新考核周期标记为最新的
+     *
+     * @param personIds   人员ID(可传多个)
+     * @param otherFilter 其他条件
+     * @author W.Y.C
+     * @date 2025/09/28 21:18
+     */
+    public static void markAsCurrentNewest(Collection personIds, QFilter otherFilter) {
+        List<DynamicObject> perfManagerColl = getNewestPerfManagerByPersonAndBeginYear(personIds, otherFilter);
         DynamicObject[] updatePerfManagerColl = new DynamicObject[perfManagerColl.size()];
         for (int i = 0; i < perfManagerColl.size(); i++) {
             DynamicObject dynamicObject = perfManagerColl.get(i);
@@ -167,4 +346,31 @@ public class PerfManagerHelper {
         }
         SaveServiceHelper.update(updatePerfManagerColl);
     }
+
+    /**
+     * 创建默认的查询字段构建器
+     *
+     * @return QueryFieldBuilder实例
+     */
+    private static QueryFieldBuilder createDefaultQueryFieldBuilder() {
+        return QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .addIdNumberName(FormConstant.NCKD_EMPPOSORGREL)
+                .addIdNumberName(FormConstant.NCKD_PERSON)
+                .addIdNumberName(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT)
+                .addIdNumberName(PerfManagerFormConstant.NCKD_SECONDYEARRESULT)
+                .addIdNumberName(PerfManagerFormConstant.NCKD_THIRDYEARRESULT)
+                .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
+                .add(PerfManagerFormConstant.NCKD_ENDYEAR)
+                .add(PerfManagerFormConstant.NCKD_ACTENDYEAR)
+                .add(PerfManagerFormConstant.NCKD_WHYEND)
+                .add(PerfManagerFormConstant.NCKD_THESTATUS)
+                .add(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST)
+                .add(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY)
+                .addIdNumberName(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT)
+                .addGroup(new String[]{PerfManagerFormConstant.NCKD_PERFMANAGERENTRY},
+                        PerfManagerFormConstant.NCKD_APPRAISALRESULT,
+                        PerfManagerFormConstant.NCKD_APPRAISALYEAR,
+                        PerfManagerFormConstant.NCKD_ISALLRANKSOURCE);
+    }
 }

+ 34 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfmanagerQueryListPlugin.java

@@ -0,0 +1,34 @@
+package nckd.jxccl.opmc.pm.plugin.form;
+
+import kd.bos.form.ShowType;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.report.ReportShowParameter;
+import kd.sdk.plugin.Plugin;
+
+/**
+* 人员考评列表
+* 实体标识:nckd_perfmanager_query
+* @author W.Y.C
+* @date 2025/11/14 9:38
+* @version 1.0
+*/
+public class PerfmanagerQueryListPlugin extends AbstractListPlugin implements Plugin {
+
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        if(e.getOperationResult() != null && e.getOperationResult().isSuccess()){
+            String operateKey = e.getOperateKey();
+            if("annualperfdetail".equalsIgnoreCase(operateKey)){
+                // 打开单据列表
+                ReportShowParameter reportShowParameter = new ReportShowParameter();
+                reportShowParameter.setFormId("nckd_annualperfdetail");
+                reportShowParameter.getOpenStyle().setShowType(ShowType.MainNewTabPage); // 设置打开方式
+                reportShowParameter.setCaption("年度绩效结果明细");
+                this.getView().showForm(reportShowParameter);
+
+            }
+        }
+    }
+}

+ 264 - 63
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/BatchEvalCycleFormPlugin.java

@@ -9,13 +9,14 @@ import kd.bos.entity.operate.result.IOperateInfo;
 import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.form.ConfirmCallBackListener;
 import kd.bos.form.ConfirmTypes;
+import kd.bos.form.FormShowParameter;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.MessageBoxResult;
-import kd.bos.form.control.Control;
+import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.org.utils.DynamicObjectUtils;
-import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.exception.ValidationException;
@@ -25,16 +26,21 @@ import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 import nckd.jxccl.opmc.pm.helper.PerfManagerHelper;
+import org.apache.commons.lang3.StringUtils;
 
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
 import java.util.EventObject;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.Objects;
 import java.util.StringJoiner;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -47,54 +53,127 @@ import java.util.stream.Collectors;
 public class BatchEvalCycleFormPlugin extends AbstractFormPlugin implements Plugin {
 
     @Override
-    public void registerListener(EventObject e) {
-        this.addClickListeners(FormConstant.BTN_OK_OP);
-    }
-
+    public void afterCreateNewData(EventObject e) {
+        // 获取当前页面的FormShowParameter对象
+        FormShowParameter showParameter = this.getView().getFormShowParameter();
+        //获取列表选择的人员
+        String isAddOrUpdate = ConvertUtil.toStr(showParameter.getCustomParam("nckd_isaddorupdate"));
+        if("2".equalsIgnoreCase(isAddOrUpdate)) {
+            //批量修改
+            this.getModel().setValue("nckd_isaddorupdate", isAddOrUpdate);
+            this.getView().updateView("nckd_isaddorupdate");
 
-    @Override
-    public void click(EventObject evt) {
-
-        super.click(evt);
-        Control source = (Control)evt.getSource();
-        if(FormConstant.BTN_OK_OP.equalsIgnoreCase(source.getKey())){
-            DynamicObjectCollection perfManagerList = this.getModel().getEntryEntity(FormConstant.NCKD_ENTRYENTITY);
-            Set<Long> personIds = new HashSet<>(perfManagerList.size());
-            //转换为考核周期实体
-            for (DynamicObject dynamicObject : perfManagerList) {
-                DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
-                personIds.add(person.getLong(FormConstant.ID_KEY));
+            List<Long> selectedRows = ConvertUtil.toList(showParameter.getCustomParam("selectedRows"));
+            int max = Math.max(selectedRows.size() - 1, 0);
+            if(max > 0) {
+                this.getModel().batchCreateNewEntryRow(FormConstant.NCKD_ENTRYENTITY, max);
             }
+            DynamicObject[] perfManagerList = PerfManagerHelper.getById(selectedRows);
+            AtomicInteger index = new AtomicInteger(0);
+            for (DynamicObject perfManager : perfManagerList) {
+                int i = index.getAndIncrement();
+                Date beginYear = perfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                Date endYear = perfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
+                this.getModel().setValue(PerfManagerFormConstant.PERFMANAGER_ENTITYID,perfManager, i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_EMPPOSORGREL,perfManager.getDynamicObject(PerfManagerFormConstant.NCKD_EMPPOSORGREL), i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_PERSON,perfManager.getDynamicObject(PerfManagerFormConstant.NCKD_PERSON), i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_BEGINYEAR,beginYear, i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_ENDYEAR,endYear, i);
+                this.getModel().setValue(PerfManagerFormConstant.DESCRIPTION_KEY,perfManager.getString(FormConstant.DESCRIPTION_KEY), i);
 
-            //最新上一考核周期
-            DynamicObject[] newestPerfManagerByPerson = PerfManagerHelper.getNewestPerfManagerByPerson(personIds);
-            Map<Long, DynamicObject> lastPerfManagerMap; lastPerfManagerMap = Arrays.stream(newestPerfManagerByPerson)
-                    .collect(Collectors.toMap(
-                            perfManager -> perfManager.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY),
-                            perfManager -> perfManager,
-                            (existing, replacement) -> existing
-                    ));
-            StringJoiner confirMmsg = new StringJoiner(",");
-            for (DynamicObject lastPerfManager : lastPerfManagerMap.values()) {
-                String personName = lastPerfManager.getDynamicObject(FormConstant.NCKD_PERSON).getString(FormConstant.NAME_KEY);
-                LocalDateTime beginYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
-                LocalDateTime endYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
-                confirMmsg.add(StrFormatter.format("{}(周期:{}~{})", personName, beginYear.getYear(), endYear.getYear()));
-            }
-            if(confirMmsg.length() > 0){
-                // 创建回调监听器,指定回调ID和处理此回调的插件(this)
-                ConfirmCallBackListener confirmCallBacks = new ConfirmCallBackListener(PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID, this);
-                // 弹出确认框
-                this.getView().showConfirm(StrFormatter.format("检测到本次新增的人员中存在【未结束】的考核周期,新周期新增成功后会自动结束上一周期,请确认是否继续。{}", confirMmsg),
-                        MessageBoxOptions.OKCancel,
-                        ConfirmTypes.Default,
-                        confirmCallBacks);
-            }else{
-                executeOperateSave();
+                DynamicObjectCollection entrys = perfManager.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                for (DynamicObject entry : entrys) {
+                    boolean isAllRankSource = entry.getBoolean(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE);
+                    Date appraisalYear = entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+
+                    if (isAllRankSource && appraisalYear != null) {
+                        LocalDateTime appraisalLocalDate = DateUtil.toLocalDateTime(appraisalYear);
+                        LocalDateTime beginLocalDate = DateUtil.toLocalDateTime(beginYear);
+
+                        // 计算是第几年(分录中的年份-(周期开始时间 + 1))
+                        long yearsDiff = appraisalLocalDate.getYear() - beginLocalDate.getYear() + 1;
+                        // 根据年份差异禁用对应的考核结果字段
+                        switch ((int) yearsDiff) {
+                            case 1:
+                                this.getView().setEnable(Boolean.FALSE, i, PerfManagerFormConstant.NCKD_FIRSTYEARRESULT);
+                                break;
+                            case 2:
+                                this.getView().setEnable(Boolean.FALSE, i, PerfManagerFormConstant.NCKD_SECONDYEARRESULT);
+                                break;
+                            case 3:
+                                this.getView().setEnable(Boolean.FALSE, i, PerfManagerFormConstant.NCKD_THIRDYEARRESULT);
+                                break;
+                        }
+                    }
+                }
+
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT,perfManager.getDynamicObject(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT), i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_SECONDYEARRESULT,perfManager.getDynamicObject(PerfManagerFormConstant.NCKD_SECONDYEARRESULT), i);
+                this.getModel().setValue(PerfManagerFormConstant.NCKD_THIRDYEARRESULT,perfManager.getDynamicObject(PerfManagerFormConstant.NCKD_THIRDYEARRESULT), i);
             }
 
+            this.getView().updateView(FormConstant.NCKD_ENTRYENTITY);
         }
+    }
 
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        if(e.getOperationResult() != null && e.getOperationResult().isSuccess()){
+            String operateKey = e.getOperateKey();
+            if(FormConstant.CONFIRM_OP.equalsIgnoreCase(operateKey)){
+                String isAddOrUpdate = ConvertUtil.toStr(this.getModel().getValue("nckd_isaddorupdate"));
+                boolean isUpdate = "2".equalsIgnoreCase(isAddOrUpdate);
+
+                DynamicObjectCollection perfManagerList = this.getModel().getEntryEntity(FormConstant.NCKD_ENTRYENTITY);
+                Map<Long, Date> personBeginYearMap = new HashMap<>(perfManagerList.size());
+                //转换为考核周期实体
+                for (DynamicObject dynamicObject : perfManagerList) {
+                    DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
+                    Long personId = person.getLong(FormConstant.ID_KEY);
+                    Date beginYear = dynamicObject.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+
+                    if (beginYear != null) {
+                        personBeginYearMap.put(personId, beginYear);
+                    }
+                }
+                if(isUpdate){
+                    //修改
+                    executeOperateSave();
+                }else{
+                    //获取开始时间之前状态为正常的考核周期
+                    Map<Long, DynamicObject[]> lastPerfManagerMap = new HashMap<>(personBeginYearMap.size());
+                    for (Map.Entry<Long, Date> longDateEntry : personBeginYearMap.entrySet()) {
+
+                        DynamicObject[] beforeBeginYear = PerfManagerHelper.getBeforeBeginYear(longDateEntry.getValue(), Collections.singletonList(longDateEntry.getKey()), null);
+                        lastPerfManagerMap.put(longDateEntry.getKey(), beforeBeginYear);
+                    }
+
+                    StringJoiner confirmMsg = new StringJoiner(",");
+                    for (DynamicObject[] lastPerfManagerArray : lastPerfManagerMap.values()) {
+                        for (DynamicObject lastPerfManager : lastPerfManagerArray) {
+                            String personName = lastPerfManager.getDynamicObject(FormConstant.NCKD_PERSON).getString(FormConstant.NAME_KEY);
+                            LocalDateTime beginYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                            LocalDateTime endYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                            confirmMsg.add(StrFormatter.format("{}(周期:{}~{})", personName, beginYear.getYear(), endYear.getYear()));
+
+                        }
+                    }
+                    if(confirmMsg.length() > 0){
+                        // 创建回调监听器,指定回调ID和处理此回调的插件(this)
+                        ConfirmCallBackListener confirmCallBacks = new ConfirmCallBackListener(PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID, this);
+                        // 弹出确认框
+                        this.getView().showConfirm(StrFormatter.format("检测到本次新增的人员中存在【未结束】的考核周期,新周期新增成功后会自动结束上一周期,并将上一周期结束时间更新为当前新周期开始年前一年。请确认是否继续。{}", confirmMsg),
+                                MessageBoxOptions.OKCancel,
+                                ConfirmTypes.Default,
+                                confirmCallBacks);
+                    }else{
+                        executeOperateSave();
+                    }
+                }
+
+            }
+        }
     }
 
     @Override
@@ -102,50 +181,172 @@ public class BatchEvalCycleFormPlugin extends AbstractFormPlugin implements Plug
         // 判断回调ID和用户点击的按钮
         if (PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID.equals(messageBoxClosedEvent.getCallBackId())
                 && messageBoxClosedEvent.getResult() == MessageBoxResult.Yes) {
-
             executeOperateSave();
         }
     }
 
     private void executeOperateSave() {
-        DynamicObject createorg = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.CREATEORG_KEY));
-        DynamicObject useorg = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.USEORG_KEY));
+
+        DynamicObject createOrg = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.CREATEORG_KEY));
+        DynamicObject useOrg = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.USEORG_KEY));
         DynamicObject org = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.ORG_KEY));
 
+        String isAddOrUpdate = ConvertUtil.toStr(this.getModel().getValue("nckd_isaddorupdate"));
+        boolean isUpdate = "2".equalsIgnoreCase(isAddOrUpdate);
         DynamicObjectCollection perfManagerList = this.getModel().getEntryEntity(FormConstant.NCKD_ENTRYENTITY);
-        Set<Long> personIds = new HashSet<>(perfManagerList.size());
-        List<DynamicObject> savePerfManager = new ArrayList<>();
+        List<Long> perfManagerIds = Collections.emptyList();
+        Map<Long, DynamicObject> perfManagerMap = null;
+        if(isUpdate){
+            perfManagerIds = perfManagerList.stream()
+                    .map(dynamicObject -> dynamicObject.getDynamicObject(PerfManagerFormConstant.PERFMANAGER_ENTITYID))
+                    .filter(Objects::nonNull)
+                    .map(perfManager -> perfManager.getLong(FormConstant.ID_KEY))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+            DynamicObject[] byId = PerfManagerHelper.getById(perfManagerIds);
+            perfManagerMap = Arrays.stream(byId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toMap(
+                            perfManager -> perfManager.getLong(FormConstant.ID_KEY),
+                            perfManager -> perfManager,
+                            (existing, replacement) -> existing
+                    ));
+        }
+        List<DynamicObject> savePerfManagerList = new ArrayList<>();
+
         //转换为考核周期实体
         for (DynamicObject dynamicObject : perfManagerList) {
             DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
             String personName = person.getString(FormConstant.NAME_KEY);
             LocalDateTime beginYear = DateUtil.toLocalDateTime(dynamicObject.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
             LocalDateTime endYear = DateUtil.toLocalDateTime(dynamicObject.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
-            DynamicObject newPerfManager = EntityHelper.newAvailableBasicEntity(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
-            DynamicObjectUtils.copy(dynamicObject, newPerfManager);
-            newPerfManager.set(PerfManagerFormConstant.CREATEORG_KEY, createorg);
-            newPerfManager.set(PerfManagerFormConstant.USEORG_KEY, useorg);
-            newPerfManager.set(PerfManagerFormConstant.ORG_KEY, org);
-            newPerfManager.set(PerfManagerFormConstant.CTRLSTRATEGY_KEY, CtrlStrategyEnum.GLOBAL_SHARE.getCtrlStrategy());
-            newPerfManager.set(FormConstant.NAME_KEY, StrFormatter.format("【{}】{}~{}的考核周期",personName,beginYear.getYear(),endYear.getYear()));
-            savePerfManager.add(newPerfManager);
-            personIds.add(person.getLong(FormConstant.ID_KEY));
+            String description = dynamicObject.getString(PerfManagerFormConstant.DESCRIPTION_KEY);
+            DynamicObject savePerfManager = null;
+            if(isUpdate){
+                //修改
+                DynamicObject perfManager = dynamicObject.getDynamicObject(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
+                long id = perfManager.getLong(FormConstant.ID_KEY);
+                DynamicObject dbPerfManager = perfManagerMap.get(id);
+
+
+                dbPerfManager.set(PerfManagerFormConstant.DESCRIPTION_KEY, description);
+                LocalDateTime dbBeginYear = DateUtil.toLocalDateTime(dbPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                LocalDateTime dbEndYear = DateUtil.toLocalDateTime(dbPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+
+                boolean isCycleRangeChange = Boolean.FALSE;
+                DynamicObjectCollection dbPerfManagerEntry = dbPerfManager.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                if(!Objects.equals(dbBeginYear, beginYear)){
+                    isCycleRangeChange = true;
+                    dbPerfManager.set(PerfManagerFormConstant.NCKD_BEGINYEAR, DateUtil.toDate(beginYear));
+                }
+                if(!Objects.equals(dbEndYear, endYear)){
+                    isCycleRangeChange = true;
+                    dbPerfManager.set(PerfManagerFormConstant.NCKD_ENDYEAR, DateUtil.toDate(endYear));
+                }
+                Iterator<DynamicObject> iterator = dbPerfManagerEntry.iterator();
+                while (iterator.hasNext()) {
+                    DynamicObject entry = iterator.next();
+                    Date appraisalYear = entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+                    if (appraisalYear != null) {
+                        LocalDateTime appraisalLocalDate = DateUtil.toLocalDateTime(appraisalYear);
+                        long yearsDiff = appraisalLocalDate.getYear() - dbBeginYear.getYear();
+                        // 判断年份是否在范围外
+                        if (appraisalLocalDate.getYear() < beginYear.getYear() || appraisalLocalDate.getYear() > endYear.getYear()) {
+                            // 删除不在范围内的考核结果记录
+                            iterator.remove();
+                        }
+                    }
+                }
+
+                // 考核结果写回到分录,save操作的时候会将分录的结果写回到表头
+                DynamicObject firstYearResult = dynamicObject.getDynamicObject(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT);
+                DynamicObject secondYearResult = dynamicObject.getDynamicObject(PerfManagerFormConstant.NCKD_SECONDYEARRESULT);
+                DynamicObject thirdYearResult = dynamicObject.getDynamicObject(PerfManagerFormConstant.NCKD_THIRDYEARRESULT);
+
+                DynamicObject dbFirstYearResult = dbPerfManager.getDynamicObject(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT);
+                DynamicObject dbSecondYearResult = dbPerfManager.getDynamicObject(PerfManagerFormConstant.NCKD_SECONDYEARRESULT);
+                DynamicObject dbThirdYearResult = dbPerfManager.getDynamicObject(PerfManagerFormConstant.NCKD_THIRDYEARRESULT);
+
+                // 判断跟数据库的有没有变化,有变化才写回到分录
+               /* Long firstYearResultId = firstYearResult != null ? firstYearResult.getLong(FormConstant.ID_KEY) : null;
+                Long dbFirstYearResultId = dbFirstYearResult != null ? dbFirstYearResult.getLong(FormConstant.ID_KEY) : null;
+                if (!Objects.equals(firstYearResultId, dbFirstYearResultId) || isCycleRangeChange) {
+                    updateAppraisalResult(dbPerfManagerEntry, beginYear, firstYearResult, 0);
+                }
+
+                Long secondYearResultId = secondYearResult != null ? secondYearResult.getLong(FormConstant.ID_KEY) : null;
+                Long dbSecondYearResultId = dbSecondYearResult != null ? dbSecondYearResult.getLong(FormConstant.ID_KEY) : null;
+                if (!Objects.equals(secondYearResultId, dbSecondYearResultId) || isCycleRangeChange) {
+                    updateAppraisalResult(dbPerfManagerEntry, beginYear, secondYearResult, 1);
+                }
+
+                Long thirdYearResultId = thirdYearResult != null ? thirdYearResult.getLong(FormConstant.ID_KEY) : null;
+                Long dbThirdYearResultId = dbThirdYearResult != null ? dbThirdYearResult.getLong(FormConstant.ID_KEY) : null;
+                if (!Objects.equals(thirdYearResultId, dbThirdYearResultId) || isCycleRangeChange) {
+                    updateAppraisalResult(dbPerfManagerEntry, beginYear, thirdYearResult, 2);
+                }*/
+                savePerfManager = dbPerfManager;
+            }else{
+                //新增
+                savePerfManager = EntityHelper.newEntity(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
+                DynamicObjectUtils.copy(dynamicObject, savePerfManager);
+                savePerfManager.set(PerfManagerFormConstant.CTRLSTRATEGY_KEY, CtrlStrategyEnum.GLOBAL_SHARE.getCtrlStrategy());
+                savePerfManager.set(FormConstant.NAME_KEY, StrFormatter.format("【{}】{}~{}的考核周期",personName,beginYear.getYear(),endYear.getYear()));
+                savePerfManager.set(PerfManagerFormConstant.CREATEORG_KEY, createOrg);
+                savePerfManager.set(PerfManagerFormConstant.ORG_KEY, org);
+                savePerfManager.set(PerfManagerFormConstant.USEORG_KEY, useOrg);
+            }
+            savePerfManagerList.add(savePerfManager);
         }
 
         OperateOption option = OperateOption.create();
         option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE+"");
-        OperationResult operationResult =  OperationServiceHelper.executeOperate(FormConstant.SAVE_OP, PerfManagerFormConstant.PERFMANAGER_ENTITYID, savePerfManager.toArray(new DynamicObject[0]), option);
+        //告诉OP此次操作类型
+        option.setVariableValue("isUpdate", isUpdate+"");
+
+        OperationResult operationResult =   SaveServiceHelper.saveOperate(FormConstant.SAVE_OP, PerfManagerFormConstant.PERFMANAGER_ENTITYID, savePerfManagerList.toArray(new DynamicObject[0]), option);
 
         if (!operationResult.isSuccess()) {
-            StringJoiner errorMsg = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+            StringJoiner errorMsgJoiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
             for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
-                errorMsg.add(error.getMessage());
+                errorMsgJoiner.add(error.getMessage());
+            }
+            String errorMsg = errorMsgJoiner.toString();
+            if(StringUtils.isBlank(errorMsg)){
+                errorMsg = operationResult.getMessage();
             }
-            throw new ValidationException(errorMsg.toString());
+            throw new ValidationException(errorMsg);
         }else{
             this.getView().getParentView().showSuccessNotification("保存成功");
             this.getView().getParentView().invokeOperation(FormConstant.REFRESH_OP);
             this.getView().close();
         }
     }
+
+
+    private void updateAppraisalResult(DynamicObjectCollection perfManagerEntry,
+                                       LocalDateTime beginYear,
+                                       DynamicObject result,
+                                       int yearOffset) {
+        boolean isExist = false;
+        for (DynamicObject entry : perfManagerEntry) {
+            Date appraisalYear = entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+            if (appraisalYear != null) {
+                LocalDateTime appraisalLocalDate = DateUtil.toLocalDateTime(appraisalYear);
+                long yearsDiff = appraisalLocalDate.getYear() - beginYear.getYear();
+                if (yearsDiff == yearOffset) {
+                    entry.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, result);
+                    isExist = true;
+                    break;
+                }
+            }
+        }
+
+        if (!isExist) {
+            DynamicObject entry = perfManagerEntry.addNew();
+            entry.set(PerfManagerFormConstant.NCKD_APPRAISALYEAR,
+                    DateUtil.toDate(DateUtil.addYears(beginYear, yearOffset)));
+            entry.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, result);
+        }
+    }
 }

+ 0 - 14
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/PerfManagerFormPlugin.java

@@ -1,36 +1,22 @@
 package nckd.jxccl.opmc.pm.plugin.form.cycle;
 
-import kd.bos.bill.AbstractBillPlugIn;
-import kd.bos.dataentity.entity.DynamicObject;
-import kd.bos.dataentity.entity.DynamicObjectCollection;
-import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.datamodel.events.PropertyChangedArgs;
 import kd.bos.form.control.EntryGrid;
-import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.field.DateEdit;
 import kd.bos.form.field.FieldEdit;
-import kd.bos.form.operate.FormOperate;
 import kd.bos.form.plugin.AbstractFormPlugin;
 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.QueryServiceHelper;
-import kd.bos.util.CollectionUtils;
 import kd.sdk.plugin.Plugin;
-import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
-import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
 import java.util.Date;
 import java.util.EventObject;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
 * 考核周期表单插件

+ 48 - 31
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/cycle/PerfManagerListPlugin.java

@@ -1,55 +1,42 @@
 package nckd.jxccl.opmc.pm.plugin.form.cycle;
 
-import kd.bos.algo.Algo;
-import kd.bos.algo.AlgoContext;
-import kd.bos.algo.DataSet;
-import kd.bos.algo.Row;
-import kd.bos.common.enums.EnableEnum;
-import kd.bos.consts.PermItemConst;
-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.QueryEntityType;
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.entity.datamodel.IListModel;
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.entity.datamodel.events.PackageDataEvent;
 import kd.bos.entity.operate.OperateOptionConst;
-import kd.bos.entity.operate.result.IOperateInfo;
 import kd.bos.entity.operate.result.OperationResult;
-import kd.bos.ext.hr.service.query.QueryEntityHelper;
-import kd.bos.form.CloseCallBack;
-import kd.bos.form.FormShowParameter;
-import kd.bos.form.MessageBoxOptions;
-import kd.bos.form.ShowType;
+import kd.bos.form.*;
 import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeCreateListColumnsArgs;
+import kd.bos.form.events.BeforeCreateListDataProviderArgs;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
-import kd.bos.form.events.ClosedCallBackEvent;
 import kd.bos.form.operate.FormOperate;
+import kd.bos.list.IListColumn;
+import kd.bos.list.IListView;
+import kd.bos.list.ListColumn;
 import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.mvc.list.ListDataProvider;
+import kd.bos.mvc.list.QueryBuilderFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
-import kd.hr.hbp.common.model.AuthorizedOrgResultWithSub;
-import kd.hr.hbp.common.model.OrgSubInfo;
-import kd.sdk.hr.hbp.business.helper.permission.HRPermissionServiceHelper;
+import kd.sdk.hr.haos.business.helper.HAOSServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
-import nckd.jxccl.base.common.constant.SystemQueryConstant;
-import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
-import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
-import nckd.jxccl.opmc.pm.plugin.operate.cycle.PerfManagerSaveOpPlugin;
 
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringJoiner;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -65,7 +52,6 @@ public class PerfManagerListPlugin extends AbstractListPlugin implements Plugin
     public void itemClick(ItemClickEvent evt) {
         String itemKey = evt.getItemKey();
         if ("nckd_new".equals(itemKey)) {
-            //弹出【批量】在职人员初定窗口
             FormShowParameter showParameter = new FormShowParameter();
             showParameter.setFormId(PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID);
             showParameter.getOpenStyle().setShowType(ShowType.Modal);
@@ -80,6 +66,7 @@ public class PerfManagerListPlugin extends AbstractListPlugin implements Plugin
     public void beforeDoOperation(BeforeDoOperationEventArgs args) {
         String operateKey = ((FormOperate) args.getSource()).getOperateKey();
         if (operateKey.equals("list_cyclegenerate")) {
+            //周期生成
             OperateOption option = OperateOption.create();
             option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE+"");
             DynamicObject perfManager = EntityHelper.newEntity(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
@@ -100,4 +87,34 @@ public class PerfManagerListPlugin extends AbstractListPlugin implements Plugin
             }
         }
     }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        if(e.getOperationResult() != null && e.getOperationResult().isSuccess()) {
+            String operateKey = e.getOperateKey();
+            if ("batchupdate".equals(operateKey)) {
+
+                this.getView().setVisible(Boolean.FALSE, "nckd_advconbaritemap","nckd_advconbaritemap1");
+                ListSelectedRowCollection selectedRows = this.getSelectedRows();
+                FormShowParameter showParameter = new FormShowParameter();
+                showParameter.setFormId(PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID);
+                showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                showParameter.setCaption("批量修改考核周期");
+                showParameter.setSendToClient(true);
+                showParameter.setCloseCallBack(new CloseCallBack(this, PerfManagerFormConstant.BATCHEVALCYCLE_ENTITYID));
+                showParameter.setCustomParam("nckd_isaddorupdate","2");
+                if(!selectedRows.isEmpty()) {
+                    List<Long> selectedIds = selectedRows.stream()
+                            .map(row -> ConvertUtil.toLong(row.getPrimaryKeyValue()))
+                            .collect(Collectors.toList());
+                    showParameter.setCustomParam("selectedRows", selectedIds);
+                }
+                StyleCss styleCss = new StyleCss();
+               /* styleCss.setWidth("1100");
+                styleCss.setHeight("600");*/
+                showParameter.getOpenStyle().setInlineStyleCss(styleCss);
+                this.getView().showForm(showParameter);
+            }
+        }
+    }
 }

+ 39 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailFormPlugin.java

@@ -0,0 +1,39 @@
+package nckd.jxccl.opmc.pm.plugin.form.result;
+
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.form.CloseCallBack;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.ShowType;
+import kd.bos.form.StyleCss;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+* 年度绩效结果明细-插件
+* 实体标识:nckd_annualperfdetail
+* @author W.Y.C
+* @date 2025/11/15 21:51
+* @version 1.0
+*/
+public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        if(e.getOperationResult() != null && e.getOperationResult().isSuccess()) {
+            String operateKey = e.getOperateKey();
+            if ("deemedresult".equals(operateKey)) {
+
+                FormShowParameter showParameter = new FormShowParameter();
+                showParameter.setFormId("nckd_updateresult");
+                showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                this.getView().showForm(showParameter);
+            }
+        }
+    }
+}

+ 230 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailReportListDataPlugin.java

@@ -0,0 +1,230 @@
+package nckd.jxccl.opmc.pm.plugin.form.result;
+
+import kd.bos.algo.DataSet;
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.QueryEntityType;
+import kd.bos.entity.report.*;
+import kd.bos.ext.hr.service.query.QueryEntityHelper;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
+
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+* 年度绩效结果明细-报表取数插件
+* 实体标识:nckd_annualperfdetail
+* @author W.Y.C
+* @date 2025/11/13 16:09
+* @version 1.0
+*/
+public class AnnualPerfDetailReportListDataPlugin extends AbstractReportListDataPlugin implements Plugin {
+
+    @Override
+    public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable {
+
+        // 构建查询字段
+        QueryFieldBuilder queryFieldBuilder = buildQueryFieldBuilder();
+
+
+        // 获取5年前的年份
+        int fiveYearsAgo = LocalDate.now().minusYears(5).getYear();
+        Date beginDate = DateUtil.toDate(LocalDate.of(fiveYearsAgo, 1, 1));
+
+        int currentYear = LocalDate.now().getYear() - 1; // 不包含今年
+        Date endDate = DateUtil.toDate(LocalDate.of(currentYear, 1, 1));
+        //查询最近5年的数据
+        QFilter qFilter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, endDate)
+                .and(new QFilter(PerfManagerFormConstant.NCKD_ENDYEAR, QCP.large_equals, beginDate));
+
+        // 处理快速过滤条件
+        processFastFilter(reportQueryParam, qFilter);
+
+        //其他过滤条件
+        processFilter(reportQueryParam, qFilter);
+
+        // 执行基础查询
+        QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("annualperfdetailquery");
+        DataSet dataSet = QueryEntityHelper.getInstance().getQueryDataSet(queryEntityType,queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, null);
+
+        dataSet.print( true);
+
+        //过滤调实际结束实际之外的年份分录
+        DataSet withYearFields = dataSet.filter("year("+String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALYEAR) +") " +
+                "<= year(CASE WHEN nckd_actendyear is not null THEN nckd_actendyear ELSE "+PerfManagerFormConstant.NCKD_ENDYEAR+" end)");
+        withYearFields.print(true);
+
+        // 动态添加年份字段;行转列
+        int tempIndex = 1;
+        for (int year = fiveYearsAgo; year <= currentYear; year++) {
+            withYearFields = withYearFields.addField(
+                    "CASE WHEN year(" + String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALYEAR) + ") = " + year + " THEN " + String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT,FormConstant.ID_KEY)+ " ELSE null END",
+                    "temp_" + tempIndex
+            );
+            tempIndex++;
+        }
+        withYearFields.print(true);
+
+        // 按分组字段聚合
+        DataSet result = withYearFields.groupBy(new String[]{
+                        String.join(".", FormConstant.NCKD_EMPPOSORGREL,FormConstant.EMPLOYEE_KEY, FormConstant.EMP_NUMBER_KEY),
+                        String.join(".", FormConstant.NCKD_EMPPOSORGREL,FormConstant.EMPLOYEE_KEY, FormConstant.NAME_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_FIRSTORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_SECONDORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_THIRDORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_FOURTHORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_FIFTHORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.NCKD_SIXTHORG,FormConstant.ID_KEY),
+                        String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.POS_STATUS,FormConstant.ID_KEY),
+                        String.join(".", "last_perfmanager", PerfManagerFormConstant.NCKD_BEGINYEAR),
+                        String.join(".", "last_perfmanager", PerfManagerFormConstant.NCKD_ENDYEAR)
+                })
+                .max("temp_1", "1_result")
+                .max("temp_2", "2_result")
+                .max("temp_3", "3_result")
+                .max("temp_4", "4_result")
+                .max("temp_5", "5_result")
+                .finish();
+        result.print( true);
+        return result;
+    }
+
+    @Override
+    public List<AbstractReportColumn> getColumns(List<AbstractReportColumn> columns) throws Throwable {
+        // 获取5年前的年份
+        int fiveYearsAgo = LocalDate.now().minusYears(5).getYear();
+        int currentYear = LocalDate.now().getYear() - 1; // 不包含今年
+
+        for (AbstractReportColumn column : columns) {
+            if (column instanceof ReportColumn) {
+                ReportColumn rColumn = (ReportColumn) column;
+                String fieldKey = rColumn.getFieldKey();
+                // 检查是否为年份相关的字段
+                if (fieldKey != null && fieldKey.startsWith("nckd_year")) {
+                    try {
+                        // 提取年份索引(从字段名中提取数字)
+                        int yearIndex = Integer.parseInt(fieldKey.replace("nckd_year", ""));
+                        // 设置对应年份的标题
+                        int displayYear = fiveYearsAgo + yearIndex - 1;
+                        rColumn.setCaption(new LocaleString(displayYear + "年"));
+                    } catch (NumberFormatException ignored) {
+                    }
+                }
+            }
+        }
+        return columns;
+    }
+
+    /**
+     * 构建查询字段
+     */
+    private QueryFieldBuilder buildQueryFieldBuilder() {
+        return QueryFieldBuilder.create()
+                .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
+                .add(PerfManagerFormConstant.NCKD_ENDYEAR)
+                .add(PerfManagerFormConstant.NCKD_ACTENDYEAR)
+                .addGroup(new String[]{PerfManagerFormConstant.PERFMANAGERENTRY},PerfManagerFormConstant.NCKD_APPRAISALRESULT)
+                .addIdNumberName(PerfManagerFormConstant.PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALRESULT)
+                .addGroup(new String[]{PerfManagerFormConstant.PERFMANAGERENTRY},PerfManagerFormConstant.NCKD_APPRAISALYEAR)
+                .addGroup(new String[]{PerfManagerFormConstant.PERFMANAGERENTRY},PerfManagerFormConstant.ID_KEY)
+                .addGroup(new String[]{FormConstant.NCKD_EMPPOSORGREL,FormConstant.EMPLOYEE_KEY}, FormConstant.EMP_NUMBER_KEY, FormConstant.NAME_KEY)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.POS_STATUS)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_FIRSTORG)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_SECONDORG)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_THIRDORG)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_FOURTHORG)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_FIFTHORG)
+                .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_SIXTHORG)
+                .addGroup(new String[]{"last_perfmanager"},
+                        PerfManagerFormConstant.NCKD_BEGINYEAR,
+                        PerfManagerFormConstant.NCKD_ENDYEAR);
+
+    }
+
+    /**
+     * 处理过滤条件
+     */
+    private void processFilter(ReportQueryParam reportQueryParam, QFilter qFilter) {
+        List<QFilter> qFilters = reportQueryParam.getFilter().getQFilters();
+            for (QFilter filter : qFilters) {
+            //由于页面命名限制不允许配置对应数据库字段(例如:position.id的格式),这里只能手动转换为数据库实际的字段
+            String property = filter.getProperty();
+            if ("nckd_posstatus.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", FormConstant.HRPI_EMPPOSORGREL,FormConstant.POS_STATUS,FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_beginyearfilter".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter("last_perfmanager."+PerfManagerFormConstant.NCKD_BEGINYEAR,filter.getCP(),filter.getValue()));
+            }else if("nckd_endyearfilter".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter("last_perfmanager."+PerfManagerFormConstant.NCKD_ENDYEAR,filter.getCP(),filter.getValue()));
+            }
+        }
+    }
+
+    /**
+     * 处理快速过滤条件
+     */
+    private void processFastFilter(ReportQueryParam reportQueryParam, QFilter qFilter) {
+        FastFilter fastFilter = reportQueryParam.getFilter().getFastFilter();
+        if (fastFilter != null) {
+            List<Map<String, List<Object>>> fastFilterList = fastFilter.getFastFilter();
+            for (Map<String, List<Object>> stringListMap : fastFilterList) {
+                //nckd_empnumfastfilter转为employee.empnumber,nckd_namefastfilter转换为employee.name,转换后添加到fields中
+                List<Object> fieldList = stringListMap.get("FieldName");
+                List<Object> valueList = stringListMap.get("Value");
+                String[] fields = new String[fieldList.size()];
+                for (int i = 0; i < fieldList.size(); i++) {
+                    fields[i] = fieldList.get(i).toString()
+                            .replace("nckd_empnumfastfilter", String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY,FormConstant.EMP_NUMBER_KEY))
+                            .replace("nckd_namefastfilter", String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.EMPLOYEE_KEY,FormConstant.NAME_KEY));
+                }
+                //valueList转到values
+                String[] values = new String[valueList.size()];
+                for (int i = 0; i < valueList.size(); i++) {
+                    values[i] = valueList.get(i).toString();
+                }
+                QFilter orFilter = null;
+                for (String field : fields) {
+                    for (String value : values) {
+                        if (orFilter == null) {
+                            orFilter = new QFilter(field, QCP.like, "%" + value + "%");
+                        } else {
+                            orFilter = orFilter.or(field, QCP.like, "%" + value + "%");
+                        }
+                    }
+
+                }
+
+               if(orFilter != null) {
+                   qFilter.and(orFilter);
+               }
+            }
+        }
+    }
+
+    /**
+     * 处理过滤条件
+     */
+   /* private void processFilter(ReportQueryParam reportQueryParam, QFilter qFilter) {
+        List<QFilter> qFilters = reportQueryParam.getFilter().getQFilters();
+        for (QFilter filter : qFilters) {
+            //由于页面命名限制不允许配置对应数据库字段(例如:position.id的格式),这里只能手动转换为数据库实际的字段
+            String property = filter.getProperty();
+            if ("nckd_positionfilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_orgfilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", FormConstant.COMPANY_KEY, FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_joblevelhrffilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_begindatefilter".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_BEGINDATE),filter.getCP(),filter.getValue()));
+            }
+        }
+    }*/
+}

+ 35 - 16
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/CycleGenerateOpPlugin.java

@@ -1,6 +1,5 @@
 package nckd.jxccl.opmc.pm.plugin.operate.cycle;
 
-import com.google.common.collect.Maps;
 import kd.bos.algo.Algo;
 import kd.bos.algo.AlgoContext;
 import kd.bos.algo.DataSet;
@@ -19,26 +18,22 @@ import kd.bos.entity.operate.result.IOperateInfo;
 import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
-import kd.bos.entity.plugin.args.RollbackOperationArgs;
 import kd.bos.ext.hr.service.query.QueryEntityHelper;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
-import kd.bos.servicehelper.BusinessDataServiceHelper;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
-import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.hr.hbp.common.model.AuthorizedOrgResultWithSub;
 import kd.hr.hbp.common.model.OrgSubInfo;
 import kd.sdk.hr.hbp.business.helper.permission.HRPermissionServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
-import nckd.jxccl.base.common.constant.SystemQueryConstant;
+import nckd.jxccl.base.common.constant.QueryConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.exception.ValidationException;
 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.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
 import java.time.LocalDateTime;
@@ -72,7 +67,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
         //先结束当前周期
-        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+       /* QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
                 .add(PerfManagerFormConstant.NCKD_WHYEND)
                 .add(PerfManagerFormConstant.NCKD_WAGEEXPLAIN)
@@ -91,7 +86,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
             //实际结束时间(如果endYear大于当前时间(年)则使用endYear否则使用当前时间(年))
             dbPerfManager.set(PerfManagerFormConstant.NCKD_ACTENDYEAR,DateUtil.toDate(personPerfInfo.getActEndYear()));
         }
-        SaveServiceHelper.update(dbPerfManagerArray);
+        SaveServiceHelper.update(dbPerfManagerArray);*/
 
 
         //调用save-op
@@ -99,6 +94,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
         for (PerfManagerSaveOpPlugin.PersonPerfInfo value : pendingCyclePersonnelMap.values()) {
             DynamicObject newPerfManager = EntityHelper.newAvailableBasicEntity(PerfManagerFormConstant.PERFMANAGER_ENTITYID);
             String personName = value.getPerson().getString(FormConstant.NAME_KEY);
+            newPerfManager.set(FormConstant.NCKD_EMPPOSORGREL, value.getEmpPosOrgRel());
             newPerfManager.set(FormConstant.NCKD_PERSON, value.getPerson());
             newPerfManager.set(PerfManagerFormConstant.NCKD_BEGINYEAR, DateUtil.toDate(value.getBeginYear()));
             newPerfManager.set(PerfManagerFormConstant.NCKD_ENDYEAR, DateUtil.toDate(value.getEndYear()));
@@ -115,6 +111,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
         }else {
             OperateOption option = OperateOption.create();
             option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE + "");
+            option.setVariableValue("cyclegenerate", Boolean.TRUE + "");
             OperationResult operationResult = OperationServiceHelper.executeOperate(FormConstant.SAVE_OP, PerfManagerFormConstant.PERFMANAGER_ENTITYID, savePerfManager.toArray(new DynamicObject[0]), option);
 
             if (!operationResult.isSuccess()) {
@@ -151,8 +148,8 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
      */
     public static Map<Long, PerfManagerSaveOpPlugin.PersonPerfInfo> getPendingCyclePersonnel() {
         LocalDateTime now = DateUtil.beginOfYear(DateUtil.now());
-        LocalDateTime lastDate = now;
-        // LocalDateTime lastDate = DateUtil.minusYears(now, 1);
+        // LocalDateTime lastDate = now;
+        LocalDateTime lastDate = DateUtil.minusYears(now, 1);
         LocalDateTime lastBegin = DateUtil.beginOfYear(lastDate);
         LocalDateTime lastEnd = DateUtil.endOfYear(lastDate);
 
@@ -185,6 +182,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
             DataSet perfManagerQueryDataSet = QueryEntityHelper.getInstance().getQueryDataSet(
                     perfManagerQueryEntityType, perfManagerQueryFieldBuilder.buildSelectLowerCase(),
                     new QFilter[]{perfManagerFilter}, perfManagerQueryFieldBuilder.buildOrder());
+            perfManagerQueryDataSet.print(true);
 
             // 收集所有人员ID
             collectPersonIds(personIds, perfManagerQueryDataSet.copy());
@@ -231,8 +229,9 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
      */
     private static DynamicObjectCollection queryNewHirePersons(QFilter filter) {
         QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                .addIdNumberName(FormConstant.HRPI_EMPLOYEE);
-        QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(SystemQueryConstant.HSPM_ASSIGNMENTQUERY);
+                .addIdNumberName(FormConstant.HRPI_EMPLOYEE)
+                .addGroup(new String[]{FormConstant.HRPI_EMPPOSORGREL},FormConstant.ID_KEY);
+        QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(QueryConstant.PERSON_QUERY);
         return QueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFieldBuilder.buildSelect(), new QFilter[]{filter}, null);
     }
 
@@ -245,6 +244,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
             List<Long> orgIds = extractOrgIds(userAdminOrgWithSub.getHasPermOrgsWithSub());
             perfManagerFilter.and(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG), QCP.in, orgIds);
         }
+        perfManagerFilter.and(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.IS_SEQLATESTRECORD), QCP.equals,EnableEnum.YES.getCode());
         return perfManagerFilter;
     }
 
@@ -258,6 +258,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
                 .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
                 .add(PerfManagerFormConstant.NCKD_ENDYEAR)
                 .addIdNumberName(FormConstant.NCKD_PERSON)
+                .addGroup(new String[]{FormConstant.HRPI_EMPPOSORGREL},FormConstant.ID_KEY)
                 .addGroup(new String[]{PerfManagerFormConstant.PERFMANAGERENTRY}, FormConstant.ID_KEY, PerfManagerFormConstant.NCKD_APPRAISALYEAR)
                 .addIdNumberName(PerfManagerFormConstant.PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT);
     }
@@ -282,14 +283,18 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
                                                  LocalDateTime now) {
         for (DynamicObject person : newHirePersonList) {
             long personId = person.getLong(String.join(".", FormConstant.HRPI_EMPLOYEE, FormConstant.ID_KEY));
+            long empPosOrgRelId = person.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY));
             // 判断是否已有周期,如果有则不生成
             if (!personIds.contains(personId)) {
+                DynamicObject empPosOrgRel = EntityHelper.newEntity(FormConstant.HRPI_EMPPOSORGREL);
+                empPosOrgRel.set(FormConstant.ID_KEY, empPosOrgRelId);
+
                 String personName = person.getString(String.join(".", FormConstant.HRPI_EMPLOYEE, FormConstant.NAME_KEY));
                 DynamicObject cyclePerson = EntityHelper.newEntity(FormConstant.HRPI_EMPLOYEE);
                 cyclePerson.set(FormConstant.ID_KEY, personId);
                 cyclePerson.set(FormConstant.NAME_KEY, personName);
                 PerfManagerSaveOpPlugin.PersonPerfInfo personPerfInfo = new PerfManagerSaveOpPlugin.PersonPerfInfo(
-                        cyclePerson, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成(新入职)");
+                        cyclePerson,empPosOrgRel, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成(新入职)");
                 personPerfInfo.setWhyEnd("新入职员工");
                 personPerfInfoMap.put(personId, personPerfInfo);
             }
@@ -315,17 +320,21 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
             Row next = lastYearFailFilter.next();
             Long id = next.getLong(FormConstant.ID_KEY);
             Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            Long empPosOrgRelId = next.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY));
             String personName = next.getString(String.join(".", FormConstant.NCKD_PERSON, FormConstant.NAME_KEY));
             String appraisalResult = next.getString(String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY,
                     PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
             Date endYear = next.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
 
+            DynamicObject empPosOrgRel = EntityHelper.newEntity(FormConstant.HRPI_EMPPOSORGREL);
+            empPosOrgRel.set(FormConstant.ID_KEY, empPosOrgRelId);
+
             DynamicObject person = EntityHelper.newEntity(FormConstant.HRPI_EMPLOYEE);
             person.set(FormConstant.ID_KEY, personId);
             person.set(FormConstant.NAME_KEY, personName);
 
             PerfManagerSaveOpPlugin.PersonPerfInfo personPerfInfo = new PerfManagerSaveOpPlugin.PersonPerfInfo(
-                    person, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
+                    person,empPosOrgRel, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
             personPerfInfo.setId(id);
 
             LocalDateTime endYearLocalDateTime = DateUtil.toLocalDateTime(endYear);
@@ -361,11 +370,13 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
         Map<Long, String> personIdAndPersonNameMap = new HashMap<>();
         Map<Long, Long> personIdAndIdMap = new HashMap<>();
         Map<Long, Date> personIdAndEndDateMap = new HashMap<>();
+        Map<Long, Long> personIdAndEmpPosOrgRelMap = new HashMap<>();
 
         while (lastYearNoPerfFilter.hasNext()) {
             Row next = lastYearNoPerfFilter.next();
             Long id = next.getLong(FormConstant.ID_KEY);
             Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            Long empPosOrgRelId = next.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY));
             String personName = next.getString(String.join(".", FormConstant.NCKD_PERSON, FormConstant.NAME_KEY));
             String appraisalResultNumber = next.getString(String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY,
                     PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
@@ -379,6 +390,7 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
                 personIdAndPersonNameMap.put(personId, personName);
                 personIdAndIdMap.put(personId, id);
                 personIdAndEndDateMap.put(personId, endYear);
+                personIdAndEmpPosOrgRelMap.put(personId, empPosOrgRelId);
             }
         }
 
@@ -390,12 +402,15 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
 
         for (Long filteredPersonId : filteredPersonIds) {
             String personName = personIdAndPersonNameMap.get(filteredPersonId);
+            DynamicObject empPosOrgRel = EntityHelper.newEntity(FormConstant.HRPI_EMPPOSORGREL);
+            empPosOrgRel.set(FormConstant.ID_KEY, personIdAndEmpPosOrgRelMap.get(filteredPersonId));
+
             DynamicObject person = EntityHelper.newEntity(FormConstant.HRPI_EMPLOYEE);
             person.set(FormConstant.ID_KEY, filteredPersonId);
             person.set(FormConstant.NAME_KEY, personName);
 
             PerfManagerSaveOpPlugin.PersonPerfInfo personPerfInfo = new PerfManagerSaveOpPlugin.PersonPerfInfo(
-                    person, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
+                    person,empPosOrgRel, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
             personPerfInfo.setId(personIdAndIdMap.get(filteredPersonId));
             personPerfInfo.setWhyEnd("上年度无绩效结果");
 
@@ -423,16 +438,20 @@ public class CycleGenerateOpPlugin extends AbstractOperationServicePlugIn implem
             Row next = yearEvalEndFilter.next();
             Long id = next.getLong(FormConstant.ID_KEY);
             Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            Long empPosOrgRelId = next.getLong(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ID_KEY));
             String personName = next.getString(String.join(".", FormConstant.NCKD_PERSON, FormConstant.NAME_KEY));
             Date endYear = next.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
 
             if (personPerfInfoMap.get(personId) == null) {
+                DynamicObject empPosOrgRel = EntityHelper.newEntity(FormConstant.HRPI_EMPPOSORGREL);
+                empPosOrgRel.set(FormConstant.ID_KEY, empPosOrgRelId);
+
                 DynamicObject person = EntityHelper.newEntity(FormConstant.HRPI_EMPLOYEE);
                 person.set(FormConstant.ID_KEY, personId);
                 person.set(FormConstant.NAME_KEY, personName);
 
                 PerfManagerSaveOpPlugin.PersonPerfInfo personPerfInfo = new PerfManagerSaveOpPlugin.PersonPerfInfo(
-                        person, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
+                        person,empPosOrgRel, DateUtil.toDate(now), DateUtil.toDate(DateUtil.addYears(now, 2)), "周期生成");
                 personPerfInfo.setId(id);
                 personPerfInfo.setWhyEnd("三年考评周期结束(周期生成)");
 

+ 3 - 3
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerDeleteOpPlugin.java

@@ -43,7 +43,7 @@ public class PerfManagerDeleteOpPlugin extends AbstractOperationServicePlugIn im
                     DynamicObject data = rowDataEntity.getDataEntity();
                     boolean isCurrentNewest = data.getBoolean(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST);
                     if(!isCurrentNewest){
-                        addMessage(rowDataEntity,"不能删除历史周期,只能删除最新考核周期");
+                        // addMessage(rowDataEntity,"不能删除历史周期,只能删除最新考核周期");
                     }
                     String theStatus = data.getString(PerfManagerFormConstant.NCKD_THESTATUS);
                     if(!"1".equalsIgnoreCase(theStatus)){
@@ -67,7 +67,7 @@ public class PerfManagerDeleteOpPlugin extends AbstractOperationServicePlugIn im
         }
         //将人员所有周期标记为非最新
         PerfManagerHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
-        //将当前人员最新周期(周期开始时间最近的一条)标记为最新的
-        PerfManagerHelper.markAsCurrentNewest(personIds,new QFilter(FormConstant.ID_KEY, QCP.not_in,ids));
+        //将当前人员最新周期(周期开始时间最近的一条)标记为最新的,并将最新一条“结束”状态标记为“正常”状态
+        PerfManagerHelper.markAsCurrentNewestAndUpdateStatus(personIds,new QFilter(FormConstant.ID_KEY, QCP.not_in,ids));
     }
 }

+ 439 - 111
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerSaveOpPlugin.java

@@ -23,6 +23,7 @@ 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.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
@@ -31,15 +32,7 @@ import nckd.jxccl.opmc.pm.helper.PerfManagerHelper;
 import org.apache.commons.lang3.StringUtils;
 
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.StringJoiner;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -51,11 +44,9 @@ import java.util.stream.Collectors;
  */
 public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
 
-    private List<PersonPerfInfo> personPerfInfos = new ArrayList<>();
 
-    private Map<Long, DynamicObject> lastPerfManagerMap;
 
-    private  List<Long> personIds;
+    Map<Long, Date> personBeginYearMap = new HashMap<>();
     private  List<Long> ids = new ArrayList<>();
 
     private final static String INTERACTION_SPONORE = PerfManagerSaveOpPlugin.class.getName();
@@ -63,80 +54,158 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
     @Override
     public void onAddValidators(AddValidatorsEventArgs e) {
+        String invoker = (String)this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        boolean dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker);
+        boolean isUpdate = ConvertUtil.toBoolean(this.getOption().getVariableValue("isUpdate",StringUtils.EMPTY),Boolean.FALSE);
         e.addValidator(new AbstractValidator() {
             @Override
             public void validate() {
+                Boolean isCycleGenerate = ConvertUtil.toBoolean(this.getOption().getVariableValue("cyclegenerate", StringUtils.EMPTY), Boolean.FALSE);
                 //第一个循环先获取表单数据,这里需要兼容单人和批量的数据包
+                Map<Long, List<PersonPerfInfo>> currentBatchData = new HashMap<>();
                 for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
                     DynamicObject data = rowDataEntity.getDataEntity();
                     int dataEntityIndex = rowDataEntity.getDataEntityIndex();
                     long id = data.getLong(FormConstant.ID_KEY);
-
                     DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
+                    person = person == null ? data.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
+                    data.set(FormConstant.NCKD_PERSON, person);
                     Date beginYear = data.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
                     Date endYear = data.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
                     String description = data.getString(FormConstant.DESCRIPTION_KEY);
                     DynamicObjectCollection perfManagerEntry = data.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
-                    PersonPerfInfo personPerfInfo = new PersonPerfInfo(person, beginYear, endYear, description, dataEntityIndex, perfManagerEntry);
-                    if(id > 0){
-                        personPerfInfo.setId(id);
+                    Long personId = person.getLong(FormConstant.ID_KEY);
+                    if (beginYear != null) {
+                        personBeginYearMap.put(personId, beginYear);
+                    }
+                    if (id > 0) {
                         ids.add(id);
                     }
-                    personPerfInfos.add(personPerfInfo);
+
+                    PersonPerfInfo perfInfo = new PersonPerfInfo(person, null,
+                            data.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR),
+                            data.getDate(PerfManagerFormConstant.NCKD_ENDYEAR),
+                            data.getString(FormConstant.DESCRIPTION_KEY),
+                            rowDataEntity.getDataEntityIndex());
+
+                    currentBatchData.computeIfAbsent(personId, k -> new ArrayList<>()).add(perfInfo);
+                }
+                if(!isUpdate) {
+                    if (!dataMigration) {
+                        if (!isCycleGenerate) {
+                            //检查同一批次内同一人员的周期重叠
+                            for (Map.Entry<Long, List<PersonPerfInfo>> entry : currentBatchData.entrySet()) {
+                                List<PersonPerfInfo> personPerfList = entry.getValue();
+                                if (personPerfList.size() > 1) {
+                                    // 对同一人员的多个周期进行相互比较
+                                    for (int i = 0; i < personPerfList.size(); i++) {
+                                        PersonPerfInfo info1 = personPerfList.get(i);
+                                        for (int j = i + 1; j < personPerfList.size(); j++) {
+                                            PersonPerfInfo info2 = personPerfList.get(j);
+                                            String personName = info2.getPerson().getString(FormConstant.NAME_KEY);
+
+                                            // 检查开始年份是否相同
+                                            if (isSameYear(info1.getBeginYear(), info2.getBeginYear())) {
+                                                addFatalErrorMessage(getDataEntities()[info1.getDataEntityIndex()],
+                                                        StrFormatter.format("同批次数据中,人员【{}】存在相同的周期开始年份:{}",
+                                                                personName, info1.getBeginYear().getYear()));
+                                            } else {
+                                                // 只有开始年份不相同时才检查重叠
+                                                // 检查周期是否重叠
+                                                String overlapInfo = getCycleOverlapInfo(
+                                                        info1.getBeginYear(), info1.getEndYear(), null,
+                                                        info2.getBeginYear(), info2.getEndYear(), null);
+
+                                                if (StringUtils.isNotBlank(overlapInfo)) {
+                                                    addFatalErrorMessage(getDataEntities()[info1.getDataEntityIndex()],
+                                                            StrFormatter.format("同批次数据中,人员【{}】存在重叠周期:{}", personName, overlapInfo));
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
                 }
 
                 QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                         .addIdNumberName(FormConstant.NCKD_PERSON)
                         .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
-                        .add(PerfManagerFormConstant.NCKD_ENDYEAR);
-                personIds = personPerfInfos.stream()
-                        .map(personPerfInfo -> personPerfInfo.getPerson().getLong(FormConstant.ID_KEY))
-                        .collect(Collectors.toList());
-                QFilter filter = new QFilter(FormConstant.NCKD_PERSON, QCP.in, personIds)
-                        .and(PerfManagerFormConstant.NCKD_THESTATUS,QCP.equals,"1");
-                if(!ids.isEmpty()){
+                        .add(PerfManagerFormConstant.NCKD_ENDYEAR)
+                        .add(PerfManagerFormConstant.NCKD_ACTENDYEAR);
+                QFilter filter = new QFilter(FormConstant.NCKD_PERSON, QCP.in, personBeginYearMap.keySet());
+                if(!ids.isEmpty()) {
                     filter = filter.and(new QFilter(FormConstant.ID_KEY, QCP.not_in, ids));
                 }
+                //根据人员查询出当前自己之外的其他考核周期
                 DynamicObjectCollection query = QueryServiceHelper.query(PerfManagerFormConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
-
-                //开始校验 - 优化后的代码
+                //按人员分组
                 Map<Long, List<DynamicObject>> groupedQueryResults = query.stream()
                         .collect(Collectors.groupingBy(
                                 dynamicObject -> dynamicObject.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
                         ));
-
-                for (PersonPerfInfo personPerfInfo : personPerfInfos) {
-
-                    long personId = personPerfInfo.getPerson().getLong(FormConstant.ID_KEY);
-                    LocalDateTime beginYear = personPerfInfo.getBeginYear();
-                    LocalDateTime endYear = personPerfInfo.getEndYear();
+                for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                    DynamicObject data = rowDataEntity.getDataEntity();
+                    DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
+                    person = person == null ? data.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
+                    long personId = person.getLong(FormConstant.ID_KEY);
+                    String personName = person.getString(FormConstant.NAME_KEY);
+                    LocalDateTime beginYear = ConvertUtil.toLocalDateTime(data.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    LocalDateTime endYear = ConvertUtil.toLocalDateTime(data.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
 
                     // 判断beginYear和endYear是不是间隔3年,例如:开始2025年,结束必须为2027年
                     if (beginYear != null && endYear != null) {
                         int beginYearValue = beginYear.getYear();
                         int endYearValue = endYear.getYear();
                         if (endYearValue - beginYearValue != 2) {
-                            addFatalErrorMessage(getDataEntities()[personPerfInfo.getDataEntityIndex()],
+                            addFatalErrorMessage(rowDataEntity,
                                     StrFormatter.format("周期开始年份【{}】与结束年份【{}】必须间隔3年,请检查!",
                                             beginYearValue, endYearValue));
                         }
                     }
 
-
                     //校验是否存在相同周期开始年的记录 begin
                     List<DynamicObject> personRecords = groupedQueryResults.getOrDefault(personId, Collections.emptyList());
                     for (DynamicObject dynamicObject : personRecords) {
                         LocalDateTime dbBeginYear = DateUtil.toLocalDateTime(dynamicObject.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
-                        //判断beginYear和dbBeginYear的年份是否相同
-                        if (isSameYear(beginYear, dbBeginYear)) {
-                            addFatalErrorMessage(getDataEntities()[personPerfInfo.getDataEntityIndex()],
-                                    StrFormatter.format("人员【{}】已经存在周期开始年份【{}】的周期,无需进行创建。",
-                                            personPerfInfo.getPerson().getString(FormConstant.NAME_KEY),
-                                            beginYear.getYear()));
+                        LocalDateTime dbEndYear = DateUtil.toLocalDateTime(dynamicObject.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                        Date actEndYear = dynamicObject.getDate(PerfManagerFormConstant.NCKD_ACTENDYEAR);
+                        LocalDateTime dbActEndYear = actEndYear != null ? DateUtil.toLocalDateTime(actEndYear) : null;
+                        if(!isUpdate) {
+                            if (!dataMigration) {
+                                if (!isCycleGenerate) {
+                                    // 判断周期是否重叠并获取重叠信息,已结束周期使用实际结束时间
+                                    String overlapInfo = getCycleOverlapInfo(beginYear, endYear, null, dbBeginYear, dbEndYear, dbActEndYear);
+                                    if (StringUtils.isNotBlank(overlapInfo)) {
+                                        addFatalErrorMessage(rowDataEntity,
+                                                StrFormatter.format("人员【{}】的考核周期与已有周期在{}重叠,请检查!",
+                                                        personName, overlapInfo));
+                                    }
+                                }
+                            }
+
+                            //判断beginYear和dbBeginYear的年份是否相同
+                            if (isSameYear(beginYear, dbBeginYear)) {
+                                addFatalErrorMessage(rowDataEntity,
+                                        StrFormatter.format("人员【{}】已经存在周期开始年份【{}】的周期,无需进行创建。",
+                                                personName,
+                                                beginYear.getYear()));
+                            }
                         }
+
+                        //当前周期必须大于之前周期的开始时间
+                        /*if (beginYear != null && beginYear.isBefore(dbBeginYear)) {
+                            addFatalErrorMessage(rowDataEntity,
+                                    StrFormatter.format("人员【{}】当前周期开始时间【{}】必须大于之前周期开始时间【{}】。",
+                                            personName,
+                                            beginYear.getYear(),
+                                            dbBeginYear.getYear()));
+                        }*/
                     }
+
                     //校验是否存在相同周期开始年的记录 end
-                    DynamicObjectCollection entrys = personPerfInfo.getEntrys();
+                    DynamicObjectCollection entrys = data.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
                     if(entrys != null) {
                         List<Date> dateList = entrys.stream().map(entry -> entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR)).collect(Collectors.toList());
                         if (beginYear != null && endYear != null) {
@@ -151,7 +220,7 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
                             if (!outOfRangeYears.isEmpty()) {
                                 String outOfRangeYearsStr = String.join(",", outOfRangeYears);
-                                addFatalErrorMessage(getDataEntities()[personPerfInfo.getDataEntityIndex()],
+                                addFatalErrorMessage(rowDataEntity,
                                         StrFormatter.format("考评年份【{}】不在周期范围内,请检查!", outOfRangeYearsStr));
                             }
                         }
@@ -159,11 +228,13 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
                         List<String> duplicateYears = getDuplicateYears(dateList);
                         if (!duplicateYears.isEmpty()) {
                             String duplicateYearsStr = String.join(",", duplicateYears);
-                            addFatalErrorMessage(getDataEntities()[personPerfInfo.getDataEntityIndex()],
+                            addFatalErrorMessage(rowDataEntity,
                                     StrFormatter.format("考评结果存在重复的考核年份【{}】,请检查!", duplicateYearsStr));
                         }
                     }
 
+                    //规则: 如果年度排名管理中已存在某年度的考核结果,则不允许在人员考评管理中修改该年度的考核结果。
+                    //规则: 对于已处理的调薪情况,不允许修改或删除相关的年度考核结果。
                 }
             }
 
@@ -173,36 +244,71 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
         });
     }
 
+    /**
+     * 判断两个考核周期是否重叠,并返回重叠描述
+     *
+     * @param beginYear1 第一个周期开始年份
+     * @param endYear1 第一个周期结束年份
+     * @param actEndYear1 第一个周期实际结束年份(如果已结束)
+     * @param beginYear2 第二个周期开始年份
+     * @param endYear2 第二个周期结束年份
+     * @param actEndYear2 第二个周期实际结束年份(如果已结束)
+     * @return 如果周期重叠返回重叠描述,否则返回空字符串
+     */
+    private String getCycleOverlapInfo(LocalDateTime beginYear1, LocalDateTime endYear1, LocalDateTime actEndYear1,
+                                       LocalDateTime beginYear2, LocalDateTime endYear2, LocalDateTime actEndYear2) {
+        if (beginYear1 == null || endYear1 == null || beginYear2 == null || endYear2 == null) {
+            return "";
+        }
+
+        // 如果周期已结束,使用实际结束时间,否则使用计划结束时间
+        LocalDateTime realEndYear1 = actEndYear1 != null ? actEndYear1 : endYear1;
+        LocalDateTime realEndYear2 = actEndYear2 != null ? actEndYear2 : endYear2;
+
+        // 两个区间重叠的条件是:一个区间的开始时间小于等于另一个区间的结束时间,
+        // 且该区间的结束时间大于等于另一个区间的开始时间
+        if (!beginYear1.isAfter(realEndYear2) && !realEndYear1.isBefore(beginYear2)) {
+            // 计算重叠区间
+            LocalDateTime overlapStart = beginYear1.isAfter(beginYear2) ? beginYear1 : beginYear2;
+            LocalDateTime overlapEnd = realEndYear1.isBefore(realEndYear2) ? realEndYear1 : realEndYear2;
+
+            if (overlapStart.equals(overlapEnd)) {
+                return StrFormatter.format("{}年", overlapStart.getYear());
+            } else {
+                return StrFormatter.format("{}年~{}年", overlapStart.getYear(), overlapEnd.getYear());
+            }
+        }
+
+        return "";
+    }
+
     @Override
     public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
-        //最新上一考核周期
-        QFilter filter = null;
-        if(!ids.isEmpty()) {
-            filter = new QFilter(FormConstant.ID_KEY, QCP.not_in, ids);
+        if(!this.getOperationResult().isSuccess()){
+            e.setCancel(true);
+            return;
         }
-        List<DynamicObject> newestPerfManagerByPerson = PerfManagerHelper.getNewestPerfManagerByPersonAndBeginYear(personIds,filter);
-        lastPerfManagerMap = newestPerfManagerByPerson.stream()
-                .collect(Collectors.toMap(
-                        perfManager -> perfManager.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY),
-                        perfManager -> perfManager,
-                        (existing, replacement) -> existing
-                ));
-
-        String ignoreInteraction = this.getOption().getVariableValue(OperateOptionConst.IGNOREINTERACTION, "");
-        if(!"true".equalsIgnoreCase(ignoreInteraction)){
-            personIds = personPerfInfos.stream()
-                    .map(personPerfInfo -> personPerfInfo.getPerson().getLong(FormConstant.ID_KEY))
-                    .collect(Collectors.toList());
-
-            StringJoiner confirMmsg = new StringJoiner(",");
-            for (DynamicObject lastPerfManager : lastPerfManagerMap.values()) {
-                String personName = lastPerfManager.getDynamicObject(FormConstant.NCKD_PERSON).getString(FormConstant.NAME_KEY);
-                LocalDateTime beginYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
-                LocalDateTime endYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
-                confirMmsg.add(StrFormatter.format("{}(周期:{}~{})", personName, beginYear.getYear(), endYear.getYear()));
+        String ignoreInteraction = this.getOption().getVariableValue(OperateOptionConst.IGNOREINTERACTION,StringUtils.EMPTY);
+        if(StringUtils.isBlank(ignoreInteraction) || !"true".equalsIgnoreCase(ignoreInteraction)) {
+            StringJoiner confirmMsg = new StringJoiner(",");
+            for (DynamicObject dataEntity : e.getDataEntities()) {
+                long id = dataEntity.getLong(FormConstant.ID_KEY);
+                Date date = dataEntity.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                long personId = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY);
+                QFilter filter = null;
+                if (id > 0) {
+                    filter = new QFilter(FormConstant.ID_KEY, QCP.not_in, id);
+                }
+                DynamicObject[] lastPerfManagerArray = PerfManagerHelper.getBeforeBeginYear(date, Collections.singletonList(personId), filter);
+                for (DynamicObject lastPerfManager : lastPerfManagerArray) {
+                    String personName = lastPerfManager.getDynamicObject(FormConstant.NCKD_PERSON).getString(FormConstant.NAME_KEY);
+                    LocalDateTime beginYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    LocalDateTime endYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                    confirmMsg.add(StrFormatter.format("{}(周期:{}~{})", personName, beginYear.getYear(), endYear.getYear()));
+                }
             }
-            if(!lastPerfManagerMap.isEmpty()){
-                e.cancel = !this.showInteractionMessage(confirMmsg.toString());
+            if (confirmMsg.length() > 0) {
+                e.cancel = !this.showInteractionMessage(confirmMsg.toString());
             }
         }
 
@@ -210,10 +316,16 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
         //事务开始之后将其他考核周期设置为非最新
-        PerfManagerHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
+        PerfManagerHelper.markAsNotCurrentNewest(personBeginYearMap.keySet().toArray(new Long[0]));
 
+        Boolean isCycleGenerate = ConvertUtil.toBoolean(this.getOption().getVariableValue("cyclegenerate",StringUtils.EMPTY),Boolean.FALSE);
+        Boolean isUpdate = ConvertUtil.toBoolean(this.getOption().getVariableValue("isUpdate",StringUtils.EMPTY),Boolean.FALSE);
+        List<DynamicObject> updatePerManager = new ArrayList<>();
         for (DynamicObject dataEntity : e.getDataEntities()) {
+            long id = dataEntity.getLong(FormConstant.ID_KEY);
             DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+            person = person == null ? dataEntity.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
+            dataEntity.set(FormConstant.NCKD_PERSON, person);
             LocalDateTime beginYear = DateUtil.toLocalDateTime(dataEntity.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
             LocalDateTime endYear = DateUtil.toLocalDateTime(dataEntity.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
             long personId = person.getLong(FormConstant.ID_KEY);
@@ -224,25 +336,259 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
             if(StringUtils.isBlank(name)){
                 dataEntity.set(FormConstant.NAME_KEY, StrFormatter.format("【{}】{}~{}的考核周期",personName,beginYear.getYear(),endYear.getYear()));
             }
-            //上一周期标记为“已结束”并设置“实际结束时间” begin
-            DynamicObject lastPerfManager = lastPerfManagerMap.get(personId);
-            if(lastPerfManager != null && "1".equalsIgnoreCase(lastPerfManager.getString(PerfManagerFormConstant.NCKD_THESTATUS))) {
-                lastPerfManager.set(PerfManagerFormConstant.NCKD_THESTATUS, "3");
-                LocalDateTime localDateTime = DateUtil.minusYears(beginYear, 1);
-                lastPerfManager.set(PerfManagerFormConstant.NCKD_ACTENDYEAR, DateUtil.toDate(localDateTime));
-                dataEntity.set(PerfManagerFormConstant.NCKD_LASTPERFMANAGER, lastPerfManagerMap.get(personId));
+
+
+            /*DynamicObject lastPerfManager = lastPerfManagerMap.get(personId);
+            if(lastPerfManager != null) {
+                LocalDateTime date = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                if("1".equalsIgnoreCase(lastPerfManager.getString(PerfManagerFormConstant.NCKD_THESTATUS)) && date.getYear() >= beginYear.getYear()) {
+
+                }
+
+               LocalDateTime lastBeginYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                LocalDateTime lastEndYear = DateUtil.toLocalDateTime(lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                if(!Objects.equals(lastBeginYear, beginYear) || !Objects.equals(lastEndYear, endYear)) {
+                    //当周期范围发生变化,需要保障数据连续性:若新旧考评周期存在重叠年份,且该年份在旧周期中已存在来源于【年度绩效排名】的考核结果,则新周期将自动继承该结果。
+                    DynamicObjectCollection lastPerfManagerEntrys = lastPerfManager.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                    DynamicObjectCollection perfManagerEntrys = dataEntity.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                    for (DynamicObject lastPerfManagerEntry : lastPerfManagerEntrys) {
+                        Date lastAppraisalYear = lastPerfManagerEntry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+                        DynamicObject appraisalResult = lastPerfManagerEntry.getDynamicObject(PerfManagerFormConstant.NCKD_APPRAISALRESULT);
+                        if (lastAppraisalYear != null && appraisalResult != null) {
+                            boolean isAllRankSource = lastPerfManagerEntry.getBoolean(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE);
+                            if (isAllRankSource) {
+                                LocalDateTime lastAppraisalYearLocalDateTime = DateUtil.toLocalDateTime(lastAppraisalYear);
+                                //判断appraisalYearLocalDateTime是不是在beginYear和endYear之间
+                                if (DateUtil.isInRange(lastAppraisalYearLocalDateTime, beginYear, endYear)) {
+                                    boolean isExist = false;
+                                    for (DynamicObject perfManager : perfManagerEntrys) {
+                                        Date appraisalYear = perfManager.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+                                        if(appraisalYear != null){
+                                            //如果分录存在相同年份则更新
+                                            LocalDateTime appraisalYearLocalDateTime = DateUtil.toLocalDateTime(appraisalYear);
+                                            if(appraisalYearLocalDateTime.getYear() == lastAppraisalYearLocalDateTime.getYear()){
+                                                perfManager.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, appraisalResult);
+                                                isExist = true;
+                                            }
+                                        }
+                                    }
+                                    if(!isExist) {
+                                        //分录不存在当前年份,则添加
+                                        DynamicObject dynamicObject = perfManagerEntrys.addNew();
+                                        dynamicObject.set(PerfManagerFormConstant.NCKD_APPRAISALYEAR, lastAppraisalYear);
+                                        dynamicObject.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, appraisalResult);
+                                        dynamicObject.set(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE, true);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }*/
+
+
+            //分录考核结果写入到表头 begin
+            dataEntity.set(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT, null);
+            dataEntity.set(PerfManagerFormConstant.NCKD_SECONDYEARRESULT, null);
+            dataEntity.set(PerfManagerFormConstant.NCKD_THIRDYEARRESULT, null);
+            DynamicObjectCollection entrys = dataEntity.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+            for (DynamicObject entry : entrys) {
+                boolean isAllRankSource = entry.getBoolean(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE);
+                Date appraisalYear = entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+                DynamicObject appraisalResult = entry.getDynamicObject(PerfManagerFormConstant.NCKD_APPRAISALRESULT);
+
+                if (appraisalYear != null && appraisalResult != null) {
+                    LocalDateTime appraisalLocalDate = DateUtil.toLocalDateTime(appraisalYear);
+                    LocalDateTime beginLocalDate = beginYear;
+
+                    // 计算是第几年(分录中的年份-(周期开始时间 + 1))
+                    long yearsDiff = appraisalLocalDate.getYear() - beginLocalDate.getYear() + 1;
+                    // 根据年份差异设置表头对应的考核结果字段
+                    switch ((int) yearsDiff) {
+                        case 1:
+                            dataEntity.set(PerfManagerFormConstant.NCKD_FIRSTYEARRESULT, appraisalResult);
+                            break;
+                        case 2:
+                            dataEntity.set(PerfManagerFormConstant.NCKD_SECONDYEARRESULT, appraisalResult);
+                            break;
+                        case 3:
+                            dataEntity.set(PerfManagerFormConstant.NCKD_THIRDYEARRESULT, appraisalResult);
+                            break;
+                    }
+                }
             }
-            //上一周期标记为“已结束”并设置“实际结束时间” end
+            //分录考核结果写入到表头 end
+        }
+
+    }
+
+    /**
+     * 查找给定日期之后的第一个考核周期
+     *
+     * @param allPerfManagerList 该人员的所有考核周期列表
+     * @param currentDate 当前周期的开始日期
+     * @return 下一个考核周期,如果不存在则返回null
+     */
+    private DynamicObject findNextCycle(List<DynamicObject> allPerfManagerList, Date currentDate) {
+        if (allPerfManagerList == null || allPerfManagerList.isEmpty() || currentDate == null) {
+            return null;
         }
 
+        LocalDateTime currentLocalDate = DateUtil.toLocalDateTime(currentDate);
 
-        DynamicObject[] lastPerfManagerArray = lastPerfManagerMap.values().toArray(new DynamicObject[0]);
-        SaveServiceHelper.update(lastPerfManagerArray);
+        return allPerfManagerList.stream()
+                .filter(Objects::nonNull)
+                .filter(perfManager -> {
+                    Date beginYear = perfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                    if (beginYear != null) {
+                        LocalDateTime beginYearLocal = DateUtil.toLocalDateTime(beginYear);
+                        return beginYearLocal.isAfter(currentLocalDate);
+                    }
+                    return false;
+                })
+                .min((perf1, perf2) -> {
+                    LocalDateTime begin1 = DateUtil.toLocalDateTime(perf1.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    LocalDateTime begin2 = DateUtil.toLocalDateTime(perf2.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    return begin1.compareTo(begin2);
+                })
+                .orElse(null);
+    }
+
+    /**
+     * 查找给定日期之前的最后一个考核周期
+     *
+     * @param allPerfManagerList 该人员的所有考核周期列表
+     * @param currentDate 当前周期的开始日期
+     * @return 上一个考核周期,如果不存在则返回null
+     */
+    private DynamicObject findPreviousCycle(List<DynamicObject> allPerfManagerList, Date currentDate) {
+        if (allPerfManagerList == null || allPerfManagerList.isEmpty() || currentDate == null) {
+            return null;
+        }
+
+        LocalDateTime currentLocalDate = DateUtil.toLocalDateTime(currentDate);
+
+        return allPerfManagerList.stream()
+                .filter(Objects::nonNull)
+                .filter(perfManager -> {
+                    Date beginYear = perfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                    if (beginYear != null) {
+                        LocalDateTime beginYearLocal = DateUtil.toLocalDateTime(beginYear);
+                        return beginYearLocal.isBefore(currentLocalDate);
+                    }
+                    return false;
+                })
+                .max((perf1, perf2) -> {
+                    LocalDateTime begin1 = DateUtil.toLocalDateTime(perf1.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    LocalDateTime begin2 = DateUtil.toLocalDateTime(perf2.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    return begin1.compareTo(begin2);
+                })
+                .orElse(null);
     }
 
     @Override
     public void endOperationTransaction(EndOperationTransactionArgs e) {
-        PerfManagerHelper.markAsCurrentNewest(personIds,null);
+        PerfManagerHelper.markAsCurrentNewest(personBeginYearMap.keySet(),null);
+
+        //上一周期标记为“已结束”并设置“实际结束时间” begin
+        Set<Long> personIds = new HashSet<>();
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+            person = person == null ? dataEntity.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
+            long personId = person.getLong(FormConstant.ID_KEY);
+            personIds.add(personId);
+        }
+
+
+        Boolean isCycleGenerate = ConvertUtil.toBoolean(this.getOption().getVariableValue("cyclegenerate",StringUtils.EMPTY),Boolean.FALSE);
+        Boolean isUpdate = ConvertUtil.toBoolean(this.getOption().getVariableValue("isUpdate",StringUtils.EMPTY),Boolean.FALSE);
+        DynamicObject[] beforeBeginYearList = null;
+        if(!isUpdate){
+            beforeBeginYearList = PerfManagerHelper.getByPersonId(personIds, null);
+        }
+        List<DynamicObject> updatePerManager = new ArrayList<>();
+        List<DynamicObject> updateDataEntity = new ArrayList<>();
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            long id = dataEntity.getLong(FormConstant.ID_KEY);
+            DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+            person = person == null ? dataEntity.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL).getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
+            dataEntity.set(FormConstant.NCKD_PERSON, person);
+            LocalDateTime beginYear = DateUtil.toLocalDateTime(dataEntity.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+            LocalDateTime endYear = DateUtil.toLocalDateTime(dataEntity.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+            long personId = person.getLong(FormConstant.ID_KEY);
+
+            if(!isUpdate) {
+                //找出当前周期前还未结束的考核周期
+                QFilter filter = null;
+                if (id > 0) {
+                    filter = new QFilter(FormConstant.ID_KEY, QCP.not_in, id);
+                }
+//                DynamicObject[] beforeBeginYear = PerfManagerHelper.getBeforeBeginYear(DateUtil.toDate(beginYear), Collections.singletonList(personId), filter);
+                List<DynamicObject> beforeBeginYear = Arrays.stream(beforeBeginYearList)
+                        .filter(obj -> {
+                            Date dbBeginYear = obj.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                            Long dbPersonId = obj.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY);
+                            Long dbId = obj.getLong(FormConstant.ID_KEY);
+                            String theStatus = obj.getString(PerfManagerFormConstant.NCKD_THESTATUS);
+                            return dbBeginYear != null && dbBeginYear.compareTo(DateUtil.toDate(beginYear)) <= 0 && dbPersonId.equals(personId) && !dbId.equals(id) && theStatus.equalsIgnoreCase(EnableEnum.YES.getCode());
+                        })
+                        .collect(Collectors.toList());
+                if (!beforeBeginYear.isEmpty()) {
+//                    DynamicObject[] allPerfManagerArray = PerfManagerHelper.getByPersonId(Collections.singletonList(personId), filter);
+                    List<DynamicObject> allPerfManagerList = Arrays.stream(beforeBeginYearList)
+                            .filter(obj -> {
+                                Long dbPersonId = obj.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY);
+                                Long dbId = obj.getLong(FormConstant.ID_KEY);
+                                return  dbPersonId.equals(personId) && !dbId.equals(id);
+                            })
+                            .collect(Collectors.toList());
+                    allPerfManagerList.add(dataEntity);
+                    for (DynamicObject lastPerfManager : beforeBeginYear) {
+                        lastPerfManager.set(PerfManagerFormConstant.NCKD_THESTATUS, "3");
+                        //找到当前周期的下一周期
+                        Date currentBeginYear = lastPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                        Date currentEndYear = lastPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
+
+                        DynamicObject nextCycle = findNextCycle(allPerfManagerList, currentBeginYear);
+                        if (nextCycle != null) {
+                            // 获取下一周期的开始年份
+                            Date nextBeginYear = nextCycle.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                            LocalDateTime nextBeginLocalDateTime = DateUtil.toLocalDateTime(nextBeginYear);
+                            // 计算预期的实际结束年份:下一周期开始时间-1年
+                            LocalDateTime expectedActEndYear = DateUtil.minusYears(nextBeginLocalDateTime, 1);
+                            LocalDateTime currentEndLocalDateTime = DateUtil.toLocalDateTime(currentEndYear);
+                            // 如果预期结束时间大于等于当前周期的结束时间,说明中间有断层
+                            // 则实际结束时间设置为当前周期的结束时间
+                            // 否则设置为预期的实际结束时间(下一周期开始时间-1年)
+                            LocalDateTime actEndYear = expectedActEndYear.isAfter(currentEndLocalDateTime) ?
+                                    currentEndLocalDateTime : expectedActEndYear;
+                            lastPerfManager.set(PerfManagerFormConstant.NCKD_ACTENDYEAR, DateUtil.toDate(actEndYear));
+                        } else {
+                            // 如果没有找到下一周期,则实际结束时间设置为当前周期的结束时间
+                            lastPerfManager.set(PerfManagerFormConstant.NCKD_ACTENDYEAR, currentEndYear);
+                        }
+                        String str = isCycleGenerate ? "周期生成" : "手动/导入新增周期";
+                        lastPerfManager.set(PerfManagerFormConstant.NCKD_WHYEND, StrFormatter.format("{}({}~{}),系统自动结束前周期", str, beginYear.getYear(), endYear.getYear()));
+                        updatePerManager.add(lastPerfManager);
+                    }
+                    //找到当前周期的上一周期
+                    DynamicObject previousCycle = findPreviousCycle(allPerfManagerList, DateUtil.toDate(beginYear));
+                    if (previousCycle != null) {
+                        dataEntity.set(PerfManagerFormConstant.NCKD_LASTPERFMANAGER, previousCycle);
+                        updateDataEntity.add(dataEntity);
+                    }
+                }
+            }
+
+        }
+        if(!updatePerManager.isEmpty()) {
+            DynamicObject[] lastPerfManagerArray = updatePerManager.toArray(new DynamicObject[0]);
+            SaveServiceHelper.update(lastPerfManagerArray);
+        }
+        if(!updateDataEntity.isEmpty()) {
+            SaveServiceHelper.update(updateDataEntity.toArray(new DynamicObject[0]));
+        }
+        //上一周期标记为“已结束”并设置“实际结束时间” end
     }
 
     private boolean showInteractionMessage(String confirMmsg) {
@@ -260,7 +606,7 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
         InteractionContext interactionContext = new InteractionContext();
         interactionContext.setSimpleMessage("存在【未结束】的考核周期");
         OperateErrorInfo errorInfo = new OperateErrorInfo();
-        errorInfo.setMessage(StrFormatter.format("检测到本次新增的人员中存在【未结束】的考核周期,新周期新增成功后会自动结束上一周期。{}", confirMmsg));
+        errorInfo.setMessage(StrFormatter.format("检测到本次新增的人员中存在【未结束】的考核周期,新周期新增成功后会自动结束上一周期,并将上一周期结束时间更新为当前新周期开始年前一年。{}", confirMmsg));
         errorInfo.setLevel(ErrorLevel.Warning);
 
         Map<String, String> custInfos = new HashMap<>();
@@ -297,34 +643,35 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
     public static class PersonPerfInfo {
         private  Long id;
         private final DynamicObject person;
+        private final DynamicObject empPosOrgRel;
         private final LocalDateTime beginYear;
         private final LocalDateTime endYear;
         private final String description;
         private int dataEntityIndex;
         private DynamicObjectCollection entrys;
-        private DynamicObject useorg;
-        private DynamicObject org;
-        private DynamicObject createorg;
         private String whyEnd;
         private LocalDateTime actEndYear;
 
-        public PersonPerfInfo(DynamicObject person, Date beginYear, Date endYear, String description) {
+        public PersonPerfInfo(DynamicObject person,DynamicObject empPosOrgRel, Date beginYear, Date endYear, String description) {
             this.person = person;
+            this.empPosOrgRel = empPosOrgRel;
             this.beginYear = DateUtil.toLocalDateTime(beginYear);
             this.endYear = DateUtil.toLocalDateTime(endYear);
             this.description = description;
         }
 
-        public PersonPerfInfo(DynamicObject person, Date beginYear, Date endYear, String description, int dataEntityIndex) {
+        public PersonPerfInfo(DynamicObject person, DynamicObject empPosOrgRel, Date beginYear, Date endYear, String description, int dataEntityIndex) {
             this.person = person;
+            this.empPosOrgRel = empPosOrgRel;
             this.beginYear = DateUtil.toLocalDateTime(beginYear);
             this.endYear = DateUtil.toLocalDateTime(endYear);
             this.description = description;
             this.dataEntityIndex = dataEntityIndex;
         }
 
-        public PersonPerfInfo(DynamicObject person, Date beginYear, Date endYear, String description, int dataEntityIndex, DynamicObjectCollection entrys) {
+        public PersonPerfInfo(DynamicObject person, DynamicObject empPosOrgRel, Date beginYear, Date endYear, String description, int dataEntityIndex, DynamicObjectCollection entrys) {
             this.person = person;
+            this.empPosOrgRel = empPosOrgRel;
             this.beginYear = DateUtil.toLocalDateTime(beginYear);
             this.endYear = DateUtil.toLocalDateTime(endYear);
             this.description = description;
@@ -352,33 +699,14 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
             return person;
         }
 
-        public DynamicObjectCollection getEntrys() {
-            return entrys;
-        }
-
-        public DynamicObject getCreateorg() {
-            return createorg;
-        }
-
-        public void setCreateorg(DynamicObject createorg) {
-            this.createorg = createorg;
-        }
-
-        public DynamicObject getOrg() {
-            return org;
-        }
-
-        public void setOrg(DynamicObject org) {
-            this.org = org;
+        public DynamicObject getEmpPosOrgRel() {
+            return empPosOrgRel;
         }
 
-        public DynamicObject getUseorg() {
-            return useorg;
+        public DynamicObjectCollection getEntrys() {
+            return entrys;
         }
 
-        public void setUseorg(DynamicObject useorg) {
-            this.useorg = useorg;
-        }
 
         public Long getId() {
             return id;

+ 186 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/result/UpdateResultOpPlugin.java

@@ -0,0 +1,186 @@
+package nckd.jxccl.opmc.pm.plugin.operate.result;
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.operate.OperateOptionConst;
+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.servicehelper.operation.OperationServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.AppraisalResultEnum;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
+import nckd.jxccl.opmc.pm.helper.PerfManagerHelper;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+* 视同合格/优秀/无结果
+* 实体标识:nckd_updateresult
+* @author W.Y.C
+* @date 2025/11/14 16:50
+* @version 1.0
+*/
+public class UpdateResultOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    //缓存需要修改的数据,key:考核周期ID
+    Map<Long, List<PendingUpdate>> pendingUpdateMap = new java.util.HashMap<>();
+
+    private class PendingUpdate {
+
+        Long entryId;
+        Date appraisalYear;
+        DynamicObject appraisalResult;
+
+        public PendingUpdate(Long entryId,Date appraisalYear,DynamicObject appraisalResult) {
+            this.entryId = entryId;
+            this.appraisalResult = appraisalResult;
+            this.appraisalYear = appraisalYear;
+        }
+    }
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+
+        e.addValidator(new AbstractValidator() {
+            @Override
+            public void validate() {
+                if(getOperationResult() != null && getOperationResult().isSuccess()) {
+                    for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                        DynamicObject data = rowDataEntity.getDataEntity();
+                        DynamicObjectCollection entryColl = data.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+                        for (DynamicObject entry : entryColl) {
+                            DynamicObject empPosOrgRel = entry.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL);
+                            DynamicObject employee = empPosOrgRel.getDynamicObject(FormConstant.EMPLOYEE_KEY);
+                            long personId = employee.getLong(FormConstant.ID_KEY);
+                            Date appraisalYear = entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR);
+                            LocalDateTime appraisalYearLocalDateTime = DateUtil.toLocalDateTime(appraisalYear);
+                            DynamicObject appraisalResult = entry.getDynamicObject(PerfManagerFormConstant.NCKD_APPRAISALRESULT);
+
+                            DynamicObject[] dbPerfManagerArray = PerfManagerHelper.getByDate(appraisalYear, Collections.singletonList(personId));
+                            if (dbPerfManagerArray == null || dbPerfManagerArray.length == 0) {
+                                this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("员工【{}】没有【{}】年的考核周期", employee.getString(FormConstant.NAME_KEY), DateUtil.getYear(appraisalYear)));
+                            }else{
+                                for (DynamicObject dbPerfManager : dbPerfManagerArray) {
+                                    long dbId = dbPerfManager.getLong(FormConstant.ID_KEY);
+                                    Date dbBeginYear = dbPerfManager.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR);
+                                    Date dbEndYear = dbPerfManager.getDate(PerfManagerFormConstant.NCKD_ENDYEAR);
+                                    if(dbPerfManager.getDate(PerfManagerFormConstant.NCKD_ACTENDYEAR) != null){
+                                        //如果有实际结束周期则结束时间为实际结束周期
+                                        dbEndYear = dbPerfManager.getDate(PerfManagerFormConstant.NCKD_ACTENDYEAR);
+                                    }
+                                    DynamicObjectCollection dbEntryColl = dbPerfManager.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                                    boolean isExist = false;
+                                    for (DynamicObject dbEntry : dbEntryColl) {
+                                        long entryId = dbEntry.getLong(FormConstant.ID_KEY);
+                                        LocalDateTime dbAppraisalYear = DateUtil.toLocalDateTime(dbEntry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR));
+                                        //只取年份在考核周期内的数据
+
+                                        if(dbAppraisalYear.getYear() >= DateUtil.getYear(dbBeginYear) &&  dbAppraisalYear.getYear()  <= DateUtil.getYear(dbEndYear)){
+                                            if(dbAppraisalYear.getYear() == appraisalYearLocalDateTime.getYear()) {
+                                                isExist = true;
+                                                DynamicObject dbAppraisalResult = dbEntry.getDynamicObject(PerfManagerFormConstant.NCKD_APPRAISALRESULT);
+                                                boolean dbIsAllRankSource = dbEntry.getBoolean(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE);
+                                                if (dbAppraisalResult != null) {
+                                                    if (dbIsAllRankSource) {
+                                                        this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("员工【{}】已存在【{}】年的考核结果【{}】,并且结果来源于全排名。", employee.getString(FormConstant.NAME_KEY), DateUtil.getYear(appraisalYear), dbAppraisalResult.getString(FormConstant.NAME_KEY)));
+                                                    } else {
+                                                        String dbAppraisalResultNumber = dbAppraisalResult.getString(FormConstant.NUMBER_KEY);
+                                                        if (!AppraisalResultEnum.NONE.getCode().equalsIgnoreCase(dbAppraisalResultNumber)) {
+                                                            if (!Objects.equals(dbAppraisalResult.getLong(FormConstant.ID_KEY), appraisalResult.getLong(FormConstant.ID_KEY))) {
+                                                                this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("员工【{}】已存在【{}】年的考核结果【{}】,不能修改。", employee.getString(FormConstant.NAME_KEY), DateUtil.getYear(appraisalYear), dbAppraisalResult.getString(FormConstant.NAME_KEY)));
+                                                            } else {
+                                                                //原绩效跟要更新的绩效一样,忽略
+                                                            }
+                                                        } else {
+                                                            //原绩效结果等于"无",又不是来源于全排名。可以修改
+                                                            List<PendingUpdate> pendingUpdateList = pendingUpdateMap.computeIfAbsent(dbId, k -> new ArrayList<>());
+                                                            PendingUpdate pendingUpdate = new PendingUpdate(entryId, appraisalYear, appraisalResult);
+                                                            pendingUpdateList.add(pendingUpdate);
+                                                            pendingUpdateMap.put(dbId, pendingUpdateList);
+                                                        }
+                                                    }
+                                                } else {
+                                                    //原绩效结果为空。可以修改
+                                                    List<PendingUpdate> pendingUpdateList = pendingUpdateMap.computeIfAbsent(dbId, k -> new ArrayList<>());
+                                                    PendingUpdate pendingUpdate = new PendingUpdate(entryId, appraisalYear, appraisalResult);
+                                                    pendingUpdateList.add(pendingUpdate);
+                                                    pendingUpdateMap.put(dbId, pendingUpdateList);
+                                                }
+                                            }
+                                        }
+                                    }
+                                    if(!isExist){
+                                        //考核周期分录没有该年份的数据
+                                        //检查全排名是否存在
+                                        Map<Long, AppraisalResultEnum> perfRankResult = PerfManagerHelper.getPerfRankResult(appraisalYear, Collections.singletonList(personId));
+                                        boolean isPass = true;
+                                        if(!perfRankResult.isEmpty()){
+                                            AppraisalResultEnum appraisalResultEnum = perfRankResult.get(personId);
+                                            if(appraisalResultEnum != null){
+                                                this.addFatalErrorMessage(rowDataEntity, StrFormatter.format("【{}】【{}】年的考核结果已在全排名中产生【{}】,不能修改。", employee.getString(FormConstant.NAME_KEY), DateUtil.getYear(appraisalYear), appraisalResultEnum.getName()));
+                                                isPass = false;
+                                            }
+                                        }
+                                        if(isPass) {
+                                            List<PendingUpdate> pendingUpdateList = pendingUpdateMap.computeIfAbsent(dbId, k -> new ArrayList<>());
+                                            PendingUpdate pendingUpdate = new PendingUpdate(null, appraisalYear, appraisalResult);
+                                            pendingUpdateList.add(pendingUpdate);
+                                            pendingUpdateMap.put(dbId, pendingUpdateList);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+
+        if(!pendingUpdateMap.isEmpty()){
+            DynamicObject[] updatePerfManagerArray = PerfManagerHelper.getById(pendingUpdateMap.keySet());
+            for (DynamicObject updatePerfManager : updatePerfManagerArray) {
+                long id = updatePerfManager.getLong(FormConstant.ID_KEY);
+                List<PendingUpdate> pendingUpdateList = pendingUpdateMap.get(id);
+                DynamicObjectCollection updatePerfManagerEntryColl = updatePerfManager.getDynamicObjectCollection(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY);
+                for (PendingUpdate pendingUpdate : pendingUpdateList) {
+                    if(pendingUpdate.entryId != null) {
+                        for (DynamicObject updatePerfManagerEntry : updatePerfManagerEntryColl) {
+                            long entryId = updatePerfManagerEntry.getLong(FormConstant.ID_KEY);
+                            if (Objects.equals(entryId, pendingUpdate.entryId)) {
+                                updatePerfManagerEntry.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, pendingUpdate.appraisalResult);
+                                updatePerfManagerEntry.set(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE, false);
+                            }
+                        }
+                    }else{
+                        DynamicObject newEntry = updatePerfManagerEntryColl.addNew();
+                        newEntry.set(PerfManagerFormConstant.NCKD_APPRAISALYEAR,pendingUpdate.appraisalYear);
+                        newEntry.set(PerfManagerFormConstant.NCKD_APPRAISALRESULT, pendingUpdate.appraisalResult);
+                        newEntry.set(PerfManagerFormConstant.NCKD_ISALLRANKSOURCE, false);
+                    }
+                }
+
+            }
+
+            OperateOption option = OperateOption.create();
+            option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE + "");
+            //告诉OP此次操作类型
+            option.setVariableValue("isUpdate", Boolean.TRUE+"");
+            OperationResult operationResult = OperationServiceHelper.executeOperate(FormConstant.SAVE_OP, PerfManagerFormConstant.PERFMANAGER_ENTITYID, updatePerfManagerArray, option);
+        }
+
+    }
+}

+ 16 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordBizExtRegister.java

@@ -0,0 +1,16 @@
+package nckd.jxccl.sit.hcsi.business.coordination.api;
+
+import kd.hr.hbp.business.coordination.api.ext.CoordBizExtRegister;
+import kd.hr.hbp.business.coordination.model.CoordBizContext;
+
+/**
+ * Tyx 2025-10-28
+ * 社保档案注册器,注册在 薪资数据集成 -> 员工变动协作配置 -> 协作字段配置 -> 社保档案的bizextregister字段中
+ */
+public class SinsurCoordBizExtRegister implements CoordBizExtRegister {
+    @Override
+    public void registerHandler(CoordBizContext context) {
+        CoordBizExtRegister.super.registerHandler(context);
+        context.registerHandlers(CoordBizContext.CoordBizStage.AFTER_OPERATE, new SinsurCoordBizSaveHandler());
+    }
+}

+ 104 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordBizSaveHandler.java

@@ -0,0 +1,104 @@
+package nckd.jxccl.sit.hcsi.business.coordination.api;
+
+import com.kingdee.util.Uuid;
+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.entity.operate.result.OperationResult;
+import kd.bos.exception.KDBizException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.hr.hbp.business.coordination.api.ext.CoordBizSaveHandler;
+import kd.hr.hbp.business.coordination.model.CoordBizContext;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import nckd.jxccl.sit.hcsi.business.importtaskguide.ImportTaskGuideImportService;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Tyx 2025-10-28
+ * 社保档案处理器,生成社保档案头之后,保存核定单分录的险种基数设置信息。
+ */
+public class SinsurCoordBizSaveHandler implements CoordBizSaveHandler {
+
+    private static final Log log = LogFactory.getLog(SinsurCoordBizSaveHandler.class);
+    private static HRBaseServiceHelper SIBASE_HELPER = new HRBaseServiceHelper("hcsi_sibase");
+    private static String SIBASE_ENTITY = "hcsi_sibase";
+    //字段对应的Map, k = 核定单分录字段标识, v = 社保档案险种基数设置字段标识
+    private static Map<String, String> fieldMap = new HashMap<String, String>();
+    static {
+        fieldMap.put("nckd_welfaretype","welfaretype");
+        fieldMap.put("nckd_insured","insured");
+        fieldMap.put("nckd_paybaseofemployee","paybaseofemployee");
+        fieldMap.put("nckd_paybaseofcompany","paybaseofcompany");
+        fieldMap.put("nckd_startdate","startdate");
+        fieldMap.put("nckd_enddate","enddate");
+        fieldMap.put("nckd_description","description");
+        fieldMap.put("nckd_sinsurdimension","nckd_sinsurdimension");
+    }
+
+    /**
+     * 实际处理方法
+     * @param map key = hcsi_sinsurfile, value = 社保档案Dyn
+     * @param map1 unknown
+     * @param dynamicObject 核定单Dyn
+     * @param coordBizContext 协作业务上下文
+     * @return
+     */
+    @Override
+    public Map<String, Object> handle(Map<String, DynamicObject> map, Map<String, Map<String, Object>> map1, DynamicObject dynamicObject, CoordBizContext coordBizContext) {
+        log.info(" -------- begin SinsurCoordBizSaveHandler handle --------");
+        // 返回的Map
+        Map<String, Object> returnMap = new HashMap<String, Object>();
+        DynamicObject sinsurFile = map.get("hcsi_sinsurfile");
+        if(!ObjectUtils.isEmpty(sinsurFile)) {
+            log.info("-------- 当前处理社保档案编号 = " + sinsurFile.getString("number"));
+            DynamicObjectCollection sibaseCols = new DynamicObjectCollection();
+            // 获取档案Id
+            Long fileId = sinsurFile.getLong("id");
+            // 获取核定单分录数据
+            DynamicObjectCollection entryCols = dynamicObject.getDynamicObjectCollection("nckd_entryentity");
+            for (DynamicObject entry : entryCols) {
+                // 新建数据包
+                DynamicObject dyn = BusinessDataServiceHelper.newDynamicObject(SIBASE_ENTITY);
+                // 设置字段,单独处理下档案,计薪人员,参保单位,其余字段从fieldMap从处理;
+                dyn.set("sinsurfile", sinsurFile);
+                dyn.set("employee", sinsurFile.getDynamicObject("employee"));
+                dyn.set("welfarepayer", sinsurFile.getDynamicObject("welfarepayer"));
+                dyn.set("id", Uuid.create());
+                Iterator it = fieldMap.keySet().iterator();
+                while(it.hasNext()) {
+                    String key = it.next().toString();
+                    dyn.set(fieldMap.get(key), entry.get(key));
+                }
+                sibaseCols.add(dyn);
+            }
+            OperationResult result = SaveServiceHelper.saveOperate(SIBASE_ENTITY, sibaseCols.stream().toArray(DynamicObject[]::new), OperateOption.create());
+            // 标品SinSurFileCoordServiceHelper.saveBizObject方法中,判断success写反了,这里暂时反着写,后续等标品调整
+            // TODO
+            if(result.isSuccess()) {
+                returnMap.put("success",false);
+            }
+            else {
+                throw new KDBizException("-------- 险种基数保存失败:" + result.getMessage());
+                //returnMap.put("success",true);
+                //returnMap.put("msg", result.getMessage());
+            }
+//            if(result.isSuccess()) {
+//                returnMap.put("success",true);
+//            }
+//            else {
+//                returnMap.put("success",false);
+//                returnMap.put("msg", result.getMessage());
+//            }
+            log.info("-------- bizSaveHandler result = " + result + " --------");
+        }
+        return returnMap;
+    }
+}

+ 156 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/datacomparison/DataComparisonPushService.java

@@ -0,0 +1,156 @@
+package nckd.jxccl.sit.hcsi.business.datacomparison;
+
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.swc.hsas.mservice.BizDataService;
+import nckd.jxccl.sit.hcsi.common.constant.SitConstant;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 2025-11-19 Tyx
+ * 数据比对推送前端业务数据服务类
+ */
+public class DataComparisonPushService {
+
+    private static final Log logger = LogFactory.getLog(DataComparisonPushService.class);
+
+    public DataComparisonPushService() {
+
+    }
+
+    /**
+     * 实际执行推送数据
+     */
+    public Map doPush(List<DynamicObject> entryList, Date bsed) {
+        Map param = buildParam(entryList, bsed);
+        BizDataService bizDataService = new BizDataService();
+        Map response = bizDataService.synBizData(param);
+        logger.info("推送数据返回结果:" + response);
+        return response;
+    }
+
+    /**
+     * 构建参数,数据比对中一行分录会生成两行业务数据
+     *
+     * @return
+     */
+    public Map buildParam(List<DynamicObject> entryList, Date bsed) {
+        Map map = new HashMap();
+        map.put("operate", "1");    //1-新增 2-更正
+        map.put("opsystem", "社保数据比对");
+
+        List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
+        Map<String,Long> bizItemMap = getBizItemId();
+
+        Map<Long, Long> empposorgMap = getEmpPosOrgRelIds(entryList, bsed);
+
+        for(DynamicObject entry : entryList) {
+            // 个人缴费金额
+            Map<String, Object> dataMapA = new HashMap();
+            // 个人补缴金额
+            Map<String, Object> dataMapB = new HashMap();
+            //获取险种编码
+            String welfaretypeNo = entry.getString("welfaretypeid.number");
+
+            //bizdatacode 前端业务数据识别码(唯一) 这里用分录ID+分隔符判断
+            dataMapA.put("bizdatacode", entry.getPkValue().toString() + SitConstant.SeparatorA);
+            dataMapB.put("bizdatacode", entry.getPkValue().toString() + SitConstant.SeparatorB);
+            //batchcode 批次号 直接用分录ID
+            dataMapA.put("batchcode", entry.getPkValue().toString());
+            dataMapB.put("batchcode", entry.getPkValue().toString());
+            //业务项目ID 业务项目上新增了一个字段【nckd_matchkey】 = 险种编码 + _A / _B, A : 个人缴费,B : 个人补缴
+            dataMapA.put("bizitemid", bizItemMap.get(welfaretypeNo + SitConstant.SeparatorA));
+            dataMapB.put("bizitemid", bizItemMap.get(welfaretypeNo + SitConstant.SeparatorB));
+            //employee
+            dataMapA.put("employeeid", entry.getLong("employee.id"));
+            dataMapB.put("employeeid", entry.getLong("employee.id"));
+            //value
+            BigDecimal valueA = entry.getBigDecimal("personsysvalue");
+            BigDecimal valueB = entry.getBigDecimal("personsysvaluea");
+            dataMapA.put("value", valueA);
+            dataMapB.put("value", valueB);
+            //empposorgrelid 计薪人员任职经历
+            Long empPosOrgRelId = empposorgMap.get(entry.getLong("employee.id"));
+            dataMapA.put("empposorgrelid", empPosOrgRelId);
+            dataMapB.put("empposorgrelid", empPosOrgRelId);
+            //datasources 数据来源 90 - 社保数据比对
+            dataMapA.put("datasources", "90");
+            dataMapB.put("datasources", "90");
+            //srcsystem 前端系统
+            dataMapA.put("srcsystem", "hpdi");
+            dataMapB.put("srcsystem", "hpdi");
+
+            //startdate
+            dataMapA.put("startdate", bsed);
+            dataMapB.put("startdate", bsed);
+
+            //currencyid
+            dataMapA.put("currencyid", "1");
+            dataMapB.put("currencyid", "1");
+
+            //备注 description
+            dataMapA.put("description", entry.getString("nckd_remark"));
+            dataMapB.put("description", entry.getString("nckd_remark"));
+
+            // 如果value = 0 则跳过这条数据
+            if(valueA.compareTo(BigDecimal.ZERO) > 0) {
+                data.add(dataMapA);
+            }
+            if(valueB.compareTo(BigDecimal.ZERO) > 0) {
+                data.add(dataMapB);
+            }
+         }
+        map.put("data", data);
+        return map;
+    }
+
+
+    /**
+     * 获取业务项目ID
+     * 取业务项目类别.编码 = JT004
+     * @return
+     */
+    public Map<String, Long> getBizItemId() {
+        QFilter filter = new QFilter("bizitemcategory.number", QCP.equals, "JT004");
+        String selectFields = "id,nckd_matchkey";
+        DynamicObjectCollection cols = SitConstant.BIZITEM_HELPER.queryOriginalCollection(selectFields, new QFilter[]{filter});
+        Map<String,Long> map = cols.stream().collect(Collectors.toMap((dyx) -> {
+            return dyx.getString("nckd_matchkey");
+        }, (dyx) -> {
+            return dyx.getLong("id");
+        }, (key1, key2) -> {
+            return key2;
+        }));
+        return map;
+    }
+
+    public Map<Long, Long> getEmpPosOrgRelIds(List<DynamicObject> list, Date date) {
+        Set employeeIds = list.stream().map(i -> i.getLong("employee.id")).collect(Collectors.toSet());
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("startdate", QCP.less_equals, date);
+        filter.and("enddate", QCP.large_equals, date);
+        filter.and("isprimary", QCP.equals, "1");
+        HRBaseServiceHelper helper = new HRBaseServiceHelper("hrpi_empposorgrel");
+        String selectFields = "id,employee.id";
+        DynamicObjectCollection cols = helper.queryOriginalCollection(selectFields, new QFilter[]{filter});
+        Map<Long, Long> map = cols.stream().collect(Collectors.toMap((dyx) -> {
+            return dyx.getLong("employee.id");
+        }, (dyx) -> {
+            return dyx.getLong("id");
+        }, (key1, key2) -> {
+            return key2;
+        }));
+        return map;
+    }
+
+
+}

+ 10 - 10
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/datacomparison/DataComparisonQueryService.java

@@ -24,8 +24,8 @@ import java.util.stream.Collectors;
 public class DataComparisonQueryService {
     private static final Log logger = LogFactory.getLog(DataComparisonQueryService.class);
     private static final String SINSURTASK = "hcsi_sinsurtask";
-    String[] leftFields = new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type", "personSysValue", "companySysValue"};
-    String[] rightFields =  new String[]{"personOutValue", "companyOutValue"};
+    String[] leftFields = new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "employee", "type", "personSysValue", "personSysValuea", "companySysValue", "companySysValuea"};
+    String[] rightFields =  new String[]{"personOutValue", "personOutValuea","companyOutValue", "companyOutValuea"};
     IDataModel model = null;
     IFormView view = null;
     public DataComparisonQueryService(IDataModel model,IFormView view) {
@@ -97,17 +97,17 @@ public class DataComparisonQueryService {
      * @return
      */
     public DataSet groupCalPersonDetail (DataSet calPersonDataSet) {
-        calPersonDataSet.groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value");
+        calPersonDataSet.groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","employee","type"}).sum("value").sum("value1");
         DataSet dataSet1 = calPersonDataSet.copy();
         DataSet dataSet2 = calPersonDataSet.copy();
 
         // 按照类型分组汇总
-        DataSet sumSet1 = dataSet1.filter("type = '1'").groupBy(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type"}).sum("value").finish();
-        DataSet sumSet2 = dataSet2.filter("type = '2'").groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value").finish();
+        DataSet sumSet1 = dataSet1.filter("type = '1'").groupBy(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "employee", "type"}).sum("value").sum("value1").finish();
+        DataSet sumSet2 = dataSet2.filter("type = '2'").groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard", "employee", "type"}).sum("value").sum("value1").finish();
 
         // join
         JoinDataSet joinDataSet = sumSet1.join(sumSet2).on("welfarepayer", "welfarepayer").on("welfaretypeid", "welfaretypeid").on("empnumber", "empnumber").on("empname", "empname").on("empidcard", "empidcard");
-        DataSet dataSet = joinDataSet.select(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type", "value as personSysValue"}, new String[]{"value as companySysValue"}).finish();
+        DataSet dataSet = joinDataSet.select(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "employee", "type", "value as personSysValue","value1 as personSysValuea"}, new String[]{"value as companySysValue","value1 as companySysValuea"}).finish();
 
         return dataSet;
     }
@@ -126,17 +126,17 @@ public class DataComparisonQueryService {
     }
 
     public DataSet groupCalPersonDetailOut (DataSet calPersonDataSet) {
-        calPersonDataSet.groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value");
+        calPersonDataSet.groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value").sum("value1");
         DataSet dataSet1 = calPersonDataSet.copy();
         DataSet dataSet2 = calPersonDataSet.copy();
 
         // 按照类型分组汇总
-        DataSet sumSet1 = dataSet1.filter("type = '1'").groupBy(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type"}).sum("value").finish();
-        DataSet sumSet2 = dataSet2.filter("type = '2'").groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value").finish();
+        DataSet sumSet1 = dataSet1.filter("type = '1'").groupBy(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type"}).sum("value").sum("value1").finish();
+        DataSet sumSet2 = dataSet2.filter("type = '2'").groupBy(new String[]{"welfarepayer","welfaretypeid","empnumber","empname","empidcard","type"}).sum("value").sum("value1").finish();
 
         // join
         JoinDataSet joinDataSet = sumSet1.join(sumSet2).on("welfarepayer", "welfarepayer").on("welfaretypeid", "welfaretypeid").on("empnumber", "empnumber").on("empname", "empname").on("empidcard", "empidcard");
-        DataSet dataSet = joinDataSet.select(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type", "value as personOutValue"}, new String[]{"value as companyOutValue"}).finish();
+        DataSet dataSet = joinDataSet.select(new String[]{"welfarepayer", "welfaretypeid", "empnumber", "empname", "empidcard", "type", "value as personOutValue", "value1 as personOutValuea"}, new String[]{"value as companyOutValue","value1 as companyOutValuea"}).finish();
 
         return dataSet;
     }

+ 7 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/common/constant/SitConstant.java

@@ -3,5 +3,12 @@ package nckd.jxccl.sit.hcsi.common.constant;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 
 public class SitConstant {
+    /*社保档案*/
     public static final HRBaseServiceHelper SINSURFILE_HELPER = new HRBaseServiceHelper("hcsi_sinsurfile");
+
+    /*业务项目*/
+    public static final HRBaseServiceHelper BIZITEM_HELPER = new HRBaseServiceHelper("hsbs_bizitem");
+
+    public static final String SeparatorA = "_A";
+    public static final String SeparatorB = "_B";
 }

+ 218 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/coordination/SinSurEmpChgCoordBatchEditPluginEx.java

@@ -0,0 +1,218 @@
+package nckd.jxccl.sit.hcsi.formplugin.web.coordination;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.db.DB;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.field.FieldEdit;
+import kd.bos.form.operate.AbstractOperate;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.org.utils.DynamicObjectUtils;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRStringUtils;
+import kd.sdk.plugin.Plugin;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Tyx 2025-10-27
+ * 变动核定单表单插件扩展【nckd_hpdi_empcoordbat_ext】
+ */
+public class SinSurEmpChgCoordBatchEditPluginEx extends AbstractFormPlugin implements Plugin {
+
+    private static final HRBaseServiceHelper COORDBILL_HELPER = new HRBaseServiceHelper("hpdi_empcoordverifbill3");
+    private static final List<String> defaultQueryFields = (List) Stream.of("id", "employeev", "assignment", "coordruleparam").collect(Collectors.toList());
+    /**
+     * 在标品加载完数据之后,再获取一下核定单分录的数据展示出来
+     * @param e
+     */
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+        List<String> queryFields = this.getQueryFields();
+        HRBaseServiceHelper helper = new HRBaseServiceHelper(this.getEntityNumber());
+        DynamicObject[] billCols = helper.load(String.join(",", queryFields), new QFilter[]{new QFilter("id", "in", this.getSelectIds())});
+        showEntryData(billCols);
+    }
+
+    private void showEntryData(DynamicObject[] billCols) {
+        Set<String> entryFields = this.getEntryFields();
+        // 根据ID提取出来
+        Map<Long, DynamicObject> billMap = (Map) Arrays.stream(billCols).collect(Collectors.toMap((dyx) -> {
+            return dyx.getLong("id");
+        }, (dyx) -> {
+            return dyx;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+        DynamicObjectCollection entryCols = this.getModel().getEntryEntity("entryentity");
+        for(DynamicObject entry : entryCols) {
+            Long billId = entry.getLong("id");
+            DynamicObject bill = billMap.get(billId);
+            DynamicObjectCollection billEntryCols = bill.getDynamicObjectCollection("nckd_entryentity");
+            DynamicObjectCollection subEntryCols = entry.getDynamicObjectCollection("nckd_subentryentity");
+            subEntryCols.clear();
+            for(DynamicObject billEntry : billEntryCols) {
+                DynamicObject subEntry = subEntryCols.addNew();
+                for(String field : entryFields) {
+                    subEntry.set(field, billEntry.get(field));
+                }
+            }
+        }
+        this.getView().updateView("nckd_subentryentity");
+    }
+
+
+
+
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+        IDataModel model = this.getModel();
+        AbstractOperate op = (AbstractOperate)args.getSource();
+        String operateKey = op.getOperateKey();
+        // 批量设置前校验,查询当前选中的单据体的子单据体是否有数据
+        if(HRStringUtils.equals(operateKey, "batchsetting")){
+            int row = model.getEntryCurrentRowIndex("entryentity");
+            model.setEntryCurrentRowIndex("entryentity", row);
+            int rowCount = model.getEntryRowCount("nckd_subentryentity");
+            if(rowCount == 0) {
+                args.setCancel(true);
+                this.getView().showTipNotification("请先维护险种基数!");
+            }
+        }
+
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+        AbstractOperate op = (AbstractOperate)afterDoOperationEventArgs.getSource();
+        String operateKey = op.getOperateKey();
+        if (afterDoOperationEventArgs.getOperationResult().isSuccess() && (HRStringUtils.equals(operateKey, "save") || HRStringUtils.equals(operateKey, "confirmeffect"))) {
+            doSaveSubEntry();
+        }
+        else if(HRStringUtils.equals(operateKey, "batchsetting")){
+            doBatchSetting();
+        }
+    }
+
+    /**
+     * 批量设置其余子分录的险种信息
+     */
+    public void doBatchSetting() {
+        IDataModel model = this.getModel();
+        DynamicObject bill = model.getDataEntity(true);
+        DynamicObjectCollection entryCols = bill.getDynamicObjectCollection("entryentity");
+        // 当前选中行
+        int row = model.getEntryCurrentRowIndex("entryentity");
+        // 当前选中行的子单据体数据
+        DynamicObjectCollection subCols = entryCols.get(row).getDynamicObjectCollection("nckd_subentryentity");
+
+        for(int i = 0; i < entryCols.size(); i++) {
+            if(i == row)
+                continue;
+            // 获取本次要执行覆盖的子单据体
+            DynamicObjectCollection otherSubCols = entryCols.get(i).getDynamicObjectCollection("nckd_subentryentity");
+            // 清除数据
+            otherSubCols.clear();
+            // 从subCols copy 过来
+            for(DynamicObject subEntry : subCols) {
+                DynamicObject otherSubEntry = otherSubCols.addNew();
+                DynamicObjectUtils.copy(subEntry, otherSubEntry);
+            }
+        }
+        this.getView().updateView("nckd_subentryentity");
+    }
+
+    public void checkSubEntryData() {
+        IDataModel model = this.getModel();
+        DynamicObject dyn = model.getDataEntity(true);
+        DynamicObjectCollection entryCols = dyn.getDynamicObjectCollection("entryentity");
+    }
+
+    /**
+     * 执行保存,把当前页面的子单据体保存到核定单上;
+     */
+    public void doSaveSubEntry () {
+        DynamicObject bill = this.getModel().getDataEntity(true);
+        DynamicObjectCollection entryCols = bill.getDynamicObjectCollection("entryentity");
+        DynamicObjectCollection saveCols = new DynamicObjectCollection();
+        Set<String> subEntryFields = getEntryFields();
+        for (DynamicObject entry : entryCols) {
+            // 获取到核定单ID
+            Long coordBillId = entry.getLong("id");
+            // 获取到核定单Dyn
+            DynamicObject coordBill = COORDBILL_HELPER.loadOne(coordBillId);
+            // 获取当前页面子单据体的信息
+            DynamicObjectCollection subEntryCols = entry.getDynamicObjectCollection("nckd_subentryentity");
+            // 保存核定单的分录
+            DynamicObjectCollection coordBillEntryCols = coordBill.getDynamicObjectCollection("nckd_entryentity");
+            // 清空核定单的分录
+            coordBillEntryCols.clear();
+            // 根据当前页面子单据体信息->核定单分录数据
+            for (DynamicObject subEntry : subEntryCols) {
+                DynamicObject billEntry = coordBillEntryCols.addNew();
+                for(String field : subEntryFields) {
+                    billEntry.set(field, subEntry.get(field));
+                }
+            }
+            saveCols.add(coordBill);
+        }
+        if(saveCols.size() > 0) {
+            COORDBILL_HELPER.save(saveCols);
+        }
+    }
+
+    /**
+     * 获取子单据体字段
+     * @return
+     */
+    public Set<String> getEntryFields() {
+        Set<String> subEntryFields = EntityMetadataCache.getDataEntityType("hpdi_empcoordbatchedit3").getAllEntities().get("nckd_subentryentity").getFields().keySet();
+        return subEntryFields;
+    }
+
+    /**
+     * 获取核定单ID
+     * @return
+     */
+    protected List<Long> getSelectIds() {
+        String selectIdStr = (String)this.getView().getFormShowParameter().getCustomParam("selectIds");
+        return (List<Long>) SerializationUtils.fromJsonStringToList(selectIdStr, Long.class);
+    }
+
+    /**
+     * 获取实体标识
+     * @return
+     */
+    protected String getEntityNumber() {
+        return (String)this.getView().getFormShowParameter().getCustomParam("entityNumber");
+    }
+
+    /**
+     * 构建查询字段
+     * @return
+     */
+    protected List<String> getQueryFields() {
+        EntryGrid entryGrid = (EntryGrid)this.getView().getControl("entryentity");
+        List<FieldEdit> entryFields = entryGrid.getFieldEdits();
+        Set<String> queryFields = (Set)entryFields.stream().map(FieldEdit::getFieldKey).collect(Collectors.toSet());
+        queryFields.remove("adminorg");
+        queryFields.remove("position");
+        queryFields.addAll(defaultQueryFields);
+        Set<String> fields = this.getEntryFields();
+        queryFields.addAll(fields.stream().map(field -> "nckd_entryentity." + field).collect(Collectors.toList()));
+        return new ArrayList(queryFields);
+    }
+
+}

+ 99 - 7
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/datacomparison/DetailCompareBillEdit.java

@@ -16,15 +16,14 @@ import kd.bos.form.operate.FormOperate;
 import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
-import kd.bos.orm.query.QCP;
-import kd.bos.orm.query.QFilter;
-import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import nckd.jxccl.sit.hcsi.business.datacomparison.DataComparisonPushService;
 import nckd.jxccl.sit.hcsi.business.datacomparison.DataComparisonQueryService;
+import nckd.jxccl.sit.hcsi.common.constant.SitConstant;
+import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
-import java.util.Arrays;
-import java.util.EventObject;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * Tyx 2025-10-31
@@ -75,8 +74,27 @@ public class DetailCompareBillEdit extends AbstractFormPlugin {
         EntryGrid entryGrid = this.getControl("nckd_entryentity");
         int[] selectRows = entryGrid.getSelectRows();
         if(selectRows.length == 0 ) {
+            args.setCancel(true);
             this.getView().showTipNotification("请先选中数据!");
         }
+
+        //判断下是否推送过
+        IDataModel model = this.getModel();
+        DynamicObjectCollection entryCols = model.getEntryEntity("nckd_entryentity");
+        List<Integer> pushedRows = new ArrayList();
+        for(int rowIndex : selectRows) {
+            String dataStatus = entryCols.get(rowIndex).getString("nckd_datastatus");
+            if("C".equals(dataStatus)) {
+                pushedRows.add(rowIndex+1);
+            }
+        }
+
+        if(pushedRows.size() > 0) {
+            args.setCancel(true);
+            this.getView().showTipNotification("第"+StringUtils.join(pushedRows, ",")+"行数据已推送,请重新选择!");
+        }
+
+
     }
 
     /**
@@ -87,6 +105,7 @@ public class DetailCompareBillEdit extends AbstractFormPlugin {
         EntryGrid entryGrid = this.getControl("nckd_entryentity");
         int[] selectRows = entryGrid.getSelectRows();
         if(selectRows.length == 0 ) {
+            args.setCancel(true);
             this.getView().showTipNotification("请先选中数据!");
         }
     }
@@ -114,6 +133,8 @@ public class DetailCompareBillEdit extends AbstractFormPlugin {
         }
     }
 
+
+
     @Override
     public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
         super.afterDoOperation(afterDoOperationEventArgs);
@@ -137,9 +158,80 @@ public class DetailCompareBillEdit extends AbstractFormPlugin {
 
 
     private void doPush() {
-        logger.info("推送数据到算薪系统");
+        logger.info("推送数据到前端业务数据");
+        DataComparisonPushService service = new DataComparisonPushService();
+        IDataModel model = this.getModel();
+        Date bsed = model.getDataEntity(true).getDate("nckd_period.startdate");
+        EntryGrid entryGrid = this.getControl("nckd_entryentity");
+        int[] selectRows = entryGrid.getSelectRows();
+        List<DynamicObject> list = new ArrayList<>();
+        for (int i : selectRows) {
+            list.add(model.getEntryEntity("nckd_entryentity").get(i));
+        }
+        // 业务数据返回参数
+        Map response = service.doPush(list, bsed);
+
+        // 处理返回结果
+        List<Map> result = (List<Map>) response.get("result");
+        handlePushResult(result, list);
+        this.getView().updateView("nckd_entryentity");
     }
 
+
+    /**
+     *
+     * @param result,推送业务数据返回的结果,bizdatacode = 分录ID + _A / _B
+     * @param entryList
+     */
+    private void handlePushResult (List<Map> result, List<DynamicObject> entryList) {
+        // 根据bizdatacode提取
+        Map map = result.stream().collect(Collectors.toMap((dyx) -> {
+            return dyx.get("bizdatacode").toString();
+        }, (dyx) -> {
+            return dyx;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+
+        for (DynamicObject entry : entryList) {
+            String entryId = entry.getPkValue().toString();
+            String keyA = entryId + SitConstant.SeparatorA;
+            String keyB = entryId + SitConstant.SeparatorB;
+
+            String errorMsgA = null,errorMsgB = null,statusA = "1",statusB = "1";
+
+            Map actualMapA = (Map) map.get(keyA);
+
+            if(!ObjectUtils.isEmpty(actualMapA)) {
+                statusA = actualMapA.get("status").toString();
+                if(!"1".equals(statusA)) {
+                    errorMsgA = actualMapA.get("errormsg").toString();
+                }
+            }
+
+            Map actualMapB = (Map) map.get(keyB);
+
+            if(!ObjectUtils.isEmpty(actualMapB)) {
+                statusB = actualMapA.get("status").toString();
+                if(!"1".equals(statusB)) {
+                    errorMsgB = actualMapB.get("errormsg").toString();
+                }
+            }
+
+            if("1".equals(statusA) && "1".equals(statusB)) {
+                entry.set("nckd_datastatus", "C");
+                entry.set("nckd_pushlog", null);
+            }
+            else {
+                String msg = StringUtils.defaultString(errorMsgA) + StringUtils.defaultString(errorMsgB);
+                entry.set("nckd_pushlog", msg);
+            }
+        }
+
+
+    }
+
+
     /**
      * 比对实际执行方法
      */

+ 1 - 1
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/SinsurTemplateEdit.java

@@ -282,7 +282,7 @@ public class SinsurTemplateEdit extends AbstractFormPlugin implements Plugin {
                     if ("1".equals(itemType)) {
                         tableValueSetter.addRow(new Object[]{matchColumn, itemParam.get("itemType"), itemId, itemId, 0L, 0L, 0L, dataObj.get("nckd_itemnumber"), dataObj.get("nckd_itemname"), itemParam.get("dataTypeId"), common, unicode});
                     } else if ("2".equals(itemType)) {
-                        tableValueSetter.addRow(new Object[]{matchColumn, itemParam.get("itemType"), itemId, 0L, 0L, itemId, 0L, dataObj.get("nckd_itemnumber"), dataObj.get("nckd_itemname"), itemParam.get("dataTypeId"), common, unicode});
+                        tableValueSetter.addRow(new Object[]{matchColumn, itemParam.get("itemType"), itemId, itemId, 0L, 0L, 0L, dataObj.get("nckd_itemnumber"), dataObj.get("nckd_itemname"), itemParam.get("dataTypeId"), common, unicode});
                     } else if ("4".equals(itemType)) {
                         tableValueSetter.addRow(new Object[]{matchColumn, itemParam.get("itemType"), itemId, 0L, 0L, 0L, itemId, dataObj.get("nckd_itemnumber"), dataObj.get("nckd_itemname"), itemParam.get("dataTypeId"), common, unicode});
                     } else if ("3".equals(itemType)) {

+ 13 - 3
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/SitItemSelectAddItemPlugin.java

@@ -44,12 +44,14 @@ public class SitItemSelectAddItemPlugin extends AbstractBasePlugIn {
     private static final String TREE_VIEW = "nckd_treeviewap";
     private static final String FORM_MOVE_TO = "hsas_calitemmoveto";
     private static final String ITEM_TYPE_II = "II";
-
+    private static final String BASE_TYPE_II = "BI";
     private static final Map<String, String> ITEM_TYPE_MAPPING = new HashMap();
     private static final Map<String, String> ITEM_CATEGORY_MAPPING =  new HashMap();
     static {
         ITEM_TYPE_MAPPING.put("1", ITEM_TYPE_II);
+        ITEM_TYPE_MAPPING.put("2", BASE_TYPE_II);
         ITEM_CATEGORY_MAPPING.put(ITEM_TYPE_II, ITEM_TYPE_II);
+        ITEM_CATEGORY_MAPPING.put(BASE_TYPE_II, BASE_TYPE_II);
     }
 
     @Override
@@ -234,6 +236,7 @@ public class SitItemSelectAddItemPlugin extends AbstractBasePlugIn {
     private List<TreeNode> buildTreeNodeList(Map<String, Map<String, Map<String, Object>>> dataMap) {
         List<TreeNode> list = new ArrayList<>();
         list.addAll(SitItemSelectAddItemServiceHelper.loadInsuranceItemChildNode(null, false, dataMap));
+        list.addAll(SitItemSelectAddItemServiceHelper.loadBaseItemChildNode(false));
         return list;
     }
 
@@ -330,7 +333,7 @@ public class SitItemSelectAddItemPlugin extends AbstractBasePlugIn {
         }
 
         String treeNodeId = treeNode.getId();
-        if (treeNodeId.equals("0_@_BS") || treeNodeId.equals("1_@_BS")) {
+        if (treeNodeId.equals("II") || treeNodeId.equals("BI")) {
             return false;
         }
 
@@ -341,7 +344,12 @@ public class SitItemSelectAddItemPlugin extends AbstractBasePlugIn {
 //        String treeNodeId = treeNode.getId();
 //        String itemCategory = treeNodeId.substring(0, treeNodeId.indexOf('_'));
 //        String itemCategory = ITEM_TYPE_II;
-        return normalizeItemCategory(ITEM_TYPE_II);
+        if(treeNode.getId().equals("999990") || treeNode.getId().equals("999991")){
+            return normalizeItemCategory(BASE_TYPE_II);
+        }
+        else {
+            return normalizeItemCategory(ITEM_TYPE_II);
+        }
     }
 
     private void removeCheckNode(int[] rows) {
@@ -384,6 +392,8 @@ public class SitItemSelectAddItemPlugin extends AbstractBasePlugIn {
         switch (itemCategory) {
             case ITEM_TYPE_II:
                 return "insuranceitemkey";
+            case BASE_TYPE_II:
+                return "baseitemkey";
             default:
                 return "";
         }

+ 25 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/helper/SinsurTemplateHelper.java

@@ -30,9 +30,34 @@ public class SinsurTemplateHelper {
         }
         Map<String, Map<String, Object>> itemParamMap = new HashMap(itemDatas.size());
         itemParamMap.putAll(getItemParamMap("number", IIUnicodeList, "1", "sitbs_insuranceitem", existItemDataMap, (Long) null));
+        //单独处理一下基本项目:滞纳金跟利息
+        itemParamMap.putAll(getBaseItemParamMap());
         return itemParamMap;
     }
 
+    public static Map<String, Map<String, Object>> getBaseItemParamMap() {
+        Map<String, Map<String, Object>> itemParamMap = new HashMap(2);
+        for (int i = 0; i < 2; i++) {
+            Map<String, Object> itemParam = new HashMap(6);
+            if (i == 0) {
+                itemParam.put("id", 999990L);
+                itemParam.put("dataTypeId", 1020L);
+                itemParam.put("itemType", "2");
+                itemParam.put("itemname", "滞纳金");
+                itemParamMap.put("999990", itemParam);
+            }
+            else {
+                itemParam.put("id", 999991L);
+                itemParam.put("dataTypeId", 1020L);
+                itemParam.put("itemType", "2");
+                itemParam.put("itemname", "利息");
+                itemParamMap.put("999991", itemParam);
+            }
+        }
+        return itemParamMap;
+    }
+
+
     public static Map<String, Map<String, Object>> getItemParamMap(String key, List<String> keyList, String itemType, String entityName, Map<Long, Map<String, Object>> existItemDataMap, Long orgId) {
         if (CollectionUtils.isEmpty(keyList)) {
             return new HashMap(0);

+ 41 - 10
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/tp/helper/SitItemSelectAddItemServiceHelper.java

@@ -22,7 +22,9 @@ public class SitItemSelectAddItemServiceHelper {
     private static final String DEFAULT_ORDER = "number asc";
 
     private static final String INSURANCEITEM_KEY = "insuranceitemkey";
+    private static final String BASEITEM_KEY = "baseitemkey";
     private static final String ITEM_TYPE_II = "II";
+    private static final String BASE_TYPE_II = "BI";
     private static final String PREFIX_SEPARATOR = "_@_";
 
     /**
@@ -34,9 +36,26 @@ public class SitItemSelectAddItemServiceHelper {
         Map<String, Map<String, Map<String, Object>>> dataMap = new HashMap(4);
         Map<String, Map<String, Object>> insuranceItemMap = this.getInsuranceItemList(false, (QFilter)null, (QFilter)null, orgId);
         dataMap.put("insuranceitemkey", insuranceItemMap);
+        dataMap.put("baseitemkey", getBaseItemMap());
         return dataMap;
     }
 
+
+    public Map<String, Map<String, Object>> getBaseItemMap() {
+        Map<String, Map<String, Object>> baseItemMap = new HashMap<>(3);
+        Map<String, Object> itemMap1 = new HashMap<>(3);
+        itemMap1.put("id", "999990");
+        itemMap1.put("number", "999990");
+        itemMap1.put("name", "滞纳金");
+        baseItemMap.put("滞纳金", itemMap1);
+        Map<String, Object> itemMap2 = new HashMap<>(3);
+        itemMap2.put("id", "999991");
+        itemMap2.put("number", "999991");
+        itemMap2.put("name", "利息");
+        baseItemMap.put("利息", itemMap2);
+        return baseItemMap;
+    }
+
     private Map<String, Map<String, Object>> getInsuranceItemList(boolean isProrationFormula, QFilter countryQFilter, QFilter calBlockQFilter, long orgId) {
         if (isProrationFormula) {
             return Collections.emptyMap();
@@ -150,18 +169,30 @@ public class SitItemSelectAddItemServiceHelper {
         else {
             return totalNodeList;
         }
-//
-//
-//
-//        insuranceItemList.forEach(item ->  {
-//            TreeNode itemNode = new TreeNode(ITEM_TYPE_II, ITEM_TYPE_II + "_" + item.get("id").toString(),
-//                    item.get("name").toString());
-//            treeNodeList.add(itemNode);
-//        });
-//
-//        return treeNodeList;
     }
 
+    public static List<TreeNode> loadBaseItemChildNode(boolean isExpend) {
+        // 最后返回树:
+        List<TreeNode> totalNodeList = new ArrayList<>(2);
+
+        // 构建根节点:
+        TreeNode itemRoot = new TreeNode("", BASE_TYPE_II,
+                ResManager.loadKDString("基本项目-II", "FormulaItemOrFuncTreeHelper_1",
+                        "swc-hsas-business", new Object[0]));
+        itemRoot.setExpend(isExpend);
+        itemRoot.setIsOpened(isExpend);
+        totalNodeList.add(itemRoot);
+
+        //构建子节点,这里固定滞纳金和利息
+        List<TreeNode> children = new ArrayList<>(2);
+        children.add(new TreeNode(BASE_TYPE_II, "999990", "滞纳金"));
+        children.add(new TreeNode(BASE_TYPE_II, "999991", "利息"));
+        itemRoot.setChildren(children);
+        return totalNodeList;
+    }
+
+
+
     private static List<Map<String, Object>> getInsuranceItemList(String name,
                                                                Map<String, Map<String, Map<String, Object>>> dataMap) {
         return SWCStringUtils.isEmpty(name)

+ 27 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/opplugin/web/coordination/SinsurEmpConfirmOpEx.java

@@ -0,0 +1,27 @@
+package nckd.jxccl.sit.hcsi.opplugin.web.coordination;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.hr.hbp.common.util.HRStringUtils;
+import kd.hr.hbp.opplugin.web.HRDataBaseOp;
+import nckd.jxccl.sit.hcsi.validate.web.SinsurEmpCoordBillValidator;
+
+/**
+ * Tyx 2025-10-28
+ * 社保核定单-确认生效扩展插件【nckd_hpdi_empcoordver_ext】
+ */
+public class SinsurEmpConfirmOpEx extends HRDataBaseOp {
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+    }
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+        e.addValidator(new SinsurEmpCoordBillValidator());
+    }
+}

+ 9 - 3
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/utils/ReportUtils.java

@@ -22,8 +22,11 @@ public class ReportUtils {
             +"entryentity.nckd_itemvalue as value";
 
     //参保单位,险种,工号,姓名,证件号,单位缴费金额 = 单位固定+单位缴费+单位补缴,个人缴费金额=个人固定+个人缴费+个人补缴
+    //2025-11-17调整 单位缴费金额 = 单位固定+单位缴费,单位补缴金额 = 单位补缴,个人缴费金额=个人固定+个人缴费;个人补缴 = 个人补缴;
     private static String CALPERSON_FIELDS = "welfarepayer.id as welfarepayer,entryentity.insuranceitem.group.id as welfaretypeid,"
-            +"empnumberdb as empnumber,namedb as empname,percre.number as empidcard,entryentity.amountvalue as value,"
+            +"empnumberdb as empnumber,namedb as empname,percre.number as empidcard,employee.id as employee,"
+            +"case when entryentity.insuranceitem.insurancetypeattr.number in ('1006_S','1005_S','1009_S','1010_S') then entryentity.amountvalue else 0 end as value,"
+            +"case when entryentity.insuranceitem.insurancetypeattr.number in ('1011_S','1012_S') then entryentity.amountvalue else 0 end as value1,"
             +"case when entryentity.insuranceitem.insurancetypeattr.number in ('1005_S','1009_S','1011_S') then '1'"
             +"     when entryentity.insuranceitem.insurancetypeattr.number in ('1006_S','1010_S','1012_S') then '2' end as type";
     private static final String[] INSURANCEPROP = new String[]{"1005_S","1006_S","1009_S","1010_S","1011_S","1012_S"};
@@ -88,8 +91,11 @@ public class ReportUtils {
         DataSet insuranceItemDataSet = queryInsuranceItemDataSet(itemIds);
         DataSet dataSet = outsideDataSet.join(insuranceItemDataSet).on("itemid","id").select(
                 new String[]{"welfarepayer","empnumber","empname","empidcard","cast(value as Decimal(10,2)) as value"},
-                new String[]{"welfaretypeid", "case when attrnumber in ('1005_S','1009_S','1011_S') then '1' when attrnumber in ('1006_S','1010_S','1012_S') then '2' end as type"}).finish();
-        return dataSet;
+                new String[]{"attrnumber", "welfaretypeid", "case when attrnumber in ('1005_S','1009_S','1011_S') then '1' when attrnumber in ('1006_S','1010_S','1012_S') then '2' end as type"}).finish();
+        DataSet dataSet1 = dataSet.select("welfarepayer","empnumber","empname","empidcard","welfaretypeid","attrnumber","type",
+                "case when attrnumber in ('1006_S','1005_S','1009_S','1010_S') then value else 0 end as value",
+                "case when attrnumber in ('1011_S','1012_S') then value else 0 end as value1");
+        return dataSet1;
     }
 
     /**

+ 27 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/validate/web/SinsurEmpCoordBillValidator.java

@@ -0,0 +1,27 @@
+package nckd.jxccl.sit.hcsi.validate.web;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.validate.AbstractValidator;
+
+/**
+ * Tyx 2025-10-29
+ * 社保核定单校验器,弱校验
+ * 当存在单据体未维护子单据体的情况弹窗提示
+ */
+public class SinsurEmpCoordBillValidator extends AbstractValidator {
+
+    @Override
+    public void validate() {
+        String key = this.getOperateKey();
+        for (ExtendedDataEntity dataEntity : dataEntities) {
+            // TODO 个别类型的核定单不校验
+            DynamicObject bill = dataEntity.getDataEntity();
+            DynamicObjectCollection entryCols = bill.getDynamicObjectCollection("nckd_entryentity");
+            if(entryCols.size() == 0) {
+                this.addWarningMessage(dataEntity, "未维护险种基数信息");
+            }
+        }
+    }
+}

+ 14 - 21
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hcdm/business/annualincome/AnnualIncomeService.java

@@ -1,12 +1,17 @@
 package nckd.jxccl.swc.hcdm.business.annualincome;
 
 
+import kd.bos.algo.DataSet;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.QueryEntityType;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
+import kd.bos.orm.ORM;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
 import kd.sdk.swc.hsas.business.internal.spi.CalResultQueryService;
 import kd.swc.hsbp.common.constants.SWCBaseConstants;
 import nckd.jxccl.swc.constants.SwcConstant;
@@ -38,9 +43,10 @@ public class AnnualIncomeService {
      * @param calPersonIds
      * @param itemIds
      */
-    public void querySalaryDetail(List<Long> calPersonIds, List<Long> itemIds) {
+    public Map<Long, Map<Long, Object>> querySalaryDetail(List<Long> calPersonIds, List<Long> itemIds) {
         logger.info("开始查询薪资明细数据,参数:{},{}", calPersonIds, itemIds);
         Map<Long, Map<Long, Object>> map = CalResultQueryService.get().getSalaryResult(calPersonIds, itemIds);
+        return map;
     }
 
 
@@ -82,32 +88,19 @@ public class AnnualIncomeService {
     }
     /**
      * 根据核算任务ID获取核算人员
-     *
+     * 标品CalResultQueryService.get().getCalPersonInfos会报错,详见问题列表52项;
+     * 新增一个hsas_salarycalresultqueryex查询
+     * 这里改成先通过getCalPersonIds获取到核算人员ID,再获取相关信息
      * @param taskIds
      * @return
      */
     private DynamicObjectCollection queryCalPersonByCalPayRollTask (List<Long> taskIds) {
         logger.info("开始查询核算人员数据,参数:{}", taskIds);
-//        //QFilter filter = new QFilter("hsas_calpayrolltask.id", QCP.in, taskIds);
-//        QFilter filter = new QFilter("filenumber", QCP.equals, "999999");
-//        // 计薪人员,工号,证件号
-//        List<String> selectFields = new ArrayList<String>();
-//        //selectFields.add("employee.id");
-//        selectFields.add("filenumber");
-//        //selectFields.add("hsas_calpayrolltask.id");
-//        //selectFields.add("hsbs_percre.number");
-//        DynamicObjectCollection calPersonIds = CalResultQueryService.get().getCalPersonInfos(selectFields, filter.toArray(), null, 0, 1000);
-//        return calPersonIds;
-        
-        QFilter filter = new QFilter("empnumber", QCP.equals, "999999");
-        List<String> selectFields = new ArrayList<>(SWCBaseConstants.INITCAPACITY_ARRAYLIST);
-        selectFields.add("id");
-        selectFields.add("empnumber");
-        DynamicObjectCollection calPersonCols = CalResultQueryService.get().getCalPersonInfos(selectFields, new QFilter[]{filter}, "id", 0, 1000);
+        QFilter filter = new QFilter("hsas_calpayrolltask.id", QCP.in, taskIds);
+        QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("hsas_salarycalresultqueryex");
+        DataSet dataSet = HRQueryEntityHelper.getInstance().getQueryDataSet(queryEntityType, "id,hsbs_percre.number,hsbs_employee.id,hsbs_employee.empnumber,hsas_calpayrolltask.payrolldate", new QFilter[]{filter}, "id", false, 0, 1000);
+        DynamicObjectCollection calPersonCols = ORM.create().toPlainDynamicObjectCollection(dataSet);
         return calPersonCols;
-        
-        
-        
     }
 
 }

+ 77 - 13
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hcdm/formplugin/annualincome/SalAnnualIncomeBillEdit.java

@@ -1,6 +1,5 @@
 package nckd.jxccl.swc.hcdm.formplugin.annualincome;
 
-import kd.ai.gai.flow.dt.D;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.utils.ObjectUtils;
@@ -10,19 +9,13 @@ import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.operate.FormOperate;
 import kd.bos.form.plugin.AbstractFormPlugin;
-import kd.bos.orm.query.QCP;
-import kd.bos.orm.query.QFilter;
-import kd.epm.eb.common.utils.CalendarHelper;
-import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.swc.hcdm.business.annualincome.AnnualIncomeService;
 import nckd.jxccl.swc.utils.SwcUtils;
 
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.EventObject;
-import java.util.List;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * Tyx 2025-11-05
@@ -87,15 +80,86 @@ public class SalAnnualIncomeBillEdit extends AbstractFormPlugin implements Plugi
         Date endDate = SwcUtils.getLastDayOfYear(calYear);
         // 声明service
         AnnualIncomeService service = new AnnualIncomeService(payRollGrp.getLong("id"), calYear, endDate);
-        // 获取核算人员信息
+        // 获取核算人员信息,一个核算人员一条记录
         DynamicObjectCollection calPersonDyns = service.queryCalPersonByPayRollGroup();
-//        List<Long> calPersonIds = service.queryCalPersonByPayRollGroup();
+        List<Long> calPersonIds = calPersonDyns.stream().map((o) -> {
+            return o.getLong("id");
+        }).collect(Collectors.toList());
         // 获取薪酬项目ID,这里固定为应发工资 JT_283
         List<String> itemNos = new ArrayList<String>();
         itemNos.add("JT_283");
         List<Long> itemIds = SwcUtils.getSalaryItemIdByNumber(itemNos);
-//        service.querySalaryDetail(calPersonIds, itemIds);
+        // 获取薪资明细数据 一个核算人员一条<k1,v1<k2,v2>>,k1=核算人员ID,k2=薪酬项目ID,v2=金额
+        Map<Long, Map<Long, Object>> salaryMap = service.querySalaryDetail(calPersonIds, itemIds);
+
+        // 把核算人员相关信息和薪资明细合并在一起
+        Map<Long, Map<String, Object>> unionMap = (Map) calPersonDyns.stream().collect(Collectors.toMap((dyx) -> {
+            return dyx.getLong("id");
+        }, (dyx) -> {
+            Map map = new HashMap();
+            map.put("employeeId", dyx.getLong("hsbs_employee.id"));
+            map.put("idCardNo", dyx.getLong("hsbs_percre.number"));
+            map.put("payRollDate", dyx.getDate("hsas_calpayrolltask.payrolldate"));
+            map.put("amount", salaryMap.get(dyx.getLong("id")).get(itemIds.get(0)));
+            return map;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+        // 按照employeeId提取出来
+        List<Map<String, Object>> finalResult = dealGroupData(unionMap);
+
+
+        // 界面赋值
+        for (Map map : finalResult) {
+            int rowIndex = model.createNewEntryRow("nckd_entryentity");
+            model.setValue("nckd_employee", map.get("employeeId"), rowIndex);
+            model.setValue("nckd_idcardno", map.get("idCardNo"), rowIndex);
+            model.setValue("nckd_totalsalary", map.get("totalAmount"), rowIndex);
+            model.setValue("nckd_month", map.get("uniqueDateCount"), rowIndex);
+        }
+
     }
 
+    public List<Map<String, Object>> dealGroupData (Map<Long, Map<String, Object>> unionMap) {
+        Map<String, Map<String, Object>> result = new HashMap<>();
+        Iterator<Long> it = unionMap.keySet().iterator();
+        while(it.hasNext()) {
+            Map<String, Object> record = unionMap.get(it.next());
+            String key = record.get("employeeId") + "_" + record.get("idCardNo");
+
+            if (!result.containsKey(key)) {
+                Map<String, Object> summary = new HashMap<>();
+                summary.put("employeeId", record.get("employeeId"));
+                summary.put("idCardNo", record.get("idCardNo"));
+                summary.put("totalAmount", new BigDecimal("0"));
+                summary.put("uniqueDates", new HashSet<String>());
+                result.put(key, summary);
+            }
 
+            Map<String, Object> summary = result.get(key);
+            // 累加金额
+            BigDecimal currentAmount = (BigDecimal) summary.get("totalAmount");
+            summary.put("totalAmount", currentAmount.add((BigDecimal) record.get("amount")));
+
+            // 添加日期到集合中(自动去重)
+            @SuppressWarnings("unchecked")
+            Set<Date> uniqueDates = (Set<Date>) summary.get("uniqueDates");
+            uniqueDates.add((Date) record.get("payRollDate"));
+        }
+
+        // 转换结果格式,将Set的大小作为日期计数
+        List<Map<String, Object>> finalResult = new ArrayList<>();
+        for (Map<String, Object> summary : result.values()) {
+            Map<String, Object> finalRecord = new HashMap<>();
+            finalRecord.put("employeeId", summary.get("employeeId"));
+            finalRecord.put("idCardNo", summary.get("idCardNo"));
+            finalRecord.put("totalAmount", summary.get("totalAmount"));
+            @SuppressWarnings("unchecked")
+            Set<Date> uniqueDates = (Set<Date>) summary.get("uniqueDates");
+            finalRecord.put("uniqueDateCount", uniqueDates.size());
+            finalResult.add(finalRecord);
+        }
+
+        return finalResult;
+    }
 }

+ 23 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hpdi/business/coordination/api/SwcCoordBizExtRegister.java

@@ -0,0 +1,23 @@
+package nckd.jxccl.swc.hpdi.business.coordination.api;
+
+import kd.hr.hbp.business.coordination.api.ext.CoordBizBeforeGenerateHandler;
+import kd.hr.hbp.business.coordination.api.ext.CoordBizExtRegister;
+import kd.hr.hbp.business.coordination.model.CoordBizContext;
+
+/**
+ * Tyx 2025-11-18
+ * 薪资档案注册器,注册在 薪资数据集成 -> 员工变动协作配置 -> 协作字段配置 -> 薪资档案的bizextregister字段中
+ */
+public class SwcCoordBizExtRegister implements CoordBizExtRegister {
+
+    @Override
+    public void registerHandler(CoordBizContext context) {
+        CoordBizExtRegister.super.registerHandler(context);
+        context.registerHandlers(CoordBizContext.CoordBizStage.END_OPERATE, new SwcCoordBizSaveHandler());
+    }
+
+    @Override
+    public CoordBizBeforeGenerateHandler beforeGenerateHandler() {
+        return kd.hr.hbp.business.coordination.api.ext.CoordBizBeforeGenerateHandler.Default.DEFAULT;
+    }
+}

+ 56 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hpdi/business/coordination/api/SwcCoordBizSaveHandler.java

@@ -0,0 +1,56 @@
+package nckd.jxccl.swc.hpdi.business.coordination.api;
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.exception.KDBizException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.hr.hbp.business.coordination.api.ext.CoordBizSaveHandler;
+import kd.hr.hbp.business.coordination.model.CoordBizContext;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SwcCoordBizSaveHandler implements CoordBizSaveHandler {
+
+    private static final Log log = LogFactory.getLog(SwcCoordBizSaveHandler.class);
+    private static HRBaseServiceHelper SALARYGROUP_HELPER = new HRBaseServiceHelper("nckd_persalarygroup");
+    private static String SALARYGROUP_ENTITY = "nckd_persalarygroup";
+    @Override
+    public Map<String, Object> handle(Map<String, DynamicObject> map, Map<String, Map<String, Object>> map1, DynamicObject dynamicObject, CoordBizContext coordBizContext) {
+        log.info(" -------- begin SwcCoordBizSaveHandler handle --------");
+        // 返回的Map
+        Map<String, Object> returnMap = new HashMap<String, Object>();
+        DynamicObject salaryFile = map.get("hsas_salaryfile");
+        if(!ObjectUtils.isEmpty(salaryFile)) {
+            Date startDate = salaryFile.getDate("nckd_groupstartdate");
+            Date endDate = salaryFile.getDate("nckd_groupenddate");
+            DynamicObject salaryGroup = salaryFile.getDynamicObject("nckd_salarygroup");
+            DynamicObject employee = salaryFile.getDynamicObject("employee");
+
+            DynamicObject dyn = SALARYGROUP_HELPER.generateEmptyDynamicObject();
+            dyn.set("startdate", startDate);
+            dyn.set("enddate", endDate);
+            dyn.set("nckd_salarygroup", salaryGroup);
+            dyn.set("nckd_employee", employee);
+            dyn.set("nckd_salaryfile", salaryFile);
+
+            OperationResult result = SaveServiceHelper.saveOperate(SALARYGROUP_ENTITY, new DynamicObject[]{dyn}, OperateOption.create());
+            log.info("-------- 薪酬分类保存结果:" + result.isSuccess());
+            if(result.isSuccess()) {
+                returnMap.put("success",true);
+            }
+            else {
+                returnMap.put("success",false);
+                log.info("-------- 薪酬分类错误信息:" + result.getMessage());
+                throw new KDBizException("-------- 薪酬分类保存失败:" + result.getMessage());
+            }
+        }
+        return returnMap;
+    }
+}

+ 4 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/hsbs/formplugin/web/basedata/salarygroup/PerSalaryGroupEdit.java

@@ -17,5 +17,9 @@ public class PerSalaryGroupEdit extends AbstractFormPlugin implements Plugin {
         if (null != employeeIdObj) {
             this.getView().getModel().setValue("nckd_employee", Long.parseLong(String.valueOf(employeeIdObj)));
         }
+        Object salaryFileObj = this.getView().getParentView().getFormShowParameter().getCustomParam("salaryfileId");
+        if (null != salaryFileObj) {
+            this.getView().getModel().setValue("nckd_salaryfile", Long.parseLong(String.valueOf(salaryFileObj)));
+        }
     }
 }

+ 9 - 4
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/business/CalResultCoverSalaryItemExtPlugin.java

@@ -4,10 +4,12 @@ import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.param.CustomParam;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
 import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
 import kd.bos.servicehelper.permission.PermissionServiceHelper;
-import kd.sdk.swc.hsas.common.events.calperson.CalResultCoverSalaryItemEvent;
-import kd.sdk.swc.hsas.formplugin.extpoint.resultcover.ICalResultCoverSalaryItemExtPlugin;
+import kd.sdk.swc.hsas.business.extpoint.payroll.ICalPersonCalResultCoverExtPlugin;
+import kd.sdk.swc.hsas.common.events.calperson.CalResultCoverSalaryItemEventArgs;
 
 import kd.swc.hsas.business.calpayrolltask.CalPayrollTaskHelper;
 import kd.swc.hsbp.business.servicehelper.SWCDataServiceHelper;
@@ -23,9 +25,12 @@ import java.util.Map;
  * @author turborao
  * @date 2025/10/31 10:05
  */
-public class CalResultCoverSalaryItemExtPlugin  implements ICalResultCoverSalaryItemExtPlugin {
+public class CalResultCoverSalaryItemExtPlugin implements ICalPersonCalResultCoverExtPlugin {
+
+    private static final Log logger = LogFactory.getLog(CalResultCoverSalaryItemExtPlugin.class);
     @Override
-    public void fillExtSalaryItem(CalResultCoverSalaryItemEvent event) {
+    public void onFillExtSalaryItem(CalResultCoverSalaryItemEventArgs event) {
+        logger.info("进入计算结果覆盖薪酬项目扩展埋点");
         ArrayList excludeItemIdList = new ArrayList<Long>();
         /**
          * 获取当前用户

+ 184 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/plugin/form/PendingSalaryAdjListPlugin.java

@@ -0,0 +1,184 @@
+package nckd.jxccl.swc.init.plugin.form;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.param.CustomParam;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.list.plugin.AbstractListPlugin;
+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 kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
+import kd.sdk.plugin.Plugin;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 员工待定调薪清单
+ * @author turborao
+ * @date 2025/11/19 11:35
+ */
+public class PendingSalaryAdjListPlugin extends AbstractListPlugin implements Plugin {
+
+    /**
+     * 员工待定调薪清单
+     */
+    private static final String entityName = "nckd_pendingsalaryadj";
+    /**
+     * 入职单
+     */
+    private static final String onhasonbrdEntityName = "hom_personhasonbrd";
+    /**
+     * 调动单
+     */
+    private static final String transferEntityName = "hdm_transferapply";
+
+    private static int days = 5;
+
+    private static Log logger = LogFactory.getLog(PendingSalaryAdjListPlugin.class);
+
+    private DynamicObjectCollection salaryTypeDyns = null;
+    @Override
+    public void initialize() {
+        super.initialize();
+        /**
+         * 获取 公共自定义参数  PENDSALARYADJ_DAYS
+         */
+        CustomParam customParam = new CustomParam();
+        customParam.getSearchKeySet().add("PENDSALARYADJ_DAYS");
+        Map<String, String> cusTomMap = SystemParamServiceHelper.loadCustomParameterFromCache(customParam);
+        String pendsalaryadjDays  = cusTomMap.get("PENDSALARYADJ_DAYS");
+        days = Integer.parseInt(pendsalaryadjDays);
+
+        ArrayList<String> salaryTypeList = new ArrayList<>();
+        salaryTypeList.add("tiaozhengtiaoxin");  //岗位调整调薪
+        salaryTypeList.add("100009");  //入职定薪新
+        QFilter qFilter11 = new QFilter("number", QCP.in, salaryTypeList);
+        salaryTypeDyns = QueryServiceHelper.query("hsbs_salaryadjustrsn","id,number,name",new QFilter[]{qFilter11},"number");
+
+        //加载入职单与调动单的数据
+        getAdjustBillData();
+    }
+
+
+    @Override
+    public void beforeBindData(EventObject e) {
+        super.beforeBindData(e);
+
+    }
+
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+
+        FormOperate formOperate = (FormOperate)args.getSource();
+        String key = formOperate.getOperateKey();
+        switch (key) {
+            case "refresh":
+                //加载入职单与调动单的数据
+                getAdjustBillData();
+                break;
+
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs args) {
+        super.afterDoOperation(args);
+        FormOperate formOperate = (FormOperate)args.getSource();
+        String key = formOperate.getOperateKey();
+        switch (key) {
+            case "apply_adj":
+                break;
+            case "flag_adj":
+                break;
+            case "flag_ignore":
+                break;
+            case "cleanflag":
+                break;
+        }
+    }
+
+    /**
+     * 获取入职单与调动单的数据
+     */
+    public void getAdjustBillData() {
+
+        LocalDate currentDate = LocalDate.now();
+        // Subtract 5 days
+        LocalDate daysAgo = currentDate.minusDays(days);
+
+        ArrayList<DynamicObject> billDynList = new ArrayList<>();
+
+        /**
+         * 查询入职单
+         */
+        String selectField2 = "id,billno,b_effectivedate,ba_em_tid,ba_em_empnumber,ba_em_name,ba_po_position.id,ba_po_adminorg,ba_po_job.id,ajoblevel.id,ba_po_job.jobseq.id";
+        QFilter qFilter1 = new QFilter("enrollstatus", QCP.equals, "3"); // 入职状态 = 已入职
+        QFilter qFilter2 = new QFilter("billstatus", QCP.equals, "C"); // 单据状态 = 已审核
+        QFilter qFilter3 = new QFilter("b_effectivedate", QCP.large_equals, daysAgo);
+
+        DynamicObjectCollection onhasonbrdDyns = QueryServiceHelper.query(onhasonbrdEntityName, selectField2,new QFilter[]{qFilter1,qFilter2,qFilter3});
+
+        Map<Long, DynamicObject> onhasonbrdMap = (Map)onhasonbrdDyns.stream().collect(Collectors.toMap((obj) -> {
+            return obj.getLong("id");
+        }, (obj) -> {
+            return obj;
+        }, (k1, k2) -> {
+            return k1;
+        }));
+
+        //入职  将Map转换为List
+        List<Long> onhasonbrdIDs = onhasonbrdMap.keySet().stream().collect(Collectors.toList());
+
+        QFilter qFilter22 = new QFilter("nckd_billid", QCP.in, onhasonbrdIDs);
+        List<Object> listIDs = QueryServiceHelper.queryPrimaryKeys(entityName,new QFilter[]{qFilter22}, null, Integer.MAX_VALUE);
+        DynamicObject billDyn =  BusinessDataServiceHelper.newDynamicObject(entityName);
+        DynamicObject[] billDyns = BusinessDataServiceHelper.load(listIDs.toArray(),billDyn.getDynamicObjectType());
+
+        int onhasonbrdCount = 0;
+        Map<Long, DynamicObject> billMap =
+                Arrays.stream(billDyns)
+                        .collect(Collectors.toMap(
+                                detail -> detail.getLong("nckd_billid"),
+                                detail -> detail, // 整个 DynamicObject 作为 value
+                                (existing, replacement) -> existing // 保留前面的值
+                        ));
+
+        for(DynamicObject onhasonbrdDyn: onhasonbrdDyns){
+            DynamicObject billDyn1 = billMap.get(onhasonbrdDyn.getLong("id"));
+            if(billDyn1 != null) continue;
+            DynamicObject dyn = BusinessDataServiceHelper.newDynamicObject(entityName);
+            dyn.set("billno", onhasonbrdDyn.getString("billno"));
+            dyn.set("nckd_billid", onhasonbrdDyn.getLong("id"));
+            dyn.set("nckd_billtype", "入职单");
+            dyn.set("billstatus", "A");  ///待处理
+            dyn.set("nckd_employeefield", onhasonbrdDyn.getLong("ba_em_tid"));
+            dyn.set("nckd_changedate", onhasonbrdDyn.getDate("b_effectivedate"));
+            dyn.set("nckd_salaryadjus", salaryTypeDyns.get(0).getLong("id"));  //定调薪类型
+            dyn.set("nckd_newhradminorg", onhasonbrdDyn.getLong("ba_po_adminorg"));  //部门
+            dyn.set("nckd_newposition", onhasonbrdDyn.getLong("ba_po_position.id"));  //岗位
+            dyn.set("nckd_newjoblevel", onhasonbrdDyn.getLong("ajoblevel.id"));  //职级
+            dyn.set("nckd_newjobseqhr", onhasonbrdDyn.getLong("ba_po_job.jobseq.id"));  //职位序列
+            onhasonbrdCount++;
+            billDynList.add(dyn);
+        }
+
+        int length = 0;
+        if(billDynList.size() != 0) {
+            //保存
+            Object[] update = SaveServiceHelper.save(billDynList.toArray(new DynamicObject[0]));
+            length = update.length;
+            this.getView().showSuccessNotification("成功导入,"+length+"条数据,其中入职单"+onhasonbrdCount+"条,",3000); // 成功类型
+        }
+
+    }
+}

+ 91 - 91
code/wtc/nckd-jxccl-wtc/src/main/java/nckd/jxccl/wtc/task/SyncPunchCardTask.java

@@ -1,92 +1,92 @@
-package nckd.jxccl.wtc.task;
-
-import com.alibaba.fastjson.JSON;
-import com.hikvision.artemis.sdk.ArtemisHttpUtil;
-import com.hikvision.artemis.sdk.config.ArtemisConfig;
-import kd.bos.context.RequestContext;
-import kd.bos.entity.param.CustomParam;
-import kd.bos.exception.KDException;
-import kd.bos.schedule.executor.AbstractTask;
-import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
-import kd.sdk.plugin.Plugin;
-import nckd.jxccl.wtc.utils.SyncPunchCardHelper;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 后台任务插件
- */
-public class SyncPunchCardTask extends AbstractTask implements Plugin {
-    private static final String ARTEMIS_PATH = "/artemis";
-    static final String getCamsApi = ARTEMIS_PATH + "/api/acs/v2/door/events";
-    static Map<String, String> path = new HashMap<String, String>(2) {
-        {
-            put("https://", getCamsApi);
-        }
-    };
-
-    public void initConfig() {
-        CustomParam customParam = new CustomParam();
-        customParam.getSearchKeySet().add("PUNCH_CARD_HOST");
-        customParam.getSearchKeySet().add("PUNCH_CARD_APP_KEY");
-        customParam.getSearchKeySet().add("PUNCH_CARD_APP_SECRET");
-        Map<String, String> cusTomMap = SystemParamServiceHelper.loadCustomParameterFromCache(customParam);
-        ArtemisConfig.host  = cusTomMap.get("PUNCH_CARD_HOST");
-        ArtemisConfig.appKey = cusTomMap.get("PUNCH_CARD_APP_KEY");
-        ArtemisConfig.appSecret = cusTomMap.get("PUNCH_CARD_APP_SECRET");
-    }
-
-    @Override
-    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
-        try {
-            initConfig();
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-            Calendar date = Calendar.getInstance();
-            System.out.println(sdf.format(date.getTime()));
-            String endTime = sdf.format(date.getTime()) + "T23:59:59+08:00";
-            Integer days = Integer.valueOf(map.get("days").toString());
-            date.set(Calendar.DATE, date.get(Calendar.DATE) - days);
-            System.out.println(sdf.format(date.getTime()));
-            String startTime = sdf.format(date.getTime()) + "T00:00:00+08:00";
-            getAllCards(startTime, endTime);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public static void getAllCards(String startTime, String endTime) {
-        JSONObject firstPageJson = getOnePageCard(startTime, endTime, 1);
-        JSONArray firstPageListJson = firstPageJson.getJSONArray("list");
-        // 保存第一页
-        SyncPunchCardHelper.savePunchCardData(firstPageListJson);
-        Integer totalPage = firstPageJson.getInteger("totalPage");
-        if (totalPage <= 1) {
-            return;
-        }
-        // 从第二页开始保存
-        for(int i=2; i<=totalPage; i++) {
-            JSONObject pageJson = getOnePageCard(startTime, endTime, i);
-            JSONArray pageListJson = pageJson.getJSONArray("list");
-            SyncPunchCardHelper.savePunchCardData(pageListJson);
-        }
-    }
-
-    public static JSONObject getOnePageCard(String startTime, String endTime, int pageNo) {
-        JSONObject jsonBody = new JSONObject();
-        jsonBody.put("pageNo", pageNo);
-        jsonBody.put("pageSize", 1000);
-        jsonBody.put("startTime", startTime);
-        jsonBody.put("endTime", endTime);
-        jsonBody.put("userId", "admin");
-        String body = jsonBody.toString();
-        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, "application/json", null);
-        JSONObject jsonData = JSON.parseObject(result).getJSONObject("data");
-        return jsonData;
-    }
-
+package nckd.jxccl.wtc.task;
+
+import com.alibaba.fastjson.JSON;
+import com.hikvision.artemis.sdk.ArtemisHttpUtil;
+import com.hikvision.artemis.sdk.config.ArtemisConfig;
+import kd.bos.context.RequestContext;
+import kd.bos.entity.param.CustomParam;
+import kd.bos.exception.KDException;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.wtc.utils.SyncPunchCardHelper;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 后台任务插件
+ */
+public class SyncPunchCardTask extends AbstractTask implements Plugin {
+    private static final String ARTEMIS_PATH = "/artemis";
+    static final String getCamsApi = ARTEMIS_PATH + "/api/acs/v2/door/events";
+    static Map<String, String> path = new HashMap<String, String>(2) {
+        {
+            put("https://", getCamsApi);
+        }
+    };
+
+    public void initConfig() {
+        CustomParam customParam = new CustomParam();
+        customParam.getSearchKeySet().add("PUNCH_CARD_HOST");
+        customParam.getSearchKeySet().add("PUNCH_CARD_APP_KEY");
+        customParam.getSearchKeySet().add("PUNCH_CARD_APP_SECRET");
+        Map<String, String> cusTomMap = SystemParamServiceHelper.loadCustomParameterFromCache(customParam);
+        ArtemisConfig.host  = cusTomMap.get("PUNCH_CARD_HOST");
+        ArtemisConfig.appKey = cusTomMap.get("PUNCH_CARD_APP_KEY");
+        ArtemisConfig.appSecret = cusTomMap.get("PUNCH_CARD_APP_SECRET");
+    }
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        try {
+            initConfig();
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            Calendar date = Calendar.getInstance();
+            System.out.println(sdf.format(date.getTime()));
+            String endTime = sdf.format(date.getTime()) + "T23:59:59+08:00";
+            Integer days = Integer.valueOf(map.get("days").toString());
+            date.set(Calendar.DATE, date.get(Calendar.DATE) - days);
+            System.out.println(sdf.format(date.getTime()));
+            String startTime = sdf.format(date.getTime()) + "T00:00:00+08:00";
+            getAllCards(startTime, endTime);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void getAllCards(String startTime, String endTime) {
+        JSONObject firstPageJson = getOnePageCard(startTime, endTime, 1);
+        JSONArray firstPageListJson = firstPageJson.getJSONArray("list");
+        // 保存第一页
+        SyncPunchCardHelper.savePunchCardData(firstPageListJson);
+        Integer totalPage = firstPageJson.getInteger("totalPage");
+        if (totalPage <= 1) {
+            return;
+        }
+        // 从第二页开始保存
+        for(int i=2; i<=totalPage; i++) {
+            JSONObject pageJson = getOnePageCard(startTime, endTime, i);
+            JSONArray pageListJson = pageJson.getJSONArray("list");
+            SyncPunchCardHelper.savePunchCardData(pageListJson);
+        }
+    }
+
+    public static JSONObject getOnePageCard(String startTime, String endTime, int pageNo) {
+        JSONObject jsonBody = new JSONObject();
+        jsonBody.put("pageNo", pageNo);
+        jsonBody.put("pageSize", 1000);
+        jsonBody.put("startTime", startTime);
+        jsonBody.put("endTime", endTime);
+        jsonBody.put("userId", "admin");
+        String body = jsonBody.toString();
+        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, "application/json", null);
+        JSONObject jsonData = JSON.parseObject(result).getJSONObject("data");
+        return jsonData;
+    }
+
 }