SupplierBills.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using System.Windows;
  6. using System.Windows.Media.Imaging;
  7. using Comal.Classes;
  8. using InABox.Clients;
  9. using InABox.Configuration;
  10. using InABox.Core;
  11. using InABox.DynamicGrid;
  12. using InABox.WPF;
  13. using NPOI.Util;
  14. namespace PRSDesktop;
  15. public class SupplierBillsSettings : IUserConfigurationSettings
  16. {
  17. public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
  18. }
  19. public class SupplierBills : DynamicDataGrid<Bill>
  20. {
  21. private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
  22. private static readonly BitmapImage? data = PRSDesktop.Resources.pencil.AsBitmapImage();
  23. private static readonly BitmapImage? check = PRSDesktop.Resources.checklist.AsBitmapImage();
  24. private SupplierBillPanelProperties _settings = null;
  25. private SupplierBillsSettings _userSettings;
  26. public SupplierBills()
  27. {
  28. _settings = new GlobalConfiguration<SupplierBillPanelProperties>().Load();
  29. _userSettings = new UserConfiguration<SupplierBillsSettings>().Load();
  30. FilterComponent.BuiltInFilters.Add(new("My Unapproved Bills", GetMyUnapprovedBillsFilter));
  31. FilterComponent.SetSettings(_userSettings.Filters, false);
  32. FilterComponent.OnFiltersSelected += FilterComponent_OnFilterSelected;
  33. HiddenColumns.Add(x => x.IsApproved);
  34. HiddenColumns.Add(x => x.DataEntered);
  35. HiddenColumns.Add(x => x.Checked);
  36. HiddenColumns.Add(x => x.ApprovalSet.ID);
  37. ActionColumns.Add(new DynamicImageColumn(DataEntered_Image, null) { ToolTip = DataEntered_ToolTip });
  38. ActionColumns.Add(new DynamicImageColumn(Checked_Image, null) { ToolTip = Checked_ToolTip });
  39. ActionColumns.Add(new DynamicImageColumn(Approved_Image, null) { ToolTip = Approved_ToolTip });
  40. PostUtils.AddPostColumn(this);
  41. ActionColumns.Add(new DynamicMenuColumn(BuildMenu));
  42. }
  43. private Filter<Bill> GetMyUnapprovedBillsFilter()
  44. {
  45. return new Filter<Bill>(x => x.ID)
  46. .InQuery(
  47. new Filter<BillApproval>(x => x.Employee.ID).IsEqualTo(App.EmployeeID)
  48. .And(x => x.Approved).IsEqualTo(DateTime.MinValue),
  49. x => x.Bill.ID);
  50. }
  51. private void BuildMenu(DynamicMenuColumn column, CoreRow? row)
  52. {
  53. if (row is null) return;
  54. var menu = column.GetMenu();
  55. var approvalSetItem = menu.AddItem("Approval Set", null, null);
  56. approvalSetItem.AddItem("Loading...", null, null, enabled: false);
  57. var id = row.Get<Bill, Guid>(x => x.ID);
  58. var selectedApproval = row.Get<Bill, Guid>(x => x.ApprovalSet.ID);
  59. Task.Run(() =>
  60. {
  61. return Client.Query<BillApprovalSet>(
  62. null,
  63. Columns.None<BillApprovalSet>().Add(x => x.ID).Add(x => x.Code).Add(x => x.Description))
  64. .ToArray<BillApprovalSet>();
  65. }).ContinueWith(approvals =>
  66. {
  67. approvalSetItem.Items.Clear();
  68. foreach(var approval in approvals.Result)
  69. {
  70. var item = approvalSetItem.AddItem($"{approval.Code}: {approval.Description}", null, (row, approval), ApprovalSet_Click);
  71. item.IsChecked = approval.ID == selectedApproval;
  72. }
  73. }, TaskScheduler.FromCurrentSynchronizationContext());
  74. }
  75. private void ApprovalSet_Click((CoreRow row, BillApprovalSet approvalSet) item)
  76. {
  77. var bill = item.row.ToObject<Bill>();
  78. if (bill.ApprovalSet.ID == item.approvalSet.ID) return;
  79. BillApprovalSetEmployee[] oldEmployees;
  80. if(bill.ApprovalSet.ID != Guid.Empty)
  81. {
  82. oldEmployees = Client.Query(
  83. new Filter<BillApprovalSetEmployee>(x => x.ApprovalSet.ID).IsEqualTo(bill.ApprovalSet.ID),
  84. Columns.None<BillApprovalSetEmployee>().Add(x => x.Employee.ID),
  85. new SortOrder<BillApprovalSetEmployee>(x => x.Sequence))
  86. .ToArray<BillApprovalSetEmployee>();
  87. }
  88. else
  89. {
  90. oldEmployees = [];
  91. }
  92. var newEmployees = Client.Query(
  93. new Filter<BillApprovalSetEmployee>(x => x.ApprovalSet.ID).IsEqualTo(item.approvalSet.ID),
  94. Columns.None<BillApprovalSetEmployee>().Add(x => x.Employee.ID),
  95. new SortOrder<BillApprovalSetEmployee>(x => x.Sequence))
  96. .ToArray<BillApprovalSetEmployee>();
  97. var approvals = Client.Query(
  98. new Filter<BillApproval>(x => x.Bill.ID).IsEqualTo(bill.ID),
  99. Columns.None<BillApproval>()
  100. .Add(x => x.ID)
  101. .Add(x => x.Approved)
  102. .Add(x => x.Employee.ID)
  103. .Add(x => x.Sequence),
  104. new SortOrder<BillApproval>(x => x.Sequence))
  105. .ToArray<BillApproval>();
  106. var toDelete = new List<BillApproval>();
  107. var toSave = new List<BillApproval>();
  108. foreach(var employee in newEmployees)
  109. {
  110. if(!approvals.Any(x => x.Employee.ID == employee.Employee.ID))
  111. {
  112. var newApproval = new BillApproval();
  113. newApproval.Employee.CopyFrom(employee.Employee);
  114. newApproval.Bill.CopyFrom(bill);
  115. toSave.Add(newApproval);
  116. }
  117. }
  118. foreach(var approval in approvals)
  119. {
  120. if(approval.Approved == DateTime.MinValue
  121. && oldEmployees.Any(x => x.Employee.ID == approval.Employee.ID)
  122. && !newEmployees.Any(x => x.Employee.ID == approval.Employee.ID))
  123. {
  124. toDelete.Add(approval);
  125. }
  126. else
  127. {
  128. toSave.Add(approval);
  129. }
  130. }
  131. foreach(var (i, approval) in toSave.WithIndex())
  132. {
  133. approval.Sequence = i;
  134. }
  135. Client.Delete(toDelete, "Deleted by changing approval set.");
  136. Client.Save(toSave, "Updated when changing approval set.");
  137. bill.ApprovalSet.CopyFrom(item.approvalSet);
  138. SaveItem(bill);
  139. Refresh(false, true);
  140. }
  141. public override DynamicEditorPages LoadEditorPages(Bill item)
  142. {
  143. // Need to re-order the pages, since we don't want approvals before lines or documents.
  144. var pages = base.LoadEditorPages(item);
  145. if(pages.TryGetPage<SupplierBillLineGrid>(out var billLineGrid))
  146. {
  147. billLineGrid.Order = 0;
  148. }
  149. if(pages.TryGetPage<DynamicDocumentGrid<BillDocument, Bill, BillLink>>(out var docGrid))
  150. {
  151. docGrid.Order = 1;
  152. }
  153. if(pages.TryGetPage<DynamicOneToManyGrid<Bill, BillApproval>>(out var approvalGrid))
  154. {
  155. approvalGrid.Order = 2;
  156. }
  157. return pages;
  158. }
  159. private FrameworkElement? Approved_ToolTip(DynamicActionColumn column, CoreRow? row)
  160. {
  161. return row is null ? column.TextToolTip("Has this bill been approved?")
  162. : !row.Get<Bill, bool>(x => x.IsApproved) ? column.TextToolTip("Not yet approved")
  163. : column.TextToolTip("Approved");
  164. }
  165. private FrameworkElement? Checked_ToolTip(DynamicActionColumn column, CoreRow? row)
  166. {
  167. return row is null ? column.TextToolTip("Has this bill been checked?")
  168. : row.Get<Bill, DateTime>(x => x.Checked).IsEmpty() ? column.TextToolTip("Not yet checked")
  169. : column.TextToolTip("Checked");
  170. }
  171. private FrameworkElement? DataEntered_ToolTip(DynamicActionColumn column, CoreRow? row)
  172. {
  173. return row is null ? column.TextToolTip("Has this bill been entered via Data Entry?")
  174. : row.Get<Bill, DateTime>(x => x.DataEntered).IsEmpty() ? column.TextToolTip("Data not entered.")
  175. : column.TextToolTip("Data Entered");
  176. }
  177. protected override void DoReconfigure(DynamicGridOptions options)
  178. {
  179. base.DoReconfigure(options);
  180. options.FilterRows = true;
  181. options.SelectColumns = true;
  182. options.MultiSelect = true;
  183. options.RecordCount = true;
  184. options.ShowHelp = true;
  185. }
  186. private BitmapImage? DataEntered_Image(CoreRow? row)
  187. {
  188. return (row != null) && row.Get<Bill, DateTime>(x => x.DataEntered).IsEmpty()
  189. ? null
  190. : data;
  191. }
  192. private BitmapImage? Checked_Image(CoreRow? row)
  193. {
  194. return (row != null) && row.Get<Bill, DateTime>(x => x.Checked).IsEmpty()
  195. ? null
  196. : check;
  197. }
  198. private BitmapImage? Approved_Image(CoreRow? row)
  199. {
  200. return (row != null) && !row.Get<Bill, bool>(x => x.IsApproved)
  201. ? null
  202. : tick;
  203. }
  204. public Bill[] LoadBills(CoreRow[] rows)
  205. {
  206. return LoadItems(rows);
  207. }
  208. protected override void DoValidate(Bill[] items, List<string> errors)
  209. {
  210. base.DoValidate(items, errors);
  211. if(items.Any(x => x.Number.IsNullOrWhiteSpace()) && !_settings.AllowBlankBillNumbers)
  212. {
  213. errors.Add($"[{nameof(Bill.Number)}] may not be blank!");
  214. }
  215. if(items.Any(x => x.SupplierLink.ID == Guid.Empty))
  216. {
  217. errors.Add($"[{nameof(Bill.SupplierLink)}] may not be blank!");
  218. }
  219. }
  220. private void FilterComponent_OnFilterSelected(DynamicGridSelectedFilterSettings settings)
  221. {
  222. _userSettings.Filters = settings;
  223. new UserConfiguration<SupplierBillsSettings>().Save(_userSettings);
  224. }
  225. }