1
0

6 コミット 5fbe1766e6 ... 43943a4ef6

作者 SHA1 メッセージ 日付
  lisheng 43943a4ef6 Merge branch 'master' of http://111.75.220.136:10030/turborao/jyyy 1 ヶ月 前
  lisheng 832c930b0c 1.报销工作台按照当前分页数据显示金额合计 1 ヶ月 前
  lisheng b878b214a6 1.个人额度同步后台任务 1 ヶ月 前
  lisheng 741a60b3e6 1.公司额度制度发生变化,手工更新所有人员的费用额度 1 ヶ月 前
  lisheng 5fd76f5567 1.职级变更同步差旅报销级别任务:HR人员主任职职级更新时,次日通过调度任务更新差旅报销级别。 1 ヶ月 前
  lisheng 5a544807ae 1.年度复制额度任务:次年复制前一年全部额度,生成新年额度数据。 1 ヶ月 前

+ 110 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/LimitRelasetList.java

@@ -0,0 +1,110 @@
+package nckd.jimin.jyyy.fi.plugin.form;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.util.StringUtils;
+import kd.bos.context.RequestContext;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.CloseCallBack;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.schedule.api.JobInfo;
+import kd.bos.schedule.api.JobType;
+import kd.bos.schedule.api.TaskInfo;
+import kd.bos.schedule.form.JobForm;
+import nckd.jimin.jyyy.fi.plugin.operate.Helper.PersonReimQuotaHelper;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+public class LimitRelasetList extends AbstractListPlugin {
+    private static final String OP_REFRESH_ALLQUOTA = "refresh_allquota";
+
+    private static final String OP_REFRESH_ALLLEVER = "refresh_alllever";
+
+
+    private PersonReimQuotaHelper personReimQuotaHelper;
+
+    @Override
+    public void initialize() {
+        super.initialize();
+        personReimQuotaHelper = new PersonReimQuotaHelper();
+    }
+
+    protected PersonReimQuotaHelper getPersonReimQuotaHelper() {
+        return Optional.ofNullable(personReimQuotaHelper).orElse(new PersonReimQuotaHelper());
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs args) {
+        super.afterDoOperation(args);
+        String operateKey = args.getOperateKey();
+        OperationResult operationResult = args.getOperationResult();
+        if(operationResult != null && !operationResult.isSuccess()){
+            return;
+        }
+        if ( OP_REFRESH_ALLQUOTA.equals(operateKey)) {
+            dispatch("更新个人额度","updatequota");
+        }
+
+        if ( OP_REFRESH_ALLLEVER.equals(operateKey)) {
+            dispatch("更新差旅级别","updatelever");
+        }
+    }
+    /**
+     * 回调事件,在任务处理完毕后继续后续处理
+     */
+    @Override
+    public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
+        super.closedCallBack(closedCallBackEvent);
+        if (StringUtils.equals(closedCallBackEvent.getActionId(), "taskcloseback")) {
+            Object returnData = closedCallBackEvent.getReturnData();
+            if (returnData == null) {
+                return;
+            }
+            if (returnData instanceof Map) {
+                Map<String, Object> result = (Map<String, Object>)returnData;
+                if (result.containsKey("taskinfo")) {
+                    String taskInfoStr = (String)result.get("taskinfo");
+                    if (!StringUtils.isEmpty(taskInfoStr)) {
+                        TaskInfo taskInfo = JSON.parseObject(taskInfoStr,TaskInfo.class);
+                        if (taskInfo.isTaskEnd()) {
+                            // 获取任务执行完毕,生成的内容
+                            String data = taskInfo.getData();
+                            if(!StringUtils.isEmpty(data)){
+                                JSONObject dataJson = JSON.parseObject(data);
+                                this.getView().showSuccessNotification(dataJson.getString("message"));
+                            }else{
+                                this.getView().showSuccessNotification("执行成功。");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    /**
+     * 创建任务目标,发布新任务
+     */
+    private void dispatch(String jobName,String updateType) {
+        // 创建任务目标
+        JobInfo jobInfo = new JobInfo();
+        jobInfo.setAppId("em");                // 执行类所在的应用名
+        jobInfo.setJobType(JobType.REALTIME);   // 即时执行
+        jobInfo.setName(jobName);
+        jobInfo.setId(UUID.randomUUID().toString());        // 随机产生一个JobId (任务目标的标识)
+        jobInfo.setTaskClassname("nckd.jimin.jyyy.fi.task.PersonReimBigJobTask");
+        // 自定义参数
+        Map<String,Object> params = new HashMap<>();
+        params.put("updatetype", updateType);
+        jobInfo.setParams(params);
+        jobInfo.setRunByUserId(RequestContext.get().getCurrUserId());
+        // 回调参数,设置一个回调处理标识(actionId)
+        CloseCallBack closeCallBack = new CloseCallBack(this, "taskcloseback");
+        // 发布任务,并显示进度
+        JobForm.dispatch(jobInfo, this.getView(), closeCallBack);
+    }
+}

+ 45 - 5
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/ReimWorkBenchesDetailFormPlugin.java

@@ -17,6 +17,7 @@ import kd.bos.entity.MainEntityType;
 import kd.bos.entity.botp.runtime.ConvertOperationResult;
 import kd.bos.entity.botp.runtime.PushArgs;
 import kd.bos.entity.datamodel.ListSelectedRow;
+import kd.bos.entity.datamodel.events.TotalEntriesEventArgs;
 import kd.bos.entity.filter.FilterBuilder;
 import kd.bos.entity.filter.FilterCondition;
 import kd.bos.entity.operate.result.OperationResult;
@@ -56,6 +57,7 @@ import nckd.jimin.jyyy.fi.common.util.PrintPdfUtil;
 import nckd.jimin.jyyy.fi.common.util.ReceiveTicketUtils;
 
 import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -98,14 +100,13 @@ public class ReimWorkBenchesDetailFormPlugin extends AbstractFormPlugin implemen
     public void initialize() {
         super.initialize();
         reimHelper = new ReimWorkBenchesHelper();
+
+        EntryGrid entryGrid = this.getControl(ENTITY_ENTRY);
+        entryGrid.setInterveneTotal(true);
     }
 
     protected ReimWorkBenchesHelper getReimHelper(){
-        if(reimHelper != null){
-            return reimHelper;
-        }else{
-            return new ReimWorkBenchesHelper();
-        }
+        return Optional.ofNullable(reimHelper).orElse(new ReimWorkBenchesHelper());
     }
 
     @Override
@@ -117,6 +118,43 @@ public class ReimWorkBenchesDetailFormPlugin extends AbstractFormPlugin implemen
     }
 
 
+    @Override
+    public void totalEntriesByCondition(TotalEntriesEventArgs e) {
+        super.totalEntriesByCondition(e);
+        setEntryTotalAmount();
+    }
+
+
+    protected void setEntryTotalAmount(){
+        EntryGrid entryGrid = this.getControl(ENTITY_ENTRY);
+        DynamicObject[] entryPageDataArray = entryGrid.getEntryData().getDataEntitys();
+
+        BigDecimal applyAmount = BigDecimal.ZERO;
+        BigDecimal approveAmount = BigDecimal.ZERO;
+        BigDecimal balanceAmount = BigDecimal.ZERO;
+        BigDecimal payAmount = BigDecimal.ZERO;
+        BigDecimal notpayAmount = BigDecimal.ZERO;
+        for(DynamicObject entry : entryPageDataArray){
+            applyAmount = applyAmount.add(entry.getBigDecimal("applyamount"));
+            approveAmount = approveAmount.add(entry.getBigDecimal("approveamount"));
+            balanceAmount = balanceAmount.add(entry.getBigDecimal("balanceamount"));
+            payAmount = payAmount.add(entry.getBigDecimal("payamount"));
+            notpayAmount = notpayAmount.add(entry.getBigDecimal("notpayamount"));
+        }
+        Map<String,String> totalAmountMap = new HashMap<>(5);
+        totalAmountMap.put("applyAmount",applyAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+        totalAmountMap.put("approveAmount",approveAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+        totalAmountMap.put("balanceAmount",balanceAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+        totalAmountMap.put("payAmount",payAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+        totalAmountMap.put("notpayAmount",notpayAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+        entryGrid.setFloatButtomData(totalAmountMap);
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+    }
+
     @Override
     public void beforeBindData(EventObject e) {
         super.beforeBindData(e);
@@ -128,6 +166,8 @@ public class ReimWorkBenchesDetailFormPlugin extends AbstractFormPlugin implemen
         super.afterBindData(e);
 
         updateEntryFiledTitle();
+
+        setEntryTotalAmount();
     }
 
     @Override

+ 102 - 204
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/PersonReimSyncTaskOp.java → code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/Helper/PersonReimQuotaHelper.java

@@ -1,28 +1,16 @@
-package nckd.jimin.jyyy.fi.plugin.operate;
+package nckd.jimin.jyyy.fi.plugin.operate.Helper;
 
-import com.google.common.collect.Lists;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
-import kd.bos.dataentity.entity.DynamicObjectCollection;
-import kd.bos.dataentity.utils.StringUtils;
-import kd.bos.db.tx.TX;
-import kd.bos.db.tx.TXHandle;
-import kd.bos.entity.EntityMetadataCache;
-import kd.bos.entity.operate.result.OperateErrorInfo;
 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.AfterOperationArgs;
-import kd.bos.entity.validate.ErrorLevel;
 import kd.bos.exception.KDBizException;
-import kd.bos.logging.Log;
-import kd.bos.logging.LogFactory;
 import kd.bos.org.utils.DynamicObjectUtils;
 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.basedata.BaseDataServiceHelper;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.fi.gl.util.BigDecimalUtil;
@@ -35,88 +23,62 @@ import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
-public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
-    private static final Log logger = LogFactory.getLog(PersonReimSyncTaskOp.class);
-    @Override
-    public void onPreparePropertys(PreparePropertysEventArgs e) {
-        super.onPreparePropertys(e);
-    }
+public class PersonReimQuotaHelper {
+    /**
+     * 通过 费用项目与职级岗位关系配置 获取额度信息
+     * 按照币别分组,允许设置不同币别的方案
+     * @return
+     */
+    public Map<Long, Map<Long, BigDecimal>> getExpenseJobAmount(Object positionId , Object jobLeverId){
 
-    @Override
-    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
-        super.afterExecuteOperationTransaction(e);
-        DynamicObject[] dataEntities = e.getDataEntities();
-        String operationKey = e.getOperationKey();
+        StringJoiner joiner = new StringJoiner(",");
+        joiner.add(LimitRelasetConstant.KEY_NCKD_CURRENCY);
+        joiner.add(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM);
+        joiner.add(LimitRelasetConstant.KEY_NCKD_AMOUNT);
 
-        List<Object> billIdList = Arrays.stream(dataEntities).map(r -> r.get(PersonReimSyncTaskConstant.ID)).collect(Collectors.toList());
-        // 同步个人额度
-        if(PersonReimSyncTaskConstant.OPERATE.SYNCPERSONREIM.equals(operationKey)){
-            List<List<Object>> patchList = Lists.partition(billIdList, 50);
-            for(List<Object> patchBillList : patchList){
-                DynamicObject[] billDatas = BusinessDataServiceHelper.load(patchBillList.toArray(), EntityMetadataCache.getDataEntityType(PersonReimSyncTaskConstant.ENTITYID));
-                Arrays.stream(billDatas).forEach(r -> doSyncPersonReim(r));
-            }
+        DynamicObject[] limitRelasetArray = BusinessDataServiceHelper.load(LimitRelasetConstant.ENTITYID, joiner.toString(), new QFilter[]{
+                new QFilter(LimitRelasetConstant.KEY_NCKD_POSITIONHR, QCP.equals, positionId),
+                new QFilter(LimitRelasetConstant.KEY_NCKD_JOBLEVELHR, QCP.equals, jobLeverId),
+                new QFilter(LimitRelasetConstant.KEY_ENABLE, QCP.equals, "1"),
+                new QFilter(LimitRelasetConstant.KEY_STATUS, QCP.equals, "C")
+        });
+        if(limitRelasetArray == null || limitRelasetArray.length == 0){
+            return new HashMap<>();
         }
-
+        return Arrays.stream(limitRelasetArray)
+                .filter(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM) != null)
+                .collect(Collectors.groupingBy(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_CURRENCY).getLong(PersonReimSyncTaskConstant.ID),
+                        Collectors.toMap(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM).getLong(PersonReimSyncTaskConstant.ID),
+                                r -> r.getBigDecimal(LimitRelasetConstant.KEY_NCKD_AMOUNT), (a, b) -> a)));
     }
-    protected void doSyncPersonReim(DynamicObject billInfo){
-        // 校验数据是否合格
-        DynamicObject user = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER);
-        DynamicObject org = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG);
-        DynamicObject preOrg = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG_PRE);
-        DynamicObject position = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR);
-        DynamicObject prePosition = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE);
-        DynamicObject jobLever = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR);
-        DynamicObject preJobLever = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE);
 
-        int year = billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_YEAR);
-        // 增加事务控制,如果发生异常,回滚生成的额度信息,保持数据一致
-        try(TXHandle h = TX.requiresNew(getClass().getName()+"doSyncPersonReim")){
-            try {
-                // 校验数据
-                checkData(billInfo);
-                // 获取总额度  费用项目、职级、岗位
-                Map<Long, Map<Long, BigDecimal>> currencyReimJobAmount = getExpenseJobAmount(position.getPkValue(), jobLever.getPkValue());
-                // 判断组织是否存在便变更,组织变更则作废原组织
-                if(!org.getPkValue().equals(preOrg.getPkValue())){
-                    doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),preOrg.getLong(PersonReimSyncTaskConstant.ID),null,null,year);
-                }
-
-                // 变更前岗位、职位为空,直接新增
-                if(prePosition == null || preJobLever == null){
-                    createPersonReimDirect(billInfo,currencyReimJobAmount);
-                }else{
-                    Map<Long, Map<Long, BigDecimal>> sourceCurrencyReimJobAmount = getExpenseJobAmount(prePosition.getPkValue(), preJobLever.getPkValue());
-                    Set<Long> invalidCurrencySet = sourceCurrencyReimJobAmount.keySet().stream()
-                            .filter(r -> !currencyReimJobAmount.keySet().contains(r)).collect(Collectors.toSet());
-                    // 作废已删除的币别数据
-                    logger.info(String.format("需要作废的币别ID为:" + StringUtils.join(",",invalidCurrencySet)));
-                    invalidCurrencySet.forEach(r -> doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),org.getLong(PersonReimSyncTaskConstant.ID),null,r,year));
-                    for(Map.Entry<Long, Map<Long, BigDecimal>> currencyRow : currencyReimJobAmount.entrySet()){
-                        Long currencyId = currencyRow.getKey();
-                        Map<Long, BigDecimal> expenseJobAmount = currencyRow.getValue();
-                        if(sourceCurrencyReimJobAmount.containsKey(currencyId)){
-                            Set<Long> invalidExpenseSet = sourceCurrencyReimJobAmount.get(currencyId).keySet().stream()
-                                    .filter(r -> !expenseJobAmount.keySet().contains(r)).collect(Collectors.toSet());
-                            logger.info(String.format("需要作废的费用类型ID为:" + StringUtils.join(",",invalidExpenseSet)));
-                            invalidExpenseSet.forEach(r -> doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),org.getLong(PersonReimSyncTaskConstant.ID),r,currencyId,year));
-                        }
-                        for(Map.Entry<Long, BigDecimal> dataRow : expenseJobAmount.entrySet()){
-                            Long expenseItemId = dataRow.getKey();
-                            BigDecimal amount = dataRow.getValue();
-                            createPersonReimByExpense(billInfo,currencyId,expenseItemId,amount);
-                        }
-                    }
-                }
-                addRecord(billInfo,"创建成功。",Boolean.TRUE);
-            }catch (Exception e){
-                logger.error(String.format("单据%s同步额度失败",billInfo.getString(PersonReimSyncTaskConstant.KEY_NUMBER)),e);
-                String errorMsg = StringUtils.isEmpty(e.getMessage())?"同步额度时发生未知异常,请用traceid在monitor通过任务编码查看日志详情。":e.getMessage();
-                addRecord(billInfo,errorMsg,Boolean.FALSE);
-                h.markRollback();
+    /**
+     * 作废条件范围内的个人额度信息
+     * @param userId
+     * @param companyId
+     * @param expenseItemId
+     * @param currencyId
+     */
+    public void doInvalidPersonReim(Long userId , Long companyId , Long expenseItemId ,Long currencyId , int year){
+        // 作废币别下
+        List<QFilter> filterList = new ArrayList<>();
+        if(userId != null && userId == 0L){
+            filterList.add(new QFilter("employee.id", QCP.equals, userId));
+            if(companyId != null && companyId != 0L){
+                filterList.add(new QFilter("company.id", QCP.equals, companyId));
             }
+            if(expenseItemId != null && expenseItemId != 0L){
+                filterList.add(new QFilter("expenseitem.id", QCP.equals, expenseItemId));
+            }
+            filterList.add(new QFilter("currency.id", QCP.equals, currencyId));
+            filterList.add(new QFilter("auditstatus", QCP.equals, "1"));
+            filterList.add(new QFilter("dateyear", QCP.equals, String.valueOf(year)));
+            DynamicObject[] invalidPersonReimArray = BusinessDataServiceHelper.load(BillTypeConstants.ER_REIMBURSEAMOUNT, "auditstatus", filterList.toArray(new QFilter[0]));
+            for(DynamicObject invalidPersonReim : invalidPersonReimArray){
+                invalidPersonReim.set("auditstatus","2");
+            }
+            SaveServiceHelper.save(invalidPersonReimArray);
         }
-        SaveServiceHelper.save(new DynamicObject[]{billInfo});
     }
 
     /**
@@ -124,7 +86,7 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
      * @param billInfo
      * @param currencyReimJobAmount
      */
-    protected void createPersonReimDirect(DynamicObject billInfo,Map<Long, Map<Long, BigDecimal>> currencyReimJobAmount){
+    public void createPersonReimDirect(DynamicObject billInfo,Map<Long, Map<Long, BigDecimal>> currencyReimJobAmount){
         for(Map.Entry<Long, Map<Long, BigDecimal>> currencyRow : currencyReimJobAmount.entrySet()){
             Long currencyId = currencyRow.getKey();
             Map<Long, BigDecimal> expenseJobAmount = currencyRow.getValue();
@@ -136,13 +98,18 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
         }
     }
 
-    protected void createPersonReimByExpense(DynamicObject billInfo,Long currencyId,Long expenseItemId,BigDecimal amount){
-        // 创建个人额度表
-        DynamicObject personReim = createPersonReim(
+    public void createPersonReimByExpense(DynamicObject billInfo,Long currencyId,Long expenseItemId,BigDecimal amount){
+
+        PersonReimQuotaVO personReimQuotaVO = new PersonReimQuotaVO(
                 billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER).getLong(PersonReimSyncTaskConstant.ID),
                 billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG).getLong(PersonReimSyncTaskConstant.ID),
                 expenseItemId,
-                currencyId,amount,billInfo);
+                currencyId,amount,
+                billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_YEAR),
+                billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_MONTH),
+                billInfo.getString(PersonReimSyncTaskConstant.ID));
+        // 创建个人额度表
+        DynamicObject personReim = createPersonReim(personReimQuotaVO,true);
         // 调用审核
         OperationResult saveOp = OperationServiceHelper.executeOperate("save", BillTypeConstants.ER_REIMBURSEAMOUNT, new DynamicObject[]{personReim}, OperateOption.create());
         if(!saveOp.isSuccess() || saveOp.getSuccessPkIds().size() == 0){
@@ -154,68 +121,20 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
         }
     }
 
