Переглянути джерело

```
feat(BaseMedicalAllowance): 新增基本医疗补助计算功能

新增员工基本医疗补助的自动计算与手动更新功能。包括:
- 提供操作插件 BaseMedicalAllowanceOpPlugin 支持界面按钮触发计算- 实现业务逻辑类 BaseMedicalAllowanceServiceImpl 处理具体计算规则
- 支持按人员或全量计算,并生成年度补助记录- 根据员工岗位、职级、职称等信息匹配补助标准金额
- 自动保存计算结果并返回操作提示信息
```

turborao 1 місяць тому
батько
коміт
52582e778a

+ 11 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/business/BaseMedicalAllowanceService.java

@@ -0,0 +1,11 @@
+package nckd.jxccl.swc.init.business;
+
+import java.util.List;
+import java.util.Map;
+
+public interface BaseMedicalAllowanceService {
+
+    public abstract Map<String, String> calcBaseMedicalAllowanceForPersons(List<Long> personIds);
+
+    public abstract Map<String, String> calcBaseMedicalAllowanceForAll();
+}

+ 281 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/business/BaseMedicalAllowanceServiceImpl.java

@@ -0,0 +1,281 @@
+package nckd.jxccl.swc.init.business;
+
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+
+
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * 员工信息维护(社保专用)列表界面提供一个功能按钮,可以进行即时计算,
+ * 后台提供定时任务(每年12月31日24点执行一次),当年度待遇根据上年度12月31日的人员状态和记录计算补助待遇,
+ * 按照年度生成多行记录,记录历史。当专员手动执行前端更新按钮时(需选中人员),
+ * 系统需根据手动的当前时间获取人员状态和记录计算补助待遇,包括对当前记录进行更新,
+ * 新增人员待遇记录生成。(离职人员不做处理)
+ * 金额调整
+ * 2400--->4800
+ * 1800--->3600
+ * 1200--->2400
+ * 840--->1800
+ * author: turborao
+ * date: 2025/10/27 13:05
+ */
+public class BaseMedicalAllowanceServiceImpl implements BaseMedicalAllowanceService{
+    public Map<String, String> calcBaseMedicalAllowanceForAll() {
+
+        //取员工档案
+        String billKey = "hcsi_sinsurfile";
+        QFilter filter1 = new QFilter("iscurrentversion", QCP.equals, true); // 启用
+        QFilter filter2 = new QFilter("datastatus", QCP.equals, "1"); // 启用
+
+        String selectFields="id,employee.id,employee.number,nckd_benefitentry.nckd_effeyear,nckd_benefitentry.nckd_suppbenefit";
+        DynamicObject[] datas = BusinessDataServiceHelper.load(billKey, selectFields, new QFilter[]{filter1.and(filter2)});
+
+        Map<String, String> result = calcBaseMedicalAllowance(datas);
+
+        return result;
+    }
+
+    public Map<String, String> calcBaseMedicalAllowanceForPersons(List<Long> billIds) {
+
+        //取员工档案
+        String billKey = "hcsi_sinsurfile";
+        QFilter filter1 = new QFilter("iscurrentversion", QCP.equals, true); // 启用
+        QFilter filter2 = new QFilter("datastatus", QCP.equals, "1"); // 启用
+        QFilter filter3 = new QFilter("id", QCP.in, billIds); // 启用
+
+        String selectFields="id,employee.id,employee.number,nckd_benefitentry.nckd_effeyear,nckd_benefitentry.nckd_suppbenefit";
+        DynamicObject[] datas = BusinessDataServiceHelper.load(billKey, selectFields, new QFilter[]{filter1.and(filter2).and(filter3)});
+
+        Map<String, String> result = calcBaseMedicalAllowance(datas);
+
+        return result;
+
+    }
+
+
+    public Map<String, String> calcBaseMedicalAllowance(DynamicObject[] datas) {
+
+        // 获取当前日期
+        LocalDate currentDate = LocalDate.now();
+
+        // 获取上一年的年份
+        //int previousYear = currentDate.minusYears(1).getYear();
+        int previousYear = currentDate.getYear();
+
+        Map<Long, DynamicObject> employeeFileMaps = Arrays.stream(datas).collect(
+                Collectors.toMap(
+                        obj -> obj.getLong("employee.id"),
+                        obj -> obj,
+                        (k1, k2) -> k1
+                )
+        );
+        //将Map转换为List
+        List<Long> employeeIDs = employeeFileMaps.keySet().stream().collect(Collectors.toList());
+        ArrayList<DynamicObject> dynList = new ArrayList<>();
+        /***
+         * 获取员工基本信息
+         */
+        QFilter qFilter1 = new QFilter("id",QCP.in, employeeIDs);
+        QFilter qFilter11 = new QFilter("iscurrentversion", QCP.equals, true); // 启用
+        QFilter qFilter12 = new QFilter("datastatus", QCP.equals, "1"); // 启用
+        DataSet employeeDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"hrpi_employee", "id,number,name", new QFilter[]{qFilter1,qFilter11,qFilter12},null);
+
+        /***
+         * 获取员工档案 任职经历  岗位
+         */
+        QFilter qFilter2 = new QFilter("employee.id",QCP.in, employeeIDs);
+        QFilter qFilter21 = new QFilter("iscurrentdata", QCP.equals, true); // 启用
+        DataSet empOrgRelDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"hrpi_empposorgrel", "id,employee.id,company.id,company.name,position.id,position.number,position.name,adminorg.number", new QFilter[]{qFilter2,qFilter21},null);
+
+        /***
+         * 获取员工档案 雇佣信息  用工关系状态
+         */
+        QFilter qFilter3 = new QFilter("employee.id",QCP.in, employeeIDs);
+        QFilter qFilter31 = new QFilter("iscurrentdata", QCP.equals, true); // 启用
+        QFilter qFilter32 = new QFilter("ishired", QCP.equals, "1"); // 是否在职
+        DataSet empentrelDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"hrpi_empentrel", "id,employee.id,laborrelstatus.number", new QFilter[]{qFilter3,qFilter31,qFilter32},null);
+
+        /***
+         * 获取员工档案 党政职务  职务级别
+         */
+        QFilter qFilter4 = new QFilter("employee.id",QCP.in, employeeIDs);
+        QFilter qFilter41 = new QFilter("iscurrentdata", QCP.equals, true); // 启用
+        DataSet posgradeDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"nckd_hrpi_partyposh", "id,employee.id,nckd_posgrade.number", new QFilter[]{qFilter4,qFilter41},null);
+
+        /***
+         * 获取员工档案 职称信息 职称级别  hrpi_perprotitle
+         */
+        QFilter qFilter5 = new QFilter("employee.id",QCP.in, employeeIDs);
+        QFilter qFilter51 = new QFilter("ishigh", QCP.equals, "1"); // 启用
+        DataSet perprotitleDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"hrpi_perprotitle", "id,employee.id,prolevel.number", new QFilter[]{qFilter5,qFilter51},null);
+
+        /***
+         * 获取员工档案 职业资格 职业资格等级  hrpi_perocpqual
+         */
+        QFilter qFilter6 = new QFilter("employee.id",QCP.in, employeeIDs);
+        QFilter qFilter61 = new QFilter("ismajor", QCP.equals, "1"); // 启用
+        DataSet perocpqualeDS = QueryServiceHelper.queryDataSet(this.getClass().getName(),"hrpi_perocpqual", "id,employee.id,qualevel.number", new QFilter[]{qFilter6,qFilter61},null);
+
+
+        DataSet reDataset = null;
+        reDataset = employeeDS.leftJoin(empOrgRelDS).on("id", "employee.id")
+                .select(employeeDS.getRowMeta().getFieldNames(),new String[] {"company.id as compId","company.name as compName","position.id as positionId","position.number as positionNum","position.name as positionName","adminorg.number as adminorgNum"})
+                .finish();
+        reDataset.print(true);
+
+        reDataset = reDataset.leftJoin(empentrelDS).on("id", "employee.id")
+                .select(reDataset.getRowMeta().getFieldNames(),new String[] {"laborrelstatus.number as laborrelstatusNum"})
+                .finish();
+        reDataset.print(true);
+
+        reDataset = reDataset.leftJoin(posgradeDS).on("id", "employee.id")
+                .select(reDataset.getRowMeta().getFieldNames(),new String[] {"nckd_posgrade.number as posgradeNum"})
+                .finish();
+        reDataset.print(true);
+
+        reDataset = reDataset.leftJoin(perprotitleDS).on("id", "employee.id")
+                .select(reDataset.getRowMeta().getFieldNames(),new String[] {"prolevel.number as prolevelNum"})
+                .finish();
+        reDataset.print(true);
+
+        reDataset = reDataset.leftJoin(perocpqualeDS).on("id", "employee.id")
+                .select(reDataset.getRowMeta().getFieldNames(),new String[] {"qualevel.number as qualevelNum"})
+                .finish();
+        reDataset.print(true);
+
+        Iterator<Row> iterator = reDataset.iterator();
+        while (iterator.hasNext()) {
+            Row row = iterator.next();
+            DynamicObject employeeFile = employeeFileMaps.get(row.getLong("id"));
+            String adminorgNum = row.getString("adminorgNum") == null ? "" : row.getString("adminorgNum");
+            String positionNum = row.getString("positionNum") == null ? "" : row.getString("positionNum");
+            String positionName = row.getString("positionName") == null ? "" : row.getString("positionName");
+            String posgradeNum = row.getString("posgradeNum")  == null ? "" : row.getString("posgradeNum");
+            String laborrelstatusNum = row.getString("laborrelstatusNum") == null ? "" : row.getString("laborrelstatusNum");
+            String prolevelNum = row.getString("prolevelNum") == null ? "" : row.getString("prolevelNum");
+            String qualevelNum = row.getString("qualevelNum")  == null ? "" : row.getString("qualevelNum");
+
+            int money = 0;
+            List<String> L4800 = Arrays.asList("1", "2");
+            List<String> L3600 = Arrays.asList("3", "4", "5");
+            List<String> L2400 = Arrays.asList("6", "7", "8", "9");
+            List<String> prolevelHigeList = Arrays.asList("001", "011");
+            List<String> prolevelMiddList = Arrays.asList("002", "010");
+            /**
+             * 公司领导班子成员:对应组织为集团本部-股份公司领导-公司领导和集团本部-集团公司领导在内的所有正式员工【4800/年】
+             */
+            if(laborrelstatusNum.equals("1010_S") && adminorgNum.equals("01010201") && positionNum.equals("01010101")){
+                money = 4800;
+            }
+            /**
+             * 监事会主席:对应职位编码为ZW01010377的正式员工 【4800/年】
+             * 专职外部董事:对应职位编码为ZW01010173的正式员工 【4800/年】
+             */
+            else if (laborrelstatusNum.equals("1010_S")  && (positionNum.equals("ZW01010377") || positionNum.equals("ZW01010173"))) {
+                money = 4800;
+            }
+            /**
+             * 专职监事正副职:对应职位编码为ZW01010378和ZW01010379的正式员工【3600/年】
+             */
+            else if (laborrelstatusNum.equals("1010_S")  && (positionNum.equals("ZW01010378") || positionNum.equals("ZW01010379"))) {
+                money = 3600;
+            }
+            /**
+             * 职务级别为"厅级、副厅级"的正式员工 4800
+             */
+            else if (laborrelstatusNum.equals("1010_S")  &&  L4800.contains(posgradeNum) ) {
+                money = 4800;
+            }
+            /**
+             * 职务级别为"中层正职
+             * 中层副职
+             * 中层副职(托管)"的正式员工 3600
+             */
+            else if (laborrelstatusNum.equals("1010_S")  &&  L3600.contains(posgradeNum) ) {
+                money = 3600;
+            }
+            /**
+             * 已聘任正高级职称或特级技师:对应职称级别为正高级、正高级(虚拟);技能等级为特级技师的正式员工 3600
+             */
+            else if (laborrelstatusNum.equals("1010_S")  && (prolevelHigeList.contains(prolevelNum)  ||  qualevelNum.equals("0"))) {
+                money = 3600;
+            }
+            /**
+             * 已聘任副高级职称或高级技师:对应职称级别为副高级、副高级(虚拟);技能等级为高级技师的正式员工 2400
+             */
+            else if (laborrelstatusNum.equals("1010_S")  && (prolevelMiddList.contains(prolevelNum)  ||  qualevelNum.equals("1"))) {
+                money = 2400;
+            }
+            /**
+             * 职务级别为"基层正职
+             * 基层正职(托管)
+             * 基层副职
+             * 基层副职(托管)"的正式员工 2400
+             */
+            else if (laborrelstatusNum.equals("1010_S")  &&  L2400.contains(posgradeNum) ) {
+                money = 2400;
+            }
+            /**
+             * 协理员:职位名称包含“协理员”的所有正式员工 【2400/年】
+             */
+            else if (laborrelstatusNum.equals("1010_S")  &&  positionName.contains("协理员") ) {
+                money = 2400;
+            }
+
+            /**
+             * 职务级别为"其他"的正式员工 1800
+             */
+            else if (laborrelstatusNum.equals("1010_S")  &&  posgradeNum.equals("10") ) {
+                money = 1800;
+            }else if (laborrelstatusNum.equals("1010_S")) {
+                money = 1800;
+            }
+
+
+            DynamicObjectCollection benefitEntrys = employeeFile.getDynamicObjectCollection("nckd_benefitentry");
+
+            List<DynamicObject> currYearDyn =
+                    benefitEntrys.stream().filter(e -> e.getInt("nckd_effeyear") == previousYear).collect(Collectors.toList());
+            DynamicObject benefitEntry = null;
+            if(currYearDyn.size() == 0){
+                benefitEntry = benefitEntrys.addNew() ;
+            }else{
+                benefitEntry = currYearDyn.get(0);
+            }
+
+            benefitEntry.set("nckd_effeyear", previousYear);
+            benefitEntry.set("nckd_suppbenefit", money);
+
+            dynList.add(employeeFile);
+        }
+
+        Map<String, String> result = new HashMap<>();
+        int length = 0;
+        if(dynList.size() == 0) {
+            result.put("code", "200");
+            result.put("msg", "当年度待遇标准计算(补充医疗保险),执行成功,但本次没有人员数据!" );
+        }else{
+            //保存
+            Object[] update = SaveServiceHelper.save(dynList.toArray(new DynamicObject[0]));
+            length = update.length;
+            result.put("code", "200");
+            result.put("msg", "当年度待遇标准计算(补充医疗保险),执行成功,本次计算人员数量" + length);
+        }
+
+        return result;
+    }
+
+
+}

