SupplierPurchaseOrderItemOneToMany.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Media.Imaging;
  7. using Comal.Classes;
  8. using InABox.Clients;
  9. using InABox.Core;
  10. using InABox.DynamicGrid;
  11. using InABox.WPF;
  12. namespace PRSDesktop;
  13. public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseOrder,PurchaseOrderItem>
  14. {
  15. private Button bill;
  16. private Button? createConsignment;
  17. //private Button? viewconsign;
  18. private Button receive;
  19. private Button assignLocation;
  20. private List<Tuple<PurchaseOrderItem, JobRequisitionItemPurchaseOrderItem>> JobRequisitionItems = new();
  21. public SupplierPurchaseOrderItemOneToMany() : base()
  22. {
  23. HiddenColumns.Add(x => x.ID);
  24. HiddenColumns.Add(x => x.Description);
  25. HiddenColumns.Add(x => x.TaxRate);
  26. HiddenColumns.Add(x => x.ExTax);
  27. HiddenColumns.Add(x => x.Tax);
  28. HiddenColumns.Add(x => x.IncTax);
  29. HiddenColumns.Add(x => x.ReceivedDate);
  30. HiddenColumns.Add(x => x.Qty);
  31. HiddenColumns.Add(x => x.Balance);
  32. HiddenColumns.Add(x => x.PORevision);
  33. HiddenColumns.Add(x => x.DueDate);
  34. HiddenColumns.Add(x => x.SupplierCode);
  35. HiddenColumns.Add(x => x.BillLine.ID);
  36. HiddenColumns.Add(x => x.Consignment.ID);
  37. HiddenColumns.Add(x => x.Job.ID);
  38. HiddenColumns.Add(x => x.StockLocation.ID);
  39. HiddenColumns.Add(x => x.PurchaseGL.ID);
  40. HiddenColumns.Add(x => x.CostCentre.ID);
  41. HiddenColumns.Add(x => x.Product.ID);
  42. HiddenColumns.Add(x => x.Product.Code);
  43. HiddenColumns.Add(x => x.Product.Name);
  44. HiddenColumns.Add(x => x.Style.ID);
  45. HiddenColumns.Add(x => x.TaxCode.ID);
  46. HiddenColumns.Add(x => x.TaxCode.Code);
  47. HiddenColumns.Add(x => x.TaxCode.Description);
  48. HiddenColumns.Add(x => x.TaxCode.Rate);
  49. foreach (var column in new Columns<PurchaseOrderItem>()
  50. .AddDimensionsColumns(x => x.Dimensions, Dimensions.ColumnsType.Local)
  51. .GetColumns())
  52. {
  53. HiddenColumns.Add(column);
  54. }
  55. HiddenColumns.Add(x => x.PurchaseOrderLink.SupplierLink.ID);
  56. HiddenColumns.Add(x => x.PurchaseOrderLink.Category.ID);
  57. HiddenColumns.Add(x => x.Product.DigitalForm.ID);
  58. HiddenColumns.Add(x => x.Product.DigitalForm.Description);
  59. HiddenColumns.Add(x => x.Product.Image.ID);
  60. HiddenColumns.Add(x => x.Product.Image.FileName);
  61. ActionColumns.Add(new DynamicImageManagerColumn<PurchaseOrderItem>(this, x => x.Product.Image, true)
  62. { Position = DynamicActionColumnPosition.Start });
  63. HiddenColumns.Add(x => x.FormCount);
  64. HiddenColumns.Add(x => x.OpenForms);
  65. ActionColumns.Add(new DynamicMenuColumn(BuildFormsMenu) { Position = DynamicActionColumnPosition.End });
  66. ActionColumns.Add(new DynamicImageColumn(FormsImage) { Position = DynamicActionColumnPosition.Start, ToolTip = FormsToolTip });
  67. }
  68. protected override void Init()
  69. {
  70. base.Init();
  71. if (Security.IsAllowed<CanViewConsignmentModule>())
  72. {
  73. createConsignment = AddButton("Add to Consignment", null, AddToConsignment);
  74. createConsignment.IsEnabled = false;
  75. }
  76. receive = AddButton("Receive Items", null, ReceiveItems);
  77. receive.IsEnabled = false;
  78. bill = AddButton("Enter Bill", null, EnterBill);
  79. bill.IsEnabled = false;
  80. assignLocation = AddButton("Assign Location", null, AssignLocation);
  81. }
  82. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  83. {
  84. base.DoReconfigure(options);
  85. if (!ReadOnly && Security.CanEdit<PurchaseOrderItem>())
  86. {
  87. options.Add(DynamicGridOption.DirectEdit);
  88. options.Add(DynamicGridOption.DragTarget);
  89. }
  90. //if (!IsDirectEditMode(options))
  91. //{
  92. options.Add(DynamicGridOption.FilterRows);
  93. //}
  94. }
  95. protected override void OnAfterRefresh()
  96. {
  97. base.OnAfterRefresh();
  98. JobRequisitionItems.RemoveAll(x => !Items.Contains(x.Item1));
  99. }
  100. public override void AfterSave(object item)
  101. {
  102. base.AfterSave(item);
  103. var toSave = new List<JobRequisitionItemPurchaseOrderItem>();
  104. foreach (var poItem in Items)
  105. {
  106. var jriPois = JobRequisitionItems.Where(x => x.Item1 == poItem).Select(x => x.Item2);
  107. foreach (var jriPoi in jriPois)
  108. {
  109. jriPoi.PurchaseOrderItem.ID = poItem.ID;
  110. }
  111. toSave.AddRange(jriPois);
  112. }
  113. Client.Save(toSave, "");
  114. }
  115. private void BuildFormsMenu(DynamicMenuColumn column, CoreRow? row)
  116. {
  117. if (row == null) return;
  118. if (Security.CanEdit<PurchaseOrderItem>())
  119. {
  120. column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine, enabled: row.Get<PurchaseOrderItem, DateTime>(x => x.ReceivedDate).IsEmpty());
  121. }
  122. if(row.Get<PurchaseOrderItem, Guid>(x => x.Consignment.ID) != Guid.Empty)
  123. {
  124. column.AddItem("View Consignment", null, ViewConsignment);
  125. }
  126. var formsItem = column.AddItem("Digital Forms", PRSDesktop.Resources.kanban, null);
  127. DynamicGridUtils.PopulateFormMenu<PurchaseOrderItemForm, PurchaseOrderItem, PurchaseOrderItemLink>(
  128. formsItem,
  129. row.Get<PurchaseOrderItem, Guid>(x => x.ID),
  130. row.ToObject<PurchaseOrderItem>);
  131. }
  132. private void SplitLine(CoreRow? row)
  133. {
  134. if (row is null)
  135. return;
  136. var qty = row.Get<PurchaseOrderItem, double>(x => x.Qty);
  137. var value = qty / 2;
  138. if(DoubleEdit.Execute("Enter quantity to split on:", 0.0, qty, ref value))
  139. {
  140. var poi = LoadItem(row);
  141. IEnumerable<Guid> jobRequiItemIDs;
  142. if(poi.ID == Guid.Empty)
  143. {
  144. // If not saved yet, we will have any jriPOIs in the transient list.
  145. jobRequiItemIDs = JobRequisitionItems.Where(x => x.Item1 == poi).Select(x => x.Item2.JobRequisitionItem.ID).ToList();
  146. }
  147. else
  148. {
  149. // Otherwise, they'll all be in the database.
  150. jobRequiItemIDs = Client.Query(
  151. new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.PurchaseOrderItem.ID).IsEqualTo(poi.ID),
  152. new Columns<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID))
  153. .ExtractValues<JobRequisitionItemPurchaseOrderItem, Guid>(x => x.JobRequisitionItem.ID);
  154. }
  155. var newLine = new PurchaseOrderItem
  156. {
  157. };
  158. newLine.BillLine.ID = poi.BillLine.ID;
  159. newLine.BillLine.Synchronise(poi.BillLine);
  160. newLine.StockLocation.ID = poi.StockLocation.ID;
  161. newLine.StockLocation.Synchronise(poi.StockLocation);
  162. newLine.Consignment.ID = poi.Consignment.ID;
  163. newLine.Consignment.Synchronise(poi.Consignment);
  164. newLine.PurchaseGL.ID = poi.PurchaseGL.ID;
  165. newLine.PurchaseGL.Synchronise(poi.PurchaseGL);
  166. newLine.CostCentre.ID = poi.CostCentre.ID;
  167. newLine.CostCentre.Synchronise(poi.CostCentre);
  168. newLine.Product.ID = poi.Product.ID;
  169. newLine.Product.Synchronise(poi.Product);
  170. newLine.Style.ID = poi.Style.ID;
  171. newLine.Style.Synchronise(poi.Style);
  172. newLine.TaxCode.ID = poi.TaxCode.ID;
  173. newLine.TaxCode.Synchronise(poi.TaxCode);
  174. newLine.PurchaseOrderLink.ID = poi.PurchaseOrderLink.ID;
  175. newLine.PurchaseOrderLink.Synchronise(poi.PurchaseOrderLink);
  176. newLine.Job.ID = poi.Job.ID;
  177. newLine.Job.Synchronise(poi.Job);
  178. newLine.Dimensions.CopyFrom(poi.Dimensions);
  179. newLine.Description = poi.Description;
  180. newLine.TaxRate = poi.TaxRate;
  181. newLine.ExTax = poi.ExTax;
  182. newLine.Tax = poi.Tax;
  183. newLine.IncTax = poi.IncTax;
  184. newLine.Cost = poi.Cost;
  185. newLine.Balance = poi.Balance;
  186. newLine.PORevision = poi.PORevision;
  187. newLine.DueDate = poi.DueDate;
  188. newLine.SupplierCode = poi.SupplierCode;
  189. poi.Qty = value;
  190. newLine.Qty = qty - value;
  191. foreach(var jriID in jobRequiItemIDs)
  192. {
  193. // Add to a list to be saved later.
  194. var jriPoi = new JobRequisitionItemPurchaseOrderItem();
  195. jriPoi.JobRequisitionItem.ID = jriID;
  196. JobRequisitionItems.Add(new(newLine, jriPoi));
  197. }
  198. SaveItem(poi);
  199. SaveItem(newLine);
  200. Refresh(false, true);
  201. DoChanged();
  202. }
  203. }
  204. private void ViewConsignment(CoreRow? row)
  205. {
  206. if (row is null) return;
  207. var consignmentID = row.Get<PurchaseOrderItem, Guid>(x => x.Consignment.ID);
  208. var consignments = Client.Query(
  209. new Filter<Consignment>(x => x.ID).IsEqualTo(consignmentID),
  210. DynamicGridUtils.LoadEditorColumns(new Columns<Consignment>()))
  211. .ToObjects<Consignment>().ToArray();
  212. DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(Consignment)).EditItems(consignments);
  213. }
  214. private FrameworkElement? FormsToolTip(DynamicActionColumn arg1, CoreRow? arg2)
  215. {
  216. var text = arg2 == null || arg2.Get<PurchaseOrderItem, Guid>(x => x.Product.DigitalForm.ID) == Guid.Empty
  217. ? ""
  218. : arg2.Get<PurchaseOrderItem, int>(c => c.FormCount) == 0
  219. ? "No forms found for this item"
  220. : arg2.Get<PurchaseOrderItem, int>(c => c.OpenForms) > 0
  221. ? "Incomplete forms found"
  222. : "All forms completed";
  223. return string.IsNullOrWhiteSpace(text) ? null : arg1.TextToolTip(text);
  224. }
  225. private BitmapImage? FormsImage(CoreRow? arg)
  226. {
  227. if (arg == null)
  228. return PRSDesktop.Resources.quality.AsBitmapImage();
  229. return arg.Get<PurchaseOrderItem, Guid>(x => x.Product.DigitalForm.ID) == Guid.Empty
  230. ? null
  231. : arg.Get<PurchaseOrderItem, int>(c => c.FormCount) == 0
  232. ? PRSDesktop.Resources.warning.AsBitmapImage()
  233. : arg.Get<PurchaseOrderItem, int>(c => c.OpenForms) > 0
  234. ? PRSDesktop.Resources.warning.AsBitmapImage()
  235. : PRSDesktop.Resources.quality.AsBitmapImage();
  236. }
  237. private bool AddToConsignment(Button sender, CoreRow[] rows)
  238. {
  239. if (!rows.Any())
  240. {
  241. MessageBox.Show("Please select a row first");
  242. return false;
  243. }
  244. var poItems = LoadItems(rows);
  245. if(poItems.Any(x => x.ID == Guid.Empty))
  246. {
  247. MessageBox.Show("Please save this purchase order first.");
  248. return false;
  249. }
  250. var menu = new ContextMenu();
  251. menu.AddItem("New Consignment", null, () =>
  252. {
  253. var consign = new Consignment();
  254. consign.Supplier.ID = rows.First().Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.SupplierLink.ID);
  255. consign.Category.ID = rows.First().Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.Category.ID);
  256. if (new DynamicDataGrid<Consignment>().EditItems(new[] { consign }, LoadConsignmentLines, true))
  257. {
  258. foreach (var item in poItems)
  259. {
  260. item.Consignment.ID = consign.ID;
  261. }
  262. new Client<PurchaseOrderItem>().Save(poItems, "Added to new consignment");
  263. Refresh(false, true);
  264. }
  265. });
  266. menu.AddItem("Existing Consignment", null, () =>
  267. {
  268. var popupList = new PopupList(typeof(Consignment), Guid.Empty, Array.Empty<string>());
  269. popupList.OnDefineFilter += type =>
  270. {
  271. return new Filter<Consignment>(x => x.Closed).IsNotEqualTo(DateTime.MinValue);
  272. };
  273. if (popupList.ShowDialog() == true)
  274. {
  275. foreach (var item in poItems)
  276. {
  277. item.Consignment.ID = popupList.ID;
  278. }
  279. new Client<PurchaseOrderItem>().Save(poItems, "Added to existing consignment");
  280. Refresh(false, true);
  281. }
  282. });
  283. menu.IsOpen = true;
  284. return false;
  285. }
  286. private CoreTable? LoadConsignmentLines(Type type)
  287. {
  288. if (type == typeof(PurchaseOrderItem))
  289. {
  290. var result = new CoreTable();
  291. result.LoadColumns(typeof(PurchaseOrderItem));
  292. result.LoadRows(SelectedRows);
  293. return result;
  294. }
  295. else
  296. {
  297. return null;
  298. }
  299. }
  300. private static bool EnterBill(Button sender, CoreRow[] rows)
  301. {
  302. if (!rows.Any())
  303. {
  304. MessageBox.Show("Please select a row first");
  305. return false;
  306. }
  307. var bill = new Bill();
  308. bill.SupplierLink.ID = rows.First()
  309. .Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.SupplierLink.ID);
  310. bill.BillDate = DateTime.Today;
  311. return new DynamicDataGrid<Bill>().EditItems(new[] { bill }, (type) =>
  312. {
  313. return LoadBillLines(type, rows);
  314. }, true);
  315. }
  316. private static CoreTable LoadBillLines(Type type, CoreRow[] rows)
  317. {
  318. var result = new CoreTable();
  319. result.LoadColumns(typeof(BillLine));
  320. foreach (var row in rows)
  321. {
  322. var billrow = result.NewRow();
  323. billrow.Set<BillLine, Guid>(x => x.OrderItem.ID, row.Get<PurchaseOrderItem, Guid>(x => x.ID));
  324. var description = new List<string>();
  325. if (row.Get<PurchaseOrderItem, Guid>(x => x.Product.ID) != Guid.Empty)
  326. description.Add(string.Format("{0} : {1}", row.Get<PurchaseOrderItem, string>(x => x.Product.Code),
  327. row.Get<PurchaseOrderItem, string>(x => x.Product.Name)));
  328. var Description = row.Get<PurchaseOrderItem, string>(x => x.Description);
  329. if (!string.IsNullOrEmpty(Description))
  330. description.Add(Description);
  331. billrow.Set<BillLine, string>(x => x.Description, string.Join("\n", description));
  332. billrow.Set<BillLine, Guid>(x => x.TaxCode.ID, row.Get<PurchaseOrderItem, Guid>(x => x.TaxCode.ID));
  333. billrow.Set<BillLine, string>(x => x.TaxCode.Code, row.Get<PurchaseOrderItem, string>(x => x.TaxCode.Code));
  334. billrow.Set<BillLine, string>(x => x.TaxCode.Description, row.Get<PurchaseOrderItem, string>(x => x.TaxCode.Description));
  335. billrow.Set<BillLine, double>(x => x.TaxCode.Rate, row.Get<PurchaseOrderItem, double>(x => x.TaxCode.Rate));
  336. billrow.Set<BillLine, double>(x => x.TaxRate, row.Get<PurchaseOrderItem, double>(x => x.TaxRate));
  337. billrow.Set<BillLine, double>(x => x.ExTax, row.Get<PurchaseOrderItem, double>(x => x.ExTax));
  338. billrow.Set<BillLine, double>(x => x.Tax, row.Get<PurchaseOrderItem, double>(x => x.Tax));
  339. billrow.Set<BillLine, double>(x => x.IncTax, row.Get<PurchaseOrderItem, double>(x => x.IncTax));
  340. result.Rows.Add(billrow);
  341. }
  342. return result;
  343. }
  344. private bool ReceiveItems(Button sender, CoreRow[] rows)
  345. {
  346. if (!rows.Any())
  347. {
  348. MessageBox.Show("Please select a row first");
  349. return false;
  350. }
  351. var now = DateTime.Now;
  352. using (new WaitCursor())
  353. {
  354. var items = LoadItems(rows);
  355. foreach (var item in items)
  356. item.ReceivedDate = now;
  357. new Client<PurchaseOrderItem>().Save(items, "Consignment Items Received");
  358. }
  359. return true;
  360. }
  361. public static bool AssignLocation(Button btn, CoreRow[] rows)
  362. {
  363. if (!rows.Any())
  364. {
  365. MessageBox.Show("Please select at least one row to assign");
  366. return false;
  367. }
  368. var menu = new ContextMenu();
  369. menu.AddItem("Create New Location", null, () =>
  370. {
  371. var grid = new StockLocationGrid();
  372. var location = new StockLocation();
  373. if (grid.EditItems(new StockLocation[] { location }))
  374. AssignLocationToItems(location.ID, rows);
  375. });
  376. menu.AddItem("Choose Existing", null, () =>
  377. {
  378. var popup = new PopupList(typeof(StockLocation), Guid.Empty, new string[] { });
  379. if (popup.ShowDialog() == true)
  380. AssignLocationToItems(popup.ID, rows);
  381. });
  382. menu.IsOpen = true;
  383. return true;
  384. }
  385. private static void AssignLocationToItems(Guid locationID, CoreRow[] rows)
  386. {
  387. var items = new List<PurchaseOrderItem>();
  388. foreach (CoreRow row in rows)
  389. {
  390. var item = row.ToObject<PurchaseOrderItem>();
  391. item.StockLocation.ID = locationID;
  392. items.Add(item);
  393. }
  394. Client.Save(items, "Added stock location from PurchaseOrderItem Grid");
  395. }
  396. /*private StockLocation[] QueryLocations()
  397. {
  398. CoreTable table = new Client<StockLocation>().Query(new Filter<StockLocation>(x => x.Active).IsEqualTo(true), new Columns<StockLocation>(x => x.ID));
  399. List<StockLocation> locations = new List<StockLocation>();
  400. foreach (CoreRow row in table.Rows)
  401. locations.Add(row.ToObject<StockLocation>());
  402. return locations.ToArray();
  403. }*/
  404. protected override void SelectItems(CoreRow[]? rows)
  405. {
  406. if (createConsignment != null)
  407. {
  408. createConsignment.IsEnabled =
  409. rows != null
  410. && !rows.Any(r => Entity.IsEntityLinkValid<PurchaseOrderItem, ConsignmentLink>(x => x.Consignment, r))
  411. && !rows.Any(r => !r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty())
  412. && !ReadOnly && Security.CanEdit<Consignment>();
  413. }
  414. receive.IsEnabled =
  415. rows != null
  416. && !rows.Any(r => Entity.IsEntityLinkValid<PurchaseOrderItem, ConsignmentLink>(x => x.Consignment, r))
  417. && !rows.Any(r => r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty() == false)
  418. && !ReadOnly && Security.CanEdit<PurchaseOrderItem>();
  419. bill.IsEnabled =
  420. rows != null
  421. && !rows.Any(r => r.IsEntityLinkValid<PurchaseOrderItem, BillLineLink>(x => x.BillLine))
  422. && !ReadOnly && Security.CanEdit<PurchaseOrderItem>();
  423. assignLocation.IsEnabled =
  424. rows != null && !ReadOnly && Security.CanEdit<PurchaseOrderItem>();
  425. base.SelectItems(rows);
  426. }
  427. protected override void CustomiseEditor(PurchaseOrderItem[] items, DynamicGridColumn column, BaseEditor editor)
  428. {
  429. base.CustomiseEditor(items, column, editor);
  430. if(items.Any(x => x.ReceivedDate != DateTime.MinValue) && !new Column<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(column.ColumnName) && editor.Editable == Editable.Enabled)
  431. {
  432. editor.Editable = editor.Editable.Combine(Editable.Disabled);
  433. }
  434. }
  435. protected override void OnAfterEditorValueChanged(DynamicEditorGrid? grid, PurchaseOrderItem[] items, AfterEditorValueChangedArgs args, Dictionary<string, object?> changes)
  436. {
  437. base.OnAfterEditorValueChanged(grid, items, args, changes);
  438. if (args.ColumnName.Equals("Product.ID") || args.ColumnName.Equals("Job.ID") || args.ColumnName.Equals("Dimensions") || args.ColumnName.StartsWith("Dimensions.") || args.ColumnName.Equals("Style.ID"))
  439. {
  440. PurchaseOrder.UpdateCosts(
  441. items,
  442. Item.SupplierLink.ID,
  443. changes
  444. );
  445. }
  446. }
  447. protected override Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm editor, PurchaseOrderItem[] items, string name,
  448. object value)
  449. {
  450. var results = base.EditorValueChanged(editor, items, name, value);
  451. if (name.Equals("ProductLink.TaxCode.ID"))
  452. DynamicGridUtils.UpdateEditorValue(items, "TaxCode.ID", (Guid)value, results);
  453. return results;
  454. }
  455. }