-
-    protected void checkData(DynamicObject billInfo){
-        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER) == null){
-            throw new KDBizException("同步人员不能为空。");
-        }
-        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG) == null){
-            throw new KDBizException("组织不能为空。");
-        }
-        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR) == null){
-            throw new KDBizException("岗位人员不能为空。");
-        }
-        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR) == null){
-            throw new KDBizException("职级不能为空。");
-        }
-    }
-
-    /**
-     * 作废条件范围内的个人额度信息
-     * @param userId
-     * @param companyId
-     * @param expenseItemId
-     * @param currencyId
-     */
-    protected void doInvalidPersonReim(Long userId , Long companyId , Long expenseItemId ,Long currencyId , int year){
-        // 作废币别下
-        List<QFilter> filterList = new ArrayList<>();
-        if(userId != null && userId == 0L){
-            filterList.add(new QFilter("employee.id", QCP.equals, userId));
-            if(companyId != null && companyId != 0L){
-                filterList.add(new QFilter("company.id", QCP.equals, companyId));
-            }
-            if(expenseItemId != null && expenseItemId != 0L){
-                filterList.add(new QFilter("expenseitem.id", QCP.equals, expenseItemId));
-            }
-            filterList.add(new QFilter("currency.id", QCP.equals, currencyId));
-            filterList.add(new QFilter("auditstatus", QCP.equals, "1"));
-            filterList.add(new QFilter("dateyear", QCP.equals, String.valueOf(year)));
-            DynamicObject[] invalidPersonReimArray = BusinessDataServiceHelper.load(BillTypeConstants.ER_REIMBURSEAMOUNT, "auditstatus", filterList.toArray(new QFilter[0]));
-            for(DynamicObject invalidPersonReim : invalidPersonReimArray){
-                invalidPersonReim.set("auditstatus","2");
-            }
-            SaveServiceHelper.save(invalidPersonReimArray);
-        }
-    }
-
-
     /**
      * 获取个人额度同步表 创建某个费用类型的个人额度
-     * @param userId
-     * @param companyId
-     * @param expenseItemId
-     * @param currencyId
-     * @param amount
-     * @param billInfo
+     * @param quotaVO
      * @return
      */
