|
@@ -1,5 +1,7 @@
|
|
|
using System;
|
|
|
+using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
+using Avalonia.Media;
|
|
|
using Comal.Classes;
|
|
|
using InABox.Avalonia;
|
|
|
using InABox.Core;
|
|
@@ -8,49 +10,198 @@ namespace PRS.Avalonia;
|
|
|
|
|
|
public class InOutModel : CoreRepository<InOutModel, InOutShell, Employee>
|
|
|
{
|
|
|
- private Tuple<Guid, TimeSpan, TimeSpan>[] _statuses;
|
|
|
+ private Dictionary<Guid, TimeSheet[]> _timeSheets = [];
|
|
|
+ private StandardLeave[] _standardLeave = [];
|
|
|
+ private Dictionary<Guid, LeaveRequest[]> _leaveRequests = [];
|
|
|
+ private Dictionary<Guid, Activity> _activities = [];
|
|
|
+ private Dictionary<Guid, EmployeeRosterItem[]> _rosters = [];
|
|
|
|
|
|
public InOutModel(IModelHost host, Func<Filter<Employee>> filter, Func<string>? filename = null) : base(host, filter, filename)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
- protected override void Initialize()
|
|
|
+ public class InOutStatus(string status, Color colour, bool clockedOn)
|
|
|
{
|
|
|
- base.Initialize();
|
|
|
- _statuses = new Tuple<Guid, TimeSpan, TimeSpan>[] { };
|
|
|
- }
|
|
|
+ public string Status { get; set; } = status;
|
|
|
|
|
|
- public bool IsClockedOn(Guid id)
|
|
|
- {
|
|
|
- return _statuses.Any(x => x.Item1.Equals(id) && x.Item3.Equals(TimeSpan.Zero));
|
|
|
- }
|
|
|
+ public Color Colour { get; set; } = colour;
|
|
|
|
|
|
- public TimeSpan StartTime(Guid id)
|
|
|
- {
|
|
|
- return _statuses.FirstOrDefault(x => x.Item1.Equals(id))?.Item2 ?? TimeSpan.Zero;
|
|
|
+ public bool ClockedOn { get; set; } = clockedOn;
|
|
|
}
|
|
|
|
|
|
- public TimeSpan FinishTime(Guid id)
|
|
|
+ public InOutStatus StatusObject(InOutShell shell)
|
|
|
{
|
|
|
- return _statuses.FirstOrDefault(x => x.Item1.Equals(id))?.Item3 ?? TimeSpan.Zero;
|
|
|
+ var time = DateTime.Now.TimeOfDay;
|
|
|
+
|
|
|
+ TimeSheet? closedTimeSheet = null;
|
|
|
+ if(_timeSheets.TryGetValue(shell.ID, out var sheets))
|
|
|
+ {
|
|
|
+ var openTimeSheet = sheets.FirstOrDefault(x => x.Finish == TimeSpan.Zero || x.Finish > time);
|
|
|
+ if(openTimeSheet is not null)
|
|
|
+ {
|
|
|
+ if(_activities.TryGetValue(openTimeSheet.ActivityLink.ID, out var activity))
|
|
|
+ {
|
|
|
+ return new(openTimeSheet.Address, Color.TryParse(activity.Color, out var colour) ? colour : Colors.LightGreen, true);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new(openTimeSheet.Address, Colors.LightGreen, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ closedTimeSheet = sheets?.MaxBy(x => x.Start);
|
|
|
+ // Check leave and rosters before giving a result.
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(_standardLeave.Length > 0)
|
|
|
+ {
|
|
|
+ var leave = _standardLeave.FirstOrDefault(x =>
|
|
|
+ (x.From < DateTime.Today || x.FromTime <= time) &&
|
|
|
+ (x.To > DateTime.Today || x.ToTime >= time));
|
|
|
+ if(leave is not null)
|
|
|
+ {
|
|
|
+ var activity = _activities.GetValueOrDefault(leave.LeaveType.ID);
|
|
|
+ return new(
|
|
|
+ activity?.Description ?? "Leave",
|
|
|
+ activity is not null && Color.TryParse(activity.Color, out var colour) ? colour : Colors.Gainsboro,
|
|
|
+ false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(_leaveRequests.TryGetValue(shell.ID, out var leaveRequests))
|
|
|
+ {
|
|
|
+ var leave = leaveRequests.FirstOrDefault(x =>
|
|
|
+ (x.From < DateTime.Today || x.FromTime <= time) &&
|
|
|
+ (x.To > DateTime.Today || x.ToTime >= time));
|
|
|
+ if(leave is not null)
|
|
|
+ {
|
|
|
+ var activity = _activities.GetValueOrDefault(leave.LeaveType.ID);
|
|
|
+ return new(
|
|
|
+ activity?.Description ?? "Leave",
|
|
|
+ activity is not null && Color.TryParse(activity.Color, out var colour) ? colour : Colors.Gainsboro,
|
|
|
+ false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(_rosters.TryGetValue(shell.ID, out var rosters))
|
|
|
+ {
|
|
|
+ var roster = RosterUtils.GetRoster(rosters, shell.RosterStart, DateTime.Today);
|
|
|
+ if(roster is null)
|
|
|
+ {
|
|
|
+ if (closedTimeSheet is not null)
|
|
|
+ {
|
|
|
+ return new("Finished", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new("Not rostered on", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var block = RosterUtils.GetBlocks(roster, DateTime.Today, TimeSpan.MinValue, TimeSpan.MaxValue)
|
|
|
+ .FirstOrDefault(x => x.Start <= time && time <= x.Finish);
|
|
|
+ if(block is null)
|
|
|
+ {
|
|
|
+ if (closedTimeSheet is not null)
|
|
|
+ {
|
|
|
+ return new("Finished", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new("Not rostered on", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(closedTimeSheet is not null)
|
|
|
+ {
|
|
|
+ if(closedTimeSheet.Finish >= block.Start)
|
|
|
+ {
|
|
|
+ return new("Finished early", Colors.LightSalmon, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new("Overdue", Colors.LightSalmon, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new("Overdue", Colors.LightSalmon, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (closedTimeSheet is not null)
|
|
|
+ {
|
|
|
+ return new("Finished", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return new("Not rostered on", Colors.Gainsboro, false);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
protected override void BeforeLoad(MultiQuery query)
|
|
|
{
|
|
|
base.BeforeLoad(query);
|
|
|
- query.Add<TimeSheet>(
|
|
|
+ query.Add(
|
|
|
new Filter<TimeSheet>(x => x.Date).IsEqualTo(DateTime.Today),
|
|
|
new Columns<TimeSheet>(ColumnTypeFlags.None).Add(x => x.EmployeeLink.ID)
|
|
|
.Add(x => x.Start)
|
|
|
.Add(x => x.Finish)
|
|
|
+ .Add(x => x.Address)
|
|
|
+ .Add(x => x.ActivityLink.ID)
|
|
|
+ );
|
|
|
+ query.Add(
|
|
|
+ new Filter<StandardLeave>(x => x.From).IsLessThanOrEqualTo(DateTime.Today)
|
|
|
+ .And(x => x.To).IsGreaterThanOrEqualTo(DateTime.Today),
|
|
|
+ Columns.None<StandardLeave>().Add(x => x.From)
|
|
|
+ .Add(x => x.FromTime)
|
|
|
+ .Add(x => x.To)
|
|
|
+ .Add(x => x.ToTime)
|
|
|
+ .Add(x => x.LeaveType.ID)
|
|
|
+ );
|
|
|
+
|
|
|
+ query.Add(
|
|
|
+ new Filter<LeaveRequest>(x => x.From).IsLessThanOrEqualTo(DateTime.Today)
|
|
|
+ .And(x => x.To).IsGreaterThanOrEqualTo(DateTime.Today)
|
|
|
+ .And(x => x.Status).IsNotEqualTo(LeaveRequestStatus.Rejected),
|
|
|
+ Columns.None<LeaveRequest>().Add(x => x.EmployeeLink.ID)
|
|
|
+ .Add(x => x.From)
|
|
|
+ .Add(x => x.FromTime)
|
|
|
+ .Add(x => x.To)
|
|
|
+ .Add(x => x.ToTime)
|
|
|
+ .Add(x => x.LeaveType.ID)
|
|
|
);
|
|
|
+ query.Add(
|
|
|
+ new Filter<EmployeeRosterItem>(x => x.Employee.ID).InQuery(EffectiveFilter(), x => x.ID),
|
|
|
+ sort: new SortOrder<EmployeeRosterItem>(x => x.Day));
|
|
|
+ query.Add(
|
|
|
+ columns: Columns.None<Activity>()
|
|
|
+ .Add(x => x.ID)
|
|
|
+ .Add(x => x.Color)
|
|
|
+ .Add(x => x.Description));
|
|
|
}
|
|
|
|
|
|
protected override void AfterLoad(MultiQuery query)
|
|
|
{
|
|
|
base.AfterLoad(query);
|
|
|
- _statuses = query.Get<TimeSheet>()
|
|
|
- .ToTuples<TimeSheet, Guid, TimeSpan, TimeSpan>(x => x.EmployeeLink.ID, x => x.Start, x => x.Finish)
|
|
|
- .ToArray();
|
|
|
+ _timeSheets = query.Get<TimeSheet>()
|
|
|
+ .ToObjects<TimeSheet>()
|
|
|
+ .GroupBy(x => x.EmployeeLink.ID)
|
|
|
+ .ToDictionary(x => x.Key, x => x.ToArray());
|
|
|
+ _standardLeave = query.Get<StandardLeave>()
|
|
|
+ .ToObjects<StandardLeave>().ToArray();
|
|
|
+ _leaveRequests = query.Get<LeaveRequest>()
|
|
|
+ .ToObjects<LeaveRequest>()
|
|
|
+ .GroupBy(x => x.EmployeeLink.ID)
|
|
|
+ .ToDictionary(x => x.Key, x => x.ToArray());
|
|
|
+ _rosters = query.Get<EmployeeRosterItem>()
|
|
|
+ .ToObjects<EmployeeRosterItem>()
|
|
|
+ .GroupBy(x => x.Employee.ID)
|
|
|
+ .ToDictionary(x => x.Key, x => x.ToArray());
|
|
|
+ _activities = query.Get<Activity>()
|
|
|
+ .ToObjects<Activity>().ToDictionary(x => x.ID);
|
|
|
}
|
|
|
}
|