Browse Source

Merge remote-tracking branch 'origin/master'

彭佳杰 6 ngày trước cách đây
mục cha
commit
0f1763d939
100 tập tin đã thay đổi với 4448 bổ sung323 xóa
  1. 41 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  2. 27 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/hbp/formplugin/web/coordination/EmpChangeCoordinationListTplPluginEx.java
  3. 89 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/DateTimeUtils.java
  4. 9 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/DateUtil.java
  5. 3 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/PatternUtil.java
  6. 84 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/entity/helper/EntityHelper.java
  7. 75 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/hrpi/helper/EmpPosOrgRelHelper.java
  8. 96 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/OrgHelper.java
  9. 132 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/sit/helper/SITHelper.java
  10. 6 6
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/swc/helper/AdjFileServiceHelper.java
  11. 1 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/swc/helper/SWCHelper.java
  12. 18 18
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/excells/plugin/operate/PushAdjustOpPlugin.java
  13. 81 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/haos/plugin/DealOrgLongNamePlugin.java
  14. 12 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/business/transfer/TransferApplyBillServiceHelper.java
  15. 151 1
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/common/transfer/TransferApplyBillConstant.java
  16. 130 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/plugin/form/transfer/TransferApplyBillFormPlugin.java
  17. 317 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/plugin/operate/transfer/CancelContractAfterEffectOpPlugin.java
  18. 24 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/PersonnelPredictionService.java
  19. 33 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/api/IPersonnelPredictionService.java
  20. 36 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/servicehelper/ServiceFactory.java
  21. 4 18
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/htm/plugin/form/quitapply/QuitApplyFormPlugin.java
  22. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/business/.gitkeep
  23. 22 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/common/AppflgConstant.java
  24. 35 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/common/IntJobPostConstant.java
  25. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/mservice/.gitkeep
  26. 467 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/form/recgen/IntJobPostFormPlugin.java
  27. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/operate/.gitkeep
  28. 43 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/operate/IntJobPostAuditedOpPlugin.java
  29. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/other/.gitkeep
  30. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/report/.gitkeep
  31. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/workflow/.gitkeep
  32. 0 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/webapi/.gitkeep
  33. 1 1
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/report/UnAdjustedReportFormPlugin.java
  34. 8 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/annualadjust/report/UnAnnualAdjustReportFormPlugin.java
  35. 5 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/other/report/KeyBeHavEvalReptQueryPlugin.java
  36. 5 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java
  37. 107 76
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/file/PersonPosFileSaveOpPlugin.java
  38. 13 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/PerfRankMgmtSaveOpPlugin.java
  39. 20 12
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/task/PsmsAdjustSalaryTask.java
  40. 4 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/common/SanDingConstant.java
  41. 27 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/plugin/form/SanDingTaskFormPlugin.java
  42. 3 1
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/plugin/operate/SendTaskOpPlugin.java
  43. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillAfterBindDataService.java
  44. 3 3
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBeforeBindDataService.java
  45. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillClosedCallBackService.java
  46. 14 12
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillConfirmCallBackService.java
  47. 245 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillEffectService.java
  48. 19 14
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillPropertyChangedService.java
  49. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillSaveHelper.java
  50. 123 10
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillServiceHelper.java
  51. 4 2
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionBillConstant.java
  52. 33 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionHRConstant.java
  53. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryAddFormPlugin.java
  54. 9 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryUpdatePlugin.java
  55. 185 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionAssignFormPlugin.java
  56. 5 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillFormPlugin.java
  57. 2 2
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillListPlugin.java
  58. 60 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionHRListPlugin.java
  59. 6 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PosBillEntryNewPositionTempSaveOpPlugin.java
  60. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillDeleteOpPlugin.java
  61. 123 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillEffectOpPlugin.java
  62. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/business/.gitkeep
  63. 22 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/common/AppflgConstant.java
  64. 24 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/common/bd/LawEntityConstant.java
  65. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/mservice/.gitkeep
  66. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/form/.gitkeep
  67. 117 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/form/bd/LawEntityChgFormPlugin.java
  68. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/operate/.gitkeep
  69. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/other/.gitkeep
  70. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/report/.gitkeep
  71. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/workflow/.gitkeep
  72. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/webapi/.gitkeep
  73. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/business/.gitkeep
  74. 22 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/common/AppflgConstant.java
  75. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/mservice/.gitkeep
  76. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/form/.gitkeep
  77. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/operate/.gitkeep
  78. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/other/.gitkeep
  79. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/report/.gitkeep
  80. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/workflow/.gitkeep
  81. 0 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/webapi/.gitkeep
  82. 337 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/EmployeePrintPlugin.java
  83. 125 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PerfBatchPrintListPlugin.java
  84. 268 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PerfDetailPrintPlugin.java
  85. 52 8
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PrintPerfDetailReportListDataPlugin.java
  86. 64 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/SelectDatePrintFormPlugin.java
  87. 17 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailFormPlugin.java
  88. 38 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailReportListDataPlugin.java
  89. 116 63
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerSaveOpPlugin.java
  90. 19 17
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/salary/PushAdjustOpPlugin.java
  91. 12 16
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/salary/SalaryAdjOpPlugin.java
  92. 74 9
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/annualincome/AnnualIncomeCalculateService.java
  93. 1 3
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordBizSaveHandler.java
  94. 14 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurCoordExtRegister.java
  95. 85 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/coordination/api/SinsurVerifBillBeforeSaveHandlerEx.java
  96. 10 7
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/datacomparison/DataComparisonPushService.java
  97. 2 2
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/importtaskguide/ImportTaskGuideExportService.java
  98. 9 2
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/importtaskguide/ImportTaskGuideImportService.java
  99. 30 0
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/coordination/HCSIEmpCoordVerifBillEditEx.java
  100. 25 1
      code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/formplugin/web/coordination/SinSurEmpChgCoordBatchEditPluginEx.java

