OvertimeUtils.cs 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace Comal.Classes
  5. {
  6. public static class OvertimeUtils
  7. {
  8. public delegate void EvaluateOvertimeDelegate<TBlock>(TBlock block, OvertimeInterval? interval, TimeSpan duration);
  9. /// <summary>
  10. /// Evaluate the overtime intervals given, using <paramref name="blocks"/> as the list of blocks that have been worked for this day.
  11. /// <paramref name="durationSelector"/> is used to get the duration of each <typeparamref name="TBlock"/>, and
  12. /// <paramref name="evaluateBlock"/> is called for every distinct block that needs evaluating. So if a given <typeparamref name="TBlock"/>
  13. /// overlaps with multiple overtime intervals, <paramref name="evaluateBlock"/> will be called for each interval, with the length of time overlapped
  14. /// with that interval provided.
  15. /// </summary>
  16. public static void EvaluateOvertime<TBlock>(
  17. IEnumerable<TBlock> blocks,
  18. OvertimeInterval[] overtimeIntervals,
  19. Func<TBlock, TimeSpan> durationSelector,
  20. EvaluateOvertimeDelegate<TBlock> evaluateBlock
  21. )
  22. {
  23. var curOvertimeIdx = 0;
  24. OvertimeInterval? GetOvertimeInterval() => curOvertimeIdx < overtimeIntervals.Length ? overtimeIntervals[curOvertimeIdx] : null;
  25. var curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero;
  26. foreach (var block in blocks)
  27. {
  28. var duration = durationSelector(block);
  29. while (duration > TimeSpan.Zero)
  30. {
  31. var interval = GetOvertimeInterval();
  32. if (interval != null)
  33. {
  34. switch (interval.IntervalType)
  35. {
  36. case OvertimeIntervalType.Interval:
  37. if (duration >= curInterval)
  38. {
  39. // In this case, the block is more than the rest of
  40. // the current interval, so we use up all the remaining interval
  41. // time, and then move to the next interval.
  42. evaluateBlock(block, interval, curInterval);
  43. duration -= curInterval;
  44. ++curOvertimeIdx;
  45. curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero;
  46. }
  47. else
  48. {
  49. evaluateBlock(block, interval, duration);
  50. // Otherwise, we use up the entire block, and decrease the interval by the duration remaining.
  51. curInterval -= duration;
  52. duration = TimeSpan.Zero;
  53. }
  54. break;
  55. case OvertimeIntervalType.RemainingTime:
  56. // In this case, the interval is unchanged.
  57. evaluateBlock(block, interval, duration);
  58. duration = TimeSpan.Zero;
  59. break;
  60. default:
  61. throw new NotImplementedException($"Not implemented Overtime interval type {interval.IntervalType}");
  62. }
  63. }
  64. else
  65. {
  66. // If there is no overtime interval, then we use up the rest of the time on
  67. // the block with a blank PayrollID. Theoretically, this shouldn't happen,
  68. // since the "RemainingTime" interval is required.
  69. evaluateBlock(block, null, duration);
  70. duration = TimeSpan.Zero;
  71. }
  72. }
  73. }
  74. }
  75. }
  76. }