-    protected DynamicObject createPersonReim(Long userId , Long companyId , Long expenseItemId ,Long currencyId,
-                                             BigDecimal amount , DynamicObject billInfo){
-
-        int year = billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_YEAR);
+    public DynamicObject createPersonReim(PersonReimQuotaVO  quotaVO , boolean isAddnew){
 
-        int month = billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_MONTH);
+        int year = quotaVO.getYear();
+        int month = quotaVO.getMonth();
+        BigDecimal amount = quotaVO.getAmount();
+        Long userId = quotaVO.getUserId();
+        Long companyId = quotaVO.getCompanyId();
+        Long expenseItemId = quotaVO.getExpenseItemId();
+        Long currencyId = quotaVO.getCurrencyId();
 
         Date now = new Date();
         long currUserId = RequestContext.get().getCurrUserId();
@@ -240,6 +159,11 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
             personAmountBill.set("wbsrcbillid",oriPersonAmount.getPkValue());
             setPersonAmountData(personAmountBill,amount,month);
         }else{
+            // 无需新增时直接返回
+            if(!isAddnew){
+                return null;
+            }
+
             DynamicObject userInfo = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
             DynamicObject companyInfo = BusinessDataServiceHelper.loadSingleFromCache(companyId, "bos_org");
             DynamicObject expenseItemInfo = BusinessDataServiceHelper.loadSingleFromCache(expenseItemId, "er_expenseitemedit");
@@ -273,11 +197,37 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
 
         // 岗位新增变动:positionchange ;年度复制:yearcopy  ; 新增字段,与标准字段区分,防止与标品逻辑冲突
         personAmountBill.set("nckd_sourcetype","positionchange");
-        personAmountBill.set("nckd_sourcebillid",billInfo.getPkValue());
+        personAmountBill.set("nckd_sourcebillid",quotaVO.getSourcebillid());
 
         return personAmountBill;
     }
 
