Parcourir la source

Merge remote-tracking branch 'origin/master'

wangjun il y a 4 semaines
Parent
commit
b6b160e6ed

+ 16 - 1
code/base/nckd-jimin-base-helper/src/main/java/nckd/base/helper/CommonHelperUtils.java

@@ -72,7 +72,7 @@ public class CommonHelperUtils {
     }
 
     /**
-     *
+     * 返回日期字符格式
      * @param date
      * @param pattern
      * @return
@@ -82,6 +82,21 @@ public class CommonHelperUtils {
         return sdf.format(date);
     }
 
+    /**
+     *
+     * @param dateString
+     * @param pattern
+     * @return
+     */
+    public static Date getDateStringFormat(String dateString, String pattern){
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        try {
+            return sdf.parse(dateString);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * 格式化BigDecimal对象
      * @param objValue

+ 108 - 1
code/jyyy/nckd-jimin-jyyy-fi/src/main/java/nckd/jimin/jyyy/fi/webapi/SRMSynPayApiPlugin.java

@@ -20,6 +20,7 @@ import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.imc.bdm.common.constant.BotpCallBackLogConstant;
 import nckd.base.helper.CommonHelperUtils;
 import javax.validation.Valid;
 import java.io.Serializable;
@@ -550,6 +551,10 @@ public class SRMSynPayApiPlugin implements Serializable {
             return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
         }
 
+        //合同台账
+        qFilter = new QFilter("contractcode", QCP.equals, contract);
+        DynamicObject contractInfo = BusinessDataServiceHelper.loadSingle("er_contractbill", qFilter.toArray());
+
         //单据类型
         DynamicObject billTypeInfo = CommonHelperUtils.queryBaseDynamicObject("bos_billtype", "number", "er_prepaybill_BT_S");
 
@@ -562,6 +567,7 @@ public class SRMSynPayApiPlugin implements Serializable {
             returnMessage = e.getMessage();
             return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
         }
+//        Date repaymentDate = CommonHelperUtils.getDateStringFormat("2099-12-31", "yyyy-MM-dd");
 
         dailyLoanBill.set("bizdate", bizDate);//申请日期
         dailyLoanBill.set("billstatus", "A");//单据状态
@@ -584,6 +590,7 @@ public class SRMSynPayApiPlugin implements Serializable {
         dailyLoanBill.set("detailtype", "biztype_other");//关联业务
         dailyLoanBill.set("ispush", "false");//合同下推生成
         dailyLoanBill.set("nckd_srmstatus", "1");//srm状态,1:SRM已推送;2:已退回SRM;3:已反写SRM;4:反写SRM失败
+//        dailyLoanBill.set("repaymentdate", repaymentDate);//预计冲销日期
 
         BigDecimal totalReimburseAmount = BigDecimal.ZERO;//报销金额合计
 
@@ -632,6 +639,12 @@ public class SRMSynPayApiPlugin implements Serializable {
         dailyLoanBill.set("expenseentryentity", expenseEntry);
 
         //收款明细
+        //支付方式:默认为 银企直连
+        qFilter = new QFilter("number", QCP.equals, "BANK");
+
+        DynamicObject settleType = BusinessDataServiceHelper.loadSingle("bd_settlementtype", qFilter.toArray());
+        DynamicObject supplier = null;
+
         DynamicObjectCollection accountEntry  = dailyLoanBill.getDynamicObjectCollection("accountentry");
         type = accountEntry.getDynamicObjectType();
         JSONArray dailyLoanBillAccountEntry = inputData.getJSONArray("dailyLoanBillAccountEntry");
@@ -653,11 +666,13 @@ public class SRMSynPayApiPlugin implements Serializable {
                 return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
             }
 
-            DynamicObject supplier = CommonHelperUtils.queryBaseDynamicObject("bd_supplier", "societycreditcode", payerName);
+            supplier = CommonHelperUtils.queryBaseDynamicObject("bd_supplier", "societycreditcode", payerName);
             if(supplier == null){
                 returnMessage = "供应商(" + payerName + ")在星瀚系统中未匹配到数据!";
                 return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
             }
+            //供应商银行账号信息
+            DynamicObjectCollection entryBanks = supplier.getDynamicObjectCollection("entry_bank");
 
             DynamicObject currency = CommonHelperUtils.queryBaseDynamicObject("bd_currency", "number", currencyType);
             if(currency == null){
@@ -672,9 +687,24 @@ public class SRMSynPayApiPlugin implements Serializable {
             BigDecimal amountOriLocal = amountOri.multiply(exchangeRate).setScale(4, BigDecimal.ROUND_HALF_UP);
 
             DynamicObject entry = new DynamicObject(type);
+
+            entry.set("paymode", settleType);//支付方式
             entry.set("payertype", "bd_supplier");//收款人类型
             entry.set("supplier", supplier);//收款人
             entry.set("payername", supplier.getString("name"));//收款人名称
+
+            if(entryBanks.size() > 0){
+                for(DynamicObject entryBank : entryBanks){
+                    if(entryBank.getBoolean("isdefault_bank")){
+                        entry.set("payeraccount", entryBank.getString("bankaccount"));//银行账号
+                        entry.set("payeraccountname", entryBank.getString("accountname"));//账户名称
+                        entry.set("payerbank", entryBank.getDynamicObject("bank"));//开户银行
+
+                        break;
+                    }
+                }
+            }
+
             entry.set("accountcurrency", currency);//币别
             entry.set("accexchangerate", exchangeRate);//汇率
             entry.set("orireceiveamount", amountOri);//收款金额
@@ -688,6 +718,83 @@ public class SRMSynPayApiPlugin implements Serializable {
         }
         dailyLoanBill.set("accountentry", accountEntry);
 
+        if(supplier == null){
+            returnMessage = "无收款供应商信息!";
+            return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
+        }
+
+        //合同信息
+        if(contractInfo != null){
+            DynamicObjectCollection contractEntry  = dailyLoanBill.getDynamicObjectCollection("contractentry");
+            type = contractEntry.getDynamicObjectType();
+
+            DynamicObject currency = contractInfo.getDynamicObject("contractcurrency");
+            currency = CommonHelperUtils.queryBaseDynamicObject("bd_currency", "number", currency.getString("number"));
+
+            if(currency == null){
+                returnMessage = "币别(" + orgUnit + ")在星瀚系统中未匹配到数据!";
+                return buildReturnData(code, srmBillNo, returnMessage, null, null, null);
+            }
+
+            BigDecimal exchangeRate = BigDecimal.ONE;
+            if(currency.getLong("id") != 1L){
+                exchangeRate = CommonHelperUtils.getExchangeRate(exchangeTableId, currency.getLong("id"), 1L, bizDate);
+            }
+
+            BigDecimal contractAmount = contractInfo.getBigDecimal("contractamount");//合同总额(初始)
+            BigDecimal oriapplyAmount = contractInfo.getBigDecimal("oriapplyamount");//合同总额(变更后)
+
+            DynamicObjectCollection contractPartyEntry = contractInfo.getDynamicObjectCollection("contractpartyentry"); //
+            if(contractPartyEntry.size() > 0){
+                for(DynamicObject partyEntry : contractPartyEntry){
+                    DynamicObject entry = new DynamicObject(type);
+
+                    DynamicObject contractparty = partyEntry.getDynamicObject("contractparty");
+                    if(contractparty.getLong("id") != supplier.getLong("id")){
+                        continue;
+                    }
+
+                    entry.set("seq", 1);
+                    entry.set("contractcode", contract);//合同号
+                    entry.set("contractname", contractInfo.getString("contractname"));//合同名称
+                    entry.set("signdate", contractInfo.getDate("signdate"));//签订日期
+                    entry.set("contractdescription", contractInfo.getString("description"));//合同说明
+                    entry.set("contractapplier", contractInfo.getDynamicObject("applier"));//经办人
+                    entry.set("contractcostorg", contractInfo.getDynamicObject("costcompany"));//核算组织
+                    entry.set("contractparta", contractInfo.getDynamicObject("parta"));//甲方old
+                    entry.set("contractpartbtype", "bd_supplier");//乙方类型
+                    entry.set("contractpartb", contractparty);//乙方
+
+                    DynamicObjectCollection payplanEntrys = contractInfo.getDynamicObjectCollection("expenseentryentity");
+                    if(payplanEntrys.size() > 0){
+                        DynamicObject planEntry = payplanEntrys.get(0);
+
+                        entry.set("contractexppaytypeid", planEntry.getDynamicObject("paymenttypeid"));//付款类型
+                        entry.set("contractexphappendate", planEntry.getDate("happendate"));//预计付款日期
+                        entry.set("contractexpproject", planEntry.getDynamicObject("std_project"));//项目
+                        entry.set("contractexpenseitem", planEntry.getDynamicObject("expenseitem"));//费用项目
+                        entry.set("contractexpcurrency", planEntry.getDynamicObject("entrycurrency"));//币种
+                        entry.set("contractexpchangerate", planEntry.getBigDecimal("exchangerate"));//汇率
+                        entry.set("contractexpquotetype", "0");//换算方式
+                        entry.set("contractnotpayamount", planEntry.getBigDecimal("expnotpayamount"));//未付金额本位币
+                        entry.set("contractcanloanamount", planEntry.getBigDecimal("canloanamount"));//可预付金额
+                        entry.set("concurrcanloanamount", planEntry.getBigDecimal("canloancurramount"));//可预付金额本位币
+                    }
+
+                    entry.set("contractpartatypenew", "bos_org");//甲方类型
+                    entry.set("contractpartanew", costCompany);//甲方
+                    entry.set("nckd_contractamt", contractAmount);//合同总额(初始)
+                    entry.set("nckd_oriapplyamt", oriapplyAmount);//合同总额(变更后)
+
+                    contractEntry.add(entry);
+
+                    break;
+                }
+            }
+
+            dailyLoanBill.set("contractentry", contractEntry);
+        }
+
 
         dailyLoanBill.set("loanamount", totalReimburseAmount);//报销金额
         dailyLoanBill.set("approveamount", totalReimburseAmount);//核定金额

+ 10 - 11
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/tsrsc/plugin/form/YearCrApplyFormPlugin.java

@@ -550,21 +550,20 @@ public class YearCrApplyFormPlugin extends AbstractBillPlugIn implements BeforeF
                 int[] rows2 = treeEntryEntity2.getSelectRows();
                 DynamicObject nckdRecruitcompany2 = (DynamicObject)this.getModel().getValue("org");
 
-
-
-                QFilter qFilter1 = new QFilter("adminorg.number", QCP.like, nckdRecruitcompany2.getString("number") + "%");
+                //QFilter qFilter1 = new QFilter("adminorg.number", QCP.like, nckdRecruitcompany2.getString("number") + "%");
                 DynamicObject nckdRecruitorg = (DynamicObject)this.getModel().getValue("nckd_recruitorg", rows2[0]);
                 if(ObjectUtils.isNotEmpty(nckdRecruitorg)){
-                    qFilter1.and("adminorg.id", QCP.equals, nckdRecruitorg.getPkValue());
+                    QFilter qFilter1 = new QFilter("adminorg.id", QCP.equals, nckdRecruitorg.getPkValue());
+                    showParameter3.getListFilterParameter().setFilter(qFilter1);
                 }
-                showParameter3.getListFilterParameter().setFilter(qFilter1);
-                break;
-            case "org":
-                ListShowParameter showParameter4 = (ListShowParameter)e.getFormShowParameter();
-                // 去除部门
-                QFilter qFilter4 = new QFilter("orgpattern.number", "in", COMPANY_LIST2);
-                showParameter4.getListFilterParameter().setFilter(qFilter4);
+
                 break;
+//            case "org":
+//                ListShowParameter showParameter4 = (ListShowParameter)e.getFormShowParameter();
+//                // 去除部门
+//                QFilter qFilter4 = new QFilter("orgpattern.number", "in", COMPANY_LIST2);
+//                showParameter4.getListFilterParameter().setFilter(qFilter4);
+//                break;
             default:
                 break;
         }

+ 13 - 12
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/tsrsc/plugin/form/YearCrApplyPlanFormPlugin.java

@@ -148,16 +148,16 @@ public class YearCrApplyPlanFormPlugin extends AbstractBillPlugIn implements Bef
     @Override
     public void beforeF7Select(BeforeF7SelectEvent e) {
         String fieldKey = e.getProperty().getName();
-        switch (fieldKey) {
-            case "org":
-                ListShowParameter showParameter4 = (ListShowParameter) e.getFormShowParameter();
-                // 去除部门
-                QFilter qFilter4 = new QFilter("orgpattern.number", "in", COMPANY_LIST2);
-                showParameter4.getListFilterParameter().setFilter(qFilter4);
-                break;
-            default:
-                break;
-        }
+//        switch (fieldKey) {
+//            case "org":
+//                ListShowParameter showParameter4 = (ListShowParameter) e.getFormShowParameter();
+//                // 去除部门
+//                QFilter qFilter4 = new QFilter("orgpattern.number", "in", COMPANY_LIST2);
+//                showParameter4.getListFilterParameter().setFilter(qFilter4);
+//                break;
+//            default:
+//                break;
+//        }
     }
 
 
@@ -176,6 +176,7 @@ public class YearCrApplyPlanFormPlugin extends AbstractBillPlugIn implements Bef
         super.beforeItemClick(evt);
         String key = evt.getItemKey();
         //监听分录刷新招聘申请按钮
+        //携带年度招聘申请的数据到本单
         if (key.equals("nckd_advconbaritemap1")) {
             DynamicObjectCollection entryentity = this.getModel().getDataEntity(true).getDynamicObjectCollection("entryentity");
             // 获取年度
@@ -201,7 +202,7 @@ public class YearCrApplyPlanFormPlugin extends AbstractBillPlugIn implements Bef
                 summary.or("entryentity.nckd_casreplanid", QCP.equals, pkValue);
             }
             qFilter.and(summary);
-            // 年度招聘计划数据
+            // 年度招聘申请数据
             DynamicObject[] loads = BusinessDataServiceHelper.load("nckd_yearapply", "id,org,org.id,billstatus,entryentity,entryentity.nckd_recruitorg," +
                     "entryentity.nckd_recruitpost,entryentity.nckd_recruitnum,entryentity.nckd_majortype,entryentity.nckd_qualifications," +
                     "entryentity.nckd_payrange,entryentity.nckd_employcategory,entryentity.nckd_recruittype,nckd_year,entryentity.nckd_summary," +
@@ -212,7 +213,7 @@ public class YearCrApplyPlanFormPlugin extends AbstractBillPlugIn implements Bef
             }
             ORM orm = ORM.create();
             for (DynamicObject dynamicObject : loads) {
-                // 获取招聘计划的下级分录
+                // 获取招聘申请的下级分录
                 DynamicObjectCollection entryentity1 = dynamicObject.getDynamicObjectCollection("entryentity");
                 if (ObjectUtils.isEmpty(entryentity1)) {
                     continue;

+ 140 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtam/explugin/TpApplyTimeCalculateExtPluginEx.java

@@ -0,0 +1,140 @@
+package nckd.jimin.jyyy.hr.wtc.wtam.explugin;
+
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.sdk.wtc.wtam.business.applytime.TpApplyTimeCalculateEvent;
+import kd.sdk.wtc.wtam.business.applytime.TpApplyTimeCalculateExtPlugin;
+import kd.sdk.wtc.wtbs.common.dto.shift.ShiftDetailDto;
+import kd.sdk.wtc.wtbs.common.dto.shift.ShiftDto;
+import kd.wtc.wtbs.business.util.ShiftParseUtil;
+import kd.wtc.wtbs.common.model.evaluation.ShiftDetail;
+import kd.wtc.wtbs.common.model.shift.RefDateType;
+import kd.wtc.wtbs.common.util.WTCDateUtils;
+import kd.wtc.wtbs.common.util.third.util.DateUtils;
+import kd.wtc.wtbs.common.util.third.util.StringUtils;
+import kd.wtc.wtes.business.model.shift.OutWorkType;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Description      :出差申请时长计算扩展,处理跨天逻辑,去除休息时长
+ * @author Tyx
+ * @date  2025/5/16
+ * 标识 nckd_casrecrapply
+ */
+
+
+public class TpApplyTimeCalculateExtPluginEx implements TpApplyTimeCalculateExtPlugin {
+    private static Log log = LogFactory.getLog(TpApplyTimeCalculateExtPluginEx.class);
+    @Override
+    public void onCalculateApplyTime(TpApplyTimeCalculateEvent tpApplyTimeCalculateEvent) {
+        log.info("开始执行出差单申请时长计算扩展:");
+        //时长类型,非时分的情况下直接返回
+        String unit = tpApplyTimeCalculateEvent.getEntryDy().getString("unit");
+        if(!"B".equals(unit))
+            return;
+        Date startDate = tpApplyTimeCalculateEvent.getEntryDy().getDate("startdate");       //分录出差开始时间
+        LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();   //分录出差开始时间
+        Date endDate = tpApplyTimeCalculateEvent.getEntryDy().getDate("enddate");         //分录出差结束时间
+        LocalDate endLocalDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();       //分录出差结束时间
+        //String startDateStr = sdf.format(startDate);
+        //String endDateStr = sdf.format(endDate);
+        //排班信息 key:日期,value:shiftDto
+        Map shiftMap = tpApplyTimeCalculateEvent.getShiftMap();
+
+        //默认班次时长
+        ShiftDto defaultShift = (ShiftDto) shiftMap.get(startLocalDate);
+        BigDecimal defaultDayHours = defaultShift.getShiftMiddleRuleDto().getAlldayhour();
+        BigDecimal DECIMAL_ONE_HOUR_SECONDS =  new BigDecimal(3600L);
+        BigDecimal defaultDayHoursLong = defaultDayHours.multiply(DECIMAL_ONE_HOUR_SECONDS);
+        log.info("默认班次时长:{},默认班次时长(秒):{}", defaultDayHours, defaultDayHoursLong);
+        //相差天数
+        long days = ChronoUnit.DAYS.between(startLocalDate, endLocalDate);
+
+        //计算时长
+        BigDecimal valHours = BigDecimal.ZERO;
+        BigDecimal sumHours = BigDecimal.ZERO;
+        BigDecimal sumDays = BigDecimal.ZERO;
+        for (int i = 0 ; i <= days; i++) {
+            // i == 0时,为出差开始这天,单独处理时长; i == days为出差结束这天,单独处理时长
+            if(i == 0 || i == days) {
+                Instant instant = null;
+                if(i == 0) {
+                    instant = startLocalDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
+                }
+                else if (i == days) {
+                    instant = endLocalDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
+                }
+                ShiftDto shiftDto = (ShiftDto) shiftMap.get(startLocalDate);
+                BigDecimal calHours = dealDayApplyTime(startDate, endDate, shiftDto, Date.from(instant));
+                valHours = valHours.add(calHours);
+                log.info("{} : 计算时长为 : {}", Date.from(instant), valHours);
+            }
+            // i <> 0 && i <> days时,为中间段日期,默认按班次时长计算
+            if(i != 0 && i != days) {
+                valHours = valHours.add(defaultDayHoursLong);
+            }
+        }
+        // 转换成小时
+        sumHours = valHours.divide(DECIMAL_ONE_HOUR_SECONDS, 6, RoundingMode.HALF_UP);
+        // 转换成天数
+        sumDays = sumHours.divide(defaultDayHours, 6, RoundingMode.HALF_UP);
+        // 回写结果
+        tpApplyTimeCalculateEvent.getResult().setValHour(sumHours);
+        tpApplyTimeCalculateEvent.getResult().setValDay(sumDays);
+    }
+
+    /**
+     * 根据班次计算每天的出差时长
+     * @param startDate  出差开始时间
+     * @param endDate    出差结束时间
+     * @param shiftDto   班次信息
+     * @param rosterDate 班次时间
+     */
+    public BigDecimal dealDayApplyTime (Date startDate, Date endDate, ShiftDto shiftDto, Date rosterDate) {
+        List<ShiftDetailDto> shiftList = shiftDto.getShiftDetailDtoList();
+        BigDecimal sumHours = BigDecimal.ZERO;
+        for(ShiftDetailDto detailDto : shiftList) {
+            if(detailDto.getOutWorkType().equals(OutWorkType.BREAK.code))
+                continue;
+            Date prevEndDateTime = getRosterStartDate(detailDto, rosterDate);
+            Date detailEndDate = getRosterDetailEndDate(detailDto, rosterDate);
+            long shiftDetailInsectionTimes = WTCDateUtils.getDateInsectionTimes(startDate, endDate, prevEndDateTime, detailEndDate);
+            sumHours = sumHours.add(BigDecimal.valueOf(shiftDetailInsectionTimes));
+        }
+        return sumHours;
+    }
+
+    public static Date getRosterStartDate(ShiftDetailDto detail, Date rosterDate) {
+        Date date = rosterDate;
+        String refStartDay = detail.getRefStartDay();
+        if (StringUtils.equals(RefDateType.NEXTDAY.code, refStartDay)) {
+            date = DateUtils.addDays(rosterDate, 1);
+        }
+
+        int shiftStartDate = detail.getShiftStartDate();
+        date = WTCDateUtils.getDate(date, shiftStartDate);
+        return date;
+    }
+
+    public static Date getRosterDetailEndDate(ShiftDetailDto shiftDetail, Date rosterDate) {
+        Date rosterShiftEndDate = WTCDateUtils.getDate(rosterDate, shiftDetail.getShiftEndDate());
+        if (StringUtils.equals(shiftDetail.getRefEndDay(), RefDateType.NEXTDAY.code)) {
+            rosterShiftEndDate = DateUtils.addDays(rosterShiftEndDate, 1);
+        }
+
+        return rosterShiftEndDate;
+    }
+
+
+}

+ 139 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtam/explugin/TvlBillTimeBucketSplitExtPluginEx.java

@@ -0,0 +1,139 @@
+package nckd.jimin.jyyy.hr.wtc.wtam.explugin;
+
+import com.google.common.collect.Lists;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.util.Set;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.sdk.wtc.wtes.business.tie.init.bill.TvlBillTimeBucketSplitEvent;
+import kd.sdk.wtc.wtes.business.tie.init.bill.TvlBillTimeBucketSplitExtPlugin;
+import kd.sdk.wtc.wtes.business.tie.model.roster.RosterExt;
+import kd.sdk.wtc.wtes.business.tie.model.roster.ShiftSpecExt;
+import kd.sdk.wtc.wtes.business.tie.model.roster.ShiftTableSingleExt;
+import kd.sdk.wtc.wtes.business.tie.model.timebucket.AttBillTimeBucketExt;
+import org.apache.curator.shaded.com.google.common.collect.Sets;
+/**
+ * Description      :出差核算时长计算扩展,处理跨天逻辑,去除休息时长
+ * 反编译标品 kd.sdk.wtc.wtes.business.tie.init.bill.TvlBillTimeBucketSplitExtPluginDemo
+ * @author Tyx
+ * @date  2025/5/16
+ * 标识 nckd_casrecrapply
+ */
+
+public class TvlBillTimeBucketSplitExtPluginEx implements TvlBillTimeBucketSplitExtPlugin {
+
+    private static Log log = LogFactory.getLog(TvlBillTimeBucketSplitExtPluginEx.class);
+
+    @Override
+    public void splitVaBillTimeBucket(TvlBillTimeBucketSplitEvent tvlBillTimeBucketSplitEvent) {
+        log.info("出差核算时长二开开始");
+        Set<AttBillTimeBucketExt> timeBucketExtSetRes = Sets.newHashSet();
+        for (AttBillTimeBucketExt billTimeBucketExt : tvlBillTimeBucketSplitEvent.getAttBillTimeBucketExtList()) {
+            DynamicObject entryRowDy = billTimeBucketExt.getEntryRowDy();
+            ShiftTableSingleExt shiftTableSingleExt = tvlBillTimeBucketSplitEvent.getShiftTableExt().shiftTableSingle(billTimeBucketExt.getAttPersonId());
+            if (entryRowDy != null && "3".equals(entryRowDy.getString("startmethod"))) {
+                LocalDateTime startDate = billTimeBucketExt.getStartTime();
+                LocalDateTime endDate = billTimeBucketExt.getEndTime();
+                // 2025-05-20 Tyx 如果endDate > shiftendDate 则取shiftendDate
+                LocalDate shiftstartDate = tvlBillTimeBucketSplitEvent.getStartDate();
+                LocalDate shiftendDate = tvlBillTimeBucketSplitEvent.getEndDate();
+                Set<AttBillTimeBucketExt> timeBucketExtSet = Sets.newHashSet();
+                while (shiftstartDate.compareTo((ChronoLocalDate)shiftendDate) <= 0) {
+                    LocalDateTime shiftStart = this.getshiftStartDateTime(shiftstartDate, shiftTableSingleExt);
+                    LocalDateTime shiftEnd = this.getshiftEndDateTime(shiftstartDate, shiftTableSingleExt);
+                    LocalDateTime shiftStartNext = this.getshiftStartDateTime(shiftstartDate.plusDays(1L), shiftTableSingleExt);
+                    LocalDateTime start = (startDate.compareTo((ChronoLocalDateTime<?>)shiftStart) > 0) ? startDate : shiftStart;
+                    LocalDateTime end = (endDate.compareTo((ChronoLocalDateTime<?>)shiftStartNext) < 0) ? endDate : shiftEnd;
+                    if (start.compareTo((ChronoLocalDateTime<?>)end) < 0) {
+                        timeBucketExtSet.add(billTimeBucketExt.newInstanceResetTime(billTimeBucketExt, shiftstartDate, start, end));
+                    }
+                    shiftstartDate = shiftstartDate.plusDays(1L);
+                }
+                if (timeBucketExtSet.isEmpty()) {
+                    timeBucketExtSet.add(billTimeBucketExt);
+                }
+                timeBucketExtSetRes.addAll(timeBucketExtSet);
+            }
+        }
+        tvlBillTimeBucketSplitEvent.setAttBillTimeBucketExtList(Lists.newArrayList((Iterable)timeBucketExtSetRes));
+    }
+
+    private LocalDateTime getshiftStartDateTime(final LocalDate localDate, final ShiftTableSingleExt shiftTableSingleExt) {
+        final RosterExt rosterExt = shiftTableSingleExt.getRoster(localDate);
+        final ShiftSpecExt shiftSpecExt = rosterExt.getShiftSpec();
+        if (shiftSpecExt.isOffNonPlan()) {
+            return this.getshiftStartDateTimeNoPlan(localDate, shiftTableSingleExt);
+        }
+        return this.getshiftStartDateTimeHasPlan(localDate, shiftTableSingleExt);
+    }
+
+    private LocalDateTime getshiftStartDateTimeNoPlan(final LocalDate localDate, final ShiftTableSingleExt shiftTableSingleExt) {
+        final RosterExt rosterExt = shiftTableSingleExt.getRoster(localDate);
+        final ShiftSpecExt shiftSpecExt = rosterExt.getShiftSpec();
+        LocalDateTime shiftStart = localDate.atStartOfDay();
+        if (shiftSpecExt.isOffNonPlan()) {
+            final RosterExt preRosterExt = shiftTableSingleExt.getRoster(localDate.plusDays(-1L));
+            final ShiftSpecExt preShiftSpecExt = preRosterExt.getShiftSpec();
+            if (!preShiftSpecExt.isOffNonPlan()) {
+                final LocalDateTime preShiftEndDateTime = this.getshiftEndDateTime(localDate.plusDays(-1L), shiftTableSingleExt);
+                shiftStart = ((shiftStart.compareTo((ChronoLocalDateTime<?>)preShiftEndDateTime) > 0) ? shiftStart : preShiftEndDateTime);
+            }
+        }
+        return shiftStart;
+    }
+
+    private LocalDateTime getshiftStartDateTimeHasPlan(final LocalDate localDate, final ShiftTableSingleExt shiftTableSingleExt) {
+        final RosterExt rosterExt = shiftTableSingleExt.getRoster(localDate);
+        final ShiftSpecExt shiftSpecExt = rosterExt.getShiftSpec();
+        LocalDateTime shiftStart = localDate.atStartOfDay();
+        if (shiftSpecExt.isOffNonPlan()) {
+            return shiftStart;
+        }
+        if (RefDateType.TODAY.code.equals(shiftSpecExt.getFirstRefDateType())) {
+            shiftStart = LocalDateTime.of(localDate, shiftSpecExt.getEarliestShiftTime());
+        }
+        else if (RefDateType.NEXTDAY.code.equals(shiftSpecExt.getFirstRefDateType())) {
+            shiftStart = LocalDateTime.of(localDate.plusDays(1L), shiftSpecExt.getEarliestShiftTime());
+        }
+        else if (RefDateType.LASTDAY.code.equals(shiftSpecExt.getFirstRefDateType())) {
+            shiftStart = LocalDateTime.of(localDate.plusDays(-1L), shiftSpecExt.getEarliestShiftTime());
+        }
+        return shiftStart;
+    }
+
+    private LocalDateTime getshiftEndDateTime(final LocalDate localDate, final ShiftTableSingleExt shiftTableSingleExt) {
+        final RosterExt rosterExt = shiftTableSingleExt.getRoster(localDate);
+        final ShiftSpecExt shiftSpecExt = rosterExt.getShiftSpec();
+        LocalDateTime shiftEnd = localDate.plusDays(1L).atStartOfDay();
+        if (shiftSpecExt.isOffNonPlan()) {
+            return shiftEnd;
+        }
+        if (RefDateType.TODAY.code.equals(shiftSpecExt.getLastRefDateType())) {
+            shiftEnd = LocalDateTime.of(localDate, shiftSpecExt.getLastShiftTime());
+        }
+        else if (RefDateType.NEXTDAY.code.equals(shiftSpecExt.getLastRefDateType())) {
+            shiftEnd = LocalDateTime.of(localDate.plusDays(1L), shiftSpecExt.getLastShiftTime());
+        }
+        else if (RefDateType.LASTDAY.code.equals(shiftSpecExt.getLastRefDateType())) {
+            shiftEnd = LocalDateTime.of(localDate.plusDays(-1L), shiftSpecExt.getLastShiftTime());
+        }
+        return shiftEnd;
+    }
+
+    enum RefDateType
+    {
+        TODAY("D"),
+        NEXTDAY("C"),
+        LASTDAY("Q");
+
+        public final String code;
+
+        private RefDateType(final String code) {
+            this.code = code;
+        }
+    }
+}