using comal.timesheets.StoreRequis; using Comal.Classes; using InABox.Clients; using InABox.Core; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using InABox.Mobile; using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; using static comal.timesheets.RequiItems; using LogType = InABox.Core.LogType; namespace comal.timesheets { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class StoreRequiScannerPage { #region Fields / Constructor public delegate bool OnScanEvent(object sender, string barcode); public event OnScanEvent OnScan; List shells = new List(); List oldShells = new List(); Requisition requisition = new Requisition(); bool loading = false; Dictionary itemRowScannerRawResultPairs = new Dictionary(); Dictionary itemRowScannerProcessedResultPairs = new Dictionary(); bool firstLoad = true; bool containsNotes = false; public StoreRequiScannerPage(Guid requiID) { InitializeComponent(); ConfigAll(requiID); } #endregion #region Config void ConfigAll(Guid requiID) { NavigationPage.SetHasBackButton(this, false); SetScannerOptions(); if (requiID != Guid.Empty) { requisition.ID = requiID; LoadExistingRequi(); } if (!HoldingsLoaded) { Timer t = null; t = new Timer(new TimerCallback((object o) => { if (HoldingsLoaded) { Device.BeginInvokeOnMainThread(() => { ConfigDisplay(); }); t.Dispose(); } }), null, 0, 500); } else { loadingLbl.IsVisible = false; addBtn.IsEnabled = true; Task.Run(() => { Thread.Sleep(1500); Device.BeginInvokeOnMainThread(() => { ConfigDisplay(); }); }); } } void SetScannerOptions() { var options = new ZXing.Mobile.MobileBarcodeScanningOptions() { PossibleFormats = new List() { ZXing.BarcodeFormat.QR_CODE }, AutoRotate = false, TryInverted = true, TryHarder = true, }; _scanView.Options = options; _scanView.IsAnalyzing = true; _scanView.IsScanning = true; _scanView.AutoFocus(); _scanView.OnScanResult += ScanView_OnScanResult; } protected override void OnAppearing() { base.OnAppearing(); if (!firstLoad) _scanView.IsAnalyzing = true; } protected override void OnDisappearing() { _scanView.IsAnalyzing = false; base.OnDisappearing(); } async void LoadExistingRequi() { await Task.Run(() => { CoreTable table = QueryRequi(); while(table == null) table = QueryRequi(); requisition = table.Rows.FirstOrDefault().ToObject(); string notes = CheckNotes(requisition.Notes); if (!string.IsNullOrWhiteSpace(requisition.Request) || !string.IsNullOrWhiteSpace(notes)) { StoreRequiItemShell shell1 = new StoreRequiItemShell() { IsNotes = true, IsNotNotes = false, BorderColor = Color.FromHex("#9f4576") }; shell1.Summary = !string.IsNullOrWhiteSpace(requisition.Request) ? "REQUEST: " + requisition.Request + System.Environment.NewLine : ""; shell1.Summary = shell1.Summary + notes; shells.Insert(0, shell1); containsNotes = true; } }); await Task.Run(() => { CoreTable table = QueryRequiItems(); while(table == null) table = QueryRequiItems(); if (table.Rows.Any()) { Device.BeginInvokeOnMainThread(() => { saveBtn.IsVisible = true; }); foreach (CoreRow row in table.Rows) { StoreRequiItemShell shell = new StoreRequiItemShell() { ID = row.Get(x => x.ID), ProductID = row.Get(x => x.Product.ID), ProductName = row.Get(x => x.Product.Name), ProductCode = row.Get(x => x.Product.Code), Quantity = row.Get(x => x.Quantity), LocationID = row.Get(x => x.Location.ID), LocationName = row.Get(x => x.Location.Description), Picked = row.Get(x => x.Picked), }; shells.Add(shell); oldShells.Add(shell); } } Device.BeginInvokeOnMainThread(() => { requiItemListView.ItemsSource = shells; if (containsNotes) { countLbl.Text = "Number of items: " + (shells.Count - 1); } else { countLbl.Text = "Number of items: " + shells.Count; } }); }); } private CoreTable QueryRequi() { try { return new Client().Query( new Filter(x => x.ID).IsEqualTo(requisition.ID) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private CoreTable QueryRequiItems() { try { return new Client().Query ( new Filter(x => x.RequisitionLink.ID).IsEqualTo(requisition.ID), new Columns( x => x.ID, x => x.Product.ID, x => x.Product.Name, x => x.Product.Code, x => x.Quantity, x => x.Location.ID, x => x.Location.Description, x => x.Picked ) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private string CheckNotes(string[] notes) { string combinednotes = "----------" + System.Environment.NewLine + "NOTES: " + System.Environment.NewLine; if (notes.Count() > 0) { foreach (var note in notes) { combinednotes = combinednotes + note + System.Environment.NewLine; } } return combinednotes; } void ConfigDisplay() { loadingLbl.IsVisible = false; addBtn.IsEnabled = true; double width = scannerGrid.Width / 6; double height = scannerGrid.Height / 6; lblv1.WidthRequest = height / 2; lblh1.IsVisible = true; lblv1.IsVisible = true; lblv2.WidthRequest = height / 2; lblh2.IsVisible = true; lblv2.IsVisible = true; lblv3.WidthRequest = height / 2; lblh3.IsVisible = true; lblv3.IsVisible = true; lblv4.WidthRequest = height / 2; lblh4.IsVisible = true; lblv4.IsVisible = true; scannerGrid.RaiseChild(lblv1); scannerGrid.RaiseChild(lblh1); scannerGrid.RaiseChild(lblv2); scannerGrid.RaiseChild(lblh2); scannerGrid.RaiseChild(lblv3); scannerGrid.RaiseChild(lblh3); scannerGrid.RaiseChild(lblv4); scannerGrid.RaiseChild(lblh4); firstLoad = false; } #endregion #region Scanning private void ScanView_OnScanResult(ZXing.Result result) { Device.BeginInvokeOnMainThread(async () => { if (!loading) { loading = true; if (RequiItems.HoldingsLoaded) { bool bOK = true; if (OnScan != null) bOK = OnScan(this, result.Text); if (bOK) { if (!itemRowScannerRawResultPairs.Values.Contains(result.Text)) { if (!itemRowScannerProcessedResultPairs.Values.Contains(result.Text)) { Vibration.Vibrate(); var player = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current; player.Load("requiitemadded.mp3"); player.Play(); using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding")) { string rawResult = result.Text; Tuple tuple = ProcessResult(result.Text); LoadProduct(tuple, rawResult); } } else { loading = false; } } else { loading = false; } } else { loading = false; } } else { loading = false; } } }); } private Tuple ProcessResult(string result) { double qty = 1; if (result.Contains("*")) { try { int i = result.IndexOf("*"); string remainder = result.Substring(i); result = result.Remove(i); string s1 = remainder.Substring(1); qty = Convert.ToDouble(s1); } catch { loading = false; } } Tuple tuple = new Tuple(result, qty); return tuple; } private void LoadProduct(Tuple processedResultQtyTuple, string rawResult) { Device.BeginInvokeOnMainThread(async () => { //lookup product in productshells cache ProductShell product = App.Data.Products.FirstOrDefault(x => x.Code.Equals(processedResultQtyTuple.Item1)); //lookup holding for product in holdings cache try { var list = CreateHoldingsList(product.ID); if (list.Count == 1) //one stockholding - auto choose holding AddStoreRequiItemShell(list.First(), product, rawResult, processedResultQtyTuple); else if (list.Count > 1) //more than one stockholding - user choose shelf UserSelectFromList(list, product, rawResult, processedResultQtyTuple); else if (list.Count == 0) DisplayAlert("No Holdings Found for Product", "", "OK"); loading = false; } catch (Exception e) { DisplayAlert("Error", e.Message, "OK"); loading = false; return; } }); } private void UserSelectFromList(List list, ProductShell product, string rawResult, Tuple processedResultQtyTuple) { Dictionary holdingLocationIDs = new Dictionary(); foreach (StockHoldingShell holding in list) { if (!holdingLocationIDs.ContainsKey(holding.LocationName)) holdingLocationIDs.Add(holding.LocationName, holding.LocationID); } List options = holdingLocationIDs.Keys.ToList(); ListSelectionPage page = new ListSelectionPage(options); page.OnSimpleListTapped += (locationName) => { AddStoreRequiItemShell(list.Find(x => x.LocationName == locationName), product, rawResult, processedResultQtyTuple); }; Navigation.PushAsync(page); } private List CreateHoldingsList(Guid productID) { List list = new List(); CoreTable table = DoHoldingsQuery(productID); while (table == null) table = DoHoldingsQuery(productID); foreach (CoreRow row in table.Rows) { if (row.Get(x => x.Units) == 0.0) continue; list.Add(CreateHoldingShell(row)); } return list; } private CoreTable DoHoldingsQuery(Guid productID) { try { return new Client().Query( new Filter(x => x.Product.ID).IsEqualTo(productID), new Columns( x => x.ID, x => x.Product.ID, x => x.Location.ID, x => x.Location.Description, x => x.Units, x => x.Job.ID, x => x.Job.JobNumber, x => x.Job.Name, x => x.Style.ID, x => x.Style.Code, x => x.Style.Description, x => x.Dimensions.Unit.ID, x => x.Dimensions.Unit.HasQuantity, x => x.Dimensions.Unit.HasLength, x => x.Dimensions.Unit.HasHeight, x => x.Dimensions.Unit.HasWeight, x => x.Dimensions.Unit.HasWidth, x => x.Dimensions.Quantity, x => x.Dimensions.Length, x => x.Dimensions.Height, x => x.Dimensions.Weight, x => x.Dimensions.Width, x => x.Dimensions.Unit.Format, x => x.Dimensions.Unit.Formula, x => x.Dimensions.UnitSize ) ); } catch (Exception ex) { InABox.Mobile.MobileLogging.Log(ex); return null; } } private StockHoldingShell CreateHoldingShell(CoreRow row) { StockHoldingShell holding = new StockHoldingShell() { ProductID = row.Get(x => x.Product.ID), LocationID = row.Get(x => x.Location.ID), LocationName = row.Get(x => x.Location.Description), Units = row.Get(x => x.Units), JobID = row.Get(x => x.Job.ID), JobNumber = row.Get(x => x.Job.JobNumber), JobName = row.Get(x => x.Job.Name), StyleID = row.Get(x => x.Style.ID), StyleCode = row.Get(x => x.Style.Code), StyleName = row.Get(x => x.Style.Description) }; holding.DimensionsUnitID = row.Get(x => x.Dimensions.Unit.ID); holding.DimensionsHasQuantity = row.Get(x => x.Dimensions.Unit.HasQuantity); holding.DimensionsHasLength = row.Get(x => x.Dimensions.Unit.HasLength); holding.DimensionsHasHeight = row.Get(x => x.Dimensions.Unit.HasHeight); holding.DimensionsHasWeight = row.Get(x => x.Dimensions.Unit.HasWeight); holding.DimensionsHasWidth = row.Get(x => x.Dimensions.Unit.HasWidth); holding.DimensionsQuantity = row.Get(x => x.Dimensions.Quantity); holding.DimensionsLength = row.Get(x => x.Dimensions.Length); holding.DimensionsHeight = row.Get(x => x.Dimensions.Height); holding.DimensionsWeight = row.Get(x => x.Dimensions.Weight); holding.DimensionsWidth = row.Get(x => x.Dimensions.Width); holding.DimensionsUnitFormat = row.Get(x => x.Dimensions.Unit.Format); holding.DimensionsUnitFormula = row.Get(x => x.Dimensions.Unit.Formula); holding.DimensionsUnitSize = row.Get(x => x.Dimensions.UnitSize); holding.LocationName = holding.LocationName + " (Units: " + holding.Units + ")" + Environment.NewLine + "Style: " + holding.StyleName + Environment.NewLine + "Job: " + holding.JobNumber + Environment.NewLine + "Size: " + holding.DimensionsUnitSize; return holding; } private void AddStoreRequiItemShell(StockHoldingShell holding, ProductShell product, string rawResult, Tuple processedResultQtyTuple) { StoreRequiItemShell shell = new StoreRequiItemShell { ProductID = product.ID, ProductName = product.Name, ProductCode = product.Code, Quantity = 1, LocationID = holding.LocationID, LocationName = holding.LocationName, StyleID = holding.StyleID, JobID = holding.StyleID }; shell.Dimensions.Unit.ID = holding.DimensionsUnitID; shell.Dimensions.Unit.HasQuantity = holding.DimensionsHasQuantity; shell.Dimensions.Unit.HasLength = holding.DimensionsHasLength; shell.Dimensions.Unit.HasHeight = holding.DimensionsHasHeight; shell.Dimensions.Unit.HasWeight = holding.DimensionsHasWeight; shell.Dimensions.Unit.HasWidth = holding.DimensionsHasWidth; shell.Dimensions.Quantity = holding.DimensionsQuantity; shell.Dimensions.Length = holding.DimensionsLength; shell.Dimensions.Height = holding.DimensionsHeight; shell.Dimensions.Weight = holding.DimensionsWeight; shell.Dimensions.Width = holding.DimensionsWidth; shell.Dimensions.Unit.Format = holding.DimensionsUnitFormat; shell.Dimensions.Unit.Formula = holding.DimensionsUnitFormula; shell.Dimensions.UnitSize = holding.DimensionsUnitSize; shells.Add(shell); itemRowScannerRawResultPairs.Add(shell, rawResult); itemRowScannerProcessedResultPairs.Add(shell, processedResultQtyTuple.Item1); requiItemListView.ItemsSource = null; requiItemListView.ItemsSource = shells; countLbl.IsVisible = true; if (containsNotes) { countLbl.Text = "Number of items: " + (shells.Count - 1); } else { countLbl.Text = "Number of items: " + shells.Count; } saveBtn.IsVisible = true; loading = false; } #endregion #region Button Presses private async void ExitBtn_Clicked(object sender, EventArgs e) { if (shells.Count > 0) { if (containsNotes && shells.Count > 1) { string chosenOption = await DisplayActionSheet("Leave without saving?", "Cancel", null, "Yes", "No"); switch (chosenOption) { case "Cancel": return; case "Yes": Navigation.PopAsync(); break; case "No": return; default: return; } } else if (containsNotes && shells.Count == 1) { Navigation.PopAsync(); } else { string chosenOption = await DisplayActionSheet("Leave without saving?", "Cancel", null, "Yes", "No"); switch (chosenOption) { case "Cancel": return; case "Yes": Navigation.PopAsync(); break; case "No": return; default: return; } } } else Navigation.PopAsync(); } void SaveBtn_Clicked(object sender, EventArgs e) { StoreRequiConfirmationPage page = new StoreRequiConfirmationPage(requisition, shells, oldShells); page.OnSaveSelected += () => { Navigation.PopAsync(); }; Navigation.PushAsync(page); } private async void RequiItem_Tapped(object sender, EventArgs e) { var shell = requiItemListView.SelectedItem as StoreRequiItemShell; if (shell == null) return; await RequiItemTappedAsync(shell); } private async Task RequiItemTappedAsync(StoreRequiItemShell shell) { string pickstatus = shell.Picked == DateTime.MinValue ? "not picked" : "picked (" + shell.Picked.ToString("dd MMM yy") + ")"; string options = shell.Picked == DateTime.MinValue ? ". Mark as Picked?" : ". Remove Picked status?"; string chosenOption = await DisplayActionSheet("Line is " + pickstatus + options, "Cancel", null, "Yes", "No"); if (chosenOption != "Yes") return; shell.Picked = shell.Picked == DateTime.MinValue ? DateTime.Today : DateTime.MinValue; shell.Colour = shell.Picked == DateTime.MinValue ? Color.Default : Color.FromHex("#8fbc8f"); requiItemListView.ItemsSource = null; requiItemListView.ItemsSource = shells; } void ReduceQtyBtn_Clicked(object sender, EventArgs e) { var shell = ((TappedEventArgs)e).Parameter as StoreRequiItemShell; if (shell == null) return; if (shell.Quantity <= 1) { shells.Remove(shell); itemRowScannerRawResultPairs.Remove(shell); itemRowScannerProcessedResultPairs.Remove(shell); if (containsNotes) { countLbl.Text = "Number of items: " + (shells.Count - 1); if (shells.Count == 1) { saveBtn.IsVisible = false; } } else { countLbl.Text = "Number of items: " + shells.Count; if (shells.Count == 0) { saveBtn.IsVisible = false; } } } else { shell.Quantity--; } requiItemListView.ItemsSource = null; requiItemListView.ItemsSource = shells; } void IncreaseQtyBtn_Clicked(object sender, EventArgs e) { var shell = ((TappedEventArgs)e).Parameter as StoreRequiItemShell; if (shell == null) return; shell.Quantity++; requiItemListView.ItemsSource = null; requiItemListView.ItemsSource = shells; } void AddItem_Clicked(object sender, EventArgs e) { if (App.Data.Products.Loaded) { if (loading) return; loading = true; var products = new ProductSelectionPage( (product) => { Tuple tuple = new Tuple(product.Code, 1); LoadProduct(new Tuple(product.Code, 1), product.Code); } ); Navigation.PushAsync(products); } } void Qty_Changed(object sender, EventArgs e) { } #endregion } }