Kaynağa Gözat

PRS MOBILE - Refactoring of Assignment changes, moved some files to appropriate areas

Nick-PRSDigital@bitbucket.org 2 yıl önce
ebeveyn
işleme
7f388a7728

+ 1 - 1
prs.mobile/comal.timesheets.Android/Resources/Resource.designer.cs

@@ -14,7 +14,7 @@ namespace comal.timesheets.Droid
 {
 {
 	
 	
 	
 	
-	[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.1.0.5")]
+	[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.0.0.73")]
 	public partial class Resource
 	public partial class Resource
 	{
 	{
 		
 		

+ 0 - 0
prs.mobile/comal.timesheets/CacheLoader.cs → prs.mobile/comal.timesheets/Data Classes/CacheLoader.cs


+ 0 - 0
prs.mobile/comal.timesheets/DigitalFormsHelper.cs → prs.mobile/comal.timesheets/DigitalForms/DataModels/DigitalFormsHelper.cs


+ 90 - 219
prs.mobile/comal.timesheets/Main/MainPage.xaml.cs

@@ -39,11 +39,9 @@ namespace comal.timesheets
         bool bSharedDeviceFirstLoad = true;
         bool bSharedDeviceFirstLoad = true;
         bool bSharedDevice = false;
         bool bSharedDevice = false;
         int NumberOfNotfications = 0;
         int NumberOfNotfications = 0;
-        private JobShell _job = new JobShell();
         string deviceName = "";
         string deviceName = "";
         string matchedDeviceName = "";
         string matchedDeviceName = "";
         int notCount = 1;
         int notCount = 1;
-        Assignment currentAssignment = null;
         #endregion
         #endregion
 
 
         #region Constructor
         #region Constructor
@@ -52,50 +50,24 @@ namespace comal.timesheets
             InitializeComponent();
             InitializeComponent();
             try
             try
             {
             {
-                App.GPS.OnLocationFound += LocationFound;
-                App.GPS.OnLocationError += LocationError;
-                App.Bluetooth.OnScanFinished += ScanFinished;
-                App.Data.DataChanged += DataChanged;
-                App.Data.DataRefreshed += DataRefreshed;
+                InitEvents();
 
 
-                GlobalVariables.EmpID = GlobalVariables.GetEmployeeID();
-                GlobalVariables.EmpName = GlobalVariables.GetEmployeeName();
-
-                App.Data.Employee.ID = GlobalVariables.EmpID;
-                App.Data.Employee.Name = GlobalVariables.EmpName;
-
-                MessagingCenter.Subscribe<App>(this, App.MessageOnResume,
-                (o) =>
-                {
-                    if (!App.GPS.RecentlyLocated)
-                        App.GPS.GetLocation();
-                    RefreshScreen();
-                }
-                );
-                _timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
-                _employee = App.Data.Employee;
-                _jobs = App.Data.Jobs;
-
-                deviceName = MobileUtils.GetDeviceID();
+                InitData();
 
 
                 LoadCacheLists();
                 LoadCacheLists();
 
 
                 InitToolEntryList();
                 InitToolEntryList();
 
 
-                Timer t = new Timer(RecentlyAskedToUpdateTimer, null, 600000, 600000); //user is reminded to update when opening screen after timer of 10 minutes
-                updateCounter = 1; //user is forced to update after 3rd reminder
-
-                Timer t1 = new Timer(RecentlyUpdatedTilesTimer, null, 30000, 30000);
-                //bluetooth data is allowed to upload once every minute, notifications refreshing is piggybacked to this too
+                InitTimers();
 
 
                 InitNotificationCentre();
                 InitNotificationCentre();
-                firstLoad = false;
-                //if (GlobalVariables.EmpID == Guid.Parse("40f6ccd9-5272-4b1a-99bf-de7542205aac"))
-                //RunCustomScript();
-                NotifyChanges();
-
+                                
                 CheckCurrentAssignment();
                 CheckCurrentAssignment();
 
 
+                NotifyChanges();
+
+                //if (GlobalVariables.EmpID == Guid.Parse("40f6ccd9-5272-4b1a-99bf-de7542205aac"))
+                //RunCustomScript();
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {
@@ -103,57 +75,82 @@ namespace comal.timesheets
             }
             }
             NavigationPage.SetHasBackButton(this, false);
             NavigationPage.SetHasBackButton(this, false);
         }
         }
+        private void RunCustomScript()
+        {
+
+        }
+
+
+        private void InitEvents()
+        {
+            App.GPS.OnLocationFound += LocationFound;
+            App.GPS.OnLocationError += LocationError;
+            App.Bluetooth.OnScanFinished += ScanFinished;
+            App.Data.DataChanged += DataChanged;
+            App.Data.DataRefreshed += DataRefreshed;
+
+            MessagingCenter.Subscribe<App>(this, App.MessageOnResume,
+            (o) =>
+            {
+                if (!App.GPS.RecentlyLocated)
+                     App.GPS.GetLocation();
+                    RefreshScreen();
+            }
+            );
+        }
+
+        private void InitData()
+        {
+            GlobalVariables.EmpID = GlobalVariables.GetEmployeeID();
+            GlobalVariables.EmpName = GlobalVariables.GetEmployeeName();
+
+            App.Data.Employee.ID = GlobalVariables.EmpID;
+            App.Data.Employee.Name = GlobalVariables.EmpName;
+
+            _timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
+            _employee = App.Data.Employee;
+            _jobs = App.Data.Jobs;
+
+            deviceName = MobileUtils.GetDeviceID();
+
+            firstLoad = false;
+        }
+
+        private void InitTimers()
+        {
+            Timer t = new Timer(RecentlyAskedToUpdateTimer, null, 600000, 600000); //user is reminded to update when opening screen after timer of 10 minutes
+            updateCounter = 1; //user is forced to update after 3rd reminder
+
+            Timer t1 = new Timer(RecentlyUpdatedTilesTimer, null, 30000, 30000);
+            //bluetooth data is allowed to upload once every minute, notifications refreshing is piggybacked to this too
+        }
 
 
         private void CheckCurrentAssignment()
         private void CheckCurrentAssignment()
         {
         {
             Task.Run(() =>
             Task.Run(() =>
             {
             {
-                Thread.Sleep(5000);
-                StartAssignmentTimer();
-                CoreTable table = new Client<Assignment>().Query(
-                    new Filter<Assignment>(x => x.EmployeeLink.ID).IsEqualTo(GlobalVariables.EmpID)
-                    .And(x => x.Date).IsEqualTo(DateTime.Today.Date)
-                    .And(x => x.Actual.Finish).IsEqualTo(null),
-                    null,
-                    new SortOrder<Assignment>(x => x.Actual.Start, SortDirection.Ascending));
-                if (!table.Rows.Any())
-                {
-                    _job.OnJobIDChanged += OnJobIDChanged;
+                var assgn = MainPageUtils.CheckCurrentAssignment();
+
+                if (assgn == null)
                     return;
                     return;
-                }
 
 
-                var assgn = table.Rows.LastOrDefault().ToObject<Assignment>();
                 Device.BeginInvokeOnMainThread(async () =>
                 Device.BeginInvokeOnMainThread(async () =>
                 {
                 {
-                    string chosenOption = await DisplayActionSheet("Continue with current unfinished assignment?", "Cancel", null, "Yes", "No");
-                    switch(chosenOption) 
+                    string chosenOption = await DisplayActionSheet("Continue with current unfinished assignment? (" + assgn.Title + " )", "Cancel", null, "Yes", "No");
+                    switch (chosenOption)
                     {
                     {
                         case "Yes":
                         case "Yes":
-                            currentAssignment = assgn;
-                            SaveCurrentAssignment("PRS Mobile main screen - saving assignment on re-login to App");
-                            if (currentAssignment.JobLink.ID != Guid.Empty)
-                            {
-                                _job.ID = currentAssignment.JobLink.ID;
-                                var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(_job.ID)).Rows.FirstOrDefault().ToObject<Job>();
-                                _job.JobNumber = job.JobNumber;
-                                _job.Name = job.Name;
-                                RefreshJobBtn();
-                                _job.OnJobIDChanged += OnJobIDChanged;
-                            }            
+                            MainPageUtils.UseCurrentAssignment(assgn);
+                            RefreshJobBtn();
                             break;
                             break;
                         default:
                         default:
-                            _job.OnJobIDChanged += OnJobIDChanged;
+                            MainPageUtils.Job.OnJobIDChanged += MainPageUtils.OnJobIDChanged;
                             break;
                             break;
                     };
                     };
-                });               
+                });
             });
             });
         }
         }
 
 
-        private void RunCustomScript()
-        {
-
-        }
-
         private void NotifyChanges()
         private void NotifyChanges()
         {
         {
             Task.Run(() =>
             Task.Run(() =>
@@ -368,10 +365,10 @@ namespace comal.timesheets
 
 
         private void RefreshJobBtn()
         private void RefreshJobBtn()
         {
         {
-            if (_job.ID == Guid.Empty)
+            if (MainPageUtils.Job.ID == Guid.Empty)
                 jobBtn.Text = "No Job Selected";
                 jobBtn.Text = "No Job Selected";
-            else if(!string.IsNullOrWhiteSpace(_job.JobNumber))
-                jobBtn.Text = "Job: " + _job.JobNumber;
+            else if (!string.IsNullOrWhiteSpace(MainPageUtils.Job.JobNumber))
+                jobBtn.Text = "Job: " + MainPageUtils.Job.JobNumber;
         }
         }
 
 
         private void RefreshJobFromTimeSheet(CoreRow timesheet)
         private void RefreshJobFromTimeSheet(CoreRow timesheet)
@@ -380,14 +377,14 @@ namespace comal.timesheets
             if (!jobid.Equals(Guid.Empty))
             if (!jobid.Equals(Guid.Empty))
             {
             {
                 jobBtn.Text = String.Format("{0}: {1}", timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber), timesheet.Get<TimeSheet, String>(x => x.JobLink.Name));
                 jobBtn.Text = String.Format("{0}: {1}", timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber), timesheet.Get<TimeSheet, String>(x => x.JobLink.Name));
-                _job.ID = timesheet == null ? Guid.Empty : timesheet.Get<TimeSheet, Guid>(x => x.JobLink.ID);
-                _job.JobNumber = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber);
-                _job.Name = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.Name);
+                MainPageUtils.Job.ID = timesheet == null ? Guid.Empty : timesheet.Get<TimeSheet, Guid>(x => x.JobLink.ID);
+                MainPageUtils.Job.JobNumber = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber);
+                MainPageUtils.Job.Name = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.Name);
             }
             }
             else
             else
             {
             {
                 jobBtn.Text = "No Job Selected";
                 jobBtn.Text = "No Job Selected";
-                _job = new JobShell();
+                MainPageUtils.Job = new JobShell();
             }
             }
         }
         }
 
 