+    public void syncReimLever(Long userId ,Long companyId,Long jobId){
+
+        DynamicObject reimLever = getReimLever(jobId, companyId);
+        if(reimLever != null){
+            DynamicObject realReimSettingQuery = QueryServiceHelper.queryOne("er_reimbursesetting_rel", "id", new QFilter[]{
+                    new QFilter("user", QCP.equals, userId),
+                    new QFilter("company", QCP.equals, companyId)
+            });
+            // 存在则修改
+            if(realReimSettingQuery != null){
+                DynamicObject reimSettingInfo = BusinessDataServiceHelper.loadSingle(realReimSettingQuery.get("id"),"er_reimbursesetting_rel");
+                reimSettingInfo.set("reimburselevel",reimLever.get("id"));
+                SaveServiceHelper.save(new DynamicObject[]{ reimSettingInfo });
+            }
+        }
+    }
+
+    private DynamicObject getReimLever(Long jobId , Long companyId) {
+        // 查询差旅级别
+        List<QFilter> leverFilterList = new ArrayList<>();
+        // 基础资料标准过滤条件
+        leverFilterList.add(BaseDataServiceHelper.getBaseDataFilter("er_reimburselevel", companyId));
+        leverFilterList.add(new QFilter("enable", QCP.equals,"1"));
+        leverFilterList.add(new QFilter("nckd_joblevelhr.fbasedataid", QCP.equals,jobId));
+        return QueryServiceHelper.queryOne("er_reimburselevel", "id", leverFilterList.toArray(new QFilter[0]));
+    }
     /**
      * 对个人额度表的额度字段赋值
      * @param personAmountBill 个人额度表
@@ -293,56 +243,4 @@ public class PersonReimSyncTaskOp extends AbstractOperationServicePlugIn {
         }
 
     }
-
-
-    protected void addRecord(DynamicObject billInfo , String successMsg , Boolean isSuccess){
-        DynamicObjectCollection recordEntryCol = billInfo.getDynamicObjectCollection(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.ENTITYID);
-        DynamicObject record = recordEntryCol.addNew();
-        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_DATE,new Date());
-        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_TRACEID, RequestContext.get().getTraceId());
-        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_ISSUCCESS,isSuccess?1:0);
-        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_MSG, StringUtils.substring(successMsg,0,500));
-
-        billInfo.set(PersonReimSyncTaskConstant.KEY_NCKD_SYNCSTATUS,isSuccess?"40":"30");
-
-        if(!isSuccess){
-            OperationResult operationResult = this.getOperationResult();
-            operationResult.setSuccess(false);
-            operationResult.getSuccessPkIds().removeIf(r -> billInfo.getPkValue().equals(r));
-            OperateErrorInfo operateErrorInfo = new OperateErrorInfo();
-            operateErrorInfo.setMessage(successMsg);
-            operateErrorInfo.setLevel(ErrorLevel.Error);
-            operationResult.addErrorInfo(operateErrorInfo);
-        }
-
-
-    }
-
-    /**
-     * 通过 费用项目与职级岗位关系配置 获取额度信息
-     * 按照币别分组,允许设置不同币别的方案
-     * @return
-     */
-    protected Map<Long, Map<Long, BigDecimal>> getExpenseJobAmount(Object positionId , Object jobLeverId){
-
-        StringJoiner joiner = new StringJoiner(",");
-        joiner.add(LimitRelasetConstant.KEY_NCKD_CURRENCY);
-        joiner.add(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM);
-        joiner.add(LimitRelasetConstant.KEY_NCKD_AMOUNT);
-
-        DynamicObject[] limitRelasetArray = BusinessDataServiceHelper.load(LimitRelasetConstant.ENTITYID, joiner.toString(), new QFilter[]{
-                new QFilter(LimitRelasetConstant.KEY_NCKD_POSITIONHR, QCP.equals, positionId),
-                new QFilter(LimitRelasetConstant.KEY_NCKD_JOBLEVELHR, QCP.equals, jobLeverId),
-                new QFilter(LimitRelasetConstant.KEY_ENABLE, QCP.equals, "1"),
-                new QFilter(LimitRelasetConstant.KEY_STATUS, QCP.equals, "C")
-        });
-        if(limitRelasetArray == null || limitRelasetArray.length == 0){
-            return new HashMap<>();
-        }
-        return Arrays.stream(limitRelasetArray)
-                .filter(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM) != null)
-                .collect(Collectors.groupingBy(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_CURRENCY).getLong(PersonReimSyncTaskConstant.ID),
-                        Collectors.toMap(r -> r.getDynamicObject(LimitRelasetConstant.KEY_NCKD_EXPENSEITEM).getLong(PersonReimSyncTaskConstant.ID),
-                                r -> r.getBigDecimal(LimitRelasetConstant.KEY_NCKD_AMOUNT), (a, b) -> a)));
-    }
 }

+ 170 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/PersonReimSyncQuotaTaskOp.java

