QuickPickModule.xaml.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Comal.Classes;
  7. using Comal.Classes.SecurityDescriptors;
  8. using InABox.Core;
  9. using InABox.Mobile;
  10. using Xamarin.Essentials;
  11. using Xamarin.Forms;
  12. using Xamarin.Forms.Xaml;
  13. using XF.Material.Forms.UI.Dialogs;
  14. using Result = ZXing.Result;
  15. namespace PRS.Mobile
  16. {
  17. public class HoldingUnitCalculator : AbstractConverter<StockHoldingShell, double>
  18. {
  19. public RequisitionItemModel RequisitionItems { get; set; }
  20. protected override double Convert(StockHoldingShell shell, object? parameter = null)
  21. {
  22. return Calculate(shell, RequisitionItems);
  23. }
  24. public static double Calculate(StockHoldingShell holding, RequisitionItemModel? items)
  25. {
  26. var instock = holding.Units;
  27. var picked = items?.Items.Where(x =>
  28. x.LocationID == holding.LocationID
  29. && x.ProductID == holding.ProductID
  30. && x.StyleID == holding.StyleID
  31. && x.JobID == holding.JobID
  32. && x.DimensionsLength == holding.DimensionsLength
  33. && x.DimensionsWidth == holding.DimensionsWidth
  34. && x.DimensionsHeight == holding.DimensionsHeight
  35. && x.DimensionsWeight == holding.DimensionsWeight
  36. && holding.DimensionsQuantity == x.DimensionsQuantity)
  37. .Sum(x => x.ActualQuantity) ?? 0.0;
  38. return instock - picked;
  39. }
  40. }
  41. public class QuickPickViewModel : BindableObject
  42. {
  43. public bool HasItems { get; set; }
  44. public StockHoldingModel Holdings { get; private set; }
  45. public void RefreshHoldings(bool force, Guid barcode)
  46. {
  47. Holdings.Refresh(true, () =>
  48. {
  49. IsLocation = Holdings.Items.Select(x => x.LocationID).All(x => x == barcode);
  50. IsProduct = Holdings.Items.Select(x => x.ProductID).All(x => x == barcode);
  51. });
  52. }
  53. public bool IsLocation { get; private set; }
  54. public bool IsProduct { get; private set; }
  55. public string Title => IsLocation
  56. ? $"{Holdings.Items.FirstOrDefault()?.LocationCode} - {Holdings.Items.FirstOrDefault()?.LocationDescription}"
  57. : IsProduct
  58. ? $"{Holdings.Items.FirstOrDefault()?.ProductCode} - {Holdings.Items.FirstOrDefault()?.ProductName}"
  59. : "Scan Barcode";
  60. public RequisitionItemModel RequisitionItems { get; private set; }
  61. public JobShell Job { get; set; }
  62. public QuickPickViewModel()
  63. {
  64. Holdings = new StockHoldingModel(App.Data, () => new Filter<StockHolding>().None());
  65. RequisitionItems = new RequisitionItemModel(App.Data, () => new Filter<RequisitionItem>().None());
  66. Job = App.Data.Jobs.CreateItem();
  67. }
  68. }
  69. [XamlCompilation(XamlCompilationOptions.Compile)]
  70. public partial class QuickPickModule : MobilePage
  71. {
  72. public QuickPickModule()
  73. {
  74. InitializeComponent();
  75. UnitCalculator.RequisitionItems = _viewModel.RequisitionItems;
  76. var options = new ZXing.Mobile.MobileBarcodeScanningOptions()
  77. {
  78. PossibleFormats = new List<ZXing.BarcodeFormat>() { ZXing.BarcodeFormat.QR_CODE },
  79. AutoRotate = false,
  80. TryInverted = true,
  81. TryHarder = true,
  82. };
  83. _scanView.Options = options;
  84. _scanView.IsAnalyzing = false;
  85. _scanView.IsScanning = true;
  86. }
  87. protected override void OnAppearing()
  88. {
  89. base.OnAppearing();
  90. try
  91. {
  92. _scanView.IsAnalyzing = _tabStrip.SelectedIndex == 0;
  93. }
  94. catch (Exception e)
  95. {
  96. InABox.Mobile.MobileLogging.Log(e);
  97. }
  98. }
  99. protected override void OnDisappearing()
  100. {
  101. try
  102. {
  103. _scanView.IsAnalyzing = false;
  104. }
  105. catch (Exception e)
  106. {
  107. InABox.Mobile.MobileLogging.Log(e);
  108. }
  109. base.OnDisappearing();
  110. }
  111. private void _tabStrip_OnSelectionChanged(object sender, EventArgs e)
  112. {
  113. _scanView.IsAnalyzing = _tabStrip.SelectedIndex == 0;
  114. _viewModel.HasItems = _viewModel.RequisitionItems.Any();
  115. }
  116. private bool bProcessing = false;
  117. private Guid _lastGuid = Guid.Empty;
  118. private async void ScanView_OnScanResult(ZXing.Result result)
  119. {
  120. if (bProcessing)
  121. return;
  122. if (!Guid.TryParse(result.Text, out Guid id) || id == Guid.Empty || id == _lastGuid)
  123. return;
  124. _lastGuid = id;
  125. bProcessing = true;
  126. using (await MaterialDialog.Instance.LoadingDialogAsync($"Processing..."))
  127. {
  128. Vibration.Vibrate();
  129. SearchLocationID(id);
  130. bProcessing = false;
  131. }
  132. }
  133. private void SearchLocationID(Guid id)
  134. {
  135. //App.Data.StockLocations.Refresh(false);
  136. //var location = App.Data.StockLocations.FirstOrDefault(x => Guid.Equals(x.ID,id));
  137. //if (location != null)
  138. //{
  139. _viewModel.Holdings.Filter = () =>
  140. new Filter<StockHolding>(x => x.Location.ID).IsEqualTo(id).Or(x=>x.Product.ID).IsEqualTo(id);
  141. _viewModel.RefreshHoldings(true, id);
  142. //}
  143. }
  144. private void SelectFromHolding_Click(object sender, EventArgs e)
  145. {
  146. var holding = (sender as MobileCard)?.BindingContext as StockHoldingShell;
  147. if (holding == null)
  148. return;
  149. ShowPopup(() =>
  150. {
  151. var qty = HoldingUnitCalculator.Calculate(holding, _viewModel.RequisitionItems);
  152. var editor = new SelectFromHoldingView(holding, qty, _viewModel.RequisitionItems);
  153. editor.OnSave += (o, args) =>
  154. {
  155. if (!args.Quantity.IsEffectivelyEqual(0.0))
  156. {
  157. var requiItem = _viewModel.RequisitionItems.CreateItem();
  158. requiItem.LocationID = args.Holding.LocationID;
  159. requiItem.ProductID = args.Holding.ProductID;
  160. requiItem.ProductCode = args.Holding.ProductCode;
  161. requiItem.ProductName = args.Holding.ProductName;
  162. requiItem.StyleID = args.Holding.StyleID;
  163. requiItem.StyleCode = args.Holding.StyleCode;
  164. requiItem.StyleDescription = args.Holding.StyleDescription;
  165. requiItem.JobID = args.Holding.JobID;
  166. requiItem.DimensionsUnitID = args.Holding.DimensionsUnitID;
  167. requiItem.DimensionsHeight = args.Holding.DimensionsHeight;
  168. requiItem.DimensionsWidth = args.Holding.DimensionsWidth;
  169. requiItem.DimensionsLength = args.Holding.DimensionsLength;
  170. requiItem.DimensionsQuantity = args.Holding.DimensionsQuantity;
  171. requiItem.DimensionsWeight = args.Holding.DimensionsWeight;
  172. requiItem.DimensionsValue = args.Holding.DimensionsValue;
  173. requiItem.DimensionsUnitSize = args.Holding.DimensionsUnitSize;
  174. requiItem.ImageID = args.Holding.ImageID;
  175. if (args.Holding.Parent.Images.TryGetValue(args.Holding.ImageID, out var _image))
  176. _viewModel.RequisitionItems.Images[args.Holding.ImageID] = _image;
  177. requiItem.SourceJRI = args.JobRequisitionItemID;
  178. requiItem.TargetJRI = args.JobRequisitionItemID;
  179. requiItem.Quantity = args.Quantity;
  180. requiItem.ActualQuantity = args.Quantity;
  181. requiItem.Cost = args.Holding.AverageCost;
  182. requiItem.Done = true;
  183. _viewModel.RequisitionItems.CommitItem(requiItem);
  184. _viewModel.Holdings.Refresh(false);
  185. DismissPopup();
  186. }
  187. };
  188. editor.OnCancel += (o, args) => DismissPopup();
  189. return editor;
  190. },
  191. new PopupManagerConfiguration()
  192. {
  193. RequestedHeight = Math.Min(700,500 + (holding.Allocations.Length * 55))
  194. });
  195. }
  196. private void SelectJob_Clicked(object sender, MobileButtonClickEventArgs args)
  197. {
  198. ShowPopup(() => SelectionView.Execute<JobShell>(
  199. (columns) =>
  200. {
  201. columns.Add(new MobileGridTextColumn<JobShell>()
  202. {
  203. Column = x => x.JobNumber,
  204. Width = GridLength.Auto,
  205. Caption = "#",
  206. Alignment = TextAlignment.Center
  207. });
  208. columns.Add(new MobileGridTextColumn<JobShell>()
  209. {
  210. Column = x => x.Name,
  211. Width = GridLength.Star,
  212. Caption = "Select Job",
  213. Alignment = TextAlignment.Start
  214. });
  215. },
  216. (refresh) => App.Data.Jobs.Refresh(false),
  217. (jobs) =>
  218. {
  219. _viewModel.Job = jobs.FirstOrDefault() ?? App.Data.Jobs.CreateItem();
  220. DismissPopup();
  221. }));
  222. }
  223. private async void SaveList_Click(object sender, MobileMenuButtonClickedEventArgs args)
  224. {
  225. if (_viewModel.Job.ID == Guid.Empty && !Security.IsAllowed<AllowMobileQuickPickWithoutJob>())
  226. {
  227. await MaterialDialog.Instance.AlertAsync("Please select a Job before saving!", "Error");
  228. return;
  229. }
  230. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Saving"))
  231. {
  232. try
  233. {
  234. var _requiModel = new RequisitionModel(App.Data, () => new Filter<Requisition>().None());
  235. var requi = _requiModel.CreateItem();
  236. requi.Title = $"Quick Pick by {App.Data.Me.Name}";
  237. requi.RequestedByID = App.Data.Me.ID;
  238. requi.Due = DateTime.Now;
  239. requi.JobID = _viewModel.Job.ID;
  240. _requiModel.CommitItem(requi);
  241. _requiModel.Save("Requisition Created from Mobile Quick Pick Module");
  242. foreach (var _item in _viewModel.RequisitionItems.Items)
  243. {
  244. if (_viewModel.Job.ID != _item.JobID)
  245. _item.TargetJRI = Guid.Empty;
  246. // This should stop the multi-transaction thingy if returning items to stock
  247. if (_item.Quantity.IsEffectivelyLessThan(0.0))
  248. _item.JobID = requi.JobID;
  249. _item.RequisitionID = requi.ID;
  250. }
  251. _viewModel.RequisitionItems.Save("Created from Mobile Quick Pick Module");
  252. requi.Filled = requi.Due;
  253. requi.StockUpdated = requi.Filled;
  254. _requiModel.Save("Stock Movements created from Mobile Quick Pick Module");
  255. }
  256. catch (Exception e)
  257. {
  258. InABox.Mobile.MobileLogging.Log(e);
  259. }
  260. }
  261. Navigation.PopAsync();
  262. }
  263. private async void DeleteRequisitionItem(object sender, MobileMenuButtonClickedEventArgs args)
  264. {
  265. var shell = ((sender as MobileMenuButton)?.BindingContext as RequisitionItemShell);
  266. if (shell == null)
  267. return;
  268. if (await MaterialDialog.Instance.ConfirmAsync("Delete Picked Item?", "Confirm") == true)
  269. {
  270. _viewModel.RequisitionItems.DeleteItem(shell);
  271. }
  272. }
  273. private void AddHolding_Clicked(object sender, MobileMenuButtonClickedEventArgs args)
  274. {
  275. var first = _viewModel.Holdings.FirstOrDefault();
  276. if (first == null)
  277. return;
  278. var holding = _viewModel.Holdings.CreateItem();
  279. holding.LocationID = first.LocationID;
  280. holding.LocationCode = first.LocationCode;
  281. holding.LocationDescription = first.LocationDescription;
  282. var transaction = new StockTransaction(StockTransactionType.StockTake, holding, holding);
  283. transaction.Allocations = new StockTransactionAllocation[]
  284. {
  285. new StockTransactionAllocation()
  286. {
  287. ID = Guid.Empty,
  288. Description = "General Stock",
  289. Quantity = 0.0F,
  290. Maximum = double.MaxValue
  291. }
  292. };
  293. var popup = new StockTakeEdit(transaction);
  294. popup.TransactionSaved += (o,e) =>
  295. {
  296. holding.ProductID = transaction.ProductID;
  297. holding.ProductCode = transaction.ProductCode;
  298. holding.ProductName = transaction.ProductName;
  299. holding.DimensionsUnitID = transaction.DimensionsUnitID;
  300. holding.DimensionsHeight = transaction.DimensionsHeight;
  301. holding.DimensionsWidth = transaction.DimensionsWidth;
  302. holding.DimensionsLength = transaction.DimensionsLength;
  303. holding.DimensionsQuantity = transaction.DimensionsQuantity;
  304. holding.DimensionsWeight = transaction.DimensionsWeight;
  305. holding.DimensionsValue = transaction.DimensionsValue;
  306. holding.DimensionsUnitSize = transaction.DimensionsUnitSize;
  307. holding.StyleID = transaction.Target.StyleID;
  308. holding.StyleCode = transaction.Target.StyleCode;
  309. holding.StyleDescription = transaction.Target.StyleDescription;
  310. holding.JobID = transaction.Target.JobID;
  311. holding.JobNumber = transaction.Target.JobNumber;
  312. holding.JobName = transaction.Target.JobName;
  313. holding.AverageCost = transaction.Cost;
  314. //holding.Units = transaction.Quantity;
  315. _viewModel.Holdings.CommitItem(holding);
  316. var requiItem = _viewModel.RequisitionItems.CreateItem();
  317. requiItem.LocationID = holding.LocationID;
  318. requiItem.ProductID = holding.ProductID;
  319. requiItem.ProductCode = holding.ProductCode;
  320. requiItem.ProductName = holding.ProductName;
  321. requiItem.StyleID = holding.StyleID;
  322. requiItem.StyleCode = holding.StyleCode;
  323. requiItem.StyleDescription = holding.StyleDescription;
  324. requiItem.JobID = holding.JobID;
  325. requiItem.DimensionsUnitID = holding.DimensionsUnitID;
  326. requiItem.DimensionsHeight = holding.DimensionsHeight;
  327. requiItem.DimensionsWidth = holding.DimensionsWidth;
  328. requiItem.DimensionsLength = holding.DimensionsLength;
  329. requiItem.DimensionsQuantity = holding.DimensionsQuantity;
  330. requiItem.DimensionsWeight = holding.DimensionsWeight;
  331. requiItem.DimensionsValue = holding.DimensionsValue;
  332. requiItem.DimensionsUnitSize = holding.DimensionsUnitSize;
  333. requiItem.ImageID = holding.ImageID;
  334. if (holding.Parent.Images.TryGetValue(holding.ImageID, out var _image))
  335. _viewModel.RequisitionItems.Images[holding.ImageID] = _image;
  336. requiItem.SourceJRI = Guid.Empty;
  337. requiItem.TargetJRI = Guid.Empty;
  338. requiItem.Quantity = 0 - transaction.Quantity;
  339. requiItem.ActualQuantity = 0 - transaction.Quantity;
  340. requiItem.Cost = transaction.Cost;
  341. requiItem.Done = true;
  342. _viewModel.RequisitionItems.CommitItem(requiItem);
  343. _viewModel.Holdings.Refresh(false);
  344. DismissPopup();
  345. };
  346. Navigation.PushAsync(popup);
  347. }
  348. }
  349. }