+ 41 - 1
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -14,11 +14,23 @@ public class FormConstant {
     public static final String HR_INVOKER_PARAM_INVOKER = "hrInvokerParam#invoker";
     public static final String DATA_MIGRATION = "dataMigration";
 
+    //====================================== 标品云ID ======================================
+    /** 工时假勤云 */
+    public static final String WTC_CLOUD = "wtc";
+
     //====================================== 标品应用ID ======================================
     /** 消息中心 */
     public static final String WFTASK_APP = "wftask";
     /** 组织管理 */
     public static final String HOMS_APP = "homs";
+    /** HR通用服务 */
+    public static final String HRCS_APP = "hrcs";
+    /** 员工信息中心 */
+    public static final String HRPI_APP = "hrpi";
+    /** 工时假勤基础服务 */
+    public static final String WTBS_APP = "wtbs";
+    /** 工时假勤规则 */
+    public static final String WTP_APP = "wtp";
 
     //====================================== 标品实体标识(需要小写) ======================================
     /**学历-实体标识*/
@@ -91,6 +103,22 @@ public class FormConstant {
     public static final String HSAS_CALPERSON = "hsas_calperson";
     /** 定调薪类型  实体标识 */
     public static final String HSBS_SALARYADJUSTRSN = "hsbs_salaryadjustrsn";
+    /** 定调薪类型  实体标识 */
+    public static final String HSBS_SALARYITEM = "hsbs_salaryitem";
+    /** 单据类型  实体标识 */
+    public static final String BOS_BILLTYPE = "bos_billtype";
+    /** 国家  实体标识 */
+    public static final String BD_COUNTRY = "bd_country";
+    /** 国家  实体标识 */
+    public static final String BD_CURRENCY = "bd_currency";
+    /** 定调薪方案 实体标识 */
+    public static final String HCDM_ADJAPPROVESCM = "hcdm_adjapprovescm";
+    /** 汇率标 实体标识 */
+    public static final String BD_EXRATETABLE = "bd_exratetable";
+    /** 频度 实体标识 */
+    public static final String HSBS_CALFREQUENCY = "hsbs_calfrequency";
+    /** 考核结果实体标识 */
+    public static final String NCKD_APPRAISALRESULT = "nckd_appraisalresult";
     /**
      * 定调薪类型 默认值  用于  定调薪清单处理
      * */
@@ -144,6 +172,12 @@ public class FormConstant {
     public static final String HAOS_ORGBATCHTREELISTF7_ENTITY = "haos_orgbatchtreelistf7";
     /** 组织快速维护实体标识 */
     public static final String HAOS_ADMINORGDETAIL_ENTITY = "haos_adminorgdetail";
+    /** 企业建模-岗位实体标识 */
+    public static final String BOS_POSITION_ENTITY = "bos_position";
+    /** 合同申请父页面实体标识 */
+    public static final String HLCM_CONTRACTAPPLY_ENTITY = "hlcm_contractapply";
+    /** 合同解除实体标识 */
+    public static final String HLCM_CONTRACTAPPLYCANCEL_ENTITY = "hlcm_contractapplycancel";
 
     //====================================== 标品op ======================================
     /** 确认框确认按钮 */
@@ -461,6 +495,10 @@ public class FormConstant {
     public static final String PHONE_KEY = "phone";
     /** 参加工作日期 */
     public static final String NCKD_JOINWORKTIME = "nckd_joinworktime";
+    /** 参加工作日期 */
+    public static final String JOINWORKTIME_KEY = "joinworktime";
+    /** 新法定拟退休日期 */
+    public static final String NCKD_NEWPLANRETDT = "nckd_newplanretdt";
     /** 出生日期 */
     public static final String BIRTHDAY = "birthday";
     /** 性别 */
@@ -477,7 +515,9 @@ public class FormConstant {
 
 
     /** 岗位工资标准(jt002)*/
-    public static final Long STANDARDITEM_ID_KEY = 2321899710350111744L;
+    public static final String POS_STANDARD_ITEM_NUMBER = "jt002";
+    /** 员工调薪(console_20)*/
+    public static final String HCDM_APPLYBILL_BT_ADJ = "hcdm_applybill_BT_ADJ";
 
     /** 全职任职类型编码 */
     public static final String FULLSERVICE_NUMBER = "1010_S";

+ 27 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/hbp/formplugin/web/coordination/EmpChangeCoordinationListTplPluginEx.java

@@ -0,0 +1,27 @@
+package nckd.jxccl.base.common.hbp.formplugin.web.coordination;
+
+import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.hr.hbp.formplugin.web.HRDataBaseList;
+import kd.sdk.plugin.Plugin;
+
+import java.util.EventObject;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Tyx 2026-01-05
+ * 核定单列表插件
+ * 处理tab页签,添加借调变动大类,标品后续优化,先自行处理
+ */
+public class EmpChangeCoordinationListTplPluginEx extends HRDataBaseList {
+
+    private final List<String> defaultTabNumbers = (List) Stream.of("tab_all", "tab_1010_s", "tab_1090_s", "tab_1020_s", "tab_1070_s", "tab_1030_s", "tab_1040_s", "tab_1050_s", "tab_1160_s", "tab_1120_s", "tab_9990_s", "tab_9980_s").collect(Collectors.toList());
+
+    @Override
+    public void beforeBindData(EventObject e) {
+        super.beforeBindData(e);
+        this.getPageCache().put("tabNumbers", SerializationUtils.toJsonString(defaultTabNumbers));
+    }
+}

+ 89 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/DateTimeUtils.java

@@ -0,0 +1,89 @@
+package nckd.jxccl.base.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class DateTimeUtils {
+
+    public static String format(Date d, String fmt) {
+        return format(d, fmt, (TimeZone)null);
+    }
+
+    public static String format(Date d, String fmt, TimeZone timezone) {
+        return format(d, fmt, timezone, (Locale)null);
+    }
+
+    public static String format(Date d, String fmt, TimeZone timezone, Locale locale) {
+        if (fmt == null) {
+            fmt = "yyyy-MM-dd HH:mm:ss";
+        }
+
+        SimpleDateFormat df;
+        if (locale == null) {
+            df = new SimpleDateFormat(fmt);
+        } else {
+            df = new SimpleDateFormat(fmt, locale);
+        }
+
+        if (timezone == null) {
+            df.setTimeZone(TimeZone.getDefault());
+        } else {
+            df.setTimeZone(timezone);
+        }
+
+        return df.format(d);
+    }
+
+    public static Date parseDate(String s) throws ParseException {
+        try {
+            return parseDate(s, "yyyy-MM-dd HH:mm:ss");
+        } catch (ParseException var6) {
+            try {
+                return parseDate(s, "yyyy-MM-dd");
+            } catch (ParseException var5) {
+                try {
+                    return parseDate(s, "MM/dd/yyyy HH:mm:ss");
+                } catch (ParseException var4) {
+                    try {
+                        return parseDate(s, "MM/dd/yyyy");
+                    } catch (ParseException var3) {
+                        try {
+                            return parseDate(s, "HH:mm:ss");
+                        } catch (ParseException var2) {
+                            throw new ParseException("can not understand your format", -1);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public static Date parseDate(String s, String fmt) throws ParseException {
+        return parseDate(s, fmt, (TimeZone)null);
+    }
+
+    public static Date parseDate(String s, String fmt, TimeZone timezone) throws ParseException {
+        return parseDate(s, fmt, timezone, (Locale)null);
+    }
+
+    public static Date parseDate(String s, String fmt, TimeZone timezone, Locale locale) throws ParseException {
+        SimpleDateFormat df;
+        if (locale == null) {
+            df = new SimpleDateFormat(fmt);
+        } else {
+            df = new SimpleDateFormat(fmt, locale);
+        }
+
+        if (timezone == null) {
+            timezone = TimeZone.getDefault();
+        }
+
+        df.setTimeZone(timezone);
+        df.setLenient(false);
+        return df.parse(s);
+    }
+
+}

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

@@ -47,6 +47,15 @@ public class DateUtil {
         return LocalDateTime.now();
     }
 
+    /**
+     * 获取当前日期时间
+     *
+     * @return 当前日期时间
+     */
+    public static Date nowDate() {
+        return toDate(LocalDateTime.now());
+    }
+
     /**
      * 转换为Date对象
      *

+ 3 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PatternUtil.java → code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/PatternUtil.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.common.hr;
+package nckd.jxccl.base.common.utils;
 
 import kd.bos.entity.ISVInfo;
 import kd.bos.logging.Log;
@@ -10,6 +10,8 @@ import java.util.regex.Pattern;
 
 /**
  * 模型工具类
+ * 用于校验属性名称是否包含特殊字符或是否扩展字段
+ * @from: kd.hr.homs.business.utils.PatternUtil
  * @author: jtd
  * @date: 2025/12/24 19:45
  */

+ 84 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/entity/helper/EntityHelper.java

@@ -1,16 +1,26 @@
 package nckd.jxccl.base.entity.helper;
 
+import com.google.common.collect.Lists;
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.MainEntityType;
 import kd.bos.entity.constant.StatusEnum;
+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.user.UserServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 
 import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 实体(DynamicObject) 帮助类
@@ -75,4 +85,78 @@ public class EntityHelper {
         dynamicObject.set(FormConstant.ENABLE, EnableEnum.YES.getCode());
         return dynamicObject;
     }
+
+
+    /**
+     * 根据编码查询最新的id
+     * @param entityNumber
+     * @param number
+     */
+    public static Long getLatestIdByNumber(String entityNumber, String number) {
+        Map<String, Long> map = getLatestIdByNumber(entityNumber, Lists.newArrayList(number));
+        return !map.isEmpty() ? map.get(number) : null;
+    }
+
+    /**
+     * 根据编码查询最新的id
+     * @param entityNumber
+     * @param number
+     * @return
+     */
+    public static Map<String,Long> getLatestIdByNumber(String entityNumber, List<String> number) {
+        QFilter filter = new QFilter("number", QCP.in, number)
+                .and(QFilterCommonHelper.getCurrentVersionFilter())
+                .and(QFilterCommonHelper.getStatusFilter())
+                .and(QFilterCommonHelper.getEnableFilter());
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(FormConstant.NUMBER_KEY)
+                .add(FormConstant.MODIFY_TIME_KEY);
+        DynamicObjectCollection query = QueryServiceHelper.query(entityNumber, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+
+        // 按number分组,取每组的第一条(即最新的记录)
+        return query.stream()
+                .collect(Collectors.groupingBy(
+                        obj -> obj.getString(FormConstant.NUMBER_KEY)
+                ))
+                .entrySet()
+                .stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        entry -> entry.getValue().get(0).getLong(FormConstant.ID_KEY) // 取每组第一条记录的ID
+                ));
+    }
+
+    /**
+     * 根据编码查询id
+     * @param entityNumber
+     * @param number
+     */
+    public static Long getIdByNumber(String entityNumber,String number) {
+        Map<String, Long> map = getIdByNumber(entityNumber, Lists.newArrayList(number));
+        return !map.isEmpty() ? map.get(number) : null;
+    }
+
+
+    /**
+     * 根据编码查询id
+     * @param entityNumber
+     * @param number
+     * @return
+     */
+    public static Map<String,Long> getIdByNumber(String entityNumber, List<String> number) {
+        QFilter filter = new QFilter("number", QCP.in, number)
+                .and(QFilterCommonHelper.getStatusFilter())
+                .and(QFilterCommonHelper.getEnableFilter());
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create().add(FormConstant.ID_KEY).add(FormConstant.NUMBER_KEY);
+        DynamicObjectCollection query = QueryServiceHelper.query(entityNumber, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+        return query.stream()
+                .collect(Collectors.toMap(
+                        obj -> obj.getString(FormConstant.NUMBER_KEY),  // key: number
+                        obj -> obj.getLong(FormConstant.ID_KEY)         // value: id
+                ));
+
+    }
+
+
 }

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

@@ -102,7 +102,42 @@ public class EmpPosOrgRelHelper {
     }
 
     /**
-     * 根据多个员工ID查询最新的任职经历信息
+     * 根据多个员工ID查询最新的任职经历信息(含不在岗)
+     *
+     * @param employeeIds 员工ID集合
+     * @return 员工的任职经历信息数组
+     * @author W.Y.C
+     * @date: 2025/11/04 16:02
+     */
+    public static Map<Long, DynamicObject> queryEmpPosOrgRelWithOutPostByEmployeesMap(Collection<Long> employeeIds) {
+        DynamicObject[] dynamicObjects = queryEmpPosOrgRelByEmployeesWithOutPost(employeeIds, null);
+        // 按员工ID分组,并取每个员工的最新记录(按开始日期)
+        Map<Long, List<DynamicObject>> grouped = new HashMap<>();
+        for (DynamicObject obj : dynamicObjects) {
+            Long empId = obj.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+            grouped.computeIfAbsent(empId, k -> new ArrayList<>()).add(obj);
+        }
+
+        // 从每组中选取开始日期最新的记录
+        Map<Long, DynamicObject> result = new HashMap<>();
+        for (Map.Entry<Long, List<DynamicObject>> entry : grouped.entrySet()) {
+            List<DynamicObject> records = entry.getValue();
+            DynamicObject latestRecord = records.stream()
+                    .max((o1, o2) -> {
+                        Date date1 = o1.getDate(FormConstant.STARTDATE);
+                        Date date2 = o2.getDate(FormConstant.STARTDATE);
+                        return date1.compareTo(date2);
+                    })
+                    .orElse(null);
+            result.put(entry.getKey(), latestRecord);
+        }
+
+        return result;
+
+    }
+
+    /**
+     * 根据多个员工ID查询最新的任职经历信息(含不在岗)
      *
      * @param employeeIds 员工ID集合
      * @return 员工的任职经历信息数组
@@ -135,6 +170,7 @@ public class EmpPosOrgRelHelper {
         return result;
 
     }
+
     /**
      * 根据多个员工ID查询最新的任职经历信息(含历史)
      *
@@ -222,6 +258,44 @@ public class EmpPosOrgRelHelper {
         return BusinessDataServiceHelper.load(FormConstant.HRPI_EMPPOSORGREL, selectProperties, new QFilter[]{queryFilter}, order);
     }
 
+    /**
+     * 获取员工最新有效任职经历(含不在岗)
+     * 查询条件包括:
+     * 1. 员工ID在指定范围内
+     * 2. 是最新记录(IS_SEQLATESTRECORD=YES)
+     * 3. 未删除(IS_DELETED=NO)
+     * 4. 是主任职(IS_PRIMARY=YES)
+     *
+     * @param employeeIds 员工ID集合
+     * @param otherFilter 其他查询条件(可选)
+     * @return 员工的任职经历信息数组
+     * @author W.Y.C
+     * @date: 2025/11/04 16:02
+     */
+    public static DynamicObject[] queryEmpPosOrgRelByEmployeesWithOutPost(Collection<Long> employeeIds, QFilter otherFilter) {
+        // 构建基础查询条件
+
+        QFilter queryFilter = new QFilter(FormConstant.EMPLOYEE_KEY, QCP.in, employeeIds)
+                .and(FormConstant.IS_SEQLATESTRECORD, QCP.equals, EnableEnum.YES.getCode())
+                .and(FormConstant.IS_DELETED, QCP.equals, EnableEnum.NO.getCode())
+                .and(FormConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode())
+                .and(FormConstant.STARTDATE, QCP.less_equals, new Date())
+                .and(FormConstant.ENDDATE, QCP.large_equals, new Date())
+                .and(FormConstant.ISCURRENTDATA, QCP.equals, EnableEnum.YES.getCode());
+
+        // 添加额外查询条件(如果有)
+        if (otherFilter != null) {
+            queryFilter.and(otherFilter);
+        }
+
+        // 构建查询字段和排序规则
+        String selectProperties = selectProperties().buildSelect();
+        String order = selectProperties().buildOrder();
+
+        // 执行查询并返回结果
+        return BusinessDataServiceHelper.load(FormConstant.HRPI_EMPPOSORGREL, selectProperties, new QFilter[]{queryFilter}, order);
+    }
+
     /**
      * 获取员工有效任职经历(含历史)
      * 查询条件包括:

+ 96 - 1
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/OrgHelper.java

@@ -1,11 +1,18 @@
 package nckd.jxccl.base.org.helper;
 
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.entity.helper.EntityHelper;
 
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Org帮助类
@@ -16,6 +23,13 @@ import java.util.List;
  */
 public class OrgHelper {
 
+    static QFilter isNotCurrent = new QFilter("iscurrentversion", "=", '0');
+    static QFilter isCurrent = new QFilter("iscurrentversion", "=", "1");
+    static QFilter dataStatusFilter = new QFilter("datastatus", "in", Arrays.asList("1", "2"));
+    static QFilter dataStatusCurrent = new QFilter("datastatus", "in", Arrays.asList("-3", "0"));
+    static QFilter enableFilter = new QFilter("enable", "=", "1");
+    static QFilter initStatusFilter = new QFilter("initstatus", "=", "2");
+
     /**
      * 获取创建组织
      * @param entity 实体id
@@ -60,5 +74,86 @@ public class OrgHelper {
     }
 
 
+    /**
+     * 获取组织结构编码Map
+     * key = structnumber, value = Map包含number和name
+     * @param structNumberSet
+     * @param modifyTime
+     * @return
+     */
+    public static Map<String, Map<String, String>> getStructNumberMap(Set<String> structNumberSet, Date modifyTime) {
+        HRBaseServiceHelper adminOrg = new HRBaseServiceHelper("haos_adminorgdetail");
+        QFilter structNumberFilter = new QFilter("structnumber", "in", structNumberSet);
+        if (Objects.isNull(modifyTime)) {
+            structNumberFilter.and(isCurrent);
+        } else {
+            QFilter effDateFilter = (new QFilter("bsed", "<=", modifyTime)).and(new QFilter("bsled", ">=", modifyTime));
+            structNumberFilter.and(isNotCurrent).and(effDateFilter);
+        }
+
+        DynamicObject[] adminOrgNumber = adminOrg.query("id,structnumber,name,number", new QFilter[]{structNumberFilter, dataStatusFilter});
+        Stream<DynamicObject> dynamicObjectStream = Arrays.stream(adminOrgNumber).filter(distinctByKey1((s) -> {
+            return s.getString("structnumber");
+        }));
+        Map<String, Map<String, String>> stringStringMap = (Map)dynamicObjectStream.collect(Collectors.toMap((dy) -> {
+            return (String)Optional.ofNullable(dy.getString("structnumber")).orElse("");
+        }, (dy) -> {
+            Map map = new HashMap<>();
+            map.put("number",(String)Optional.ofNullable(dy.getString("number")).orElse(""));
+            map.put("name",(String)Optional.ofNullable(dy.getString("name")).orElse(""));
+            return map;
+        }));
+        structNumberSet.removeAll(stringStringMap.keySet());
+        if (structNumberSet.size() == 0) {
+            return stringStringMap;
+        } else {
+            structNumberFilter = new QFilter("structnumber", "in", structNumberSet);
+            DynamicObject[] adminOrgNumberNot = adminOrg.query("id,structnumber,name,number", new QFilter[]{structNumberFilter, isCurrent, dataStatusCurrent, enableFilter});
+            dynamicObjectStream = Arrays.stream(adminOrgNumberNot).filter(distinctByKey1((s) -> {
+                return s.getString("structnumber");
+            }));
+            Map<String, Map<String, String>> stringStringMapNot = (Map)dynamicObjectStream.collect(Collectors.toMap((dy) -> {
+                return (String)Optional.ofNullable(dy.getString("structnumber")).orElse("");
+            }, (dy) -> {
+                Map map = new HashMap<>();
+                map.put("number",(String)Optional.ofNullable(dy.getString("number")).orElse(""));
+                map.put("name",(String)Optional.ofNullable(dy.getString("name")).orElse(""));
+                return map;
+            }));
+            stringStringMap.putAll(stringStringMapNot);
+            return stringStringMap;
+        }
+    }
+
+
+    /**
+     * 根据组织的structlongnumber处理组织的长名称
+     * @param org
+     * @param structMap
+     */
+    public static void dealLongName(DynamicObject org, Map<String, Map<String, String>> structMap) {
+        String structLongNumber = org.getString("structlongnumber");
+        String[] structNumbers =  structLongNumber.split("!");
+        String[] longNames = new String[structNumbers.length];
+        String[] longNumbers = new String[structNumbers.length];
+        for(int i = 0; i<structNumbers.length; i++) {
+            if(structMap.containsKey(structNumbers[i])) {
+                longNames[i] = structMap.get(structNumbers[i]).get("name");
+                longNumbers[i] = structMap.get(structNumbers[i]).get("number");
+            }
+        }
+        org.set("nckd_orglongname", String.join("_", longNames));
+    }
+
+
+    static <T> Predicate<T> distinctByKey1(Function<? super T, ?> keyExtractor) {
+        Map<Object, Boolean> seen = new ConcurrentHashMap();
+        return (t) -> {
+            return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
+        };
+    }
+
+
+
 
 }

+ 132 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/sit/helper/SITHelper.java

@@ -0,0 +1,132 @@
+package nckd.jxccl.base.sit.helper;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class SITHelper {
+
+    /*党政职务*/
+    public static final HRBaseServiceHelper PARTYPOSH_HELPER = new HRBaseServiceHelper("nckd_hrpi_partyposh");
+    /*雇佣信息*/
+    public static final HRBaseServiceHelper EMPENTREL_HELPER = new HRBaseServiceHelper("hsbs_empentrel");
+    /*服务年限*/
+    public static final HRBaseServiceHelper PERSERLEN_HELPER = new HRBaseServiceHelper("hsbs_perserlen");
+    /*职称信息*/
+    public static final HRBaseServiceHelper PERPROTITLE_HELPER = new HRBaseServiceHelper("hsbs_perprotitle");
+    /**
+     * 根据员工ID获取入职日期
+     * 员工档案-雇佣信息-入职日期
+     *
+     * @param employeeIds
+     * @return
+     */
+    public Map<Long, Date> getJoinDateByEmployeeID(List<Long> employeeIds) {
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("startdate", QCP.less_equals, new Date());
+        filter.and("enddate", QCP.large_equals, new Date());
+        filter.and("empstagelatestrecord", QCP.equals, "1");
+        String selectFields = "employee.id, entrydate";
+        DynamicObjectCollection cols = EMPENTREL_HELPER.queryOriginalCollection(selectFields, filter.toArray());
+        return (Map<Long, Date>) cols.stream().filter(obj -> obj.getDate("entrydate") != null).collect(Collectors.toMap((obj) -> {
+            return obj.getLong("employee.id");
+        }, (obj) -> {
+            return obj.getDate("entrydate");
+        }, (k1, k2) -> {
+            return k1;
+        }));
+    }
+
+    /**
+     * 根据员工ID获取离职日期
+     * 员工档案-服务年限-离职日期
+     *
+     * @param employeeIds
+     * @return
+     */
+    public Map<Long, Date> getQuitDateByEmployeeID(List<Long> employeeIds) {
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("iscurrentdata", QCP.equals, "1");
+        String selectFields = "employee.id, nckd_departdate";
+        DynamicObjectCollection cols = PERSERLEN_HELPER.queryOriginalCollection(selectFields, filter.toArray());
+        return (Map<Long, Date>) cols.stream().filter(obj -> obj.getDate("nckd_departdate") != null).collect(Collectors.toMap((obj) -> {
+            return obj.getLong("employee.id");
+        }, (obj) -> {
+            return obj.getDate("nckd_departdate");
+        }, (k1, k2) -> {
+            return k1;
+        }));
+    }
+
+    /**
+     * 根据员工ID获取职务层级
+     * 员工档案-党政职务变更履历-职务层级
+     * @param employeeIds
+     * @return Map, k = employeeId, v = 职位层级名称
+     */
+    public Map<Long, String> getZwcjByEmployeeID(List<Long> employeeIds) {
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("startdate", QCP.less_equals, new Date());
+        filter.and("enddate", QCP.large_equals, new Date());
+        String selectFields = "employee.id, nckd_posgrade.name";
+        DynamicObjectCollection cols = PARTYPOSH_HELPER.queryOriginalCollection(selectFields, filter.toArray());
+        return (Map<Long, String>) cols.stream().collect(Collectors.toMap((obj) -> {
+            return obj.getLong("employee.id");
+        }, (obj) -> {
+            return obj.getString("nckd_posgrade.name");
+        }, (k1, k2) -> {
+            return k1;
+        }));
+    }
+
+    /**
+     * 根据员工ID获取职称级别ID
+     * 员工档案-职称信息-职称级别
+     *
+     * @param employeeIds
+     * @return
+     */
+    public Map<Long, Long> getProtitleLevelIdByEmployeeID(List<Long> employeeIds) {
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("iscompany", QCP.equals, "1");
+        String selectFields = "employee.id, prolevel.id";
+        DynamicObjectCollection cols = PERPROTITLE_HELPER.queryOriginalCollection(selectFields, filter.toArray());
+        return (Map<Long, Long>) cols.stream().collect(Collectors.toMap((obj) -> {
+            return obj.getLong("employee.id");
+        }, (obj) -> {
+            return obj.getLong("prolevel.id");
+        }, (k1, k2) -> {
+            return k1;
+        }));
+    }
+
+    /**
+     * 根据员工ID获取职称级别
+     * 员工档案-职称信息-职称级别
+     *
+     * @param employeeIds
+     * @return
+     */
+    public Map<Long, DynamicObject> getProtitleLevelDynByEmployeeID(List<Long> employeeIds) {
+        QFilter filter = new QFilter("employee.id", QCP.in, employeeIds);
+        filter.and("iscompany", QCP.equals, "1");
+        String selectFields = "employee.id, prolevel.name";
+        DynamicObject[] cols = PERPROTITLE_HELPER.load(selectFields, filter.toArray());
+        return (Map<Long, DynamicObject>) Arrays.stream(cols).collect(Collectors.toMap((obj) -> {
+            return obj.getLong("employee.id");
+        }, (obj) -> {
+            return obj.getDynamicObject("prolevel");
+        }, (k1, k2) -> {
+            return k1;
+        }));
+    }
+
+}

+ 6 - 6
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/swc/helper/AdjFileServiceHelper.java

@@ -5,7 +5,6 @@ import kd.bos.entity.validate.BillStatus;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 import kd.bos.servicehelper.DispatchServiceHelper;
-import kd.sdk.swc.hcdm.business.helper.HCDMAdjFileServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.entity.helper.EntityHelper;
@@ -33,12 +32,13 @@ public class AdjFileServiceHelper {
     /**
      * 获取员工某个薪酬项目最新定调薪记录
      * @param personIds 人员id
-     * @param standardItemId 定调薪项目id
+     * @param standardItemNumber 定调薪项目编码
      * @return: java.util.List<nckd.jxccl.base.swc.helper.AdjFileServiceHelper.SalaryAdjustmentResult>
      * @author W.Y.C
      * @date: 2025/12/21 16:06
      */
-    public static List<SalaryAdjustmentResult> getLastDecAdjRecords(List<Long> personIds,Long standardItemId) {
+    public static List<SalaryAdjustmentResult> getLastDecAdjRecords(List<Long> personIds,String standardItemNumber) {
+        Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, standardItemNumber);
         //获取员工最新任职
         Map<Long, DynamicObject> empPosOrgRelByEmployeesMap = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployeesMap(personIds);
         List<Long> allEmpPosOrgRelIds = empPosOrgRelByEmployeesMap.values().stream()
@@ -48,7 +48,7 @@ public class AdjFileServiceHelper {
         List<SalaryAdjustmentResult> salaryAdjustmentResultList = new ArrayList<>();
 
         DynamicObject standardItem = EntityHelper.newEntity(FormConstant.HSBS_STANDARDITEM);
-        standardItem.set(FormConstant.ID_KEY, standardItemId);
+        standardItem.set(FormConstant.ID_KEY, salaryItemId);
         Map<String, Object> adjFileParams = new HashMap<>();
         adjFileParams.put("employees", personIds);
         List<String> status = new ArrayList<>();
@@ -75,7 +75,7 @@ public class AdjFileServiceHelper {
                 // 调薪档案ID
                 dataItem.put("adjfile", adjFileId);
                 // 调薪项目ID
-                dataItem.put("standarditem", standardItemId);
+                dataItem.put("standarditem", salaryItemId);
                 // 查询基准日期
                 dataItem.put("startdate", new Date());
                 // 唯一标识
@@ -86,7 +86,7 @@ public class AdjFileServiceHelper {
             adjRecordParams.put("selprops", "salarystdv.rankentry.rank.id,salarystdv.rankentry.rank.name,salarystdv.rankentry.rank.number,salarystdv.rankentry.rank.index,salarystdv.rankentry.frankindex,company.id,company.name,hcdmorg.id,");
             //查询定调薪记录
             //hcdm_decadjrecor
-            Map<String, Object> lastDecAdjRecordMap = HCDMAdjFileServiceHelper.getLastDecAdjRecords(adjRecordParams);
+            Map<String, Object> lastDecAdjRecordMap = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "getLastDecAdjRecords", adjRecordParams);
             if (ConvertUtil.toBoolean(lastDecAdjRecordMap.get("success"))) {
                 List<Map> datas = ConvertUtil.toList(lastDecAdjRecordMap.get("data"), ArrayList::new);
                 for (Map data : datas) {

+ 1 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/swc/helper/SWCHelper.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.base.swc.helper;
 
+import com.google.common.collect.Lists;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.tree.TreeNode;

+ 18 - 18
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/excells/plugin/operate/PushAdjustOpPlugin.java

@@ -1,37 +1,30 @@
 package nckd.jxccl.hr.excells.plugin.operate;
 
 import com.alibaba.fastjson.JSON;
-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.db.tx.TX;
 import kd.bos.db.tx.TXHandle;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.MainEntityType;
-import kd.bos.entity.operate.result.IOperateInfo;
-import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
 import kd.bos.entity.plugin.PreparePropertysEventArgs;
-import kd.bos.entity.plugin.args.AfterOperationArgs;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
 import kd.bos.entity.validate.BillStatus;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
-import kd.bos.servicehelper.BusinessDataServiceHelper;
 import kd.bos.servicehelper.DispatchServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
-import kd.sdk.swc.hcdm.business.helper.HCDMApplyBillServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 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.StrFormatter;
+import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.base.hrpi.helper.EmpPosOrgRelHelper;
 import nckd.jxccl.hr.excells.common.ExcellsConstant;
-import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 
 import java.util.ArrayList;
@@ -39,7 +32,6 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 /**
@@ -65,6 +57,14 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
 
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, "jt007");
+        Long billTypeId = EntityHelper.getIdByNumber(FormConstant.BOS_BILLTYPE, FormConstant.HCDM_APPLYBILL_BT_ADJ);
+        Long countryId = EntityHelper.getIdByNumber(FormConstant.BD_COUNTRY, "001");
+        Long salaryAdjuStrSnId = EntityHelper.getIdByNumber(FormConstant.HSBS_SALARYADJUSTRSN, "youxiusheng");
+        Long currencyId = EntityHelper.getIdByNumber(FormConstant.BD_CURRENCY, "CNY");
+        Long adjApproveScmId = EntityHelper.getLatestIdByNumber(FormConstant.HCDM_ADJAPPROVESCM, "dingtiaoxin");
+        Long exRateTableId = EntityHelper.getIdByNumber(FormConstant.BD_EXRATETABLE, "ERT-01");
+        Long calFrequencyId = EntityHelper.getIdByNumber(FormConstant.HSBS_CALFREQUENCY, "000_m_001");
 
         List<Long> ids = new ArrayList<>();
         List<Long> personIds = new ArrayList<>();
@@ -127,19 +127,19 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
                 applyBill.put("_uniquecode", data.getLong(FormConstant.ID_KEY));
                 applyBill.put("org", orgId);
                 //定调薪明细字段显示方案   调薪明细字段
-                applyBill.put("billtype", 2215975998602655744L);
+                applyBill.put("billtype", billTypeId);
                 //国家
-                applyBill.put("billcountry", 1000001L);
+                applyBill.put("billcountry", countryId);
                 //定调薪类型(优秀生津贴)
-                applyBill.put("salaryadjrsn", 2352338051369287680L);
+                applyBill.put("salaryadjrsn", salaryAdjuStrSnId);
                 //默认币种
-                applyBill.put("billcurrency", 1L);
+                applyBill.put("billcurrency", currencyId);
                 //定调薪方案
-                applyBill.put("salaryadjscm", 2322515162646457344L);
+                applyBill.put("salaryadjscm", adjApproveScmId);
                 //汇率日期
                 applyBill.put("exchangeratedate", new Date());
                 //汇率表
-                applyBill.put("exctable", 2321965096026258432L);
+                applyBill.put("exctable", exRateTableId);
                 //默认生效日期
                 applyBill.put("effectivedate", data.getDate(FormConstant.CREATE_TIME_KEY));
                 //草稿状态
@@ -155,8 +155,8 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
                 Map<String, Object> applyBillEntry = new HashMap<>();
                 applyBillEntry.put("adjfile", adjFileId);
                 applyBillEntry.put("employee", employeeId);
-                applyBillEntry.put("standarditem", 2321901326927149056L);    //定调薪项目
-                applyBillEntry.put("frequency", 1095454108284088320L);       //频度  月
+                applyBillEntry.put("standarditem", salaryItemId);    //定调薪项目
+                applyBillEntry.put("frequency", calFrequencyId);       //频度  月
                 applyBillEntry.put("amount", entity.getBigDecimal(FormConstant.NCKD_MONEY));
                 applyBillEntry.put("position", position.getLong(FormConstant.ID_KEY));
                 applyBillEntry.put("reason", entity.getString(ExcellsConstant.NCKD_DESC));
@@ -180,7 +180,7 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
             List<DynamicObject> saveDynamicObjects = new ArrayList<>();
             TXHandle tx = TX.requiresNew();
             try {
-                Map<String, Object> result = HCDMApplyBillServiceHelper.saveDraftApplyBill(papams);
+                Map<String, Object> result = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "saveDraftApplyBill", papams);
                 logger.info("推送定调薪结果:{}", JSON.toJSONString(result));
 
                 if (!ConvertUtil.toBoolean(result.get("success")) || result.get("data") == null || ConvertUtil.toList(result.get("data")).isEmpty()) {

+ 81 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/haos/plugin/DealOrgLongNamePlugin.java

@@ -0,0 +1,81 @@
+package nckd.jxccl.hr.haos.plugin;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bec.api.IEventServicePlugin;
+import kd.bos.bec.model.KDBizEvent;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.sdk.hr.haos.business.helper.HAOSServiceHelper;
+import nckd.jxccl.base.org.helper.OrgHelper;
+
+import java.util.*;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+
+/**
+ * Tyx 2026-01-09
+ * 事件订阅调用插件
+ * 事件名:行政组织变动.生效
+ */
+public class DealOrgLongNamePlugin implements IEventServicePlugin {
+
+    private static final Log logger = LogFactory.getLog(DealOrgLongNamePlugin.class);
+    private static HRBaseServiceHelper ADMINORGDETAIL_HELPER = new HRBaseServiceHelper("haos_adminorgdetail");
+    
+    @Override
+    public Object handleEvent(KDBizEvent evt) {
+
+        // 获取事件参数,格式:
+        // [{"data":[{"beforeversion":2388628286830828544,"isbelongcompanychange":false,"boid":2309486703347249189,"changeoperate":1030,"changescene":1030,"afterversion":2388631384106227714}]}]
+        String paramStr = evt.getSource();
+        JSONArray jsonArray = JSONArray.parseArray(paramStr);
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject jsonObject = jsonArray.getJSONObject(i);
+            JSONArray dataArr = jsonObject.getJSONArray("data");
+            // TODO 不确定这里data会不会为空
+            JSONObject data = dataArr.getJSONObject(0);
+
+            // 获取变动场景id,目前只处理 组织新设-1010,调整上级-1020,变更信息-1030的情况:
+            Long sceneId = data.getLong("changescene");
+            if(sceneId.equals(1010L) || sceneId.equals(1020L) || sceneId.equals(1030L)) {
+                Long boId = data.getLong("boid");
+                updateOrgLongName(boId);
+            }
+        }
+        return IEventServicePlugin.super.handleEvent(evt);
+    }
+
+    /**
+     * 更新组织长名称
+     * @param boId 组织boId
+     */
+    public void updateOrgLongName (Long boId) {
+        DynamicObject org = ADMINORGDETAIL_HELPER.loadOne(boId);
+        String structLongNumber = org.getString("structlongnumber");
+        List<Map<String, Object>> subOrgList = HAOSServiceHelper.querySubOrgToList(Collections.singletonList(boId), new Date(), 6);
+
+        Set subOrgIdset = subOrgList.stream().map(i -> i.get("orgId")).collect(Collectors.toSet());
+        String selectProperties = "id,boid,number,name,structlongnumber,structnumber,nckd_orglongname";
+        QFilter idQFilter = new QFilter("id", "in", subOrgIdset);
+        DynamicObject[] subOrgDyns = ADMINORGDETAIL_HELPER.query(selectProperties, new QFilter[]{idQFilter});
+        Set structLongNumberSet = Arrays.stream(subOrgDyns).map(dyx -> dyx.get("structlongnumber").toString())
+                .flatMap(longNumber -> Arrays.stream(longNumber.split("!")))
+                .collect(Collectors.toSet());
+
+        Map<String, Map<String, String>> structMap = OrgHelper.getStructNumberMap(structLongNumberSet, null);
+        for (DynamicObject subOrg : subOrgDyns) {
+            OrgHelper.dealLongName(subOrg, structMap);
+        }
+        SaveServiceHelper.update(subOrgDyns);
+    }
+
+
+
+
+}

+ 12 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/business/transfer/TransferApplyBillServiceHelper.java

@@ -0,0 +1,12 @@
+package nckd.jxccl.hr.hdm.business.transfer;
+
+/**
+ * 调动单服务类
+ * @author: jtd
+ * @date: 2026/1/6 19:54
+ */
+public class TransferApplyBillServiceHelper {
+
+
+
+}

+ 151 - 1
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/common/transfer/TransferApplyBillConstant.java

@@ -10,15 +10,164 @@ import nckd.jxccl.base.common.constant.FormConstant;
 public class TransferApplyBillConstant extends FormConstant {
     /** 获取员工近亲属关系查询实体标识 */
     public static final String NCKD_HRPI_GETFAMILYRELATION_ENTITY = "nckd_hrpi_getfamilyrelation";
+    /** 劳动合同档案实体标识 */
+    public static final String HLCM_CONTRACTSOURCE_ENTITY = "hlcm_contractsource";
+    /** 劳动合同档案实体标识 */
+    public static final String HLCM_CONTRACT_ENTITY = "hlcm_contract";
+    /** 解除/终止合同原因实体标识 */
+    public static final String HLCM_CONTRACTREASON_ENTITY = "hlcm_contractreason";
 
+    /** 调动生效后解除合同OP标识 */
+    public static final String CANCLECONTRACT_AFTEREFFECT_OP = "canclecontract_aftereffect";
+
+    /** 调出公司(业务对象) */
+    public static final String BCOMPANYBO_KEY = "bcompanybo";
     /** 调出公司 */
     public static final String BCOMPANY_KEY = "bcompany";
     /** 调入公司 */
     public static final String ACOMPANY_KEY = "acompany";
-    /** 配偶、近亲属关系.员工 */
+    /** 原组织分配 */
+    public static final String BB_A_TID_KEY = "bb_a_tid";
+    /** 实际调动日期 */
+    public static final String B_EFFECTIVEDATE_KEY = "b_effectivedate";
+    /** 常驻工作地 */
+    public static final String BB_PO_WORKPLACE_KEY = "bb_po_workplace";
+    /** 国籍 */
+    public static final String NATIONALITY_KEY = "nationality";
+    /** 联系电话 */
+    public static final String EMPPHONE_KEY = "empphone";
+    /** 行政组织(业务对象) */
+    public static final String BB_PO_ADMINORGBO_KEY = "bb_po_adminorgbo";
+    /** 行政组织 */
+    public static final String BB_PO_ADMINORG_KEY = "bb_po_adminorg";
+    /** 岗位(业务对象) */
+    public static final String BB_PO_POSITIONBO_KEY = "bb_po_positionbo";
+    /** 职位 */
+    public static final String BB_PO_JOB_KEY = "bb_po_job";
+    /** 任职序号 */
+    public static final String BB_PO_ORGRELSEQ_KEY = "bb_po_orgrelseq";
+    /** 员工组 */
+    public static final String BB_A_EMPGROUP_KEY = "bb_a_empgroup";
+
+    /** 员工 */
     public static final String NCKD_EMPID_KEY = "nckd_empid";
+
+    /** 合同协议类型 */
+    public static final String PROTOCOLTYPE_KEY = "protocoltype";
+    /** 合同状态 */
+    public static final String CONTRACTSTATUS_KEY = "contractstatus";
+    /** 合同开始日期 */
+    public static final String STARTDATE_KEY = "startdate";
+    /** 计划结束日期 */
+    public static final String PLANENDDATE_KEY = "planenddate";
+    /** 应签单位 */
+    public static final String SUGGESTSIGNCOMPANY_KEY = "suggestsigncompany";
+    /** 实签单位 */
+    public static final String ACTUALSIGNCOMPANY_KEY = "actualsigncompany";
+    /** 证件类型 */
+    public static final String CARDTYPE_KEY = "cardtype";
+    /** 证件号码 */
+    public static final String CARDNUMBER_KEY = "cardnumber";
+    /** 户籍地址 */
+    public static final String HOUSEHOLDREGISTER_KEY = "householdregister";
+    /** 居住地址 */
+    public static final String RESIDENTIALADDRESS_KEY = "residentialaddress";
+    /** 实签单位历史 */
+    public static final String ACTUALSIGNCOMPANYHIS_KEY = "actualsigncompanyhis";
+    /** 合同期限类型 */
+    public static final String PERIODTYPE_KEY = "periodtype";
+    /** 合同期限单位 */
+    public static final String PERIODUNIT_KEY = "periodunit";
+    /** 合同期限 */
+    public static final String PERIOD_KEY = "period";
+    /** 签署方式 */
+    public static final String SIGNWAY_KEY = "signway";
+    /** 实际结束日期 */
+    public static final String ENDDATE_KEY = "enddate";
+    /** 签订日期 */
+    public static final String SIGNEDDATE_KEY = "signeddate";
+    /** 部门 */
+    public static final String ORGMODEL_KEY = "orgmodel";
+    /** 岗位 */
+    public static final String POSITIONMODEL_KEY = "positionmodel";
+    /** 拟调动日期 */
+    public static final String PLANDATE_KEY = "plandate";
+
+
+
     /** 原合同是否解除 */
     public static final String NCKD_ISTERMORIGCONT_KEY = "nckd_istermorigcont";
+    /** 任职类型(变动前) */
+    public static final String BB_PO_POSTYPE_KEY = "bb_po_postype";
+    /** 姓名 */
+    public static final String EMPNAME_KEY = "empname";
+    /** 公司历史 */
+    public static final String SIGNEDCOMPANYHIS_KEY = "signedcompanyhis";
+    /** 行政组织历史F7 */
+    public static final String DEPARTMENTHIS_KEY = "departmenthis";
+    /** 岗位历史 */
+    public static final String POSTIONHIS_KEY = "postionhis";
+    /** 国籍 */
+    public static final String NATION_KEY = "nation";
+    /** 签署业务类型 */
+    public static final String BUSINESSTYPE_KEY = "businesstype";
+    /** 常驻工作地 */
+    public static final String BASELOCATION_KEY = "baselocation";
+    /** 合同解除日期 */
+    public static final String CANCELDATE_KEY = "canceldate";
+    /** 合同编号 */
+    public static final String OLDCONTRACT_KEY = "oldcontract";
+    /** 业务处理状态 */
+    public static final String HANDLESTATUS_KEY = "handlestatus";
+    /** 实际签订日期 */
+    public static final String ACTUALSIGNDATE_KEY = "actualsigndate";
+    /** 职位 */
+    public static final String JOB_KEY = "job";
+    /** 公司 */
+    public static final String CURCOMPANY_KEY = "curcompany";
+    /** 部门 */
+    public static final String CURDEPT_KEY = "curdept";
+    /** 岗位 */
+    public static final String CURPOSTION_KEY = "curpostion";
+    /** 职位 */
+    public static final String CURJOB_KEY = "curjob";
+    /** 任职序号 */
+    public static final String ORGRELSEQ_KEY = "orgrelseq";
+    /** 调令号 */
+    public static final String NCKD_TRANSFERNO_KEY = "nckd_transferno";
+    /** 调档函地址 */
+    public static final String NCKD_TRANSFERADDR_KEY = "nckd_transferaddr";
+    /** 调档函邮编 */
+    public static final String NCKD_TRANSFERZIP_KEY = "nckd_transferzip";
+    /** 调档函联系人 */
+    public static final String NCKD_TRANSFERCONT_KEY = "nckd_transfercont";
+    /** 调档函电话 */
+    public static final String NCKD_TRANSFERTEL_KEY = "nckd_transfertel";
+    /** 是否生成调令顺序号 */
+    public static final String NCKD_GEN_SERIALNO_KEY = "nckd_gen_serialno";
+    /** 调令顺序号 */
+    public static final String NCKD_TRANSFER_SERIALNO_KEY = "nckd_transfer_serialno";
+
+    /** 续签次数(续签用) */
+    public static final String RENEWCOUNT_KEY = "renewcount";
+    /** 人事管理组织 */
+    public static final String ERMANORG_KEY = "ermanorg";
+    /** 员工管理部门 */
+    public static final String ADMINORORG_KEY = "adminororg";
+    /** 员工组 */
+    public static final String ERMANPERORG_KEY = "ermanperorg";
+    /** 档案类型 */
+    public static final String CONTRACTCATEGORY_KEY = "contractcategory";
+    /** 解除原因 */
+    public static final String CANCELREASONTYPE_KEY = "cancelreasontype";
+
+    /** 常驻工作地 */
+    public static final String LOCALTION_KEY = "location";
+    /** 员工组 */
+    public static final String PERSONGROUP_KEY = "persongroup";
+    /** 员工管理部门 */
+    public static final String AFFILIATEADMINORG_KEY = "affiliateadminorg";
+
 
     /** 员工.员工编码 */
     public static final String EMPLOYEE_EMPNUMBER = "employee.empnumber";
@@ -37,4 +186,5 @@ public class TransferApplyBillConstant extends FormConstant {
 
     /** 近亲属关系确认回调 */
     public static final String FAMILY_RELATION_CONFIRM_CALLBACK = "familyRelationConfirm";
+
 }

+ 130 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/plugin/form/transfer/TransferApplyBillFormPlugin.java

@@ -1,26 +1,40 @@
 package nckd.jxccl.hr.hdm.plugin.form.transfer;
 
+
+import kd.bos.bill.OperationStatus;
+import kd.bos.coderule.api.CodeRuleInfo;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.QueryEntityType;
+import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.form.ConfirmCallBackListener;
 import kd.bos.form.ConfirmTypes;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.MessageBoxResult;
+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.servicehelper.MetadataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
 import kd.hr.hbp.common.util.HRDynamicObjectUtils;
+import kd.hr.hbp.common.util.HRObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
+import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hr.hdm.common.transfer.TransferApplyBillConstant;
 
 import java.util.ArrayList;
+import java.util.EventObject;
 import java.util.List;
 
 /**
@@ -30,6 +44,57 @@ import java.util.List;
  * @date: 2026/1/4 15:34
  */
 public class TransferApplyBillFormPlugin extends AbstractFormPlugin {
+    Log log = LogFactory.getLog(TransferApplyBillFormPlugin.class);
+
+    private static final HRBaseServiceHelper transferApplyHelper = HRBaseServiceHelper.create(TransferApplyBillConstant.HDM_TRANSFERAPPLY);
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        DynamicObject dataEntity = getModel().getDataEntity();
+        String billStatus = dataEntity.getString(TransferApplyBillConstant.BILL_STATUS_KEY);
+        // 如果不是已提交或审批中
+        if (!HRStringUtils.equalsAny(billStatus, RegBillStatusEnum.ALREADYSUBMIT.getCode(), RegBillStatusEnum.APPROVING.getCode())) {
+            return;
+        }
+
+        // 获取当前登录用户
+        long currentUserId = UserServiceHelper.getCurrentUserId();
+        String selectFields = QueryFieldBuilder.create()
+                .add(TransferApplyBillConstant.ID_KEY)
+                .add(TransferApplyBillConstant.NCKD_TRANSFERADDR_KEY)
+                .add(TransferApplyBillConstant.NCKD_TRANSFERZIP_KEY)
+                .add(TransferApplyBillConstant.NCKD_TRANSFERCONT_KEY)
+                .add(TransferApplyBillConstant.NCKD_TRANSFERTEL_KEY)
+                .buildSelect();
+        // 查询最近一笔调动单修改人是该用户的数据
+        DynamicObject transferApplyDy = transferApplyHelper.queryOriginalOne(selectFields, new QFilter[]{new QFilter(TransferApplyBillConstant.BILL_STATUS_KEY, QCP.equals, RegBillStatusEnum.APPROVEPASSED.getCode()).and(TransferApplyBillConstant.MODIFIER_KEY, QCP.equals, currentUserId)}, String.format("%s desc", TransferApplyBillConstant.MODIFY_TIME_KEY));
+        if (HRObjectUtils.isEmpty(transferApplyDy)) {
+            return;
+        }
+
+        getModel().setValue(TransferApplyBillConstant.NCKD_TRANSFERADDR_KEY, transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERADDR_KEY));
+        getModel().setValue(TransferApplyBillConstant.NCKD_TRANSFERZIP_KEY, transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERZIP_KEY));
+        getModel().setValue(TransferApplyBillConstant.NCKD_TRANSFERCONT_KEY, transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERCONT_KEY));
+        getModel().setValue(TransferApplyBillConstant.NCKD_TRANSFERTEL_KEY, transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERTEL_KEY));
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+
+        // 获取当前登录用户
+        long currentUserId = UserServiceHelper.getCurrentUserId();
+        // 查询最近一笔调动单创建人是该用户的数据
+        DynamicObject transferApplyDy = transferApplyHelper.queryOriginalOne(String.join(",", TransferApplyBillConstant.ID_KEY, TransferApplyBillConstant.NCKD_TRANSFERNO_KEY), new QFilter[]{new QFilter(TransferApplyBillConstant.BILL_STATUS_KEY, QCP.equals, RegBillStatusEnum.APPROVEPASSED.getCode()).and(TransferApplyBillConstant.CREATOR_KEY, QCP.equals, currentUserId)}, String.format("%s desc", TransferApplyBillConstant.MODIFY_TIME_KEY));
+        if (HRObjectUtils.isEmpty(transferApplyDy)) {
+            return;
+        }
+
+        // 设置调令号
+        getModel().setValue(TransferApplyBillConstant.NCKD_TRANSFERNO_KEY, transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERNO_KEY));
+    }
 
     @Override
     public void beforeDoOperation(BeforeDoOperationEventArgs args) {
@@ -42,7 +107,72 @@ public class TransferApplyBillFormPlugin extends AbstractFormPlugin {
                 if (!HRStringUtils.equals(Boolean.TRUE.toString(), getView().getPageCache().get(TransferApplyBillConstant.FAMILY_RELATION_CONFIRM_PAGE_CACHE))) {
                     validateFamilyRelation(args);
                 }
+
+                List<Boolean> booleanList = new ArrayList<>();
+                if (OperationStatus.ADDNEW.equals(getView().getFormShowParameter().getStatus())) {
+                    // 如果是新增
+                    booleanList.add(true);
+                } else {
+                    // 判断变更的属性是否影响调令顺序号生成
+                    DynamicObject transferApplyDy = transferApplyHelper.loadOne(String.join(",", TransferApplyBillConstant.PLANDATE_KEY, TransferApplyBillConstant.NCKD_TRANSFERNO_KEY), getModel().getValue(TransferApplyBillConstant.ID_KEY));
+                    boolean isChange;
+                    Object oldPlanDate = transferApplyDy.get(TransferApplyBillConstant.PLANDATE_KEY);
+                    Object newPlanDate = getModel().getValue(TransferApplyBillConstant.PLANDATE_KEY);
+                    isChange = compareValues(oldPlanDate, newPlanDate);
+                    booleanList.add(isChange);
+
+                    Object oldTransferNo = transferApplyDy.get(TransferApplyBillConstant.NCKD_TRANSFERNO_KEY);
+                    Object newTransferNo = getModel().getValue(TransferApplyBillConstant.NCKD_TRANSFERNO_KEY);
+                    isChange = isChange || compareValues(oldTransferNo, newTransferNo);
+                    booleanList.add(isChange);
+                }
+
+                if (booleanList.stream().allMatch(Boolean::booleanValue)) {
+                    getModel().setValue(TransferApplyBillConstant.NCKD_GEN_SERIALNO_KEY, true);
+                } else {
+                    getModel().setValue(TransferApplyBillConstant.NCKD_GEN_SERIALNO_KEY, false);
+                }
+        }
+    }
+
+    private Boolean compareValues(Object newValue, Object oldValue) {
+        if (HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue)) {
+            return false;
+        } else if ((HRObjectUtils.isEmpty(oldValue) && !HRObjectUtils.isEmpty(newValue)) || (!HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue))) {
+            return true;
+        } else {
+            return !HRDynamicObjectUtils.compareValues(oldValue, newValue);
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        switch (operateKey){
+            case TransferApplyBillConstant.SAVE_OP:
+                OperationResult operationResult = afterDoOperationEventArgs.getOperationResult();
+                if (!operationResult.isSuccess() || operationResult.getSuccessPkIds().isEmpty() || !getModel().getDataEntity().getBoolean(TransferApplyBillConstant.NCKD_GEN_SERIALNO_KEY)) {
+                    break;
+                }
+
+                generateTransferSerialNo();
+        }
+    }
+
+    /**
+     * 生成调令顺序号
+     */
+    private void generateTransferSerialNo() {
+        DynamicObject dataEntity = getModel().getDataEntity();
+        CodeRuleInfo codeRuleInfo = CodeRuleServiceHelper.getCodeRule(dataEntity.getDataEntityType().getName(), dataEntity, dataEntity.getString(String.join(".", TransferApplyBillConstant.ORG_KEY, TransferApplyBillConstant.ID_KEY)));
+        if (HRObjectUtils.isEmpty(codeRuleInfo) || !HRStringUtils.equals("调令号生成规则", codeRuleInfo.getName())) {
+            dataEntity.set(TransferApplyBillConstant.NCKD_TRANSFER_SERIALNO_KEY, null);
+        } else {
+            dataEntity.set(TransferApplyBillConstant.NCKD_TRANSFER_SERIALNO_KEY, CodeRuleServiceHelper.getNumber(codeRuleInfo, dataEntity));
         }
+        SaveServiceHelper.save(new DynamicObject[]{dataEntity});
     }
 
     private void validateFamilyRelation(BeforeDoOperationEventArgs args) {

+ 317 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hdm/plugin/operate/transfer/CancelContractAfterEffectOpPlugin.java

@@ -0,0 +1,317 @@
+package nckd.jxccl.hr.hdm.plugin.operate.transfer;
+
+import com.google.common.collect.Lists;
+import kd.bos.dataentity.entity.DynamicObject;
+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.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.exception.BosErrorCode;
+import kd.bos.exception.ErrorCode;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+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.orm.util.CollectionUtils;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRMServiceHelper;
+import kd.hr.hbp.common.api.HrApiResponse;
+import kd.hr.hbp.common.util.HRObjectUtils;
+import kd.hr.hbp.common.util.HRStringUtils;
+import kd.hr.hlcm.common.enums.ContractCategoryEnum;
+import kd.hr.hlcm.common.enums.PostypeEnum;
+import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
+import kd.sdk.hr.hlcm.common.enums.BusinessTypeEnum;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.hr.hdm.common.transfer.TransferApplyBillConstant;
+import nckd.jxccl.hr.homs.common.application.event.GECEventConstant;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 调动生效后解除合同操作插件
+ * @entity: nckd_hdm_transferbase_ext
+ * @operate: canclecontract_aftereffect
+ * @author: jtd
+ * @date: 2026/1/6 11:38
+ */
+public class CancelContractAfterEffectOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log log = LogFactory.getLog(CancelContractAfterEffectOpPlugin.class);
+    private static final HRBaseServiceHelper contractSourceHelper = HRBaseServiceHelper.create(TransferApplyBillConstant.HLCM_CONTRACTSOURCE_ENTITY);
+    private static final HRBaseServiceHelper contractHelper = HRBaseServiceHelper.create(TransferApplyBillConstant.HLCM_CONTRACT_ENTITY);
+    private static final HRBaseServiceHelper contractApplyCancelHelper = HRBaseServiceHelper.create(TransferApplyBillConstant.HLCM_CONTRACTAPPLYCANCEL_ENTITY);
+    private static final HRBaseServiceHelper contractReasonHelper = HRBaseServiceHelper.create(TransferApplyBillConstant.HLCM_CONTRACTREASON_ENTITY);
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().addAll(billEntityType.getAllFields().keySet());
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+
+        DynamicObject[] dataEntites = e.getDataEntities();
+        List<DynamicObject> transferData = Arrays.stream(dataEntites).filter(data -> data.getBoolean(TransferApplyBillConstant.NCKD_ISTERMORIGCONT_KEY)).collect(Collectors.toList());
+        List<DynamicObject> cancelContractDyoList = new ArrayList<DynamicObject>();
+        for (DynamicObject data : transferData) {
+            cancelContractDyoList.add(buildContract(data));
+        }
+
+        OperationResult operationResult = SaveServiceHelper.saveOperate(TransferApplyBillConstant.HLCM_CONTRACTAPPLYCANCEL_ENTITY, cancelContractDyoList.toArray(new DynamicObject[0]));
+        if (!operationResult.isSuccess()) {
+            List<String> errorMsgList = new ArrayList<String>();
+            for (IOperateInfo operateInfo : operationResult.getAllErrorOrValidateInfo()) {
+                errorMsgList.add(operateInfo.getMessage());
+            }
+
+            if(StringUtils.isBlank(operationResult.getMessage())){
+                errorMsgList.add(operationResult.getMessage());
+            }
+            throw new KDBizException(new ErrorCode("CancelContractAfterEffect.SaveContract.Error", String.join(System.lineSeparator(), errorMsgList)));
+        }
+    }
+
+    private  DynamicObject buildContract(DynamicObject data) {
+        DynamicObject cancelContract = getCancelContractByErManFile(data);
+
+        DynamicObject contractApplyCancelDyo = contractApplyCancelHelper.generateEmptyDynamicObject();
+        // 设置 姓名
+        contractApplyCancelDyo.set(TransferApplyBillConstant.EMPNAME_KEY, data.get(String.join(".", TransferApplyBillConstant.BB_EM_TID, TransferApplyBillConstant.NAME_KEY)));
+        // 设置 人事管理组织
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ORG_KEY, data.get(TransferApplyBillConstant.ORG_KEY));
+        // 设置 工号
+        contractApplyCancelDyo.set(TransferApplyBillConstant.EMP_NUMBER_KEY, data.get(String.join(".", TransferApplyBillConstant.BB_EM_TID, TransferApplyBillConstant.EMP_NUMBER_KEY)));
+        // 设置 证件类型
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CARDTYPE_KEY, cancelContract.get(TransferApplyBillConstant.CARDTYPE_KEY));
+        // 设置 证件号码
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CARDNUMBER_KEY, cancelContract.get(TransferApplyBillConstant.CARDNUMBER_KEY));
+        // 设置 单据编号
+        /*CodeRuleInfo codeRuleInfo = CodeRuleServiceHelper.getCodeRule(contractApplyCancelDyo.getDataEntityType().getName(), contractApplyCancelDyo, data.getString(String.format("%s_id", TransferApplyBillConstant.ORG_KEY)));
+        String number = CodeRuleServiceHelper.readNumber(codeRuleInfo, contractApplyCancelDyo);
+        contractApplyCancelDyo.set(TransferApplyBillConstant.NUMBER_KEY, number);*/
+        // 设置 审批状态
+        contractApplyCancelDyo.set(TransferApplyBillConstant.AUDIT_STATUS, RegBillStatusEnum.TEMPSTORAGE.getCode());// 暂存
+        // 设置 公司历史
+        contractApplyCancelDyo.set(TransferApplyBillConstant.SIGNEDCOMPANYHIS_KEY, data.get(TransferApplyBillConstant.BCOMPANY_KEY));
+        // 设置 行政组织历史F7
+        contractApplyCancelDyo.set(TransferApplyBillConstant.DEPARTMENTHIS_KEY, data.get(TransferApplyBillConstant.BB_PO_ADMINORG_KEY));
+        // 设置 岗位历史
+        contractApplyCancelDyo.set(TransferApplyBillConstant.POSTIONHIS_KEY, data.get(TransferApplyBillConstant.BB_PO_POSITION));
+        // 设置 国籍
+        contractApplyCancelDyo.set(TransferApplyBillConstant.NATION_KEY, data.get(String.join(".", TransferApplyBillConstant.BB_EM_TID, TransferApplyBillConstant.NATIONALITY_KEY)));
+        // 设置 常驻工作地
+        contractApplyCancelDyo.set(TransferApplyBillConstant.BASELOCATION_KEY, data.get(TransferApplyBillConstant.BB_PO_WORKPLACE_KEY));
+        // 设置 联系方式
+        contractApplyCancelDyo.set(TransferApplyBillConstant.EMPPHONE_KEY, cancelContract.getString(TransferApplyBillConstant.EMPPHONE_KEY));
+        // 设置 合同协议类型
+        contractApplyCancelDyo.set(TransferApplyBillConstant.PROTOCOLTYPE_KEY, "1");// 劳动合同
+        // 设置 签署业务类型
+        contractApplyCancelDyo.set(TransferApplyBillConstant.BUSINESSTYPE_KEY, BusinessTypeEnum.CANCEL.getCombKey());
+        // 设置 签署方式
+        contractApplyCancelDyo.set(TransferApplyBillConstant.SIGNWAY_KEY, cancelContract.get(TransferApplyBillConstant.SIGNWAY_KEY));
+        // 设置 合同开始日期
+        contractApplyCancelDyo.set(TransferApplyBillConstant.STARTDATE_KEY, cancelContract.get(TransferApplyBillConstant.STARTDATE_KEY));
+        // 设置 实际结束日期
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ENDDATE_KEY, cancelContract.get(TransferApplyBillConstant.ENDDATE_KEY));
+        // 设置 签订日期
+        //contractApplyCancelDyo.set(TransferApplyBillConstant.SIGNEDDATE_KEY, cancelContract.get(TransferApplyBillConstant.SIGNEDDATE_KEY));// 解除不能设置
+        // 设置 合同期限
+        contractApplyCancelDyo.set(TransferApplyBillConstant.PERIOD_KEY, cancelContract.get(TransferApplyBillConstant.PERIOD_KEY));
+        // 设置 实签单位
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ACTUALSIGNCOMPANY_KEY, cancelContract.get(TransferApplyBillConstant.ACTUALSIGNCOMPANY_KEY));
+        // 设置 实签单位历史
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ACTUALSIGNCOMPANYHIS_KEY, cancelContract.get(TransferApplyBillConstant.ACTUALSIGNCOMPANYHIS_KEY));
+        // 设置 合同解除日期
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CANCELDATE_KEY, data.get(TransferApplyBillConstant.B_EFFECTIVEDATE_KEY));
+        // 设置 合同编号
+        contractApplyCancelDyo.set(TransferApplyBillConstant.OLDCONTRACT_KEY, cancelContract);
+        // 设置 业务处理状态
+        //contractApplyCancelDyo.set(TransferApplyBillConstant.HANDLESTATUS_KEY);
+        // 设置 任职类型
+        contractApplyCancelDyo.set(TransferApplyBillConstant.POSTYPE, data.get(TransferApplyBillConstant.BB_PO_POSTYPE_KEY));
+        // 设置 实际签订日期
+        //contractApplyCancelDyo.set(TransferApplyBillConstant.ACTUALSIGNDATE_KEY);
+        // 设置 合同期限单位
+        //contractApplyCancelDyo.set(TransferApplyBillConstant.PERIODUNIT_KEY);
+        // 设置 单据状态
+        contractApplyCancelDyo.set(TransferApplyBillConstant.BILL_STATUS_KEY, RegBillStatusEnum.TEMPSTORAGE.getCode());
+        // 设置 职位
+        Long jobHis = getJobHis(data.getLong(String.join(".", TransferApplyBillConstant.BB_PO_JOB_KEY, TransferApplyBillConstant.ID_KEY)));
+        contractApplyCancelDyo.set(TransferApplyBillConstant.JOB_KEY, jobHis);
+        // 设置 公司
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CURCOMPANY_KEY, data.get(TransferApplyBillConstant.BCOMPANYBO_KEY));
+        // 设置 部门
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CURDEPT_KEY, data.get(TransferApplyBillConstant.BB_PO_ADMINORGBO_KEY));
+        // 设置 岗位
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CURPOSTION_KEY, data.get(TransferApplyBillConstant.BB_PO_POSITIONBO_KEY));
+        // 设置 职位
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CURJOB_KEY, data.get(TransferApplyBillConstant.BB_PO_JOB_KEY));
+        // 设置 员工(HR员工)
+        contractApplyCancelDyo.set(TransferApplyBillConstant.EMPLOYEE_KEY, data.get(TransferApplyBillConstant.BB_EM_TID));
+        // 设置 员工(组织分配)
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ASSIGNMENT, data.get(TransferApplyBillConstant.BB_A_TID_KEY));
+        // 设置 任职序号
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ORGRELSEQ_KEY, data.get(TransferApplyBillConstant.BB_PO_ORGRELSEQ_KEY));
+        // 设置 居住地址
+        contractApplyCancelDyo.set(TransferApplyBillConstant.RESIDENTIALADDRESS_KEY, cancelContract.getString(TransferApplyBillConstant.RESIDENTIALADDRESS_KEY));
+        // 设置 户籍地址
+        contractApplyCancelDyo.set(TransferApplyBillConstant.HOUSEHOLDREGISTER_KEY, cancelContract.getString(TransferApplyBillConstant.HOUSEHOLDREGISTER_KEY));
+
+        // 设置 续签次数(续签用)
+        contractApplyCancelDyo.set(TransferApplyBillConstant.RENEWCOUNT_KEY, getRenewCount(contractApplyCancelDyo));
+        // 设置 人事管理组织
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ERMANORG_KEY, data.get(TransferApplyBillConstant.ORG_KEY));
+        // 设置 管理部门
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ADMINORORG_KEY, data.get(TransferApplyBillConstant.BB_PO_ADMINORGBO_KEY));
+        // 设置 员工组
+        contractApplyCancelDyo.set(TransferApplyBillConstant.ERMANPERORG_KEY, data.get(TransferApplyBillConstant.BB_A_EMPGROUP_KEY));
+        // 设置 档案类型
+        setContractCategory(contractApplyCancelDyo);
+        // 设置 解除原因
+        DynamicObject contractReasonDy = contractReasonHelper.generateEmptyDynamicObject();
+        contractReasonDy.set(TransferApplyBillConstant.ID_KEY, 1040L);
+        contractApplyCancelDyo.set(TransferApplyBillConstant.CANCELREASONTYPE_KEY, contractReasonDy);// 其他
+
+        // 设置 创建人
+        contractApplyCancelDyo.set(GECEventConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
+
+        return contractApplyCancelDyo;
+    }
+
+    // from: kd.hr.hlcm.business.domian.repository.ContractSignApplyRepository.getCancelContractByErManFile
+    public DynamicObject getCancelContractByErManFile(DynamicObject dataEntity) {
+        QFilter protocolTypeFilter = new QFilter(TransferApplyBillConstant.PROTOCOLTYPE_KEY, QCP.equals, "1");// 劳动合同
+        QFilter personFilter = new QFilter(TransferApplyBillConstant.EMPLOYEE_KEY, QCP.equals, dataEntity.getLong(String.format(Locale.ROOT, "%s_id", TransferApplyBillConstant.BB_EM_TID)));
+        QFilter posTypeFilter = new QFilter(TransferApplyBillConstant.POSTYPE, QCP.equals, dataEntity.getLong(String.format(Locale.ROOT, "%s_id", TransferApplyBillConstant.BB_PO_POSTYPE_KEY)));
+        QFilter contractStatusFilter = new QFilter(TransferApplyBillConstant.CONTRACTSTATUS_KEY, QCP.equals, "1");// 生效中
+        QFilter isCurFilter = new QFilter(TransferApplyBillConstant.IS_CURRENT_VERSION, QCP.equals, "1");
+        String selectProperties = QueryFieldBuilder.create().add(TransferApplyBillConstant.STARTDATE_KEY)
+                .add(TransferApplyBillConstant.PLANENDDATE_KEY)
+                .add(TransferApplyBillConstant.NUMBER_KEY)
+                .add(TransferApplyBillConstant.SUGGESTSIGNCOMPANY_KEY)
+                .add(TransferApplyBillConstant.ACTUALSIGNCOMPANY_KEY)
+                .add(TransferApplyBillConstant.CONTRACTSTATUS_KEY)
+                .add(TransferApplyBillConstant.NAME_KEY)
+                .add(TransferApplyBillConstant.CARDTYPE_KEY)
+                .add(TransferApplyBillConstant.CARDNUMBER_KEY)
+                .add(TransferApplyBillConstant.HOUSEHOLDREGISTER_KEY)
+                .add(TransferApplyBillConstant.RESIDENTIALADDRESS_KEY)
+                .add(TransferApplyBillConstant.EMPPHONE_KEY)
+                .add(TransferApplyBillConstant.ACTUALSIGNCOMPANYHIS_KEY)
+                .add(TransferApplyBillConstant.PERIODTYPE_KEY)
+                .add(TransferApplyBillConstant.PERIODUNIT_KEY)
+                .add(TransferApplyBillConstant.PERIOD_KEY)
+                .buildSelect();
+        // 这里要查所有属性出来,否则用到没查出来的字段会报异常
+        DynamicObject[] contractDy = contractSourceHelper.loadDynamicObjectArray(new QFilter[]{protocolTypeFilter, personFilter, contractStatusFilter, posTypeFilter, isCurFilter});
+
+        return getNewestContract(contractDy).orElse(null);
+    }
+
+    // from: kd.hr.hlcm.business.domian.repository.ContractSignApplyRepository.getNewestContract
+    private Optional<DynamicObject> getNewestContract(DynamicObject[] contracts) {
+        Comparator<DynamicObject> multipleComparator = buildNewestComparator();
+        return Stream.of(contracts).max(multipleComparator);
+    }
+
+    // from: kd.hr.hlcm.business.domian.repository.ContractSignApplyRepository.buildNewestComparator
+    private Comparator<DynamicObject> buildNewestComparator() {
+        Comparator<DynamicObject> contractStatusComparator = Comparator.comparing((dy) -> dy.getString(TransferApplyBillConstant.CONTRACTSTATUS_KEY));
+        Comparator<DynamicObject> comparingStartDate = Comparator.comparing((dy) -> dy.getDate(TransferApplyBillConstant.STARTDATE_KEY));
+        Comparator<DynamicObject> comparingId = Comparator.comparingLong((dy) -> dy.getLong(TransferApplyBillConstant.ID_KEY));
+        Comparator<DynamicObject> multipleComparing = contractStatusComparator.thenComparing(comparingStartDate).thenComparing(comparingId);
+        return multipleComparing;
+    }
+
+    // from: kd.hr.hlcm.business.domian.repository.ContractSignApplyRepository.getRenewCount
+    public static int getRenewCount(DynamicObject signBill) {
+        DynamicObject oldContract = signBill.getDynamicObject(TransferApplyBillConstant.OLDCONTRACT_KEY);
+        String businessType = signBill.getString(TransferApplyBillConstant.BUSINESSTYPE_KEY);
+        if (Objects.isNull(oldContract)) {
+            log.warn("renew select oldContract is null,curBusinessType|{}", businessType);
+            return 0;
+        } else {
+            DynamicObject preContract = contractHelper.queryOne(TransferApplyBillConstant.RENEWCOUNT_KEY, new QFilter[]{new QFilter(TransferApplyBillConstant.ID_KEY, QCP.equals, oldContract.getPkValue())});
+            int preRenewCount = preContract.getInt(TransferApplyBillConstant.RENEWCOUNT_KEY);
+            log.info("preRenewCount|{},curBusinessType|{}", preRenewCount, businessType);
+            return HRStringUtils.equals(businessType, BusinessTypeEnum.RENEW.getCombKey()) ? preRenewCount + 1 : preRenewCount;
+        }
+    }
+
+    // from: kd.hr.hlcm.business.domian.utils.ContractSignUtils.setContractCategory
+    public static void setContractCategory(DynamicObject signBill) {
+        if (HRStringUtils.equals(signBill.getString(TransferApplyBillConstant.PROTOCOLTYPE_KEY), "1")) {
+            DynamicObject posType = signBill.getDynamicObject(TransferApplyBillConstant.POSTYPE);
+            if (!HRObjectUtils.isEmpty(posType)) {
+                Object posTypeId = posType.getPkValue();
+                if (Objects.equals(posTypeId, PostypeEnum.FULL.getCombKey())) {
+                    signBill.set(TransferApplyBillConstant.CONTRACTCATEGORY_KEY, ContractCategoryEnum.MAIN.getCombKey());
+                } else if (Objects.equals(posTypeId, PostypeEnum.EXPATRIATE.getCombKey())) {
+                    signBill.set(TransferApplyBillConstant.CONTRACTCATEGORY_KEY, ContractCategoryEnum.OUTER.getCombKey());
+                } else {
+                    log.warn("posTypeId|{} is not 1010 or 1040", posTypeId);
+                }
+
+            }
+        }
+    }
+
+    // from: kd.hr.hlcm.business.infrastructure.client.hbpm.impl.HbpmServiceImpl.getJobHis(java.lang.Long)
+    public Long getJobHis(Long jobId) {
+        Map<Long, Long> jobHisMap = getJobHis(Lists.newArrayList(jobId), new Date());
+        return Optional.ofNullable(jobHisMap.get(jobId)).orElse(0L);
+    }
+
+    // from: kd.hr.hlcm.business.infrastructure.client.hbpm.impl.HbpmServiceImpl.getJobHis(java.util.List<java.lang.Long>)
+    public Map<Long, Long> getJobHis(List<Long> jobIds, Date date) {
+        filterEmpty(jobIds);
+        Map<Long, Long> currIdHisIdMap = new HashMap<>(jobIds.size());
+        if (jobIds.isEmpty())
+            return currIdHisIdMap;
+        try {
+            HrApiResponse jobHisMap = HRMServiceHelper.invokeHRMPService("hbjm", "IHBJMJobService", "queryJobHisInfoByBoIdToMap", new Object[] { jobIds, date });
+            log.info("IHBJMJobService queryJobHisInfoByBoIdToMap param response|{}", jobIds, jobHisMap);
+            if (HRObjectUtils.isEmpty(jobHisMap))
+                return currIdHisIdMap;
+            List<Map<String, Object>> dataMap = (List<Map<String, Object>>)jobHisMap.getData();
+            if (CollectionUtils.isEmpty(dataMap))
+                return currIdHisIdMap;
+            for (Map<String, Object> map : dataMap) {
+                Object vid = map.get(TransferApplyBillConstant.ID_KEY);
+                Object id = map.get(TransferApplyBillConstant.BOID_KEY);
+                if (vid != null && id != null)
+                    currIdHisIdMap.put((Long)id, Long.valueOf(vid.toString()));
+            }
+            log.info("getJobHis result|{}", currIdHisIdMap);
+        } catch (Exception ex) {
+            log.error("HbpmServiceImpl.getJobHis======jobBoid error..", ex);
+            throw new KDException(ex, BosErrorCode.systemError, new Object[0]);
+        }
+        return currIdHisIdMap;
+    }
+
+    // from: kd.hr.hlcm.business.infrastructure.client.hbpm.impl.HbpmServiceImpl.filterEmpty
+    private void filterEmpty(List<Long> ids) {
+        log.info("before filter ids|{}", ids);
+        ids.removeIf((id) -> id == null || id == 0L);
+        log.info("after filter ids|{}", ids);
+    }
+}

