Pārlūkot izejas kodu

1.新增商旅财务对账底稿调度任务,创建底稿数据
2.新增下推底稿确认单操作,创建当前期间的底稿确认单
3.新增下推商旅对账单操作,创建对应的商旅对账单

lisheng 2 dienas atpakaļ
vecāks
revīzija
4a2c57333b
13 mainītis faili ar 1090 papildinājumiem un 0 dzēšanām
  1. 5 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/BillConstant.java
  2. 140 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/BusTravelCheckBillConstant.java
  3. 24 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/DepSecretaryConstant.java
  4. 39 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/FinanceBillConfirmConstant.java
  5. 82 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/TravelFinanDraftConstant.java
  6. 76 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/TripReqBillConstant.java
  7. 7 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/util/CommonUtils.java
  8. 105 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/BusTravelCheckBillEdit.java
  9. 226 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/FinanDraftConfirmEdit.java
  10. 67 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/PersonPositionListProvider.java
  11. 81 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/TravelFinanDraftList.java
  12. 21 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/travelcheck/FinanDraftConfirmOp.java
  13. 217 0
      code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/TravelFinanDraftCreateTask.java

+ 5 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/BillConstant.java

@@ -34,4 +34,9 @@ public interface BillConstant {
      */
     String KEY_BILLSTATUS = "billstatus";
 
+    /**
+     * 多选基础数据ID
+     */
+    String KEY_FBASEDATAID = "fbasedataid";
+
 }

+ 140 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/BusTravelCheckBillConstant.java

@@ -0,0 +1,140 @@
+package nckd.jimin.jyyy.fi.common.constant.travelcheck;
+
+import nckd.jimin.jyyy.fi.common.constant.BillConstant;
+
+/**
+ * 商旅对账单常量类
+ */
+public interface BusTravelCheckBillConstant extends BillConstant {
+
+    String ENTITYID = "nckd_bustravelcheckbill";
+    String ORG = "org"; // 一级部门
+    String NCKD_BZ = "nckd_bz"; // 补助
+
+    String NCKD_CHECKMONTH = "nckd_checkmonth"; // 对账期间
+
+
+    String NCKD_BZHZ_AMTSUM = "nckd_bzhz_amtsum"; // 补贴金额合计
+
+    String NCKD_DEPSECRETARY = "nckd_depsecretary"; // 部门秘书
+
+    String NCKD_FINANDRAFT_CONFIRM = "nckd_finandraft_confirm"; // 底稿确认单
+
+
+    interface NCKD_BZ_ENTRY{
+
+        String ENTITYID = "nckd_bz_entry";
+        String NCKD_BZ_COMPANY = "nckd_bz_company"; // 法人主体
+        String NCKD_BZ_TRAVELER = "nckd_bz_traveler"; // 出差人
+        String NCKD_BZ_TRAVELERNO = "nckd_bz_travlererno"; // 工号
+        String NCKD_BZ_SUMDAYS = "nckd_bz_sumdays"; // 出差天数合计
+        String NCKD_BZ_AMOUNT = "nckd_bz_amount"; // 补助金额
+        String NCKD_BZ_PAYERBANK = "nckd_bz_payerbank"; // 银行账号
+        String NCKD_BZ_ACCOUNT = "nckd_bz_account"; // 开户行
+        String NCKD_BZ_CHECKAMT = "nckd_bz_checkamt"; // 核定金额
+        String NCKD_BZ_DESCRIBE = "nckd_bz_describe"; // 部门反馈
+        String NCKD_BZ_DEPFULLNAME = "nckd_bz_depfullname"; // 部门名称
+        String NCKD_BZ_POST = "nckd_bz_post"; // 岗位
+        String NCKD_BZ_EXPBURNDEND = "nckd_bz_expburndend"; // 费用承担部门
+        String NCKD_BZ_EXPCOLL = "nckd_bz_expcoll"; // 费用归集说明
+        String NCKD_BZ_SUPDAYS = "nckd_bz_supdays"; // 补助未涵盖天数
+        String NCKD_BZ_TRAVELDAYS = "nckd_bz_traveldays"; // 出差天数
+        String NCKD_BZ_ADJAMOUNT = "nckd_bz_adjamount"; // 补助调整金额
+        String NCKD_BZ_FINOPINION = "nckd_bz_finopinion"; // 财务建议说明
+
+        String NCKD_BZ_FINANDRAFT = "nckd_bz_finandraft"; // 财务对账底稿
+    }
+
+    /**
+     * 商旅费用
+     */
+    interface NCKD_SL_ENTRY {
+        String NCKD_SL_TRAVELER = "nckd_sl_traveler"; // 出差人
+        String NCKD_SL_TRAVELERNO = "nckd_sl_travlererno"; // 工号
+        String NCKD_SL_PLANPAYAMT = "nckd_sl_planpayamt"; // 机票金额
+        String NCKD_SL_TRAINPAYAMT = "nckd_sl_trainpayamt"; // 火车金额
+        String NCKD_SL_HOTELPAYAMT = "nckd_sl_hotelpayamt"; // 酒店金额
+        String NCKD_SL_ORDERAMT = "nckd_sl_orderamt"; // 合计金额
+        String NCKD_SL_SERVER = "nckd_sl_server"; // 服务商
+    }
+
+    interface NCKD_BZ_SUPDATA{
+
+        String ENTITYID = "nckd_bz_supdata";
+        /**
+         * 出差申请单号
+         */
+        String NCKD_TRIPREQBILL = "nckd_tripreqbill";
+
+        /**
+         * 出差人
+         */
+        String NCKD_APPLIERNAME = "nckd_appliername";
+
+        /**
+         * 人员编号
+         */
+        String NCKD_APPLIERNUMBER = "nckd_appliernumber";
+
+        /**
+         * 部门名称
+         */
+        String NCKD_ORGLONGNAME = "nckd_orglongname";
+
+        /**
+         * 职位
+         */
+        String NCKD_APPLIERPOSITIONSTR = "nckd_applierpositionstr";
+
+        /**
+         * 出发地
+         */
+        String NCKD_FROM = "nckd_from";
+
+        /**
+         * 目的地
+         */
+        String NCKD_TO = "nckd_to";
+
+        /**
+         * 行程开始日期
+         */
+        String NCKD_STARTDATE = "nckd_startdate";
+
+        /**
+         * 行程结束日期
+         */
+        String NCKD_ENDDATE = "nckd_enddate";
+
+        /**
+         * 预计出差天数
+         */
+        String NCKD_PLANDAYS = "nckd_plandays";
+
+        /**
+         * 事由
+         */
+        String NCKD_DESCRIPTION = "nckd_description";
+
+        /**
+         * 未涵盖日期范围
+         */
+        String NCKD_SUPDAYS = "nckd_supdays";
+
+        /**
+         * 财务建议出差天数
+         */
+        String NCKD_CHECKSUPDAY = "nckd_checksupday";
+
+        /**
+         * 补助金额
+         */
+        String NCKD_SUAPMT = "nckd_suapmt";
+
+        /**
+         * 说明
+         */
+        String NCKD_REMARK = "nckd_remark";
+    }
+
+}

