RequisitionPanel.xaml.cs 19 KB

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