+ 24 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/PersonnelPredictionService.java

@@ -0,0 +1,24 @@
+package nckd.jxccl.hr.hspm.mservice;
+
+import nckd.jxccl.hr.hspm.mservice.api.IPersonnelPredictionService;
+
+import java.util.Date;
+
+/**
+ * TODO
+ *
+ * @author W.Y.C
+ * @version 1.0
+ * @date 2026/1/5 14:47
+ */
+public class PersonnelPredictionService implements IPersonnelPredictionService {
+    @Override
+    public Double predictRetirement(Long orgId,Date beginDate, Date endDate) {
+        return 20.0;
+    }
+
+    @Override
+    public Double predictAttrition(Long orgId,Date beginDate, Date endDate) {
+        return 10.0;
+    }
+}

+ 33 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/api/IPersonnelPredictionService.java

@@ -0,0 +1,33 @@
+package nckd.jxccl.hr.hspm.mservice.api;
+
+import java.util.Date;
+
+/**
+* 人员变动预测服务
+* 提供关于未来人员减少的预测数据,包括退休和减员。
+* @author W.Y.C
+* @date 2026/1/5 14:19
+* @version 1.0
+*/
+public interface IPersonnelPredictionService {
+
+    /**
+     * 预测单位/部门在未来某时间范围内的预计退休人数
+     * @param beginDate 查询开始日期
+     * @param endDate 查询结束日期
+     * @return: java.lang.Double
+     * @author W.Y.C
+     * @date: 2026/01/05 14:20
+     */
+    Double predictRetirement(Long orgId,Date beginDate, Date endDate);
+
+    /**
+     * 预测单位/部门在未来某时间范围内的预计减员人数
+     * @param beginDate 查询开始日期
+     * @param endDate 查询结束日期
+     * @return: java.lang.Double
+     * @author W.Y.C
+     * @date: 2026/01/05 14:21
+     */
+    Double predictAttrition(Long orgId,Date beginDate, Date endDate);
+}

+ 36 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/servicehelper/ServiceFactory.java

@@ -0,0 +1,36 @@
+package nckd.jxccl.hr.hspm.servicehelper;
+
+import kd.bos.dataentity.TypesContainer;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * TODO
+ *
+ * @author W.Y.C
+ * @version 1.0
+ * @date 2026/1/5 15:22
+ */
+public class ServiceFactory {
+
+    private static final Map<String, String> SERVICE_MAP = new HashMap<String, String>() {
+        {
+            this.put("IPersonnelPredictionService", "nckd.jxccl.hr.hspm.mservice.PersonnelPredictionService");
+        }
+    };
+
+    public static <T> T getService(Class<T> clazz) {
+        return (T)getService(clazz.getSimpleName());
+    }
+
+    public static Object getService(String serviceName) {
+        String className = (String)SERVICE_MAP.get(serviceName);
+        if (className == null) {
+            throw new RuntimeException(String.format(Locale.ROOT, "%s service not find", serviceName));
+        } else {
+            return TypesContainer.getOrRegisterSingletonInstance(className);
+        }
+    }
+}

+ 4 - 18
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/htm/plugin/form/quitapply/QuitApplyFormPlugin.java

@@ -6,10 +6,8 @@ import kd.bos.entity.datamodel.events.PropertyChangedArgs;
 import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.DispatchServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
-import kd.sdk.wtc.wtp.business.attfile.AttFileVersion;
-import kd.sdk.wtc.wtp.business.helper.WTPServiceHelper;
-import kd.sdk.wtc.wtp.business.quota.QuotaQueryParam;
 import nckd.jxccl.hr.htm.common.quitapply.QuitApplyConstant;
 
 import java.time.ZoneId;
@@ -79,25 +77,13 @@ public class QuitApplyFormPlugin extends AbstractFormPlugin {
      */
     private void setUnUsedAnnualLeave(Long employeeId, Date contractEndDate) {
         // 获取人员考勤档案
-        AttFileVersion attFile = WTPServiceHelper.getAttFile(contractEndDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), employeeId);
-        if (attFile == null) {
+        Long attFileBoId = DispatchServiceHelper.invokeService("nckd.jxccl.wtc.wtp.servicehelper", QuitApplyConstant.WTP_APP, "IWTPCustomerService", "getAttFileBoId", new Object[]{contractEndDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), employeeId});
+        if (attFileBoId == 0L) {
             getView().showTipNotification("未获取到应休未休剩余年假,请检查离职人员是否存在离职时间内有效的考勤档案");
             return;
         }
 
-        // 查询应休未休剩余年假
-        QuotaQueryParam quotaQueryParam = new QuotaQueryParam();
-        // 考勤档案
-        quotaQueryParam.setAttFileBoId(attFile.getBoId());
-        // 休假类型
-        quotaQueryParam.setQuotaTypeIdList(Collections.singletonList(1666695290893207552L));
-        // 仅使用范围过滤
-        quotaQueryParam.setRangQueryType(0);
-        // 开始时间
-        quotaQueryParam.setStartDate(contractEndDate);
-        // 结束时间
-        quotaQueryParam.setEndDate(contractEndDate);
-        List<DynamicObject> quotaList = WTPServiceHelper.queryQuota(quotaQueryParam);
+        List<DynamicObject> quotaList = DispatchServiceHelper.invokeService("nckd.jxccl.wtc.wtp.servicehelper", QuitApplyConstant.WTP_APP, "IWTPCustomerService", "queryQuota", new Object[]{attFileBoId, Collections.singletonList(1666695290893207552L), 0, contractEndDate, contractEndDate});
         if (quotaList != null && !quotaList.isEmpty()) {
             DynamicObject quotaDy = quotaList.get(0);
             getModel().setValue(QuitApplyConstant.NCKD_UNUSEDANNUALLEAVE_KEY, quotaDy.getBigDecimal(QuitApplyConstant.USABLEVALUE_KEY));

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/.gitkeep → code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/business/.gitkeep


+ 22 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/common/AppflgConstant.java

@@ -0,0 +1,22 @@
+/**
+ * This is a kingdee cosmic template project that is automatically generated by the Kingdee cosmic development assistant plugin. 
+ * If there are any issues during the use process, you can provide feedback to the kingdee developer community website.
+ * Website: https://developer.kingdee.com/developer?productLineId=29
+ * Author: liebin.zheng
+ * Generate Date: 2025-05-26 16:28:10
+ */
+package nckd.jxccl.hr.ijp.common;
+
+/**
+ * hr云init应用-通用常量类<br>
+ * 代码中不能存在硬编码敏感信息,如账号、密码、http外链、ftp外链、邮箱等。<br>
+ * 标识或缓存的常量,需以"KEY_"、"FID_"、"ENTRY_"或"SUBENTRY_"作为变量的前缀。<br>
+ *
+ * @author nckd
+ * @date 2025-05-26 16:28:10
+ */
+public class AppflgConstant {
+	
+	public static final String KEY_APP_NAME = "hr-init";
+
+}

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

@@ -0,0 +1,35 @@
+package nckd.jxccl.hr.ijp.common;
+
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+ * 三类人员记录生成常量类
+ *
+ * @author W.Y.C
+ * @version 1.0
+ * @date 2026-01-07 11:44:49
+ */
+public class IntJobPostConstant extends FormConstant {
+    /** 三类人员记录生成-实体标识 */
+    public static final String INTJOBPOST_ENTITYID = "nckd_intjobpost";
+    /** 所属年度 */
+    public static final String NCKD_YEAR = "nckd_year";
+    /** 进入市场时间 */
+    public static final String NCKD_BIZDAY = "nckd_bizday";
+    /** 员工类别 */
+    public static final String NCKD_TALENTTYPE = "nckd_talenttype";
+    /** 单据体 */
+    public static final String NCKD_ENTRYENTITY = "nckd_entryentity";
+    /** 姓名 */
+    public static final String NCKD_EMPLOYEEFIELD = "nckd_employeefield";
+    /** 上上年度考核结果 */
+    public static final String NCKD_BEFORELAST = "nckd_beforelast";
+    /** 上年度考核结果 */
+    public static final String NCKD_APPRAISALRESULT = "nckd_appraisalresult";
+    /** 是否进入市场 */
+    public static final String NCKD_ISENTER = "nckd_isenter";
+    /** 是否手动新增 */
+    public static final String NCKD_ISJOG = "nckd_isjog";
+    /** 获取员工 */
+    public static final String FETCHEMP_OP = "fetchemp";
+}

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/mservice/.gitkeep


+ 467 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/form/recgen/IntJobPostFormPlugin.java

@@ -0,0 +1,467 @@
+package nckd.jxccl.hr.ijp.plugin.form.recgen;
+
+import com.google.common.collect.Lists;
+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.EntityMetadataCache;
+import kd.bos.entity.QueryEntityType;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.ConfirmCallBackListener;
+import kd.bos.form.ConfirmTypes;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.MessageBoxResult;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.MessageBoxClosedEvent;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
+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.ConvertUtil;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+import nckd.jxccl.base.hrpi.helper.EmpPosOrgRelHelper;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.ijp.common.IntJobPostConstant;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+* 三类人员记录生成
+* 实体标识:nckd_intjobpost
+* @author W.Y.C
+* @date 2026/1/7 11:41
+* @version 1.0
+*/
+public class IntJobPostFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    // 常量定义
+    private static final int FIVE_YEARS = 5;
+    private static final String[] POST_TYPES = {"JTCC_1002", "1010_S"};
+    private static final String PERF_MANAGER_ENTRY = "nckd_perfmanagerentry";
+    private static final String PERF_MANAGER = "nckd_perfmanager";
+    private static final String APPRAISAL_RESULT = "nckd_appraisalresult";
+    private static final String APPRAISAL_YEAR = "nckd_appraisalyear";
+    private static final String PERSON_FIELD = FormConstant.NCKD_PERSON;
+    private static final String[] PERFORMANCE_EVALUATION_TYPES = {"JTCC_1002", "1010_S"}; // 任职类型:上挂/全职任职
+    
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs evt) {
+        if(evt.getOperationResult() != null && evt.getOperationResult().isSuccess()){
+            String operateKey = evt.getOperateKey();
+            if(IntJobPostConstant.FETCHEMP_OP.equalsIgnoreCase(operateKey)){
+                String talentType = ConvertUtil.toStr(this.getModel().getValue(IntJobPostConstant.NCKD_TALENTTYPE));
+                DynamicObject org = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(IntJobPostConstant.NCKD_ORG));
+                if(StringUtils.isBlank(talentType)){
+                    this.getView().showTipNotification("请先选择人员类别!");
+                }else if(org == null){
+                    this.getView().showTipNotification("请先选择二级单位!");
+                }else{
+                    switch (talentType.toUpperCase()) {
+                        case "A":
+                            getExcellentEmp();
+                            break;
+                        case "C":
+                            getPerformanceEndEmployees();
+                            break;
+                        default:
+                            // 其他类型暂不处理
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void beforePropertyChanged(PropertyChangedArgs e) {
+        String name = e.getProperty().getName();
+        if (PerfRankMgmtConstant.NCKD_ORG.equalsIgnoreCase(name) || IntJobPostConstant.NCKD_TALENTTYPE.equalsIgnoreCase(name)) {
+            ChangeData[] changeSet = e.getChangeSet();
+            Object oldValue = changeSet[0].getOldValue();
+            Object newValue = changeSet[0].getNewValue();
+            if(!Objects.equals(newValue, oldValue)) {
+                DynamicObjectCollection entryEntities = getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_ENTRYENTITY);
+                boolean hasNonEmptyPerson = entryEntities.stream()
+                        .anyMatch(entry -> entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERSON) != null);
+                if(hasNonEmptyPerson) {
+                    // 直接弹出确认框,无需回滚
+                    // 表单字段修改前交互
+                    this.getView().showConfirm(
+                            "确认修改【二级组织或员工类别】吗?",
+                            "请注意:修改【二级组织或员工类别】会导致员工范围变化会清空现有员工信息。您需要在之后重新生成。确认要修改吗?",
+                            MessageBoxOptions.OKCancel,
+                            ConfirmTypes.Delete,
+                            new ConfirmCallBackListener(PerfRankMgmtConstant.NCKD_ORG, this)
+                    );
+                }
+            }
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        String name = e.getProperty().getName();
+        if (PerfRankMgmtConstant.NCKD_PERSON.equalsIgnoreCase(name)) {
+            String talentType = ConvertUtil.toStr(this.getModel().getValue(IntJobPostConstant.NCKD_TALENTTYPE));
+            ChangeData[] changeSet = e.getChangeSet();
+            int rowIndex = changeSet[0].getRowIndex();
+            Object oldValue = changeSet[0].getOldValue();
+            Object newValue = changeSet[0].getNewValue();
+            if(!Objects.equals(newValue, oldValue) && "B".equalsIgnoreCase(talentType)) {
+                // 获取考核结果信息
+                LocalDateTime lastYear = DateUtil.minusYears(DateUtil.now(), 1);
+                LocalDateTime beforeLastYear = DateUtil.minusYears(DateUtil.now(), 2);
+                DynamicObject person = ConvertUtil.toDynamicObject(newValue);
+                Map<Long, List<DynamicObject>> perfManagerMap = getPerformanceEvaluationResults(Lists.newArrayList(person.getLong(FormConstant.ID_KEY)));
+                if(perfManagerMap != null && perfManagerMap.get(person.getLong(FormConstant.ID_KEY)) != null){
+                    List<DynamicObject> perfManagerList = perfManagerMap.get(person.getLong(FormConstant.ID_KEY));
+                    for (DynamicObject perfManager : perfManagerList) {
+                        Date appraisalYear = perfManager.getDate(APPRAISAL_YEAR);
+                        long appraisalResultId = perfManager.getLong(String.join(".", APPRAISAL_RESULT, FormConstant.ID_KEY));
+                        String appraisalResultName = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NAME_KEY));
+                        String appraisalResultNumber = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NUMBER_KEY));
+                        DynamicObject appraisalResult = EntityHelper.newEntity(FormConstant.NCKD_APPRAISALRESULT, appraisalResultId);
+                        appraisalResult.set(FormConstant.NAME_KEY, appraisalResultName);
+                        appraisalResult.set(FormConstant.NUMBER_KEY, appraisalResultNumber);
+                        if(lastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                            this.getModel().setValue(IntJobPostConstant.NCKD_APPRAISALRESULT, appraisalResult);
+                        }
+                        if(beforeLastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                            this.getModel().setValue(IntJobPostConstant.NCKD_BEFORELAST, appraisalResult);
+                        }
+                    }
+                }
+
+
+            }
+        }
+    }
+
+    @Override
+    public void confirmCallBack(MessageBoxClosedEvent evt) {
+        super.confirmCallBack(evt);
+        if(PerfRankMgmtConstant.NCKD_ORG.equalsIgnoreCase(evt.getCallBackId())){
+            // 用户确认:允许修改继续,并清空分录
+            if (evt.getResult() == MessageBoxResult.Yes) {
+                DynamicObjectCollection entryEntities = getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_ENTRYENTITY);
+                entryEntities.clear();
+                this.getModel().updateEntryCache(entryEntities);
+                this.getView().updateView(PerfRankMgmtConstant.NCKD_ENTRYENTITY);
+            }
+        }
+    }
+
+    /**
+     * 获取优秀生员工
+     * 本单位工作(工作信息-进本单位时间)满5年,距法定退休年龄(工作信息-新法定退休日期)超过5年且上年度员工绩效考核为优秀的所有员工
+     * @return: void
+     * @author W.Y.C
+     * @date: 2026/01/08 15:46
+     */
+    private void getExcellentEmp(){
+        DynamicObject org = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(IntJobPostConstant.NCKD_ORG));
+        
+        // 获取满足基本条件的员工
+        Map<Long, DynamicObject> personMap = getBasicEmployeeCondition(org, "A");
+        
+        // 获取考核结果信息
+        Map<Long, List<DynamicObject>> perfManagerMap = getPerformanceEvaluationResults(personMap.keySet());
+        
+        // 处理员工数据,筛选符合条件的优秀员工
+        List<PersonData> personDataList = processExcellentEmployeeData(perfManagerMap);
+        
+        // 更新界面显示
+        updateEntryEntity(personDataList);
+    }
+
+    /**
+     * 获取满足基本条件的员工
+     * @param org 组织信息
+     * @param talentType 类型
+     * @return 员工信息映射
+     */
+    private Map<Long, DynamicObject> getBasicEmployeeCondition(DynamicObject org, String talentType) {
+        //前5年
+        LocalDateTime fiveYearsAgo = DateUtil.minusYears(DateUtil.now(), FIVE_YEARS);
+        //后5年
+        LocalDateTime fiveYearsLater = DateUtil.addYears(DateUtil.now(), FIVE_YEARS);
+
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(FormConstant.STARTDATE)
+                .addIdNumberName(FormConstant.ADMINORG)
+                .addIdNumberName(FormConstant.POSITION_KEY)
+                .addGroup(new String[]{FormConstant.EMPLOYEE_KEY}, FormConstant.ID_KEY, FormConstant.NAME_KEY, FormConstant.EMP_NUMBER_KEY)
+                .orderDesc(FormConstant.STARTDATE);
+
+        QFilter qFilter = new QFilter(String.join(".", FormConstant.ADMINORG, FormConstant.NCKD_SECONDORG), QCP.equals, org.getLong(FormConstant.ID_KEY))
+                .and(QFilterCommonHelper.getValidDateFilter(FormConstant.STARTDATE, FormConstant.ENDDATE))
+                //任职类型:上挂/全职任职
+                .and(String.join(".", FormConstant.POSTYPE, FormConstant.NUMBER_KEY), QCP.in, PERFORMANCE_EVALUATION_TYPES)
+                //在职人员
+                .and(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_HIRED), QCP.equals, EnableEnum.YES.getCode());
+        if(talentType.equalsIgnoreCase("A")) {
+            //进本单位满5年
+            qFilter.and(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.STARTDATE), QCP.less_equals, DateUtil.toDate(fiveYearsAgo));
+        }
+
+        //新法定退休日期超过5年条件,或退休日期为空
+        QFilter retirementDateFilter = new QFilter(String.join(".", FormConstant.HRPI_PERSERLEN, FormConstant.NCKD_NEWPLANRETDT), QCP.is_null, null)
+                .or(String.join(".", FormConstant.HRPI_PERSERLEN, FormConstant.NCKD_NEWPLANRETDT), QCP.large_equals, DateUtil.toDate(fiveYearsLater));
+        qFilter.and(retirementDateFilter);
+        
+        QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("personfilequery");
+        DynamicObjectCollection personList = HRQueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder());
+        
+        // 调试信息,仅在开发阶段使用
+        // for (DynamicObject dynamicObject : personList) {
+        //     String number = dynamicObject.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.NUMBER_KEY));
+        //     System.out.println(number);
+        // }
+        
+        return personList.stream()
+                .collect(Collectors.groupingBy(
+                        obj -> obj.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY))
+                ))
+                .entrySet()
+                .stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        entry -> entry.getValue().get(0) // 由于已按日期降序排列,取第一条即为最新
+                ));
+    }
+
+    /**
+     * 获取绩效考核结果
+     * @param personList 员工ID
+     * @return 绩效考核结果映射
+     */
+    private Map<Long, List<DynamicObject>> getPerformanceEvaluationResults(Collection<Long> personList) {
+        QueryFieldBuilder perfManagerQueryFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(PERF_MANAGER, FormConstant.NCKD_PERSON)
+                .addIdNumberName(APPRAISAL_RESULT)
+                .add(APPRAISAL_YEAR);
+
+        Date beginDate = DateUtil.beginOfYear(DateUtil.minusYears(DateUtil.nowDate(), 2));
+        Date endDate = DateUtil.endOfYear(DateUtil.minusYears(DateUtil.nowDate(), 1));
+        QFilter perfManagerFilter = new QFilter(APPRAISAL_YEAR, QCP.large_equals, beginDate)
+                .and(APPRAISAL_YEAR, QCP.less_equals, endDate)
+                .and(String.join(".", PERF_MANAGER, FormConstant.NCKD_PERSON), QCP.in, personList);
+        
+        DynamicObjectCollection perfManagerColl = QueryServiceHelper.query(PERF_MANAGER_ENTRY, perfManagerQueryFieldBuilder.buildSelect(), new QFilter[]{perfManagerFilter});
+        
+        return perfManagerColl.stream()
+                .collect(Collectors.groupingBy(perfManager ->
+                        perfManager.getLong(String.join(".", PERF_MANAGER, FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
+                ));
+    }
+
+    /**
+     * 处理优秀员工数据
+     * @param perfManagerMap 绩效考核结果映射
+     * @return 符合条件的员工数据列表
+     */
+    private List<PersonData> processExcellentEmployeeData(Map<Long, List<DynamicObject>> perfManagerMap) {
+        //取出上年度
+        LocalDateTime lastYear = DateUtil.minusYears(DateUtil.now(), 1);
+        LocalDateTime beforeLastYear = DateUtil.minusYears(DateUtil.now(), 2);
+        List<PersonData> personDataList = new ArrayList<>();
+        
+        for (Map.Entry<Long, List<DynamicObject>> perfManagerEntry : perfManagerMap.entrySet()) {
+            Long key = perfManagerEntry.getKey();
+            List<DynamicObject> perfManagerList = perfManagerEntry.getValue();
+            
+            PersonData personData = new PersonData();
+            boolean isExcellent = false;
+            
+            for (DynamicObject perfManager : perfManagerList) {
+                Date appraisalYear = perfManager.getDate(APPRAISAL_YEAR);
+                String appraisalResultNumber = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NUMBER_KEY));
+                Long personId = perfManager.getLong(String.join(".", PERF_MANAGER, FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                
+                personData.personId = personId;
+                
+                if(lastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                    AppraisalResultEnum appraisalResult = AppraisalResultEnum.getByCode(appraisalResultNumber);
+                    if((appraisalResult == AppraisalResultEnum.EXCELLENT || appraisalResult == AppraisalResultEnum.DEEMED_EXCELLENT)) {
+                        isExcellent = true;
+                        personData.appraisalResultId = perfManager.getLong(String.join(".", APPRAISAL_RESULT, FormConstant.ID_KEY));
+                        personData.appraisalResultNumber = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NUMBER_KEY));
+                        personData.appraisalResultName = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NAME_KEY));
+                    }
+                }
+                
+                if(beforeLastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                    personData.beforeLastResultId = perfManager.getLong(String.join(".", APPRAISAL_RESULT, FormConstant.ID_KEY));
+                    personData.beforeLastResultNumber = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NUMBER_KEY));
+                    personData.beforeLastResultName = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NAME_KEY));
+                }
+            }
+            
+            if(isExcellent) {
+                personDataList.add(personData);
+            }
+        }
+        
+        return personDataList;
+    }
+
+    /**
+     * 获取绩效末等员工
+     * 查询年度绩效考核定格为"不合格"或连续两年定格为"基本合格"的所有员工(距法定退休年龄小于5年(含)的员工除外)
+     * @return:
+     * @author W.Y.C
+     * @date: 2026/01/08 15:47
+     */
+    public void getPerformanceEndEmployees(){
+        DynamicObject org = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(IntJobPostConstant.NCKD_ORG));
+        
+        // 获取满足基本条件的员工
+        Map<Long, DynamicObject> personMap = getBasicEmployeeCondition(org, "C");
+        
+        // 获取考核结果信息
+        Map<Long, List<DynamicObject>> perfManagerMap = getPerformanceEvaluationResults(personMap.keySet());
+        
+        // 处理员工数据,筛选符合条件的绩效末等员工
+        List<PersonData> personDataList = processPerformanceEndEmployeeData(perfManagerMap);
+        
+        // 更新界面显示
+        updateEntryEntity(personDataList);
+    }
+
+    /**
+     * 处理绩效末等员工数据
+     * @param perfManagerMap 绩效考核结果映射
+     * @return 符合条件的员工数据列表
+     */
+    private List<PersonData> processPerformanceEndEmployeeData(Map<Long, List<DynamicObject>> perfManagerMap) {
+        //取出上年度
+        LocalDateTime lastYear = DateUtil.minusYears(DateUtil.now(), 1);
+        LocalDateTime beforeLastYear = DateUtil.minusYears(DateUtil.now(), 2);
+        List<PersonData> personDataList = new ArrayList<>();
+        
+        for (Map.Entry<Long, List<DynamicObject>> perfManagerEntry : perfManagerMap.entrySet()) {
+            Long key = perfManagerEntry.getKey();
+            List<DynamicObject> perfManagerList = perfManagerEntry.getValue();
+            
+            PersonData personData = new PersonData();
+            boolean isUnQualified = false;
+            //连续两年为基本合格
+            int basicallyQualifiedCount = 0;
+            
+            for (DynamicObject perfManager : perfManagerList) {
+                Date appraisalYear = perfManager.getDate(APPRAISAL_YEAR);
+                Long appraisalResultId = perfManager.getLong(String.join(".", APPRAISAL_RESULT, FormConstant.ID_KEY));
+                String appraisalResultNumber = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NUMBER_KEY));
+                String appraisalResultName = perfManager.getString(String.join(".", APPRAISAL_RESULT, FormConstant.NAME_KEY));
+                AppraisalResultEnum appraisalResult = AppraisalResultEnum.getByCode(appraisalResultNumber);
+                Long personId = perfManager.getLong(String.join(".", PERF_MANAGER, FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                
+                personData.personId = personId;
+                
+                if(lastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                    if((appraisalResult == AppraisalResultEnum.UN_QUALIFIED || appraisalResult == AppraisalResultEnum.DEEMED_UNQUALIFIED)
+                            || (appraisalResult == AppraisalResultEnum.BASICALLY_QUALIFIED || appraisalResult == AppraisalResultEnum.DEEMED_BASICALLY_QUALIFIED)) {
+                        if((appraisalResult == AppraisalResultEnum.UN_QUALIFIED || appraisalResult == AppraisalResultEnum.DEEMED_UNQUALIFIED)) {
+                            isUnQualified = true;
+                        } else if(appraisalResult == AppraisalResultEnum.BASICALLY_QUALIFIED || appraisalResult == AppraisalResultEnum.DEEMED_BASICALLY_QUALIFIED) {
+                            basicallyQualifiedCount++;
+                        }
+                        personData.appraisalResultId = appraisalResultId;
+                        personData.appraisalResultNumber = appraisalResultNumber;
+                        personData.appraisalResultName = appraisalResultName;
+                    }
+                }
+                
+                if(beforeLastYear.getYear() == DateUtil.getYear(appraisalYear)) {
+                    if(appraisalResult == AppraisalResultEnum.BASICALLY_QUALIFIED || appraisalResult == AppraisalResultEnum.DEEMED_BASICALLY_QUALIFIED) {
+                        basicallyQualifiedCount++;
+                    }
+                    personData.beforeLastResultId = appraisalResultId;
+                    personData.beforeLastResultNumber = appraisalResultNumber;
+                    personData.beforeLastResultName = appraisalResultName;
+                }
+            }
+            
+            if(isUnQualified || basicallyQualifiedCount >= 2) { // 至少2次基本合格
+                personDataList.add(personData);
+            }
+        }
+        
+        return personDataList;
+    }
+
+    /**
+     * 更新条目实体
+     * @param personDataList 员工数据列表
+     */
+    private void updateEntryEntity(List<PersonData> personDataList) {
+        if(!personDataList.isEmpty()) {
+            List<Long> personIds = personDataList.stream()
+                    .map(personData -> personData.personId)
+                    .collect(Collectors.toList());
+            Map<Long, DynamicObject> empPosOrgRelMap = EmpPosOrgRelHelper.queryEmpPosOrgRelByEmployeesMap(personIds);
+            DynamicObjectCollection entryEntityColl = this.getModel().getDataEntity(true).getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+            
+            for (PersonData personData : personDataList) {
+                DynamicObject empPosOrgRel = empPosOrgRelMap.get(personData.personId);
+                if(empPosOrgRel != null) {
+                    DynamicObject entry = entryEntityColl.addNew();
+                    entry.set(IntJobPostConstant.NCKD_ISJOG, EnableEnum.NO.getCode());
+                    entry.set(IntJobPostConstant.NCKD_ISENTER, EnableEnum.YES.getCode());
+                    entry.set(FormConstant.NCKD_PERSON, empPosOrgRel.getDynamicObject(FormConstant.EMPLOYEE_KEY));
+                    entry.set(FormConstant.NCKD_DEP, empPosOrgRel.getDynamicObject(FormConstant.ADMINORG));
+                    entry.set(FormConstant.NCKD_POSITION_KEY, empPosOrgRel.getDynamicObject(FormConstant.POSITION_KEY));
+                    
+                    DynamicObject appraisalResult = EntityHelper.newEntity(FormConstant.NCKD_APPRAISALRESULT, personData.appraisalResultId);
+                    appraisalResult.set(FormConstant.NAME_KEY, personData.appraisalResultName);
+                    appraisalResult.set(FormConstant.NUMBER_KEY, personData.appraisalResultNumber);
+                    entry.set(IntJobPostConstant.NCKD_APPRAISALRESULT, appraisalResult);
+
+                    if (personData.beforeLastResultId != null && personData.beforeLastResultId > 0) {
+                        DynamicObject beforeAppraisalResult = EntityHelper.newEntity(FormConstant.NCKD_APPRAISALRESULT, personData.appraisalResultId);
+                        beforeAppraisalResult.set(FormConstant.NAME_KEY, personData.beforeLastResultName);
+                        beforeAppraisalResult.set(FormConstant.NUMBER_KEY, personData.beforeLastResultNumber);
+                        entry.set(IntJobPostConstant.NCKD_BEFORELAST, beforeAppraisalResult);
+                    }
+                }
+            }
+            this.getView().showSuccessNotification("获取成功!");
+            this.getModel().updateEntryCache(entryEntityColl);
+            this.getView().updateView(FormConstant.NCKD_ENTRYENTITY);
+        }else{
+            this.getView().showTipNotification("无符合条件的员工!");
+        }
+    }
+    
+    class PersonData{
+        Long personId;
+        Long appraisalResultId;
+        String appraisalResultNumber;
+        String appraisalResultName;
+        Long beforeLastResultId;
+        String beforeLastResultNumber;
+        String beforeLastResultName;
+    }
+}

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/operate/.gitkeep


+ 43 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/operate/IntJobPostAuditedOpPlugin.java

