using comal.timesheets.CustomControls; using Comal.Classes; using InABox.Clients; using InABox.Core; using Plugin.Media; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; using System.Threading.Tasks; using Xamarin.CommunityToolkit.Extensions; using Xamarin.CommunityToolkit.UI.Views; using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; namespace comal.timesheets { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class StocktakePage : ContentPage { #region Fields, Constructor and OnAppearing List stockLocationShells = new List(); List stockHoldingShells = new List(); string DeviceType = ""; StockLocationShell openLocation = new StockLocationShell(); bool bOpening = false; Color stockHoldingCheckedColor = Color.FromHex("#3eb489"); List movementsToUpdate = new List(); public StocktakePage(Guid locationid) { InitializeComponent(); var idiom = DeviceInfo.Idiom; if (idiom.Equals(DeviceIdiom.Tablet)) { DeviceType = "Tablet"; } NavigationPage.SetHasBackButton(this, false); if (locationid != Guid.Empty) LoadLocation(locationid); } protected override void OnAppearing() { base.OnAppearing(); bOpening = false; } #endregion #region Button Presses private async void ExitWithoutSaving(object sender, EventArgs e) { if (openLocation.ID != Guid.Empty) { 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(); } private void AddLocation_Clicked(object sender, EventArgs e) { if (bOpening) return; bOpening = true; StockLocationSelectionPage stockLocationSelectionPage = new StockLocationSelectionPage(true); stockLocationSelectionPage.OnMultiLocationSelected += (s) => { foreach (StockLocationShell shell in s) { if (!stockLocationShells.Any(x => x.ID == shell.ID)) { shell.Color = Color.Default; stockLocationShells.Add(shell); if (string.IsNullOrWhiteSpace(shell.NumberOfHoldings)) FindHoldingNumber(shell); } } Device.BeginInvokeOnMainThread(() => { leftListView.ItemsSource = null; leftListView.ItemsSource = stockLocationShells; }); }; Navigation.PushAsync(stockLocationSelectionPage); } private void AddItem_Clicked(object sender, EventArgs e) { if (openLocation.ID != Guid.Empty) { if (GlobalVariables.ProductsLoaded) { ProductList products = new ProductList(GlobalVariables.ProductShells, true); products.OnProductSelected += (() => { StockHoldingShell shell = new StockHoldingShell(); shell.Code = products.SelectedProduct.Code; shell.Name = products.SelectedProduct.Name; shell.ImageID = products.SelectedProduct.ImageID; shell.ProductID = products.SelectedProduct.ID; shell.DisplayJob = openLocation.JobNumber; if (!string.IsNullOrWhiteSpace(openLocation.JobNumber)) { shell.JobNumber = openLocation.JobNumber.Substring(6); } shell.JobID = openLocation.JobID; shell.Units = 0; shell.DisplayUnits = "Units: 0"; shell.DisplayFinish = "Finish: Select"; shell.DimensionsUnitID = products.SelectedProduct.DimensionsUnitID; shell.DimensionsQuantity = products.SelectedProduct.DimensionsQuantity; shell.DimensionsLength = products.SelectedProduct.DimensionsLength; shell.DimensionsWidth = products.SelectedProduct.DimensionsWidth; shell.DimensionsHeight = products.SelectedProduct.DimensionsHeight; shell.DimensionsWeight = products.SelectedProduct.DimensionsWeight; shell.DimensionsValue = products.SelectedProduct.DimensionsValue; shell.DimensionsUnitSize = products.SelectedProduct.DimensionsUnitSize; shell.DimensionsHasHeight = products.SelectedProduct.DimensionsHasHeight; shell.DimensionsHasLength = products.SelectedProduct.DimensionsHasLength; shell.DimensionsHasWidth = products.SelectedProduct.DimensionsHasWidth; shell.DimensionsHasWeight = products.SelectedProduct.DimensionsHasWeight; shell.DimensionsHasQuantity = products.SelectedProduct.DimensionsHasQuantity; shell.DimensionsUnitFormula = products.SelectedProduct.DimensionsUnitFormula; shell.DimensionsUnitFormat = products.SelectedProduct.DimensionsUnitFormat; shell.DimensionsUnitCode = products.SelectedProduct.DimensionsUnitCode; shell.DimensionsUnitDescription = products.SelectedProduct.DimensionsUnitDescription; stockHoldingShells.Add(shell); rightListView.ItemsSource = null; rightListView.ItemsSource = stockHoldingShells; if (shell.ImageID != Guid.Empty) LoadImage(shell); }); Navigation.PushAsync(products); } } else { string alert = ""; if (stockLocationShells.Count == 0) { alert = "Add a location first"; } else { alert = "Open a location first"; } DisplayAlert("Alert", alert, "OK"); } } #endregion #region Lists Tapped private async void LeftListView_Tapped(object sender, EventArgs e)// open/close a location { StockLocationShell stockLocationShell = leftListView.SelectedItem as StockLocationShell; if (openLocation.ID == stockLocationShell.ID) //close location and save movements { bool allComplete = true; bool allIncomplete = true; foreach (StockHoldingShell shell in stockHoldingShells) { if (shell.Color != stockHoldingCheckedColor) allComplete = false; else allIncomplete = false; } if (!allComplete && !allIncomplete) { DisplayAlert("Alert", "Not all holdings have been checked for this location / pack", "OK"); return; } if (allIncomplete) { openLocation.ID = Guid.Empty; openLocation.Code = ""; openLocation.Description = ""; stockLocationShell.Color = Color.Default; stockLocationShell.OpenCloseLocation = "Open"; int index = stockLocationShells.FindIndex(x => x.ID.Equals(stockLocationShell.ID)); stockLocationShells.RemoveAt(index); stockLocationShells.Insert(index, stockLocationShell); leftListView.ItemsSource = null; leftListView.ItemsSource = stockLocationShells; rightListView.ItemsSource = null; stockHoldingShells.Clear(); rightCountLbl.Text = "Items: " + stockHoldingShells.Count(); movementsToUpdate.Clear(); return; } StockTakeCompletionPage stockTakeCompletionPage = new StockTakeCompletionPage(movementsToUpdate); stockTakeCompletionPage.OnStockTakeCompleted += () => { openLocation.ID = Guid.Empty; openLocation.Code = ""; openLocation.Description = ""; stockLocationShell.Color = stockHoldingCheckedColor; stockLocationShell.OpenCloseLocation = "Completed"; int index = stockLocationShells.FindIndex(x => x.ID.Equals(stockLocationShell.ID)); stockLocationShells.RemoveAt(index); stockLocationShells.Insert(index, stockLocationShell); leftListView.ItemsSource = null; leftListView.ItemsSource = stockLocationShells; rightListView.ItemsSource = null; stockHoldingShells.Clear(); rightCountLbl.Text = "Items: " + stockHoldingShells.Count(); movementsToUpdate.Clear(); }; Navigation.PushAsync(stockTakeCompletionPage); } else if (openLocation.ID == Guid.Empty) //open the location and load holdings { Filter filter = new Filter(x => x.Location.ID).IsEqualTo(stockLocationShell.ID); openLocation.ID = stockLocationShell.ID; openLocation.Code = stockLocationShell.Code; openLocation.Description = stockLocationShell.Description; stockLocationShell.Color = Color.Orange; stockLocationShell.OpenCloseLocation = "Close"; LoadHoldings(filter); int index = stockLocationShells.FindIndex(x => x.ID.Equals(stockLocationShell.ID)); stockLocationShells.RemoveAt(index); stockLocationShells.Insert(index, stockLocationShell); leftListView.ItemsSource = null; leftListView.ItemsSource = stockLocationShells; } else if (openLocation.ID != Guid.Empty) { DisplayAlert("Alert", "Currently Open Location must be closed first", "OK"); } } private void RightListView_Tapped(object sender, EventArgs e) //open popup to confirm, on closing turns green { if (bOpening) return; bOpening = true; StockHoldingShell shell = rightListView.SelectedItem as StockHoldingShell; StockHoldingShell originalShell = DuplicateShell(shell); ProductStyle style = new ProductStyle() { ID = shell.StyleID, Code = shell.StyleCode, Description = shell.Finish }; Job job = new Job() { ID = shell.JobID, JobNumber = shell.JobNumber }; RecTransferPopup popup = new RecTransferPopup(shell, style, job, job, true); StockMovement additionalmovement = new StockMovement(); popup.OnRecTransferItemAccepted += () => { StockMovement movement = new StockMovement() { }; movement.Dimensions.Unit.ID = shell.DimensionsUnitID; movement.Dimensions.Quantity = shell.DimensionsQuantity; movement.Dimensions.Length = shell.DimensionsLength; movement.Dimensions.Width = shell.DimensionsWidth; movement.Dimensions.Height = shell.DimensionsHeight; movement.Dimensions.Weight = shell.DimensionsWeight; movement.Dimensions.Unit.HasHeight = shell.DimensionsHasHeight; movement.Dimensions.Unit.HasLength = shell.DimensionsHasLength; movement.Dimensions.Unit.HasWidth = shell.DimensionsHasWidth; movement.Dimensions.Unit.HasWeight = shell.DimensionsHasWeight; movement.Dimensions.Unit.HasQuantity = shell.DimensionsHasQuantity; movement.Dimensions.Unit.Code = shell.DimensionsUnitCode; movement.Dimensions.Unit.Description = shell.DimensionsUnitDescription; movement.Dimensions.Value = shell.DimensionsValue; movement.Dimensions.Unit.Formula = shell.DimensionsUnitFormula; movement.Dimensions.Unit.Format = shell.DimensionsUnitFormat; movement.Dimensions.UnitSize = shell.DimensionsUnitSize; movement.Product.ID = shell.ProductID; movement.Product.Code = shell.Code; movement.Product.Name = shell.Name; movement.Employee.ID = GlobalVariables.EmpID; movement.Employee.Name = GlobalVariables.EmpName; movement.Location.ID = openLocation.ID; movement.Location.Description = openLocation.Description; movement.Location.Code = openLocation.Code; movement.Job.ID = shell.JobID; movement.Job.JobNumber = shell.JobNumber; movement.Style.ID = shell.StyleID; movement.Style.Code = shell.StyleCode; movement.Style.Description = shell.Finish; if (popup.Shell.Units < originalShell.Units) { movement.Issued = originalShell.Units - popup.Shell.Units; movement.Notes = "Updated incorrect Qty during stocktake. Less Qty than expected."; } else if (popup.Shell.Units > originalShell.Units) { movement.Received = popup.Shell.Units - originalShell.Units; movement.Notes = "Updated incorrect Qty during stocktake. More Qty than expected."; } else if (popup.Shell.Units == originalShell.Units) { movement.Received = 0; movement.Issued = 0; movement.Notes = "Confirmed correct Qty during stocktake."; } //if style is changed, create a movement that issues everything from the original style //in addition to the first movement created being changed to a receive of the entered qty in the new style //no need to find the difference in Qty from the original. First movement qty issued should be 0 if (popup.Shell.StyleID != originalShell.StyleID) { additionalmovement.Dimensions.Unit.ID = shell.DimensionsUnitID; additionalmovement.Dimensions.Quantity = shell.DimensionsQuantity; additionalmovement.Dimensions.Length = shell.DimensionsLength; additionalmovement.Dimensions.Width = shell.DimensionsWidth; additionalmovement.Dimensions.Height = shell.DimensionsHeight; additionalmovement.Dimensions.Weight = shell.DimensionsWeight; additionalmovement.Dimensions.Unit.HasHeight = shell.DimensionsHasHeight; additionalmovement.Dimensions.Unit.HasLength = shell.DimensionsHasLength; additionalmovement.Dimensions.Unit.HasWidth = shell.DimensionsHasWidth; additionalmovement.Dimensions.Unit.HasWeight = shell.DimensionsHasWeight; additionalmovement.Dimensions.Unit.HasQuantity = shell.DimensionsHasQuantity; additionalmovement.Dimensions.Unit.Code = shell.DimensionsUnitCode; additionalmovement.Dimensions.Unit.Description = shell.DimensionsUnitDescription; additionalmovement.Dimensions.Value = shell.DimensionsValue; additionalmovement.Dimensions.Unit.Formula = shell.DimensionsUnitFormula; additionalmovement.Dimensions.Unit.Format = shell.DimensionsUnitFormat; additionalmovement.Dimensions.UnitSize = shell.DimensionsUnitSize; additionalmovement.Product.ID = shell.ProductID; additionalmovement.Product.Code = shell.Code; additionalmovement.Product.Name = shell.Name; additionalmovement.Employee.ID = GlobalVariables.EmpID; additionalmovement.Employee.Name = GlobalVariables.EmpName; additionalmovement.Location.ID = openLocation.ID; additionalmovement.Location.Description = openLocation.Description; additionalmovement.Location.Code = openLocation.Code; additionalmovement.Job.ID = shell.JobID; additionalmovement.Job.JobNumber = shell.JobNumber; additionalmovement.Style.ID = originalShell.StyleID; additionalmovement.Style.Code = originalShell.StyleCode; additionalmovement.Style.Description = originalShell.Finish; additionalmovement.Notes = movement.Notes + " Also changed finish during stocktake"; additionalmovement.Issued = originalShell.Units; movementsToUpdate.Add(additionalmovement); movement.Issued = 0; movement.Received = popup.Shell.Units; movement.Notes = movement.Notes + " Also changed finish during stocktake"; } movementsToUpdate.Add(movement); shell.Color = stockHoldingCheckedColor; int index = stockHoldingShells.FindIndex(x => x.ID.Equals(shell.ID)); stockHoldingShells.RemoveAt(index); stockHoldingShells.Insert(index, shell); Device.BeginInvokeOnMainThread(() => { rightListView.ItemsSource = null; rightListView.ItemsSource = stockHoldingShells; }); }; Navigation.PushAsync(popup); } #endregion #region Utilities private async void FindHoldingNumber(StockLocationShell shell) { //TODO replace code that showed number of holdings //Task.Run(() => //{ // CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(shell.ID), // new Columns(x => x.Holdings)); // if (table.Rows.Any()) // { // List list = table.Rows.First().Values; // shell.NumberOfHoldings = "Holdings: " + list[0].ToString(); // int index = stockLocationShells.FindIndex(x => x.ID.Equals(shell.ID)); // stockLocationShells.RemoveAt(index); // stockLocationShells.Insert(index, shell); // Device.BeginInvokeOnMainThread(() => // { // leftListView.ItemsSource = null; // leftListView.ItemsSource = stockLocationShells; // }); // } //}); } private async void LoadLocation(Guid locationid) { using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading")) { CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(locationid), new Columns(x => x.Description, x => x.Code, x => x.Warehouse.Description, x => x.Area.Description, x => x.Job.JobNumber) ); if (table.Rows.Any()) { StockLocation location = table.Rows.First().ToObject(); StockLocationShell shell = new StockLocationShell() { ID = locationid, Description = location.Description, Warehouse = "Warehouse: " + location.Warehouse.Description, Area = "Area: " + location.Area.Description, JobNumber = "Job: " + location.Job.JobNumber, Code = location.Code, NumberOfHoldings = "Holdings: LOADING...", JobID = location.Job.ID, JobName = location.Job.Name }; stockLocationShells.Add(shell); Device.BeginInvokeOnMainThread(() => { leftListView.ItemsSource = null; leftListView.ItemsSource = stockLocationShells; }); FindHoldingNumber(shell); } } } private StockHoldingShell DuplicateShell(StockHoldingShell shell) { StockHoldingShell NewShell = new StockHoldingShell() { ID = shell.ID, Code = shell.Code, Name = shell.Name, DimensionsUnitID = shell.DimensionsUnitID, DimensionsQuantity = shell.DimensionsQuantity, DimensionsLength = shell.DimensionsLength, DimensionsWidth = shell.DimensionsWidth, DimensionsHeight = shell.DimensionsHeight, DimensionsWeight = shell.DimensionsWeight, DimensionsValue = shell.DimensionsValue, DimensionsHasHeight = shell.DimensionsHasHeight, DimensionsHasLength = shell.DimensionsHasLength, DimensionsHasQuantity = shell.DimensionsHasQuantity, DimensionsHasWidth = shell.DimensionsHasWidth, DimensionsHasWeight = shell.DimensionsHasWeight, DimensionsUnitCode = shell.DimensionsUnitCode, DimensionsUnitDescription = shell.DimensionsUnitDescription, DimensionsUnitFormat = shell.DimensionsUnitFormat, DimensionsUnitFormula = shell.DimensionsUnitFormula, DimensionsUnitSize = shell.DimensionsUnitSize, Finish = shell.Finish, Units = shell.Units, DisplayUnits = shell.DisplayUnits, JobID = shell.JobID, JobName = shell.JobName, JobNumber = shell.JobNumber, DisplayJob = shell.DisplayJob, DisplayFinish = shell.DisplayFinish, StyleID = shell.StyleID, StyleCode = shell.StyleCode, ProductID = shell.ProductID, DisplaySize = shell.DisplaySize, LastRowHeight = shell.LastRowHeight, ImageID = shell.ImageID, ImageSource = shell.ImageSource, ImageVisible = shell.ImageVisible, Color = shell.Color }; return NewShell; } private void LoadImage(StockHoldingShell shell) { CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(shell.ImageID)); if (table.Rows.Any()) { CoreRow docrow = table.Rows.FirstOrDefault(); if (docrow != null) { byte[] data = docrow.Get(x => x.Data); ImageSource src = ImageSource.FromStream(() => new MemoryStream(data)); if (src != null) { shell.ImageSource = src; shell.ImageVisible = true; if (DeviceType == "Tablet") { shell.LastRowHeight = 300; } else { shell.LastRowHeight = 150; } Device.BeginInvokeOnMainThread(() => { rightListView.ItemsSource = null; rightListView.ItemsSource = stockHoldingShells; }); } } } } private void LoadImages() { Task.Run(() => { foreach (StockHoldingShell shell in stockHoldingShells) { if (shell.ImageID != Guid.Empty) { LoadImage(shell); } } }); } private async void LoadHoldings(Filter _filter) { await Task.Run(() => { CoreTable table = new Client().Query ( _filter, new Columns ( x => x.ID, x => x.Product.Code, x => x.Product.Name, x => x.Style.Description, x => x.Units, x => x.Location.ID, x => x.Job.ID, x => x.Job.Name, x => x.Job.JobNumber, x => x.Style.ID, x => x.Style.Code, x => x.Product.ID, x => x.Product.Image.ID, x => x.Dimensions.UnitSize, x => x.Dimensions.Unit.ID, x => x.Dimensions.Quantity, x => x.Dimensions.Length, x => x.Dimensions.Width, x => x.Dimensions.Height, x => x.Dimensions.Weight, x => x.Dimensions.Value, x => x.Dimensions.Unit.HasQuantity, x => x.Dimensions.Unit.HasLength, x => x.Dimensions.Unit.HasWeight, x => x.Dimensions.Unit.HasWidth, x => x.Dimensions.Unit.HasHeight, x => x.Dimensions.Unit.Format, x => x.Dimensions.Unit.Formula ), null ); if (table.Rows.Any()) { foreach (CoreRow row in table.Rows) { StockHoldingShell shell = new StockHoldingShell(); shell.ID = row.Get(x => x.Location.ID).ToString() + row.Get(x => x.Product.ID).ToString() + row.Get(x => x.Job.ID).ToString() + row.Get(x => x.Style.ID).ToString() + row.Get(x => x.Dimensions.UnitSize); shell.Code = row.Get(x => x.Product.Code); shell.Name = row.Get(x => x.Product.Name); shell.Finish = row.Get(x => x.Style.Description); shell.DisplayFinish = "Finish: " + shell.Finish; shell.Units = row.Get(x => x.Units); shell.DimensionsUnitSize = row.Get(x => x.Dimensions.UnitSize); shell.DisplaySize = "UOM: " + shell.DimensionsUnitSize; shell.DisplayUnits = "Units: " + shell.Units; shell.JobID = row.Get(x => x.Job.ID); shell.JobName = row.Get(x => x.Job.Name); shell.JobNumber = row.Get(x => x.Job.JobNumber); shell.DisplayJob = "Job: " + shell.JobNumber; shell.StyleID = row.Get(x => x.Style.ID); shell.StyleCode = row.Get(x => x.Style.Code); shell.ProductID = row.Get(x => x.Product.ID); shell.ImageID = row.Get(x => x.Product.Image.ID); shell.DimensionsUnitID = row.Get(x => x.Dimensions.Unit.ID); shell.DimensionsQuantity = row.Get(x => x.Dimensions.Quantity); shell.DimensionsLength = row.Get(x => x.Dimensions.Length); shell.DimensionsWidth = row.Get(x => x.Dimensions.Width); shell.DimensionsHeight = row.Get(x => x.Dimensions.Height); shell.DimensionsWeight = row.Get(x => x.Dimensions.Weight); shell.DimensionsValue = row.Get(x => x.Dimensions.Value); shell.DimensionsHasHeight = row.Get(x => x.Dimensions.Unit.HasHeight); shell.DimensionsHasWeight = row.Get(x => x.Dimensions.Unit.HasWeight); shell.DimensionsHasLength = row.Get(x => x.Dimensions.Unit.HasLength); shell.DimensionsHasQuantity = row.Get(x => x.Dimensions.Unit.HasQuantity); shell.DimensionsHasWidth = row.Get(x => x.Dimensions.Unit.HasWidth); shell.DimensionsUnitFormat = row.Get(x => x.Dimensions.Unit.Format); shell.DimensionsUnitFormula = row.Get(x => x.Dimensions.Unit.Formula); if (!shell.Code.Contains("FREIGHT") && shell.Units != 0) { stockHoldingShells.Add(shell); } } LoadImages(); Device.BeginInvokeOnMainThread(() => { rightCountLbl.Text = "Items: " + stockHoldingShells.Count(); rightListView.ItemsSource = null; rightListView.ItemsSource = stockHoldingShells; }); } }); } #endregion } public class StockLocationShell { public Guid ID { get; set; } public string Description { get; set; } public string Code { get; set; } public string Warehouse { get; set; } public string Area { get; set; } public string NumberOfHoldings { get; set; } public string OpenCloseLocation { get; set; } public Color Color { get; set; } public string JobNumber { get; set; } public Guid JobID { get; set; } public String JobName { get; set; } public List PoItems { get; set; } public string NumberOfReceivedItems { get; set; } public StockLocationShell() { ID = Guid.Empty; Description = ""; Warehouse = ""; Area = ""; NumberOfHoldings = ""; OpenCloseLocation = "Open"; Color = Color.Default; JobNumber = ""; Code = ""; PoItems = new List(); NumberOfReceivedItems = ""; JobID = Guid.Empty; JobName = ""; } } }