@@ -0,0 +1,170 @@
+package nckd.jimin.jyyy.fi.plugin.operate;
+
+import com.google.common.collect.Lists;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.db.tx.TX;
+import kd.bos.db.tx.TXHandle;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.operate.result.OperateErrorInfo;
+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.AfterOperationArgs;
+import kd.bos.entity.plugin.args.InitOperationArgs;
+import kd.bos.entity.validate.ErrorLevel;
+import kd.bos.exception.KDBizException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import nckd.jimin.jyyy.fi.common.constant.PersonReimSyncTaskConstant;
+import nckd.jimin.jyyy.fi.plugin.operate.Helper.PersonReimQuotaHelper;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class PersonReimSyncQuotaTaskOp extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(PersonReimSyncQuotaTaskOp.class);
+
+    protected PersonReimQuotaHelper personReimQuotaHelper;
+    @Override
+    public void initialize(InitOperationArgs e) {
+        super.initialize(e);
+        personReimQuotaHelper = new PersonReimQuotaHelper();
+    }
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+        DynamicObject[] dataEntities = e.getDataEntities();
+        String operationKey = e.getOperationKey();
+
+        List<Object> billIdList = Arrays.stream(dataEntities).map(r -> r.get(PersonReimSyncTaskConstant.ID)).collect(Collectors.toList());
+        // 同步个人额度
+        if(PersonReimSyncTaskConstant.OPERATE.SYNCPERSONREIM.equals(operationKey)){
+            List<List<Object>> patchList = Lists.partition(billIdList, 50);
+            for(List<Object> patchBillList : patchList){
+                DynamicObject[] billDatas = BusinessDataServiceHelper.load(patchBillList.toArray(), EntityMetadataCache.getDataEntityType(PersonReimSyncTaskConstant.ENTITYID));
+                Arrays.stream(billDatas).forEach(r -> doSyncPersonReim(r));
+            }
+        }
+
+    }
+    protected void doSyncPersonReim(DynamicObject billInfo){
+        // 校验数据是否合格
+        DynamicObject user = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER);
+        DynamicObject org = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG);
+        DynamicObject preOrg = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG_PRE);
+        DynamicObject position = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR);
+        DynamicObject prePosition = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE);
+        DynamicObject jobLever = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR);
+        DynamicObject preJobLever = billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE);
+
+        int year = billInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_YEAR);
+        // 增加事务控制,如果发生异常,回滚生成的额度信息,保持数据一致
+        try(TXHandle h = TX.requiresNew(getClass().getName()+"doSyncPersonReim")){
+            try {
+                // 校验数据
+                checkData(billInfo);
+                // 获取总额度  费用项目、职级、岗位
+                Map<Long, Map<Long, BigDecimal>> currencyReimJobAmount = personReimQuotaHelper.getExpenseJobAmount(position.getPkValue(), jobLever.getPkValue());
+                // 判断组织是否存在便变更,组织变更则作废原组织
+                if(!org.getPkValue().equals(preOrg.getPkValue())){
+                    personReimQuotaHelper.doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),preOrg.getLong(PersonReimSyncTaskConstant.ID),null,null,year);
+                }
+
+                // 变更前岗位、职位为空,直接新增
+                if(prePosition == null || preJobLever == null){
+                    personReimQuotaHelper.createPersonReimDirect(billInfo,currencyReimJobAmount);
+                }else{
+                    Map<Long, Map<Long, BigDecimal>> sourceCurrencyReimJobAmount = personReimQuotaHelper.getExpenseJobAmount(prePosition.getPkValue(), preJobLever.getPkValue());
+                    Set<Long> invalidCurrencySet = sourceCurrencyReimJobAmount.keySet().stream()
+                            .filter(r -> !currencyReimJobAmount.keySet().contains(r)).collect(Collectors.toSet());
+                    // 作废已删除的币别数据
+                    logger.info(String.format("需要作废的币别ID为:" + StringUtils.join(",",invalidCurrencySet)));
+                    invalidCurrencySet.forEach(r -> personReimQuotaHelper.doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),org.getLong(PersonReimSyncTaskConstant.ID),null,r,year));
+                    for(Map.Entry<Long, Map<Long, BigDecimal>> currencyRow : currencyReimJobAmount.entrySet()){
+                        Long currencyId = currencyRow.getKey();
+                        Map<Long, BigDecimal> expenseJobAmount = currencyRow.getValue();
+                        if(sourceCurrencyReimJobAmount.containsKey(currencyId)){
+                            Set<Long> invalidExpenseSet = sourceCurrencyReimJobAmount.get(currencyId).keySet().stream()
+                                    .filter(r -> !expenseJobAmount.keySet().contains(r)).collect(Collectors.toSet());
+                            logger.info(String.format("需要作废的费用类型ID为:" + StringUtils.join(",",invalidExpenseSet)));
+                            invalidExpenseSet.forEach(r -> personReimQuotaHelper.doInvalidPersonReim(user.getLong(PersonReimSyncTaskConstant.ID),org.getLong(PersonReimSyncTaskConstant.ID),r,currencyId,year));
+                        }
+                        for(Map.Entry<Long, BigDecimal> dataRow : expenseJobAmount.entrySet()){
+                            Long expenseItemId = dataRow.getKey();
+                            BigDecimal amount = dataRow.getValue();
+                            personReimQuotaHelper.createPersonReimByExpense(billInfo,currencyId,expenseItemId,amount);
+                        }
+                    }
+                }
+                addRecord(billInfo,"创建成功。",Boolean.TRUE);
+            }catch (Exception e){
+                logger.error(String.format("单据%s同步额度失败",billInfo.getString(PersonReimSyncTaskConstant.KEY_NUMBER)),e);
+                String errorMsg = StringUtils.isEmpty(e.getMessage())?"同步额度时发生未知异常,请用traceid在monitor通过任务编码查看日志详情。":e.getMessage();
+                addRecord(billInfo,errorMsg,Boolean.FALSE);
+                h.markRollback();
+            }
+        }
+        SaveServiceHelper.save(new DynamicObject[]{billInfo});
+    }
+
+
+
+
+    protected void checkData(DynamicObject billInfo){
+        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER) == null){
+            throw new KDBizException("同步人员不能为空。");
+        }
+        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_ORG) == null){
+            throw new KDBizException("组织不能为空。");
+        }
+        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR) == null){
+            throw new KDBizException("岗位人员不能为空。");
+        }
+        if(billInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR) == null){
+            throw new KDBizException("职级不能为空。");
+        }
+    }
+
+
+
+
+
+
+
+    protected void addRecord(DynamicObject billInfo , String successMsg , Boolean isSuccess){
+        DynamicObjectCollection recordEntryCol = billInfo.getDynamicObjectCollection(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.ENTITYID);
+        DynamicObject record = recordEntryCol.addNew();
+        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_DATE,new Date());
+        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_TRACEID, RequestContext.get().getTraceId());
+        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_ISSUCCESS,isSuccess?1:0);
+        record.set(PersonReimSyncTaskConstant.NCKD_RECORDENTRY.KEY_NCKD_SYNC_MSG, StringUtils.substring(successMsg,0,500));
+
+        billInfo.set(PersonReimSyncTaskConstant.KEY_NCKD_SYNCSTATUS,isSuccess?"40":"30");
+
+        if(!isSuccess){
+            OperationResult operationResult = this.getOperationResult();
+            operationResult.setSuccess(false);
+            operationResult.getSuccessPkIds().removeIf(r -> billInfo.getPkValue().equals(r));
+            OperateErrorInfo operateErrorInfo = new OperateErrorInfo();
+            operateErrorInfo.setMessage(successMsg);
+            operateErrorInfo.setLevel(ErrorLevel.Error);
+            operationResult.addErrorInfo(operateErrorInfo);
+        }
+
+
+    }
+
+
+}

+ 134 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/PersonReimBigJobTask.java