@@ -0,0 +1,43 @@
+package nckd.jxccl.hr.ijp.plugin.operate;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+* 内部人力资源市场-三类人员记录生成-审批通过
+* 实体标识:nckd_intjobpost
+* @author W.Y.C
+* @date 2026/1/8 21:34
+* @version 1.0
+*/
+public class IntJobPostAuditedOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+
+        for (DynamicObject dataEntity : e.getDataEntities()) {
+            DynamicObjectCollection dynamicObjectCollection = dataEntity.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+        }
+        //1、阶段1:备份原有记录
+        // 条件:单据中要进入市场(CFIsenter=1)的员工 + 他们最新的记录 + 记录来源不是当前单据
+        //目的:为反审批时恢复数据做准备
+
+        //阶段2:关闭原记录
+        //逻辑:一个员工只能有一条活跃记录(FIsSingle=1)
+        //状态变化:1(在市场中) → 4(退出市场)
+
+        //阶段3:创建新记录
+        //新记录状态:FIsSingle=1, CFInsidehrRecordState='1'
+
+        //阶段3:特殊处理
+        //A类员工:有预计退出时间(1年后)
+        //非A类员工:预计退出时间为null
+
+
+
+    }
+}

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/other/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/report/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/plugin/workflow/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/ijp/webapi/.gitkeep


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

@@ -49,7 +49,7 @@ public class UnAdjustedReportFormPlugin extends AbstractReportFormPlugin impleme
 
     @Override
     public void beforeQuery(ReportQueryParam queryParam) {
-        ReportList reportList = this.getControl("reportlistap");
+        ReportList reportList = this.getControl(FormConstant.REPORTLISTAP);
         reportList.setSelectedAll(true);
     }
 

+ 8 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/annualadjust/report/UnAnnualAdjustReportFormPlugin.java

@@ -4,6 +4,7 @@ import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.datamodel.ListSelectedRow;
 import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.form.CloseCallBack;
 import kd.bos.form.FormShowParameter;
 import kd.bos.form.ShowType;