@@ -617,7 +614,7 @@ namespace comal.timesheets
         {
         {
             bRecentlyUpdatedTiles = false;
             bRecentlyUpdatedTiles = false;
             App.Data.Refresh(true);
             App.Data.Refresh(true);
-            SearchForNewNotifications();
+            MainPageUtils.SearchForNewNotifications();
         }
         }
 
 
         private void LocationFound(LocationServices sender)
         private void LocationFound(LocationServices sender)
@@ -696,125 +693,20 @@ namespace comal.timesheets
         #endregion
         #endregion
 
 
         #region Utilities
         #region Utilities
-        private void OnJobIDChanged(Guid jobid)
-        {
-            if (currentAssignment == null)
-                CreateNewAssignment(jobid);
-
-            else
-            {
-                SaveCurrentAssignment("PRS Mobile main screen - saving assignment on job change", true);
-                CreateNewAssignment(jobid);
-            }
-        }
-
-        private void StartAssignmentTimer()
-        {
-            Timer t = new Timer(AssignmentTimerCallback, null, 300000, 300000);
-        }
-
-        private void AssignmentTimerCallback(object state)
-        {
-            if (currentAssignment != null)
-                SaveCurrentAssignment("PRS MObile main screen - Saving assignment on 5 minute timer");
-        }
-
-        private void CreateNewAssignment(Guid jobid)
-        {
-            currentAssignment = new Assignment { Date = DateTime.Now.Date };
-            currentAssignment.EmployeeLink.ID = GlobalVariables.EmpID;
-            currentAssignment.JobLink.ID = jobid;
-            currentAssignment.Actual.Start = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
-            currentAssignment.Booked.Start = currentAssignment.Actual.Start;
-            currentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay.Add(new TimeSpan(0, 5, 0));
-            var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(jobid)).Rows.FirstOrDefault().ToObject<Job>();
-            currentAssignment.Title = "Clocking on to job " + job.Name + " (" + job.JobNumber + ") on PRS Mobile";
-            new Client<Assignment>().Save(currentAssignment, "Changed job on mobile - creating new assignment");
-        }
-
-        private void SaveCurrentAssignment(string auditnote, bool complete = false)
-        {
-            if (!complete)
-                currentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
-            else
-                currentAssignment.Actual.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
-
-            new Client<Assignment>().Save(currentAssignment, auditnote);
-        }
-
-        static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
-        {
-            int f = 0;
-            double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
-            if (m >= 0.5)
-                f = 1;
-            return new DateTime(((dt.Ticks / d.Ticks) + f) * d.Ticks);
-        }
-
         private void InitNotificationCentre()
         private void InitNotificationCentre()
         {
         {
-            try
+            LocalNotificationCenter.Current.NotificationActionTapped += (Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) =>
             {
             {
-                LocalNotificationCenter.Current.NotificationActionTapped += (Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) =>
+                if (MainPageUtils.DetermineCorrectPage(e) != null)
                 {
                 {
-                    string data = e.Request.ReturningData;
-                    int index = data.IndexOf("$");
-                    Guid ID = Guid.Parse(data.Remove(index));
-                    string type = data.Substring(index + 1);
-                    if (type == "Comal.Classes.Kanban")
-                    {
-                        Device.BeginInvokeOnMainThread(() =>
-                        {
-                            AddEditTask page = new AddEditTask(ID);
-                            Navigation.PushAsync(page);
-                        });
-                    }
-                    else if (type == "Comal.Classes.Delivery")
+                    Device.BeginInvokeOnMainThread(() =>
                     {
                     {
-                        Device.BeginInvokeOnMainThread(() =>
-                        {
-                            DeliveryDetails page = new DeliveryDetails(ID);
-                            Navigation.PushAsync(page);
-                        });
-                    }
-                };
-            }
-            catch { }
-        }
+                        Navigation.PushAsync(MainPageUtils.DetermineCorrectPage(e));
+                    });
+                }
+            };
 
 
-        private async void SearchForNewNotifications()
-        {
-            //notifications poll reliably in the background for Anroid, unreliable for iOS due to difficulty with cross-platform plugins for notifications
-            try
-            {
-                await Task.Run(() =>
-                {
-                    if (ClientFactory.UserGuid != Guid.Empty)
-                    {
-                        CoreTable table = new Client<Notification>().Query
-                        (new Filter<Notification>(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid).And(X => X.Closed).IsEqualTo(DateTime.MinValue),
-                            new Columns<Notification>(
-                                   x => x.ID, //0
-                                   x => x.Sender.Name, //1   
-                                   x => x.Title, //2
-                                   x => x.Created, //3
-                                   x => x.Description, //4
-                                   x => x.EntityType, //5
-                                   x => x.EntityID //6
-                                   )
-                        );
-                        if (NumberOfNotfications == table.Rows.Count()) //no new notifications or none present at all
-                            return;
-                        else //new notifications or previous notifications have now been dismissed
-                        {
-                            NumberOfNotfications = table.Rows.Count();
-                            RefreshOnNotificationsChange();
-                            CheckNotificationsPushed(table);
-                        }
-                    }
-                });
-            }
-            catch { }
+            MainPageUtils.OnMainPageNotificationsChanged += RefreshOnNotificationsChange;
         }
         }
 
 
         private void RefreshOnNotificationsChange()
         private void RefreshOnNotificationsChange()
