MainPage.xaml.cs 33 KB

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