@@ -33,6 +34,7 @@ import java.util.List;
 */
 public class UnAnnualAdjustReportFormPlugin extends AbstractReportFormPlugin implements Plugin {
 
+
     @Override
     public void registerListener(EventObject e) {
         this.addItemClickListeners(FormConstant.TOOLBARAP);
@@ -49,6 +51,12 @@ public class UnAnnualAdjustReportFormPlugin extends AbstractReportFormPlugin imp
 
     }
 
+    @Override
+    public void beforeQuery(ReportQueryParam queryParam) {
+        ReportList reportList = this.getControl(FormConstant.REPORTLISTAP);
+        reportList.setSelectedAll(true);
+    }
+
     @Override
     public void itemClick(ItemClickEvent evt) {
         String itemKey = evt.getItemKey();

+ 5 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/other/report/KeyBeHavEvalReptQueryPlugin.java

@@ -85,8 +85,8 @@ public class KeyBeHavEvalReptQueryPlugin extends AbstractReportListDataPlugin im
                 RequestContext.get().getCurrUserId(), "nckd_psmsfile", PositionStructureConstant.PERSONPOSFILE_ENTITYID,
                 PermItemConst.ITEM_VIEW, new HashMap<>());
         if (dataRule != null) {
-            Object value = dataRule.getValue();
-            qFilter.and(String.join(".",FormConstant.NCKD_PERSON,FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG), QCP.in, value);
+//            Object value = dataRule.getValue();
+            qFilter.and(dataRule);
         }
 
         processFastFilter(reportQueryParam, qFilter);
@@ -94,6 +94,7 @@ public class KeyBeHavEvalReptQueryPlugin extends AbstractReportListDataPlugin im
         processFilter(reportQueryParam, qFilter);
 
         DataSet mainTableDataSet = queryMainTableDataSet(qFilter);
+        mainTableDataSet.print(true);
 
         // 6. 关联主表和行转列结果
         DataSet finalResultDataSet = joinMainAndPivotData(mainTableDataSet, pivotResultDataSet);
@@ -287,6 +288,8 @@ public class KeyBeHavEvalReptQueryPlugin extends AbstractReportListDataPlugin im
      * 查询主表数据
      */
     private DataSet queryMainTableDataSet(QFilter qFilter) {
+
+
         QueryFieldBuilder queryFieldBuilder = buildQueryFieldBuilder();
         return QueryServiceHelper.queryDataSet("KeyBeHavEvalReptQueryPlugin", 
                 PositionStructureConstant.KEYBEHAVEVAL_ENTITYID, 

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

@@ -234,6 +234,10 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
                 .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);
+        if(query.isEmpty()){
+            this.getView().showTipNotification("未配置排名比例信息");
+            return;
+        }
         Map<String, BigDecimal> appraisalResultRatioMap = new HashMap<>();
         query.forEach(dynamicObject -> {
             String key = dynamicObject.getString(String.join(".", FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
@@ -499,6 +503,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
                 QFilter qFilter = new QFilter(String.join(".",FormConstant.ADMINORG, FormConstant.STRUCTLONGNUMBER), QCP.like, structLongNumber + "%")
                         .and(FormConstant.STARTDATE, QCP.less_equals, targetDate)
                         .and(FormConstant.ENDDATE, QCP.large_equals, targetDate)
+                        //任职类型:上挂全职任职
                         .and(String.join( ".", FormConstant.POSTYPE, FormConstant.NUMBER_KEY), QCP.in,new String[]{"JTCC_1002", "1010_S"});
 
                 //在职人员

+ 107 - 76
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/file/PersonPosFileSaveOpPlugin.java

@@ -11,14 +11,14 @@ 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.DispatchServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.sdk.plugin.Plugin;
-import kd.sdk.swc.hcdm.business.helper.HCDMSalaryStdServiceHelper;
-import kd.sdk.swc.hcdm.common.dto.stdtab.match.StdTableDataMatchParam;
-import kd.sdk.swc.hcdm.common.dto.stdtab.match.StdTableDataMatchResult;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
+import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.base.swc.helper.AdjFileServiceHelper;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 
@@ -29,7 +29,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
 * 员工职位档案-保存
@@ -46,6 +45,7 @@ public class PersonPosFileSaveOpPlugin extends AbstractOperationServicePlugIn im
 
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        logger.info("beginOperationTransaction start: processing {} data entities", e.getDataEntities().length);
 
         //职位津贴=职位系数 X 所在岗级岗位工资一档金额
         List<Long> allPersonIds = new ArrayList<>(e.getDataEntities().length);
@@ -68,92 +68,123 @@ public class PersonPosFileSaveOpPlugin extends AbstractOperationServicePlugIn im
                 }
             }
         }
+        
+        logger.info("Collected person IDs count: {}, job level IDs count: {}", allPersonIds.size(), jobLevelIds.size());
 
         //====================================== 获取当前人员所在定调的岗位标准工资一档金额 begin ======================================
-        Map<Long, BigDecimal> amountMap = new HashMap<>(e.getDataEntities().length);
-        if(!allPersonIds.isEmpty() && !jobLevelIds.isEmpty()) {
-            //获取人员最新岗位工资标准定薪记录
-            List<AdjFileServiceHelper.SalaryAdjustmentResult> salaryAdjustmentResultList = AdjFileServiceHelper.getLastDecAdjRecords(allPersonIds, FormConstant.STANDARDITEM_ID_KEY);
-            if (!salaryAdjustmentResultList.isEmpty()) {
-                //薪酬标准ID
-                List<Long> salaryStIds = salaryAdjustmentResultList.stream().map(result -> result.salaryStDv.getLong(FormConstant.ID_KEY)).collect(Collectors.toList());
-                QFilter filter = new QFilter(FormConstant.ID_KEY, QCP.in, salaryStIds)
-                        .and(String.join(".", "rankentry", "rank", FormConstant.NUMBER_KEY), QCP.equals, "01");
-                //获取标准表中01档的薪档
-                QueryFieldBuilder salaryStandFieldBuilder = QueryFieldBuilder.create()
-                        .add(FormConstant.ID_KEY)
-                        .addIdNumberNameWithExtras(new String[]{"rankentry", "rank"},FormConstant.INDEX_KEY);
-                DynamicObjectCollection salaryStandardColl = QueryServiceHelper.query("hcdm_salarystandard", salaryStandFieldBuilder.buildSelect(), new QFilter[]{filter});
-                Map<Long, DynamicObject> salaryStandardMap = salaryStandardColl.stream()
-                        .collect(Collectors.toMap(
-                                obj -> obj.getLong(FormConstant.ID_KEY),
-                                obj -> obj
-                        ));
+        try {
+            Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, FormConstant.POS_STANDARD_ITEM_NUMBER);
+            logger.info("Retrieved salary item ID: {}", salaryItemId);
+            
+            Map<Long, BigDecimal> amountMap = new HashMap<>(e.getDataEntities().length);
+            if (!allPersonIds.isEmpty() && !jobLevelIds.isEmpty()) {
+                logger.info("Processing salary adjustment records for {} person IDs", allPersonIds.size());
+                
+                //获取人员最新岗位工资标准定薪记录
+                List<AdjFileServiceHelper.SalaryAdjustmentResult> salaryAdjustmentResultList = AdjFileServiceHelper.getLastDecAdjRecords(allPersonIds, FormConstant.POS_STANDARD_ITEM_NUMBER);
+                logger.info("Retrieved {} salary adjustment results", salaryAdjustmentResultList.size());
+                
+                if (!salaryAdjustmentResultList.isEmpty()) {
+                    //薪酬标准ID
+                    List<Long> salaryStIds = salaryAdjustmentResultList.stream().map(result -> result.salaryStDv.getLong(FormConstant.ID_KEY)).collect(Collectors.toList());
+                    logger.info("Retrieved salary standard IDs: {}", salaryStIds.size());
+                    
+                    QFilter filter = new QFilter(FormConstant.ID_KEY, QCP.in, salaryStIds)
+                            .and(String.join(".", "rankentry", "rank", FormConstant.NUMBER_KEY), QCP.equals, "01");
+                    //获取标准表中01档的薪档
+                    QueryFieldBuilder salaryStandFieldBuilder = QueryFieldBuilder.create()
+                            .add(FormConstant.ID_KEY)
+                            .addIdNumberNameWithExtras(new String[]{"rankentry", "rank"}, FormConstant.INDEX_KEY);
+                    DynamicObjectCollection salaryStandardColl = QueryServiceHelper.query("hcdm_salarystandard", salaryStandFieldBuilder.buildSelect(), new QFilter[]{filter});
+                    logger.info("Retrieved {} salary standard records", salaryStandardColl.size());
+                    
+                    Map<Long, DynamicObject> salaryStandardMap = salaryStandardColl.stream()
+                            .collect(Collectors.toMap(
+                                    obj -> obj.getLong(FormConstant.ID_KEY),
+                                    obj -> obj
+                            ));
+
+                    if (!salaryStandardMap.isEmpty()) {
+                        logger.info("Created salary standard map with {} entries", salaryStandardMap.size());
+                        List<Map<String,Object>> matchParams = new ArrayList<>();
+                        for (AdjFileServiceHelper.SalaryAdjustmentResult result : salaryAdjustmentResultList) {
+                            Map<String,Object> stdTableDataMatchParam = new HashMap<>();
 
-                if (!salaryStandardMap.isEmpty()) {
-                    List<StdTableDataMatchParam> matchParams = new ArrayList<>();
-                    for (AdjFileServiceHelper.SalaryAdjustmentResult result : salaryAdjustmentResultList) {
-                        StdTableDataMatchParam stdTableDataMatchParam = new StdTableDataMatchParam();
-                        stdTableDataMatchParam.setStdTableId(result.salaryStDv.getLong(FormConstant.ID_KEY));
-                        stdTableDataMatchParam.setStdItemId(FormConstant.STANDARDITEM_ID_KEY);
-                        stdTableDataMatchParam.setGradeId(result.salaryGrade.getLong(FormConstant.ID_KEY));
-                        DynamicObject dynamicObject = salaryStandardMap.get(result.salaryStDv.getLong(FormConstant.ID_KEY));
-                        if(dynamicObject != null) {
-                            long rankId = dynamicObject.getLong(String.join(".", "rankentry", "rank", FormConstant.ID_KEY));
-                            stdTableDataMatchParam.setRankId(rankId);
-                            matchParams.add(stdTableDataMatchParam);
+                            stdTableDataMatchParam.put("stdTableId",result.salaryStDv.getLong(FormConstant.ID_KEY));
+                            stdTableDataMatchParam.put("stdItemId",salaryItemId);
+                            stdTableDataMatchParam.put("gradeId",result.salaryGrade.getLong(FormConstant.ID_KEY));
+                            DynamicObject dynamicObject = salaryStandardMap.get(result.salaryStDv.getLong(FormConstant.ID_KEY));
+                            if (dynamicObject != null) {
+                                long rankId = dynamicObject.getLong(String.join(".", "rankentry", "rank", FormConstant.ID_KEY));
+                                stdTableDataMatchParam.put("rankId",rankId);
+                                matchParams.add(stdTableDataMatchParam);
+                            }
                         }
-                    }
-                    if(!matchParams.isEmpty()) {
-                        //获取薪酬项目、薪等、薪档对应金额(入参params的数组下标和出参的数组下标一一对应)
-                        List<StdTableDataMatchResult> stdTableDataMatchResults = HCDMSalaryStdServiceHelper.matchStdTableData(matchParams);
-                        for (int i = 0; i < salaryAdjustmentResultList.size(); i++) {
-                            AdjFileServiceHelper.SalaryAdjustmentResult result = salaryAdjustmentResultList.get(i);
-                            if (i < stdTableDataMatchResults.size() && stdTableDataMatchResults.get(i) != null) {
-                                //当前薪等01档的金额
-                                amountMap.put(result.employee.getLong(FormConstant.ID_KEY), stdTableDataMatchResults.get(i).getAmount());
+                        logger.info("Created {} match parameters", matchParams.size());
+                        
+                        if (!matchParams.isEmpty()) {
+                            //获取薪酬项目、薪等、薪档对应金额(入参params的数组下标和出参的数组下标一一对应)
+//                            List<StdTableDataMatchResult> stdTableDataMatchResults = HCDMSalaryStdServiceHelper.matchStdTableData(matchParams);
+                            List<Map<String,Object>> stdTableDataMatchResults = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "matchStdTableData", matchParams);
+                            logger.info("Retrieved {} standard table match results", stdTableDataMatchResults.size());
+                            
+                            for (int i = 0; i < salaryAdjustmentResultList.size(); i++) {
+                                AdjFileServiceHelper.SalaryAdjustmentResult result = salaryAdjustmentResultList.get(i);
+                                if (i < stdTableDataMatchResults.size() && stdTableDataMatchResults.get(i) != null) {
+                                    //当前薪等01档的金额
+                                    amountMap.put(result.employee.getLong(FormConstant.ID_KEY), ConvertUtil.toBigDecimal(stdTableDataMatchResults.get(i).get("amount")));
+                                }
                             }
+                            logger.info("Populated amount map with {} entries", amountMap.size());
                         }
+                    } else {
+                        logger.warn("未获取薪酬标准表中01档的薪档数据,薪酬标准ID:{}", salaryStIds);
                     }
                 } else {
-                    logger.warn("未获取薪酬标准表中01档的薪档数据,薪酬标准ID:{}",salaryStIds);
+                    logger.warn("未获取到人员岗位工资标准定薪记录,人员ID:{}", allPersonIds);
                 }
-            } else {
-                logger.warn("未获取到人员岗位工资标准定薪记录,人员ID:{}",allPersonIds);
             }
-        }
-        //====================================== 获取当前人员所在定调的岗位标准工资一档金额 end ======================================
-        Map<Long, DynamicObject> jobLevelMap = new HashMap<>(e.getDataEntities().length);
-        if(!jobLevelIds.isEmpty()) {
-            MainEntityType jobLevelEntityType = EntityMetadataCache.getDataEntityType(FormConstant.HBJM_JOBLEVELHR);
-            //重新加载职级信息,避免获取不到系数
-            DynamicObject[] load = BusinessDataServiceHelper.load(jobLevelIds.toArray(new Long[0]), jobLevelEntityType);
-            jobLevelMap = Arrays.stream(load)
-                    .collect(Collectors.toMap(
-                            obj -> obj.getLong(FormConstant.ID_KEY),
-                            obj -> obj
-                    ));
-        }
+            //====================================== 获取当前人员所在定调的岗位标准工资一档金额 end ======================================
+            Map<Long, DynamicObject> jobLevelMap = new HashMap<>(e.getDataEntities().length);
+            if (!jobLevelIds.isEmpty()) {
+                logger.info("Loading job level information for {} IDs", jobLevelIds.size());
+                MainEntityType jobLevelEntityType = EntityMetadataCache.getDataEntityType(FormConstant.HBJM_JOBLEVELHR);
+                //重新加载职级信息,避免获取不到系数
+                DynamicObject[] load = BusinessDataServiceHelper.load(jobLevelIds.toArray(new Long[0]), jobLevelEntityType);
+                jobLevelMap = Arrays.stream(load)
+                        .collect(Collectors.toMap(
+                                obj -> obj.getLong(FormConstant.ID_KEY),
+                                obj -> obj
+                        ));
+                logger.info("Loaded {} job level records", jobLevelMap.size());
+            }
 
-        for (DynamicObject dataEntity : e.getDataEntities()) {
-            long jobLevelId = dataEntity.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.ID_KEY));
-            DynamicObject jobLevel = jobLevelMap.get(jobLevelId);
-            if(jobLevel != null){
-                DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
-                long personId = person.getLong(FormConstant.ID_KEY);
-                //当前岗位工资01档金额
-                BigDecimal amount = amountMap.get(personId);
-                if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
-                    BigDecimal coefficient = jobLevel.getBigDecimal(PositionStructureConstant.NCKD_COEFFICIENT);
-                    if(coefficient.compareTo(BigDecimal.ZERO) > 0) {
-                        dataEntity.set(PositionStructureConstant.NCKD_COEFFICIENT, coefficient);
-                        dataEntity.set(PositionStructureConstant.NCKD_CURRENTPOSTSALARY, amount);
-                        dataEntity.set(PositionStructureConstant.NCKD_POSTALLOWANCE, coefficient.multiply(amount));
+            logger.info("Processing {} data entities for coefficient and salary calculation", e.getDataEntities().length);
+            for (DynamicObject dataEntity : e.getDataEntities()) {
+                long jobLevelId = dataEntity.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.ID_KEY));
+                DynamicObject jobLevel = jobLevelMap.get(jobLevelId);
+                if (jobLevel != null) {
+                    DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+                    long personId = person.getLong(FormConstant.ID_KEY);
+                    //当前岗位工资01档金额
+                    BigDecimal amount = amountMap.get(personId);
+                    if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
+                        BigDecimal coefficient = jobLevel.getBigDecimal(PositionStructureConstant.NCKD_COEFFICIENT);
+                        if (coefficient.compareTo(BigDecimal.ZERO) > 0) {
+                            dataEntity.set(PositionStructureConstant.NCKD_COEFFICIENT, coefficient);
+                            dataEntity.set(PositionStructureConstant.NCKD_CURRENTPOSTSALARY, amount);
+                            dataEntity.set(PositionStructureConstant.NCKD_POSTALLOWANCE, coefficient.multiply(amount));
+                            logger.info("Updated person ID: {}, coefficient: {}, current post salary: {}, post allowance: {}", 
+                                personId, coefficient, amount, coefficient.multiply(amount));
+                        }
+                    } else {
+                        logger.warn("未获取到员工【{}】最新岗位工资标准定薪记录01档的薪等金额,人员ID:{}", person.getString(FormConstant.NAME_KEY), personId);
                     }
-                } else {
-                    logger.warn("未获取到员工【{}】最新岗位工资标准定薪记录01档的薪等金额,人员ID:", person.getString(FormConstant.NAME_KEY));
                 }
             }
+            logger.info("beginOperationTransaction completed successfully");
+        }catch (Exception e1){
+            logger.error("获取人员岗位工资标准定薪记录01档的薪等金额异常", e1);
         }
     }
 

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

@@ -15,6 +15,8 @@ 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.logging.Log;
+import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
@@ -30,6 +32,7 @@ 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.hstu.business.helper.HonorStudentHelper;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.plugin.operate.performance.validate.PerfRankMgmtSaveValidate;
@@ -60,6 +63,8 @@ import java.util.stream.Collectors;
 */
 public class PerfRankMgmtSaveOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
 
+    private static final Log logger = LogFactory.getLog(PerfRankMgmtSaveOpPlugin.class);
+
     @Override
     public void onPreparePropertys(PreparePropertysEventArgs e) {
         e.getFieldKeys().addAll(this.billEntityType.getAllFields().keySet());
@@ -214,11 +219,17 @@ public class PerfRankMgmtSaveOpPlugin extends AbstractOperationServicePlugIn imp
                 DynamicObject empPosOrg = empPosOrgRelByEmployeesMap.get(person.getLong(FormConstant.ID_KEY));
                 if(entry.getDynamicObject(FormConstant.NCKD_DEP) == null){
                     //赋值组织
-                    entry.set(FormConstant.NCKD_DEP,empPosOrg.get(FormConstant.ADMINORG));
+                    if(empPosOrg != null) {
+                        entry.set(FormConstant.NCKD_DEP, empPosOrg.get(FormConstant.ADMINORG));
+                    }else{
+                        logger.warn("【{}】人员没有任职信息", person.getLong(FormConstant.ID_KEY));
+                    }
                 }
                 if(entry.getDynamicObject(PositionStructureConstant.NCKD_POSITIONHR) == null){
                     //赋值岗位
-                    entry.set(PositionStructureConstant.NCKD_POSITIONHR,empPosOrg.get(FormConstant.POSITION_KEY));
+                    if(empPosOrg != null) {
+                        entry.set(PositionStructureConstant.NCKD_POSITIONHR, empPosOrg.get(FormConstant.POSITION_KEY));
+                    }
                 }
 
                 //生成R排名

+ 20 - 12
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/task/PsmsAdjustSalaryTask.java

@@ -18,10 +18,10 @@ import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.schedule.executor.AbstractTask;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.DispatchServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
-import kd.sdk.swc.hcdm.business.helper.HCDMApplyBillServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.psms.AdjustTypeEnum;
 import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
@@ -30,6 +30,7 @@ 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.base.swc.helper.AdjFileServiceHelper;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 
@@ -62,7 +63,14 @@ public class PsmsAdjustSalaryTask extends AbstractTask implements Plugin {
     @Override
     public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
         logger.info("开始执行职位体系推送薪酬任务, 参数: {}", JSON.toJSONString(map));
-        
+        Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, "jt008");
+        Long billTypeId = EntityHelper.getIdByNumber(FormConstant.BOS_BILLTYPE, FormConstant.HCDM_APPLYBILL_BT_ADJ);
+        Long countryId = EntityHelper.getIdByNumber(FormConstant.BD_COUNTRY, "001");
+        Long salaryAdjuStrSnId = EntityHelper.getIdByNumber(FormConstant.HSBS_SALARYADJUSTRSN, "zhijibiandong");
+        Long currencyId = EntityHelper.getIdByNumber(FormConstant.BD_CURRENCY, "CNY");
+        Long adjApproveScmId = EntityHelper.getLatestIdByNumber(FormConstant.HCDM_ADJAPPROVESCM, "dingtiaoxin");
+        Long exRateTableId = EntityHelper.getIdByNumber(FormConstant.BD_EXRATETABLE, "ERT-01");
+        Long calFrequencyId = EntityHelper.getIdByNumber(FormConstant.HSBS_CALFREQUENCY, "000_m_001");
         //1:为本月数据,2:为上月数据
         Integer type = ConvertUtil.toInt(map.get("type"));
         Date beginDateParam = ConvertUtil.toDate(map.get("beginDateParam"));
@@ -172,7 +180,7 @@ public class PsmsAdjustSalaryTask extends AbstractTask implements Plugin {
         if(!needAdjustSalaryId.isEmpty()){
             logger.info("开始获取人员最新岗位工资标准定薪记录");
             //获取人员最新岗位工资标准定薪记录
-            List<AdjFileServiceHelper.SalaryAdjustmentResult> salaryAdjustmentResultList = AdjFileServiceHelper.getLastDecAdjRecords(needPersonId, FormConstant.STANDARDITEM_ID_KEY);
+            List<AdjFileServiceHelper.SalaryAdjustmentResult> salaryAdjustmentResultList = AdjFileServiceHelper.getLastDecAdjRecords(needPersonId, FormConstant.POS_STANDARD_ITEM_NUMBER);
             Map<Long, List<AdjFileServiceHelper.SalaryAdjustmentResult>> salaryAdjustmentResultMap = salaryAdjustmentResultList.stream()
                     .collect(Collectors.groupingBy(result -> result.employee.getLong(FormConstant.ID_KEY)));
             
@@ -230,19 +238,19 @@ public class PsmsAdjustSalaryTask extends AbstractTask implements Plugin {
                                 applyBill.put("org", orgId);
                             }
                             //定调薪明细字段显示方案   调薪明细字段
-                            applyBill.put("billtype", 2215975998602655744L);
+                            applyBill.put("billtype", billTypeId);
                             //国家
-                            applyBill.put("billcountry", 1000001L);
+                            applyBill.put("billcountry", countryId);
                             //定调薪类型
-                            applyBill.put("salaryadjrsn", 2352338490244480000L);
+                            applyBill.put("salaryadjrsn", salaryAdjuStrSnId);
                             //默认币种
-                            applyBill.put("billcurrency", 1L);
+                            applyBill.put("billcurrency", currencyId);
                             //定调薪方案
-                            applyBill.put("salaryadjscm", 2322515162646457344L);
+                            applyBill.put("salaryadjscm", adjApproveScmId);
                             //汇率日期
                             applyBill.put("exchangeratedate", new Date());
                             //汇率表
-                            applyBill.put("exctable", 2321965096026258432L);
+                            applyBill.put("exctable", exRateTableId);
                             //默认生效日期
                             applyBill.put("effectivedate", date);
                             //草稿状态
@@ -274,8 +282,8 @@ public class PsmsAdjustSalaryTask extends AbstractTask implements Plugin {
                             Long positionId = personPosFile.getLong(String.join(".",PositionStructureConstant.NCKD_POSITIONHR, FormConstant.ID_KEY));
                             applyBillEntry.put("adjfile", salaryAdjustmentResult.adjFileInfo.getLong(FormConstant.ID_KEY));
                             applyBillEntry.put("employee", employeeId);
-                            applyBillEntry.put("standarditem", 2321901533681170432L);    //定调薪项目   职位系数
-                            applyBillEntry.put("frequency", 1095454108284088320L);       //频度  月
+                            applyBillEntry.put("standarditem", salaryItemId);    //定调薪项目   职位系数
+                            applyBillEntry.put("frequency", calFrequencyId);       //频度  月
                             applyBillEntry.put("amount", coefficient);
                             applyBillEntry.put("position", positionId);
                             applyBillEntry.put("joblevel", jobLevel.getLong(FormConstant.ID_KEY));
@@ -291,7 +299,7 @@ public class PsmsAdjustSalaryTask extends AbstractTask implements Plugin {
                             papams.put("isUseMatchAmount", Boolean.TRUE);
                             
                             logger.info("准备推送员工 {} 的定调薪申请单", person.getString(FormConstant.NAME_KEY));
-                            Map<String, Object> result = HCDMApplyBillServiceHelper.saveDraftApplyBill(papams);
+                            Map<String, Object> result = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "saveDraftApplyBill", papams);
                             if (!ConvertUtil.toBoolean(result.get("success")) || result.get("data") == null || ConvertUtil.toList(result.get("data")).isEmpty()) {
                                 logger.error("【{}】推送定调薪失败,原因:{}", person.getString(FormConstant.NAME_KEY), JSON.toJSONString(result));
                                 throw new ValidationException("【"+person.getString(FormConstant.NAME_KEY)+"】推送定调薪失败,原因:" + JSON.toJSONString(result));

+ 4 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/common/SanDingConstant.java

@@ -65,12 +65,16 @@ public class SanDingConstant extends FormConstant {
     public static final String NCKD_ISCOMPLATED_KEY = "nckd_iscomplated";
     /** 上一批次适用生效日期 */
     public static final String NCKD_STARTDATE_LAST_KEY = "nckd_startdate_last";
+    /** 原定员数 */
+    public static final String NCKD_ORI_ASR_KEY = "nckd_ori_asr";
     /** 定员数 */
     public static final String NCKD_AUTHORIZEDSTRENGTH_KEY = "nckd_authorizedstrength";
     /** 缺编人数 */
     public static final String NCKD_STAFFINGSHORTFALL_KEY = "nckd_staffingshortfall";
     /** 附件 */
     public static final String ATTACHMENTPANEL_KEY = "attachmentpanel";
+    /** 提交人 */
+    public static final String NCKD_SUBMITTER_KEY = "nckd_submitter";
 
     /** 行政组织.ID */
     public static final String HAOS_ADMINORGHRF7_ID = "haos_adminorghrf7.id";

+ 27 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/plugin/form/SanDingTaskFormPlugin.java

@@ -1,9 +1,14 @@
 package nckd.jxccl.hr.sdm.plugin.form;
 
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.form.control.AttachmentPanel;
+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.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
 import nckd.jxccl.hr.sdm.common.SanDingConstant;
 
@@ -29,4 +34,26 @@ public class SanDingTaskFormPlugin extends AbstractFormPlugin {
             }
         }
     }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        switch (operateKey) {
+            case SanDingConstant.SUBMIT_OP:
+                setSubmitter(afterDoOperationEventArgs);
+        }
+    }
+
+    private void setSubmitter(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        OperationResult operationResult = afterDoOperationEventArgs.getOperationResult();
+        if (!operationResult.isSuccess() || operationResult.getSuccessPkIds().isEmpty()) {
+            return;
+        }
+
+        DynamicObject dataEntity = getModel().getDataEntity();
+        dataEntity.set(SanDingConstant.NCKD_SUBMITTER_KEY, UserServiceHelper.getCurrentUserId());
+        SaveServiceHelper.save(new DynamicObject[]{dataEntity});
+    }
 }

+ 3 - 1
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/sdm/plugin/operate/SendTaskOpPlugin.java

@@ -318,7 +318,7 @@ public class SendTaskOpPlugin extends AbstractOperationServicePlugIn {
             }
         }
         // 处理成 key->岗位BOID@年 value->数量
-        Map<String, Integer> kgsddEmpPosOrgRelMap = kgsddEmpPosOrgRelIdListMap.entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().size()));
+        Map<String, Integer> kgsddEmpPosOrgRelMap = kgsddEmpPosOrgRelIdListMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().size()));
         // key->单位BOID@岗位BOID value->定员数
         Map<String, Integer> lastSanDingTaskMap = new HashMap<String, Integer>();
         DynamicObject lastSanDingPlanDyo = SdmBusinessService.getLastSanDingPlanDyo(billDyo.getLong(SanDingConstant.ID_KEY));
@@ -382,6 +382,8 @@ public class SendTaskOpPlugin extends AbstractOperationServicePlugIn {
                     sanDingTaskEntryDyo.set(SanDingConstant.NCKD_ADMINORG, EntityHelper.newEntity(SanDingConstant.ADMINORG_ENTITYID, positionOrgMap.get("adminOrgSourceVid")));
                     // 设置 岗位
                     sanDingTaskEntryDyo.set(SanDingConstant.NCKD_POSITION_KEY, EntityHelper.newEntity(SanDingConstant.HBPM_POSITIONHR, positionOrgMap.get("positionSourceVid")));
+                    // 设置 原定员数
+                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_ORI_ASR_KEY, lastSanDingTaskMap.getOrDefault(entryDyo.getLong(companyBoIdKey)+"@"+positionBoId, 0));
                     // 设置 定员数
                     sanDingTaskEntryDyo.set(SanDingConstant.NCKD_AUTHORIZEDSTRENGTH_KEY, lastSanDingTaskMap.getOrDefault(entryDyo.getLong(companyBoIdKey)+"@"+positionBoId, 0));
                     // 设置 实际占编人数

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillAfterBindDataService.java

@@ -19,7 +19,7 @@ import kd.bos.orm.util.CollectionUtils;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRArrayUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;;
+import nckd.jxccl.base.common.utils.PatternUtil;;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;

+ 3 - 3
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBeforeBindDataService.java

@@ -1,6 +1,7 @@
 package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import kd.bos.common.enums.EnableEnum;
+import kd.bos.consts.OrgViewTypeConst;
 import kd.bos.context.RequestContext;
 import kd.bos.form.IFormView;
 import kd.bos.form.container.Container;
@@ -9,7 +10,6 @@ import kd.bos.logging.LogFactory;
 import kd.bos.permission.api.HasPermOrgResult;
 import kd.bos.servicehelper.model.PermissionStatus;
 import kd.bos.servicehelper.org.OrgUnitServiceHelper;
-import kd.bos.servicehelper.org.OrgViewType;
 import kd.bos.servicehelper.permission.PermissionServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
 import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
@@ -63,7 +63,7 @@ public class PositionBillBeforeBindDataService extends PositionBillBaseService {
             if (businessUnit != null) {
                 getModel().setValue(PositionBillConstant.ORG_KEY, businessUnit);
             } else if (!Boolean.TRUE.toString().equals(getView().getFormShowParameter().getCustomParam(PositionBillConstant.CP_ISFROMBILLCLICK))) {
-                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), "21", PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewTypeConst.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {
@@ -78,7 +78,7 @@ public class PositionBillBeforeBindDataService extends PositionBillBaseService {
             getView().cacheFormShowParameter();
         } else if (Boolean.TRUE.toString().equals(getView().getPageCache().get(PositionBillConstant.PC_ISFROMIMPORT)) || getView().getParentView() != null && (PositionBillConstant.NCKD_POSTMGR_APPHOME_ENTITY.equals(getView().getParentView().getEntityId()) || getView().getParentView().getEntityId().startsWith("wf_"))) {
             if (getView().getParentView() != null && PositionBillConstant.NCKD_POSTMGR_APPHOME_ENTITY.equals(getView().getParentView().getEntityId()) && getView().getPageCache().get(PositionBillConstant.PC_ISFROMHOMEPAGE) == null) {
-                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), "21", PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewTypeConst.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillClosedCallBackService.java

@@ -16,7 +16,7 @@ import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRDyObjectPropUtil;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;;
+import nckd.jxccl.base.common.utils.PatternUtil;;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
 

+ 14 - 12
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillConfirmCallBackService.java

@@ -19,7 +19,7 @@ import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.base.common.utils.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
 
@@ -214,16 +214,18 @@ public class PositionBillConfirmCallBackService extends PositionBillBaseService
             transKeyMap.putAll(PositionBillServiceHelper.getPosBillEntryTransKeyMap());
             // 获取岗位要转换的键值
             transKeyMap.putAll(PositionBillServiceHelper.getPositionTransKeyMap());
-            // 排除字段
-            List<String> positionExcludeKeyList = PositionBillServiceHelper.getPositionExcludeKeyList();
-            positionExcludeKeyList.forEach(transKeyMap::remove);
+            // 获取岗位所有属性
+            Set<String> positionProperties = PositionBillServiceHelper.getPositionProperties();
 
             QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create();
             for(IDataEntityProperty iDataEntityProperty : entryEntityInfo) {
                 String propName = iDataEntityProperty.getName();
                 if (PatternUtil.isExProperty(propName) && propName.endsWith(lineSuffix)) {
                     String selectName = PositionBillServiceHelper.getNoLineSuffixProp(propName, lineSuffix);
-                    queryFieldBuilder.add(transKeyMap.getOrDefault(selectName, selectName));
+                    selectName = transKeyMap.getOrDefault(selectName, selectName);
+                    if (positionProperties.contains(selectName)) {
+                        queryFieldBuilder.add(selectName);
+                    }
                 }
             }
 
@@ -238,14 +240,14 @@ public class PositionBillConfirmCallBackService extends PositionBillBaseService
                 String propName = iDataEntityProperty.getName();
                 if (PatternUtil.isExProperty(propName) && propName.endsWith(lineSuffix)) {
                     String selectName = PositionBillServiceHelper.getNoLineSuffixProp(propName, lineSuffix);
-                    if (positionExcludeKeyList.contains(selectName) || HRStringUtils.equals(selectName, PositionBillConstant.NCKD_POSDUTY_ENTRY_ENTITY_KEY)) {
-                        continue;
-                    }
-                    // 原上级岗位特殊处理
-                    if (HRStringUtils.equals(selectName, PositionBillConstant.NCKD_PARENT)) {
-                        entryEntityDyn.set(PositionBillConstant.NCKD_ORIPARENT_KEY+lineSuffix, position.get(transKeyMap.getOrDefault(selectName, selectName)));
+                    selectName = transKeyMap.getOrDefault(selectName, selectName);
+                    if (positionProperties.contains(selectName)) {
+                        // 原上级岗位特殊处理
+                        if (HRStringUtils.equals(selectName, PositionBillConstant.NCKD_PARENT)) {
+                            entryEntityDyn.set(PositionBillConstant.NCKD_ORIPARENT_KEY+lineSuffix, position.get(selectName));
+                        }
+                        entryEntityDyn.set(propName, position.get(selectName));
                     }
-                    entryEntityDyn.set(propName, position.get(transKeyMap.getOrDefault(selectName, selectName)));
                 }
             }
 

+ 245 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillEffectService.java

@@ -0,0 +1,245 @@
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.dataentity.metadata.clr.DataEntityPropertyCollection;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.IOperateInfo;
+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.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.service.operation.OperationServiceImpl;
+import kd.hr.hbp.business.service.history.util.HisModelCopyUtil;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRDynamicObjectUtils;
+import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位申请单生效服务
+ * @from: kd.hr.homs.business.service.batcheffect.OrgBillBatchEffectService
+ * @author: jtd
+ * @date: 2026/1/10 15:49
+ */
+public class PositionBillEffectService {
+    private static final Log log = LogFactory.getLog(PositionBillEffectService.class);
+
+    HRBaseServiceHelper positionHelper = new HRBaseServiceHelper(PositionBillConstant.HBPM_POSITIONHR);
+
+    public void billEffect(DynamicObject[] bills) {
+        for (DynamicObject bill : bills) {
+            Date effDate = bill.getDate(PositionBillConstant.NCKD_EFFDT);
+            if (effDate == null) {
+                throw new KDBizException("生效日期不能为空");
+            }
+
+            long billId = bill.getLong(PositionBillConstant.ID_KEY);
+            Map<String, List<DynamicObject>> billEntryPositionDyMap = getBillEntryPositionDyMap(billId);
+            Map<Long, DynamicObject> positionBoIdMap = getAllCurrentVersionParentDyMap(billEntryPositionDyMap);
+            newAddPositionEffect(billEntryPositionDyMap.get(PositionChangeTypeEnum.ADD.getNumber()), billId, effDate);
+            confirmChangePositionEffect(billEntryPositionDyMap.get(PositionChangeTypeEnum.CHANGE.getNumber()), positionBoIdMap, billId, effDate);
+            auditBill(billId);
+        }
+    }
+
+    private void confirmChangePositionEffect(List<DynamicObject> confirmChangePositionEntity, Map<Long, DynamicObject> positionBoIdMap, long billId, Date effDate) {
+        if (confirmChangePositionEntity != null && !confirmChangePositionEntity.isEmpty()) {
+            Map<Long, DynamicObject> dbPositionMap = getDbDyMap(confirmChangePositionEntity);
+            List<DynamicObject> confirmChangePositionDys = Lists.newArrayListWithExpectedSize(confirmChangePositionEntity.size());
+            HisModelCopyUtil hisModelCopyUtil = new HisModelCopyUtil();
+
+            // 获取 行政组织业务ID
+            List<Long> adminorgBoIds = Lists.newArrayListWithExpectedSize(confirmChangePositionEntity.size());
+            for (DynamicObject confirmChangePosition : confirmChangePositionEntity) {
+                adminorgBoIds.add(confirmChangePosition.getLong(PositionBillConstant.NCKD_ADMINORGBOID_KEY));
+            }
+
+            // 根据组织BOID从数据库中获取组织信息
+            DynamicObject[] adminOrgDyos = HRBaseServiceHelper.create(PositionBillConstant.ADMINORG_ENTITYID).loadDynamicObjectArray(new QFilter[]{new QFilter(PositionBillConstant.ID_KEY, QCP.in, adminorgBoIds)});
+            Map<Long, DynamicObject> dbAdminOrgMap = Maps.newHashMapWithExpectedSize(adminOrgDyos.length);
+            for (DynamicObject adminOrgDyo : adminOrgDyos) {
+                dbAdminOrgMap.put(adminOrgDyo.getLong(PositionBillConstant.BOID_KEY), adminOrgDyo);
+            }
+
+            // 获取岗位要转换的键值
+            Map<String, String> positionTransKeyMap = PositionBillServiceHelper.getPositionTransKeyMap();
+            for(DynamicObject confirmChangePosition : confirmChangePositionEntity) {
+                DynamicObject positionDy = confirmChangePosition.getDynamicObject(PositionBillConstant.NCKD_POSITION_KEY);
+                long positionId = positionDy.getLong(PositionBillConstant.BOID_KEY);
+                DynamicObject dbPositionDy = dbPositionMap.get(positionId);
+                if (dbPositionDy != null) {
+                    DynamicObject copyDy = hisModelCopyUtil.copyTempVersionData(dbPositionDy, positionHelper);
+                    HRDynamicObjectUtils.copy(confirmChangePosition, copyDy, positionTransKeyMap);
+                    DynamicObject parentVersionDy = confirmChangePosition.getDynamicObject(PositionBillConstant.NCKD_PARENT);
+                    if (parentVersionDy != null) {
+                        copyDy.set(PositionBillConstant.PARENT_KEY, positionBoIdMap.get(parentVersionDy.getLong(PositionBillConstant.BOID_KEY)));
+                    } else {
+                        copyDy.set(PositionBillConstant.PARENT_KEY, null);
+                    }
+
+                    copyDy.set(PositionBillConstant.BSED_KEY, effDate);
+                    copyDy.set(PositionBillConstant.ENABLE, EnableEnum.YES.getCode());
+                    // 设置 行政组织为业务对象
+                    copyDy.set(PositionBillConstant.ADMINORG, dbAdminOrgMap.get(confirmChangePosition.getLong(PositionBillConstant.NCKD_ADMINORGBOID_KEY)));
+
+                    confirmChangePositionDys.add(copyDy);
+                }
+            }
+
+            if (!confirmChangePositionDys.isEmpty()) {
+                OperateOption operateOption = OperateOption.create();
+                operateOption.setVariableValue(OperateOptionConst.ISHASRIGHT, "true");
+                OperationServiceImpl opImpl = new OperationServiceImpl();
+                operateOption.setVariableValue(OperateOptionConst.SKIPCHECKPERMISSION, "true");
+                OperationResult operationResult = opImpl.localInvokeOperation(PositionBillConstant.CONFIRMCHANGE_OP, confirmChangePositionDys.toArray(new DynamicObject[0]), operateOption);
+                if (!operationResult.isSuccess()) {
+                    StringBuilder errorMsg = new StringBuilder();
+
+                    for(IOperateInfo operateInfo : operationResult.getAllErrorOrValidateInfo()) {
+                        errorMsg.append(operateInfo.getMessage());
+                    }
+
+                    throw new KDBizException(errorMsg.toString());
+                }
+            }
+
+        }
+    }
+
+    private void newAddPositionEffect(List<DynamicObject> newAddPositionEntity, long billId, Date effDate) {
+        if (newAddPositionEntity != null && !newAddPositionEntity.isEmpty()) {
+            Map<Long, DynamicObject> dbPositionMap = getDbDyMap(newAddPositionEntity);
+            List<DynamicObject> addPositionDys = Lists.newArrayListWithExpectedSize(newAddPositionEntity.size());
+
+            for(DynamicObject addPositionEntry : newAddPositionEntity) {
+                DynamicObject positionDy = addPositionEntry.getDynamicObject(PositionBillConstant.NCKD_POSITION_KEY);
+                if (positionDy == null) {
+                    log.error("PositionBillEffectService.newAddPositionEffect positionDy is empty");
+                } else {
+                    long positionId = positionDy.getLong(PositionBillConstant.ID_KEY);
+                    DynamicObject dbPositionDy = dbPositionMap.get(positionId);
+                    if (dbPositionDy == null) {
+                        log.error("PositionBillEffectService.newAddPositionEffect dbPositionDy is empty");
+                    } else {
+                        dbPositionDy.set(PositionBillConstant.BSED_KEY, effDate);
+                        dbPositionDy.set(PositionBillConstant.ISSTANDARDPOS_KEY, "0");
+                        addPositionDys.add(dbPositionDy);
+                    }
+                }
+            }
+
+            if (!addPositionDys.isEmpty()) {
+                OperateOption operateOption = OperateOption.create();
+                operateOption.setVariableValue(OperateOptionConst.ISHASRIGHT, "true");
+                OperationServiceImpl opImpl = new OperationServiceImpl();
+                operateOption.setVariableValue(OperateOptionConst.SKIPCHECKPERMISSION, "true");
+                OperationResult submitResult = opImpl.localInvokeOperation(PositionBillConstant.SAVE_OP, addPositionDys.toArray(new DynamicObject[0]), operateOption);
+                if (!submitResult.isSuccess()) {
+                    StringBuilder errorMsg = new StringBuilder();
+
+                    for(IOperateInfo operateInfo : submitResult.getAllErrorOrValidateInfo()) {
+                        errorMsg.append(operateInfo.getMessage());
+                    }
+
+                    throw new KDBizException(errorMsg.toString());
+                }
+            }
+
+        }
+    }
+
+    private Map<Long, DynamicObject> getDbDyMap(List<DynamicObject> entryDys) {
+        Set<Long> positionIds = Sets.newHashSetWithExpectedSize(entryDys.size());
+
+        for(DynamicObject entryOrg : entryDys) {
+            long positionId = getDyBdPropId(entryOrg, PositionBillConstant.NCKD_POSITION_KEY);
+            positionIds.add(positionId);
+        }
+
+        List<Long> boIds = Arrays.stream(positionHelper.queryOriginalArray(PositionBillConstant.BOID_KEY, new QFilter[]{new QFilter(PositionBillConstant.ID_KEY, QCP.in, positionIds)})).map((dy) -> dy.getLong(PositionBillConstant.BOID_KEY)).collect(Collectors.toList());
+        DynamicObject[] dbPositionDys = positionHelper.loadDynamicObjectArray(new QFilter[]{new QFilter(PositionBillConstant.ID_KEY, QCP.in, boIds)});
+        return Arrays.stream(dbPositionDys).collect(Collectors.toMap((dy) -> dy.getLong(PositionBillConstant.ID_KEY), Function.identity()));
+    }
+
+    // from: kd.hr.haos.business.util.PropertyGetUtils.getDyBdPropId
+    private long getDyBdPropId(DynamicObject dy, String property) {
+        DataEntityPropertyCollection properties = dy.getDataEntityType().getProperties();
+        IDataEntityProperty propertyType = properties.get(property);
+        if (propertyType == null) {
+            if (property.endsWith(".id")) {
+                property = property.substring(0, property.lastIndexOf("."));
+                propertyType = properties.get(property);
+                if (propertyType == null) {
+                    return 0L;
+                }
+            } else {
+                if (!property.endsWith("_id")) {
+                    return 0L;
+                }
+
+                property = property.substring(0, property.lastIndexOf("_"));
+                propertyType = properties.get(property);
+                if (propertyType == null) {
+                    return 0L;
+                }
+            }
+        }
+
+        Object propObj = dy.get(property);
+        if (propObj instanceof Long) {
+            return (Long) propObj;
+        } else {
+            return propObj instanceof DynamicObject ? ((DynamicObject) propObj).getLong("id") : 0L;
+        }
+    }
+
+    private Map<String, List<DynamicObject>> getBillEntryPositionDyMap(Long billId) {
+        HRBaseServiceHelper helper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+        QFilter filter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, billId);
+        DynamicObject[] entries = helper.loadDynamicObjectArray(new QFilter[]{filter});
+        return Arrays.stream(entries).collect(Collectors.groupingBy((dy) -> dy.getString(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))));
+    }
+
+    private Map<Long, DynamicObject> getAllCurrentVersionParentDyMap(Map<String, List<DynamicObject>> entryValues) {
+        List<Long> parentPositionBoIds = Lists.newArrayListWithExpectedSize(10);
+
+        for(Map.Entry<String, List<DynamicObject>> entry : entryValues.entrySet()) {
+            for(DynamicObject entryValue : entry.getValue()) {
+                DynamicObject parentPositionDy = entryValue.getDynamicObject(PositionBillConstant.NCKD_PARENT);
+                if (parentPositionDy != null) {
+                    parentPositionBoIds.add(parentPositionDy.getLong(PositionBillConstant.BOID_KEY));
+                }
+            }
+        }
+
+        DynamicObject[] dbPositionDys = positionHelper.loadDynamicObjectArray(new QFilter[]{new QFilter(PositionBillConstant.ID_KEY, QCP.in, parentPositionBoIds)});
+        return Arrays.stream(dbPositionDys).collect(Collectors.toMap((dy) -> dy.getLong(PositionBillConstant.ID_KEY), Function.identity()));
+    }
+
+    public void auditBill(long billId) {
+        HRBaseServiceHelper serviceHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILL_ENTITY);
+        DynamicObject billDy = serviceHelper.loadOne(PositionBillConstant.BILL_STATUS_KEY, (new QFilter(PositionBillConstant.ID_KEY, QCP.equals, billId)).toArray());
+        if (billDy != null) {
+            billDy.set(PositionBillConstant.BILL_STATUS_KEY, RegBillStatusEnum.APPROVEPASSED.getCode());
+            serviceHelper.updateOne(billDy);
+        }
+
+    }
+}

+ 19 - 14
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillPropertyChangedService.java

@@ -15,12 +15,12 @@ import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.base.common.utils.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * 岗位申请单属性变更服务类
@@ -83,19 +83,24 @@ public class PositionBillPropertyChangedService extends PositionBillBaseService
         transKeyMap.putAll(PositionBillServiceHelper.getPosBillEntryTransKeyMap());
         // 获取岗位要转换的键值
         transKeyMap.putAll(PositionBillServiceHelper.getPositionTransKeyMap());
-        // 排除字段
-        List<String> positionExcludeKeyList = PositionBillServiceHelper.getPositionExcludeKeyList();
-        positionExcludeKeyList.forEach(transKeyMap::remove);
+        // 获取岗位所有属性
+        Set<String> positionProperties = PositionBillServiceHelper.getPositionProperties();
 
         StringBuilder selectSqlBuilder = new StringBuilder();
         for(IDataEntityProperty iDataEntityProperty : entryEntityInfo) {
             String propName = iDataEntityProperty.getName();
             if (PatternUtil.isExProperty(propName) && propName.endsWith(lineSuffix)) {
                 String selectName = PositionBillServiceHelper.getNoLineSuffixProp(propName, lineSuffix);
-                selectSqlBuilder.append(",").append(transKeyMap.getOrDefault(selectName, selectName));
+                selectName = transKeyMap.getOrDefault(selectName, selectName);
+                if (positionProperties.contains(selectName)) {
+                    selectSqlBuilder.append(",").append(selectName);
+                }
             }
         }
-        selectSqlBuilder.deleteCharAt(0);
+
+        if (selectSqlBuilder.length() > 0) {
+            selectSqlBuilder.deleteCharAt(0);
+        }
 
         BasedataProp basedataProp = (BasedataProp) property;
         String baseEntityId = basedataProp.getBaseEntityId();
@@ -122,14 +127,14 @@ public class PositionBillPropertyChangedService extends PositionBillBaseService
                 String propName = iDataEntityProperty.getName();
                 if (PatternUtil.isExProperty(propName) && propName.endsWith(lineSuffix)) {
                     String selectName = PositionBillServiceHelper.getNoLineSuffixProp(propName, lineSuffix);
-                    if (positionExcludeKeyList.contains(selectName) || HRStringUtils.equals(selectName, PositionBillConstant.NCKD_POSDUTY_ENTRY_ENTITY_KEY)) {
-                        continue;
-                    }
-                    // 原上级岗位特殊处理
-                    if (HRStringUtils.equals(selectName, PositionBillConstant.NCKD_PARENT)) {
-                        entryEntityDyn.set(PositionBillConstant.NCKD_ORIPARENT_KEY+lineSuffix, position.get(transKeyMap.getOrDefault(selectName, selectName)));
+                    selectName = transKeyMap.getOrDefault(selectName, selectName);
+                    if (positionProperties.contains(selectName)) {
+                        // 原上级岗位特殊处理
+                        if (HRStringUtils.equals(selectName, PositionBillConstant.NCKD_PARENT)) {
+                            entryEntityDyn.set(PositionBillConstant.NCKD_ORIPARENT_KEY+lineSuffix, position.get(selectName));
+                        }
+                        entryEntityDyn.set(propName, position.get(selectName));
                     }
-                    entryEntityDyn.set(propName, position.get(transKeyMap.getOrDefault(selectName, selectName)));
                 }
             }
 

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillSaveHelper.java

@@ -13,7 +13,7 @@ import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRDynamicObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.base.common.utils.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
 

+ 123 - 10
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillServiceHelper.java

@@ -3,7 +3,6 @@ package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.kingdee.util.StringUtils;
 import kd.bos.bill.BillShowParameter;
 import kd.bos.bill.OperationStatus;
 import kd.bos.common.enums.EnableEnum;
@@ -12,7 +11,13 @@ import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.entity.ILocaleString;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicProperty;
 import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.db.tx.Propagation;
+import kd.bos.db.tx.TX;
+import kd.bos.db.tx.TXHandle;
+import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.datamodel.IDataModel;
 import kd.bos.entity.operate.OperateOptionConst;
@@ -48,13 +53,15 @@ import org.apache.commons.lang3.time.DateUtils;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
- * 岗位申请单帮
+ * 岗位申请单帮
  * @from: kd.hr.homs.business.domain.batchbill.repository.AdminOrgBatchChgHelper
  * @author: jtd
  * @date: 2025-10-31 14:33
@@ -62,6 +69,96 @@ import java.util.stream.Collectors;
 public class PositionBillServiceHelper {
     private static final Log log = LogFactory.getLog(PositionBillServiceHelper.class);
 
+    public static void setChangeDes(List<DynamicObject> changeDyos) {
+        setChangeDes(changeDyos, false);
+    }
+
+    public static void setChangeDes(List<DynamicObject> changeDyos, Boolean isEntry) {
+        String positionKey = isEntry ? String.format("%s_%s.%s", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.CHANGE_TAG, PositionBillConstant.BOID_KEY) : String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.BOID_KEY);
+        List<Long> changeIds = changeDyos.stream().map(changeDyo -> changeDyo.getLong(positionKey)).collect(Collectors.toList());
+
+        // 获取要记录的变更字段
+        Map<String, String> changeFieldMap = Arrays.stream(HRBaseServiceHelper.create(PositionBillConstant.NCKD_PBENTRY_CHANGE_FIELD_ENTITY).queryOriginalArray(PositionBillConstant.NUMBER_KEY, null, PositionBillConstant.INDEX_KEY)).map(changeFieldDy -> changeFieldDy.getString(PositionBillConstant.NUMBER_KEY)).collect(Collectors.toMap(Function.identity(), Function.identity(), (oldValue, newValue) -> newValue, LinkedHashMap::new));
+        // 获取岗位要转换的键值
+        Map<String, String> positionTransKeyMap = getPositionTransKeyMap();
+        for (Map.Entry<String, String> entry : changeFieldMap.entrySet()) {
+            if (positionTransKeyMap.containsKey(entry.getKey())) {
+                changeFieldMap.put(entry.getKey(), positionTransKeyMap.get(entry.getKey()));
+            }
+        }
+
+        if (isEntry) {
+            // 获取分录要转换的键值
+            Map<String, String> posBillEntryReverseTransKeyMap = getPosBillEntryTransKeyMap().entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, (oldValue, newValue) -> newValue));
+            changeFieldMap = changeFieldMap.entrySet().stream().collect(Collectors.toMap(entry -> {
+                String entryChangeField = String.join("_", entry.getKey(), PositionBillConstant.CHANGE_TAG);
+                if (posBillEntryReverseTransKeyMap.containsKey(entry.getKey())) {
+                    entryChangeField = String.join("_", posBillEntryReverseTransKeyMap.get(entry.getKey()), PositionBillConstant.CHANGE_TAG);
+                }
+                return entryChangeField;
+            }, Map.Entry::getValue, (oldValue, newValue) -> newValue, LinkedHashMap::new));
+        }
+
+        Map<String, IDataEntityProperty> positionAllFields = EntityMetadataCache.getDataEntityType(PositionBillConstant.HBPM_POSITIONHR).getAllFields();
+        Map<Long, DynamicObject> dbPositionDyMap = Arrays.stream(HRBaseServiceHelper.create(PositionBillConstant.HBPM_POSITIONHR).loadDynamicObjectArray(changeIds.toArray())).collect(Collectors.toMap(dbPositionDy -> dbPositionDy.getLong(PositionBillConstant.BOID_KEY), Function.identity()));
+        for (DynamicObject changeDyo : changeDyos) {
+            List<String> changeDesList = new ArrayList<String>();
+            DynamicObject dbPositionDyo = dbPositionDyMap.get(changeDyo.getLong(positionKey));
+            for (Map.Entry<String, String> entry : changeFieldMap.entrySet()) {
+                String positionField = entry.getValue();
+                if (!positionAllFields.containsKey(positionField)) {
+                    log.warn(HRStringUtils.format("PositionBillServiceHelper.setChangeDes not find field '{}' in position.", positionField));
+                    continue;
+                }
+
+                boolean isChange;
+                Object oldValue = dbPositionDyo.get(positionField);
+                Object newValue = changeDyo.get(entry.getKey());
+                if (HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue)) {
+                    isChange = false;
+                } else if ((HRObjectUtils.isEmpty(oldValue) && !HRObjectUtils.isEmpty(newValue)) || (!HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue))) {
+                    isChange = true;
+                } else {
+                    isChange = !HRDynamicObjectUtils.compareValues(oldValue, newValue);
+                }
+
+                if (isChange) {
+                    String fieldName = changeDyo.getDynamicObjectType().getProperty(entry.getKey()).getDisplayName().getLocaleValue();
+                    changeDesList.add(HRStringUtils.format("{}:\"{}\" -> \"{}\"", fieldName, getValue(dbPositionDyo, positionField, oldValue), getValue(changeDyo, entry.getKey(), newValue)));
+                }
+            }
+
+            changeDyo.set(isEntry ? String.join("_", PositionBillConstant.NCKD_CHANGEDES_KEY, PositionBillConstant.CHANGE_TAG) : PositionBillConstant.NCKD_CHANGEDES_KEY, changeDesList.isEmpty() ? null : String.join(System.lineSeparator(), changeDesList));
+        }
+    }
+
+    private static String getValue(DynamicObject dyo, String field, Object val) {
+        if (!HRObjectUtils.isEmpty(val)) {
+            if (val instanceof DynamicObject) {
+                return ((DynamicObject) val).getString(PositionBillConstant.NAME_KEY);
+            } else if (val instanceof DynamicObjectCollection) {
+                if (((DynamicObjectCollection) val).isEmpty()) {
+                    return " ";
+                }
+
+                List<String> list = new ArrayList<String>();
+                for (DynamicObject d : (DynamicObjectCollection) val) {
+                    DynamicProperty baseDataProp = d.getDynamicObjectType().getProperty(PositionBillConstant.FBASEDATAID_KEY);
+                    if (baseDataProp == null) {
+                        list.add(d.getString(PositionBillConstant.NAME_KEY));
+                    } else {
+                        list.add(d.getDynamicObject(PositionBillConstant.FBASEDATAID_KEY).getString(PositionBillConstant.NAME_KEY));
+                    }
+                }
+                return String.join(";", list);
+            } else {
+                return dyo.getString(field);
+            }
+        }
+
+        return " ";
+    }
+
     public static String getNoLineSuffixProp(String prop, String lineSuffix) {
         if (prop.endsWith(lineSuffix)) {
             return prop.substring(0, prop.lastIndexOf(lineSuffix));
@@ -98,10 +195,8 @@ public class PositionBillServiceHelper {
         return transKeyMap;
     }
 
-    public static List<String> getPositionExcludeKeyList() {
-        DynamicObject[] positionExcludeKeyList = HRBaseServiceHelper.create(PositionBillConstant.NCKD_POSITION_EXCLUDEKEY_ENTITY).queryOriginalArray(PositionBillConstant.NUMBER_KEY, null);
-        List<String> excludeKeyList = Arrays.stream(positionExcludeKeyList).map(positionExcludeKeyDyo -> positionExcludeKeyDyo.getString(PositionBillConstant.NUMBER_KEY)).collect(Collectors.toList());
-        return excludeKeyList;
+    public static Set<String> getPositionProperties() {
+        return EntityMetadataCache.getDataEntityType(PositionBillConstant.HBPM_POSITIONHR).getProperties().stream().map(IDataEntityProperty::getName).collect(Collectors.toSet());
     }
 
     /**
@@ -136,7 +231,7 @@ public class PositionBillServiceHelper {
             DynamicObject dynamicObject = positionBillHelper.loadDynamicObject(idFilter);
             if (dynamicObject != null) {
                 String billStatus = dynamicObject.getString(PositionBillConstant.BILL_STATUS_KEY);
-                if (!billStatus.equals(RegBillStatusEnum.TEMPSTORAGE.getCode()) && !billStatus.equals(RegBillStatusEnum.WAITRESUBMIT.getCode()) && !billStatus.equals(RegBillStatusEnum.APPROVING.getCode()) && (!billStatus.equals(RegBillStatusEnum.ALREADYSUBMIT.getCode()) || !StringUtils.equals(iFormView.getFormShowParameter().getAppId(), PositionBillConstant.WFTASK_APP))) {
+                if (!billStatus.equals(RegBillStatusEnum.TEMPSTORAGE.getCode()) && !billStatus.equals(RegBillStatusEnum.WAITRESUBMIT.getCode()) && !billStatus.equals(RegBillStatusEnum.APPROVING.getCode()) && (!billStatus.equals(RegBillStatusEnum.ALREADYSUBMIT.getCode()) || !HRStringUtils.equals(iFormView.getFormShowParameter().getAppId(), PositionBillConstant.WFTASK_APP))) {
                     String operateLocaleName = PositionChangeTypeEnum.getTipByTag(operation);
                     String auditstatusName = RegBillStatusEnum.getName(dynamicObject.getString(PositionBillConstant.BILL_STATUS_KEY));
                     iFormView.showErrorNotification(String.format("“%1$s”的单据不能“%2$s”。", auditstatusName, operateLocaleName));
@@ -177,7 +272,7 @@ public class PositionBillServiceHelper {
         Long userId = RequestContext.get().getCurrUserId();
         String permItemId = PermissionStatus.View;// 查询权限
         String appId = iFormView.getFormShowParameter().getCheckRightAppId();
-        AuthorizedOrgResult permResult = HRMServiceHelper.invokeHRMPService("hrcs", "IHRCSBizDataPermissionService", "getUserAdminOrgsF7", new Object[]{userId, appId, PositionBillConstant.NCKD_POSITIONBILL_ENTITY, permItemId, PositionBillConstant.NCKD_ADMINORGBOID_KEY, Maps.newHashMapWithExpectedSize(0)});
+        AuthorizedOrgResult permResult = HRMServiceHelper.invokeHRMPService(PositionBillConstant.HRCS_APP, "IHRCSBizDataPermissionService", "getUserAdminOrgsF7", new Object[]{userId, appId, PositionBillConstant.NCKD_POSITIONBILL_ENTITY, permItemId, PositionBillConstant.NCKD_ADMINORGBOID_KEY, Maps.newHashMapWithExpectedSize(0)});
         return permResult;
     }
 
@@ -294,8 +389,26 @@ public class PositionBillServiceHelper {
 //        hisDeleteParam.setEntityNumber(PositionBillConstant.HBPM_POSITIONHR);
 //        hisDeleteParam.setDataList(Arrays.asList(HRBaseServiceHelper.create(PositionBillConstant.HBPM_POSITIONHR).loadDynamicObjectArray(positionIdList.toArray())));
 //        HisModelServiceHelper.deleteBo(hisDeleteParam);
-        HRBaseServiceHelper positionHrHelper = new HRBaseServiceHelper(PositionBillConstant.HBPM_POSITIONHR);
-        positionHrHelper.deleteByFilter(new QFilter[]{new QFilter(PositionBillConstant.BOID_KEY, QCP.in, positionIdList)});
+
+        // 删除HR岗位暂存的数据
+        HRBaseServiceHelper.create(PositionBillConstant.HBPM_POSITIONHR).deleteByFilter(new QFilter[]{new QFilter(PositionBillConstant.BOID_KEY, QCP.in, positionIdList)});
+
+        delAddMasterForBos(positionIdList);
+    }
+
+    /**
+     * 删除BOS岗位新设暂存数据
+     * @param positionIdList
+     */
+    public static void delAddMasterForBos(List<Long> positionIdList) {
+        QFilter[] qFilters = {new QFilter(PositionBillConstant.ID_KEY, QCP.in, positionIdList)};
+        if (TX.getPropagation() != Propagation.NOT_SUPPORTED) {
+            try (TXHandle tx = TX.requiresNew()) {
+                HRBaseServiceHelper.create(PositionBillConstant.BOS_POSITION_ENTITY).deleteByFilter(qFilters);
+            }
+        } else {
+            HRBaseServiceHelper.create(PositionBillConstant.BOS_POSITION_ENTITY).deleteByFilter(qFilters);
+        }
     }
 
     public static DynamicObject getFocusEntry(String suffix, IFormView view, int row) {

+ 4 - 2
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionBillConstant.java

@@ -23,10 +23,10 @@ public class PositionBillConstant extends FormConstant {
     public static final String NCKD_POSBILLENTRYCHANGE_ENTITY = "nckd_posbillentrychange";
     /** 岗位申请分录键值转换实体标识 */
     public static final String NCKD_PBENTRY_TRANSKEY_ENTITY = "nckd_pbentry_transkey";
-    /** 岗位信息排除键值实体标识 */
-    public static final String NCKD_POSITION_EXCLUDEKEY_ENTITY = "nckd_position_excludekey";
     /** 岗位管理_应用首页实体标识 */
     public static final String NCKD_POSTMGR_APPHOME_ENTITY = "nckd_postmgr_apphome";
+    /** 岗位分录变更字段-实体标识 */
+    public static final String NCKD_PBENTRY_CHANGE_FIELD_ENTITY = "nckd_pbentry_change_field";
 
     /** 新增标识 */
     public static final String ADD_TAG = "add";
@@ -103,6 +103,8 @@ public class PositionBillConstant extends FormConstant {
     public static final String NCKD_POSBILLENTRY_KEY = "nckd_posbillentry";
     /** 岗位申请单分录 */
     public static final String NCKD_ENTRYENTITYUNDERLINE_KEY = "nckd_entryentity_";
+    /** 变更说明 */
+    public static final String NCKD_CHANGEDES_KEY = "nckd_changedes";
 
     /** 岗位生效时间页面自定义参数 */
     public static final String CP_POSITION_BSED = "position_bsed";

+ 33 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionHRConstant.java

@@ -0,0 +1,33 @@
+package nckd.jxccl.hrmp.hbpm.common.hr;
+
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+ *
+ * @entity:
+ * @author: jtd
+ * @date: 2026/1/4 21:45
+ */
+public class PositionHRConstant extends FormConstant {
+    /** 岗位分配实体标识 */
+    public static final String NCKD_POSITIONASSIGN_ENTITY = "nckd_positionassign";
+    /** 岗位信息键值转换实体标识 */
+    public static final String NCKD_POSASSIGN_TRANSKEY_ENTITY = "nckd_posassign_transkey";
+
+    /** 岗位分配操作标识 */
+    public static final String POSITIONASSIGN_OP = "positionassign";
+
+    /** 选中的数据ID页面自定义参数 */
+    public static final String SELECTED_IDS_CUSTOM_PARAM = "selectedids";
+
+    /** 岗位分配回调标识 */
+    public static final String POSITIONASSIGN_CALLBACK = "positionassign";
+
+    /** 岗位分配.设立日期 */
+    public static final String NCKD_ESTABLISHMENTDATE_KEY = "nckd_establishmentdate";
+    /** 岗位.上级岗位 */
+    public static final String PARENT_KEY = "parent";
+    /** 上级岗位 */
+    public static final String NCKD_PARENT_KEY = "nckd_parent";
+
+}

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryAddFormPlugin.java

@@ -28,7 +28,7 @@ import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.base.common.utils.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PosBillEntryFastChgHelper;
 import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;

+ 9 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryUpdatePlugin.java

@@ -21,6 +21,7 @@ import kd.bos.form.ConfirmTypes;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.MessageBoxResult;
 import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.field.BasedataEdit;
 import kd.bos.form.field.events.BeforeF7SelectEvent;
@@ -41,7 +42,7 @@ import kd.hr.hbp.common.util.HRDynamicObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
 import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PosBillEntryFastChgHelper;
 import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
-import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.base.common.utils.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import org.apache.commons.lang3.time.DateUtils;
 
@@ -386,6 +387,13 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
         }
     }
 
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+
+        PositionBillServiceHelper.setChangeDes(Collections.singletonList(getModel().getDataEntity()));
+    }
+
     public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
         super.afterDoOperation(afterDoOperationEventArgs);
         String key = afterDoOperationEventArgs.getOperateKey();

+ 185 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionAssignFormPlugin.java

@@ -0,0 +1,185 @@
+package nckd.jxccl.hrmp.hbpm.plugin.form.hr;
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.entity.property.EntryProp;
+import kd.bos.form.MessageTypes;
+import kd.bos.form.control.Control;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRDateTimeUtils;
+import kd.hr.hbp.common.util.HRDynamicObjectUtils;
+import kd.hr.hbp.common.util.HRStringUtils;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionHRConstant;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位分配表单插件
+ * @entity: nckd_positionassign
+ * @author: jtd
+ * @date: 2026/1/5 09:40
+ */
+public class PositionAssignFormPlugin extends AbstractFormPlugin {
+
+    /**
+     * 获取岗位分配转换键值
+     * @return 源字段: 目标字段
+     */
+    public static Map<String, String> getPosAssignTransKeyMap() {
+        String selectFields = QueryFieldBuilder.create().add(PositionHRConstant.NUMBER_KEY).add(PositionHRConstant.NAME_KEY).buildSelect();
+        DynamicObject[] transKeyDyos = HRBaseServiceHelper.create(PositionHRConstant.NCKD_POSASSIGN_TRANSKEY_ENTITY).queryOriginalArray(selectFields, null);
+        Map<String, String> transKeyMap = Arrays.stream(transKeyDyos).collect(Collectors.toMap(
+                transKeyDyo -> transKeyDyo.getString(PositionHRConstant.NUMBER_KEY),
+                transKeyDyo -> transKeyDyo.getString(PositionHRConstant.NAME_KEY),
+                (oldValue, newValue) -> newValue)
+        );
+        return transKeyMap;
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Object selectedIds = getView().getFormShowParameter().getCustomParam(PositionHRConstant.SELECTED_IDS_CUSTOM_PARAM);
+        if (selectedIds != null) {
+            DynamicObject[] positionHrDyns = HRBaseServiceHelper.create(PositionHRConstant.HBPM_POSITIONHR).loadDynamicObjectArray(Arrays.stream(String.valueOf(selectedIds).split(",")).map(Long::valueOf).toArray());
+            DynamicObjectCollection entryEntityDynColl = getModel().getDataEntity().getDynamicObjectCollection(PositionHRConstant.NCKD_ENTRYENTITY);
+            // 获取岗位要转换的键值
+            Map<String, String> posAssignTransKeyMap = getPosAssignTransKeyMap();
+            Map<Long, Long> oriAdminOrgData = new HashMap<Long, Long>();
+            for (DynamicObject positionHrDy : positionHrDyns) {
+                DynamicObject entryEntityDy = entryEntityDynColl.addNew();
+                HRDynamicObjectUtils.copy(positionHrDy, entryEntityDy, posAssignTransKeyMap);
+                // 设置ID
+                entryEntityDy.set(PositionHRConstant.ID_KEY, positionHrDy.getLong(PositionHRConstant.ID_KEY));
+                // 设立日期
+                entryEntityDy.set(PositionHRConstant.NCKD_ESTABLISHMENTDATE_KEY, HRDateTimeUtils.getNowDate());
+                oriAdminOrgData.put(positionHrDy.getLong(PositionHRConstant.ID_KEY), positionHrDy.getLong(String.join(".", PositionHRConstant.ADMINORG, PositionHRConstant.ID_KEY)));
+            }
+            getView().updateView(PositionHRConstant.NCKD_ENTRYENTITY);
+
+            getModel().setDataChanged(false);
+        }
+    }
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+
+        addClickListeners(PositionHRConstant.BTN_OK_OP);
+    }
+
+    @Override
+    public void click(EventObject evt) {
+        super.click(evt);
+
+        String key = ((Control) evt.getSource()).getKey();
+        if (HRStringUtils.equals(key, PositionHRConstant.BTN_OK_OP)) {
+            Object[] positionIds = getModel().getDataEntity(true).getDynamicObjectCollection(PositionHRConstant.NCKD_ENTRYENTITY).stream().map(entryEntityDy -> entryEntityDy.getLong(PositionHRConstant.ID_KEY)).toArray();
+            if (positionIds.length == 0) {
+                getView().showMessage("没有数据可执行。");
+            } else {
+                DynamicObject[] positionDyns = HRBaseServiceHelper.create(PositionHRConstant.HBPM_POSITIONHR).loadDynamicObjectArray(positionIds);
+                Map<Long, DynamicObject> positionDyMap = Arrays.stream(positionDyns).collect(Collectors.toMap(positionDy -> positionDy.getLong(PositionHRConstant.ID_KEY), Function.identity()));
+                Boolean needSave = hasDataNeedSave(positionDyMap);
+                if (needSave) {
+                    confirm(positionDyMap);
+                }
+            }
+        }
+    }
+
+    private Boolean hasDataNeedSave(Map<Long, DynamicObject> positionDyMap) {
+        DynamicObjectCollection entryEntityDynColl = getModel().getDataEntity(true).getDynamicObjectCollection(PositionHRConstant.NCKD_ENTRYENTITY);
+        // 获取分录名称
+        EntryProp entryEntityProp = (EntryProp) getModel().getDataEntityType().getProperty(PositionHRConstant.NCKD_ENTRYENTITY);
+        String entryEntityName = entryEntityProp.getDisplayName().getLocaleValue();
+        String adminOrgName = entryEntityProp.getItemType().getProperties().get(PositionHRConstant.NCKD_ADMINORG).getDisplayName().getLocaleValue();
+
+        List<String> errorMsgList = new ArrayList<String>();
+        for (int i = 0; i < entryEntityDynColl.size(); i++) {
+            DynamicObject entryEntityDy = entryEntityDynColl.get(i);
+            Long oriAdminOrg = positionDyMap.get(entryEntityDy.getLong(PositionHRConstant.ID_KEY)).getLong(String.join(".", PositionHRConstant.ADMINORG, PositionHRConstant.ID_KEY));
+            Long newAdminOrg = entryEntityDy.getLong(String.join(".", PositionHRConstant.NCKD_ADMINORG, PositionHRConstant.ID_KEY));
+            // 判断行政组织是否发生变更
+            if (oriAdminOrg.equals(newAdminOrg)) {
+                errorMsgList.add(HRStringUtils.format("请变更“{}”第{}行:“{}”。", new Object[]{entryEntityName, i+1, adminOrgName}));
+            }
+        }
+
+        if (!errorMsgList.isEmpty()) {
+            getView().showMessage(null, String.join(System.lineSeparator(), errorMsgList), MessageTypes.Default);
+            return false;
+        }
+
+        return true;
+    }
+
+    private void confirm(Map<Long, DynamicObject> positionDyMap) {
+        DynamicObjectCollection entryEntityDyoColl = getModel().getDataEntity(true).getDynamicObjectCollection(PositionHRConstant.NCKD_ENTRYENTITY);
+        List<DynamicObject> dataEntities = new ArrayList<DynamicObject>();
+        HRBaseServiceHelper positionServiceHelper = HRBaseServiceHelper.create(PositionHRConstant.HBPM_POSITIONHR);
+        String orgId = "";
+        for (DynamicObject entryEntityDy : entryEntityDyoColl) {
+            DynamicObject newDyo = positionServiceHelper.generateEmptyDynamicObject();
+            HRDynamicObjectUtils.copy(positionDyMap.get(entryEntityDy.getLong(PositionHRConstant.ID_KEY)), newDyo);
+            // 重置ID
+            newDyo.set(PositionHRConstant.ID_KEY, null);
+            newDyo.set(PositionHRConstant.BOID_KEY, null);
+            // 设置行政组织
+            newDyo.set(PositionHRConstant.ADMINORG, entryEntityDy.get(PositionHRConstant.NCKD_ADMINORG));
+            // 设置上级岗位
+            newDyo.set(PositionHRConstant.PARENT_KEY, entryEntityDy.get(PositionHRConstant.NCKD_PARENT_KEY));
+            // 设置编码
+            orgId = newDyo.getString(String.join(".", PositionHRConstant.ORG_KEY, PositionHRConstant.ID_KEY));
+            String positionNumber = CodeRuleServiceHelper.getNumber(PositionHRConstant.HBPM_POSITIONHR, newDyo, orgId);
+            newDyo.set(PositionHRConstant.NUMBER_KEY, positionNumber);
+            // 放入
+            dataEntities.add(newDyo);
+        }
+
+        OperateOption operateOption = OperateOption.create();
+        operateOption.setVariableValue(OperateOptionConst.ISHASRIGHT, Boolean.TRUE.toString());
+        operateOption.setVariableValue(OperateOptionConst.ONLY_VALIDATE, Boolean.TRUE.toString());
+        OperationResult operationResult = OperationServiceHelper.executeOperate(PositionHRConstant.SAVE_OP, PositionBillConstant.HBPM_POSITIONHR, dataEntities.toArray(new DynamicObject[0]), operateOption);
+        List<String> errorMsgList = new ArrayList<String>();
+        if (!operationResult.isSuccess()) {
+            for(IOperateInfo operateInfo : operationResult.getAllErrorOrValidateInfo()) {
+                errorMsgList.add(operateInfo.getMessage());
+            }
+
+            if (HRStringUtils.isNotBlank(operationResult.getMessage())) {
+                errorMsgList.add(operationResult.getMessage());
+            }
+
+            // 回收编码
+            String[] numbers = dataEntities.stream().map(data -> data.getString(PositionHRConstant.NUMBER_KEY)).toArray(String[]::new);
+            CodeRuleServiceHelper.recycleBatchNumber(PositionHRConstant.HBPM_POSITIONHR, dataEntities.toArray(new DynamicObject[0]), orgId, numbers);
+        }
+
+        if (!errorMsgList.isEmpty()) {
+            getView().showMessage(null ,String.join(System.lineSeparator(), errorMsgList), MessageTypes.Default);
+            return;
+        }
+
+        OperationServiceHelper.executeOperate(PositionHRConstant.SAVE_OP, PositionBillConstant.HBPM_POSITIONHR, dataEntities.toArray(new DynamicObject[0]));
+        getView().returnDataToParent("true");
+        getView().close();
+    }
+}

+ 5 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillFormPlugin.java

@@ -6,6 +6,7 @@ import com.google.common.collect.Sets;
 import kd.bos.bill.BillOperationStatus;
 import kd.bos.bill.BillShowParameter;
 import kd.bos.bill.OperationStatus;
+import kd.bos.consts.OrgViewTypeConst;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
@@ -105,7 +106,7 @@ public class PositionBillFormPlugin extends AbstractFormPlugin implements Before
         DynamicObject org = getModel().getDataEntity().getDynamicObject(PositionBillConstant.ORG_KEY);
         if (org == null) {
             // 获取是否有查询权限
-            HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), "21", PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, true);
+            HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewTypeConst.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, true);
             List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
             if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                 if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {
@@ -315,6 +316,9 @@ public class PositionBillFormPlugin extends AbstractFormPlugin implements Before
         }
 
         if (HRStringUtils.equalsAny(operateKey, PositionBillConstant.SAVE_OP, PositionBillConstant.SUBMIT_OP, PositionBillConstant.SUBMITEFFECT_OP)) {
+            PositionBillServiceHelper.setChangeDes(getModel().getDataEntity(true).getDynamicObjectCollection(PositionBillConstant.NCKD_ENTRYENTITY_CHANGE_KEY), true);
+            getView().updateView(PositionBillConstant.NCKD_ENTRYENTITY_CHANGE_KEY);
+
             if (HRStringUtils.equals(operateKey, PositionBillConstant.SUBMIT_OP) || HRStringUtils.equals(operateKey, PositionBillConstant.SUBMITEFFECT_OP)) {
                 if (!executeSaveOperation(operateKey, operate.getOption())) {
                     args.setCancel(true);

+ 2 - 2
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillListPlugin.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.hrmp.hbpm.plugin.form.hr;
 
+import kd.bos.consts.OrgViewTypeConst;
 import kd.bos.context.RequestContext;
 import kd.bos.form.events.FilterContainerSearchClickArgs;
 import kd.bos.form.events.SetFilterEvent;
@@ -7,7 +8,6 @@ import kd.bos.list.events.BeforeShowBillFormEvent;
 import kd.bos.list.plugin.AbstractListPlugin;
 import kd.bos.permission.api.HasPermOrgResult;
 import kd.bos.servicehelper.model.PermissionStatus;
-import kd.bos.servicehelper.org.OrgViewType;
 import kd.bos.servicehelper.permission.PermissionServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
@@ -38,7 +38,7 @@ public class PositionBillListPlugin extends AbstractListPlugin {
                 getView().getPageCache().remove(PositionBillConstant.PC_ISFROMBILLCLICK);
                 e.getParameter().setCustomParam(PositionBillConstant.CP_ISFROMBILLCLICK, Boolean.TRUE.toString());
             } else {
-                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), "21", PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewTypeConst.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {

+ 60 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionHRListPlugin.java

@@ -0,0 +1,60 @@
+package nckd.jxccl.hrmp.hbpm.plugin.form.hr;
+
+import kd.bos.entity.datamodel.ListSelectedRow;
+import kd.bos.form.CloseCallBack;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.ShowType;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionHRConstant;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位信息维护列表插件
+ * @entity: hbpm_positionhr
+ * @author: jtd
+ * @date: 2026/1/4 21:37
+ */
+public class PositionHRListPlugin  extends AbstractListPlugin {
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        switch(afterDoOperationEventArgs.getOperateKey()) {
+            case PositionHRConstant.POSITIONASSIGN_OP:
+                Object[] selectedRowIds = getSelectedRows().stream().map(ListSelectedRow::getPrimaryKeyValue).toArray();
+                if (selectedRowIds.length > 0) {
+                    showPositionAssignFrom(selectedRowIds);
+                } else {
+                    getView().showTipNotification("请选择要执行的数据。");
+                }
+        }
+
+    }
+
+    private void showPositionAssignFrom(Object[] selectedRowIds) {
+        FormShowParameter formShowParameter = new FormShowParameter();
+        formShowParameter.setFormId(PositionHRConstant.NCKD_POSITIONASSIGN_ENTITY);
+        formShowParameter.getOpenStyle().setShowType(ShowType.Modal);
+        formShowParameter.setCustomParam(PositionHRConstant.SELECTED_IDS_CUSTOM_PARAM, Arrays.stream(selectedRowIds).map(String::valueOf).collect(Collectors.joining(",")));
+        formShowParameter.setCloseCallBack(new CloseCallBack(getPluginName(), PositionHRConstant.POSITIONASSIGN_CALLBACK));
+        getView().showForm(formShowParameter);
+    }
+
+    @Override
+    public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
+        super.closedCallBack(closedCallBackEvent);
+
+        switch (closedCallBackEvent.getActionId()) {
+            case PositionHRConstant.POSITIONASSIGN_CALLBACK:
+                Object returnData = closedCallBackEvent.getReturnData();
+                if (returnData != null) {
+                    getView().showSuccessNotification("操作成功");
+                }
+        }
+    }
+}

+ 6 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PosBillEntryNewPositionTempSaveOpPlugin.java

@@ -24,6 +24,7 @@ import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 岗位申请-新增岗位保存操作插件
@@ -147,6 +148,11 @@ public class PosBillEntryNewPositionTempSaveOpPlugin extends AbstractOperationSe
                 positionEntity.set(PositionBillConstant.NUMBER_KEY, dbPositionEntityDy.getString(PositionBillConstant.NUMBER_KEY));
             }
         }