@@ -923,27 +815,6 @@ namespace comal.timesheets
                     notification.Image = img;
                     notification.Image = img;
 
 
                     await LocalNotificationCenter.Current.Show(notification);
                     await LocalNotificationCenter.Current.Show(notification);
-
-                    //if (Device.RuntimePlatform.Equals(Device.iOS))
-                    //{
-
-                    //    var content = new UNMutableNotificationContent();
-                    //    content.Title = "New PRS Notification: ";
-                    //    content.Subtitle = shell.Title;
-                    //    content.Body = "";
-                    //    content.Badge = 1;
-                    //    var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(1, false);
-                    //    var requestID = "request";
-                    //    var request = UNNotificationRequest.FromIdentifier(requestID, content, trigger);
-
-                    //    UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
-                    //    {
-                    //        if (err != null)
-                    //        {
-                    //            Do something with error...
-                    //        }
-                    //    });
-                    //}
                 }
                 }
                 Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
                 Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
             }
             }
@@ -1036,10 +907,10 @@ namespace comal.timesheets
                 midnightTimerOn = false;
                 midnightTimerOn = false;
                 Timer last60Seconds = new Timer(Last60SecondsTimerCallBack, null, 5000, Timeout.Infinite);
                 Timer last60Seconds = new Timer(Last60SecondsTimerCallBack, null, 5000, Timeout.Infinite);
                 clockedOffInLast5Seconds = true;
                 clockedOffInLast5Seconds = true;
