private static List <TimesheetEntryFragment> RecursivelySplitSingleTimesheetFragmentToDays(TimesheetEntryFragment fragment, int recursionLevel = 0) { const int RecursionMaxLevel = 30; if (recursionLevel > RecursionMaxLevel) { throw new HandlerException("Cannot split timesheet fragment. It's too long."); } var newFragments = new List <TimesheetEntryFragment>(); DateTime localStart = fragment.Start.ToLocalTime(); DateTime localEnd = fragment.End.ToLocalTime(); if (localStart.Year != localEnd.Year || localStart.Month != localEnd.Month || localStart.Date != localEnd.Date) { TimesheetEntryFragment newFragment = (TimesheetEntryFragment)fragment.Clone(); fragment.End = new DateTime(fragment.Start.Year, fragment.Start.Month, fragment.Start.Day, 23, 59, 59); newFragments.Add(fragment); newFragment.Start = fragment.End + TimeSpan.FromSeconds(1); if (newFragment.Start < newFragment.End) { newFragments.AddRange(RecursivelySplitSingleTimesheetFragmentToDays(newFragment, recursionLevel + 1).ToArray()); } } else { newFragments.Add(fragment); } return(newFragments); }
public static void RemoveUnexportedFragmentTypes(List <TimesheetEntryFragment> fragments, ILogger logger) { for (int i = fragments.Count - 1; i >= 0; i--) { TimesheetEntryFragment fragment = fragments[i]; if (fragment.PayType != null && fragment.PayType.GetValue("exporttoax") != true) { logger.LogDebug("One of the time fragments is of a type not exported to AX.", fragment.PayType.GetValue("name", "")); fragments.Remove(fragment); } } }
public static void RemoveTooShortFragments(List <TimesheetEntryFragment> fragments, ILogger logger, int minTimeFragmentSize) { for (int i = fragments.Count - 1; i > 0; i--) { TimesheetEntryFragment fragment = fragments[i]; TimeSpan duration = fragment.End - fragment.Start; if (duration.TotalSeconds < minTimeFragmentSize) { logger.LogInfo("One of the time fragments is too short and will not be exported.", fragment.Detail[DBQuery.Id], "Duration: " + duration.Seconds, "Minimum time in seconds: " + minTimeFragmentSize); fragments.Remove(fragment); } } }
public object Clone() { var clone = new TimesheetEntryFragment(); clone.IsRootEntry = IsRootEntry; clone.Start = Start; clone.End = End; clone.Detail = Detail; clone.PayType = PayType; clone.ProjectCategoryBase = ProjectCategoryBase; clone.ProjectId = ProjectId; clone.WorkerId = WorkerId; clone.IsTravelTime = IsTravelTime; clone.WorkerProfitCenter = WorkerProfitCenter; return(clone); }
/// <summary> /// Split timesheet entry so that it doesn't span to multiple days /// </summary> private static List <TimesheetEntryFragment> SplitTimesheetEntryWithDetailsToFragments(TimesheetEntryWithDetails entryWithDetails, ILogger logger) { BsonDocument timesheetEntry = entryWithDetails.TimesheetEntry; DateTime startTime = ((DateTime)timesheetEntry["starttimestamp"]).ToLocalTime(); DateTime currentEndTimestamp = ((DateTime)timesheetEntry["endtimestamp"]).ToLocalTime(); DateTime currentStartTimestamp = currentEndTimestamp; var timesheetEntryFragments = new List <TimesheetEntryFragment>(); // Set to true is some details (such as contract pay) mean that regular hours shouldn't be exported. bool regularHoursInvalidated = false; // Handle detail types that don't count towards regular hours foreach (Tuple <BsonDocument, BsonDocument> detailAndPayType in entryWithDetails.EntryDetailsAndPayTypes) { if ((bool)detailAndPayType.Item2.GetValue("invalidatesregularhours", false)) { regularHoursInvalidated = true; } if ((bool)detailAndPayType.Item2.GetValue("countsasregularhours", false)) { continue; } var fragment = new TimesheetEntryFragment(); fragment.Detail = detailAndPayType.Item1; fragment.PayType = detailAndPayType.Item2; fragment.ProjectCategoryBase = entryWithDetails.WorkerCategory; fragment.ProjectId = entryWithDetails.ProjectId; fragment.WorkerId = entryWithDetails.WorkerId; fragment.WorkerProfitCenter = entryWithDetails.WorkerProfitCenter; // Use timesheet entry detail's note if present and base entry's note if not fragment.Note = (string)detailAndPayType.Item1.GetValue("note", string.Empty); if (string.IsNullOrEmpty(fragment.Note)) { fragment.Note = entryWithDetails.Note; } if (!fragment.Detail.Contains("duration")) { continue; } TimeSpan duration = TimeSpan.FromMilliseconds((int)fragment.Detail["duration"]); fragment.Start = startTime; fragment.End = fragment.Start + duration; timesheetEntryFragments.Add(fragment); } // Handle detail types that count towards regular hours foreach (Tuple <BsonDocument, BsonDocument> detailAndPayType in entryWithDetails.EntryDetailsAndPayTypes) { if (!(bool)detailAndPayType.Item2.GetValue("countsasregularhours", false)) { continue; } var fragment = new TimesheetEntryFragment(); fragment.Detail = detailAndPayType.Item1; fragment.PayType = detailAndPayType.Item2; fragment.ProjectCategoryBase = entryWithDetails.WorkerCategory; fragment.ProjectId = entryWithDetails.ProjectId; fragment.WorkerId = entryWithDetails.WorkerId; fragment.WorkerProfitCenter = entryWithDetails.WorkerProfitCenter; // Use timesheet entry detail's note if present and base entry's note if not fragment.Note = (string)detailAndPayType.Item1.GetValue("note", string.Empty); if (string.IsNullOrEmpty(fragment.Note)) { fragment.Note = entryWithDetails.Note; } if (!fragment.Detail.Contains("duration")) { continue; } TimeSpan duration = TimeSpan.FromMilliseconds((int)fragment.Detail["duration"]); fragment.End = currentEndTimestamp; currentStartTimestamp = currentEndTimestamp - duration; bool breakAfter = false; if (currentStartTimestamp < startTime) { logger.LogWarning("Timesheet entry details are longer than entry itself. Skipping extra parts.", timesheetEntry[DBQuery.Id]); currentStartTimestamp = startTime; breakAfter = true; } fragment.Start = currentStartTimestamp; timesheetEntryFragments.Add(fragment); // Continue processing backwards from current start timestamp currentEndTimestamp = currentStartTimestamp; if (breakAfter) { break; } } // Add the "root" version timesheet entry not based on any detail (overtime etc.) if (currentStartTimestamp > startTime && !regularHoursInvalidated) { var fragment = new TimesheetEntryFragment(); fragment.IsRootEntry = true; fragment.IsTravelTime = (bool)timesheetEntry.GetValue("istraveltime", false); fragment.ProjectCategoryBase = entryWithDetails.WorkerCategory; fragment.ProjectId = entryWithDetails.ProjectId; fragment.WorkerId = entryWithDetails.WorkerId; fragment.Note = entryWithDetails.Note; fragment.Start = startTime; fragment.End = currentStartTimestamp; fragment.WorkerProfitCenter = entryWithDetails.WorkerProfitCenter; timesheetEntryFragments.Add(fragment); } return(timesheetEntryFragments); }