+ 24 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/DepSecretaryConstant.java

@@ -0,0 +1,24 @@
+package nckd.jimin.jyyy.fi.common.constant.travelcheck;
+
+import nckd.jimin.jyyy.fi.common.constant.BillConstant;
+
+public interface DepSecretaryConstant extends BillConstant {
+    String ENTITYID = "nckd_depsecretary";
+
+    /**
+     * 部门秘书
+     */
+    String NCKD_SECRETARY = "nckd_secretary";
+
+    String ENTITYID_ENTRY = "nckd_reladepentry";
+
+    /**
+     * 分录部门
+     */
+    String NCKD_ORG = "nckd_org";
+    /**
+     * 分录说明
+     */
+    String NCKD_REMARK = "nckd_remark";
+
+}

+ 39 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/FinanceBillConfirmConstant.java

@@ -0,0 +1,39 @@
+package nckd.jimin.jyyy.fi.common.constant.travelcheck;
+
+import nckd.jimin.jyyy.fi.common.constant.BillConstant;
+
+public interface FinanceBillConfirmConstant extends BillConstant {
+    String ENTITYID = "nckd_finandraft_confirm";
+
+    /**
+     * 对账期间
+     */
+    String NCKD_CHECKMONTH = "nckd_checkmonth";
+
+    interface DETAILENTITY{
+        String ENTITYID = "detailentity";
+        /**
+         * 部门秘书
+         */
+        String NCKD_DEPSECRETARY = "nckd_depsecretary";
+
+        /**
+         * 负责部门
+         */
+        String NCKD_CHARGE_DEPT = "nckd_charge_dept";
+
+        /**
+         * 确认状态
+         */
+        String NCKD_CONFIRMSTATUS = "nckd_confirmstatus";
+
+    }
+
+    interface DRAFTENTRY{
+        String ENTITYID = "draftentry";
+        /**
+         * 财务对账底稿F7
+         */
+        String NCKD_TRAVELFINANDRAFT = "nckd_travelfinandraft";
+    }
+}

+ 82 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/TravelFinanDraftConstant.java

@@ -0,0 +1,82 @@
+package nckd.jimin.jyyy.fi.common.constant.travelcheck;
+
+import nckd.jimin.jyyy.fi.common.constant.BillConstant;
+
+/**
+ * 商旅财务对账底稿常量类
+ */
+public interface TravelFinanDraftConstant extends BillConstant {
+
+    String ENTITYID = "nckd_travelfinandraft";
+
+    String ENTITYID_F7 = "nckd_travelfinandraftf7";
+
+    String ORG = "org"; // 申请人公司
+
+    String NCKD_DEPT = "nckd_dept"; // 申请人部门
+
+    String NCKD_CONFIRMSTATUS = "nckd_confirmstatus";//确认状态
+
+    String NCKD_TRIPREQBILL = "nckd_tripreqbill"; // 出差申请单
+    String NCKD_TRAVER = "nckd_traver"; // 出差人
+    String NCKD_AMOUNT = "nckd_amount"; // 补助金额
+    String NCKD_SUMDAYS = "nckd_sumdays"; // 补助天数
+
+
+    /**************************列表动态字段,非实体字段**************************/
+    /**
+     * 部门
+     */
+    String NCKD_DEPARTMENT = "nckd_department";
+    /**
+     * 岗位
+     */
+    String NCKD_POSITION = "nckd_position";
+
+    /**
+     * 职级
+     */
+    String NCKD_JOBLEVER = "nckd_joblever";
+
+
+     /**************************列表动态字段,非实体字段**************************/
+
+
+    /**
+     * 明细分录字段常量
+     */
+    interface NCKD_DETAILENTITY {
+        String ENTITYID = "nckd_detailentity";
+        String NCKD_SOURCE = "nckd_source"; // 数据来源
+        String NCKD_ORDER_NUMBER = "nckd_order_number"; // 订单编码
+        String NCKD_STARTDATE = "nckd_startdate"; // 开始时间
+        String NCKD_FINISHDATE = "nckd_finishdate"; // 结束时间
+        String NCKD_ORDER_AMOUNT = "nckd_order_amount"; // 订单金额
+       String NCKD_FROM = "nckd_from"; //出发地
+        String NCKD_TO = "nckd_to"; //目的地
+
+    }
+
+    /**
+     * 操作类型常量
+     */
+    interface OperateTypes {
+        String DRAFTCONFIRM = "draftconfirm"; // 底稿确认
+    }
+
+    interface ConfirmStatus {
+        /**
+         * 待确认
+         */
+        String TOCONFIRM = "10";
+        /**
+         * 待对账
+         */
+        String TOCHECK = "20"; // 待对账
+        /**
+         * 已对账
+         */
+        String CHECKED = "30"; // 已对账
+    }
+
+}

+ 76 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/constant/travelcheck/TripReqBillConstant.java

@@ -0,0 +1,76 @@
+package nckd.jimin.jyyy.fi.common.constant.travelcheck;
+
+import nckd.jimin.jyyy.fi.common.constant.BillConstant;
+
+public interface TripReqBillConstant extends BillConstant {
+
+    String ENTITYID = "er_tripreqbill";
+    String ENTITYID_F7 = "nckd_tripreqbillf7";
+
+
+    /**
+     * 申请日期
+     */
+    String BIZDATE = "bizdate";
+
+    /**
+     * 单据编号
+     */
+    String BILLNO = "billno";
+
+    /**
+     * 出发日期
+     */
+    String RSTARTDATE = "rstartdate";
+
+    /**
+     * 结束日期
+     */
+    String RENDDATE = "renddate";
+
+    /**
+     * 出差人
+     */
+    String APPLIER = "APPLIER";
+
+    /**
+     * 职位
+     */
+    String APPLIERPOSITIONSTR = "applierpositionstr";
+
+    /**
+     * 申请人公司
+     */
+    String COMPANY = "company";
+
+    /**
+     * 部门
+     */
+    String ORG = "ORG";
+
+    /**
+     * 出发地
+     */
+    String RFROM = "rfrom";
+
+    /**
+     * 目的地
+     */
+    String RTO = "rto";
+
+    /**
+     * 事由
+     */
+    String DESCRIPTION = "description";
+
+    /**
+     * 天数
+     */
+    String PLANDAYS = "plandays";
+    /**
+     * 是否生成对账底稿
+     */
+    String NCKD_TRAVEL_ISDRAFT = "nckd_travel_isdraft";
+
+
+}