@@ -0,0 +1,134 @@
+package nckd.jimin.jyyy.fi.task;
+
+import com.google.common.collect.Lists;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
+import nckd.jimin.jyyy.fi.common.util.CommonUtils;
+import nckd.jimin.jyyy.fi.plugin.operate.Helper.PersonReimQuotaHelper;
+import nckd.jimin.jyyy.fi.plugin.operate.Helper.PersonReimQuotaVO;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static kd.epm.emr.common.spread.domain.view.js.SpreadProperties.P.r;
+
+public class PersonReimBigJobTask extends AbstractTask {
+
+    private PersonReimQuotaHelper personReimQuotaHelper = new PersonReimQuotaHelper();
+
+
+    protected PersonReimQuotaHelper getPersonReimQuotaHelper() {
+        return Optional.ofNullable(personReimQuotaHelper).orElse(new PersonReimQuotaHelper());
+    }
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+        // 同步类型
+        String updateType = (String)map.get("updatetype");
+
+        // 获取全部人员生效中的岗位职级
+        DynamicObjectCollection positionCol = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPPOSORGREL, "person.id,person.number,company.id,company.number,position.id", new QFilter[]{
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("isprimary", QCP.equals, "1"),
+                new QFilter("person.id", QCP.equals, Long.parseLong("2203317114964545536"))
+        });
+
+        Map<Long, DynamicObject> personPositionMap = positionCol.stream()
+                .collect(Collectors.toMap(r -> r.getLong("person.id"), r -> r, (k1, k2) -> k1));
+        Set<Long> personPositionSet = positionCol.stream().map(r -> r.getLong("person.id")).collect(Collectors.toSet());
+
+        // 从职级职等中共查询职级发生变化的数据
+        Map<Long, Long> personJobMap = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPJOBREL, "person.id,joblevel.id", new QFilter[]{
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("person.id", QCP.in, personPositionSet)
+        }).stream().collect(Collectors.toMap(r -> r.getLong("person.id"), r -> r.getLong("joblevel.id"), (k1, k2) -> k1));
+
+        List<List<Long>> partition = Lists.partition(new ArrayList<>(personPositionSet), 4);
+
+        for(int i = 0 ; i < partition.size() ; i++){
+            List<Long> patchDataList = partition.get(i);
+
+            for(Long personId : patchDataList){
+
+                if(!personPositionMap.containsKey(personId) || !personJobMap.containsKey(personId)){
+                    continue;
+                }
+                DynamicObject positionInfo = personPositionMap.get(personId);
+
+
+                DynamicObject company = QueryServiceHelper.queryOne("bos_org", "id", new QFilter("number", QCP.equals, positionInfo.getString("company.number")).toArray());
+                DynamicObject userInfo = QueryServiceHelper.queryOne("bos_user", "id", new QFilter("number", QCP.equals, positionInfo.getString("person.number")).toArray());
+                // 更新额度
+                if("updatequota".equals(updateType)){
+                    refreshPersonQuota(userInfo.getLong("id"),company.getLong("id"),positionInfo.getLong("position.id"),(Long)personJobMap.get(personId));
+                    continue;
+                }
+
+                if("updatelever".equals(updateType)){
+                    getPersonReimQuotaHelper().syncReimLever(userInfo.getLong("id"),company.getLong("id"),(Long)personJobMap.get(personId));
+                    continue;
+                }
+            }
+
+            // 回传进度
+            feedbackProgress(100*(i+1)/partition.size());
+        }
+
+        HashMap<String, Object> result = new HashMap<>();
+        result.put("message", String.format("执行成功,总共执行条数%s",personPositionSet.size()));
+        // 输出定制结果
+        feedbackCustomdata(result);
+    }
+
+    /**
+     * 刷新个人额度表
+     * @param userId
+     * @param positionId
+     * @param jobId
+     */
+    protected void refreshPersonQuota(Long userId ,Long companyId , Long positionId,Long jobId) {
+        Map<Long, Map<Long, BigDecimal>> currencyReimJobAmount = getPersonReimQuotaHelper().getExpenseJobAmount(positionId, jobId);
+        for (Map.Entry<Long, Map<Long, BigDecimal>> currencyRow : currencyReimJobAmount.entrySet()) {
+            Long currencyId = currencyRow.getKey();
+            Map<Long, BigDecimal> expenseJobAmount = currencyRow.getValue();
+            for (Map.Entry<Long, BigDecimal> dataRow : expenseJobAmount.entrySet()) {
+                Long expenseItemId = dataRow.getKey();
+                BigDecimal amount = dataRow.getValue();
+
+                PersonReimQuotaVO quotaVO = new PersonReimQuotaVO(userId, companyId, expenseItemId, currencyId, amount);
+                quotaVO.setYear(CommonUtils.getDateFeild(new Date(), Calendar.YEAR));
+                // 更新下个月
+                quotaVO.setMonth(CommonUtils.getDateFeild(new Date(), Calendar.MONTH)+2);
+                // 创建个人额度表
+                DynamicObject personReim = getPersonReimQuotaHelper().createPersonReim(quotaVO,false);
+
+                if(personReim != null){
+                    // 调用审核
+                    OperationResult saveOp = OperationServiceHelper.executeOperate("save", BillTypeConstants.ER_REIMBURSEAMOUNT, new DynamicObject[]{personReim}, OperateOption.create());
+                    if (!saveOp.isSuccess() || saveOp.getSuccessPkIds().size() == 0) {
+                        throw new KDBizException(CommonUtils.getOperationResultErrorInfos(saveOp));
+                    }
+                    OperationResult approveOp = OperationServiceHelper.executeOperate("approve", BillTypeConstants.ER_REIMBURSEAMOUNT, new Object[]{saveOp.getSuccessPkIds().get(0)}, OperateOption.create());
+                    if (!approveOp.isSuccess() || approveOp.getSuccessPkIds().size() == 0) {
+                        throw new KDBizException(CommonUtils.getOperationResultErrorInfos(approveOp));
+                    }
+                }
+
+            }
+        }
+    }
+}

+ 113 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/PersonReimLeverSyncTask.java

@@ -0,0 +1,113 @@
+package nckd.jimin.jyyy.fi.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+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.schedule.api.StopTask;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.util.StringUtils;
+import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
+import nckd.jimin.jyyy.fi.plugin.operate.Helper.PersonReimQuotaHelper;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Map;
+import java.util.Optional;
+
+
+/**
+ * 同步人员报销级别
+ */
+public class PersonReimLeverSyncTask extends AbstractTask implements StopTask {
+
+    private static final Log log = LogFactory.getLog(PersonReimLeverSyncTask.class);
+
+    private PersonReimQuotaHelper personReimQuotaHelper = new PersonReimQuotaHelper();
+
+
+    protected PersonReimQuotaHelper getPersonReimQuotaHelper() {
+        return Optional.ofNullable(personReimQuotaHelper).orElse(new PersonReimQuotaHelper());
+    }
+    private static final String SYNCDATE = "syncdate";
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        log.info("-------- PersonReimLeverSyncTask 启动同步人员报销级别任务 --------");
+
+        LocalDate yesterday = getYesterday(map);
+        // 昨天最早的日期时间(00:00:00)
+        LocalDateTime startOfYesterday = yesterday.atStartOfDay();
+        Date beginDate = Date.from(startOfYesterday.atZone(ZoneId.systemDefault()).toInstant());
+        // 昨天最晚的日期时间(23:59:59)
+        LocalDateTime endOfYesterday = yesterday.atTime(LocalTime.MAX);
+        Date endDate = Date.from(endOfYesterday.atZone(ZoneId.systemDefault()).toInstant());
+
+
+
+        // 获取昨天
+        DynamicObjectCollection changeJobCol = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPJOBREL, "person.id,person.number,joblevel.id", new QFilter[]{
+                new QFilter("startdate", QCP.large_equals, beginDate),
+                new QFilter("startdate", QCP.less_equals, endDate),
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1")
+        });
+        log.info("changeJobCol size : {}" , changeJobCol.size());
+        changeJobCol.stream().forEach(changeJob -> syncReunimLever(changeJob));
+        log.info("-------- PersonReimLeverSyncTask 同步人员报销级别任务结束 --------");
+    }
+
+    /**
+     * 获取同步日期
+     * @param map
+     * @return
+     */
+    protected LocalDate getYesterday(Map<String, Object> map) {
+        if(map.containsKey(SYNCDATE) && StringUtils.isNotEmpty((String)map.get(SYNCDATE))){
+            return LocalDate.parse((String)map.get(SYNCDATE), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        }else{
+            return LocalDate.now().minusDays(1);
+        }
+    }
+    /**
+     * 同步人员报销级别
+     * @param changeJob
+     */
+    protected void syncReunimLever(DynamicObject changeJob) {
+        // 获取人员ID
+        Long personId = changeJob.getLong("person.id");
+        // 人员编码
+        String personNumber = changeJob.getString("person.number");
+        // 获取职级ID
+        Long jobId = changeJob.getLong("joblevel.id");
+        DynamicObject potisionInfo = fetchPositionInfo(personId);
+        if(potisionInfo == null) {
+            log.info("人员 {} 职位信息不存在", personId);
+            return;
+        }
+        String companyNumber = potisionInfo.getString("company.number");
+        DynamicObject company = QueryServiceHelper.queryOne("bos_org", "id", new QFilter("number", QCP.equals, companyNumber).toArray());
+        DynamicObject userInfo = QueryServiceHelper.queryOne("bos_user", "id", new QFilter("number", QCP.equals, personNumber).toArray());
+        getPersonReimQuotaHelper().syncReimLever(userInfo.getLong("id"),jobId,company.getLong("id"));
+
+    }
+    private DynamicObject fetchPositionInfo(Long personId) {
+        return QueryServiceHelper.queryOne(BillTypeConstants.HRPI_EMPPOSORGREL, "company.number", new QFilter[]{
+                new QFilter("businessstatus", QCP.less_equals, "1"),
+                new QFilter("iscurrentversion", QCP.less_equals, "1"),
+                new QFilter("person.id", QCP.equals, personId),
+                new QFilter("isprimary", QCP.less_equals, "1")
+        });
+    }
+
+
+
+}

