RequisitionPanel.xaml.cs 21 KB

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