RequisitionPanel.xaml.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using Comal.Classes;
  9. using InABox.Clients;
  10. using InABox.Core;
  11. using InABox.DynamicGrid;
  12. using InABox.WPF;
  13. using Motorola.Snapi;
  14. using Motorola.Snapi.Constants.Enums;
  15. using Motorola.Snapi.EventArguments;
  16. //using RestSharp;
  17. namespace PRSDesktop
  18. {
  19. /// <summary>
  20. /// Interaction logic for RequisitionPanel.xaml
  21. /// </summary>
  22. public partial class RequisitionPanel : UserControl, IPanel<Requisition>
  23. {
  24. private Requisition? _requisition;
  25. public List<IMotorolaBarcodeScanner> Scanners = new();
  26. public RequisitionPanel()
  27. {
  28. InitializeComponent();
  29. PickImage.Source = PRSDesktop.Resources.tick.AsBitmapImage(Color.White);
  30. StockImage.Source = PRSDesktop.Resources.forklift.AsBitmapImage(Color.White);
  31. TruckImage.Source = PRSDesktop.Resources.truck.AsBitmapImage();
  32. }
  33. //DateTime lastselection = DateTime.MaxValue;
  34. //DispatcherTimer timer = new DispatcherTimer();
  35. public event DataModelUpdateEvent? OnUpdateDataModel;
  36. public bool IsReady { get; set; }
  37. public Dictionary<string, object[]> Selected()
  38. {
  39. return new Dictionary<string, object[]>
  40. {
  41. { typeof(Requisition).EntityName(), Requisitions.SelectedRows },
  42. { typeof(RequisitionItem).EntityName(), Items.SelectedRows }
  43. };
  44. }
  45. public void Setup()
  46. {
  47. //Requisitions.OnSelectItem += Requisitions_OnSelectItem;
  48. //Requisitions.OnRequisitionFillStateChanged += Requisitions_OnRequisitionFillStateChanged;
  49. //Requisitions.OnRequisitionBoxesChanged += Requisitions_OnRequisitionBoxesChanged;
  50. UnPickedItems.Picked = false;
  51. UnPickedItems.Refresh(true, true);
  52. SetupScanner();
  53. Items.Reconfigure(options =>
  54. {
  55. options.BeginUpdate();
  56. if (_requisition != null)
  57. {
  58. if (_requisition.Filled.IsEmpty())
  59. {
  60. options.Add(DynamicGridOption.AddRows);
  61. options.Add(DynamicGridOption.DeleteRows);
  62. }
  63. else
  64. {
  65. options.Remove(DynamicGridOption.AddRows);
  66. options.Remove(DynamicGridOption.DeleteRows);
  67. }
  68. }
  69. options.EndUpdate();
  70. });
  71. Requisitions.Refresh(true, false);
  72. Items.Refresh(true, false);
  73. UpdateLayout();
  74. }
  75. public void Shutdown(CancelEventArgs? cancel)
  76. {
  77. ShutdownScanner();
  78. }
  79. public void CreateToolbarButtons(IPanelHost host)
  80. {
  81. //host.CreatePanelAction(new PanelAction() { Caption = "Archive Requisition", Image = PRSDesktop.Resources.delete, OnExecute = ArchiveRequisition });
  82. }
  83. public string SectionName => "Requisitions";
  84. public DataModel DataModel(Selection selection)
  85. {
  86. var ids = Requisitions.ExtractValues(x => x.ID, selection).ToArray();
  87. return new BaseDataModel<Requisition>(new Filter<Requisition>(x => x.ID).InList(ids));
  88. }
  89. public void Refresh()
  90. {
  91. Requisitions.Refresh(false, true);
  92. //lastselection = DateTime.MinValue;
  93. //Items.Refresh(true, false);
  94. }
  95. public void Heartbeat(TimeSpan time)
  96. {
  97. // Nothing to do here
  98. }
  99. private void ShutdownScanner()
  100. {
  101. try
  102. {
  103. foreach (var scanner in Scanners) scanner.Actions.ToggleLed(LedMode.GreenOff);
  104. BarcodeScannerManager.Instance.DataReceived -= Instance_DataReceived;
  105. BarcodeScannerManager.Instance.Close();
  106. }
  107. catch (Exception e)
  108. {
  109. MessageBox.Show("Error Shutting down Scanner!\n\n" + e.Message);
  110. }
  111. }
  112. private void SetupScanner()
  113. {
  114. Scanners.Clear();
  115. BarcodeScannerManager.Instance.Open();
  116. BarcodeScannerManager.Instance.RegisterForEvents(EventType.Barcode, EventType.Pnp, EventType.Image, EventType.Other, EventType.Rmd);
  117. BarcodeScannerManager.Instance.GetDevices();
  118. foreach (var scanner in BarcodeScannerManager.Instance.GetDevices())
  119. {
  120. Scanners.Add(scanner);
  121. scanner.Actions.ToggleLed(LedMode.RedOn);
  122. scanner.Actions.SoundBeeper(BeepPattern.FastWarble);
  123. }
  124. BarcodeScannerManager.Instance.DataReceived += Instance_DataReceived;
  125. }
  126. private void Instance_DataReceived(object? sender, BarcodeScanEventArgs e)
  127. {
  128. Dispatcher.Invoke(() => { ProcessCode(Scanners[(int)e.ScannerId], e.Data); });
  129. }
  130. private void ProcessCode(IMotorolaBarcodeScanner scanner, string code)
  131. {
  132. try
  133. {
  134. var iRow = Requisitions.SelectedRows.First().Index;
  135. if (iRow == -1)
  136. throw new Exception("Please select a Requsition First");
  137. var row = Requisitions.Data.Rows[iRow];
  138. var filled = row.Get<Requisition, DateTime>(x => x.Filled);
  139. if (!filled.IsEmpty())
  140. throw new Exception("Cannot Add Items to a completed Requisition");
  141. var reqid = row.Get<Requisition, Guid>(x => x.ID);
  142. var boxes = row.Get<Requisition, int>(x => x.Boxes);
  143. var sCode = code;
  144. var iQty = 1;
  145. if (sCode.Contains('*'))
  146. {
  147. var comps = sCode.Split('*');
  148. sCode = comps[0];
  149. iQty = int.Parse(comps[1].Trim());
  150. }
  151. RequisitionItem? item = null;
  152. CoreRow? itemrow = null;
  153. try
  154. {
  155. itemrow = Items.Data.Rows.FirstOrDefault(r => /* r.Get<RequisitionItem, int>(x => x.BoxNumber).Equals(boxes) && */
  156. r.Get<RequisitionItem, string>(x => x.BarCode).Equals(sCode));
  157. //itemrow = Items.Data.Rows.FirstOrDefault(r => r.Get<RequisitionItem, String>(x => x.BarCode).Equals(sCode));
  158. }
  159. catch (Exception e)
  160. {
  161. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  162. }
  163. if (itemrow != null)
  164. item = new Client<RequisitionItem>()
  165. .Load(new Filter<RequisitionItem>(x => x.ID).IsEqualTo(itemrow.Get<RequisitionItem, Guid>(x => x.ID))).FirstOrDefault();
  166. if (item != null)
  167. {
  168. item.Quantity += iQty;
  169. new Client<RequisitionItem>().Save(item, "Quantity Updated by Barcode Scanner");
  170. scanner?.Actions.SoundBeeper(BeepPattern.LowHigh);
  171. Refresh();
  172. }
  173. else
  174. {
  175. var product = new Client<Product>().Load(new Filter<Product>(x => x.Code).IsEqualTo(sCode)).FirstOrDefault();
  176. if (product != null)
  177. {
  178. item = new RequisitionItem
  179. {
  180. RequisitionLink = new RequisitionLink { ID = reqid },
  181. //BoxNumber = boxes,
  182. Code = product.Code,
  183. Description = product.Name,
  184. BarCode = sCode,
  185. Quantity = iQty
  186. };
  187. new Client<RequisitionItem>().Save(item, "Scanned by Barcode Reader");
  188. scanner?.Actions.SoundBeeper(BeepPattern.LowHigh);
  189. Refresh();
  190. }
  191. else
  192. {
  193. scanner?.Actions.SoundBeeper(BeepPattern.FourLowLong);
  194. }
  195. }
  196. }
  197. catch (Exception)
  198. {
  199. scanner?.Actions.SoundBeeper(BeepPattern.FourLowShort);
  200. }
  201. }
  202. /*private void ArchiveRequisition(PanelAction obj)
  203. {
  204. var bClosed = false;
  205. var iRow = Requisitions.SelectedRows.First().Index;
  206. if (iRow > -1)
  207. {
  208. var row = Requisitions.Data.Rows[iRow];
  209. var id = row.Get<Requisition, Guid>(x => x.ID);
  210. var filled = row.Get<Requisition, DateTime>(x => x.Filled);
  211. if (filled.IsEmpty())
  212. {
  213. MessageBox.Show("Please complete this requisition before Archiving it!");
  214. }
  215. else
  216. {
  217. var req = new Client<Requisition>().Load(new Filter<Requisition>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  218. if (req != null)
  219. {
  220. req.Archived = DateTime.Now;
  221. new Client<Requisition>().Save(req, "Requisition Archived");
  222. bClosed = true;
  223. }
  224. }
  225. }
  226. else
  227. {
  228. MessageBox.Show("Please select a requisition first!");
  229. }
  230. if (bClosed)
  231. Refresh();
  232. }*/
  233. public Dictionary<Type, CoreTable> DataEnvironment()
  234. {
  235. return new Dictionary<Type, CoreTable>
  236. {
  237. [typeof(Requisition)] = Requisitions.Data,
  238. [typeof(RequisitionItem)] = Items.Data
  239. };
  240. }
  241. //private void Timer_Tick(object sender, EventArgs e)
  242. //{
  243. // if (lastselection < DateTime.Now.AddMilliseconds(-500))
  244. // {
  245. // lastselection = DateTime.MaxValue;
  246. // LoadRequisition();
  247. // }
  248. //}
  249. private void LoadRequisition()
  250. {
  251. foreach (var scanner in Scanners)
  252. scanner.Actions.ToggleLed(_requisition != null ? LedMode.GreenOn : LedMode.RedOn);
  253. Title.Text = _requisition != null ? _requisition.Title : "";
  254. RequestedBy.Content = _requisition != null ? _requisition.RequestedBy.Name : "";
  255. DueDate.Content = _requisition != null ? string.Format("{0:dddd, dd MMM yyyy}", _requisition.Due) : "";
  256. var notes = _requisition != null ? _requisition.Notes : Array.Empty<string>();
  257. var request = _requisition != null ? CoreUtils.StripHTML(_requisition.Request) : "";
  258. Request.Text = string.Join("\n===============================\n", Utility.ProcessNotes(notes, request));
  259. MarkAsFilled.IsEnabled = _requisition != null && _requisition.Archived.IsEmpty() && _requisition.StockUpdated.IsEmpty();
  260. MarkAsFilledDescription.Content = _requisition == null || _requisition.Filled.IsEmpty() ? "Mark As Filled" : "Clear Filled Flag";
  261. UpdateStock.IsEnabled = Security.IsAllowed<CanUpdateRequisitionStockMovements>() && _requisition != null &&
  262. !_requisition.Filled.IsEmpty();
  263. UpdateStockDescription.Content =
  264. _requisition == null || _requisition.StockUpdated.IsEmpty() ? "Update Stock Holdings" : "Clear Stock Movements";
  265. TakenBy.IsEnabled = _requisition != null && !_requisition.Filled.IsEmpty() && _requisition.Archived.IsEmpty() &&
  266. !_requisition.Delivery.IsValid();
  267. TakenByDescription.Content = _requisition == null
  268. ? "Select Employee"
  269. : _requisition.Delivery.IsValid()
  270. ? _requisition.Delivery.Completed.IsEmpty()
  271. ? string.Format("Booked On Delivery #{0}", _requisition.Delivery.Number)
  272. : string.Format("Delivered on {0:dd MMM yy} (#{1})", _requisition.Delivery.Completed, _requisition.Delivery.Number)
  273. : _requisition.TakenBy.IsValid()
  274. ? string.Format("{0} ({1:dd MMM yy})", _requisition.TakenBy.Name, _requisition.Archived)
  275. : "Select Employee";
  276. }
  277. private void Requisitions_OnSelectItem(object sender, DynamicGridSelectionEventArgs e)
  278. {
  279. _requisition = e.Rows?.FirstOrDefault()?.ToObject<Requisition>();
  280. LoadRequisition();
  281. UnPickedItems.Requisition = _requisition;
  282. UnPickedItems.Refresh(false, true);
  283. Items.Requisition = _requisition;
  284. Items.Refresh(false, true);
  285. //lastselection = DateTime.Now;
  286. //Dispatcher.Invoke(() => { LoadRequisition(); });
  287. }
  288. private void TakenBy_Click(object sender, RoutedEventArgs e)
  289. {
  290. if (_requisition == null || _requisition.ID == Guid.Empty)
  291. return;
  292. var dlg = new MultiSelectDialog<Employee>(
  293. LookupFactory.DefineFilter<Employee>(),
  294. LookupFactory.DefineColumns<Employee>(),
  295. false);
  296. if (!dlg.ShowDialog())
  297. return;
  298. if (!_requisition.Filled.IsEmpty() && !_requisition.StockUpdated.IsEmpty())
  299. if (MessageBox.Show("This will remove this requisition from this list.\nAre you sure you wish to continue?", "Close Requisition?",
  300. MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
  301. return;
  302. if (_requisition.Archived.IsEmpty())
  303. _requisition.Archived = DateTime.Now;
  304. var emp = dlg.Data()?.Rows.FirstOrDefault();
  305. _requisition.TakenBy.ID = emp != null ? emp.Get<Employee, Guid>(x => x.ID) : Guid.Empty;
  306. _requisition.TakenBy.Name = emp != null ? emp.Get<Employee, string>(x => x.Name) : "";
  307. Progress.Show("Updating Requisition Delivery Status");
  308. new Client<Requisition>().Save(_requisition, "Updated [TakenBy] Flag");
  309. if (!_requisition.Filled.IsEmpty() && !_requisition.StockUpdated.IsEmpty() && !_requisition.Archived.IsEmpty())
  310. {
  311. Refresh();
  312. }
  313. else
  314. {
  315. Requisitions.UpdateRow<Requisition, Guid>(Requisitions.SelectedRows.First(), x => x.TakenBy.ID, _requisition.TakenBy.ID, false);
  316. Requisitions.UpdateRow<Requisition, string>(Requisitions.SelectedRows.First(), x => x.TakenBy.Name, _requisition.TakenBy.Name, false);
  317. Requisitions.UpdateRow<Requisition, DateTime>(Requisitions.SelectedRows.First(), x => x.Archived, _requisition.Archived);
  318. LoadRequisition();
  319. }
  320. Progress.Close();
  321. }
  322. private void MarkAsFilled_Click(object sender, RoutedEventArgs e)
  323. {
  324. if (_requisition == null)
  325. {
  326. MessageBox.Show("Please select a Requisition first!");
  327. return;
  328. }
  329. DateTime filltime = DateTime.Now;
  330. var unpickeditems = Items.Data.Rows.Where(r =>
  331. _requisition.Filled.IsEmpty()
  332. && (r.Get<RequisitionItem, Guid>(x => x.Product.ID) != Guid.Empty)
  333. && (r.Get<RequisitionItem, bool>(x => x.Product.NonStock) != true)
  334. && (r.Get<RequisitionItem, DateTime>(x => x.Picked).IsEmpty())
  335. );
  336. if (unpickeditems.Any())
  337. {
  338. var confirm = MessageBox.Show("Unpicked items exist on this requisition!\n\nDo you want to mark them as picked now?",
  339. "Unpicked Items", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
  340. if (confirm == MessageBoxResult.Cancel)
  341. return;
  342. filltime = DateTime.Now;
  343. if (confirm == MessageBoxResult.Yes)
  344. {
  345. var updates = new List<RequisitionItem>();
  346. foreach (var row in unpickeditems)
  347. {
  348. var item = row.ToObject<RequisitionItem>();
  349. item.Picked = filltime;
  350. updates.Add(item);
  351. }
  352. new Client<RequisitionItem>().Save(updates, "Marked as Picked because Requisition was marked as filled");
  353. }
  354. }
  355. _requisition.Filled = _requisition.Filled.IsEmpty() ? filltime : DateTime.MinValue;
  356. Progress.Show(_requisition.Filled.IsEmpty() ? "Clearing Delivery Items" : "Creating Delivery Items");
  357. new Client<Requisition>().Save(_requisition, "Updated Filled Flag");
  358. Requisitions.UpdateRow<Requisition, DateTime>(Requisitions.SelectedRows.First(), x => x.Filled, _requisition.Filled);
  359. LoadRequisition();
  360. Items.Refresh(false, true);
  361. Progress.Close();
  362. }
  363. private void UpdateStock_Click(object sender, RoutedEventArgs e)
  364. {
  365. if (_requisition == null)
  366. {
  367. MessageBox.Show("Please select a Requisition first!");
  368. return;
  369. }
  370. if (_requisition.StockUpdated.IsEmpty())
  371. {
  372. var emptyrows = Items.Data.Rows.Where(r =>
  373. !Entity.IsEntityLinkValid<RequisitionItem, StockLocationLink>(x => x.Location, r) &&
  374. r.Get<RequisitionItem, bool>(c => c.Product.NonStock).Equals(false));
  375. if (emptyrows.Any())
  376. {
  377. MessageBox.Show("You must select a Holding for each non-stock Item on this Requisition!", "Missing Holdings", MessageBoxButton.OK,
  378. MessageBoxImage.Error);
  379. return;
  380. }
  381. if (!_requisition.Filled.IsEmpty() && !_requisition.Archived.IsEmpty())
  382. {
  383. if (MessageBox.Show("This will remove this requisition from this list.\nAre you sure you wish to continue?", "Close Requisition?",
  384. MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
  385. return;
  386. }
  387. else
  388. {
  389. if (MessageBox.Show("Update Stock Movements?", "Confirm", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
  390. return;
  391. }
  392. _requisition.StockUpdated = DateTime.Now;
  393. }
  394. else
  395. {
  396. if (MessageBox.Show("Clear Stock Movements?", "Confirm", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
  397. return;
  398. _requisition.StockUpdated = DateTime.MinValue;
  399. }
  400. Progress.Show("Updating Stock Holdings");
  401. new Client<Requisition>().Save(_requisition, "Updated Stock Flag");
  402. if (!_requisition.Filled.IsEmpty() && !_requisition.StockUpdated.IsEmpty() && !_requisition.Archived.IsEmpty())
  403. {
  404. Refresh();
  405. }
  406. else
  407. {
  408. Requisitions.UpdateRow<Requisition, DateTime>(Requisitions.SelectedRows.First(), x => x.StockUpdated, _requisition.StockUpdated);
  409. LoadRequisition();
  410. }
  411. Progress.Close();
  412. }
  413. private void PickItems_Click(object sender, RoutedEventArgs e)
  414. {
  415. if (!UnPickedItems.SelectedRows.Any())
  416. return;
  417. ProcessItems(DateTime.Now, UnPickedItems.SelectedRows);
  418. }
  419. private void UnPickItems_Click(object sender, RoutedEventArgs e)
  420. {
  421. if (!Items.SelectedRows.Any())
  422. return;
  423. ProcessItems(DateTime.MinValue, Items.SelectedRows);
  424. }
  425. private void ProcessItems(DateTime picked, CoreRow[] rows)
  426. {
  427. var list = new List<RequisitionItem>();
  428. foreach (CoreRow row in rows)
  429. {
  430. var item = row.ToObject<RequisitionItem>();
  431. item.Picked = picked;
  432. list.Add(item);
  433. }
  434. string audittrail;
  435. if (picked == DateTime.MinValue)
  436. audittrail = "Item unpicked";
  437. else
  438. audittrail = "Item picked " + picked.ToString("dd MMM yy");
  439. new Client<RequisitionItem>().Save(list, audittrail);
  440. UnPickedItems.Refresh(false, true);
  441. Items.Refresh(false, true);
  442. }
  443. }
  444. }