+ 46 - 0
code/swc/nckd-jxccl-swc/src/main/java/nckd/jxccl/swc/init/plugin/operate/BaseMedicalAllowanceOpPlugin.java

@@ -0,0 +1,46 @@
+package nckd.jxccl.swc.init.plugin.operate;
+
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.validate.ErrorLevel;
+import kd.bos.entity.validate.ValidationErrorInfo;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.swc.init.business.BaseMedicalAllowanceServiceImpl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 单据操作插件
+ */
+public class BaseMedicalAllowanceOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        //获取列表中选择的单据内码
+        List<Long> billids = (List) Arrays.asList(e.getDataEntities()).stream().map((entityx) -> {
+            return entityx.getLong("id");
+        }).collect(Collectors.toList());
+
+        if(billids != null || billids.size() > 0) {
+            BaseMedicalAllowanceServiceImpl baseMedicalAllowanceService = new BaseMedicalAllowanceServiceImpl();
+            Map<String, String> result= baseMedicalAllowanceService.calcBaseMedicalAllowanceForPersons(billids);
+
+            if(result.get("code").equals("200")){
+                this.operationResult.setShowMessage(true);
+                this.operationResult.setSuccess(true);
+                this.operationResult.setMessage(result.get("msg"));
+            }else{
+                this.operationResult.addErrorInfo(new ValidationErrorInfo("", null,
+                        0, 0, "同步失败", "提示信息", result.get("msg"), ErrorLevel.Error));
+            }
+        }else{
+            this.operationResult.setShowMessage(true);
+            this.operationResult.setSuccess(false);
+            this.operationResult.setMessage("没选中数据!");
+        }
+    }
+}