+
+        // 不论成功与否,都调用岗位删除,BOS岗位不需要暂存数据
+        List<Long> positionIds = positionList.stream().map(positionDy -> positionDy.getLong(PositionBillConstant.ID_KEY)).collect(Collectors.toList());
+        PositionBillServiceHelper.delAddMasterForBos(positionIds);
+
         if (!errorMsgList.isEmpty()) {
             throw new KDBizException(String.join(System.lineSeparator(), errorMsgList));
         } else {

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillDeleteOpPlugin.java

@@ -40,7 +40,7 @@ public class PositionBillDeleteOpPlugin extends AbstractOperationServicePlugIn {
         if (datas.length > 0) {
             List<Long> selectedRows = Arrays.stream(datas).map((dyn) -> dyn.getLong(PositionBillConstant.ID_KEY)).collect(Collectors.toList());
             HRBaseServiceHelper positionBillEntryHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
-            QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, selectedRows);
+            QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.in, selectedRows);
             String selectProperties = QueryFieldBuilder.create().add(PositionBillConstant.ID_KEY)
                     .add(PositionBillConstant.NUMBER_KEY)
                     .add(PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY)

+ 123 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillEffectOpPlugin.java

@@ -0,0 +1,123 @@
+package nckd.jxccl.hrmp.hbpm.plugin.operate.hr;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.db.tx.TXHandle;
+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.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.hrmp.hbpm.business.hr.service.impl.PositionBillEffectService;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位申请单生效操作插件
+ * @from: kd.hr.homs.opplugin.web.orgbatch.OrgBatchChgBillEffectOp
+ * @operate: audit
+ * @author: jtd
+ * @date: 2026/1/10 15:39
+ */
+public class PositionBillEffectOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log log = LogFactory.getLog(PositionBillEffectOpPlugin.class);
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+
+        e.getFieldKeys().addAll(billEntityType.getAllFields().keySet());
+    }
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+
+        log.info("PositionBillEffectOpPlugin.beginOperationTransaction start.");
+        String operationKey = e.getOperationKey();
+        DynamicObject[] bills = e.getDataEntities();
+        if (PositionBillConstant.AUDIT_OP.equals(operationKey) || PositionBillConstant.SUBMITEFFECT_OP.equals(operationKey)) {
+            PositionBillEffectService positionBillEffectService = new PositionBillEffectService();
+            positionBillEffectService.billEffect(bills);
+        }
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+
+        DynamicObject[] bills = e.getDataEntities();
+        Set<Long> billIds = Sets.newHashSetWithExpectedSize(bills.length);
+        billIds = null;
+
+        for(DynamicObject bill : bills) {
+            billIds.add(bill.getLong(PositionBillConstant.ID_KEY));
+        }
+
+        HRBaseServiceHelper positionBillEntryEntityHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+        QFilter positionIdFilter = new QFilter(PositionBillConstant.CREATOR_KEY, QCP.equals, 0);
+        QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.in, billIds);
+        positionIdFilter.and(billIdFilter);
+        positionBillEntryEntityHelper.deleteByFilter(positionIdFilter.toArray());
+        QFilter filter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.in, billIds);
+        DynamicObject[] entries = positionBillEntryEntityHelper.loadDynamicObjectArray(new QFilter[]{filter});
+        Map<String, List<DynamicObject>> changeTypeGroupMap = Arrays.stream(entries).collect(Collectors.groupingBy((dy) -> dy.getString(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))));
+        List<DynamicObject> positionDyList = changeTypeGroupMap.get(PositionChangeTypeEnum.ADD.getNumber());
+        if (positionDyList != null && !positionDyList.isEmpty()) {
+            Map<Long, Long> positionBoIdToVidMap = Maps.newHashMapWithExpectedSize(positionDyList.size());
+
+            for(DynamicObject positionDy : positionDyList) {
+                DynamicObject positionBoDy = positionDy.getDynamicObject(PositionBillConstant.NCKD_POSITION_KEY);
+                positionBoIdToVidMap.put(positionBoDy.getLong(PositionBillConstant.ID_KEY), positionBoDy.getLong(PositionBillConstant.SOURCEVID_KEY));
+            }
+
+            List<DynamicObject> updateEntryValue = Lists.newArrayListWithExpectedSize(10);
+
+            for(Map.Entry<String, List<DynamicObject>> entry : changeTypeGroupMap.entrySet()) {
+                for(DynamicObject entryValue : entry.getValue()) {
+                    boolean change = false;
+                    long parentId = entryValue.getLong(String.join(".", PositionBillConstant.NCKD_PARENT, PositionBillConstant.ID_KEY));
+                    if (positionBoIdToVidMap.containsKey(parentId)) {
+                        entryValue.set(PositionBillConstant.NCKD_PARENT, positionBoIdToVidMap.get(parentId));
+                        change = true;
+                    }
+
+                    long positionId = entryValue.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY));
+                    if (positionBoIdToVidMap.containsKey(positionId)) {
+                        entryValue.set(PositionBillConstant.NCKD_POSITION_KEY, positionBoIdToVidMap.get(positionId));
+                        change = true;
+                    }
+
+                    if (change) {
+                        updateEntryValue.add(entryValue);
+                    }
+                }
+            }
+
+            if (!updateEntryValue.isEmpty()) {
+                positionBillEntryEntityHelper.save(updateEntryValue.toArray(new DynamicObject[0]));
+            }
+        }
+
+        TXHandle txHandle = TXHandle.get();
+        log.info("endOperationTransaction_tx_isRollBack_is_{}", txHandle.isRollback());
+    }
+}

+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/business/.gitkeep


+ 22 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/common/AppflgConstant.java

@@ -0,0 +1,22 @@
+/**
+ * This is a kingdee cosmic template project that is automatically generated by the Kingdee cosmic development assistant plugin. 
+ * If there are any issues during the use process, you can provide feedback to the kingdee developer community website.
+ * Website: https://developer.kingdee.com/developer?productLineId=29
+ * Author: liebin.zheng
+ * Generate Date: 2025-05-26 16:28:10
+ */
+package nckd.jxccl.hrmp.hbss.common;
+
+/**
+ * hr云init应用-通用常量类<br>
+ * 代码中不能存在硬编码敏感信息,如账号、密码、http外链、ftp外链、邮箱等。<br>
+ * 标识或缓存的常量,需以"KEY_"、"FID_"、"ENTRY_"或"SUBENTRY_"作为变量的前缀。<br>
+ *
+ * @author nckd
+ * @date 2025-05-26 16:28:10
+ */
+public class AppflgConstant {
+	
+	public static final String KEY_APP_NAME = "hr-init";
+
+}

+ 24 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/common/bd/LawEntityConstant.java

@@ -0,0 +1,24 @@
+package nckd.jxccl.hrmp.hbss.common.bd;
+
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+ * 法律实体常量类
+ * @author: jtd
+ * @date: 2026/1/8 21:22
+ */
+public class LawEntityConstant extends FormConstant {
+
+    /** 法律实体变更-实体标识 */
+    public static final String HBSS_LAWENTITYCHG_ENTITY = "hbss_lawentitychg";
+
+    /** 变更类型 */
+    public static final String TYPE_KEY = "type";
+
+    /** 显示法律实体变更标识-页面自定义参数 */
+    public static final String SHOWCHGFLAG_CUSTOM_PARAM = "showChgFlag";
+
+    /** 初始化-页面缓存参数 */
+    public static final String INIT_PAGECACHE = "init";
+
+}

+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/mservice/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/form/.gitkeep


+ 117 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/form/bd/LawEntityChgFormPlugin.java

@@ -0,0 +1,117 @@
+package nckd.jxccl.hrmp.hbss.plugin.form.bd;
+
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRDynamicObjectUtils;
+import kd.hr.hbp.common.util.HRObjectUtils;
+import kd.hr.hbp.common.util.HRStringUtils;
+import nckd.jxccl.base.common.utils.PatternUtil;
+import nckd.jxccl.hrmp.hbss.common.bd.LawEntityConstant;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 法律实体变更表单插件
+ * @entity: hbss_lawentitychg
+ * @author: jtd
+ * @date: 2026/1/8 19:50
+ */
+public class LawEntityChgFormPlugin extends AbstractFormPlugin {
+    Log log = LogFactory.getLog(LawEntityChgFormPlugin.class);
+
+    private static final HRBaseServiceHelper helper = HRBaseServiceHelper.create(LawEntityConstant.HBSS_LAWENTITYCHG_ENTITY);
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+
+        String showChgFlag = getView().getFormShowParameter().getCustomParam(LawEntityConstant.SHOWCHGFLAG_CUSTOM_PARAM);
+        String chgProp = e.getProperty().getName();
+        if (StringUtils.isNotEmpty(showChgFlag) && showChgFlag.equals("yes") && PatternUtil.isExProperty(chgProp)) {
+            String mulType = (String) getModel().getValue(LawEntityConstant.TYPE_KEY);
+            Object oldValue = helper.loadOne(chgProp, getModel().getValue(LawEntityConstant.ID_KEY)).get(chgProp);
+            Object newValue = getModel().getValue(chgProp);
+
+            boolean isChange;
+            if (HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue)) {
+                isChange = false;
+            } else if ((HRObjectUtils.isEmpty(oldValue) && !HRObjectUtils.isEmpty(newValue)) || (!HRObjectUtils.isEmpty(oldValue) && HRObjectUtils.isEmpty(newValue))) {
+                isChange = true;
+            } else {
+                isChange = HRDynamicObjectUtils.compareValues(oldValue, newValue);
+            }
+            getPageCache().put(chgProp, Boolean.toString(isChange));
+
+            // 获取所有二开字段的变更属性
+            List<Map.Entry<String, String>> dataEntityProperties = getPageCache().getAll().entrySet().stream().filter(entry -> PatternUtil.isExProperty(entry.getKey())).filter(entry -> HRStringUtils.equals(entry.getValue(), "true")).collect(Collectors.toList());
+            if (dataEntityProperties.isEmpty() && HRStringUtils.containsIgnoreCase(mulType, "7")) {
+                mulType = buildMulType(null, null, mulType, "7");
+            } else {
+                mulType = buildMulType(null, 1, mulType, "7");
+            }
+
+            getModel().setValue(LawEntityConstant.TYPE_KEY, mulType);
+        }
+    }
+
+    // from: kd.hr.hbss.formplugin.web.lawentity.LawEntityEditPlugin.buildMulType
+    private String buildMulType(Object oldValue, Object newValue, String mulType, String type) {
+        if (!ObjectUtils.nullSafeEquals(oldValue, newValue)) {
+            if (null == mulType) {
+                mulType = "," + type + ",";
+            } else if (!mulType.contains(type)) {
+                mulType = mulType + type + ",";
+            }
+        } else if (null != mulType && mulType.contains(type)) {
+            mulType = mulType.replaceAll(type + ",", "");
+        }
+
+        if (null != mulType && mulType.length() == 1) {
+            mulType = null;
+        }
+
+        if (null != mulType) {
+            mulType = mulSelectSort(mulType);
+        }
+
+        return mulType;
+    }
+
+    // from: kd.hr.hbss.formplugin.web.lawentity.LawEntityEditPlugin.mulSelectSort
+    private String mulSelectSort(String mulSelect) {
+        if (StringUtils.isEmpty(mulSelect)) {
+            return mulSelect;
+        } else {
+            String[] mulTypeArr = mulSelect.split(",");
+            String mulSelectSort = "";
+            ArrayList mulTypeList = new ArrayList();
+
+            for(int i = 0; i < mulTypeArr.length; ++i) {
+                if (!mulTypeArr[i].equals("")) {
+                    mulTypeList.add(mulTypeArr[i]);
+                }
+            }
+
+            Collections.sort(mulTypeList);
+
+            for(int i = 0; i < mulTypeList.size(); ++i) {
+                if ("".equals(mulSelectSort)) {
+                    mulSelectSort = "," + mulTypeList.get(i) + ",";
+                } else {
+                    mulSelectSort = mulSelectSort + mulTypeList.get(i) + ",";
+                }
+            }
+
+            return mulSelectSort;
+        }
+    }
+}

+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/operate/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/other/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/report/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/plugin/workflow/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbss/webapi/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/business/.gitkeep


+ 22 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/common/AppflgConstant.java

@@ -0,0 +1,22 @@
+/**
+ * This is a kingdee cosmic template project that is automatically generated by the Kingdee cosmic development assistant plugin. 
+ * If there are any issues during the use process, you can provide feedback to the kingdee developer community website.
+ * Website: https://developer.kingdee.com/developer?productLineId=29
+ * Author: liebin.zheng
+ * Generate Date: 2025-05-26 16:28:10
+ */
+package nckd.jxccl.hrmp.init.common;
+
+/**
+ * hr云init应用-通用常量类<br>
+ * 代码中不能存在硬编码敏感信息,如账号、密码、http外链、ftp外链、邮箱等。<br>
+ * 标识或缓存的常量,需以"KEY_"、"FID_"、"ENTRY_"或"SUBENTRY_"作为变量的前缀。<br>
+ *
+ * @author nckd
+ * @date 2025-05-26 16:28:10
+ */
+public class AppflgConstant {
+	
+	public static final String KEY_APP_NAME = "hr-init";
+
+}

+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/mservice/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/form/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/operate/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/other/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/report/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/plugin/workflow/.gitkeep


+ 0 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/init/webapi/.gitkeep


+ 337 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/EmployeePrintPlugin.java

@@ -0,0 +1,337 @@
+package nckd.jxccl.opmc.pm.plugin.form.print;
+
+import kd.bos.algo.Algo;
+import kd.bos.algo.AlgoContext;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.GroupbyDataSet;
+import kd.bos.algo.Row;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.plugin.args.CustomPrintDataEntitiesArgs;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.print.core.data.DataRowSet;
+import kd.bos.print.core.data.datasource.CustomDataSource;
+import kd.bos.print.core.data.datasource.MainDataSource;
+import kd.bos.print.core.data.datasource.PrtDataSource;
+import kd.bos.print.core.data.field.CollectionField;
+import kd.bos.print.core.data.field.Field;
+import kd.bos.print.core.data.field.IntegerField;
+import kd.bos.print.core.plugin.AbstractPrintPlugin;
+import kd.bos.print.core.plugin.event.AfterLoadDataEvent;
+import kd.bos.print.core.plugin.event.CustomDataLoadEvent;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.hr.hbp.common.cache.HRAppCache;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.algo.GroupMaxStrFunction;
+import nckd.jxccl.base.common.constant.FormConstant;
+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 java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+* 绩效结果打印
+* @author W.Y.C
+* @date 2026/1/6 15:15
+* @version 1.0
+*/
+public class EmployeePrintPlugin extends AbstractPrintPlugin implements Plugin {
+
+    //记录主数据源
+    private MainDataSource mainDataSource;
+
+    @Override
+    public void loadCustomData(CustomDataLoadEvent evt) {
+        Date printYear = HRAppCache.get("nckd_pm").get("printYear", Date.class);
+
+        // 1. 获取数据源标识
+        CustomDataSource dataSource = evt.getDataSource();
+        if (!"custom".equals(dataSource.getDsName())) {
+            return;
+        }
+
+        // 2. 获取或创建数据结果集
+        List<DataRowSet> customRows = evt.getCustomDataRows();
+
+        // 3. 组装数据(例如:从数据库查询、计算衍生字段等)
+
+        DataRowSet rowSet = new DataRowSet();
+        rowSet.put("year", new IntegerField(DateUtil.getYear(printYear)));
+        int printYearValue = DateUtil.getYear(printYear);
+        rowSet.put("yearIndex", new IntegerField(getYearIndexFromPrintYear(printYearValue)));
+
+        // 4. 将数据添加到结果集
+        customRows.add(rowSet);
+        evt.setDataSource(dataSource);
+    }
+
+    /**
+     * 根据年份获取对应的序号,近5年(以当前年份为基准)的序号分别为5/4/3/2/1
+     * 例如:当前年是2026,printYear 为2025,则序号为5(上年)
+     * @param year 年份
+     * @return 序号,最近的年份序号为5,依次递减
+     */
+    private int getYearIndexFromPrintYear(int year) {
+        int currentYear = LocalDate.now().getYear();
+
+        // 计算最近5年的范围,即从当前年份往前推5年到前1年(不包含当前年)
+        // 例如:当前年是2026年,最近5年是2021-2025,即 [2021, 2022, 2023, 2024, 2025]
+        int startYear = currentYear - 5; // 最早的年份
+        int endYear = currentYear - 1;   // 最近的年份
+
+        // 检查年份是否在最近5年范围内
+        if (year >= startYear && year <= endYear) {
+            // 计算序号:最近的年份(endYear)序号为5,前一年为4,以此类推
+            // 序号 = (year - startYear) + 1
+            // 例如:当前年是2026年,startYear=2021,endYear=2025
+            // 2025 (最近): 2025 - 2021 + 1 = 5 ✓
+            // 2024: 2024 - 2021 + 1 = 4 ✓
+            // 2023: 2023 - 2021 + 1 = 3 ✓
+            // 2022: 2022 - 2021 + 1 = 2 ✓
+            // 2021 (最远): 2021 - 2021 + 1 = 1 ✓
+            return year - startYear + 1;
+        } else {
+            // 如果不在最近5年内,返回0表示超出范围
+            return 0;
+        }
+    }
+
+    @Override
+    public void afterLoadData(AfterLoadDataEvent evt) {
+        PrtDataSource dataSource = evt.getDataSource();
+        if(!(dataSource instanceof MainDataSource)){
+            return;//非主数据源,立即返回
+        }
+        //记录主数据源,用于自定义数据源查询时,获取需要的信息
+        mainDataSource = (MainDataSource) dataSource;
+        Date printYear = HRAppCache.get("nckd_pm").get("printYear", Date.class);
+        Map<Integer, Long> selectPrintPersonIdMap = HRAppCache.get("nckd_pm").get("employeePrintIdMap", Map.class);
+        if(selectPrintPersonIdMap == null){
+            throw new ValidationException("请选择要打印的日期!");
+        }
+        List<String> seqs = ConvertUtil.toList(selectPrintPersonIdMap.keySet(), ArrayList::new);
+        LocalDate now = LocalDate.now();
+        // 获取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));
+
+        Map<String, String> positionMap = new HashMap<>();
+        Map<String, Integer> perfRankMgmtMap = new HashMap<>();
+        try (AlgoContext context = Algo.newContext()) {
+            Collection<Long> personIds = selectPrintPersonIdMap.values();
+            DataSet positionDateSet = getPositions(fiveYearsAgo, currentYear, personIds);
+            if (positionDateSet != null) {
+                while (positionDateSet.hasNext()) {
+                    Row next = positionDateSet.next();
+                    Long personId = next.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+                    int tempIndex = 1;
+                    for (int year = fiveYearsAgo; year <= currentYear; year++) {
+                        String positionName = next.getString("position_" + tempIndex);
+                        positionMap.put(personId + "nckd_position_" + tempIndex, positionName);
+                        tempIndex++;
+                    }
+                }
+            }
+
+            DataSet perfRankMgmtDataSet = getPerfRankMgmt(personIds, fiveYearsAgo, currentYear);
+            if (perfRankMgmtDataSet != null) {
+                while (perfRankMgmtDataSet.hasNext()) {
+                    Row next = perfRankMgmtDataSet.next();
+                    Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                    int tempIndex = 1;
+                    for (int year = fiveYearsAgo; year <= currentYear; year++) {
+                        Integer topran = next.getInteger("perfrank_topran_" + tempIndex);
+                        Integer topranks = next.getInteger("perfrank_topranks_" + tempIndex);
+                        perfRankMgmtMap.put(personId + "nckd_perfrank_topran_" + tempIndex, topran);
+                        perfRankMgmtMap.put(personId + "nckd_perfrank_topranks_" + tempIndex, topranks);
+                        tempIndex++;
+                    }
+                }
+            }
+        }
+
+
+        // 获取当前打印的数据集并准备过滤后的集合
+        // 2. 获取当前打印的数据集
+        List<DataRowSet> dataRowSets = evt.getDataRowSets();
+        List<DataRowSet> filteredDataRowSets = new ArrayList<>();
+
+        for (DataRowSet dataRowSet : dataRowSets) {
+            if (dataRowSet.containerKey("reportEntry")) {
+                CollectionField entryField = (CollectionField) dataRowSet.getField("reportEntry");
+                List<DataRowSet> entryRows = entryField.getValue();
+                List<DataRowSet> filteredEntryRows = new ArrayList<>();
+                for (DataRowSet entryRow : entryRows) {
+                    String seq = ConvertUtil.toStr(entryRow.getField(FormConstant.SEQ_KEY).getValue());
+                    Long personId = selectPrintPersonIdMap.get(seq + "");
+                    // 判断该分录行是否在用户勾选的列表中
+                    if (seqs.contains(seq)) {
+                        for (String fieldKey : entryRow.getFieldKeys()) {
+                            if (fieldKey.startsWith("nckd_position_")) {
+                                String positionName = positionMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(positionName != null ? positionName : "");
+                                entryRow.put(fieldKey, field);
+                            }
+                            if (fieldKey.startsWith("nckd_perfrank_topran_")) {
+                                Integer perfRankMgmt = perfRankMgmtMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(perfRankMgmt != null ? perfRankMgmt : 0);
+                                entryRow.put(fieldKey, field);
+                            }
+                            if (fieldKey.startsWith("nckd_perfrank_topranks_")) {
+                                Integer perfRankMgmt = perfRankMgmtMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(perfRankMgmt != null ? perfRankMgmt : 0);
+                                entryRow.put(fieldKey, field);
+                            }
+                        }
+                        filteredEntryRows.add(entryRow);
+                    }
+
+                }
+                // 用过滤后的分录数据替换原数据
+                if (!filteredEntryRows.isEmpty()) {
+                    DataRowSet newDataRowSet = dataRowSet.deepCopy(); // 深拷贝,避免影响原数据
+                    newDataRowSet.put("reportEntry", new CollectionField(filteredEntryRows));
+                    filteredDataRowSets.add(newDataRowSet);
+                }
+            }
+        }
+
+        // 将过滤后的数据集设置回事件,实现只打印勾选行
+        evt.setDataRowSets(filteredDataRowSets);
+
+    }
+
+
+    private static DataSet getPerfRankMgmt(Collection<Long> personIds, int beginYear, int endYear) {
+        // 年度绩效排名管理
+        QueryFieldBuilder perfRankMgmtFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(FormConstant.NCKD_PERSON)
+                .add("nckd_toprank")
+                .addGroup(new String[]{"nckd_perfrankmgmt"},"nckd_topranks","nckd_theyear");
+        QFilter perfRankMgmtFilter = new QFilter("nckd_isranking", "=", EnableEnum.YES.getCode())
+                .and(FormConstant.NCKD_PERSON, QCP.in, personIds)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.large_equals, beginYear)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.less_equals, endYear);
+        DataSet perfRankMgmtDataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_perfRank", "nckd_perfrankmgmtentry", perfRankMgmtFieldBuilder.buildSelect(), new QFilter[]{perfRankMgmtFilter}, perfRankMgmtFieldBuilder.buildOrder(), 10000);
+        int tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            //排名
+            String toprankField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_toprank ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(toprankField, "perfrankmgmt_topran_temp_" + tempIndex);
+            //总人数
+            String topranksField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_perfrankmgmt.nckd_topranks ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(topranksField, "perfrankmgmt_topranks_temp_" + tempIndex);
+            tempIndex++;
+        }
+        GroupbyDataSet groupbyDataSet = perfRankMgmtDataSet.groupBy(new String[]{String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)});
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            groupbyDataSet.max("perfrankmgmt_topran_temp_"+tempIndex, "perfrank_topran_"+tempIndex);
+            groupbyDataSet.max("perfrankmgmt_topranks_temp_"+tempIndex, "perfrank_topranks_"+tempIndex);
+            tempIndex++;
+        }
+        return groupbyDataSet.finish();
+    }
+
+
+    /**
+     * 获取近N年的岗位信息
+     * 用于查询每个员工在指定年份范围内每年年底(12月31日)所在的有效岗位
+     * 当一个员工在同一年有多个有效岗位时,选择结束日期最晚的那个岗位
+     *
+     * @param beginYear 起始年份(包含)
+     * @param endYear   结束年份(包含)
+     * @return 包含每个员工每年岗位信息的数据集
+     */
+    private DataSet getPositions(int beginYear,int endYear,Collection<Long> personIds){
+        // 查询全职任职关系,获取员工、岗位、任职起止时间等信息
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.STARTDATE)  // 任职开始日期
+                .add(FormConstant.ENDDATE)    // 任职结束日期
+                .addIdNumberName(FormConstant.POSITION_KEY)  // 岗位信息(ID、编码、名称)
+                .addIdNumberNameWithExtras(new String[]{FormConstant.EMPLOYEE_KEY},FormConstant.EMP_NUMBER_KEY)  // 员工信息(ID、编码、姓名)+ 工号
+                .orderDesc(FormConstant.EMPLOYEE_KEY,FormConstant.STARTDATE);  // 按员工ID和开始日期降序排列
+        // 筛选条件:只查询全职任职(IS_PRIMARY = YES)
+        QFilter qFilter = new QFilter(FormConstant.IS_PRIMARY,QCP.equals, EnableEnum.YES.getCode());
+        if(!personIds.isEmpty()){
+            qFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.in,personIds.toArray());
+        }
+        DataSet dataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_EMPREL", FormConstant.HRPI_EMPPOSORGREL, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder(), 1000000);
+
+        int tempIndex = 1;
+        // 遍历指定年份范围,为每一年生成对应的岗位查询字段
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取每年12-31 23:59:59作为目标日期,用于判断该员工当年年底的岗位状态
+            Date targetDate = DateUtil.toDate(LocalDateTime.of(year, 12, 31, 23, 59, 59));
+            String targetDateStr = DateUtil.format(targetDate, DateUtil.NORM_DATE_PATTERN); // 使用日期格式而非日期时间格式
+
+            // 创建复合字段:如果在目标日期员工处于有效任职状态,则生成"结束日期|岗位名称"格式的字符串
+            // 这样使用GroupMaxStrFunction时,会根据结束日期选择最晚的那个任职记录
+            //为什么用“00000000000000|”,不用null。因为当GroupMaxStrFunction进行字符串比较时,有效的日期值(如'20231231235959|经济技术部部长')会比'00000000000000|'大,从而被正确选择
+            String combinedField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'), '|', " +
+                    String.join(".", FormConstant.POSITION_KEY, FormConstant.NAME_KEY) + ") ELSE '00000000000000|' END";
+
+            // 添加岗位复合字段(包含结束日期和岗位名称)
+            dataSet = dataSet.addField(combinedField, "position_temp_" + tempIndex);
+
+            // 同时创建仅包含日期部分的字段,用于后续解析时去除日期部分,只保留岗位名称
+            String dateField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'),'|') ELSE '00000000000000|' END";
+            dataSet = dataSet.addField(dateField, "position_date_" + tempIndex);
+
+//            dataSet = dataSet.addField("'"+year+"'", "position_year_" + tempIndex);
+            tempIndex++;
+        }
+
+        // 行转列处理:按员工ID分组,使用GroupMaxStrFunction获取每个员工每年结束日期最晚的任职记录
+        tempIndex = 1;
+        GroupMaxStrFunction groupMaxStr = new GroupMaxStrFunction();
+        // 按员工ID进行分组聚合
+        GroupbyDataSet groupbyDataSet = dataSet.groupBy(new String[]{String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)});
+        // 对每一年的数据进行聚合,获取结束日期最晚的任职记录
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取岗位复合字段的最大值(即结束日期最晚的记录)
+            groupbyDataSet.agg(new GroupMaxStrFunction(),"position_temp_"+tempIndex, "position_max_"+tempIndex);
+            // 同时获取日期字段的最大值,用于后续解析
+            groupbyDataSet.agg(groupMaxStr,"position_date_"+tempIndex, "position_date_max_"+tempIndex);
+            tempIndex++;
+        }
+        DataSet result = groupbyDataSet.finish();
+
+        // 解析复合字段,提取真正的岗位名称
+        // 通过去除日期部分(YYYYMMDDHHmmss|)来获取原始的岗位名称
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            final int currentYearIndex = year - beginYear + 1;
+            result = result.addField(
+                    // 如果岗位字段有效(不为默认值'00000000000000|'),则去除日期部分,只保留岗位名称
+                    "CASE WHEN position_max_" + tempIndex + " IS NOT NULL AND position_max_" + tempIndex + " != '00000000000000|' THEN " +
+                            "REPLACE(position_max_" + tempIndex + ",position_date_max_"+tempIndex+",'') " +  // 去除日期部分,得到岗位名称
+                            "ELSE null END",
+                    "position_" + tempIndex  // 最终的岗位名称字段
+            );
+            tempIndex++;
+        }
+        return result;
+    }
+}

