public ReservationDurations GetReservationDurations(DataTable dtToolDataClean) { var reservations = GetReservationDateRangeItems(dtToolDataClean); var range = new ReservationDateRange(reservations); var result = new ReservationDurations(range); return(result); }
public void CanGetTransferDruation() { int resourceId = 14021; var dateRange = DateRange.GetDateRange(DateTime.Parse("2018-05-01")); // step 1: get ReservationDateRangeItems var costs = Provider.Data.Cost.FindToolCosts(resourceId, dateRange.EndDate); var toolBillingReservations = Provider.Billing.Tool.SelectReservations(dateRange.StartDate, dateRange.EndDate, resourceId); var reservations = ReservationDateRangeItem.GetReservationDateRangeItems(toolBillingReservations, costs); // step 2: get ReservationDurations var range = new ReservationDateRange(reservations); var durations = new ReservationDurations(range); var item = durations.First(x => x.Reservation.ReservationID == 833138); Assert.AreEqual(TimeSpan.Zero, item.TransferredDuration); //Assert.IsTrue(item.TransferredDuration.TotalMinutes > 0); }
//This handles generation of toolData table since 2011-04-01 public override void ProcessCleanData(DataTable dtToolDataClean) { // must be done before any changes are made to the table, this will be used later to calculate transfer durations _durations = GetReservationDurations(dtToolDataClean); dtToolDataClean.Columns.Add("Uses", typeof(double)); dtToolDataClean.Columns.Add("TransferredDuration", typeof(double)); dtToolDataClean.Columns.Add("IsCancelledBeforeAllowedTime", typeof(bool)); dtToolDataClean.Columns.Add("ChargeBeginDateTime", typeof(DateTime)); dtToolDataClean.Columns.Add("ChargeEndDateTime", typeof(DateTime)); dtToolDataClean.Columns.Add("ChargeDuration", typeof(double)); //handled started separately from unstarted double schedDuration, chargeDuration; DateTime beginDateTime, endDateTime, actualBeginDateTime, actualEndDateTime, chargeBeginDateTime, chargeEndDateTime; int toolDataCleanRowCount = dtToolDataClean.Rows.Count; //2011-12-05 Get ReservationHistory so we can calculate the max charge time //DataTable dtResHistory = GetReservationHistory(); for (int i = 0; i < toolDataCleanRowCount; i++) { DataRow dr = dtToolDataClean.Rows[i]; // ClientID check. dtToolDataClean contains data for all clients even when ClientID > 0 because of transferred duration calculation. if (ClientID == 0 || ClientID == dr.Field <int>("ClientID")) { //only chargeable activities get written to Tooldata if (IsChargeable(dr)) { //this means reservation was started schedDuration = dr.Field <double>("SchedDuration"); beginDateTime = dr.Field <DateTime>("BeginDateTime"); endDateTime = beginDateTime.AddMinutes(schedDuration); //to prevent auto end's modified end time, which is wrong actualBeginDateTime = dr.Field <DateTime>("ActualBeginDateTime"); actualEndDateTime = dr.Field <DateTime>("ActualEndDateTime"); dr["IsCancelledBeforeAllowedTime"] = IsCancelledBeforeAllowedTime(dr); DateTime begDate = (beginDateTime > actualBeginDateTime) ? actualBeginDateTime : beginDateTime; DateTime finDate = (endDateTime > actualEndDateTime) ? endDateTime : actualEndDateTime; chargeBeginDateTime = begDate; chargeEndDateTime = finDate; chargeDuration = (chargeEndDateTime - chargeBeginDateTime).TotalMinutes; dr["ChargeBeginDateTime"] = begDate; dr["ChargeEndDateTime"] = finDate; dr["ChargeDuration"] = chargeDuration; begDate = begDate.Date; //if the finDate is right on the edge, eg. the next day at 00:00am, we don't need to create a new row, because it's not crossing day if (finDate == new DateTime(finDate.Year, finDate.Month, finDate.Day, 0, 0, 0)) { finDate = finDate.AddSeconds(-1); } finDate = finDate.Date; if (begDate != finDate) { //handle case where reservation starts and ends on different days double runningOverTime = 0D; int loopDays = finDate.Subtract(begDate).Days; for (int j = 0; j <= loopDays; j++) { DateTime bDate = begDate.AddDays(j); DateTime fDate = begDate.AddDays(j + 1); DataRow adr = dtToolDataClean.NewRow(); adr.ItemArray = dr.ItemArray; //start by copying everything adr["ChargeBeginDateTime"] = (bDate > chargeBeginDateTime) ? bDate : chargeBeginDateTime; adr["ChargeEndDateTime"] = (fDate < chargeEndDateTime) ? fDate : chargeEndDateTime; adr["ChargeDuration"] = (adr.Field <DateTime>("ChargeEndDateTime") - adr.Field <DateTime>("ChargeBeginDateTime")).TotalMinutes; adr["Uses"] = adr.Field <double>("ChargeDuration") / chargeDuration; adr["MaxReservedDuration"] = GetMaxReservedDuration(adr); ////2011-11-08 we have to see of MaxReservedDuration is greater than the ChargeDuration - preventing cases when user shortened the reservation after the 2 hours allowed time //if (!Convert.ToBoolean(adr["IsCancelledBeforeAllowedTime"])) //{ // if (Convert.ToDouble(adr["MaxReservedDuration"]) > Convert.ToDouble(adr["ChargeDuration"])) // adr["ChargeDuration"] = adr["MaxReservedDuration"]; //} //For backward compatibilty reason, I have to repopulate the correct ActDuration as well adr["ActualBeginDateTime"] = (bDate > actualBeginDateTime) ? bDate : actualBeginDateTime; adr["ActualEndDateTime"] = (fDate < actualEndDateTime) ? fDate : actualEndDateTime; var fixDate = new DateTime(2012, 12, 1); if (Period >= fixDate) { /* * [jg 2013-01-24] * Only do this for periods after 2012-12-01. Before that time the next line of code was missing which caused the 2nd part of a split * reservation to not be written to the database. In other words the part that happened on the first day of the period. Note: this is * only for reservations that cross over two periods. For periods before Dec 2012 we are purposefully doing this wrong so that amounts * billed previously aren't changed retroactively. * * [jg 2020-02-04] * Note that only ChargeBeginDateTime ChargeEndDateTime, ChargeDuration, ActDuration, and SchedDuration are in ToolData. * Other columns like BeginDateTime, ActualBeginDateTime, etc are set here only because they're used in calculations or * checks elsewhere. */ adr["BeginDateTime"] = adr["ActualBeginDateTime"]; //for convenience below when writing new table } // ActDuration will only be for this part of a multi-part reservation because the actuals were modified above adr["ActDuration"] = adr.Field <DateTime>("ActualEndDateTime").Subtract(adr.Field <DateTime>("ActualBeginDateTime")).TotalMinutes; // make sure ActDuration is greater than 0, because if a user finish this reservation on the same day (a cross day reservation originally), // it's possible to have negative ActDuration if (adr.Field <double>("ActDuration") < 0D) { adr["ActDuration"] = 0D; //since user finished on the previous day, today's reservation actual duration is zero } if (adr.Field <DateTime>("ChargeEndDateTime") > endDateTime) { adr["OverTime"] = (adr.Field <DateTime>("ChargeEndDateTime") - endDateTime).TotalMinutes - runningOverTime; runningOverTime += adr.Field <double>("OverTime"); } else { adr["OverTime"] = 0D; } // [2022-03-25 jg] Rounding to two digits now RoundDouble(adr, "ActDuration"); RoundDouble(adr, "ChargeDuration"); RoundDouble(adr, "OverTime"); RoundDouble(adr, "MaxReservedDuration"); dtToolDataClean.Rows.Add(adr); //will add to end //make sure next month's data is not included. if (adr.Field <DateTime>("ChargeBeginDateTime") >= Period.AddMonths(1)) { adr.Delete(); } } dr.Delete(); //remove original, multi-day row } else { dr["Uses"] = 1D; if (!dr.Field <bool>("IsStarted")) { dr["ActDuration"] = dr["SchedDuration"]; } //2011-11-08 we have to see of MaxReservedDuration is greater than the ChargeDuration - preventing cases when user shortened the reservation after the 2 hours allowed time if (!dr.Field <bool>("IsCancelledBeforeAllowedTime")) { //this will return a list of reservation changigin history ordered by ModifiedDateTime DataRow[] drsResHistory = ReservationHistoryTable.Select($"ReservationID = {dr["ReservationID"]}"); //must be at least two rows (becuase I inlcude Insert event as well, when reservation is firstly created) if (drsResHistory.Length > 1) { DataRow drPrevious = null; double maxResTimeAfterTwoHoursLimit = 0.0; double currentResTime; bool flagtemp = false; DateTime beginTimeTemp, endTimeTemp; foreach (DataRow drResHist in drsResHistory) { //the idea here is compare every reservation change and find out the max reservation time if (drResHist.Field <int>("BeforeMinutes") <= 120) { flagtemp = true; beginTimeTemp = drResHist.Field <DateTime>("BeginDateTime"); endTimeTemp = drResHist.Field <DateTime>("EndDateTime"); currentResTime = (endTimeTemp - beginTimeTemp).TotalMinutes; if (currentResTime > maxResTimeAfterTwoHoursLimit) { maxResTimeAfterTwoHoursLimit = currentResTime; } } if (!flagtemp) { //We need to track the last change of reservation right before the 2 hours limit, this is our true chargeTime drPrevious = drResHist; } } if (drPrevious != null) { beginTimeTemp = drPrevious.Field <DateTime>("BeginDateTime"); endTimeTemp = drPrevious.Field <DateTime>("EndDateTime"); currentResTime = (endTimeTemp - beginTimeTemp).TotalMinutes; if (currentResTime > maxResTimeAfterTwoHoursLimit) { maxResTimeAfterTwoHoursLimit = currentResTime; } } //Lastly, make sure we charge the max time - please note that ChargeDuration could still be higher than maxResTimeAfterTwoHoursLimit because of actual tool usage time if (maxResTimeAfterTwoHoursLimit > dr.Field <double>("ChargeDuration")) { dr["ChargeDuration"] = maxResTimeAfterTwoHoursLimit; } } } // [2022-03-25 jg] Rounding to two digits now RoundDouble(dr, "ActDuration"); RoundDouble(dr, "ChargeDuration"); RoundDouble(dr, "OverTime"); RoundDouble(dr, "MaxReservedDuration"); } } else { dr.Delete(); //not chargeable, so remove } } } }