RequisitionPanel.xaml.cs 21 KB

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