+ 7 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/common/util/CommonUtils.java

@@ -15,11 +15,18 @@ import kd.bos.servicehelper.BusinessDataServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
 
 
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
 public class CommonUtils {
 
+    private static final SimpleDateFormat PERIOD_FORMAT = new SimpleDateFormat("yyyy-MM");
+
+    public static String parsePeriod(Date date){
+        return PERIOD_FORMAT.format(date);
+    }
+
     public static Set getFiledFromDataSet(DataSet dt , String field){
         DataSet copy = dt.copy();
         Set datas = new HashSet();

+ 105 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/BusTravelCheckBillEdit.java

@@ -0,0 +1,105 @@
+package nckd.jimin.jyyy.fi.plugin.form.travelcheck;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.ShowType;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.events.HyperLinkClickEvent;
+import kd.bos.form.events.HyperLinkClickListener;
+import kd.bos.list.ListShowParameter;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.BusTravelCheckBillConstant;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TripReqBillConstant;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TravelFinanDraftConstant;
+
+import java.util.EventObject;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class BusTravelCheckBillEdit extends AbstractBillPlugIn implements BusTravelCheckBillConstant, HyperLinkClickListener {
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        EntryGrid bzEntryGrid = this.getControl(NCKD_BZ_ENTRY.ENTITYID);
+        bzEntryGrid.addHyperClickListener(this);
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        String name = e.getProperty().getName();
+        int rowIndex = e.getChangeSet()[0].getRowIndex();
+        Object oldValue = e.getChangeSet()[0].getOldValue();
+        Object newValue = e.getChangeSet()[0].getNewValue();
+        // 出差申请单号
+        if(NCKD_BZ_SUPDATA.NCKD_TRIPREQBILL.equals(name)){
+            tripReqBillChanged(oldValue, newValue, rowIndex);
+        }
+        if(NCKD_BZ_SUPDATA.NCKD_SUPDAYS.equals( name) || NCKD_BZ_SUPDATA.NCKD_SUAPMT.equals(name)){
+            updateBzEntryFromUnCoveredData(rowIndex);
+        }
+    }
+
+    @Override
+    public void hyperLinkClick(HyperLinkClickEvent event) {
+        String fieldName = event.getFieldName();
+        int rowIndex = event.getRowIndex();
+        if(NCKD_BZ_ENTRY.NCKD_BZ_TRAVELER.equals(fieldName)){
+            DynamicObjectCollection finanDraftCol = (DynamicObjectCollection)this.getModel().getValue(NCKD_BZ_ENTRY.NCKD_BZ_FINANDRAFT, rowIndex);
+            List<Long> draftIdList = finanDraftCol.stream().map(r -> r.getDynamicObject(KEY_FBASEDATAID).getLong(ID)).collect(Collectors.toList());
+
+            ListShowParameter showParameter = new ListShowParameter();
+            showParameter.setBillFormId(TravelFinanDraftConstant.ENTITYID);
+            showParameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+            showParameter.setShowFilter(false);
+            showParameter.setShowQuickFilter(false);
+            for(Object draftId : draftIdList){
+                showParameter.addLinkQueryPkId(draftId);
+            }
+            showParameter.getCustomParams().put("showdetail", "true");
+            this.getView().showForm(showParameter);
+
+        }
+    }
+
+
+    protected void tripReqBillChanged(Object oldValue , Object newValue, int rowIndex){
+        if(oldValue != null){
+            DynamicObject tripReqBill = (DynamicObject) oldValue;
+            int deleteRow = getApplierBzEntryRow(tripReqBill.getDynamicObject(TripReqBillConstant.APPLIER).getLong(TripReqBillConstant.ID));
+            if(deleteRow >= 0){
+                getModel().setValue(NCKD_BZ_ENTRY.NCKD_BZ_SUPDAYS, deleteRow , rowIndex);
+                getModel().setValue(NCKD_BZ_ENTRY.NCKD_BZ_ADJAMOUNT, deleteRow , rowIndex);
+            }
+        }
+        updateBzEntryFromUnCoveredData(rowIndex);
+    }
+
+    protected void updateBzEntryFromUnCoveredData(int rowIndex){
+        DynamicObject tripReqBill = (DynamicObject)this.getModel().getValue(NCKD_BZ_SUPDATA.NCKD_TRIPREQBILL, rowIndex);
+        // 更新补助天数
+        int updateRow = getApplierBzEntryRow(tripReqBill.getDynamicObject(TripReqBillConstant.APPLIER).getLong(TripReqBillConstant.ID));
+        if(updateRow >= 0){
+            getModel().setValue(NCKD_BZ_ENTRY.NCKD_BZ_SUPDAYS, this.getModel().getValue(NCKD_BZ_SUPDATA.NCKD_SUPDAYS,rowIndex) , updateRow);
+            getModel().setValue(NCKD_BZ_ENTRY.NCKD_BZ_ADJAMOUNT, this.getModel().getValue(NCKD_BZ_SUPDATA.NCKD_SUAPMT,rowIndex)  , updateRow);
+        }
+
+    }
+
+    protected int getApplierBzEntryRow(Long applierId){
+        int entryRowCount = this.getModel().getEntryRowCount(NCKD_BZ_ENTRY.ENTITYID);
+        for(int i = 0 ; i < entryRowCount ; i++){
+            DynamicObject traverInfo = (DynamicObject) getModel().getValue(NCKD_BZ_ENTRY.NCKD_BZ_TRAVELER, i);
+            // 通过出差人查询对应的补助分录
+            if(traverInfo != null
+                    && traverInfo.getLong(ID) == applierId){
+                return i;
+            }
+        }
+        return -1;
+    }
+
+
+}

+ 226 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/FinanDraftConfirmEdit.java

@@ -0,0 +1,226 @@
+package nckd.jimin.jyyy.fi.plugin.form.travelcheck;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.bill.OperationStatus;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+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.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.CollectionUtils;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.*;
+import nckd.jimin.jyyy.fi.common.util.CommonUtils;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 财务底稿确认单 编辑界面
+ */
+public class FinanDraftConfirmEdit extends AbstractBillPlugIn {
+
+    /**
+     * 下推商旅对账
+     */
+    private static final String OPERATE_PUSHCHECKBILL = "pushcheckbill";
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+        FormShowParameter parameter = this.getView().getFormShowParameter();
+        OperationStatus status = parameter.getStatus();
+        if(OperationStatus.ADDNEW.equals(status)){
+            // 新建界面赋值
+            initDataCreateNew();
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs args) {
+        super.afterDoOperation(args);
+
+        String operateKey = args.getOperateKey();
+        if(args.getOperationResult() == null || !args.getOperationResult().isSuccess()){
+            return;
+        }
+        if(OPERATE_PUSHCHECKBILL.equals(operateKey)){
+            createBusTravelCheckBill();
+        }
+
+    }
+
+    protected void createBusTravelCheckBill() {
+        DynamicObject billInfo = getModel().getDataEntity(true);
+        DynamicObjectCollection detailEntryCol = billInfo.getDynamicObjectCollection(FinanceBillConfirmConstant.DETAILENTITY.ENTITYID);
+        List<String> operateDescList = new ArrayList<>();
+        detailEntryCol.stream().forEach(detailEntry -> operateDescList.add(createBusTravelCheckBillByDetail(billInfo,detailEntry)));
+        getView().showMessage(String.join("\n", operateDescList));
+    }
+
+    protected String createBusTravelCheckBillByDetail(DynamicObject confirmBillInfo, DynamicObject detailEntry) {
+        Long financeBillConfirmId = confirmBillInfo.getLong(FinanceBillConfirmConstant.ID);
+        Long depSecretrayId = detailEntry.getDynamicObject(FinanceBillConfirmConstant.DETAILENTITY.NCKD_DEPSECRETARY).getLong(FinanceBillConfirmConstant.ID);
+        String depSecretrayName = detailEntry.getDynamicObject(FinanceBillConfirmConstant.DETAILENTITY.NCKD_DEPSECRETARY).getString(FinanceBillConfirmConstant.KEY_NAME);
+
+        DynamicObject queryCheckBillInfo = QueryServiceHelper.queryOne(BusTravelCheckBillConstant.ENTITYID, BusTravelCheckBillConstant.KEY_BILLNO, new QFilter[]{
+                new QFilter(BusTravelCheckBillConstant.NCKD_FINANDRAFT_CONFIRM, QCP.equals, financeBillConfirmId),
+                new QFilter(BusTravelCheckBillConstant.NCKD_DEPSECRETARY, QCP.equals, depSecretrayId)
+        });
+
+        if(queryCheckBillInfo != null){
+            return String.format("部门秘书【%s】已存在商旅对账单【%s】", depSecretrayName, queryCheckBillInfo.getString(BusTravelCheckBillConstant.KEY_BILLNO));
+        }
+
+        DynamicObject busTavelCheckBillInfo = BusinessDataServiceHelper.newDynamicObject(BusTravelCheckBillConstant.ENTITYID);
+        String number = CodeRuleServiceHelper.getNumber(BusTravelCheckBillConstant.ENTITYID, busTavelCheckBillInfo, null);
+        busTavelCheckBillInfo.set(BusTravelCheckBillConstant.KEY_BILLNO, number);
+        busTavelCheckBillInfo.set(BusTravelCheckBillConstant.NCKD_CHECKMONTH, confirmBillInfo.getString(FinanceBillConfirmConstant.NCKD_CHECKMONTH));
+        busTavelCheckBillInfo.set(BusTravelCheckBillConstant.NCKD_DEPSECRETARY, detailEntry.get(FinanceBillConfirmConstant.DETAILENTITY.NCKD_DEPSECRETARY));
+        busTavelCheckBillInfo.set(BusTravelCheckBillConstant.NCKD_FINANDRAFT_CONFIRM, financeBillConfirmId);
+
+        DynamicObjectCollection draftEntryCol = detailEntry.getDynamicObjectCollection(FinanceBillConfirmConstant.DRAFTENTRY.ENTITYID);
+        DynamicObjectCollection bzEntryCol = busTavelCheckBillInfo.getDynamicObjectCollection(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.ENTITYID);
+
+        // 创建差旅补助汇总分录
+        createCheckBzEntry(bzEntryCol,draftEntryCol);
+
+        SaveServiceHelper.save(new DynamicObject[]{ busTavelCheckBillInfo });
+        return String.format("部门秘书【%s】成功创建商旅对账单【%s】", depSecretrayName, busTavelCheckBillInfo.getString(BusTravelCheckBillConstant.KEY_BILLNO));
+    }
+
+    protected void createCheckBzEntry(DynamicObjectCollection bzEntryCol,DynamicObjectCollection draftEntryCol){
+        List<Object> draftBillIdList = draftEntryCol.stream().map(r -> r.getDynamicObject(FinanceBillConfirmConstant.DRAFTENTRY.NCKD_TRAVELFINANDRAFT).getPkValue()).collect(Collectors.toList());
+
+        DynamicObject[] draftBillArray = BusinessDataServiceHelper.load(draftBillIdList.toArray(), EntityMetadataCache.getDataEntityType(TravelFinanDraftConstant.ENTITYID_F7));
+
+        // 按照出差人分组
+        Map<Long, List<DynamicObject>> applyGroupMap = Arrays.stream(draftBillArray)
+                .collect(Collectors.groupingBy(r -> r.getDynamicObject(TravelFinanDraftConstant.NCKD_TRIPREQBILL).getDynamicObject(TripReqBillConstant.APPLIER).getLong(TripReqBillConstant.ID)));
+
+        for(Map.Entry<Long, List<DynamicObject>> applierRow : applyGroupMap.entrySet()){
+            DynamicObject bzEntry = bzEntryCol.addNew();
+            Long applierId = applierRow.getKey();
+            List<DynamicObject> applierDraftList = applierRow.getValue();
+            int sumDays = 0 ;
+            BigDecimal sumAmount = BigDecimal.ZERO;
+            for(DynamicObject draftEntry : applierDraftList){
+                sumDays += draftEntry.getInt(TravelFinanDraftConstant.NCKD_SUMDAYS);
+                sumAmount = sumAmount.add(draftEntry.getBigDecimal(TravelFinanDraftConstant.NCKD_AMOUNT));
+            }
+            DynamicObject applierInfo = BusinessDataServiceHelper.loadSingleFromCache(applierId, "bos_user");
+            bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_TRAVELER, applierInfo);
+            bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_TRAVELDAYS, sumDays);
+            bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_AMOUNT, sumAmount);
+            // 设置出差人信息,工号、部门、岗位。 银行账户、开户行
+            createApplierInfo(bzEntry);
+
+            // 设置关联底稿
+            DynamicObjectCollection multiDraftCol = bzEntry.getDynamicObjectCollection(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_FINANDRAFT);
+            applierDraftList.stream().forEach(draftEntry -> multiDraftCol.addNew().set(BusTravelCheckBillConstant.KEY_FBASEDATAID, draftEntry));
+        }
+    }
+
+    protected void createApplierInfo(DynamicObject bzEntry){
+        DynamicObject traveler = bzEntry.getDynamicObject(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_TRAVELER);
+        if(traveler != null){
+            // 获取员工信息
+            DynamicObject userInfo = QueryServiceHelper.queryOne("bos_user", "number,entryentity.orgstructure.fullname,entryentity.post.name",
+                    new QFilter(BusTravelCheckBillConstant.ID, QCP.equals, traveler.getLong(BusTravelCheckBillConstant.ID)).toArray());
+            //bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_TRAVELERNO, userInfo.getString("number"));
+            bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_DEPFULLNAME, userInfo.getString("entryentity.orgstructure.fullname"));
+            bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_POST, userInfo.getString("entryentity.post.name"));
+
+            // 获取收款账户信息
+//            DynamicObject payeerInfo = QueryServiceHelper.queryOne("er_payeer", "bankaccount,bankaccount.bank",new QFilter[]{
+//                    new QFilter("payer", QCP.equals, traveler.getLong(BusTravelCheckBillConstant.ID)),
+//                    new QFilter("isdefault", QCP.equals, Boolean.TRUE)
+//            });
+//            if(payeerInfo != null){
+//                bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_ACCOUNT, payeerInfo.get("payeraccount"));
+//                bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_PAYERBANK, payeerInfo.get("payerbank"));
+//            }
+            // 收款信息通过薪酬发放设置hsas_paysetting中获取
+            DynamicObject hrPaySetting = QueryServiceHelper.queryOne("hsas_paysetting", "entryentity.perbankcard.bankcardnum,entryentity.perbankcard.bankdeposit", new QFilter[]{
+                    new QFilter("salaryfile.person.number", QCP.equals, traveler.getString("number")),
+                    new QFilter("iscurrentversion", QCP.equals, "1"),
+                    new QFilter("datastatus", QCP.equals, "1"),
+                    new QFilter("entryentity.perbankcard.accountrelation.number", QCP.equals, "1010_S")
+            });
+            if(hrPaySetting != null){
+                bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_ACCOUNT, hrPaySetting.getString("entryentity.perbankcard.bankcardnum"));
+                bzEntry.set(BusTravelCheckBillConstant.NCKD_BZ_ENTRY.NCKD_BZ_PAYERBANK, hrPaySetting.get("entryentity.perbankcard.bankdeposit"));
+            }
+        }
+    }
+
+    protected void initDataCreateNew(){
+        // 单头字段赋值
+        String period = CommonUtils.parsePeriod(new Date());
+        getModel().setValue(FinanceBillConfirmConstant.NCKD_CHECKMONTH, period);
+        // 详情分录赋值
+        initDetailEntryData();
+    }
+
+    protected void initDetailEntryData(){
+        Map<Long, List<Long>> depSecretaryOrgMap = getDepSecretaryOrgMap();
+
+        DynamicObjectCollection detailEntryCol = this.getModel().getDataEntity(true).getDynamicObjectCollection(FinanceBillConfirmConstant.DETAILENTITY.ENTITYID);
+        detailEntryCol.clear();
+
+        // 创建对账明细
+        depSecretaryOrgMap.forEach((secreataryId, deptIds) -> createDetailEntry(detailEntryCol,secreataryId, deptIds));
+
+        //getView().updateView(FinanceBillConfirmConstant.DETAILENTITY.ENTITYID);
+
+    }
+
+    protected void createDetailEntry(DynamicObjectCollection detailEntryCol,Long secreataryId, List<Long> deptIds) {
+        // 查询部门下、待确认的商旅财务对账底稿数据
+
+        Set<Long> draftIds = QueryServiceHelper.query(TravelFinanDraftConstant.ENTITYID, TravelFinanDraftConstant.ID, new QFilter[]{
+                new QFilter(TravelFinanDraftConstant.NCKD_DEPT, QFilter.in, deptIds),
+                new QFilter(TravelFinanDraftConstant.NCKD_CONFIRMSTATUS, QFilter.equals, TravelFinanDraftConstant.ConfirmStatus.TOCONFIRM),
+        }).stream().map(r -> r.getLong(TravelFinanDraftConstant.ID)).collect(Collectors.toSet());
+
+        if(CollectionUtils.isEmpty(draftIds)){
+            // 没有待确认商旅财务对账底稿数据,不创建
+            return;
+        }
+
+        DynamicObject detailEntry = detailEntryCol.addNew();
+        DynamicObject secreatary = BusinessDataServiceHelper.loadSingleFromCache(secreataryId, "bos_user");
+        detailEntry.set(FinanceBillConfirmConstant.DETAILENTITY.NCKD_DEPSECRETARY, secreatary);
+        DynamicObjectCollection multiDeptCol = detailEntry.getDynamicObjectCollection(FinanceBillConfirmConstant.DETAILENTITY.NCKD_CHARGE_DEPT);
+
+        Map<Object, DynamicObject> deptMap = BusinessDataServiceHelper.loadFromCache(deptIds.toArray(), "bos_org");
+        deptIds.stream().forEach(deptId -> multiDeptCol.addNew().set(FinanceBillConfirmConstant.KEY_FBASEDATAID, deptMap.get(deptId)));
+
+        DynamicObjectCollection draftEntryCol = detailEntry.getDynamicObjectCollection(FinanceBillConfirmConstant.DRAFTENTRY.ENTITYID);
+        DynamicObject[] draftArray = BusinessDataServiceHelper.load(draftIds.toArray(), EntityMetadataCache.getDataEntityType(TravelFinanDraftConstant.ENTITYID_F7));
+        Arrays.stream(draftArray).forEach(draftInfo -> draftEntryCol.addNew().set(FinanceBillConfirmConstant.DRAFTENTRY.NCKD_TRAVELFINANDRAFT, draftInfo));
+    }
+
+    protected Map<Long, List<Long>> getDepSecretaryOrgMap() {
+        DynamicObject[] depSecretaryArray = BusinessDataServiceHelper.load(DepSecretaryConstant.ENTITYID,
+                String.join(",", DepSecretaryConstant.NCKD_SECRETARY, DepSecretaryConstant.ENTITYID_ENTRY, DepSecretaryConstant.NCKD_ORG), new QFilter[]{
+                        new QFilter(DepSecretaryConstant.KEY_STATUS, QFilter.equals, "C"),
+                        new QFilter(DepSecretaryConstant.KEY_ENABLE, QFilter.equals, Boolean.TRUE)
+                });
+
+        // 部门秘书和及关联的组织映射
+        return Arrays.stream(depSecretaryArray).collect(Collectors.toMap(
+                r -> r.getDynamicObject(DepSecretaryConstant.NCKD_SECRETARY).getLong(DepSecretaryConstant.ID),
+                r -> r.getDynamicObjectCollection(DepSecretaryConstant.ENTITYID_ENTRY).stream()
+                        .map(entry -> entry.getDynamicObject(DepSecretaryConstant.NCKD_ORG).getLong(DepSecretaryConstant.ID))
+                        .collect(Collectors.toList()),
+                (a, b) -> a));
+
+    }
+}

