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 Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; namespace PRS.Mobile { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class StoreRequiScannerPage { #region Fields / Constructor private StockHoldingModel _holdings = new StockHoldingModel( App.Data, () => new Filter(x => x.Units).IsNotEqualTo(0.0F) ); private Task _holdingsTask = null; List shells = new List(); List oldShells = new List(); Requisition requisition = new Requisition(); Dictionary itemRowScannerRawResultPairs = new Dictionary(); Dictionary itemRowScannerProcessedResultPairs = new Dictionary(); private bool loading = true; bool containsNotes = false; public StoreRequiScannerPage(Guid requiID) { _holdingsTask = Task.Run(() => _holdings.Refresh(false)); InitializeComponent(); ConfigAll(requiID); } #endregion #region Config void ConfigAll(Guid requiID) { _scanView.Options = new ZXing.Mobile.MobileBarcodeScanningOptions() { PossibleFormats = new List() { ZXing.BarcodeFormat.QR_CODE }, AutoRotate = false, TryInverted = true, TryHarder = true, }; ConfigDisplay(); if (requiID != Guid.Empty) { requisition.ID = requiID; LoadExistingRequi(); } } protected override void OnAppearing() { base.OnAppearing(); _scanView.IsAnalyzing = true; _scanView.IsScanning = true; _scanView.AutoFocus(); } protected override void OnDisappearing() { _scanView.IsScanning = false; _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(ColumnTypeFlags.None).Add( 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() { scannerGrid.RaiseChild(topleft); scannerGrid.RaiseChild(topright); scannerGrid.RaiseChild(bottomright); scannerGrid.RaiseChild(bottomleft); } #endregion #region Scanning private async void ScanView_OnScanResult(ZXing.Result result) { if (!loading) { loading = true; Vibration.Vibrate(); try { _holdingsTask.Wait(); if (!itemRowScannerRawResultPairs.Values.Contains(result.Text)) { if (!itemRowScannerProcessedResultPairs.Values.Contains(result.Text)) { using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Adding")) { string rawResult = result.Text; Tuple tuple = ProcessResult(result.Text); var status = await LoadProduct(tuple, rawResult); if (status == LoadProductStatus.InvalidCode) await DisplayAlert("Error", "Invalid Product Code", "OK"); else if (status == LoadProductStatus.NoHoldings) await DisplayAlert("Error", "No Holdings Found for Product", "OK"); } } } } catch (Exception e) { MobileLogging.Log(e, "StoreRequiScanner"); } 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 enum LoadProductStatus { Success, InvalidCode, NoHoldings } private async Task LoadProduct(Tuple processedResultQtyTuple, string rawResult) { ProductShell? product = App.Data.Products.FirstOrDefault(x => x.Code.Equals(processedResultQtyTuple.Item1)); if (product == null) return LoadProductStatus.InvalidCode; var holdings = _holdings.Where(x => x.ProductID == product.ID).ToArray(); if (holdings.Length == 0) return LoadProductStatus.NoHoldings; else if (holdings.Length == 1) AddStoreRequiItemShell(holdings.First(), product, rawResult, processedResultQtyTuple); else UserSelectFromList(holdings, product, rawResult, processedResultQtyTuple); return LoadProductStatus.Success; } private void UserSelectFromList(StockHoldingShell[] list, ProductShell product, string rawResult, Tuple processedResultQtyTuple) { StockHoldingSelectionPage page = new StockHoldingSelectionPage(_holdings, (shell) => { AddStoreRequiItemShell(shell, product, rawResult, processedResultQtyTuple); }); Navigation.PushAsync(page); } 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.LocationDescription, 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 } }