+ 125 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PerfBatchPrintListPlugin.java

@@ -5,24 +5,51 @@ import com.alibaba.fastjson.JSONObject;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.cache.AppCache;
+import kd.bos.entity.datamodel.ListSelectedRow;
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
 import kd.bos.entity.datamodel.events.PackageDataEvent;
 import kd.bos.entity.report.ReportColumn;
+import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.form.CloseCallBack;
 import kd.bos.form.FormShowParameter;
+import kd.bos.form.IFormView;
 import kd.bos.form.ShowType;
 import kd.bos.form.control.Toolbar;
 import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.ClosedCallBackEvent;
 import kd.bos.form.events.PreOpenFormEventArgs;
+import kd.bos.form.operate.AbstractOperate;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.form.operate.printop.PrintPreview;
+import kd.bos.form.operate.printop.SelectTplPrint;
+import kd.bos.list.IListView;
+import kd.bos.metadata.dao.MetaCategory;
+import kd.bos.metadata.dao.MetadataDao;
+import kd.bos.metadata.form.FormMetadata;
+import kd.bos.mvc.report.ReportView;
+import kd.bos.orm.query.QFilter;
+import kd.bos.print.api.utils.ConfigConstUtil;
+import kd.bos.print.business.utils.OldPrintTemplateUtil;
 import kd.bos.report.ReportList;
 import kd.bos.report.plugin.AbstractReportFormPlugin;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.template.orgctrl.service.PrintTemplateServiceFactory;
+import kd.bos.template.orgctrl.utils.ParamUtils;
+import kd.hr.hbp.common.cache.HRAppCache;
 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 java.util.ArrayList;
+import java.util.Date;
 import java.util.EventObject;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
 * 人员考评套打列表
@@ -48,6 +75,12 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
         toolbar.addItemClickListener(this);
     }
 
+    @Override
+    public void beforeQuery(ReportQueryParam queryParam) {
+        ReportList reportList = this.getControl("reportlistap");
+        reportList.setSelectedAll(true);
+    }
+
     @Override
     public void itemClick(ItemClickEvent evt) {
         String itemKey = evt.getItemKey();
@@ -77,6 +110,50 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
             showParameter.setCustomParam( "personIds", personIds);
             this.getView().showForm(showParameter);
         }
+        if("nckd_employeeprint".equalsIgnoreCase(itemKey)){
+            HRAppCache.get("nckd_pm").remove( "printYear");
+            ReportList reportList = this.getView().getControl(FormConstant.REPORTLISTAP);
+            int[] selectedRowIndexes = reportList.getEntryState().getSelectedRows();
+            List<Long> personIds =  new ArrayList<>();
+            if(selectedRowIndexes == null ||  selectedRowIndexes.length == 0){
+                this.getView().showTipNotification("请选择要打印的员工!");
+                return;
+            }
+            FormShowParameter showParameter = new FormShowParameter();
+            showParameter.setFormId("nckd_selectdateprint");
+            showParameter.getOpenStyle().setShowType(ShowType.Modal);
+            showParameter.setCloseCallBack(new CloseCallBack(this, "nckd_selectdateprint"));
+            showParameter.setCaption("选择打印年份");
+            this.getView().showForm(showParameter);
+        }
+    }
+
+
+    private Map<String, String> getTemplates(String formId) {
+        String id = MetadataDao.getIdByNumber(formId, MetaCategory.Form);// 368
+        FormMetadata fm = (FormMetadata)MetadataDao.readMeta(id, MetaCategory.Form);// 369
+        String oriFormId = MetadataDao.getNumberById(fm.getEntityId());// 370
+        Map<String, String> templateMap = new LinkedHashMap(16);// 371
+        DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("bos_manageprinttpl", "number, name, enable,type,printtplid", ConfigConstUtil.isNewTenantOfVersion(7) ? new QFilter[]{new QFilter("billformid", "=", oriFormId), new QFilter("type", "!=", "A")} : new QFilter[]{new QFilter("billformid", "=", oriFormId)});// 372 373
+
+        for(DynamicObject obj : dynamicObjects) {// 375
+            DynamicObject printtpObj = obj.getDynamicObject("printtplid");// 376
+            Object tplId = obj.get("printtplid_id");// 377
+            String enable = obj.getString("enable");// 378
+            if (!"0".equals(enable)) {// 379
+                if (printtpObj != null && "B".equals(printtpObj.get("type"))) {// 383
+                    templateMap.put(tplId.toString(), printtpObj.getLocaleString("name").getLocaleValue());// 391
+                } else {
+                    QFilter qFilter = new QFilter("id", "=", tplId);// 384
+                    DynamicObject formMeta = OldPrintTemplateUtil.loadSingleFromCache("id,number,bizappid,basedatafield,modifierid", new QFilter[]{qFilter});// 385
+                    if (formMeta != null) {// 386
+                        templateMap.put(tplId.toString(), formMeta.getLocaleString("name").getLocaleValue());// 389
+                    }
+                }
+            }
+        }
+
+        return templateMap;// 394
     }
 
     @Override
@@ -90,6 +167,23 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
                 this.getView().showSuccessNotification( "文件印发及盖章时间修改成功");
                 this.getView().invokeOperation(FormConstant.REFRESH_OP);
             }
+        }else if("nckd_selectdateprint".equalsIgnoreCase(actionId)){
+            Object returnData = closedCallBackEvent.getReturnData();
+            if(returnData != null) {
+                HRAppCache.get("nckd_pm").remove( "employeePrintIdMap");
+                Date date = ConvertUtil.toDate(returnData);
+                HRAppCache.get("nckd_pm").put( "printYear", date);
+                ReportList reportList = this.getView().getControl(FormConstant.REPORTLISTAP);
+                int[] selectedRowIndexes = reportList.getEntryState().getSelectedRows();
+                Map<Integer,Long> personIdMap = new HashMap<>();
+                for (int selectedRowIndex : selectedRowIndexes) {
+                    DynamicObject rowData = reportList.getReportModel().getRowData(selectedRowIndex);
+                    personIdMap.put( rowData.getInt("fseq"), rowData.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)));
+                }
+                HRAppCache.get("nckd_pm").put( "employeePrintIdMap", personIdMap);
+                this.getView().invokeOperation("employeeprint");
+
+            }
         }
     }
 
@@ -107,7 +201,38 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
             }
 
         }
+    }
 
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        AbstractOperate operate = (AbstractOperate) args.getSource();
+        HRAppCache.get("nckd_pm").remove( "selectPrintSeq");
+        HRAppCache.get("nckd_pm").remove( "selectPrintPersonIdMap");
+        String operateKey = operate.getOperateKey();
+        if ("selecttplprint".equals(operateKey)) {
+            ReportList reportList = this.getView().getControl(FormConstant.REPORTLISTAP);
+            int[] selectedRowIndexes = reportList.getEntryState().getSelectedRows();
+            Map<Integer,Long> personIdMap = new HashMap<>();
+            List<Integer> seqList = new  ArrayList<>();
+            if(selectedRowIndexes != null && selectedRowIndexes.length > 0) {
+                for (int selectedRowIndex : selectedRowIndexes) {
+                    DynamicObject rowData = reportList.getReportModel().getRowData(selectedRowIndex);
+                    seqList.add(rowData.getInt("fseq"));
+                    personIdMap.put( rowData.getInt("fseq"), rowData.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)));
+                }
+            }else{
+                int rowCount = reportList.getReportModel().getRowCount();
+                DynamicObjectCollection rowDataColl = reportList.getReportModel().getRowData(1, rowCount, false);
+                for (DynamicObject dynamicObject : rowDataColl) {
+                    long personId = dynamicObject.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                    seqList.add(dynamicObject.getInt("fseq"));
+                    personIdMap.put(dynamicObject.getInt("fseq"), personId);
+                }
+            }
+            HRAppCache.get("nckd_pm").put( "selectPrintSeq", seqList);
+            HRAppCache.get("nckd_pm").put( "selectPrintPersonIdMap", personIdMap);
+        }else if( "employeeprint".equals(operateKey)){
 
+        }
     }
 }

+ 268 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PerfDetailPrintPlugin.java

@@ -0,0 +1,268 @@
+package nckd.jxccl.opmc.pm.plugin.form.print;
+
+import kd.bos.algo.Algo;
+import kd.bos.algo.AlgoContext;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.GroupbyDataSet;
+import kd.bos.algo.Row;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.print.core.data.DataRowSet;
+import kd.bos.print.core.data.datasource.CustomDataSource;
+import kd.bos.print.core.data.field.CollectionField;
+import kd.bos.print.core.data.field.Field;
+import kd.bos.print.core.plugin.AbstractPrintPlugin;
+import kd.bos.print.core.plugin.event.AfterLoadDataEvent;
+import kd.bos.print.core.plugin.event.BeforeLoadDataEvent;
+import kd.bos.print.core.plugin.event.CustomDataLoadEvent;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.hr.hbp.common.cache.HRAppCache;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.algo.GroupMaxStrFunction;
+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 java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+* 绩效结果打印
+* @author W.Y.C
+* @date 2026/1/6 15:15
+* @version 1.0
+*/
+public class PerfDetailPrintPlugin extends AbstractPrintPlugin implements Plugin {
+
+    @Override
+    public void afterLoadData(AfterLoadDataEvent evt) {
+        String dsName = evt.getDataSource().getDsName();
+        if(!dsName.contains("custom")) {
+            List<Integer> seqs = HRAppCache.get("nckd_pm").get("selectPrintSeq", List.class);
+            if (seqs == null || seqs.isEmpty()) {
+                return;
+            }
+
+            Map<Integer, Long> selectPrintPersonIdMap = HRAppCache.get("nckd_pm").get("selectPrintPersonIdMap", Map.class);
+            LocalDate now = LocalDate.now();
+            // 获取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));
+
+            Map<String, String> positionMap = new HashMap<>();
+            Map<String, Integer> perfRankMgmtMap = new HashMap<>();
+            try (AlgoContext context = Algo.newContext()) {
+                Collection<Long> personIds = selectPrintPersonIdMap.values();
+                DataSet positionDateSet = getPositions(fiveYearsAgo, currentYear, personIds);
+                if (positionDateSet != null) {
+                    while (positionDateSet.hasNext()) {
+                        Row next = positionDateSet.next();
+                        Long personId = next.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+                        int tempIndex = 1;
+                        for (int year = fiveYearsAgo; year <= currentYear; year++) {
+                            String positionName = next.getString("position_" + tempIndex);
+                            positionMap.put(personId + "nckd_position_" + tempIndex, positionName);
+                            tempIndex++;
+                        }
+                    }
+                }
+
+                DataSet perfRankMgmtDataSet = getPerfRankMgmt(personIds, fiveYearsAgo, currentYear);
+                if (perfRankMgmtDataSet != null) {
+                    while (perfRankMgmtDataSet.hasNext()) {
+                        Row next = perfRankMgmtDataSet.next();
+                        Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                        int tempIndex = 1;
+                        for (int year = fiveYearsAgo; year <= currentYear; year++) {
+                            Integer topran = next.getInteger("perfrank_topran_" + tempIndex);
+                            Integer topranks = next.getInteger("perfrank_topranks_" + tempIndex);
+                            perfRankMgmtMap.put(personId + "nckd_perfrank_topran_" + tempIndex, topran);
+                            perfRankMgmtMap.put(personId + "nckd_perfrank_topranks_" + tempIndex, topranks);
+                            tempIndex++;
+                        }
+                    }
+                }
+            }
+
+
+            // 获取当前打印的数据集并准备过滤后的集合
+            // 2. 获取当前打印的数据集
+            List<DataRowSet> dataRowSets = evt.getDataRowSets();
+            List<DataRowSet> filteredDataRowSets = new ArrayList<>();
+            for (DataRowSet dataRowSet : dataRowSets) {
+
+                CollectionField entryField = (CollectionField) dataRowSet.getField("reportEntry");
+                List<DataRowSet> entryRows = entryField.getValue();
+                List<DataRowSet> filteredEntryRows = new ArrayList<>();
+                for (DataRowSet entryRow : entryRows) {
+                    if (dataRowSet.containerKey("reportEntry")) {
+                        Integer seq = ConvertUtil.toInt(entryRow.getField(FormConstant.SEQ_KEY).getValue());
+                        Long personId = selectPrintPersonIdMap.get(seq + "");
+                        // 判断该分录行是否在用户勾选的列表中
+                        if (seqs.contains(seq)) {
+                            filteredEntryRows.add(entryRow); // 保留被勾选的行
+                        }
+                        for (String fieldKey : entryRow.getFieldKeys()) {
+                            if (fieldKey.startsWith("nckd_position_")) {
+                                String positionName = positionMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(positionName != null ? positionName : "");
+                                entryRow.put(fieldKey, field);
+                            }
+                            if (fieldKey.startsWith("nckd_perfrank_topran_")) {
+                                Integer perfRankMgmt = perfRankMgmtMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(perfRankMgmt != null ? perfRankMgmt : 0);
+                                entryRow.put(fieldKey, field);
+                            }
+                            if (fieldKey.startsWith("nckd_perfrank_topranks_")) {
+                                Integer perfRankMgmt = perfRankMgmtMap.get(personId + fieldKey);
+                                Field field = entryRow.getField(fieldKey);
+                                field.setValue(perfRankMgmt != null ? perfRankMgmt : 0);
+                                entryRow.put(fieldKey, field);
+                            }
+                        }
+                    }
+                }
+                // 用过滤后的分录数据替换原数据
+                if (!filteredEntryRows.isEmpty()) {
+                    DataRowSet newDataRowSet = dataRowSet.deepCopy(); // 深拷贝,避免影响原数据
+                    newDataRowSet.put("reportEntry", new CollectionField(filteredEntryRows));
+                    filteredDataRowSets.add(newDataRowSet);
+                }
+
+            }
+            // 将过滤后的数据集设置回事件,实现只打印勾选行
+            evt.setDataRowSets(filteredDataRowSets);
+        }
+    }
+
+
+    private static DataSet getPerfRankMgmt(Collection<Long> personIds, int beginYear, int endYear) {
+        // 年度绩效排名管理
+        QueryFieldBuilder perfRankMgmtFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(FormConstant.NCKD_PERSON)
+                .add("nckd_toprank")
+                .addGroup(new String[]{"nckd_perfrankmgmt"},"nckd_topranks","nckd_theyear");
+        QFilter perfRankMgmtFilter = new QFilter("nckd_isranking", "=", EnableEnum.YES.getCode())
+                .and(FormConstant.NCKD_PERSON, QCP.in, personIds)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.large_equals, beginYear)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.less_equals, endYear);
+        DataSet perfRankMgmtDataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_perfRank", "nckd_perfrankmgmtentry", perfRankMgmtFieldBuilder.buildSelect(), new QFilter[]{perfRankMgmtFilter}, perfRankMgmtFieldBuilder.buildOrder(), 10000);
+        int tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            //排名
+            String toprankField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_toprank ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(toprankField, "perfrankmgmt_topran_temp_" + tempIndex);
+            //总人数
+            String topranksField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_perfrankmgmt.nckd_topranks ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(topranksField, "perfrankmgmt_topranks_temp_" + tempIndex);
+            tempIndex++;
+        }
+        GroupbyDataSet groupbyDataSet = perfRankMgmtDataSet.groupBy(new String[]{String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)});
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            groupbyDataSet.max("perfrankmgmt_topran_temp_"+tempIndex, "perfrank_topran_"+tempIndex);
+            groupbyDataSet.max("perfrankmgmt_topranks_temp_"+tempIndex, "perfrank_topranks_"+tempIndex);
+            tempIndex++;
+        }
+        return groupbyDataSet.finish();
+    }
+
+
+    /**
+     * 获取近N年的岗位信息
+     * 用于查询每个员工在指定年份范围内每年年底(12月31日)所在的有效岗位
+     * 当一个员工在同一年有多个有效岗位时,选择结束日期最晚的那个岗位
+     *
+     * @param beginYear 起始年份(包含)
+     * @param endYear   结束年份(包含)
+     * @return 包含每个员工每年岗位信息的数据集
+     */
+    private DataSet getPositions(int beginYear,int endYear,Collection<Long> personIds){
+        // 查询全职任职关系,获取员工、岗位、任职起止时间等信息
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.STARTDATE)  // 任职开始日期
+                .add(FormConstant.ENDDATE)    // 任职结束日期
+                .addIdNumberName(FormConstant.POSITION_KEY)  // 岗位信息(ID、编码、名称)
+                .addIdNumberNameWithExtras(new String[]{FormConstant.EMPLOYEE_KEY},FormConstant.EMP_NUMBER_KEY)  // 员工信息(ID、编码、姓名)+ 工号
+                .orderDesc(FormConstant.EMPLOYEE_KEY,FormConstant.STARTDATE);  // 按员工ID和开始日期降序排列
+        // 筛选条件:只查询全职任职(IS_PRIMARY = YES)
+        QFilter qFilter = new QFilter(FormConstant.IS_PRIMARY,QCP.equals, EnableEnum.YES.getCode());
+        if(!personIds.isEmpty()){
+            qFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.in,personIds.toArray());
+        }
+        DataSet dataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_EMPREL", FormConstant.HRPI_EMPPOSORGREL, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder(), 1000000);
+
+        int tempIndex = 1;
+        // 遍历指定年份范围,为每一年生成对应的岗位查询字段
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取每年12-31 23:59:59作为目标日期,用于判断该员工当年年底的岗位状态
+            Date targetDate = DateUtil.toDate(LocalDateTime.of(year, 12, 31, 23, 59, 59));
+            String targetDateStr = DateUtil.format(targetDate, DateUtil.NORM_DATE_PATTERN); // 使用日期格式而非日期时间格式
+
+            // 创建复合字段:如果在目标日期员工处于有效任职状态,则生成"结束日期|岗位名称"格式的字符串
+            // 这样使用GroupMaxStrFunction时,会根据结束日期选择最晚的那个任职记录
+            //为什么用“00000000000000|”,不用null。因为当GroupMaxStrFunction进行字符串比较时,有效的日期值(如'20231231235959|经济技术部部长')会比'00000000000000|'大,从而被正确选择
+            String combinedField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'), '|', " +
+                    String.join(".", FormConstant.POSITION_KEY, FormConstant.NAME_KEY) + ") ELSE '00000000000000|' END";
+
+            // 添加岗位复合字段(包含结束日期和岗位名称)
+            dataSet = dataSet.addField(combinedField, "position_temp_" + tempIndex);
+
+            // 同时创建仅包含日期部分的字段,用于后续解析时去除日期部分,只保留岗位名称
+            String dateField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'),'|') ELSE '00000000000000|' END";
+            dataSet = dataSet.addField(dateField, "position_date_" + tempIndex);
+
+//            dataSet = dataSet.addField("'"+year+"'", "position_year_" + tempIndex);
+            tempIndex++;
+        }
+
+        // 行转列处理:按员工ID分组,使用GroupMaxStrFunction获取每个员工每年结束日期最晚的任职记录
+        tempIndex = 1;
+        GroupMaxStrFunction groupMaxStr = new GroupMaxStrFunction();
+        // 按员工ID进行分组聚合
+        GroupbyDataSet groupbyDataSet = dataSet.groupBy(new String[]{String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)});
+        // 对每一年的数据进行聚合,获取结束日期最晚的任职记录
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取岗位复合字段的最大值(即结束日期最晚的记录)
+            groupbyDataSet.agg(new GroupMaxStrFunction(),"position_temp_"+tempIndex, "position_max_"+tempIndex);
+            // 同时获取日期字段的最大值,用于后续解析
+            groupbyDataSet.agg(groupMaxStr,"position_date_"+tempIndex, "position_date_max_"+tempIndex);
+            tempIndex++;
+        }
+        DataSet result = groupbyDataSet.finish();
+
+        // 解析复合字段,提取真正的岗位名称
+        // 通过去除日期部分(YYYYMMDDHHmmss|)来获取原始的岗位名称
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            final int currentYearIndex = year - beginYear + 1;
+            result = result.addField(
+                    // 如果岗位字段有效(不为默认值'00000000000000|'),则去除日期部分,只保留岗位名称
+                    "CASE WHEN position_max_" + tempIndex + " IS NOT NULL AND position_max_" + tempIndex + " != '00000000000000|' THEN " +
+                            "REPLACE(position_max_" + tempIndex + ",position_date_max_"+tempIndex+",'') " +  // 去除日期部分,得到岗位名称
+                            "ELSE null END",
+                    "position_" + tempIndex  // 最终的岗位名称字段
+            );
+            tempIndex++;
+        }
+        return result;
+    }
+}

+ 52 - 8
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PrintPerfDetailReportListDataPlugin.java

@@ -13,6 +13,7 @@ import kd.bos.entity.report.AbstractReportColumn;
 import kd.bos.entity.report.AbstractReportListDataPlugin;
 import kd.bos.entity.report.DynamicReportColumnEvent;
 import kd.bos.entity.report.FastFilter;
+import kd.bos.entity.report.IReportBatchQueryInfo;
 import kd.bos.entity.report.ReportColumn;
 import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.orm.query.QCP;
@@ -20,6 +21,7 @@ import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.model.PermissionStatus;
 import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
+import kd.hr.hbp.common.cache.HRAppCache;
 import kd.sdk.hr.hbp.business.helper.permission.HRPermissionServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.algo.DistinctConcatFunction;
@@ -52,10 +54,29 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         super.getDynamicColumns(event);
     }
 
+    @Override
+    public DataSet queryBatchBy(ReportQueryParam queryParam){
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .orderDesc(FormConstant.NCKD_PERSON,PerfManagerFormConstant.NCKD_BEGINYEAR);
+        QFilter qFilter = QFilter.of("1=1");
+        //权限过滤
+        QFilter dataRule = HRPermissionServiceHelper.getDataRule(
+                RequestContext.get().getCurrUserId(), "nckd_pm", PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                PermissionStatus.View, new HashMap<>());
+        if(dataRule != null){
+            qFilter.and(dataRule);
+        }
+        IReportBatchQueryInfo byBatchInfo = queryParam.byBatchInfo();
+        byBatchInfo.setCountPerBatch(2000);
+        return QueryServiceHelper.queryDataSet(this.getClass().getName(),PerfManagerFormConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder(),1000000);
+    }
 
     @Override
     public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable {
 
+        Map<Integer,Long> selectPrintPersonIdMap = HRAppCache.get("nckd_pm").get("selectPrintPersonIdMap", Map.class);
+
         // 构建查询字段
         QueryFieldBuilder queryFieldBuilder = buildQueryFieldBuilder();
 
@@ -68,12 +89,23 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         Date endDate = DateUtil.toDate(LocalDate.of(currentYear, 1, 1));
 
 
+        // 分批加载过滤
+        IReportBatchQueryInfo byBatchInfo = reportQueryParam.byBatchInfo();
+        // 获取当前批次的分批依据行
+        List<Row> currentBatchRows = byBatchInfo.getCurrentBatchRows();
+        Set<Long> idsOfCurrentBatch = new HashSet<>(3000);
+        for (Row currentBatchRow : currentBatchRows) {
+            Long matId = currentBatchRow.getLong(0);
+            idsOfCurrentBatch.add(matId);
+        }
         //查询最近5年的数据
         /*QFilter qFilter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, endDate)
                 .and(new QFilter(PerfManagerFormConstant.NCKD_ENDYEAR, QCP.large_equals, beginDate));*/
         QFilter qFilter = QFilter.of("1=1");
+        if(!idsOfCurrentBatch.isEmpty()){
+            qFilter.and(new QFilter(FormConstant.ID_KEY, QCP.in, idsOfCurrentBatch));
+        }
         qFilter.and(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.POS_STATUS,FormConstant.POST_STATE_CLS, FormConstant.NUMBER_KEY),QCP.equals,"1010_S");
-
         // 处理快速过滤条件
         processFastFilter(reportQueryParam, qFilter);
 
@@ -86,7 +118,6 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         if(dataRule != null){
             qFilter.and(dataRule);
         }
-
         // 执行基础查询
         QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("printperfquery");
         DataSet dataSet = HRQueryEntityHelper.getInstance().getQueryDataSet(queryEntityType,queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, null);
@@ -97,7 +128,9 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
             Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
             personIds.add(personId);
         }
-
+        if(personIds.isEmpty()){
+            return dataSet;
+        }
         //过滤调实际结束实际之外的年份分录
         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)");
@@ -107,8 +140,8 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         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
-            );
+                    "temp_" + tempIndex);
+            withYearFields = withYearFields.addField(year+"","temp_year_" + tempIndex);
             tempIndex++;
         }
 
@@ -134,11 +167,16 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
                 String.join(".", PerfManagerFormConstant.DOCSTAMP_ENTITYID, PerfManagerFormConstant.NCKD_STAMPTIME),
                 String.join(".", PerfManagerFormConstant.DOCSTAMP_ENTITYID, PerfManagerFormConstant.NCKD_FILEISSUETIME),
                 String.join(".", PerfManagerFormConstant.HRPI_EMPPOSORGREL, PerfManagerFormConstant.POSITION_KEY, FormConstant.ID_KEY),
+                String.join(".", PerfManagerFormConstant.HRPI_PERSERLEN, PerfManagerFormConstant.FIRSTJOINCOMDATE_KEY),
+                String.join(".", PerfManagerFormConstant.HRPI_PERSERLEN, PerfManagerFormConstant.JOINCOMDATE_KEY),
+                String.join(".", PerfManagerFormConstant.HRPI_PERSERLEN, PerfManagerFormConstant.STARTDATE),
         });
         int tempIndex1 = 1;
         for (int year = fiveYearsAgo; year <= currentYear; year++) {
             groupbyDataSet
                     .max("temp_"+tempIndex1, tempIndex1+"_result");
+            groupbyDataSet
+                    .max("temp_year_"+tempIndex1, "year_"+tempIndex1);
             tempIndex1++;
         }
         //职称/技能使用逗号分拼接
@@ -146,6 +184,7 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         groupbyDataSet.agg(new DistinctConcatFunction(),"nckd_hrpi_empskill.nckd_qualiname","qualinames");
 
         DataSet annualPerfDetailQueryDataSet = groupbyDataSet.finish();
+        annualPerfDetailQueryDataSet.print(true);
         String[] mainTableFieldNames = annualPerfDetailQueryDataSet.getRowMeta().getFieldNames();
         // 创建新数组,长度比原数组多1
         String[] newMainTableFieldNames = new String[mainTableFieldNames.length + 1];
@@ -155,8 +194,8 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         mainTableFieldNames = newMainTableFieldNames;
         annualPerfDetailQueryDataSet.select(mainTableFieldNames);
 
-//        获取近5年的岗位信息
-        DataSet positionDateSet = getPositions(fiveYearsAgo, currentYear,personIds);
+/*//        获取近5年的岗位信息
+        DataSet positionDateSet = getPositions(fiveYearsAgo, currentYear, personIds);
         String[] pivotFieldNames = positionDateSet.getRowMeta().getFieldNames();
         annualPerfDetailQueryDataSet = annualPerfDetailQueryDataSet.join(positionDateSet, JoinType.LEFT)
                 .on(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY))
@@ -175,7 +214,11 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         pivotFieldNames = filteredFieldNames.toArray(new String[0]);
         return annualPerfDetailQueryDataSet.join(perfRankMgmtDataSet, JoinType.LEFT)
                 .on(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
-                .select(mainTableFieldNames, pivotFieldNames).finish();
+                .select(mainTableFieldNames, pivotFieldNames).finish();*/
+
+
+
+        return annualPerfDetailQueryDataSet;
     }
 
     private static DataSet getPerfRankMgmt(Set<Long> personIds, int beginYear, int endYear) {
@@ -341,6 +384,7 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
                 .add(String.join(".", FormConstant.NCKD_PERSON, FormConstant.BIRTHDAY))
                 .add(String.join(".", FormConstant.NCKD_PERSON, FormConstant.GENDER,FormConstant.NAME_KEY))
                 .add(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY,FormConstant.NAME_KEY))
+                .addGroup(new String[]{FormConstant.HRPI_PERSERLEN},FormConstant.FIRSTJOINCOMDATE_KEY,FormConstant.JOINCOMDATE_KEY,FormConstant.STARTDATE)
                 .addIdNumberName(FormConstant.HRPI_EMPENTREL,FormConstant.LABOR_REL_STATUS)
                 .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_FIRSTORG)
                 .addIdNumberName(FormConstant.HRPI_EMPPOSORGREL,FormConstant.ADMINORG,FormConstant.NCKD_SECONDORG)

+ 64 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/SelectDatePrintFormPlugin.java

@@ -0,0 +1,64 @@
+package nckd.jxccl.opmc.pm.plugin.form.print;
+
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.field.DateEdit;
+import kd.bos.form.plugin.AbstractFormPlugin;
+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.opmc.pm.common.PerfManagerFormConstant;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EventObject;
+
+/**
+* 年度调整生效表单插件
+* @author W.Y.C
+* @date 2025/10/15 10:14
+* @version 1.0
+*/
+public class SelectDatePrintFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
+        if(success && FormConstant.AFFIRM_OP.equalsIgnoreCase(operateKey)){
+            Object value = this.getModel().getValue("nckd_printdate");
+            this.getView().returnDataToParent(value);
+            this.getView().close();
+        }
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+        // 调用方法,设置日期控件的可选范围
+        setDateFieldLimit();
+    }
+
+    /**
+     * 设置日期字段的可选范围为近5年(例如2026年时,可选2021-2025年)
+     */
+    private void setDateFieldLimit() {
+        // 1. 获取日期控件对象
+        DateEdit dateEdit = this.getView().getControl("nckd_printdate");
+        if (dateEdit == null) {
+            return;
+        }
+
+        // 2. 计算日期范围
+        Calendar calendar = Calendar.getInstance();
+        int currentYear = calendar.get(Calendar.YEAR);
+
+        Date beginYear = DateUtil.minusYears(DateUtil.nowDate(), 5);
+        Date endYear = DateUtil.minusYears(DateUtil.nowDate(), 1);
+        // 3. 设置控件的可选范围
+
+        dateEdit.setMinDate(beginYear);
+        dateEdit.setMaxDate(endYear);
+
+    }
+}

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

@@ -7,14 +7,16 @@ import kd.bos.entity.operate.OperateOptionConst;
 import kd.bos.entity.operate.result.IOperateInfo;
 import kd.bos.entity.operate.result.OperateErrorInfo;
 import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.entity.validate.ErrorLevel;
 import kd.bos.form.FormShowParameter;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.ShowType;
 import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.events.PreOpenFormEventArgs;
-import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.report.ReportList;
+import kd.bos.report.ReportShowParameter;
+import kd.bos.report.plugin.AbstractReportFormPlugin;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
@@ -35,7 +37,7 @@ import java.util.stream.Stream;
 * @date 2025/11/15 21:51
 * @version 1.0
 */
-public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Plugin {
+public class AnnualPerfDetailFormPlugin extends AbstractReportFormPlugin implements Plugin {
 
     @Override
     public void preOpenForm(PreOpenFormEventArgs e) {
@@ -46,6 +48,13 @@ public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Pl
         super.preOpenForm(e);
     }
 
+    @Override
+    public void beforeQuery(ReportQueryParam queryParam) {
+        ReportList reportList = this.getControl("reportlistap");
+        reportList.setSelectedAll(true);
+    }
+
+
     @Override
     public void afterDoOperation(AfterDoOperationEventArgs e) {
         if(e.getOperationResult() != null && e.getOperationResult().isSuccess()) {
@@ -107,6 +116,12 @@ public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Pl
                         this.getView().showConfirm("提示",result.getMessage(), MessageBoxOptions.OK,null,null,null,null);
                     }
                 }
+            }else if("printlist".equalsIgnoreCase(operateKey)){
+
+                ReportShowParameter reportShowParameter = new ReportShowParameter();
+                reportShowParameter.setFormId("nckd_printlistrpt");
+                reportShowParameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+                this.getView().showForm(reportShowParameter);
             }
         }
     }

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

@@ -2,17 +2,21 @@ package nckd.jxccl.opmc.pm.plugin.form.result;
 
 import kd.bos.algo.DataSet;
 import kd.bos.algo.GroupbyDataSet;
+import kd.bos.algo.Row;
 import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.entity.LocaleString;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.QueryEntityType;
 import kd.bos.entity.report.AbstractReportColumn;
 import kd.bos.entity.report.AbstractReportListDataPlugin;
 import kd.bos.entity.report.FastFilter;
+import kd.bos.entity.report.IReportBatchQueryInfo;
 import kd.bos.entity.report.ReportColumn;
 import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.model.PermissionStatus;
 import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
 import kd.sdk.hr.hbp.business.helper.permission.HRPermissionServiceHelper;
@@ -26,8 +30,10 @@ import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 import java.time.LocalDate;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
 * 年度绩效结果明细-报表取数插件
@@ -38,6 +44,23 @@ import java.util.Map;
 */
 public class AnnualPerfDetailReportListDataPlugin extends AbstractReportListDataPlugin implements Plugin {
 
+    @Override
+    public DataSet queryBatchBy(ReportQueryParam queryParam){
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .orderDesc(FormConstant.NCKD_PERSON,PerfManagerFormConstant.NCKD_BEGINYEAR);
+        QFilter qFilter = QFilter.of("1=1");
+        //权限过滤
+        QFilter dataRule = HRPermissionServiceHelper.getDataRule(
+                RequestContext.get().getCurrUserId(), "nckd_pm", PerfManagerFormConstant.PERFMANAGER_ENTITYID,
+                PermissionStatus.View, new HashMap<>());
+        if(dataRule != null){
+            qFilter.and(dataRule);
+        }
+        IReportBatchQueryInfo byBatchInfo = queryParam.byBatchInfo();
+        byBatchInfo.setCountPerBatch(3000);
+        return QueryServiceHelper.queryDataSet(this.getClass().getName(),PerfManagerFormConstant.PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder(),1000000);
+    }
 
     @Override
     public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable {
@@ -52,10 +75,25 @@ public class AnnualPerfDetailReportListDataPlugin extends AbstractReportListData
 
         int currentYear = LocalDate.now().getYear(); // 不包含今年
         Date endDate = DateUtil.toDate(LocalDate.of(currentYear, 1, 1));
+
+        // 分批加载过滤
+        IReportBatchQueryInfo byBatchInfo = reportQueryParam.byBatchInfo();
+        // 获取当前批次的分批依据行
+        List<Row> currentBatchRows = byBatchInfo.getCurrentBatchRows();
+        Set<Long> idsOfCurrentBatch = new HashSet<>(3000);
+        for (Row currentBatchRow : currentBatchRows) {
+            Long matId = currentBatchRow.getLong(0);
+            idsOfCurrentBatch.add(matId);
+        }
         //查询最近5年的数据
         /*QFilter qFilter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, endDate)
                 .and(new QFilter(PerfManagerFormConstant.NCKD_ENDYEAR, QCP.large_equals, beginDate));*/
         QFilter qFilter = QFilter.of("1=1");
+        if(!idsOfCurrentBatch.isEmpty()){
+            qFilter.and(new QFilter(FormConstant.ID_KEY, QCP.in, idsOfCurrentBatch));
+        }
+
+
         qFilter.and(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.POS_STATUS,FormConstant.POST_STATE_CLS, FormConstant.NUMBER_KEY),QCP.equals,"1010_S");
 
         // 处理快速过滤条件
@@ -76,12 +114,10 @@ public class AnnualPerfDetailReportListDataPlugin extends AbstractReportListData
         QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("annualperfdetailquery");
         DataSet dataSet = HRQueryEntityHelper.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;

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

@@ -262,19 +262,20 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
                                     // 判断周期是否重叠并获取重叠信息,已结束周期使用实际结束时间
                                     String overlapInfo = getCycleOverlapInfo(beginYear, endYear, null, dbBeginYear, dbEndYear, dbActEndYear);
                                     if (StringUtils.isNotBlank(overlapInfo)) {
-                                        /*addFatalErrorMessage(rowDataEntity,
+                                        addFatalErrorMessage(rowDataEntity,
                                                 StrFormatter.format("人员【{}】的考核周期与已有周期在{}重叠,请检查!",
-                                                        personName, overlapInfo));*/
+                                                        personName, overlapInfo));
                                     }
                                 }
-                            }
 