+ 67 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/PersonPositionListProvider.java

@@ -0,0 +1,67 @@
+package nckd.jimin.jyyy.fi.plugin.form.travelcheck;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.mvc.list.ListDataProvider;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TravelFinanDraftConstant;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class PersonPositionListProvider extends ListDataProvider {
+
+    @Override
+    public DynamicObjectCollection getData(int start, int limit) {
+        DynamicObjectCollection data = super.getData(start, limit);
+
+        List<String> applierNameList = data.stream()
+                .map(dyx -> dyx.getString("nckd_tripreqbill.applier.number"))
+                .collect(Collectors.toList());
+
+        DynamicObjectCollection queryUserCol = QueryServiceHelper.query("bos_user", "number,entryentity.dpt.fullname", new QFilter[]{
+                new QFilter("number", QCP.in, applierNameList),
+                new QFilter("ispartjob", QCP.equals, Boolean.FALSE),
+        });
+        // 查询人员及主岗部门映射
+        Map<String, String> userDeptMap = queryUserCol.stream()
+                .collect(Collectors.toMap(dyx -> dyx.getString("number"), dyx -> dyx.getString("entryentity.dpt.fullname"), (k1, k2) -> k1));
+
+
+        // 查询当前生效的人员岗位信息
+        Map<String, String> positionMap = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPPOSORGREL, "person.number,position.name", new QFilter[]{
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("datastatus", QCP.equals, "1"),
+                new QFilter("isprimary", QCP.equals, "1"),
+                new QFilter("person.number", QCP.in, applierNameList)
+        }).stream().collect(Collectors.toMap(r -> r.getString("person.number"), r -> r.getString("position.name"), (k1, k2) -> k1));
+
+        // 查询当前生效的人员职级信息
+        Map<String, String> jobleverMap = QueryServiceHelper.query(BillTypeConstants.HRPI_EMPJOBREL, "person.number,joblevel.name", new QFilter[]{
+                new QFilter("businessstatus", QCP.equals, "1"),
+                new QFilter("datastatus", QCP.equals, "1"),
+                new QFilter("iscurrentversion", QCP.equals, "1"),
+                new QFilter("person.number", QCP.in, applierNameList)
+        }).stream().collect(Collectors.toMap(r -> r.getString("person.number"), r -> r.getString("joblevel.name"), (k1, k2) -> k1));
+
+        for(DynamicObject listRow : data){
+            String applierName = listRow.getString("nckd_tripreqbill.applier.number");
+            if(userDeptMap.containsKey(applierName)){
+                listRow.set(TravelFinanDraftConstant.NCKD_DEPARTMENT, userDeptMap.get(applierName));
+            }
+            if(positionMap.containsKey(applierName)){
+                listRow.set(TravelFinanDraftConstant.NCKD_POSITION, positionMap.get(applierName));
+            }
+            if(jobleverMap.containsKey(applierName)){
+                listRow.set(TravelFinanDraftConstant.NCKD_JOBLEVER, jobleverMap.get(applierName));
+            }
+        }
+        return data;
+    }
+
+}

