|
|
@@ -3,6 +3,7 @@ using System.Linq;
|
|
|
using Comal.Classes;
|
|
|
using InABox.Core;
|
|
|
using System;
|
|
|
+using ExCSS;
|
|
|
|
|
|
namespace Comal.Stores
|
|
|
{
|
|
|
@@ -61,6 +62,15 @@ namespace Comal.Stores
|
|
|
|
|
|
private void CheckAssignmentCosts(Assignment entity)
|
|
|
{
|
|
|
+ if(!entity.Actual.IsChanged()
|
|
|
+ && !entity.Booked.IsChanged()
|
|
|
+ && !entity.HasOriginalValue(x => x.EmployeeLink.ID)
|
|
|
+ && !entity.HasOriginalValue(x => x.Date))
|
|
|
+ {
|
|
|
+ // No change we care about.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
var changedEmployee = entity.TryGetOriginalValue(x => x.EmployeeLink.ID, out var employeeID);
|
|
|
if (!changedEmployee)
|
|
|
{
|
|
|
@@ -87,8 +97,9 @@ namespace Comal.Stores
|
|
|
var assignments = Provider.Query(
|
|
|
Filter<Assignment>.Where(x => x.Date).IsEqualTo(date)
|
|
|
.And(x => x.EmployeeLink.ID).IsEqualTo(employeeID),
|
|
|
- Columns.None<Assignment>()
|
|
|
+ Columns.Required<Assignment>()
|
|
|
.Add(x => x.ID)
|
|
|
+ .Add(x => x.Cost)
|
|
|
.Add(x => x.Actual.Start)
|
|
|
.Add(x => x.Actual.Finish)
|
|
|
.Add(x => x.Actual.Duration)
|
|
|
@@ -96,6 +107,7 @@ namespace Comal.Stores
|
|
|
.Add(x => x.Booked.Finish)
|
|
|
.Add(x => x.Booked.Duration))
|
|
|
.ToArray<Assignment>();
|
|
|
+
|
|
|
var employee = Provider.Query(
|
|
|
Filter<Employee>.Where(x => x.ID).IsEqualTo(employeeID),
|
|
|
Columns.None<Employee>()
|
|
|
@@ -110,6 +122,7 @@ namespace Comal.Stores
|
|
|
.Add(x => x.Overtime.ID),
|
|
|
new SortOrder<EmployeeRosterItem>(x => x.Day))
|
|
|
.ToArray<EmployeeRosterItem>();
|
|
|
+
|
|
|
var overtimeID = RosterUtils.GetRoster(rosterItems, employee.RosterStart, date)?.Overtime.ID ?? Guid.Empty;
|
|
|
if(overtimeID == Guid.Empty) return;
|
|
|
|
|
|
@@ -118,21 +131,57 @@ namespace Comal.Stores
|
|
|
Columns.None<OvertimeInterval>()
|
|
|
.Add(x => x.Interval)
|
|
|
.Add(x => x.IntervalType)
|
|
|
- .Add(x => x.Multiplier),
|
|
|
+ .Add(x => x.Multiplier)
|
|
|
+ .Add(x => x.IsPaid),
|
|
|
new SortOrder<OvertimeInterval>(x => x.Sequence))
|
|
|
.ToArray<OvertimeInterval>();
|
|
|
|
|
|
+ // We need to sort the assignments, because both the chopping algorithm and the overtime algorithm requires it.
|
|
|
assignments.SortBy(x => x.EffectiveStartTime());
|
|
|
- var totalHours = new Dictionary<Assignment, double>();
|
|
|
- OvertimeUtils.EvaluateOvertime(assignments, overtime, x => x.EffectiveFinishTime() - x.EffectiveStartTime(), (assignment, interval, duration) =>
|
|
|
+
|
|
|
+ // Do assignment choppage.
|
|
|
+ var lastEnd = TimeSpan.Zero;
|
|
|
+ var durations = assignments.ToArray(assignment =>
|
|
|
+ {
|
|
|
+ var start = assignment.EffectiveStartTime();
|
|
|
+ var finish = assignment.EffectiveFinishTime();
|
|
|
+ // We know this check is enough to check overlap, since the assignments are in increasing order of their start time.
|
|
|
+ if (lastEnd <= start)
|
|
|
+ {
|
|
|
+ // This assignment does not overlap any previous assignments.
|
|
|
+ lastEnd = assignment.EffectiveFinishTime();
|
|
|
+ return finish - start;
|
|
|
+ }
|
|
|
+ else if(finish >= lastEnd)
|
|
|
+ {
|
|
|
+ // This assignment ends after the assignment that we are overlapping.
|
|
|
+ var duration = finish - lastEnd;
|
|
|
+ lastEnd = finish;
|
|
|
+ return duration;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // This assignment is entirely contained within the assignment being overlapped.
|
|
|
+ return TimeSpan.Zero;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ var totalHours = new double[assignments.Length];
|
|
|
+ OvertimeUtils.EvaluateOvertime(Enumerable.Range(0, assignments.Length), overtime, i => durations[i], (i, interval, duration) =>
|
|
|
{
|
|
|
- totalHours[assignment] = totalHours.GetValueOrAdd(assignment)
|
|
|
- + duration.TotalHours * (interval?.Multiplier ?? 1);
|
|
|
+ if(interval is not null)
|
|
|
+ {
|
|
|
+ var multiplier = interval.IsPaid ? interval.Multiplier : 0;
|
|
|
+ }
|
|
|
+ if (interval?.IsPaid != false)
|
|
|
+ {
|
|
|
+ totalHours[i] += duration.TotalHours * (interval?.Multiplier ?? 1);
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
- foreach(var assignment in assignments)
|
|
|
+ foreach(var (assignment, hours) in assignments.Zip(totalHours))
|
|
|
{
|
|
|
- assignment.Cost = totalHours.GetValueOrDefault(assignment) * employee.HourlyRate;
|
|
|
+ assignment.Cost = hours * employee.HourlyRate;
|
|
|
}
|
|
|
|
|
|
Provider.Save(assignments.Where(x => x.IsChanged()));
|