using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Comal.Classes; using Comal.Classes.SecurityDescriptors; using InABox.Core; using InABox.Mobile; using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms.UI.Dialogs; using Result = ZXing.Result; namespace PRS.Mobile { public class HoldingUnitCalculator : AbstractConverter { public RequisitionItemModel RequisitionItems { get; set; } protected override double Convert(StockHoldingShell shell, object? parameter = null) { return Calculate(shell, RequisitionItems); } public static double Calculate(StockHoldingShell holding, RequisitionItemModel? items) { var instock = holding.Units; var picked = items?.Items.Where(x => x.LocationID == holding.LocationID && x.ProductID == holding.ProductID && x.StyleID == holding.StyleID && x.JobID == holding.JobID && x.DimensionsLength == holding.DimensionsLength && x.DimensionsWidth == holding.DimensionsWidth && x.DimensionsHeight == holding.DimensionsHeight && x.DimensionsWeight == holding.DimensionsWeight && holding.DimensionsQuantity == x.DimensionsQuantity) .Sum(x => x.ActualQuantity) ?? 0.0; return instock - picked; } } public class QuickPickViewModel : BindableObject { public bool HasItems { get; set; } public StockHoldingModel Holdings { get; private set; } public void RefreshHoldings(bool force, Guid barcode) { Holdings.Refresh(true, () => { IsLocation = Holdings.Items.Select(x => x.LocationID).All(x => x == barcode); IsProduct = Holdings.Items.Select(x => x.ProductID).All(x => x == barcode); }); } public bool IsLocation { get; private set; } public bool IsProduct { get; private set; } public string Title => IsLocation ? $"{Holdings.Items.FirstOrDefault()?.LocationCode} - {Holdings.Items.FirstOrDefault()?.LocationDescription}" : IsProduct ? $"{Holdings.Items.FirstOrDefault()?.ProductCode} - {Holdings.Items.FirstOrDefault()?.ProductName}" : "Scan Barcode"; public RequisitionItemModel RequisitionItems { get; private set; } public JobShell Job { get; set; } public QuickPickViewModel() { Holdings = new StockHoldingModel(App.Data, () => new Filter().None()); RequisitionItems = new RequisitionItemModel(App.Data, () => new Filter().None()); Job = App.Data.Jobs.CreateItem(); } } [XamlCompilation(XamlCompilationOptions.Compile)] public partial class QuickPickModule : MobilePage { public QuickPickModule() { InitializeComponent(); UnitCalculator.RequisitionItems = _viewModel.RequisitionItems; var options = new ZXing.Mobile.MobileBarcodeScanningOptions() { PossibleFormats = new List() { ZXing.BarcodeFormat.QR_CODE }, AutoRotate = false, TryInverted = true, TryHarder = true, }; _scanView.Options = options; _scanView.IsAnalyzing = false; _scanView.IsScanning = true; } protected override void OnAppearing() { base.OnAppearing(); try { _scanView.IsAnalyzing = _tabStrip.SelectedIndex == 0; } catch (Exception e) { InABox.Mobile.MobileLogging.Log(e); } } protected override void OnDisappearing() { try { _scanView.IsAnalyzing = false; } catch (Exception e) { InABox.Mobile.MobileLogging.Log(e); } base.OnDisappearing(); } private void _tabStrip_OnSelectionChanged(object sender, EventArgs e) { _scanView.IsAnalyzing = _tabStrip.SelectedIndex == 0; _viewModel.HasItems = _viewModel.RequisitionItems.Any(); } private bool bProcessing = false; private Guid _lastGuid = Guid.Empty; private async void ScanView_OnScanResult(ZXing.Result result) { if (bProcessing) return; if (!Guid.TryParse(result.Text, out Guid id) || id == Guid.Empty || id == _lastGuid) return; _lastGuid = id; bProcessing = true; using (await MaterialDialog.Instance.LoadingDialogAsync($"Processing...")) { Vibration.Vibrate(); SearchLocationID(id); bProcessing = false; } } private void SearchLocationID(Guid id) { //App.Data.StockLocations.Refresh(false); //var location = App.Data.StockLocations.FirstOrDefault(x => Guid.Equals(x.ID,id)); //if (location != null) //{ _viewModel.Holdings.Filter = () => new Filter(x => x.Location.ID).IsEqualTo(id).Or(x=>x.Product.ID).IsEqualTo(id); _viewModel.RefreshHoldings(true, id); //} } private void SelectFromHolding_Click(object sender, EventArgs e) { var holding = (sender as MobileCard)?.BindingContext as StockHoldingShell; if (holding == null) return; ShowPopup(() => { var qty = HoldingUnitCalculator.Calculate(holding, _viewModel.RequisitionItems); var editor = new SelectFromHoldingView(holding, qty, _viewModel.RequisitionItems); editor.OnSave += (o, args) => { if (!args.Quantity.IsEffectivelyEqual(0.0)) { var requiItem = _viewModel.RequisitionItems.CreateItem(); requiItem.LocationID = args.Holding.LocationID; requiItem.ProductID = args.Holding.ProductID; requiItem.ProductCode = args.Holding.ProductCode; requiItem.ProductName = args.Holding.ProductName; requiItem.StyleID = args.Holding.StyleID; requiItem.StyleCode = args.Holding.StyleCode; requiItem.StyleDescription = args.Holding.StyleDescription; requiItem.JobID = args.Holding.JobID; requiItem.DimensionsUnitID = args.Holding.DimensionsUnitID; requiItem.DimensionsHeight = args.Holding.DimensionsHeight; requiItem.DimensionsWidth = args.Holding.DimensionsWidth; requiItem.DimensionsLength = args.Holding.DimensionsLength; requiItem.DimensionsQuantity = args.Holding.DimensionsQuantity; requiItem.DimensionsWeight = args.Holding.DimensionsWeight; requiItem.DimensionsValue = args.Holding.DimensionsValue; requiItem.DimensionsUnitSize = args.Holding.DimensionsUnitSize; requiItem.ImageID = args.Holding.ImageID; if (args.Holding.Parent.Images.TryGetValue(args.Holding.ImageID, out var _image)) _viewModel.RequisitionItems.Images[args.Holding.ImageID] = _image; requiItem.SourceJRI = args.JobRequisitionItemID; requiItem.TargetJRI = args.JobRequisitionItemID; requiItem.Quantity = args.Quantity; requiItem.ActualQuantity = args.Quantity; requiItem.Cost = args.Holding.AverageCost; requiItem.Done = true; _viewModel.RequisitionItems.CommitItem(requiItem); _viewModel.Holdings.Refresh(false); DismissPopup(); } }; editor.OnCancel += (o, args) => DismissPopup(); return editor; }, new PopupManagerConfiguration() { RequestedHeight = Math.Min(700,500 + (holding.Allocations.Length * 55)) }); } private void SelectJob_Clicked(object sender, MobileButtonClickEventArgs args) { ShowPopup(() => SelectionView.Execute( (columns) => { columns.Add(new MobileGridTextColumn() { Column = x => x.JobNumber, Width = GridLength.Auto, Caption = "#", Alignment = TextAlignment.Center }); columns.Add(new MobileGridTextColumn() { Column = x => x.Name, Width = GridLength.Star, Caption = "Select Job", Alignment = TextAlignment.Start }); }, (refresh) => App.Data.Jobs.Refresh(false), (jobs) => { _viewModel.Job = jobs.FirstOrDefault() ?? App.Data.Jobs.CreateItem(); DismissPopup(); })); } private async void SaveList_Click(object sender, MobileMenuButtonClickedEventArgs args) { if (_viewModel.Job.ID == Guid.Empty && !Security.IsAllowed()) { await MaterialDialog.Instance.AlertAsync("Please select a Job before saving!", "Error"); return; } using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Saving")) { try { var _requiModel = new RequisitionModel(App.Data, () => new Filter().None()); var requi = _requiModel.CreateItem(); requi.Title = $"Quick Pick by {App.Data.Me.Name}"; requi.RequestedByID = App.Data.Me.ID; requi.Due = DateTime.Now; requi.JobID = _viewModel.Job.ID; _requiModel.CommitItem(requi); _requiModel.Save("Requisition Created from Mobile Quick Pick Module"); foreach (var _item in _viewModel.RequisitionItems.Items) { if (_viewModel.Job.ID != _item.JobID) _item.TargetJRI = Guid.Empty; // This should stop the multi-transaction thingy if returning items to stock if (_item.Quantity.IsEffectivelyLessThan(0.0)) _item.JobID = requi.JobID; _item.RequisitionID = requi.ID; } _viewModel.RequisitionItems.Save("Created from Mobile Quick Pick Module"); requi.Filled = requi.Due; requi.StockUpdated = requi.Filled; _requiModel.Save("Stock Movements created from Mobile Quick Pick Module"); } catch (Exception e) { InABox.Mobile.MobileLogging.Log(e); } } Navigation.PopAsync(); } private async void DeleteRequisitionItem(object sender, MobileMenuButtonClickedEventArgs args) { var shell = ((sender as MobileMenuButton)?.BindingContext as RequisitionItemShell); if (shell == null) return; if (await MaterialDialog.Instance.ConfirmAsync("Delete Picked Item?", "Confirm") == true) { _viewModel.RequisitionItems.DeleteItem(shell); } } private void AddHolding_Clicked(object sender, MobileMenuButtonClickedEventArgs args) { var first = _viewModel.Holdings.FirstOrDefault(); if (first == null) return; var holding = _viewModel.Holdings.CreateItem(); holding.LocationID = first.LocationID; holding.LocationCode = first.LocationCode; holding.LocationDescription = first.LocationDescription; var transaction = new StockTransaction(StockTransactionType.StockTake, holding, holding); transaction.Allocations = new StockTransactionAllocation[] { new StockTransactionAllocation() { ID = Guid.Empty, Description = "General Stock", Quantity = 0.0F, Maximum = double.MaxValue } }; var popup = new StockTakeEdit(transaction); popup.TransactionSaved += (o,e) => { holding.ProductID = transaction.ProductID; holding.ProductCode = transaction.ProductCode; holding.ProductName = transaction.ProductName; holding.DimensionsUnitID = transaction.DimensionsUnitID; holding.DimensionsHeight = transaction.DimensionsHeight; holding.DimensionsWidth = transaction.DimensionsWidth; holding.DimensionsLength = transaction.DimensionsLength; holding.DimensionsQuantity = transaction.DimensionsQuantity; holding.DimensionsWeight = transaction.DimensionsWeight; holding.DimensionsValue = transaction.DimensionsValue; holding.DimensionsUnitSize = transaction.DimensionsUnitSize; holding.StyleID = transaction.Target.StyleID; holding.StyleCode = transaction.Target.StyleCode; holding.StyleDescription = transaction.Target.StyleDescription; holding.JobID = transaction.Target.JobID; holding.JobNumber = transaction.Target.JobNumber; holding.JobName = transaction.Target.JobName; holding.AverageCost = transaction.Cost; //holding.Units = transaction.Quantity; _viewModel.Holdings.CommitItem(holding); var requiItem = _viewModel.RequisitionItems.CreateItem(); requiItem.LocationID = holding.LocationID; requiItem.ProductID = holding.ProductID; requiItem.ProductCode = holding.ProductCode; requiItem.ProductName = holding.ProductName; requiItem.StyleID = holding.StyleID; requiItem.StyleCode = holding.StyleCode; requiItem.StyleDescription = holding.StyleDescription; requiItem.JobID = holding.JobID; requiItem.DimensionsUnitID = holding.DimensionsUnitID; requiItem.DimensionsHeight = holding.DimensionsHeight; requiItem.DimensionsWidth = holding.DimensionsWidth; requiItem.DimensionsLength = holding.DimensionsLength; requiItem.DimensionsQuantity = holding.DimensionsQuantity; requiItem.DimensionsWeight = holding.DimensionsWeight; requiItem.DimensionsValue = holding.DimensionsValue; requiItem.DimensionsUnitSize = holding.DimensionsUnitSize; requiItem.ImageID = holding.ImageID; if (holding.Parent.Images.TryGetValue(holding.ImageID, out var _image)) _viewModel.RequisitionItems.Images[holding.ImageID] = _image; requiItem.SourceJRI = Guid.Empty; requiItem.TargetJRI = Guid.Empty; requiItem.Quantity = 0 - transaction.Quantity; requiItem.ActualQuantity = 0 - transaction.Quantity; requiItem.Cost = transaction.Cost; requiItem.Done = true; _viewModel.RequisitionItems.CommitItem(requiItem); _viewModel.Holdings.Refresh(false); DismissPopup(); }; Navigation.PushAsync(popup); } } }