+ 81 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/form/travelcheck/TravelFinanDraftList.java

@@ -0,0 +1,81 @@
+package nckd.jimin.jyyy.fi.plugin.form.travelcheck;
+
+import kd.bos.bill.BillOperationStatus;
+import kd.bos.bill.BillShowParameter;
+import kd.bos.bill.OperationStatus;
+import kd.bos.filter.FilterColumn;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.ShowType;
+import kd.bos.form.control.Control;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeCreateListDataProviderArgs;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.events.FilterContainerInitArgs;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.orm.ORM;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.FinanceBillConfirmConstant;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TravelFinanDraftConstant;
+import nckd.jimin.jyyy.fi.common.util.CommonUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class TravelFinanDraftList extends AbstractListPlugin {
+
+    @Override
+    public void filterContainerInit(FilterContainerInitArgs args) {
+        super.filterContainerInit(args);
+        List<FilterColumn> commonFilterColumns = args.getCommonFilterColumns();
+        FormShowParameter parameter = getView().getFormShowParameter();
+        Map<String, Object> customParams = parameter.getCustomParams();
+        if(customParams.containsKey("showdetail")){
+            for(FilterColumn column: commonFilterColumns){
+                column.setDefaultValue(null);
+            }
+        }
+    }
+
+    @Override
+    public void beforeCreateListDataProvider(BeforeCreateListDataProviderArgs args) {
+        super.beforeCreateListDataProvider(args);
+        //args.setListDataProvider(new PersonPositionListProvider());
+    }
+
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+        FormOperate operate = (FormOperate)args.getSource();
+        String key = operate.getOperateKey();
+        if(TravelFinanDraftConstant.OperateTypes.DRAFTCONFIRM.equals(key)){
+            String period = CommonUtils.parsePeriod(new Date());
+            // 判断当前期间是否已经存在
+            if(ORM.create().exists(FinanceBillConfirmConstant.ENTITYID, new QFilter(FinanceBillConfirmConstant.NCKD_CHECKMONTH, QCP.equals, period).toArray())){
+                this.getView().showTipNotification(String.format("期间[%s]已经存在结账确认单,请勿重复创建!", period));
+                args.setCancel(true);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs args) {
+        super.afterDoOperation(args);
+        String operateKey = args.getOperateKey();
+        if(args.getOperationResult() == null || !args.getOperationResult().isSuccess() ){
+            return;
+        }
+
+        if(TravelFinanDraftConstant.OperateTypes.DRAFTCONFIRM.equals(operateKey)){
+            BillShowParameter parameter = new BillShowParameter();
+            parameter.setFormId(FinanceBillConfirmConstant.ENTITYID);
+            parameter.setBillStatus(BillOperationStatus.ADDNEW);
+            parameter.setStatus(OperationStatus.ADDNEW);
+            parameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+            this.getView().showForm(parameter);
+        }
+    }
+}

+ 21 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/plugin/operate/travelcheck/FinanDraftConfirmOp.java

@@ -0,0 +1,21 @@
+package nckd.jimin.jyyy.fi.plugin.operate.travelcheck;
+
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+
+public class FinanDraftConfirmOp extends AbstractOperationServicePlugIn {
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+        String operationKey = e.getOperationKey();
+        // 保存或者提交后反写商旅底稿已对账
+        if("save".equals(operationKey) || "submit".equals(operationKey)){
+
+        }
+        //
+        if("delete".equals(operationKey)){
+
+        }
+    }
+}