-                if (currentAssignment != null)
+                if (MainPageUtils.CurrentAssignment != null)
                 {
                 {
-                    SaveCurrentAssignment("PRS Mobile - clocking off", true);
-                    currentAssignment = null;
+                    MainPageUtils.SaveCurrentAssignment("PRS Mobile - clocking off", true);
+                    MainPageUtils.CurrentAssignment = null;
                 }
                 }
 
 
             }
             }
@@ -1113,9 +984,9 @@ namespace comal.timesheets
                 JobSelectionPage jobSelectionPage = new JobSelectionPage();
                 JobSelectionPage jobSelectionPage = new JobSelectionPage();
                 jobSelectionPage.OnItemSelected += (() =>
                 jobSelectionPage.OnItemSelected += (() =>
                 {
                 {
-                    _job.ID = jobSelectionPage.Job.ID;
-                    _job.JobNumber = jobSelectionPage.Job.JobNumber;
-                    _job.Name = jobSelectionPage.Job.Name;
+                    MainPageUtils.Job.ID = jobSelectionPage.Job.ID;
+                    MainPageUtils.Job.JobNumber = jobSelectionPage.Job.JobNumber;
+                    MainPageUtils.Job.Name = jobSelectionPage.Job.Name;
                     RefreshScreen();
                     RefreshScreen();
                     JobPage_OnItemSelected(jobSelectionPage.Job);
                     JobPage_OnItemSelected(jobSelectionPage.Job);
                 });
                 });
