Quellcode durchsuchen

feat(pm): 添加员工绩效打印功能并优化打印数据处理

- 新增EmployeePrintPlugin实现绩效结果打印功能
- 在PerfBatchPrintListPlugin中集成员工打印选项和年份选择功能
- 实现近5年岗位信息查询和绩效排名管理数据获取
- 优化PerfDetailPrintPlugin中的数据过滤和缓存处理逻辑
- 添加模板选择功能支持多种打印模板
- 实现按年份范围的岗位信息查询和排名数据统计
- 修复打印数据过滤和字段映射问题
wyc vor 5 Tagen
Ursprung
Commit
ef799fd851

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

@@ -0,0 +1,322 @@
+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.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.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 {
+
+    @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(getYearIndexFromPrintYear(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) {
+        String dsName = evt.getDataSource().getDsName();
+        if(!dsName.equalsIgnoreCase("custom")) {
+            Date printYear = HRAppCache.get("nckd_pm").get("printYear", Date.class);
+            Map<Integer, Long> selectPrintPersonIdMap = HRAppCache.get("nckd_pm").get("employeePrintIdMap", Map.class);
+            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)) {
+                            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;
+    }
+}

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

@@ -5,6 +5,7 @@ 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;
@@ -12,6 +13,7 @@ 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;
@@ -23,15 +25,31 @@ 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;
 
 /**
 * 人员考评套打列表
@@ -92,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
@@ -105,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");
+
+            }
         }
     }
 
@@ -127,19 +206,33 @@ 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) {
-                List<Integer> seqList = new   ArrayList<>();
                 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);
                 }
-                SelectTplPrint operation = (SelectTplPrint) args.getSource();
-                operation.getParameter().put("extParam123", seqList);
             }
+            HRAppCache.get("nckd_pm").put( "selectPrintSeq", seqList);
+            HRAppCache.get("nckd_pm").put( "selectPrintPersonIdMap", personIdMap);
+        }else if( "employeeprint".equals(operateKey)){
+
         }
     }
 }

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

@@ -1,17 +1,39 @@
 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;
 
 /**
 * 绩效结果打印
@@ -23,34 +45,223 @@ public class PerfDetailPrintPlugin extends AbstractPrintPlugin implements Plugin
 
     @Override
     public void afterLoadData(AfterLoadDataEvent evt) {
-        List<Integer> seqs = ConvertUtil.toList(this.getExtParam());
+        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) {
-                Integer seq = ConvertUtil.toInt(entryRow.getField(FormConstant.SEQ_KEY).getValue());
-                // 判断该分录行是否在用户勾选的列表中
-                if (seqs.contains(seq)) {
-                    filteredEntryRows.add(entryRow); // 保留被勾选的行
+                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);
                 }
-            }
-            // 用过滤后的分录数据替换原数据
-            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;
+    }
 }

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

@@ -21,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;
@@ -74,6 +75,8 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
     @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();
 
@@ -191,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))
@@ -211,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) {

+ 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);
+
+    }
+}

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

@@ -291,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()) {
@@ -340,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){
@@ -468,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());
-            }
         }
 
     }