+ 217 - 0
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/task/TravelFinanDraftCreateTask.java

@@ -0,0 +1,217 @@
+package nckd.jimin.jyyy.fi.task;
+
+import kd.bos.algo.DataSet;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.ORM;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TravelFinanDraftConstant;
+import nckd.jimin.jyyy.fi.common.constant.travelcheck.TripReqBillConstant;
+import nckd.jimin.jyyy.fi.common.util.CommonUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * 商旅财务对账底稿创建任务
+ * 每个月月初同步尚未对账的出差申请单、差旅报销单创建底稿
+ *
+ */
+public class TravelFinanDraftCreateTask extends AbstractTask {
+    private static final Log logger = LogFactory.getLog(TravelFinanDraftCreateTask.class);
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        logger.info("TravelFinanDraftCreateTask begin");
+
+        DynamicObject tripExpenseItem = QueryServiceHelper.queryOne("er_tripexpenseitem", "nckd_travelbzstd", new QFilter("longnumber", QCP.equals, "004").toArray());
+        if(tripExpenseItem == null){
+            logger.error("没有配置补助的差旅标准。");
+            return;
+        }
+        BigDecimal bzAmount = tripExpenseItem.getBigDecimal("nckd_travelbzstd");
+        if(bzAmount == null || bzAmount.compareTo(BigDecimal.ZERO) <= 0){
+            logger.error("差旅补助金额不能为0。");
+            return;
+        }
+        // 获取本月月初
+        LocalDateTime firstDayStart = LocalDate.now().withDayOfMonth(1).atStartOfDay();
+        // 转换为Date对象
+        Date firstDay = Date.from(firstDayStart.atZone(ZoneId.systemDefault()).toInstant());
+
+        // 查询出差申请单
+        DynamicObjectCollection tripReqBillCol = QueryServiceHelper.query(TripReqBillConstant.ENTITYID, TripReqBillConstant.BILLNO, new QFilter[]{
+                // 状态为审核通过、已付款、关闭
+                new QFilter(TripReqBillConstant.KEY_BILLSTATUS, QFilter.in, Arrays.asList("E", "G", "I")),
+                new QFilter(TripReqBillConstant.NCKD_TRAVEL_ISDRAFT, QFilter.equals, Boolean.FALSE),
+                new QFilter(TripReqBillConstant.RENDDATE, QFilter.less_than, firstDay),
+        });
+
+        List<String> tripReqBillNos = tripReqBillCol.stream().map(dyx -> dyx.getString(TripReqBillConstant.BILLNO)).collect(Collectors.toList());
+
+        createTravelFinanDraft(tripReqBillNos,bzAmount);
+        logger.info("TravelFinanDraftCreateTask end");
+    }
+
+    protected void createTravelFinanDraft(List<String> billNos,BigDecimal bzAmount) {
+        // 创建商旅财务对账底稿
+        List<Long> tripReqBillList = QueryServiceHelper.query(TripReqBillConstant.ENTITYID, TripReqBillConstant.ID, new QFilter(TripReqBillConstant.BILLNO, QCP.in, billNos).toArray())
+                .stream().map(r -> r.getLong(TripReqBillConstant.ID)).collect(Collectors.toList());
+        DynamicObject[] tripReqBillArray = BusinessDataServiceHelper.load(tripReqBillList.toArray(), EntityMetadataCache.getDataEntityType(TripReqBillConstant.ENTITYID_F7));
+
+        // 获取关联的火车订单
+        Map<String, List<DynamicObject>> trainBillMap = QueryServiceHelper.query("er_trainbill", "oabillnum,ordernum,departtime,arrivetime,departcity,arrivecity,totalamount", new QFilter("oabillnum", QCP.in, billNos).toArray())
+                .stream()
+                .collect(Collectors.groupingBy(r -> r.getString("oabillnum")));
+
+        // 获取机票订单
+        Map<String, List<DynamicObject>> planBillMap = QueryServiceHelper.query("er_planebill", "oabillnum,ordernum,takeofftime,landingtime,fromcityname,tocityname,totalamount", new QFilter("oabillnum", QCP.in, billNos).toArray())
+                .stream()
+                .collect(Collectors.groupingBy(r -> r.getString("oabillnum")));
+
+        // 获取关联的差旅报销发票分录 -》 发票与费用明细分录 联查 行程信息分录 通过源单行程分录ID 联查出差申请
+        Map<String, List<DynamicObject>> tripReimInvoiceData = getTripReimInvoiceData(billNos);
+
+
+        for(DynamicObject tripReqBill : tripReqBillArray){
+            String billno = tripReqBill.getString(TripReqBillConstant.BILLNO);
+            DynamicObject travelFinanDraftInfo = BusinessDataServiceHelper.newDynamicObject(TravelFinanDraftConstant.ENTITYID);
+            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_TRIPREQBILL, tripReqBill);
+            travelFinanDraftInfo.set(TravelFinanDraftConstant.ORG, tripReqBill.get(TripReqBillConstant.COMPANY));
+            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_DEPT, tripReqBill.get(TripReqBillConstant.ORG));
+            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_CONFIRMSTATUS, "10");
+            travelFinanDraftInfo.set(TravelFinanDraftConstant.KEY_BILLNO,tripReqBill.getString(TripReqBillConstant.BILLNO));
+
+            DynamicObjectCollection draftEntryCol = travelFinanDraftInfo.getDynamicObjectCollection(TravelFinanDraftConstant.NCKD_DETAILENTITY.ENTITYID);
+            if(trainBillMap.containsKey(billno) && trainBillMap.get(billno) != null){
+                List<DynamicObject> trainBillList = trainBillMap.get(billno);
+                trainBillList.stream().forEach(trainBill -> getDraftData(draftEntryCol.addNew(),"6",trainBill.getString("ordernum"),
+                        trainBill.getDate("departtime"),trainBill.getDate("arrivetime"),
+                        trainBill.getString("departcity"),trainBill.getString("arrivecity"), trainBill.getBigDecimal("totalamount")));
+            }
+            if(planBillMap.containsKey(billno) && planBillMap.get(billno) != null){
+                List<DynamicObject> planBillList = planBillMap.get(billno);
+                planBillList.stream().forEach(planBill -> getDraftData(draftEntryCol.addNew(),"2",planBill.getString("ordernum"),
+                        planBill.getDate("takeofftime"),planBill.getDate("landingtime"),
+                        planBill.getString("fromcityname"),planBill.getString("tocityname"),planBill.getBigDecimal("totalamount")));
+            }
+            if(tripReimInvoiceData.containsKey(billno) && tripReimInvoiceData.get(billno) != null){
+                List<DynamicObject> tripReimInvoiceList = tripReimInvoiceData.get(billno);
+                tripReimInvoiceList.stream().forEach(tripReimInvoice -> getDraftData(draftEntryCol.addNew(),"7",
+                        String.join("-",tripReimInvoice.getString("tripBillno"),tripReimInvoice.getString("invoiceentry.invoiceno")),
+                        tripReimInvoice.getDate("invoiceentry.carrierdate"),null,
+                        tripReimInvoice.getString("invoiceentry.istartcity"),tripReimInvoice.getString("invoiceentry.idestcity"),tripReimInvoice.getBigDecimal("invoiceentry.totalamount")));
+            };
+
+            // 获取draftEntryCol中开始时间的额最早时间  和结束时间的最晚时间
+            Date earliestTime = draftEntryCol.stream()
+                    .filter(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE) != null)
+                    .map(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE))
+                    .min(Comparator.comparing(Date::getTime)).orElse(null);
+
+            Date latestTime = draftEntryCol.stream()
+                    .filter(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE) != null)
+                    .map(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE))
+                    .max(Comparator.comparing(Date::getTime)).orElse(null);
+            //获取latestTime与earliestTime 的日期相差天数,加上1
+            if(earliestTime != null && latestTime != null){
+                int days = getDays(earliestTime, latestTime, tripReqBill.getDynamicObject(TripReqBillConstant.COMPANY));
+                travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_SUMDAYS, days);
+                // 查询补助金额
+                travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_AMOUNT, bzAmount.multiply(BigDecimal.valueOf(days)));
+            }
+            tripReqBill.set(TripReqBillConstant.NCKD_TRAVEL_ISDRAFT, Boolean.TRUE);
+
+            SaveServiceHelper.save(new DynamicObject[]{ travelFinanDraftInfo });
+            SaveServiceHelper.save(new DynamicObject[]{ tripReqBill });
+        }
+    }
+
+    protected int getDays(Date startTime, Date endTime,DynamicObject org){
+        // 获取组织下的工作日历
+        DynamicObject workCalendar = QueryServiceHelper.queryOne("bd_workcalendar", "id", new QFilter("org", QCP.equals, org.getPkValue()).toArray());
+        List<Date> notWorkDayList = new ArrayList<>();
+        if(workCalendar != null){
+            notWorkDayList = QueryServiceHelper.query("bd_workcalendar", "dateentry.workdate", new QFilter[]{
+                    new QFilter("dateentry.datetype", QCP.equals, "3"),
+                    new QFilter("dateentry.workdate", QCP.large_equals, startTime),
+                    new QFilter("dateentry.workdate", QCP.less_equals, endTime),
+                    new QFilter("id", QCP.equals, workCalendar.getPkValue())
+            }).stream().map(r -> r.getDate("dateentry.workdate")).collect(Collectors.toList());
+        }
+        
+        // 计算开始日期和结束日期之间总天数
+        long diffInMillis = endTime.getTime() - startTime.getTime();
+        int totalDays = (int) (diffInMillis / (24 * 60 * 60 * 1000)) + 1;
+        
+        // 计算工作日天数(总天数减去非工作日天数)
+        int workDays = totalDays - notWorkDayList.size();
+        
+        return workDays;
+    }
+
+
+    protected DynamicObject getDraftData( DynamicObject draftEntry , String source, String orderNum, Date startTime,
+                                          Date endTime , String from , String to , BigDecimal amount){
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_SOURCE, source);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_ORDER_NUMBER, orderNum);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE, startTime);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE, endTime);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FROM, from);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_TO, to);
+        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_ORDER_AMOUNT, amount);
+        return draftEntry;
+    }
+
+
+
+    protected Map<String, List<DynamicObject>> getTripReimInvoiceData(List<String> billNos){
+
+        // 查询关联的行程分录ID
+        DataSet queryTripReqEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), TripReqBillConstant.ENTITYID,
+                "billno,tripentry.id", new QFilter("billno", QCP.in, billNos).toArray(), "");
+        Set tripEntryIdSet = CommonUtils.getFiledFromDataSet(queryTripReqEntryDataSet, "tripentry.id");
+        // 通过行程分录ID 联查差旅报销行程
+        // 差旅报销单行程分录ID -> 出差申请分录ID映射
+        DataSet tripReimEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "tripentry.tripentrysourceid,tripentry.entryentity.id",
+                new QFilter("tripentry.tripentrysourceid", QCP.in, tripEntryIdSet).toArray(), "");
+        Set tripReimEntryIdSet = CommonUtils.getFiledFromDataSet(tripReimEntryDataSet, "tripentry.entryentity.id");
+
+        // 获取行程信息分录下的发票信息
+        DataSet queryReimInvoiceEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "invoiceitementry.itementryid,invoiceitementry.invoiceheadentryid",
+                new QFilter("invoiceitementry.itementryid", QCP.in, tripReimEntryIdSet).toArray(), "");
+        Set invoiceEntryIdSet = CommonUtils.getFiledFromDataSet(queryReimInvoiceEntryDataSet, "invoiceitementry.invoiceheadentryid");
+
+
+        // 查询发票相关信息
+        DataSet queryInvoiceEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "billno,invoiceentry.id,invoiceentry.invoiceno,invoiceentry.carrierdate,invoiceentry.istartcity,invoiceentry.idestcity,invoiceentry.totalamount",
+                new QFilter("invoiceentry.id", QCP.in, invoiceEntryIdSet).toArray(), "");
+
+        // 建议出差申请和发票明细的关联关系
+        DataSet queryReimInvoiceDataSet = queryTripReqEntryDataSet.leftJoin(tripReimEntryDataSet)
+                .on("tripentry.id", "tripentry.tripentrysourceid").select("billno", "tripentry.entryentity.id").finish()
+                .leftJoin(queryReimInvoiceEntryDataSet)
+                .on("tripentry.entryentity.id", "invoiceitementry.itementryid").select("billno", "invoiceitementry.invoiceheadentryid").finish()
+                .leftJoin(queryInvoiceEntryDataSet)
+                .on("invoiceitementry.invoiceheadentryid", "invoiceentry.id").select(new String[]{ "billno" }, new String[]{ "billno tripBillno","invoiceentry.invoiceno", "invoiceentry.carrierdate", "invoiceentry.istartcity","invoiceentry.idestcity", "invoiceentry.totalamount" }).finish();
+        DynamicObjectCollection reimInvoiceCol = ORM.create().toPlainDynamicObjectCollection(queryReimInvoiceDataSet);
+        return reimInvoiceCol.stream().filter(r -> !Objects.isNull(r.getString("invoiceentry.invoiceno"))).collect(Collectors.groupingBy(r -> r.getString("billno")));
+
+    }
+
+}