-                            //判断beginYear和dbBeginYear的年份是否相同
-                            if (isSameYear(beginYear, dbBeginYear)) {
-                                /*addFatalErrorMessage(rowDataEntity,
-                                        StrFormatter.format("人员【{}】已经存在周期开始年份【{}】的周期,无需进行创建。",
-                                                personName,
-                                                beginYear.getYear()));*/
+
+                                //判断beginYear和dbBeginYear的年份是否相同
+                                if (isSameYear(beginYear, dbBeginYear)) {
+                                    addFatalErrorMessage(rowDataEntity,
+                                            StrFormatter.format("人员【{}】已经存在周期开始年份【{}】的周期,无需进行创建。",
+                                                    personName,
+                                                    beginYear.getYear()));
+                                }
                             }
                         }
 
@@ -290,34 +291,35 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
                     //校验是否存在相同周期开始年的记录 end
                     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) {
-                            // 校验考核年份是否在周期内
-                            List<String> outOfRangeYears = entrys.stream()
-                                    .map(entry -> entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR))
-                                    .filter(Objects::nonNull)
-                                    .filter(date -> !DateUtil.isInRange(DateUtil.toLocalDateTime(date), beginYear, endYear))
-                                    .map(date -> String.valueOf(DateUtil.getYear(DateUtil.toLocalDateTime(date))))
-                                    .distinct()
-                                    .collect(Collectors.toList());
-
-                            if (!outOfRangeYears.isEmpty()) {
-                                String outOfRangeYearsStr = String.join(",", outOfRangeYears);
+                    if(!dataMigration) {
+                        if (entrys != null) {
+                            List<Date> dateList = entrys.stream().map(entry -> entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR)).collect(Collectors.toList());
+                            if (beginYear != null && endYear != null) {
+                                // 校验考核年份是否在周期内
+                                List<String> outOfRangeYears = entrys.stream()
+                                        .map(entry -> entry.getDate(PerfManagerFormConstant.NCKD_APPRAISALYEAR))
+                                        .filter(Objects::nonNull)
+                                        .filter(date -> !DateUtil.isInRange(DateUtil.toLocalDateTime(date), beginYear, endYear))
+                                        .map(date -> String.valueOf(DateUtil.getYear(DateUtil.toLocalDateTime(date))))
+                                        .distinct()
+                                        .collect(Collectors.toList());
+
+                                if (!outOfRangeYears.isEmpty()) {
+                                    String outOfRangeYearsStr = String.join(",", outOfRangeYears);
+                                    addFatalErrorMessage(rowDataEntity,
+                                            StrFormatter.format("考评年份【{}】不在周期范围内,请检查!", outOfRangeYearsStr));
+                                }
+                            }
+                            // 校验考核年份是否重复
+                            List<String> duplicateYears = getDuplicateYears(dateList);
+                            if (!duplicateYears.isEmpty()) {
+                                String duplicateYearsStr = String.join(",", duplicateYears);
                                 addFatalErrorMessage(rowDataEntity,
-                                        StrFormatter.format("考评年份【{}】不在周期范围内,请检查!", outOfRangeYearsStr));
+                                        StrFormatter.format("考评结果存在重复的考核年份【{}】,请检查!", duplicateYearsStr));
                             }
-                        }
-                        // 校验考核年份是否重复
-                        List<String> duplicateYears = getDuplicateYears(dateList);
-                        if (!duplicateYears.isEmpty()) {
-                            String duplicateYearsStr = String.join(",", duplicateYears);
-                            addFatalErrorMessage(rowDataEntity,
-                                    StrFormatter.format("考评结果存在重复的考核年份【{}】,请检查!", duplicateYearsStr));
-                        }
 
-                        //如果是更新则需要校验是否更改已归档的考核结果
-                        if(isUpdate) {
+                            //如果是更新则需要校验是否更改已归档的考核结果
+                            if (isUpdate) {
 
 
                            /* if(!entryIds.isEmpty() && !entryResultMap.isEmpty()) {
@@ -339,16 +341,17 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
                                     }
                                 }
                             }*/
-                            if(!dbMap.isEmpty()){
-                                DynamicObject dbPerManager = dbMap.get(id);
-                                boolean salaryAdjustGenFlag = dbPerManager.getBoolean(PerfManagerFormConstant.NCKD_SALARYADJUSTGENFLAG);
-                                if(salaryAdjustGenFlag){
-                                    addFatalErrorMessage(rowDataEntity,
-                                            StrFormatter.format("人员【{}】已生成调档,不能修改。",personName));
+                                if (!dbMap.isEmpty()) {
+                                    DynamicObject dbPerManager = dbMap.get(id);
+                                    boolean salaryAdjustGenFlag = dbPerManager.getBoolean(PerfManagerFormConstant.NCKD_SALARYADJUSTGENFLAG);
+                                    if (salaryAdjustGenFlag) {
+                                        addFatalErrorMessage(rowDataEntity,
+                                                StrFormatter.format("人员【{}】已生成调档,不能修改。", personName));
+                                    }
                                 }
                             }
-                        }
 
+                        }
                     }
 
                     if(isUpdate){
@@ -467,32 +470,48 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
     @Override
     public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
-        if(!this.getOperationResult().isSuccess()){
-            e.setCancel(true);
-            return;
+
+        boolean dataMigration;
+        Object invoker = this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        if(ObjectUtils.isEmpty(invoker)){
+            Object invoker1 = this.getOption().getVariables().get("hr_hrdmvalidatetag_of_datasource");
+            Object invoker2 = this.getOption().getVariables().get("hr_hrdmsynctag_of_datasource");
+            if(!ObjectUtils.isEmpty(invoker1) || !ObjectUtils.isEmpty(invoker2)){
+                dataMigration = true;
+            } else {
+                dataMigration = false;
+            }
+        } else{
+            dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker.toString());
         }
-        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);
+        if(!dataMigration) {
+            if(!this.getOperationResult().isSuccess()){
+                e.setCancel(true);
+                return;
+            }
+            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()));
+                    }
                 }
-                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 (confirmMsg.length() > 0) {
+                    e.cancel = !this.showInteractionMessage(confirmMsg.toString());
                 }
             }
-            if (confirmMsg.length() > 0) {
-                e.cancel = !this.showInteractionMessage(confirmMsg.toString());
-            }
         }
 
     }
@@ -501,6 +520,31 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
         //事务开始之后将其他考核周期设置为非最新
         PerfManagerHelper.markAsNotCurrentNewest(personBeginYearMap.keySet().toArray(new Long[0]));
 
+        boolean dataMigration;
+        Object invoker = this.getOption().getVariables().get(FormConstant.HR_INVOKER_PARAM_INVOKER);
+        if(ObjectUtils.isEmpty(invoker)){
+            Object invoker1 = this.getOption().getVariables().get("hr_hrdmvalidatetag_of_datasource");
+            Object invoker2 = this.getOption().getVariables().get("hr_hrdmsynctag_of_datasource");
+            if(!ObjectUtils.isEmpty(invoker1) || !ObjectUtils.isEmpty(invoker2)){
+                dataMigration = true;
+            } else {
+                dataMigration = false;
+            }
+        } else{
+            dataMigration = FormConstant.DATA_MIGRATION.equalsIgnoreCase(invoker.toString());
+        }
+        Map<Long, DynamicObject> empPosOrgRelMap = new HashMap<>();
+        if(dataMigration){
+            List<Long> personIds = new ArrayList<>();
+            for (DynamicObject dataEntity : e.getDataEntities()) {
+                DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+                if(person != null){
+                    personIds.add(person.getLong(FormConstant.ID_KEY));
+                }
+            }
+            empPosOrgRelMap = EmpPosOrgRelHelper.queryEmpPosOrgRelWithOutPostByEmployeesMap(personIds);
+        }
+
         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<>();
@@ -509,7 +553,16 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
         for (DynamicObject dataEntity : e.getDataEntities()) {
             long id = dataEntity.getLong(FormConstant.ID_KEY);
             DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
+            if(dataMigration) {
+                long personId = person.getLong(FormConstant.ID_KEY);
+                DynamicObject empPosOrgRel = empPosOrgRelMap.get(personId);
+                dataEntity.set(FormConstant.NCKD_EMPPOSORGREL,empPosOrgRel);
+            }
             DynamicObject empPosOrgRel = dataEntity.getDynamicObject(FormConstant.NCKD_EMPPOSORGREL);
+            if(empPosOrgRel == null){
+                continue;
+            }
+
             person = person == null ? empPosOrgRel.getDynamicObject(FormConstant.EMPLOYEE_KEY) : person;
             dataEntity.set(FormConstant.NCKD_PERSON, person);
             LocalDateTime beginYear = DateUtil.toLocalDateTime(dataEntity.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));

+ 19 - 17
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/salary/PushAdjustOpPlugin.java

@@ -3,33 +3,28 @@ package nckd.jxccl.opmc.pm.plugin.operate.salary;
 import com.alibaba.fastjson.JSON;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
-import kd.bos.db.tx.TX;
-import kd.bos.db.tx.TXHandle;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
 import kd.bos.entity.plugin.PreparePropertysEventArgs;
 import kd.bos.entity.plugin.args.AfterOperationArgs;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
-import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
 import kd.bos.entity.validate.AbstractValidator;
-import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.DispatchServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
-import kd.sdk.swc.hcdm.business.helper.HCDMApplyBillServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.exception.ValidationException;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.opmc.pm.common.SalAdjTrackerConstant;
 
-import java.math.BigDecimal;
 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.UUID;
 
 /**
@@ -76,7 +71,14 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
     List<DynamicObject> pushAdjusts = new ArrayList<>();
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
-
+        Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, FormConstant.POS_STANDARD_ITEM_NUMBER);
+        Long billTypeId = EntityHelper.getIdByNumber(FormConstant.BOS_BILLTYPE, FormConstant.HCDM_APPLYBILL_BT_ADJ);
+        Long countryId = EntityHelper.getIdByNumber(FormConstant.BD_COUNTRY, "001");
+        Long salaryAdjuStrSnId = EntityHelper.getIdByNumber(FormConstant.HSBS_SALARYADJUSTRSN, "kaohe");
+        Long currencyId = EntityHelper.getIdByNumber(FormConstant.BD_CURRENCY, "CNY");
+        Long adjApproveScmId = EntityHelper.getLatestIdByNumber(FormConstant.HCDM_ADJAPPROVESCM, "dingtiaoxin");
+        Long exRateTableId = EntityHelper.getIdByNumber(FormConstant.BD_EXRATETABLE, "ERT-01");
+        Long calFrequencyId = EntityHelper.getIdByNumber(FormConstant.HSBS_CALFREQUENCY, "000_m_001");
         String operateKey = e.getOperationKey();
 
         List<DynamicObject> updateAdjusts = new ArrayList<>();
@@ -114,19 +116,19 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
                     applyBill.put("_uniquecode", uniquecode);
                     applyBill.put("org", orgId);
                     //定调薪明细字段显示方案   调薪明细字段
-                    applyBill.put("billtype", 2215975998602655744L);
+                    applyBill.put("billtype", billTypeId);
                     //国家
-                    applyBill.put("billcountry", 1000001L);
+                    applyBill.put("billcountry", countryId);
                     //定调薪类型
-                    applyBill.put("salaryadjrsn", 2352337648716103680L);
+                    applyBill.put("salaryadjrsn", salaryAdjuStrSnId);
                     //默认币种
-                    applyBill.put("billcurrency", 1L);
+                    applyBill.put("billcurrency", currencyId);
                     //定调薪方案
-                    applyBill.put("salaryadjscm", 2322515162646457344L);
+                    applyBill.put("salaryadjscm", adjApproveScmId);
                     //汇率日期
                     applyBill.put("exchangeratedate", new Date());
                     //汇率表
-                    applyBill.put("exctable", 2321965096026258432L);
+                    applyBill.put("exctable", exRateTableId);
                     //默认生效日期
                     applyBill.put("effectivedate", new Date());
                     //草稿状态
@@ -142,8 +144,8 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
                     Long positionId = pushAdjust.getDynamicObject(SalAdjTrackerConstant.NCKD_POSITION).getLong(FormConstant.ID_KEY);
                     applyBillEntry.put("adjfile", adjFileInfo.getLong(FormConstant.ID_KEY));
                     applyBillEntry.put("employee", employeeId);
-                    applyBillEntry.put("standarditem", 2321899710350111744L);    //定调薪项目   岗位工资标准
-                    applyBillEntry.put("frequency", 1095454108284088320L);       //频度  月
+                    applyBillEntry.put("standarditem", salaryItemId);    //定调薪项目   岗位工资标准
+                    applyBillEntry.put("frequency", calFrequencyId);       //频度  月
                     applyBillEntry.put("amount", pushAdjust.getBigDecimal(SalAdjTrackerConstant.NCKD_MONEY));
 //            applyBillEntry.put("nckd_postgrade", salaryfile.getLong("position.nckd_postgrade.id"));  //岗级
                     applyBillEntry.put("position", positionId);
@@ -159,7 +161,7 @@ public class PushAdjustOpPlugin extends AbstractOperationServicePlugIn implement
             if(!applyBillData.isEmpty()) {
                 papams.put("data", applyBillData);
                 papams.put("isUseMatchAmount", Boolean.TRUE);
-                Map<String, Object> result = HCDMApplyBillServiceHelper.saveDraftApplyBill(papams);
+                Map<String, Object> result = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "saveDraftApplyBill", papams);
                 if (!ConvertUtil.toBoolean(result.get("success")) || result.get("data") == null || ConvertUtil.toList(result.get("data")).isEmpty()) {
                     throw new ValidationException("推送定调薪失败,原因:" + JSON.toJSONString(result));
                 }

+ 12 - 16
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/salary/SalaryAdjOpPlugin.java

@@ -26,14 +26,9 @@ import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.bos.servicehelper.user.UserServiceHelper;
-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 kd.sdk.swc.hcdm.business.helper.HCDMAdjFileServiceHelper;
-import kd.sdk.swc.hcdm.business.helper.HCDMSalaryStdServiceHelper;
-import kd.sdk.swc.hcdm.common.dto.stdtab.match.StdTableDataMatchParam;
-import kd.sdk.swc.hcdm.common.dto.stdtab.match.StdTableDataMatchResult;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.exception.ValidationException;
@@ -119,6 +114,7 @@ public class SalaryAdjOpPlugin extends AbstractOperationServicePlugIn implements
                 ));
 
         List<SalaryAdjustmentResult> salaryAdjustmentResultList = new ArrayList<>();
+        Long salaryItemId = EntityHelper.getIdByNumber(FormConstant.HSBS_STANDARDITEM, FormConstant.POS_STANDARD_ITEM_NUMBER);
         for (DynamicObject perfManager : perfManagerList) {
             long id = perfManager.getLong(FormConstant.ID_KEY);
             if(perfManagerToSalaryAdjMap.get(id) == null) {
@@ -215,7 +211,7 @@ public class SalaryAdjOpPlugin extends AbstractOperationServicePlugIn implements
                         .collect(Collectors.toList());
 
                 DynamicObject standardItem = EntityHelper.newEntity(FormConstant.HSBS_STANDARDITEM);
-                standardItem.set(FormConstant.ID_KEY, FormConstant.STANDARDITEM_ID_KEY);
+                standardItem.set(FormConstant.ID_KEY, salaryItemId);
                 Map<String, Object> adjFileParams = new HashMap<>();
                 adjFileParams.put("employees", allPersonIds);
                 List<String> status = new ArrayList<>();
@@ -242,7 +238,7 @@ public class SalaryAdjOpPlugin extends AbstractOperationServicePlugIn implements
                         // 调薪档案ID
                         dataItem.put("adjfile", adjFileId);
                         // 调薪项目ID
-                        dataItem.put("standarditem", FormConstant.STANDARDITEM_ID_KEY);
+                        dataItem.put("standarditem", salaryItemId);
                         // 查询基准日期
                         dataItem.put("startdate", new Date());
                         // 唯一标识
@@ -254,7 +250,7 @@ public class SalaryAdjOpPlugin extends AbstractOperationServicePlugIn implements
                     adjRecordParams.put("data", dataList);
                     adjRecordParams.put("selprops", "salarystdv.rankentry.rank.id,salarystdv.rankentry.rank.name,salarystdv.rankentry.rank.number,salarystdv.rankentry.rank.index,salarystdv.rankentry.frankindex,company.id,company.name,");
                     //查询定调薪记录
-                    Map<String, Object> lastDecAdjRecordMap = HCDMAdjFileServiceHelper.getLastDecAdjRecords(adjRecordParams);
+                    Map<String, Object> lastDecAdjRecordMap = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "getLastDecAdjRecords", adjRecordParams);
                     if (ConvertUtil.toBoolean(lastDecAdjRecordMap.get("success"))) {
                         List<Map> datas = ConvertUtil.toList(lastDecAdjRecordMap.get("data"), ArrayList::new);
                         for (Map data : datas) {
@@ -365,17 +361,17 @@ public class SalaryAdjOpPlugin extends AbstractOperationServicePlugIn implements
                             }
                             result.salaryRank = newSalaryRank;
                             result.reason += StrFormatter.format(";{}->{}", oldSalaryRank.getString(FormConstant.NAME_KEY), newSalaryRank.getString(FormConstant.NAME_KEY));
-                            List<StdTableDataMatchParam> matchParams = new ArrayList<>();
-                            StdTableDataMatchParam stdTableDataMatchParam = new StdTableDataMatchParam();
-                            stdTableDataMatchParam.setStdTableId(result.salaryStDv.getLong(FormConstant.ID_KEY));
-                            stdTableDataMatchParam.setStdItemId(FormConstant.STANDARDITEM_ID_KEY);
-                            stdTableDataMatchParam.setGradeId(result.oldSalaryGrade.getLong(FormConstant.ID_KEY));
-                            stdTableDataMatchParam.setRankId(newSalaryRank.getLong(FormConstant.ID_KEY));
+                            List<Map<String,Object>> matchParams = new ArrayList<>();
+                            Map<String,Object> stdTableDataMatchParam = new HashMap<>();
+                            stdTableDataMatchParam.put("stdTableId", result.salaryStDv.getLong(FormConstant.ID_KEY));
+                            stdTableDataMatchParam.put("stdItemId", salaryItemId);
+                            stdTableDataMatchParam.put("gradeId", result.oldSalaryGrade.getLong(FormConstant.ID_KEY));
+                            stdTableDataMatchParam.put("rankId", newSalaryRank.getLong(FormConstant.ID_KEY));
                             matchParams.add(stdTableDataMatchParam);
                             //获取薪酬项目、薪等、薪档对应金额
-                            List<StdTableDataMatchResult> stdTableDataMatchResults = HCDMSalaryStdServiceHelper.matchStdTableData(matchParams);
+                            List<Map<String,Object>> stdTableDataMatchResults = DispatchServiceHelper.invokeService("nckd.jxccl.swc.hcdm.servicehelper", "hcdm", "ISwcService", "matchStdTableData", matchParams);
                             if (!stdTableDataMatchResults.isEmpty()) {
-                                result.amount = stdTableDataMatchResults.get(0).getAmount();
+                                result.amount = ConvertUtil.toBigDecimal(stdTableDataMatchResults.get(0).get("amount"));
                             }
                             msgJoiner.add(StrFormatter.format("【{}】{}", result.personName, result.reason));
                         }

+ 74 - 9
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/annualincome/AnnualIncomeCalculateService.java

@@ -37,6 +37,7 @@ public class AnnualIncomeCalculateService {
     }
 
     /**
+     * 已确认的数据不做调整,未确认的数据更新
      * 计算步骤:
      * 1.根据参保单位获取到人员
      * 2.根据1获取到的人员去取薪酬模块-年收入统计中的薪酬数据
@@ -49,6 +50,9 @@ public class AnnualIncomeCalculateService {
         Map<Long, DynamicObject> fileMap = getSinSurFile();
         // 根据参保单位获取人员
         Set<Long> employeeIds = fileMap.keySet();
+        // 根据参保单位+年度获取到现在已经生成数据的人员,用于排除
+        Set<Long> excludeEmployee = getExcludeEmployee();
+        employeeIds.removeAll(excludeEmployee);
         // 取薪酬-年收入统计单中的数据
         Map<Long, Map> incomeBillData = getSalIncomeBillData(employeeIds);
         // 业务项目
@@ -63,6 +67,41 @@ public class AnnualIncomeCalculateService {
         return successCount;
     }
 
+
+    /**
+     * 根据参保单位+年度获取已确认的年收入证明单数据
+     *
+     * @return
+     */
+    public Set<Long> getExcludeEmployee() {
+        QFilter filter = new QFilter("nckd_year", QCP.equals, year);
+        filter.and("nckd_welfarepayer", QCP.equals, welfarePayer.getLong("id"));
+        filter.and("billstatus", QCP.in, new String[]{"C", "H"});
+        String selectFields = "nckd_employee.id";
+        DynamicObjectCollection cols = SitConstant.SITINCOMEBILL_HELPER.queryOriginalCollection(selectFields, new QFilter[]{filter});
+        return cols.stream().map(dyx -> dyx.getLong("nckd_employee.id")).collect(Collectors.toSet());
+    }
+
+    /**
+     * 根据参保单位+年度获取需要更新的单据数据
+     * k = employee.id , v = 单据数据包
+     * @return
+     */
+    public Map<Long, DynamicObject> getExistBillInfos() {
+        QFilter filter = new QFilter("nckd_year", QCP.equals, year);
+        filter.and("nckd_welfarepayer", QCP.equals, welfarePayer.getLong("id"));
+        filter.and("billstatus", QCP.in, new String[]{"A", "B"});
+        String selectFields = "billstatus,auditstatus,nckd_year,nckd_employee,nckd_datastatus,nckd_welfarepayer,nckd_sinsurfile,org,billno,nckd_sysmonth,nckd_sysyearamount,nckd_outmonth,nckd_outyearamount,nckd_totalamount";
+        DynamicObject[] cols = SitConstant.SITINCOMEBILL_HELPER.load(selectFields, new QFilter[]{filter});
+        return Arrays.stream(cols).collect(Collectors.toMap((dyx) -> {
+            return dyx.getLong("nckd_employee.id");
+        }, (dyx) -> {
+            return dyx;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+    }
+
     /**
      * 更新年收入统计单状态
      * @param employeeIds
@@ -93,19 +132,32 @@ public class AnnualIncomeCalculateService {
      * @return
      */
     public int dealData(Map<Long, DynamicObject> fileMap, Set<Long> employeeIds, Map<Long, Map> incomeBillData, Map<Long, DynamicObject> bizItemMap, Map<Long, Map<Long, String>> bizData) {
-        DynamicObjectCollection billCols = new DynamicObjectCollection();
+        //DynamicObjectCollection billCols = new DynamicObjectCollection();
+        List<DynamicObject> addnewCols = new ArrayList<>();
+        List<DynamicObject> updateCols = new ArrayList<>();
+        Map<Long, DynamicObject> existBillInfosMap = getExistBillInfos();
+        Set<Long> existEmployeeIds = existBillInfosMap.keySet();
         for(Long employeeId : employeeIds) {
-            DynamicObject dyn = SitConstant.SITINCOMEBILL_HELPER.generateEmptyDynamicObject();
+            DynamicObject dyn;
+            boolean exists = false;
+            if(existEmployeeIds.contains(employeeId)) {
+                exists = true;
+                dyn = existBillInfosMap.get(employeeId);
+            }
+            else {
+                exists = false;
+                dyn = SitConstant.SITINCOMEBILL_HELPER.generateEmptyDynamicObject();
+                dyn.set("billstatus", "A");
+                dyn.set("auditstatus", "A");
+                dyn.set("nckd_datastatus", "A");
+                dyn.set("billno", UUID.randomUUID().toString().substring(0,29));
+            }
             // 默认值字段
-            dyn.set("billstatus", "A");
-            dyn.set("auditstatus", "A");
             dyn.set("nckd_year", year);
             dyn.set("nckd_employee", employeeId);
-            dyn.set("nckd_datastatus", "A");
             dyn.set("nckd_welfarepayer", welfarePayer);
             dyn.set("nckd_sinsurfile", fileMap.get(employeeId).getLong("id"));
             dyn.set("org", fileMap.get(employeeId).getLong("org.id"));
-            dyn.set("billno", UUID.randomUUID().toString().substring(0,29));
 
             BigDecimal amount1 = BigDecimal.ZERO;
             BigDecimal amount2 = BigDecimal.ZERO;
@@ -140,11 +192,24 @@ public class AnnualIncomeCalculateService {
             // 申报年度社保缴费基数 = (江铜内年度收入 + 江铜外年度收入) / (江铜内发放月数 + 外单位发放月数)
             BigDecimal totalAmount = calculateTotalAmount(amount1, amount2, month1, month2);
             dyn.set("nckd_totalamount", totalAmount);
-            billCols.add(dyn);
+            if(exists) {
+                updateCols.add(dyn);
+            }
+            else {
+                addnewCols.add(dyn);
+            }
         }
         // 保存数据
-        Object[] saveDyns = SaveServiceHelper.save(billCols.stream().toArray(DynamicObject[]::new));
-        int successCount = saveDyns.length;
+        int successCount = 0;
+        if(addnewCols.size() > 0) {
+            Object[] addnewDyns = SaveServiceHelper.save(addnewCols.stream().toArray(DynamicObject[]::new));
+            successCount += addnewDyns.length;
+        }
+        // 更新数据
+        if(updateCols.size() > 0) {
+            Object[] updateDyns = SaveServiceHelper.save(updateCols.stream().toArray(DynamicObject[]::new));
+            successCount += updateDyns.length;
+        }
         return successCount;
     }
 

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

@@ -1,6 +1,5 @@
 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;
@@ -14,7 +13,6 @@ 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.*;
 import java.util.stream.Collectors;
@@ -74,7 +72,7 @@ public class SinsurCoordBizSaveHandler implements CoordBizSaveHandler {
                 dyn.set("employee", sinsurFile.getDynamicObject("employee"));
                 dyn.set("welfarepayer", sinsurFile.getDynamicObject("welfarepayer"));
                 dyn.set("nckd_welfarepayertheory", sinsurFile.getDynamicObject("welfarepayertheory"));
-                dyn.set("id", Uuid.create());
+                //dyn.set("id", UUID.randomUUID().toString());
                 Iterator it = fieldMap.keySet().iterator();
                 while (it.hasNext()) {
                     String key = it.next().toString();

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

@@ -0,0 +1,14 @@
+package nckd.jxccl.sit.hcsi.business.coordination.api;
+
+import kd.hr.hbp.business.coordination.api.ext.CoordExtRegister;
+import kd.hr.hbp.business.coordination.model.CoordContext;
+import kd.hr.hbp.business.coordination.model.VerifBillStageEnum;
+
+public class SinsurCoordExtRegister implements CoordExtRegister {
+
+    @Override
+    public void registerHandler(CoordContext context) {
+        CoordExtRegister.super.registerHandler(context);
+        context.addVerifyBillBeforeSaveHandler(VerifBillStageEnum.AFTER_RULE_EXECUTE, new SinsurVerifBillBeforeSaveHandlerEx());
+    }
+}

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

@@ -0,0 +1,85 @@
+package nckd.jxccl.sit.hcsi.business.coordination.api;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.hr.hbp.business.coordination.api.ext.VerifBillBeforeSaveHandler;
+import kd.hr.hbp.business.coordination.model.CoordContext;
+import kd.hr.hbp.business.coordination.model.VerifBillStageEnum;
+import nckd.jxccl.sit.hcsi.utils.SITCoordinationUtils;
+
+import java.util.Map;
+
+/**
+ * @author: Tyx
+ * @date: 2026/01/08
+ * @description: 社保核定单保存前携带险种基数信息
+ */
+public class SinsurVerifBillBeforeSaveHandlerEx implements VerifBillBeforeSaveHandler {
+
+    private static final Log log = LogFactory.getLog(SinsurVerifBillBeforeSaveHandlerEx.class);
+
+    /**
+     * 社保核定单保存前方法:<p>
+     * 适用单据:核定单类型 = 社保 and 变动大类 = 调动 and 流动类型 = 流入 and 是否拆单 = 否
+     * @param dynamicObjects
+     * @param verifBillStageEnum
+     * @param coordContext
+     * @return
+     */
+    @Override
+    public Map<String, Object> beforeSave(DynamicObject[] dynamicObjects, VerifBillStageEnum verifBillStageEnum, CoordContext coordContext) {
+        // log.info("-------- 核定单 beforeSave 处理条数:" + dynamicObjects.length);
+
+        for (DynamicObject bill : dynamicObjects) {
+            if(checkIsNeedSiBaseInfo(bill)) {
+                dealSiBaseInfo(bill);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 判断当前核定单是否需要处理
+     * @param bill
+     * @return
+     */
+    public boolean checkIsNeedSiBaseInfo (DynamicObject bill) {
+        boolean isNeed = false;
+        boolean isSplit = bill.getBoolean("nckd_issplit");
+
+        // 协作业务对象
+        String objectNumber = bill.getDynamicObject("coordbizobject").getString("number");
+
+        DynamicObject ruleParam = bill.getDynamicObject("coordruleparam");
+        // 获取到变动大类和变动类型
+        String chgNumber = ruleParam.getString("chgevent_z.number");
+        String flowType = ruleParam.getString("flowtype_y");
+        if(objectNumber.equals("hcsi_sinsurfile") && !isSplit && chgNumber.equals("1020_S") && flowType.equals("1")) {
+            return true;
+        }
+        return isNeed;
+    }
+
+    /**
+     * 根据员工查询所有社保基数设置,并赋值到单据体中
+     * @param bill
+     */
+    public void dealSiBaseInfo (DynamicObject bill) {
+        DynamicObject ruleParam = bill.getDynamicObject("coordruleparam");
+        DynamicObject employee = ruleParam.getDynamicObject("employee");
+        DynamicObject[] sibaseCols = SITCoordinationUtils.getSiBaseByEmployeeId(employee.getLong("id"));
+        if(sibaseCols.length == 0)
+            return;
+        Map<String, String> fieldsMappingMap = SITCoordinationUtils.getFieldsMappingMap();
+        DynamicObjectCollection entryCols = bill.getDynamicObjectCollection("nckd_entryentity");
+        for(DynamicObject sibase : sibaseCols) {
+            DynamicObject entry = entryCols.addNew();
+            for (String key : fieldsMappingMap.keySet()) {
+                entry.set(fieldsMappingMap.get(key), sibase.get(key));
+            }
+        }
+    }
+
+}

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

@@ -100,13 +100,16 @@ public class DataComparisonPushService {
             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);
-            }
+            //2026-01-08 Tyx 为0不跳过
+            data.add(dataMapA);
+            data.add(dataMapB);
+//            // 如果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;

+ 2 - 2
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/importtaskguide/ImportTaskGuideExportService.java

@@ -1,10 +1,10 @@
 package nckd.jxccl.sit.hcsi.business.importtaskguide;
 
 import com.google.common.collect.Lists;
-import com.kingdee.util.DateTimeUtils;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.resource.ResManager;
 import kd.bos.dataentity.utils.StringUtils;
+import nckd.jxccl.base.common.utils.DateTimeUtils;
 import kd.bos.exception.ErrorCode;
 import kd.bos.exception.KDException;
 import kd.bos.form.FormShowParameter;
@@ -217,7 +217,7 @@ public class ImportTaskGuideExportService {
 
             try {
                 String dd = DateTimeUtils.format(new Date(), "yyyyMMdd");
-                String exportFileName = MessageFormat.format(ResManager.loadKDString("历史数据迁移任务_数据导入失败结果_{0}", "ImportTaskGuideExportService_3", "swc-hsas-business", new Object[0]), dd);
+                String exportFileName = MessageFormat.format(ResManager.loadKDString("外部数据导入任务_数据导入失败结果_{0}", "ImportTaskGuideExportService_3", "swc-hsas-business", new Object[0]), dd);
                 String sheetName = ResManager.loadKDString("数据导入模板", "ImportTaskGuideExportService_2", "swc-hsas-business", new Object[0]);
                 SXSSFSheet sheet = wb.createSheet(sheetName);
                 sheet.setRandomAccessWindowSize(-1);

+ 9 - 2
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/sit/hcsi/business/importtaskguide/ImportTaskGuideImportService.java

@@ -3,7 +3,6 @@ package nckd.jxccl.sit.hcsi.business.importtaskguide;
 import com.google.common.collect.Lists;
 import java.math.BigDecimal;
 
-import com.kingdee.util.DateTimeUtils;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
@@ -29,6 +28,7 @@ import kd.hr.hbp.common.cache.HRAppCache;
 import kd.hr.hbp.common.cache.IHRAppCache;
 import nckd.jxccl.sit.hcsi.business.importtaskguide.utils.ImportTaskUtils;
 import nckd.jxccl.sit.hcsi.formplugin.web.tp.enums.SinsurPresetItemEnum;
+import nckd.jxccl.base.common.utils.DateTimeUtils;
 import org.apache.commons.lang.exception.ExceptionUtils;
 
 import java.io.InputStream;
@@ -185,7 +185,7 @@ public class ImportTaskGuideImportService {
             successCount = 0;
             failCount = dataRowList.size();
             errDataList.clear();
-            // TODO this.setErrorData(dataRowList.size(), lineIndexVer, errDataList, e.getMessage());
+            this.setErrorData(dataRowList.size(), lineIndexVer, errDataList, e.getMessage());
             return;
         } finally {
             allErrDataList.addAll(errDataList);
@@ -359,6 +359,13 @@ public class ImportTaskGuideImportService {
         return errMap;
     }
 
+    private void setErrorData(int size, int lineIndexVer, List<Map<String, String>> errDataList, String errorMsg) {
+        for(int i = 0; i < size; ++i) {
+            errDataList.add(this.assembleErrMap(lineIndexVer, false, errorMsg));
+            ++lineIndexVer;
+        }
+    }
+
     private void checkExcelTemplate(List<Map<Integer, String>> dataHeadList, Set<String> errDataList, Map<Integer, SinsurPresetItemEnum> tplFixItemMap) {
         Map<Integer, String> dataHeadMap = (Map)dataHeadList.get(0);
         String excelName = null;

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

@@ -4,6 +4,8 @@ import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.utils.ObjectUtils;
 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.PropertyChangedArgs;
 import kd.bos.form.field.BasedataEdit;
 import kd.bos.form.field.events.BeforeF7SelectEvent;
@@ -177,6 +179,15 @@ public class HCSIEmpCoordVerifBillEditEx extends AbstractFormPlugin implements P
             QFilter filter = new QFilter("id", QCP.in, getDimensionValueForFilter(sinSurStd, welfareType));
             e.getCustomQFilters().add(filter);
         }
+        // 过滤参保标准
+        else if (HRStringUtils.equals(fieldKey, "sinsurstd_r31")) {
+            DynamicObject welfArePayer = (DynamicObject) model.getValue("welfarepayer_r31");
+            if(ObjectUtils.isEmpty(welfArePayer)) {
+                this.getView().showTipNotification("请先选择“实际参保单位”");
+                e.setCancel(true);
+                return;
+            }
+        }
     }
 
     public List<Long> getDimensionValueForFilter (DynamicObject sinSurStd, DynamicObject welfaretype) {
@@ -238,4 +249,23 @@ public class HCSIEmpCoordVerifBillEditEx extends AbstractFormPlugin implements P
     }
 
 
+    /**
+     * 新增分录的时候:
+     * 如果变动大类是非调动的情况则携带表头的理论/实际参保单位
+     * @param e
+     */
+    @Override
+    public void afterAddRow(AfterAddRowEventArgs e) {
+        super.afterAddRow(e);
+        RowDataEntity[] rowDatas = e.getRowDataEntities();
+        IDataModel model = this.getModel();
+        DynamicObject dyn = model.getDataEntity();
+        String chgNumber = dyn.getDynamicObject("coordruleparam").getString("chgevent_z.number");
+        if(!chgNumber.equals("1020_S")) {
+            for (RowDataEntity rowData : rowDatas) {
+                model.setValue("nckd_welfarepayertheory", model.getValue("welfarepayertheory_r31"), rowData.getRowIndex());
+                model.setValue("nckd_welfarepayer", model.getValue("welfarepayer_r31"), rowData.getRowIndex());
+            }
+        }
+    }
 }

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

@@ -7,6 +7,8 @@ import kd.bos.dataentity.serialization.SerializationUtils;
 import kd.bos.dataentity.utils.ObjectUtils;
 import kd.bos.entity.EntityMetadataCache;
 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.PropertyChangedArgs;
 import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.form.control.EntryGrid;
@@ -460,5 +462,27 @@ public class SinSurEmpChgCoordBatchEditPluginEx extends AbstractFormPlugin imple
         }
     }
 
-
+    /**
+     * 新增分录的时候:
+     * 如果变动大类是非调动的情况则携带表头的理论/实际参保单位
+     * @param e
+     */
+    @Override
+    public void afterAddRow(AfterAddRowEventArgs e) {
+        super.afterAddRow(e);
+        IDataModel model = this.getModel();
+        DynamicObject dyn = model.getDataEntity();
+        String entryName = e.getEntryProp().getName();
+        if(entryName.equals("nckd_subentryentity")) {
+            RowDataEntity[] rowDatas = e.getRowDataEntities();
+            int rowIndex = model.getEntryCurrentRowIndex("entryentity");
+            String chgNumber = ((DynamicObject) model.getValue("coordruleparam", rowIndex)).getString("chgevent_z.number");
+            if(!chgNumber.equals("1020_S")) {
+                for (RowDataEntity rowData : rowDatas) {
+                    model.setValue("nckd_welfarepayertheory", model.getValue("welfarepayertheory_r31"),  rowData.getRowIndex(), rowIndex);
+                    model.setValue("nckd_welfarepayer", model.getValue("welfarepayer_r31"), rowData.getRowIndex(), rowIndex);
+                }
+            }
+        }
+    }
 }

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác