MainPage.xaml.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Xamarin.Forms;
  7. using InABox.Core;
  8. using InABox.Clients;
  9. using InABox.Mobile;
  10. using Comal.Classes;
  11. using InABox.Configuration;
  12. using Xamarin.Forms.Xaml;
  13. using XF.Material.Forms.UI.Dialogs;
  14. namespace PRS.Mobile
  15. {
  16. public partial class MainPage : MobilePage
  17. {
  18. #region Fields
  19. bool bUpdatingTimesheet = false;
  20. bool midnightTimerOn = false;
  21. DateTime oneSecondBeforeMidnight = DateTime.Today.AddSeconds(864399);
  22. bool clockedOffInLast5Seconds = false;
  23. bool bRecentlyUpdatedTiles = false;
  24. string matchedDeviceName = "";
  25. int notCount = 1;
  26. private TimeSheet _currenttimesheet = null;
  27. #endregion
  28. #region Constructor
  29. private Task[] _setuptasks;
  30. public MainPage() : base()
  31. {
  32. _setuptasks = new Task[]
  33. {
  34. //Task.Run(() => App.Data.Activities.Refresh(false)),
  35. //Task.Run(() => App.Data.Employees.Refresh(false)),
  36. //Task.Run(() => App.Data.TimeSheets.Refresh(false)),
  37. Task.Run(() => App.Data.GPSTrackers.Refresh(false)),
  38. Task.Run(() => App.Data.BluetoothGates.Refresh(false)),
  39. //Task.Run(() => App.Data.Notifications.Refresh(false)),
  40. //Task.Run(() => App.Data.Kanbans.Refresh(false))
  41. };
  42. MobileLogging.Log("MainPage.Create");
  43. InitializeComponent();
  44. MobileLogging.Log("MainPage.InitNotificationCentre");
  45. InitNotificationCentre();
  46. MobileLogging.Log("MainPage.Subscribe");
  47. MessagingCenter.Subscribe<App>(this, App.MessageOnResume,
  48. (o) =>
  49. {
  50. if (!App.GPS.RecentlyLocated)
  51. App.GPS.GetLocation();
  52. RefreshScreen();
  53. }
  54. );
  55. MobileLogging.Log("MainPage.NotifyChanges");
  56. NotifyChanges();
  57. MobileLogging.Log("MainPage.Create Done");
  58. }
  59. private void NotifyChanges()
  60. {
  61. Task.Run(() =>
  62. {
  63. string changes = NotifyMobileChanges.Notifiy();
  64. if (!string.IsNullOrWhiteSpace(changes))
  65. Device.BeginInvokeOnMainThread(() =>
  66. {
  67. DisplayAlert("Latest changes", changes, "OK");
  68. });
  69. });
  70. }
  71. #endregion
  72. #region OnAppearing and Display
  73. private SelectionPage _selectionpage = null;
  74. protected override void OnAppearing()
  75. {
  76. MobileLogging.Log("MainPage.Appearing");
  77. if (_selectionpage == null)
  78. {
  79. BackButtonEnabled = App.IsSharedDevice;
  80. EnableModules();
  81. StartMonitoringNotifications();
  82. StartPromptingForUpdates();
  83. App.Data.GPSLocationUpdated += OnGPSLocationUpdated;
  84. App.Data.BluetoothScanFinished += OnBluetothScanFinished;
  85. //LoadHRToDos(); to be uncommented when ready for roll out
  86. CheckTimeSheet();
  87. }
  88. App.Data.Notifications.Refresh(false, () => Device.BeginInvokeOnMainThread(() =>
  89. {
  90. var count = App.Data.Notifications.Items.Count(x => x.Closed.IsEmpty());
  91. Notifications.Indicator = count > 0 ? count.ToString() : "";
  92. }));
  93. _selectionpage = null;
  94. Task.WaitAll(_setuptasks);
  95. RefreshScreen();
  96. base.OnAppearing();
  97. }
  98. private IMaterialModalPage _snackbar = null;
  99. protected override void OnDisappearing()
  100. {
  101. base.OnDisappearing();
  102. //App.Data.GPSLocationUpdated -= OnGPSLocationUpdated;
  103. //App.Data.BluetoothScanFinished -= OnBluetothScanFinished;
  104. _canceltasks.Cancel();
  105. }
  106. private void OnBluetothScanFinished(BluetoothEventArgs args)
  107. {
  108. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  109. }
  110. private void OnGPSLocationUpdated(GPSEventArgs args)
  111. {
  112. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  113. }
  114. private void EnableModules()
  115. {
  116. void SetupClockOnButton(bool disable)
  117. {
  118. ClockOnButton.IsVisible = !disable;
  119. jobBtn.IsVisible = !disable;
  120. addNoteBtn.IsVisible = !disable;
  121. taskBtn.IsVisible = !disable;
  122. }
  123. SetupClockOnButton(InABox.Core.Security.IsAllowed<IsJobOnlyEmployee>());
  124. Assignments.IsVisible = InABox.Core.Security.CanView<Assignment>();
  125. Deliveries.IsVisible = InABox.Core.Security.CanView<Delivery>();
  126. Forms.IsVisible = InABox.Core.Security.CanView<DigitalForm>();
  127. Equipment.IsVisible = InABox.Core.Security.CanView<Equipment>();
  128. InOut.IsVisible = InABox.Core.Security.IsAllowed<CanViewInOutBoard>();
  129. Manufacturing.IsVisible = InABox.Core.Security.CanView<ManufacturingPacket>();
  130. Products.IsVisible = InABox.Core.Security.CanView<Product>();
  131. PurchaseOrders.IsVisible = InABox.Core.Security.CanView<PurchaseOrder>();
  132. StoreRequis.IsVisible = InABox.Core.Security.CanView<Requisition>();
  133. MyTasks.IsVisible = InABox.Core.Security.CanView<Kanban>();
  134. Warehousing.IsVisible = InABox.Core.Security.CanView<StockWarehouse>();
  135. }
  136. protected override void UpdateTransportStatus()
  137. {
  138. base.UpdateTransportStatus();
  139. RefreshScreen();
  140. }
  141. private CancellationTokenSource _canceltasks = new CancellationTokenSource();
  142. private int _notificationcount = 0;
  143. private void StartMonitoringNotifications()
  144. {
  145. //notifications are allowed to upload once every 30 seconds
  146. // This will eventually be replaced with websocket pushes
  147. // but we're not ready for that yet :-(
  148. var token = _canceltasks.Token;
  149. Task.Run(
  150. () =>
  151. {
  152. while (!_canceltasks.Token.IsCancellationRequested)
  153. {
  154. App.Data.Notifications.Refresh(true);
  155. if (App.Data.Notifications.Items.Count != _notificationcount)
  156. {
  157. Dispatcher.BeginInvokeOnMainThread(() =>
  158. {
  159. _notificationcount = App.Data.Notifications.Items.Count;
  160. Notifications.Indicator = _notificationcount.ToString();
  161. });
  162. }
  163. Task.Delay(TimeSpan.FromSeconds(30), token)
  164. .Wait(token);
  165. }
  166. },
  167. token
  168. );
  169. }
  170. private void StartPromptingForUpdates()
  171. {
  172. var token = _canceltasks.Token;
  173. Task.Run(
  174. () =>
  175. {
  176. while (!token.IsCancellationRequested)
  177. {
  178. bool isLatest = MobileUtils.AppVersion.IsUsingLatestVersion().Result;
  179. if (!isLatest)
  180. {
  181. string latestVersionNumber = MobileUtils.AppVersion.GetLatestVersionNumber().Result;
  182. string chosenOption = DisplayActionSheet(
  183. $"Version {latestVersionNumber} Available. Update now?",
  184. "You will be reminded again in 10 minutes.", null, "Yes", "No").Result;
  185. if (String.Equals(chosenOption,"Yes"))
  186. Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
  187. }
  188. Task.Delay(TimeSpan.FromMinutes(10), token)
  189. .Wait(token);
  190. }
  191. }
  192. , token
  193. );
  194. }
  195. private void RefreshScreen()
  196. {
  197. bool GateReady = CheckLocation();
  198. var location = GetAddress();
  199. Title = App.Data.Me != null
  200. ? App.Data.Me.Name
  201. : "(Not Logged In)";
  202. ClockOnButton.IsEnabled = App.Data.IsConnected() && GateReady;
  203. ClockOnOffLabel.Text = GateReady ? _currenttimesheet == null ? "CLOCK ON" : "CLOCK OFF" : "PLEASE WAIT";
  204. CurrentLocation.Text = location.ToUpper().Contains("ERROR")
  205. ? "Unknown Address"
  206. : location;
  207. ClockOnButton.BackgroundColor = App.Data.IsConnected() && GateReady
  208. ? _currenttimesheet == null
  209. ? Color.FromHex("#e6e6fa")
  210. : Color.FromHex("#15C7C1")
  211. : Color.Silver;
  212. ClockOnButton.BorderColor = App.Data.IsConnected() && GateReady
  213. ? _currenttimesheet == null
  214. ? Color.FromHex("#96969a")
  215. : Color.FromHex("#059791")
  216. : Color.Gray;
  217. addNoteBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  218. jobBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  219. taskBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  220. jobBtn.Text = ((_currenttimesheet?.JobID ?? Guid.Empty) != Guid.Empty)
  221. ? $"{_currenttimesheet.JobLink.JobNumber}: {_currenttimesheet.JobLink.Name}"
  222. : "Select Job";
  223. Assignments.IsEnabled = App.Data.IsConnected();
  224. Deliveries.IsEnabled = App.Data.IsConnected();
  225. Equipment.IsEnabled = App.Data.IsConnected();
  226. Manufacturing.IsEnabled = App.Data.IsConnected();
  227. Forms.IsEnabled = App.Data.IsConnected();
  228. InOut.IsEnabled = App.Data.IsConnected();
  229. MyHR.IsEnabled = App.Data.IsConnected();
  230. Notifications.IsEnabled = App.Data.IsConnected();
  231. Products.IsEnabled = App.Data.IsConnected();
  232. PurchaseOrders.IsEnabled = App.Data.IsConnected();
  233. Site.IsEnabled = App.Data.IsConnected();
  234. StoreRequis.IsEnabled = App.Data.IsConnected();
  235. MyTasks.IsEnabled = App.Data.IsConnected();
  236. Warehousing.IsEnabled = App.Data.IsConnected();
  237. }
  238. #endregion
  239. #region Clock on/off
  240. private void CheckTimeSheet()
  241. {
  242. if (App.Data.IsConnected())
  243. {
  244. var client = new Client<TimeSheet>();
  245. var filter = new Filter<TimeSheet>(x => x.EmployeeLink.ID).IsEqualTo(App.Data.Me.ID)
  246. .And(x => x.Date).IsEqualTo(DateTime.Today)
  247. .And(x => x.Finish).IsEqualTo(TimeSpan.Zero);
  248. var table = client.Query(filter);
  249. _currenttimesheet = table.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  250. }
  251. var token = _canceltasks.Token;
  252. Task.Run(
  253. () =>
  254. {
  255. while (!token.IsCancellationRequested)
  256. {
  257. if (_currenttimesheet != null)
  258. {
  259. if (_currenttimesheet.Date != DateTime.Today)
  260. {
  261. InABox.Core.Location here = new InABox.Core.Location()
  262. {
  263. Latitude = App.GPS.Latitude,
  264. Longitude = App.GPS.Longitude,
  265. Timestamp = DateTime.Now
  266. };
  267. _currenttimesheet.FinishLocation = here;
  268. _currenttimesheet.Finish = TimeSpan.FromDays(1).Subtract(TimeSpan.FromSeconds(1));
  269. new Client<TimeSheet>().Save(_currenttimesheet,
  270. "Closing Timesheet at Modnight on Mobile Device");
  271. CreateTimeSheet(_currenttimesheet.JobLink.ID, _currenttimesheet.JobLink.JobNumber, _currenttimesheet.JobLink.Name, here, here.Address, "Creating Timeheet at Midnight on Mobile Device");
  272. }
  273. }
  274. Task.Delay(TimeSpan.FromMinutes(1), token).Wait(token);
  275. }
  276. },
  277. token
  278. );
  279. }
  280. private DateTime _debounce = DateTime.MinValue;
  281. async void ClockOnOff_Clicked(object sender, System.EventArgs e)
  282. {
  283. if (_debounce > DateTime.Now.Subtract(TimeSpan.FromMilliseconds(500)))
  284. return;
  285. _debounce = DateTime.MaxValue;
  286. string chosenOption = _currenttimesheet != null
  287. ? await DisplayActionSheet("Clock off?", "Cancel", null, "Continue", "Cancel")
  288. : "Continue";
  289. if (!String.Equals(chosenOption, "Continue"))
  290. {
  291. _debounce = DateTime.Now;
  292. return;
  293. }
  294. try
  295. {
  296. using (await MaterialDialog.Instance.LoadingDialogAsync(message: $"Clocking {(_currenttimesheet == null ? "On" : "Off")}"))
  297. {
  298. InABox.Core.Location here = new InABox.Core.Location()
  299. {
  300. Latitude = App.GPS.Latitude,
  301. Longitude = App.GPS.Longitude,
  302. Timestamp = DateTime.Now
  303. };
  304. if (_currenttimesheet != null)
  305. {
  306. FinishTimeSheet(here);
  307. }
  308. else
  309. {
  310. if (!InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  311. {
  312. var tracker = App.Data.BluetoothGates.FirstOrDefault(x => App.Bluetooth.Devices.Contains(x.DeviceID));
  313. if (tracker != null)
  314. CreateTimeSheet(tracker.JobID, tracker.JobNumber, tracker.JobName, here, App.GPS.Address, "Clocking On");
  315. }
  316. else if ((!App.GPS.Latitude.Equals(0.0F)) && (!App.GPS.Longitude.Equals(0.0F))
  317. || InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>())
  318. {
  319. var job = await ChooseNearbyJob(here);
  320. CreateTimeSheet(job?.ID ?? Guid.Empty, job?.JobNumber ?? "", job?.Name ?? "" , here, App.GPS.Address, "Clocking On");
  321. }
  322. }
  323. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  324. }
  325. }
  326. catch (Exception ex)
  327. {
  328. InABox.Mobile.MobileLogging.Log(ex);
  329. }
  330. _debounce = DateTime.Now;
  331. }
  332. private void FinishTimeSheet(InABox.Core.Location here)
  333. {
  334. if ((_currenttimesheet == null) || (_currenttimesheet.ID == Guid.Empty))
  335. {
  336. _currenttimesheet = null;
  337. return;
  338. }
  339. try
  340. {
  341. if (ZeroLengthTimesheet())
  342. new Client<TimeSheet>().Delete(_currenttimesheet, "Deleted due to zero duration timesheet");
  343. else
  344. {
  345. _currenttimesheet.Finish =
  346. new TimeSpan(DateTime.Now.TimeOfDay.Hours, DateTime.Now.TimeOfDay.Minutes, 0);
  347. _currenttimesheet.FinishLocation = here;
  348. bUpdatingTimesheet = true;
  349. new Client<TimeSheet>().Save(_currenttimesheet, "Clocking Off");
  350. }
  351. _currenttimesheet = null;
  352. }
  353. catch (Exception ex)
  354. {
  355. InABox.Mobile.MobileLogging.Log(ex);
  356. }
  357. }
  358. #endregion
  359. #region Utilities
  360. private void InitNotificationCentre()
  361. {
  362. // LocalNotificationCenter.Current.NotificationActionTapped += (Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) =>
  363. // {
  364. // if (MainPageUtils.DetermineCorrectPage(e) != null)
  365. // {
  366. // Device.BeginInvokeOnMainThread(() =>
  367. // {
  368. // Navigation.PushAsync(MainPageUtils.DetermineCorrectPage(e));
  369. // });
  370. // }
  371. // };
  372. //
  373. // MainPageUtils.OnMainPageNotificationsChanged += RefreshOnNotificationsChange;
  374. }
  375. private void CheckNotificationsPushed(CoreTable table)
  376. {
  377. // try
  378. // {
  379. // if (!Application.Current.Properties.ContainsKey("LastPushedNotifications"))
  380. // {
  381. // Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now);
  382. // }
  383. // DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString());
  384. // List<NotificationShell> toNotify = new List<NotificationShell>();
  385. // foreach (CoreRow row in table.Rows)
  386. // {
  387. // List<object> list = row.Values;
  388. // DateTime created = DateTime.Parse(list[3].ToString());
  389. // if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released
  390. // {
  391. // if (created > lastPushed)
  392. // {
  393. // if (list[1] == null) list[1] = "";
  394. // if (list[2] == null) list[2] = "";
  395. // if (list[3] == null) list[3] = DateTime.MinValue;
  396. // if (list[4] == null) list[4] = "";
  397. // if (list[5] == null) list[5] = "";
  398. // if (list[6] == null) list[6] = Guid.Empty;
  399. //
  400. // NotificationShell shell = new NotificationShell
  401. // {
  402. // ID = Guid.Parse(list[0].ToString()),
  403. // Sender = list[1].ToString(),
  404. // Title = list[2].ToString(),
  405. // Created = DateTime.Parse(list[3].ToString()),
  406. // EntityType = list[5].ToString(),
  407. // EntityID = Guid.Parse(list[6].ToString())
  408. // };
  409. // toNotify.Add(shell); //add notification to be pushed
  410. // }
  411. // }
  412. // }
  413. // if (toNotify.Count > 0)
  414. // PushNotificationsAsync(toNotify);
  415. // }
  416. // catch { }
  417. }
  418. private async Task PushNotificationsAsync(List<NotificationShell> shells)
  419. {
  420. // try
  421. // {
  422. // int count = 1;
  423. //
  424. // foreach (NotificationShell shell in shells)
  425. // {
  426. // var notification = new NotificationRequest
  427. // {
  428. // BadgeNumber = 1,
  429. // Description = shell.Title,
  430. // Title = "New PRS Notification: ",
  431. // ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType,
  432. // NotificationId = count,
  433. // };
  434. // count++;
  435. // NotificationImage img = new NotificationImage();
  436. // img.ResourceName = "icon16.png";
  437. // notification.Image = img;
  438. //
  439. // await LocalNotificationCenter.Current.Show(notification);
  440. // }
  441. // Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
  442. // }
  443. // catch { }
  444. }
  445. private async Task<JobShell> ChooseNearbyJob(InABox.Core.Location here)
  446. {
  447. var nearbyjobs = App.Data.Jobs.Where(x => here.DistanceTo(x.Location, UnitOfLength.Kilometers) < 1.0F)
  448. .ToArray();
  449. if (nearbyjobs.Length > 1)
  450. {
  451. Dictionary<String, JobShell> dict = new Dictionary<string, JobShell>();
  452. foreach (var job in nearbyjobs)
  453. dict[job.DisplayName] = job;
  454. string chosenOption = await DisplayActionSheet("Choose job site", "Cancel", null, dict.Keys.ToArray());
  455. if (string.IsNullOrEmpty(chosenOption) || chosenOption.Equals("Cancel"))
  456. return null;
  457. return dict[chosenOption];
  458. }
  459. return nearbyjobs.FirstOrDefault();
  460. }
  461. void AddNote_Tapped(System.Object sender, System.EventArgs e)
  462. {
  463. if (_currenttimesheet == null)
  464. return;
  465. var notepage = new TimeSheetNotePage(_currenttimesheet);
  466. Navigation.PushAsync(notepage);
  467. }
  468. private void TaskBtn_Tapped(object sender, EventArgs e)
  469. {
  470. _selectionpage = new TaskSelectionPage( (task) =>
  471. {
  472. if (_currenttimesheet != null)
  473. {
  474. // Not sure hwat to do here...
  475. }
  476. }
  477. );
  478. Navigation.PushAsync(_selectionpage);
  479. }
  480. // private async void RequestUserInput(Guid taskID)
  481. // {
  482. // const string addtask = "Change current assignment task";
  483. // const string newassignment = "Start a new assignment with this task";
  484. // string chosenOption = await DisplayActionSheet("Choose an option", "Cancel", null, addtask, newassignment);
  485. // switch (chosenOption)
  486. // {
  487. // case addtask:
  488. // MainPageUtils.ChangeAssignmentTask(taskID);
  489. // break;
  490. // case newassignment:
  491. // MainPageUtils.SaveCurrentAssignment("PRS Mobile main screen - saving assignment on task change", true);
  492. // MainPageUtils.CreateNewAssignment(Guid.Empty, taskID);
  493. // break;
  494. // default:
  495. // break;
  496. // }
  497. // }
  498. private void JobBtn_Tapped(object sender, EventArgs e)
  499. {
  500. _selectionpage = new JobSelectionPage(
  501. (job) =>
  502. {
  503. if (_currenttimesheet != null)
  504. {
  505. if (ZeroLengthTimesheet())
  506. {
  507. _currenttimesheet.JobLink.ID = job?.ID ?? Guid.Empty;
  508. _currenttimesheet.JobLink.JobNumber = job?.JobNumber ?? "";
  509. _currenttimesheet.JobLink.Name = job?.Name ?? "";
  510. new Client<TimeSheet>().Save(_currenttimesheet,"Updated Job Number via mobile device");
  511. }
  512. else
  513. {
  514. var here = new Location()
  515. {
  516. Latitude = App.GPS.Latitude,
  517. Longitude = App.GPS.Longitude,
  518. Address = App.GPS.Address,
  519. Timestamp = App.GPS.TimeStamp
  520. };
  521. FinishTimeSheet(here);
  522. CreateTimeSheet(job?.ID ?? Guid.Empty, job?.JobNumber ?? "", job?.Name ?? "", here, here.Address,
  523. "Switched Jobs via Mobile Device");
  524. }
  525. }
  526. }
  527. );
  528. Navigation.PushAsync(_selectionpage);
  529. }
  530. // private bool CheckTimeSheetAgainstGates(TimeSheet timesheet)
  531. // {
  532. // DateTime now = DateTime.Now;
  533. //
  534. // //var timesheet = CurrentTimeSheet();
  535. //
  536. // //Can't confirm if there is no timesheet
  537. // if (timesheet == null)
  538. // return false;
  539. //
  540. // // Can't confirm if there are no devices
  541. // if (App.Bluetooth.Devices.Length == 0)
  542. // return false;
  543. //
  544. // if (App.Data.Gates == null)
  545. // return false;
  546. //
  547. // long tsTicks = timesheet.Date.Add(timesheet.Start).Ticks;
  548. // long btTicks = App.Bluetooth.TimeStamp.Ticks;
  549. //
  550. // if (Math.Abs(tsTicks - btTicks) > new TimeSpan(0, 2, 0).Ticks)
  551. // return false;
  552. //
  553. // CoreRow firstgate = null;
  554. // List<String> gates = new List<string>();
  555. // // Scan every located d
  556. // foreach (var device in App.Bluetooth.Devices)
  557. // {
  558. // CoreRow gate = App.Data.Gates?.Rows.FirstOrDefault(r => r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID) == device);
  559. // if (gate != null)
  560. // {
  561. //
  562. // if ((gate.Get<JobTracker, bool>(x => x.IsJobSite) == true) && (firstgate == null))
  563. // firstgate = gate;
  564. //
  565. // gates.Add(gate.Get<JobTracker, String>(x => x.Gate));
  566. // }
  567. // }
  568. // if (gates.Any())
  569. // {
  570. // timesheet.Gate = String.Join(", ", gates.OrderBy(x => x));
  571. // if (firstgate != null)
  572. // {
  573. // timesheet.JobLink.ID = firstgate.Get<JobTracker, Guid>(x => x.JobLink.ID);
  574. // timesheet.JobLink.JobNumber = firstgate.Get<JobTracker, String>(x => x.JobLink.JobNumber);
  575. // timesheet.JobLink.Name = firstgate.Get<JobTracker, String>(x => x.JobLink.Name);
  576. // }
  577. // return true;
  578. // //new Client<TimeSheet>().Save(timesheet, "Confirmed Gate Entry by Bluetooth Tracker", (o, e) => { });
  579. // }
  580. //
  581. // return false;
  582. //
  583. // }
  584. private bool ZeroLengthTimesheet()
  585. {
  586. if (_currenttimesheet == null)
  587. return true;
  588. if (!String.IsNullOrWhiteSpace(_currenttimesheet.Notes))
  589. return false;
  590. if (_currenttimesheet.Date.Equals(DateTime.Today))
  591. {
  592. var diff = (DateTime.Now.TimeOfDay - _currenttimesheet.Start).TotalSeconds;
  593. if (Math.Abs(diff) < 120.0F)
  594. return true;
  595. }
  596. return false;
  597. }
  598. private async void CreateTimeSheet(Guid jobid, string jobnumber, String jobname, InABox.Core.Location location, String address, String auditmessage)
  599. {
  600. try
  601. {
  602. _currenttimesheet = new TimeSheet();
  603. _currenttimesheet.EmployeeLink.ID = App.Data.Me.ID;
  604. _currenttimesheet.Date = DateTime.Today;
  605. TimeSpan tod = DateTime.Now - DateTime.Today;
  606. tod = new TimeSpan(tod.Hours, tod.Minutes, 0);
  607. _currenttimesheet.Start = tod;
  608. _currenttimesheet.StartLocation = location;
  609. _currenttimesheet.JobLink.ID = jobid;
  610. _currenttimesheet.JobLink.JobNumber = jobnumber;
  611. _currenttimesheet.JobLink.Name = jobname;
  612. _currenttimesheet.Address = address;
  613. _currenttimesheet.SoftwareVersion = MobileUtils.AppVersion.InstalledVersionNumber + MobileUtils.GetDeviceID();
  614. //if (ClientFactory.IsAllowed<AllowTimeSheetRollover>()) CheckTimeSheetAgainstGates(timesheet);
  615. bUpdatingTimesheet = true;
  616. new Client<TimeSheet>().Save(_currenttimesheet, auditmessage);
  617. }
  618. catch (Exception ex)
  619. {
  620. InABox.Mobile.MobileLogging.Log(ex);
  621. }
  622. }
  623. private bool CheckLocation()
  624. {
  625. // a 15 minute timeout is awfully long for this process.
  626. // The App times tick over every 30 seconds, so surely we can
  627. // drop this to max 1 or 2 minutes..
  628. // Also, we would expect the GPS / Bluetooth subsystems to take care
  629. // of purging stale data
  630. if (InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>())
  631. return true;
  632. if (InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  633. return App.GPS.TimeStamp > DateTime.Now.Subtract(new TimeSpan(0, 15, 0));
  634. if (App.Bluetooth.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0)))
  635. return false;
  636. return App.Data.BluetoothGates.Any(x =>
  637. App.Bluetooth.DetectedBlueToothMACAddresses.Contains(x.DeviceID));
  638. }
  639. private String GetAddress()
  640. {
  641. if (InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  642. {
  643. if (App.GPS.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0)))
  644. {
  645. App.GPS.GetLocation(true);
  646. return InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>()? "" : "Searching for GPS";
  647. }
  648. return App.GPS.Address;
  649. }
  650. else
  651. {
  652. var gate = App.Data.BluetoothGates.FirstOrDefault(x =>
  653. App.Bluetooth.DetectedBlueToothMACAddresses.Contains(x.DeviceID));
  654. return gate?.Gate ?? "Looking for Gate";
  655. }
  656. }
  657. private async void LoadHRToDos()
  658. {
  659. try
  660. {
  661. await Task.Run(() =>
  662. {
  663. Thread.Sleep(10000);
  664. if (App.Data.UpdateHRItemsNeedingAttention())
  665. {
  666. string message = "You have HR Items needing attention. Open My HR now?";
  667. Device.BeginInvokeOnMainThread(async () =>
  668. {
  669. string chosenOption = await DisplayActionSheet(message, "Cancel", null, "Yes", "No");
  670. switch (chosenOption)
  671. {
  672. case "Cancel":
  673. break;
  674. case "No":
  675. break;
  676. default:
  677. break;
  678. case "Yes":
  679. HumanResourcesModule myHRHome = new HumanResourcesModule();
  680. Navigation.PushAsync(myHRHome);
  681. break;
  682. }
  683. });
  684. }
  685. });
  686. }
  687. catch { }
  688. }
  689. #endregion
  690. private void Settings_OnTapped(object sender, EventArgs e)
  691. {
  692. Navigation.PushAsync(new SettingsPage());
  693. }
  694. private void Assignments_OnTapped(object sender, EventArgs e)
  695. {
  696. Navigation.PushAsync(new AssignmentList());
  697. }
  698. private void Deliveries_OnTapped(object sender, EventArgs e)
  699. {
  700. Navigation.PushAsync(new DeliveryModule());
  701. }
  702. private void Equipment_OnTapped(object sender, EventArgs e)
  703. {
  704. Navigation.PushAsync(new EquipmentModule());
  705. }
  706. private void Forms_OnTapped(object sender, EventArgs e)
  707. {
  708. Navigation.PushAsync(new KanbanForms());
  709. //Navigation.PushAsync(new DigitalFormsPicker("Kanban");
  710. }
  711. private void InOut_OnTapped(object sender, EventArgs e)
  712. {
  713. Navigation.PushAsync(new StaffStatusPage());
  714. }
  715. private void Manufacturing_OnTapped(object sender, EventArgs e)
  716. {
  717. Navigation.PushAsync(new ManufacturingList());
  718. }
  719. private void MyHR_OnTapped(object sender, EventArgs e)
  720. {
  721. Navigation.PushAsync(new HumanResourcesModule());
  722. }
  723. private void Notifications_OnTapped(object sender, EventArgs e)
  724. {
  725. Navigation.PushAsync(new NotificationList());
  726. }
  727. private void Products_OnTapped(object sender, EventArgs e)
  728. {
  729. Navigation.PushAsync(new ProductList());
  730. }
  731. private void PurchaseOrders_OnTapped(object sender, EventArgs e)
  732. {
  733. Navigation.PushAsync(new PurchaseOrderList());
  734. }
  735. private void Site_OnTapped(object sender, EventArgs e)
  736. {
  737. Guid jobid =
  738. _currenttimesheet?.JobLink.ID
  739. ?? new LocalConfiguration<SiteModuleSettings>().Load().JobID;
  740. Navigation.PushAsync(
  741. new SiteModule()
  742. {
  743. JobID = jobid
  744. }
  745. );
  746. }
  747. private void StoreRequis_OnTapped(object sender, EventArgs e)
  748. {
  749. Navigation.PushAsync(new StoreRequiList());
  750. }
  751. private void MyTasks_OnTapped(object sender, EventArgs e)
  752. {
  753. var tasks = new KanbanList()
  754. {
  755. Model = App.Data.Kanbans,
  756. Title = "My Tasks"
  757. };
  758. Navigation.PushAsync(tasks);
  759. }
  760. private void Warehousing_OnTapped(object sender, EventArgs e)
  761. {
  762. Navigation.PushAsync(new WarehouseModule());
  763. }
  764. }
  765. }