|
@@ -2,6 +2,7 @@
|
|
|
using System.Drawing;
|
|
|
using System.Linq;
|
|
|
using System.Threading.Tasks;
|
|
|
+using Avalonia.Controls;
|
|
|
using Avalonia.Media;
|
|
|
// using Avalonia.Svg.Skia;
|
|
|
using Comal.Classes;
|
|
@@ -12,7 +13,8 @@ using ExCSS;
|
|
|
using InABox.Avalonia;
|
|
|
using InABox.Avalonia.Components;
|
|
|
using InABox.Avalonia.Platform;
|
|
|
-using InABox.Core;
|
|
|
+using InABox.Core;
|
|
|
+using PRS.Avalonia.Components;
|
|
|
using PRS.Avalonia.Login;
|
|
|
using PRS.Avalonia.Modules.MyHR;
|
|
|
using PRS.Avalonia.Settings;
|
|
@@ -30,14 +32,7 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
[ObservableProperty]
|
|
|
private AvaloniaModuleCollection _modules;
|
|
|
|
|
|
- [ObservableProperty] private TimeSheetModel _timeSheets = new TimeSheetModel(
|
|
|
- DataAccess,
|
|
|
- () => new Filter<TimeSheet>(x=>x.Date).IsEqualTo(DateTime.Today)
|
|
|
- .And(x=>x.Finish).IsEqualTo(TimeSpan.Zero)
|
|
|
- .And(x=>x.EmployeeLink.ID).IsEqualTo(Repositories.Me.ID)
|
|
|
- );
|
|
|
-
|
|
|
- public bool IsClockedOn => TimeSheets.Any();
|
|
|
+ public bool IsClockedOn => Repositories.CurrentTimeSheet != null;
|
|
|
|
|
|
[ObservableProperty]
|
|
|
private GeoPoint?_currentLocation;
|
|
@@ -131,6 +126,7 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
{
|
|
|
await PlatformTools.Permissions.IsPermitted(Permission.Geolocation);
|
|
|
PlatformTools.Geolocation.LocationChanged += LocationChanged;
|
|
|
+ PlatformTools.Geolocation.TimerChanged += TimerChanged;
|
|
|
PlatformTools.Geolocation.Scanning = true;
|
|
|
await base.OnActivated();
|
|
|
}
|
|
@@ -139,20 +135,61 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
{
|
|
|
PlatformTools.Geolocation.Scanning = false;
|
|
|
PlatformTools.Geolocation.LocationChanged -= LocationChanged;
|
|
|
+ PlatformTools.Geolocation.TimerChanged -= TimerChanged;
|
|
|
return base.OnDeactivated();
|
|
|
}
|
|
|
|
|
|
- private void LocationChanged(object? sender, EventArgs e)
|
|
|
+ private void TimerChanged(object sender, GeoLocationTimerArgs e)
|
|
|
+ {
|
|
|
+ GpsFrequency = e.Frequency.TotalMilliseconds;
|
|
|
+ GpsRemaining = e.Remaining.TotalMilliseconds;
|
|
|
+ }
|
|
|
+
|
|
|
+ [ObservableProperty]
|
|
|
+ private double _gpsFrequency;
|
|
|
+
|
|
|
+ [ObservableProperty]
|
|
|
+ private double _gpsRemaining;
|
|
|
+
|
|
|
+ private void LocationChanged(object? sender, GeoLocationChangedArgs e)
|
|
|
+ {
|
|
|
+ DoLocationChanged();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void DoLocationChanged()
|
|
|
{
|
|
|
CurrentLocation = PlatformTools.Geolocation.CurrentLocation;
|
|
|
- if (CurrentLocation != null)
|
|
|
- {
|
|
|
- CurrentJob = Repositories.Jobs.Items.FirstOrDefault(x => x.Geofence?.Contains(CurrentLocation) == true);
|
|
|
- }
|
|
|
+ CurrentJob = CurrentLocation != null
|
|
|
+ ? Repositories.Jobs.Items.FirstOrDefault(x => x.Geofence?.Contains(CurrentLocation) == true)
|
|
|
+ : null;
|
|
|
+ OnPropertyChanged(nameof(ClockOnLocation));
|
|
|
}
|
|
|
|
|
|
[ObservableProperty] private JobShell? _currentJob;
|
|
|
|
|
|
+ public string? ClockOnLocation
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (CurrentLocation == null)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ if (CurrentJob != null)
|
|
|
+ return CurrentJob.DisplayName;
|
|
|
+
|
|
|
+ var info = Repositories.CompanyInformation.Items.FirstOrDefault();
|
|
|
+ if (info?.Geofence?.Coordinates.Any() != true)
|
|
|
+ return null;
|
|
|
+ if (info.Geofence.Contains(CurrentLocation))
|
|
|
+ return info.CompanyName;
|
|
|
+
|
|
|
+ if (Security.IsAllowed<CanBypassGPSClockIn>() || IsClockedOn)
|
|
|
+ return "";
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
protected override async Task<TimeSpan> OnRefresh()
|
|
|
{
|
|
|
if (_clockingOn)
|
|
@@ -172,10 +209,17 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
: "";
|
|
|
|
|
|
}),
|
|
|
+
|
|
|
+ Task.Run(async () =>
|
|
|
+ {
|
|
|
+ await Repositories.RefreshCurrentTimeSheet();
|
|
|
+ }),
|
|
|
+
|
|
|
Task.Run(async () =>
|
|
|
{
|
|
|
- await TimeSheets.RefreshAsync(true);
|
|
|
+ await Repositories.RefreshCurrentAssignmentAsync();
|
|
|
}),
|
|
|
+
|
|
|
Task.Run(async () =>
|
|
|
{
|
|
|
await Repositories.Jobs.RefreshAsync(true);
|
|
@@ -183,6 +227,8 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
};
|
|
|
Task.WaitAll(tasks);
|
|
|
|
|
|
+ DoLocationChanged();
|
|
|
+
|
|
|
var result = await base.OnRefresh();
|
|
|
|
|
|
OnPropertyChanged(nameof(IsClockedOn));
|
|
@@ -195,7 +241,7 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
[RelayCommand]
|
|
|
private async Task ClockOn()
|
|
|
{
|
|
|
- if (!DataReady)
|
|
|
+ if (!DataReady || _clockingOn)
|
|
|
return;
|
|
|
|
|
|
_clockingOn = true;
|
|
@@ -205,19 +251,29 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
|
|
|
if (IsClockedOn)
|
|
|
{
|
|
|
- foreach (var ts in TimeSheets.Items)
|
|
|
- ts.ActualFinish = DateTime.Now.TimeOfDay;
|
|
|
- await TimeSheets.SaveAsync("Clocked off from Mobile App");
|
|
|
- await TimeSheets.RefreshAsync(true);
|
|
|
+ await Repositories.CloseTimeSheetAsync();
|
|
|
+ await Repositories.CloseAssignmentAsync();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var tsShell = TimeSheets.CreateItem();
|
|
|
- tsShell.Date = DateTime.Today;
|
|
|
- tsShell.ActualStart = DateTime.Now.TimeOfDay;
|
|
|
- tsShell.EmployeeID = Repositories.Me.ID;
|
|
|
- TimeSheets.CommitItem(tsShell);
|
|
|
- await TimeSheets.SaveAsync("Clocked on from Mobile App");
|
|
|
+ await Repositories.CreateTimeSheetAsync(tsShell =>
|
|
|
+ {
|
|
|
+ tsShell.JobID = CurrentJob?.ID ?? Guid.Empty;
|
|
|
+ tsShell.JobNumber = CurrentJob?.JobNumber ?? "";
|
|
|
+ tsShell.JobName = CurrentJob?.Name ?? "";
|
|
|
+ });
|
|
|
+
|
|
|
+ if (CurrentJob != null)
|
|
|
+ {
|
|
|
+ await Repositories.CreateAssignmentAsync(assShell =>
|
|
|
+ {
|
|
|
+
|
|
|
+ assShell.JobID = CurrentJob?.ID ?? Guid.Empty;
|
|
|
+ assShell.JobNumber = CurrentJob?.JobNumber ?? "";
|
|
|
+ assShell.JobName = CurrentJob?.Name ?? "";
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
_clockingOn = false;
|
|
@@ -227,4 +283,92 @@ public partial class HomePageViewModel : ViewModelBase
|
|
|
OnPropertyChanged(nameof(IsClockedOn));
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ [RelayCommand]
|
|
|
+ private async Task SelectJob()
|
|
|
+ {
|
|
|
+ var job = (await SelectionViewModel.ExecutePopup<JobShell>(model =>
|
|
|
+ {
|
|
|
+ model.Columns.BeginUpdate()
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<JobShell>
|
|
|
+ {
|
|
|
+ Column = x => x.JobNumber,
|
|
|
+ Caption = "Number",
|
|
|
+ Width = GridLength.Auto
|
|
|
+ })
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<JobShell>
|
|
|
+ {
|
|
|
+ Column = x => x.Name,
|
|
|
+ Caption = "Name",
|
|
|
+ Width = GridLength.Star
|
|
|
+ })
|
|
|
+ .EndUpdate();
|
|
|
+ model.AddFilters(Repositories.Jobs.AvailableFilters.Select(x => x.Name).NotNull());
|
|
|
+ }, args =>
|
|
|
+ {
|
|
|
+ Repositories.Jobs.SelectFilter(args.Filter);
|
|
|
+ return Repositories.Jobs.Refresh(args.Force);
|
|
|
+ }))?.FirstOrDefault();
|
|
|
+ if (job is null)
|
|
|
+ return;
|
|
|
+ await Repositories.CloseAssignmentAsync();
|
|
|
+ await Repositories.CreateAssignmentAsync(ass =>
|
|
|
+ {
|
|
|
+ ass.JobID = job.ID;
|
|
|
+ ass.JobNumber = job.JobNumber;
|
|
|
+ ass.JobName = job.Name;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ [RelayCommand]
|
|
|
+ private async Task SelectTask()
|
|
|
+ {
|
|
|
+ var task = (await SelectionViewModel.ExecutePopup<KanbanShell>(model =>
|
|
|
+ {
|
|
|
+ model.Columns.BeginUpdate()
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<KanbanShell>
|
|
|
+ {
|
|
|
+ Column = x => x.JobNumber,
|
|
|
+ Caption = "Job",
|
|
|
+ Width = GridLength.Auto
|
|
|
+ })
|
|
|
+ .Add(new AvaloniaDataGridIntegerColumn<KanbanShell>
|
|
|
+ {
|
|
|
+ Column = x => x.Number,
|
|
|
+ Caption = "Number",
|
|
|
+ Width = GridLength.Auto,
|
|
|
+ Format = "F0"
|
|
|
+ })
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<KanbanShell>
|
|
|
+ {
|
|
|
+ Column = x => x.Title,
|
|
|
+ Caption = "Title",
|
|
|
+ Width = GridLength.Star
|
|
|
+ })
|
|
|
+ .EndUpdate();
|
|
|
+ model.AddFilters(Repositories.MyTasks.AvailableFilters.Select(x => x.Name).NotNull());
|
|
|
+ }, args =>
|
|
|
+ {
|
|
|
+ Repositories.MyTasks.SelectFilter(args.Filter);
|
|
|
+ return Repositories.MyTasks.Refresh(args.Force);
|
|
|
+ }))?.FirstOrDefault();
|
|
|
+ if (task is null)
|
|
|
+ return;
|
|
|
+ await Repositories.CloseAssignmentAsync();
|
|
|
+ await Repositories.CreateAssignmentAsync(ass =>
|
|
|
+ {
|
|
|
+ ass.JobID = task.JobID;
|
|
|
+ ass.JobNumber = task.JobNumber;
|
|
|
+ ass.JobName = task.JobName;
|
|
|
+ ass.TaskID = task.ID;
|
|
|
+ ass.TaskNumber = task.Number;
|
|
|
+ ass.TaskName = task.Title;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ [RelayCommand]
|
|
|
+ private async Task TimeSheetNotes()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
}
|