+ 57 - 40
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/PersonReimSyncTask.java → code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/PersonReimQuotaSyncTask.java

@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
 import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.exception.KDBizException;
 import kd.bos.exception.KDException;
 import kd.bos.logging.Log;
@@ -31,11 +32,11 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 /**
- * 个人额度更新功能
+ * 员工额度同步任务
  */
-public class PersonReimSyncTask extends AbstractTask implements StopTask {
+public class PersonReimQuotaSyncTask extends AbstractTask implements StopTask {
 
-    private static final Log logger = LogFactory.getLog(PersonReimSyncTask.class);
+    private static final Log logger = LogFactory.getLog(PersonReimQuotaSyncTask.class);
 
     @Override
     public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
@@ -103,25 +104,25 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
         Set<Object> positionPersonSet = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPPOSORGREL, "person.id", new QFilter[]{
                 new QFilter("startdate", QCP.large_equals, beginDate),
                 new QFilter("startdate", QCP.less_equals, endDate),
-                new QFilter("businessstatus", QCP.less_equals, "1"),
-                new QFilter("iscurrentversion", QCP.less_equals, "1"),
-                new QFilter("isprimary", QCP.less_equals, "1")
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("isprimary", QCP.equals, "1")
         }).stream().map(r -> r.get("person.id")).collect(Collectors.toSet());
 
         // 从职级职等中共查询职级发生变化的数据
         Set<Object> jobPersonSet = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPJOBREL, "person.id", new QFilter[]{
                 new QFilter("startdate", QCP.large_equals, beginDate),
                 new QFilter("startdate", QCP.less_equals, endDate),
-                new QFilter("businessstatus", QCP.less_equals, "1"),
-                new QFilter("iscurrentversion", QCP.less_equals, "1")
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1")
         }).stream().map(r -> r.get("person.id")).collect(Collectors.toSet());
 
         // 合并去重,得到日期范围内变动了岗位或者职级的人员信息
         positionPersonSet.addAll(jobPersonSet);
-
+        logger.info(String.format("查询到岗位或者职级发生变化人员数量为:{} 将开始按照100分批处理。",positionPersonSet.size()));
         List<List<Object>> partition = Lists.partition(new ArrayList<>(positionPersonSet), 100);
-        int totalCount = 0 ;
-        int successCount = 0;
+
+        boolean hasErrorData = false;
         for(List<Object> patchList : partition){
 
             try{
@@ -131,39 +132,49 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
 
                 // 移除人员、年、月已存在的数据;移除岗位没有发生变化的数据
                 removeErrorData(taskCol);
-
-                Object[] successBillIds = SaveServiceHelper.save(taskCol.toArray(new DynamicObject[0]));
-                List<Object> successBillIdList = Arrays.stream(successBillIds).filter(r -> r instanceof DynamicObject).map(r -> ((DynamicObject) r).getPkValue()).collect(Collectors.toList());
+//                Object[] successBillIds = SaveServiceHelper.save(taskCol.toArray(new DynamicObject[0]));
+//                List<Object> successBillIdList = Arrays.stream(successBillIds).filter(r -> r instanceof DynamicObject).map(r -> ((DynamicObject) r).getPkValue()).collect(Collectors.toList());
 //                OperationResult operationResult = OperationServiceHelper.executeOperate(PersonReimSyncTaskConstant.OPERATE.SYNCPERSONREIM,
 //                        PersonReimSyncTaskConstant.ENTITYID, successBillIdList.toArray(), OperateOption.create());
-//                totalCount+=successBillIds.length;
-//                successCount+=operationResult.getSuccessPkIds().size();
+//                if(!operationResult.isSuccess()){
+//                    hasErrorData = true;
+//                }
 
             }catch (Exception e){
-                // 失败的怎么处理
+                // 发生异常时通过调任务通知,通过任务日志和monitor日志查看失败原因。
                 logger.error("分批执行创建人员额度同步任务异常",e);
+                hasErrorData = true;
             }
         }
-        if(totalCount != successCount){
-            throw new KDBizException(String.format("总共执行%s条数据,存在%s条失败,请在人员同步任务中查看失败信息。",totalCount,totalCount-successCount));
+        if(hasErrorData){
+            throw new KDBizException("同步人员额度时发生异常,请查询原因后再执行。");
         }
 
     }
 
+    /**
+     * 移除不符合条件的数据
+     * @param taskCol
+     */
     protected void removeErrorData(List<DynamicObject> taskCol){
-        // 移除空数据
-        taskCol.removeIf(r -> Objects.isNull(r));
-
-        // 移除岗位、职级没有发生变化的数据(只有公司发生变动不处理)
-//        taskCol.removeIf(r -> r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE) != null && r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR).getPkValue()
-//                .equals(r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE).getPkValue())
-//                && r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE) != null && r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR).getPkValue()
-//                .equals(r.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE).getPkValue()));
 
-        // 移除已存在的数据
         Iterator<DynamicObject> it = taskCol.iterator();
         while (it.hasNext()){
             DynamicObject taskInfo = it.next();
+            // 移除空数据
+            if(taskInfo == null){
+                it.remove();
+            }
+
+            // 移除岗位、职级没有发生变化的数据(只有公司发生变动不处理)
+            if(taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE) != null
+                    && taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR).getPkValue().equals(taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_POSITIONHR_PRE).getPkValue())
+                && taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE) != null
+                    && taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR).getPkValue().equals(taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_JOBLEVELHR_PRE).getPkValue())){
+                it.remove();
+            }
+
+            // 移除已存在的数据
             QFilter[] filterList = new QFilter[]{
                     new QFilter(PersonReimSyncTaskConstant.KEY_NCKD_USER,QCP.equals,taskInfo.getDynamicObject(PersonReimSyncTaskConstant.KEY_NCKD_USER).getPkValue()),
                     new QFilter(PersonReimSyncTaskConstant.KEY_NCKD_YEAR,QCP.equals,taskInfo.getInt(PersonReimSyncTaskConstant.KEY_NCKD_YEAR)),
@@ -177,7 +188,13 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
     }
 
 
