| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- using System.Collections.Generic;
- using System.Linq;
- using Comal.Classes;
- using InABox.Core;
- using System;
- using ExCSS;
- namespace Comal.Stores
- {
- internal class AssignmentStore : BaseStore<Assignment>
- {
- private void CheckActivityForms(Assignment assignment)
- {
- if (!assignment.ActivityLink.HasOriginalValue(x => x.ID))
- {
- return;
- }
- List<AssignmentForm> assignmentForms;
- if(assignment.ID != Guid.Empty)
- {
- assignmentForms = Provider.Query(
- Filter<AssignmentForm>.Where(x => x.Parent.ID).IsEqualTo(assignment.ID),
- Columns.None<AssignmentForm>().Add(x => x.ID, x => x.FormData)
- ).Rows.Select(x => x.ToObject<AssignmentForm>()).ToList();
- }
- else
- {
- assignmentForms = new();
- }
- var toDelete = assignmentForms.Where(x => string.IsNullOrWhiteSpace(x.FormData));
- Provider.Delete(toDelete, UserID);
- var activityForms = Provider.Query(
- Filter<ActivityForm>.Where(x => x.Activity.ID).IsEqualTo(assignment.ActivityLink.ID)
- .And(x => x.Form.AppliesTo).IsEqualTo(nameof(Assignment)));
- var newForms = new List<AssignmentForm>();
- foreach (var row in activityForms.Rows)
- {
- var formID = row.Get<ActivityForm, Guid>(x => x.Form.ID);
- if (!(assignmentForms.Any(x => x.Form.ID == formID) && !toDelete.Any(x => x.Form.ID == formID)))
- {
- var assignmentForm = new AssignmentForm();
- assignmentForm.Form.ID = formID;
- assignmentForm.Parent.ID = assignment.ID;
- newForms.Add(assignmentForm);
- }
- }
- Provider.Save(newForms);
- }
- protected override void AfterSave(Assignment entity)
- {
- base.AfterSave(entity);
- CheckActivityForms(entity);
- CheckAssignmentCosts(entity);
- }
- 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)
- {
- employeeID = entity.EmployeeLink.ID;
- }
- var changedDate = entity.TryGetOriginalValue(x => x.Date, out var date);
- if (!changedDate)
- {
- date = entity.Date;
- }
- if(changedEmployee || changedDate)
- {
- CheckAssignmentCosts(date, employeeID);
- }
- CheckAssignmentCosts(entity.Date, entity.EmployeeLink.ID);
- }
-
- private void CheckAssignmentCosts(DateTime date, Guid employeeID)
- {
- if (employeeID == Guid.Empty) return;
- var assignments = Provider.Query(
- Filter<Assignment>.Where(x => x.Date).IsEqualTo(date)
- .And(x => x.EmployeeLink.ID).IsEqualTo(employeeID),
- 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)
- .Add(x => x.Booked.Start)
- .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>()
- .Add(x => x.RosterStart)
- .Add(x => x.HourlyRate))
- .ToObjects<Employee>().FirstOrDefault();
- if (employee is null) return;
- var rosterItems = Provider.Query(
- Filter<EmployeeRosterItem>.Where(x => x.Employee.ID).IsEqualTo(employeeID),
- Columns.None<EmployeeRosterItem>()
- .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;
- var overtime = Provider.Query(
- Filter<OvertimeInterval>.Where(x => x.Overtime.ID).IsEqualTo(overtimeID),
- Columns.None<OvertimeInterval>()
- .Add(x => x.Interval)
- .Add(x => x.IntervalType)
- .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());
- // 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) =>
- {
- 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, hours) in assignments.Zip(totalHours))
- {
- assignment.Cost = hours * employee.HourlyRate;
- }
- Provider.Save(assignments.Where(x => x.IsChanged()));
- }
- }
- }
|