Пример #1
0
        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);
        }
Пример #2
0
        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);
                }
            }
        }
Пример #3
0
        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);
                }
            }
        }
Пример #4
0
        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);
        }
Пример #5
0
        /// <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);
        }