-
+    /**
+     * 生成人员额度同步任务
+     * @param personId
+     * @param beginDate
+     * @param endDate
+     * @return
+     */
     protected DynamicObject generalPersonReimTask(Object personId,Date beginDate , Date endDate){
         Date now = new Date();
         DynamicObject taskInfo = BusinessDataServiceHelper.newDynamicObject(PersonReimSyncTaskConstant.ENTITYID);
@@ -210,10 +227,10 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
     protected void setNewPosition(DynamicObject taskInfo,Object personId,Date beginDate , Date endDate){
         // 设置人员最新的岗位、公司信息
         DynamicObject potisionInfo = QueryServiceHelper.queryOne(BillTypeConstants.HRPI_EMPPOSORGREL, "position.id,company.number", new QFilter[]{
-                new QFilter("businessstatus", QCP.less_equals, "1"),
-                new QFilter("iscurrentversion", QCP.less_equals, "1"),
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
                 new QFilter("person.id", QCP.equals, personId),
-                new QFilter("isprimary", QCP.less_equals, "1")
+                new QFilter("isprimary", QCP.equals, "1")
         });
         if(potisionInfo == null){
             return ;
@@ -227,9 +244,9 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
     protected void setNewJob(DynamicObject taskInfo,Object personId,Date beginDate , Date endDate){
         // 设置人员最新的岗位、公司信息
         DynamicObject jobInfo = QueryServiceHelper.queryOne(BillTypeConstants.HRPI_EMPJOBREL, "joblevel.id", new QFilter[]{
-                new QFilter("businessstatus", QCP.less_equals, "1"),
-                new QFilter("iscurrentversion", QCP.less_equals, "1"),
-                new QFilter("person.id", QCP.less_equals, personId)
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("person.id", QCP.equals, personId)
         });
         if(jobInfo == null){
             return ;
@@ -240,9 +257,9 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
         // 开始日期之前,最新的岗位
         DynamicObjectCollection positionCol = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPPOSORGREL, "position.id,company.number", new QFilter[]{
                 new QFilter("enddate", QCP.less_than, beginDate),
-                new QFilter("iscurrentversion", QCP.less_equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
                 new QFilter("person.id", QCP.equals, personId),
-                new QFilter("isprimary", QCP.less_equals, "1")
+                new QFilter("isprimary", QCP.equals, "1")
         }, "enddate desc", 1);
         if(CollectionUtils.isEmpty(positionCol)){
             return ;
@@ -258,9 +275,9 @@ public class PersonReimSyncTask extends AbstractTask implements StopTask {
     protected void setOldJob(DynamicObject taskInfo,Object personId,Date beginDate , Date endDate){
         // 设置人员最新的岗位、公司信息
         DynamicObjectCollection jobCol = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPJOBREL, "joblevel.id", new QFilter[]{
-                new QFilter("startdate", QCP.less_than, beginDate),
-                new QFilter("iscurrentversion", QCP.less_equals, "1"),
-                new QFilter("person.id", QCP.less_equals, personId)
+                new QFilter("startdate", QCP.equals, beginDate),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("person.id", QCP.equals, personId)
         }, "enddate desc", 1);
         if(CollectionUtils.isEmpty(jobCol)){
             return ;

+ 144 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/PersonReimYearQuotaCopyTask.java

@@ -0,0 +1,144 @@
+package nckd.jimin.jyyy.fi.task;
+
+import com.google.common.collect.Lists;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.operate.result.OperationResult;
+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.schedule.api.StopTask;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
+import nckd.jimin.jyyy.fi.common.util.CommonUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 年度个人额度复制功能
+ */
+public class PersonReimYearQuotaCopyTask extends AbstractTask implements StopTask {
+    private static final Log logger = LogFactory.getLog(PersonReimYearQuotaCopyTask.class);
+    private static final String YEAR = "year";
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        copyYearData(map);
+    }
+
+    /**
+     * 年度个人额度复制功能
+     * @param map
+     */
+    protected void copyYearData(Map<String, Object> map){
+        logger.info("PersonReimYearCopyTask_begin");
+        int year = getYear(map);
+
+        // 复制的到第二年
+        int copyYear = year + 1;
+
+        // 查询去年的个人额度信息
+
+        List<Object> copyDataId = QueryServiceHelper.query(BillTypeConstants.ER_REIMBURSEAMOUNT, "id", new QFilter[]{
+                new QFilter("dateyear", QCP.equals, String.valueOf(year)),
+                new QFilter("auditstatus", QCP.equals, "1")
+        }).stream().map(r -> r.get("id")).collect(Collectors.toList());
+        logger.info("PersonReimYearCopyTask copyDataId size : " + copyDataId.size());
+        // 分批
+        List<List<Object>> partition = Lists.partition(copyDataId, 100);
+        List<String> failBillSet = new ArrayList<>();
+        for(List<Object> patchIdList : partition){
+
+            // 查询已经生成的数据
+            Set<Object> hasCreateIdSet = QueryServiceHelper.query(BillTypeConstants.ER_REIMBURSEAMOUNT, "nckd_sourcebillid", new QFilter[]{
+                    new QFilter("dateyear", QCP.equals, String.valueOf(copyYear)),
+                    new QFilter("nckd_sourcetype", QCP.equals, "yearcopy"),
+                    new QFilter("nckd_sourcebillid", QCP.in, patchIdList),
+            }).stream().map(r -> r.get("nckd_sourcebillid")).collect(Collectors.toSet());
+
+            // 移除
+            patchIdList.removeIf(r -> hasCreateIdSet.contains(r));
+
+            DynamicObject[] copyDataList = BusinessDataServiceHelper.load(patchIdList.toArray(), EntityMetadataCache.getDataEntityType(BillTypeConstants.ER_REIMBURSEAMOUNT));
+            for(DynamicObject reimburseQuota : copyDataList){
+                String reimPerson = "";
+                try{
+                    reimPerson = reimburseQuota.getDynamicObject("employee").getString("number");
+                    saveQuotaData(reimburseQuota,copyYear);
+                }catch (Exception e){
+                    failBillSet.add(reimPerson);
+                    logger.error(String.format("【%s】的个人额度同步失败", reimPerson ), e);
+                }
+            }
+        }
+        logger.info(String.format("失败的单据条数为:%s,请monitor检查失败原因后,在任务中重试。",failBillSet.size()));
+    }
+
+    private void saveQuotaData(DynamicObject reimburseQuota , int dateYear) {
+
+        Date now = new Date();
+        long currentUserId = UserServiceHelper.getCurrentUserId();
+        DynamicObject remiQuota = BusinessDataServiceHelper.newDynamicObject(BillTypeConstants.ER_REIMBURSEAMOUNT);
+        remiQuota.set("company", reimburseQuota.get("company"));
+        remiQuota.set("expenseitem", reimburseQuota.get("expenseitem"));
+        remiQuota.set("currency", reimburseQuota.get("currency"));
+        remiQuota.set("dateyear", dateYear);
+        remiQuota.set("totalamount", reimburseQuota.get("totalamount"));
+        remiQuota.set("costcompany", reimburseQuota.get("costcompany"));
+
+        for(int i = 1; i <= 12; i++) {
+            remiQuota.set("month" + i, reimburseQuota.get("month" + i));
+            if (i <= 4) {
+                remiQuota.set("quarter" + i, reimburseQuota.get("quarter" + i));
+            }
+        }
+
+        remiQuota.set("amounttype", "1");
+        remiQuota.set("employee", reimburseQuota.get("employee"));
+        remiQuota.set("dept", reimburseQuota.get("dept"));
+        remiQuota.set("auditstatus", "0");
+        remiQuota.set("createtime", now);
+        remiQuota.set("modifytime", now);
+        remiQuota.set("creator", currentUserId);
+        remiQuota.set("modifier", currentUserId);
+        remiQuota.set("wbsrcbilltype", "er_reimctl_new");
+
+        remiQuota.set("nckd_sourcetype", "yearcopy");
+        remiQuota.set("nckd_sourcebillid", reimburseQuota.getPkValue());
+
+        // 调用保存
+        OperationResult saveOp = OperationServiceHelper.executeOperate("save", BillTypeConstants.ER_REIMBURSEAMOUNT, new DynamicObject[]{remiQuota}, OperateOption.create());
+        if(!saveOp.isSuccess() || saveOp.getSuccessPkIds().size() == 0){
+            throw new KDBizException(CommonUtils.getOperationResultErrorInfos(saveOp));
+        }
+        OperationResult approveOp = OperationServiceHelper.executeOperate("approve", BillTypeConstants.ER_REIMBURSEAMOUNT, new Object[]{saveOp.getSuccessPkIds().get(0)}, OperateOption.create());
+        if(!approveOp.isSuccess() || approveOp.getSuccessPkIds().size() == 0){
+            throw new KDBizException(CommonUtils.getOperationResultErrorInfos(approveOp));
+        }
+    }
+
+    /**
+     * 获取额度复制年
+     * @param map
+     * @return
+     */
+    protected int getYear(Map<String, Object> map){
+
+        if(map.containsKey(YEAR) && StringUtils.isNotEmpty((String)map.get(YEAR))){
+            return Integer.parseInt(map.get(YEAR).toString());
+        }
+        // 第二年执行前一年的数据
+        return Calendar.getInstance().get(Calendar.YEAR)-1;
+    }
+
+}