| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- using InABox.Core;
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace Comal.Classes
- {
- public static class OvertimeUtils
- {
- public delegate void EvaluateOvertimeDelegate<TBlock>(TBlock block, OvertimeInterval? interval, TimeSpan duration);
- /// <summary>
- /// Evaluate the overtime intervals given, using <paramref name="blocks"/> as the list of blocks that have been worked for this day.
- /// <paramref name="durationSelector"/> is used to get the duration of each <typeparamref name="TBlock"/>, and
- /// <paramref name="evaluateBlock"/> is called for every distinct block that needs evaluating. So if a given <typeparamref name="TBlock"/>
- /// overlaps with multiple overtime intervals, <paramref name="evaluateBlock"/> will be called for each interval, with the length of time overlapped
- /// with that interval provided.
- /// </summary>
- /// <remarks>
- /// The interval passed to <paramref name="evaluateBlock"/> <i>should</i> never be <see langword="null"/>; it is only <see langword="null"/> if all the overtime
- /// is used up and there is no interval of type <see cref="OvertimeIntervalType.RemainingTime"/>; this is not allowed, so it shouldn't happen. Nevertheless,
- /// the case should be accounted for in case of bad data.
- /// </remarks>
- public static void EvaluateOvertime<TBlock>(
- IEnumerable<TBlock> blocks,
- OvertimeInterval[] overtimeIntervals,
- Func<TBlock, TimeSpan> durationSelector,
- EvaluateOvertimeDelegate<TBlock> evaluateBlock
- )
- {
- var curOvertimeIdx = 0;
- OvertimeInterval? GetOvertimeInterval() => curOvertimeIdx < overtimeIntervals.Length ? overtimeIntervals[curOvertimeIdx] : null;
- var curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero;
- foreach (var block in blocks)
- {
- var duration = durationSelector(block);
- while (duration > TimeSpan.Zero)
- {
- var interval = GetOvertimeInterval();
- if (interval != null)
- {
- switch (interval.IntervalType)
- {
- case OvertimeIntervalType.Interval:
- if (duration >= curInterval)
- {
- // In this case, the block is more than the rest of
- // the current interval, so we use up all the remaining interval
- // time, and then move to the next interval.
- evaluateBlock(block, interval, curInterval);
- duration -= curInterval;
- ++curOvertimeIdx;
- curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero;
- }
- else
- {
- evaluateBlock(block, interval, duration);
- // Otherwise, we use up the entire block, and decrease the interval by the duration remaining.
- curInterval -= duration;
- duration = TimeSpan.Zero;
- }
- break;
- case OvertimeIntervalType.RemainingTime:
- // In this case, the interval is unchanged.
- evaluateBlock(block, interval, duration);
- duration = TimeSpan.Zero;
- break;
- default:
- throw new NotImplementedException($"Not implemented Overtime interval type {interval.IntervalType}");
- }
- }
- else
- {
- // If there is no overtime interval, then we use up the rest of the time on
- // the block with a blank PayrollID. Theoretically, this shouldn't happen,
- // since the "RemainingTime" interval is required.
- Logger.Send(LogType.Error, "", $"Error with overtime intervals; no interval of type RemainingTime");
- evaluateBlock(block, null, duration);
- duration = TimeSpan.Zero;
- }
- }
- }
- }
- }
- }
|