@@ -1764,7 +1635,7 @@ namespace comal.timesheets
                     };
                     };
                     Site.OnTapped += ((object sender, EventArgs e) =>
                     Site.OnTapped += ((object sender, EventArgs e) =>
                     {
                     {
-                        Site site = new Site(new Job { ID = _job.ID, Name = _job.Name, JobNumber = _job.JobNumber });
+                        Site site = new Site(new Job { ID = MainPageUtils.Job.ID, Name = MainPageUtils.Job.Name, JobNumber = MainPageUtils.Job.JobNumber });
                         Navigation.PushAsync(site);
                         Navigation.PushAsync(site);
                         //if (_job.ID == Guid.Empty)
                         //if (_job.ID == Guid.Empty)
                         //{
                         //{
@@ -1845,7 +1716,7 @@ namespace comal.timesheets
                     toolEntry.Margin = new Thickness(5, 0, 5, 0);
                     toolEntry.Margin = new Thickness(5, 0, 5, 0);
                     flexLayout.Children.Add(toolEntry);
                     flexLayout.Children.Add(toolEntry);
                 }
                 }
-                SearchForNewNotifications();
+                MainPageUtils.SearchForNewNotifications();
                 AddBlanks();
                 AddBlanks();
             });
             });
         }
         }

+ 246 - 0
prs.mobile/comal.timesheets/Main/MainPageUtils.cs

