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