JobRequisitionItemSelectionGrid.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.DynamicGrid;
  5. using InABox.Wpf;
  6. using InABox.WPF;
  7. using Syncfusion.Data;
  8. using Syncfusion.UI.Xaml.Grid;
  9. using Syncfusion.Windows.Shared;
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.ComponentModel;
  14. using System.Data;
  15. using System.Linq;
  16. using System.Runtime.CompilerServices;
  17. using System.Text;
  18. using System.Threading.Tasks;
  19. using System.Windows;
  20. using System.Windows.Controls;
  21. using System.Windows.Data;
  22. using System.Windows.Media;
  23. namespace PRSDesktop.Panels.Reservation_Management;
  24. public class JobRequisitionItemSelectionItem : BaseObject
  25. {
  26. public double Quantity { get; set; }
  27. private double _issued;
  28. public double Issued
  29. {
  30. get => _issued;
  31. set
  32. {
  33. var oldValue = _issued;
  34. var canMinus = CanMinus;
  35. var canPlus = CanPlus;
  36. _issued = value;
  37. if(oldValue != value)
  38. {
  39. OnPropertyChanged(nameof(Issued), oldValue, value);
  40. }
  41. if(canMinus != CanMinus)
  42. {
  43. OnPropertyChanged(nameof(CanMinus), canMinus, CanMinus);
  44. }
  45. if(canPlus != CanPlus)
  46. {
  47. OnPropertyChanged(nameof(CanPlus), canPlus, CanPlus);
  48. }
  49. }
  50. }
  51. public double MaxValue { get; set; }
  52. private bool _editable = true;
  53. public bool Editable
  54. {
  55. get => _editable;
  56. set
  57. {
  58. var oldValue = _editable;
  59. var canMinus = CanMinus;
  60. var canPlus = CanPlus;
  61. _editable = value;
  62. if(oldValue != value)
  63. {
  64. OnPropertyChanged(nameof(Editable), oldValue, value);
  65. }
  66. if(canMinus != CanMinus)
  67. {
  68. OnPropertyChanged(nameof(CanMinus), canMinus, CanMinus);
  69. }
  70. if(canPlus != CanPlus)
  71. {
  72. OnPropertyChanged(nameof(CanPlus), canPlus, CanPlus);
  73. }
  74. }
  75. }
  76. public bool CanMinus => Editable && Issued > 0;
  77. public bool CanPlus => Editable && Issued < MaxValue;
  78. public JobRequisitionItem JRI { get; set; }
  79. }
  80. public abstract class JobRequisitionItemSelectionGrid<T> : DynamicItemsListGrid<T>, INotifyPropertyChanged
  81. where T : JobRequisitionItemSelectionItem, new()
  82. {
  83. private double _totalQuantity;
  84. public double TotalQuantity
  85. {
  86. get => _totalQuantity;
  87. set
  88. {
  89. _totalQuantity = value;
  90. OnPropertyChanged();
  91. }
  92. }
  93. private double _totalIssued;
  94. public double TotalIssued
  95. {
  96. get => _totalIssued;
  97. set
  98. {
  99. _totalIssued = value;
  100. OnPropertyChanged();
  101. }
  102. }
  103. private bool _observing = true;
  104. public void SetObserving(bool observing)
  105. {
  106. if(_observing != observing)
  107. {
  108. _observing = observing;
  109. if (_observing)
  110. {
  111. Recalculate();
  112. }
  113. }
  114. }
  115. protected override void Init()
  116. {
  117. base.Init();
  118. ActionColumns.Add(new DynamicTemplateColumn(TotalColumn_Template)
  119. {
  120. HeaderText = "Total",
  121. Width = 60,
  122. GetSummary = () =>
  123. {
  124. var summary = new GridSummaryColumn
  125. {
  126. TemplateSelector = new FuncTemplateSelector((item, o) => TotalQuantity_Template())
  127. };
  128. return summary;
  129. }
  130. });
  131. ActionColumns.Add(new DynamicTemplateColumn(QuantityColumn_Template)
  132. {
  133. HeaderText = "Quantity",
  134. Width = 230,
  135. GetSummary = () =>
  136. {
  137. var summary = new GridSummaryColumn
  138. {
  139. TemplateSelector = new FuncTemplateSelector((item, o) => TotalIssued_Template())
  140. };
  141. return summary;
  142. }
  143. });
  144. }
  145. protected override void DoReconfigure(DynamicGridOptions options)
  146. {
  147. base.DoReconfigure(options);
  148. options.Clear();
  149. }
  150. private FrameworkElement TotalQuantity_Template()
  151. {
  152. var quantity = new DoubleTextBox
  153. {
  154. HorizontalContentAlignment = HorizontalAlignment.Center,
  155. VerticalContentAlignment = VerticalAlignment.Center,
  156. VerticalAlignment = VerticalAlignment.Stretch,
  157. HorizontalAlignment = HorizontalAlignment.Stretch,
  158. Background = new SolidColorBrush(Colors.WhiteSmoke),
  159. IsReadOnly = true,
  160. Margin = new Thickness(0, 5, 0, 5)
  161. };
  162. quantity.Bind(DoubleTextBox.ValueProperty, this, x => x.TotalQuantity);
  163. return quantity;
  164. }
  165. private FrameworkElement TotalIssued_Template()
  166. {
  167. var quantity = new DoubleTextBox
  168. {
  169. HorizontalContentAlignment = HorizontalAlignment.Center,
  170. VerticalContentAlignment = VerticalAlignment.Center,
  171. VerticalAlignment = VerticalAlignment.Stretch,
  172. HorizontalAlignment = HorizontalAlignment.Stretch,
  173. Background = new SolidColorBrush(Colors.LightGreen),
  174. IsReadOnly = true,
  175. Margin = new Thickness(50 + 30 + 10, 5, 50 + 30 + 5, 5)
  176. };
  177. quantity.Bind(DoubleTextBox.ValueProperty, this, x => x.TotalIssued);
  178. return quantity;
  179. }
  180. private class TotalAggregate : ISummaryAggregate
  181. {
  182. public double Sum { get; private set; }
  183. private JobRequisitionItemSelectionGrid<T> Grid;
  184. public enum TotalColumn
  185. {
  186. Quantity,
  187. Issued
  188. }
  189. public TotalColumn Type { get; set; }
  190. public TotalAggregate(TotalColumn type, JobRequisitionItemSelectionGrid<T> grid)
  191. {
  192. Type = type;
  193. Grid = grid;
  194. }
  195. public Action<IEnumerable, string, PropertyDescriptor> CalculateAggregateFunc()
  196. {
  197. return AggregateFunc;
  198. }
  199. private void AggregateFunc(IEnumerable items, string property, PropertyDescriptor args)
  200. {
  201. if (items is IEnumerable<DataRowView> rows)
  202. {
  203. Sum = Type switch
  204. {
  205. TotalColumn.Quantity => Grid.TotalQuantity,
  206. TotalColumn.Issued or _ => Grid.TotalIssued
  207. };
  208. }
  209. else
  210. {
  211. Logger.Send(LogType.Error, "", $"Attempting to calculate aggregate on invalid data type '{items.GetType()}'.");
  212. }
  213. }
  214. }
  215. private class UIComponent : DynamicGridGridUIComponent<T>
  216. {
  217. public UIComponent(JobRequisitionItemSelectionGrid<T> grid)
  218. {
  219. Parent = grid;
  220. GridLines = DynamicGridLines.None;
  221. RowHeight = 40;
  222. HeaderRowHeight = 40;
  223. }
  224. protected override Brush? GetCellSelectionBackgroundBrush()
  225. {
  226. return null;
  227. }
  228. protected override Brush? GetCellSelectionForegroundBrush()
  229. {
  230. return null;
  231. }
  232. protected override Brush? GetCellBackground(CoreRow row, DynamicColumnBase column)
  233. {
  234. return base.GetCellBackground(row, column);
  235. }
  236. protected override Style GetHeaderCellStyle(DynamicColumnBase column)
  237. {
  238. var style = base.GetHeaderCellStyle(column);
  239. style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Silver)));
  240. style.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.Bold));
  241. style.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0)));
  242. style.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0.0)));
  243. return style;
  244. }
  245. protected override Style GetSummaryRowStyle()
  246. {
  247. var style = base.GetSummaryRowStyle();
  248. style.Setters.Add(new Setter(Control.BorderBrushProperty, new SolidColorBrush(Colors.Silver)));
  249. style.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(1, 1, 0, 1)));
  250. style.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 1, 0, 0)));
  251. style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.WhiteSmoke)));
  252. return style;
  253. }
  254. protected override Style GetSummaryCellStyle(DynamicColumnBase column)
  255. {
  256. var style = base.GetSummaryCellStyle(column);
  257. style.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0)));
  258. style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
  259. style.Setters.Add(new Setter(GridTableSummaryCell.HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch));
  260. style.Setters.Add(new Setter(Control.VerticalContentAlignmentProperty, VerticalAlignment.Stretch));
  261. style.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(0)));
  262. return style;
  263. }
  264. }
  265. protected override IDynamicGridUIComponent<T> CreateUIComponent()
  266. {
  267. return new UIComponent(this);
  268. }
  269. private FrameworkElement? TotalColumn_Template(CoreRow row)
  270. {
  271. var item = LoadItem(row);
  272. var quantity = new DoubleTextBox
  273. {
  274. HorizontalContentAlignment = HorizontalAlignment.Center,
  275. VerticalContentAlignment = VerticalAlignment.Center,
  276. VerticalAlignment = VerticalAlignment.Stretch,
  277. Background = new SolidColorBrush(Colors.WhiteSmoke),
  278. IsReadOnly = true,
  279. Margin = new Thickness(0, 5, 0, 5)
  280. };
  281. quantity.Bind(DoubleTextBox.ValueProperty, item, x => x.Quantity);
  282. quantity.Bind(Button.IsEnabledProperty, item, x => x.Editable);
  283. return quantity;
  284. }
  285. private FrameworkElement? QuantityColumn_Template(CoreRow row)
  286. {
  287. var item = LoadItem(row);
  288. var grid = new Grid();
  289. grid.Margin = new Thickness(5);
  290. grid.AddColumn(50);
  291. grid.AddColumn(30);
  292. grid.AddColumn(60);
  293. grid.AddColumn(30);
  294. grid.AddColumn(50);
  295. var none = new Button
  296. {
  297. Content = "None",
  298. };
  299. none.Bind(Button.IsEnabledProperty, item, x => x.Editable);
  300. none.Margin = new Thickness(0);
  301. none.Click += (o, e) => None_Click(item);
  302. grid.AddChild(none, 0, 0);
  303. var minus = new Button
  304. {
  305. Content = "-",
  306. };
  307. minus.Bind(Button.IsEnabledProperty, item, x => x.CanMinus);
  308. minus.Margin = new Thickness(5, 0, 0, 0);
  309. minus.Click += (o, e) => Minus_Click(item);
  310. grid.AddChild(minus, 0, 1);
  311. var quantity = new DoubleTextBox
  312. {
  313. HorizontalContentAlignment = HorizontalAlignment.Center,
  314. VerticalContentAlignment = VerticalAlignment.Center,
  315. VerticalAlignment = VerticalAlignment.Stretch,
  316. Margin = new Thickness(5, 0, 0, 0)
  317. };
  318. var style = new Style(typeof(DoubleTextBox));
  319. style.AddSetter(BackgroundProperty, Colors.LightYellow.ToBrush());
  320. style.AddDataTrigger()
  321. .Bind(item, x => x.Issued, true, new FuncConverter<double, bool>(issued => issued > item.Quantity))
  322. .AddSetter(BackgroundProperty, Colors.Salmon.ToBrush());
  323. quantity.Style = style;
  324. quantity.Bind(DoubleTextBox.ValueProperty, item, x => x.Issued);
  325. quantity.MinValue = 0;
  326. quantity.Bind(DoubleTextBox.MaxValueProperty, item, x => x.MaxValue);
  327. quantity.Bind(Button.IsEnabledProperty, item, x => x.Editable);
  328. grid.AddChild(quantity, 0, 2);
  329. var plus = new Button
  330. {
  331. Content = "+",
  332. };
  333. plus.Bind(Button.IsEnabledProperty, item, x => x.CanPlus);
  334. plus.Margin = new Thickness(5, 0, 0, 0);
  335. plus.Click += (o, e) => Plus_Click(item);
  336. grid.AddChild(plus, 0, 3);
  337. var all = new Button
  338. {
  339. Content = "All",
  340. };
  341. all.Bind(Button.IsEnabledProperty, item, x => x.Editable);
  342. all.Margin = new Thickness(5, 0, 0, 0);
  343. all.Click += (o, e) => All_Click(item);
  344. grid.AddChild(all, 0, 4);
  345. return grid;
  346. }
  347. private void None_Click(T item)
  348. {
  349. item.Issued = 0;
  350. }
  351. private void Minus_Click(T item)
  352. {
  353. item.Issued = Math.Max(0, item.Issued - 1);
  354. }
  355. private void Plus_Click(T item)
  356. {
  357. if(item.JRI.ID == Guid.Empty)
  358. {
  359. item.Issued += 1;
  360. }
  361. else
  362. {
  363. item.Issued = Math.Min(item.Issued + 1, item.Quantity);
  364. }
  365. }
  366. private void All_Click(T item)
  367. {
  368. item.Issued = Math.Max(0, item.Quantity);
  369. }
  370. protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sort, Action<CoreTable?, Exception?> action)
  371. {
  372. foreach(var item in Items)
  373. {
  374. item.PropertyChanged += (o, e) => Recalculate();
  375. }
  376. Recalculate();
  377. base.Reload(criteria, columns, ref sort, action);
  378. }
  379. public Dictionary<Guid, double> GetQuantities()
  380. {
  381. return Items.ToDictionary(x => x.JRI.ID, x => x.Issued);
  382. }
  383. private void Recalculate()
  384. {
  385. if (!_observing) return;
  386. TotalQuantity = Items.Sum(x => x.Quantity);
  387. TotalIssued = Items.Sum(x => x.Issued);
  388. }
  389. public event PropertyChangedEventHandler? PropertyChanged;
  390. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
  391. {
  392. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  393. }
  394. }