@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using InABox.Core;
+using InABox.Configuration;
+using InABox.Clients;
+using InABox.Mobile;
+using Comal.Classes;
+using XF.Material.Forms.UI.Dialogs;
+using comal.timesheets.CustomControls;
+using comal.timesheets.StoreRequis;
+using PRSSecurity = InABox.Core.Security;
+using Plugin.LocalNotification;
+using comal.timesheets.Tasks;
+using System.IO;
+using static comal.timesheets.CustomControls.JobShell;
+using static Android.Graphics.Paint;
+
+namespace comal.timesheets
+{
+    public delegate void MainPageNotificationsChanged();
+    public static class MainPageUtils
+    {
+        public static Assignment CurrentAssignment = null;
+        public static JobShell Job = new JobShell();
+        public static int NumberOfNotifications = 0;
+        public static event MainPageNotificationsChanged OnMainPageNotificationsChanged;
+        public static Assignment CheckCurrentAssignment()
+        {
+            Thread.Sleep(5000);
+            StartAssignmentTimer();
+
+            CoreTable table = new Client<Assignment>().Query(
+                     new Filter<Assignment>(x => x.EmployeeLink.ID).IsEqualTo(GlobalVariables.EmpID)
+                     .And(x => x.Date).IsEqualTo(DateTime.Today.Date)
+                     .And(x => x.Actual.Finish).IsEqualTo(null),
+                     null,
+                     new SortOrder<Assignment>(x => x.Actual.Start, SortDirection.Ascending));
+            if (!table.Rows.Any())
+            {
+                Job.OnJobIDChanged += OnJobIDChanged;
+                return null;
+            }
+            else
+                return table.Rows.LastOrDefault().ToObject<Assignment>();
+
+        }
+
+        private static void StartAssignmentTimer()
+        {
+            Timer t = new Timer(AssignmentTimerCallback, null, 300000, 300000);
+        }
+
+        private static void AssignmentTimerCallback(object state)
+        {
+            if (CurrentAssignment != null)
+                SaveCurrentAssignment("PRS Mobile main screen - Saving assignment on 5 minute timer");
+        }
+
+        public static void SaveCurrentAssignment(string auditnote, bool complete = false)
+        {
+            if (!complete)
+                CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
+            else
+                CurrentAssignment.Actual.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
+
+            new Client<Assignment>().Save(CurrentAssignment, auditnote);
+        }
+
+        static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
+        {
+            int f = 0;
+            double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
+            if (m >= 0.5)
+                f = 1;
+            return new DateTime(((dt.Ticks / d.Ticks) + f) * d.Ticks);
+        }
+
+        public static void UseCurrentAssignment(Assignment assgn)
+        {
+            CurrentAssignment = assgn;
+            SaveCurrentAssignment("PRS Mobile main screen - saving assignment on re-login to App");
+            if (CurrentAssignment.JobLink.ID != Guid.Empty)
+            {
+                Job.ID = CurrentAssignment.JobLink.ID;
+                var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(Job.ID)).Rows.FirstOrDefault().ToObject<Job>();
+                Job.JobNumber = job.JobNumber;
+                Job.Name = job.Name;
+                Job.OnJobIDChanged += OnJobIDChanged;
+            }
+        }
+
+        public static void OnJobIDChanged(Guid jobid)
+        {
+            if (CurrentAssignment == null)
+                CreateNewAssignment(jobid);
+
+            else
+            {
+                SaveCurrentAssignment("PRS Mobile main screen - saving assignment on job change", true);
+                CreateNewAssignment(jobid);
+            }
+        }
+
+        private static void CreateNewAssignment(Guid jobid)
+        {
+            CurrentAssignment = new Assignment { Date = DateTime.Now.Date };
+            CurrentAssignment.EmployeeLink.ID = GlobalVariables.EmpID;
+            CurrentAssignment.JobLink.ID = jobid;
+            CurrentAssignment.Actual.Start = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
+            CurrentAssignment.Booked.Start = CurrentAssignment.Actual.Start;
+            CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay.Add(new TimeSpan(0, 5, 0));
+            var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(jobid)).Rows.FirstOrDefault().ToObject<Job>();
+            CurrentAssignment.Title = "Clocking on to job " + job.Name + " (" + job.JobNumber + ") on PRS Mobile";
+            new Client<Assignment>().Save(CurrentAssignment, "Changed job on mobile - creating new assignment");
+        }
+
+        #region Notifications
+
+        public static Page DetermineCorrectPage(Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e)
+        {
+            string data = e.Request.ReturningData;
+            int index = data.IndexOf("$");
+            Guid ID = Guid.Parse(data.Remove(index));
+            string type = data.Substring(index + 1);
+            if (type == "Comal.Classes.Kanban")
+                return new AddEditTask(ID);
+            else if (type == "Comal.Classes.Delivery")
+                return new DeliveryDetails(ID);
+            else 
+                return null;
+        }
+
+        public static async void SearchForNewNotifications()
+        {
+            //notifications poll reliably in the background for Anroid, unreliable for iOS due to difficulty with cross-platform plugins for notifications
+            try
+            {
+                await Task.Run(() =>
+                {
+                    if (ClientFactory.UserGuid != Guid.Empty)
+                    {
+                        CoreTable table = new Client<Notification>().Query
+                        (new Filter<Notification>(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid).And(X => X.Closed).IsEqualTo(DateTime.MinValue),
+                            new Columns<Notification>(
+                                   x => x.ID, //0
+                                   x => x.Sender.Name, //1   
+                                   x => x.Title, //2
+                                   x => x.Created, //3
+                                   x => x.Description, //4
+                                   x => x.EntityType, //5
+                                   x => x.EntityID //6
+                                   )
+                        );
+                        if (NumberOfNotifications == table.Rows.Count()) //no new notifications or none present at all
+                            return;
+                        else //new notifications or previous notifications have now been dismissed
+                        {
+                            NumberOfNotifications = table.Rows.Count();
+                            OnMainPageNotificationsChanged?.Invoke();                          
+                            CheckNotificationsPushed(table);
+                        }
+                    }
+                });
+            }
+            catch { }
+        }
+
+        private static void CheckNotificationsPushed(CoreTable table)
+        {
+            try
+            {
+                if (!Application.Current.Properties.ContainsKey("LastPushedNotifications"))
+                {
+                    Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now);
+                }
+                DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString());
+                List<NotificationShell> toNotify = new List<NotificationShell>();
+                foreach (CoreRow row in table.Rows)
+                {
+                    List<object> list = row.Values;
+                    DateTime created = DateTime.Parse(list[3].ToString());
+                    if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released
+                    {
+                        if (created > lastPushed)
+                        {
+                            if (list[1] == null) list[1] = "";
+                            if (list[2] == null) list[2] = "";
+                            if (list[3] == null) list[3] = DateTime.MinValue;
+                            if (list[4] == null) list[4] = "";
+                            if (list[5] == null) list[5] = "";
+                            if (list[6] == null) list[6] = Guid.Empty;
+
+                            NotificationShell shell = new NotificationShell
+                            {
+                                ID = Guid.Parse(list[0].ToString()),
+                                Sender = list[1].ToString(),
+                                Title = list[2].ToString(),
+                                Created = DateTime.Parse(list[3].ToString()),
+                                EntityType = list[5].ToString(),
+                                EntityID = Guid.Parse(list[6].ToString())
+                            };
+                            toNotify.Add(shell); //add notification to be pushed
+                        }
+                    }
+                }
+                if (toNotify.Count > 0)
+                    PushNotificationsAsync(toNotify);
+            }
+            catch { }
+        }
+
+        private static async Task PushNotificationsAsync(List<NotificationShell> shells)
+        {
+            try
+            {
+                int count = 1;
+
+                foreach (NotificationShell shell in shells)
+                {
+                    var notification = new NotificationRequest
+                    {
+                        BadgeNumber = 1,
+                        Description = shell.Title,
+                        Title = "New PRS Notification: ",
+                        ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType,
+                        NotificationId = count,
+                    };
+                    count++;
+                    NotificationImage img = new NotificationImage();
+                    img.ResourceName = "icon16.png";
+                    notification.Image = img;
+
+                    await LocalNotificationCenter.Current.Show(notification);
+                }
+                Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
+            }
+            catch { }
+        }
+
+        #endregion
+    }
+}

+ 3 - 2
prs.mobile/comal.timesheets/comal.timesheets.projitems

@@ -159,9 +159,9 @@
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\DataModels\Lookups\AssignmentKanbanDataModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\DataModels\Lookups\AssignmentKanbanDataModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\DataModels\Lookups\AssignmentLookupDataModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\DataModels\Lookups\AssignmentLookupDataModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\IAssignmentPage.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Assignments\IAssignmentPage.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)CacheLoader.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Data Classes\CacheLoader.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Data Classes\NotifyChanges.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Data Classes\NotifyChanges.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)DigitalFormsHelper.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\DigitalFormsHelper.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\CustomUserControls\DigitalFormsHeader.xaml.cs">
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\CustomUserControls\DigitalFormsHeader.xaml.cs">
       <DependentUpon>DigitalFormsHeader.xaml</DependentUpon>
       <DependentUpon>DigitalFormsHeader.xaml</DependentUpon>
       <SubType>Code</SubType>
       <SubType>Code</SubType>
@@ -216,6 +216,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\IDigitalFormHostModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\IDigitalFormHostModel.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\FormPickerQueryLoader.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\FormPickerQueryLoader.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\IFormPickerQueryLoader.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)DigitalForms\DataModels\IFormPickerQueryLoader.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Main\MainPageUtils.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)MyHR\MyDetailsPage.xaml.cs">
     <Compile Include="$(MSBuildThisFileDirectory)MyHR\MyDetailsPage.xaml.cs">
       <DependentUpon>MyDetailsPage.xaml</DependentUpon>
       <DependentUpon>MyDetailsPage.xaml</DependentUpon>
       <SubType>Code</SubType>
       <SubType>Code</SubType>