SupplierPurchaseOrderItemOneToMany.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  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. using PRSDesktop.Panels.PurchaseOrders;
  13. using Syncfusion.Windows.Tools.Controls;
  14. namespace PRSDesktop;
  15. public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseOrder,PurchaseOrderItem>
  16. {
  17. private Button bill;
  18. private Button? catalog;
  19. private Button? createConsignment;
  20. //private Button? viewconsign;
  21. private Button receive;
  22. private Button assignLocation;
  23. public List<Tuple<PurchaseOrderItem, PurchaseOrderItemAllocation>> Allocations { get; private set; } = new();
  24. private bool _simplified = false;
  25. public bool Simplified
  26. {
  27. get => _simplified;
  28. set
  29. {
  30. _simplified = value;
  31. UpdateButtonVisibility();
  32. }
  33. }
  34. public SupplierPurchaseOrderItemOneToMany() : base()
  35. {
  36. HiddenColumns.Add(x => x.ID);
  37. HiddenColumns.Add(x => x.Description);
  38. HiddenColumns.Add(x => x.TaxRate);
  39. HiddenColumns.Add(x => x.ExTax);
  40. HiddenColumns.Add(x => x.Tax);
  41. HiddenColumns.Add(x => x.IncTax);
  42. HiddenColumns.Add(x => x.ReceivedDate);
  43. HiddenColumns.Add(x => x.Qty);
  44. HiddenColumns.Add(x => x.Balance);
  45. HiddenColumns.Add(x => x.PORevision);
  46. HiddenColumns.Add(x => x.DueDate);
  47. HiddenColumns.Add(x => x.SupplierCode);
  48. HiddenColumns.Add(x => x.BillLine.ID);
  49. HiddenColumns.Add(x => x.Consignment.ID);
  50. HiddenColumns.Add(x => x.StockLocation.ID);
  51. HiddenColumns.Add(x => x.PurchaseGL.ID);
  52. HiddenColumns.Add(x => x.CostCentre.ID);
  53. HiddenColumns.Add(x => x.Product.ID);
  54. HiddenColumns.Add(x => x.Product.Code);
  55. HiddenColumns.Add(x => x.Product.Name);
  56. HiddenColumns.Add(x => x.Style.ID);
  57. HiddenColumns.Add(x => x.Job.ID);
  58. HiddenColumns.Add(x => x.TaxCode.ID);
  59. HiddenColumns.Add(x => x.TaxCode.Code);
  60. HiddenColumns.Add(x => x.TaxCode.Description);
  61. HiddenColumns.Add(x => x.TaxCode.Rate);
  62. HiddenColumns.Add(x => x.PostedReference);
  63. foreach (var column in Columns.None<PurchaseOrderItem>()
  64. .AddDimensionsColumns(x => x.Dimensions, Dimensions.ColumnsType.Local))
  65. {
  66. HiddenColumns.Add(column);
  67. }
  68. HiddenColumns.Add(x => x.PurchaseOrderLink.SupplierLink.ID);
  69. HiddenColumns.Add(x => x.PurchaseOrderLink.Category.ID);
  70. HiddenColumns.Add(x => x.Product.DigitalForm.ID);
  71. HiddenColumns.Add(x => x.Product.DigitalForm.Description);
  72. HiddenColumns.Add(x => x.Product.Image.ID);
  73. HiddenColumns.Add(x => x.Product.Image.FileName);
  74. ActionColumns.Add(new DynamicImageManagerColumn<PurchaseOrderItem>(this, x => x.Product.Image, true)
  75. { Position = DynamicActionColumnPosition.Start });
  76. HiddenColumns.Add(x => x.FormCount);
  77. HiddenColumns.Add(x => x.OpenForms);
  78. ActionColumns.Add(new DynamicMenuColumn(BuildFormsMenu) { Position = DynamicActionColumnPosition.End });
  79. ActionColumns.Add(new DynamicImageColumn(FormsImage) { Position = DynamicActionColumnPosition.Start, ToolTip = FormsToolTip });
  80. }
  81. private void UpdateButtonVisibility()
  82. {
  83. if (Simplified)
  84. {
  85. if(catalog is not null)
  86. catalog.Visibility = Visibility.Collapsed;
  87. if(createConsignment is not null)
  88. createConsignment.Visibility = Visibility.Collapsed;
  89. if(receive is not null)
  90. receive.Visibility = Visibility.Collapsed;
  91. if(bill is not null)
  92. bill.Visibility = Visibility.Collapsed;
  93. if(assignLocation is not null)
  94. assignLocation.Visibility = Visibility.Collapsed;
  95. }
  96. else
  97. {
  98. if(catalog is not null)
  99. catalog.Visibility = Visibility.Visible;
  100. if(createConsignment is not null)
  101. createConsignment.Visibility = Visibility.Visible;
  102. if(receive is not null)
  103. receive.Visibility = Visibility.Visible;
  104. if(bill is not null)
  105. bill.Visibility = Visibility.Visible;
  106. if(assignLocation is not null)
  107. assignLocation.Visibility = Visibility.Visible;
  108. }
  109. }
  110. protected override void Init()
  111. {
  112. base.Init();
  113. catalog = AddButton("Catalog", PRSDesktop.Resources.product.AsBitmapImage(), SearchProducts);
  114. if (Security.IsAllowed<CanViewConsignmentModule>())
  115. {
  116. createConsignment = AddButton("Add to Consignment", null, AddToConsignment);
  117. createConsignment.IsEnabled = false;
  118. }
  119. receive = AddButton("Receive Items", null, ReceiveItems);
  120. receive.IsEnabled = false;
  121. bill = AddButton("Enter Bill", null, EnterBill);
  122. bill.IsEnabled = false;
  123. assignLocation = AddButton("Assign Location", null, AssignLocation);
  124. }
  125. protected override void DoReconfigure(DynamicGridOptions options)
  126. {
  127. base.DoReconfigure(options);
  128. if (!ReadOnly && Security.CanEdit<PurchaseOrderItem>())
  129. {
  130. options.DirectEdit = true;
  131. options.DragTarget = true;
  132. }
  133. //if (!IsDirectEditMode(options))
  134. //{
  135. options.FilterRows = true;
  136. //}
  137. }
  138. protected override void OnAfterRefresh()
  139. {
  140. base.OnAfterRefresh();
  141. Allocations.RemoveAll(x => !Items.Contains(x.Item1));
  142. }
  143. protected override void DoValidate(PurchaseOrderItem[] items, List<string> errors)
  144. {
  145. base.DoValidate(items, errors);
  146. foreach(var item in items)
  147. {
  148. if(Allocations.Any(x => x.Item1 == item && x.Item2.JobRequisitionItem.ID == Guid.Empty && x.Item2.Job.ID == item.Job.ID))
  149. {
  150. errors.Add("At least one allocation on this purchase order item is invalid due to having the same [Job] as the purchase order item.");
  151. }
  152. }
  153. }
  154. public override void AfterSave(object item)
  155. {
  156. base.AfterSave(item);
  157. var toSave = new List<PurchaseOrderItemAllocation>();
  158. foreach (var poItem in Items)
  159. {
  160. var allocations = Allocations.Where(x => x.Item1 == poItem).Select(x => x.Item2);
  161. foreach (var allocation in allocations)
  162. {
  163. allocation.Item.ID = poItem.ID;
  164. }
  165. toSave.AddRange(allocations);
  166. }
  167. Client.Save(toSave, "");
  168. }
  169. private void BuildFormsMenu(DynamicMenuColumn column, CoreRow? row)
  170. {
  171. if (row == null) return;
  172. var poiID = row.Get<PurchaseOrderItem, Guid>(x => x.ID);
  173. if (Security.CanEdit<PurchaseOrderItem>())
  174. {
  175. column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine, enabled: row.Get<PurchaseOrderItem, DateTime>(x => x.ReceivedDate).IsEmpty());
  176. }
  177. if(row.Get<PurchaseOrderItem, Guid>(x => x.Consignment.ID) != Guid.Empty)
  178. {
  179. column.AddItem("View Consignment", null, ViewConsignment);
  180. }
  181. if(poiID != Guid.Empty)
  182. {
  183. var formsItem = column.AddItem("Digital Forms", PRSDesktop.Resources.kanban, null);
  184. DynamicGridUtils.PopulateFormMenu<PurchaseOrderItemForm, PurchaseOrderItem, PurchaseOrderItemLink>(
  185. formsItem,
  186. poiID,
  187. row.ToObject<PurchaseOrderItem>);
  188. }
  189. }
  190. private void SplitLine(CoreRow? row)
  191. {
  192. if (row is null)
  193. return;
  194. var grid = new SupplierPurchaseOrderItemSplitGrid();
  195. var poi = LoadItem(row);
  196. var _allocations = Allocations.Where(x => x.Item1 == poi).Select(x => x.Item2).ToList();
  197. if(poi.ID != Guid.Empty)
  198. {
  199. // Otherwise, they'll all be in the database.
  200. _allocations.AddRange(Client.Query(
  201. new Filter<PurchaseOrderItemAllocation>(x => x.Item.ID).IsEqualTo(poi.ID)
  202. .And(x => x.ID).NotInList(_allocations.ToArray(x => x.ID)),
  203. Columns.None<PurchaseOrderItemAllocation>()
  204. .Add(x => x.ID)
  205. .Add(x => x.Job.ID).Add(x => x.JobRequisitionItem.ID).Add(x => x.Quantity))
  206. .ToObjects<PurchaseOrderItemAllocation>());
  207. }
  208. var items = _allocations.ToList(SupplierPurchaseOrderItemSplit.FromAllocation);
  209. if (SupplierPurchaseOrderItemSplitWindow.Execute(poi.Qty, items, out var value))
  210. {
  211. var newLine = CreateItem();
  212. newLine.BillLine.CopyFrom(poi.BillLine);
  213. newLine.StockLocation.CopyFrom(poi.StockLocation);
  214. newLine.Consignment.CopyFrom(poi.Consignment);
  215. newLine.PurchaseGL.CopyFrom(poi.PurchaseGL);
  216. newLine.CostCentre.CopyFrom(poi.CostCentre);
  217. newLine.Product.CopyFrom(poi.Product);
  218. newLine.Style.CopyFrom(poi.Style);
  219. newLine.Job.CopyFrom(poi.Job);
  220. newLine.TaxCode.CopyFrom(poi.TaxCode);
  221. newLine.PurchaseOrderLink.CopyFrom(poi.PurchaseOrderLink);
  222. newLine.Dimensions.CopyFrom(poi.Dimensions);
  223. // Copying across the posted reference; this would be a problem if we were to try to sync via a Poster two purchase order items with the
  224. // original posted single line; however, after discussing with Frank, we can't imagine a place where we would do this; we would generally replace
  225. // the posted lines. Hence, we can copy the posted reference.
  226. newLine.PostedReference = poi.PostedReference;
  227. newLine.Description = poi.Description;
  228. newLine.TaxRate = poi.TaxRate;
  229. newLine.ExTax = poi.ExTax;
  230. newLine.Tax = poi.Tax;
  231. newLine.IncTax = poi.IncTax;
  232. newLine.Cost = poi.Cost;
  233. newLine.Balance = poi.Balance;
  234. newLine.PORevision = poi.PORevision;
  235. newLine.DueDate = poi.DueDate;
  236. newLine.SupplierCode = poi.SupplierCode;
  237. newLine.Qty = poi.Qty - value;
  238. poi.Qty = value;
  239. foreach(var item in items)
  240. {
  241. var allocation = _allocations.FirstOrDefault(x => x.ID == item.AllocationID);
  242. if (allocation is null) continue;
  243. // Add to a list to be saved later.
  244. var newAllocation = new PurchaseOrderItemAllocation();
  245. newAllocation.Job.CopyFrom(allocation.Job);
  246. newAllocation.JobRequisitionItem.CopyFrom(allocation.JobRequisitionItem);
  247. newAllocation.Quantity = allocation.Quantity - item.SplitQuantity;
  248. allocation.Quantity = item.SplitQuantity;
  249. // Save both allocations.
  250. if(!Allocations.Any(x => x.Item2 == allocation))
  251. {
  252. Allocations.Add(new(poi, allocation));
  253. }
  254. Allocations.Add(new(newLine, newAllocation));
  255. }
  256. SaveItem(poi);
  257. SaveItem(newLine);
  258. Refresh(false, true);
  259. DoChanged();
  260. }
  261. }
  262. private void ViewConsignment(CoreRow? row)
  263. {
  264. if (row is null) return;
  265. var consignmentID = row.Get<PurchaseOrderItem, Guid>(x => x.Consignment.ID);
  266. var consignments = Client.Query(
  267. new Filter<Consignment>(x => x.ID).IsEqualTo(consignmentID),
  268. DynamicGridUtils.LoadEditorColumns(Columns.None<Consignment>()))
  269. .ToObjects<Consignment>().ToArray();
  270. DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(Consignment)).EditItems(consignments);
  271. }
  272. private FrameworkElement? FormsToolTip(DynamicActionColumn arg1, CoreRow? arg2)
  273. {
  274. var text = arg2 == null || arg2.Get<PurchaseOrderItem, Guid>(x => x.Product.DigitalForm.ID) == Guid.Empty
  275. ? ""
  276. : arg2.Get<PurchaseOrderItem, int>(c => c.FormCount) == 0
  277. ? "No forms found for this item"
  278. : arg2.Get<PurchaseOrderItem, int>(c => c.OpenForms) > 0
  279. ? "Incomplete forms found"
  280. : "All forms completed";
  281. return string.IsNullOrWhiteSpace(text) ? null : arg1.TextToolTip(text);
  282. }
  283. private BitmapImage? FormsImage(CoreRow? arg)
  284. {
  285. if (arg == null)
  286. return PRSDesktop.Resources.quality.AsBitmapImage();
  287. return arg.Get<PurchaseOrderItem, Guid>(x => x.Product.DigitalForm.ID) == Guid.Empty
  288. ? null
  289. : arg.Get<PurchaseOrderItem, int>(c => c.FormCount) == 0
  290. ? PRSDesktop.Resources.warning.AsBitmapImage()
  291. : arg.Get<PurchaseOrderItem, int>(c => c.OpenForms) > 0
  292. ? PRSDesktop.Resources.warning.AsBitmapImage()
  293. : PRSDesktop.Resources.quality.AsBitmapImage();
  294. }
  295. private bool AddToConsignment(Button sender, CoreRow[] rows)
  296. {
  297. if (!rows.Any())
  298. {
  299. MessageBox.Show("Please select a row first");
  300. return false;
  301. }
  302. var poItems = LoadItems(rows);
  303. if(poItems.Any(x => x.ID == Guid.Empty))
  304. {
  305. MessageBox.Show("Please save this purchase order first.");
  306. return false;
  307. }
  308. var menu = new ContextMenu();
  309. menu.AddItem("New Consignment", null, () =>
  310. {
  311. var consign = new Consignment();
  312. consign.Supplier.ID = rows.First().Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.SupplierLink.ID);
  313. consign.Category.ID = rows.First().Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.Category.ID);
  314. if (new DynamicDataGrid<Consignment>().EditItems(new[] { consign }, LoadConsignmentLines, true))
  315. {
  316. foreach (var item in poItems)
  317. {
  318. item.Consignment.ID = consign.ID;
  319. }
  320. new Client<PurchaseOrderItem>().Save(poItems, "Added to new consignment");
  321. Refresh(false, true);
  322. }
  323. });
  324. menu.AddItem("Existing Consignment", null, () =>
  325. {
  326. var popupList = new PopupList(typeof(Consignment), Guid.Empty, Array.Empty<string>());
  327. popupList.OnDefineFilter += type =>
  328. {
  329. return new Filter<Consignment>(x => x.Closed).IsNotEqualTo(DateTime.MinValue);
  330. };
  331. if (popupList.ShowDialog() == true)
  332. {
  333. foreach (var item in poItems)
  334. {
  335. item.Consignment.ID = popupList.ID;
  336. }
  337. new Client<PurchaseOrderItem>().Save(poItems, "Added to existing consignment");
  338. Refresh(false, true);
  339. }
  340. });
  341. menu.IsOpen = true;
  342. return false;
  343. }
  344. private CoreTable? LoadConsignmentLines(Type type)
  345. {
  346. if (type == typeof(PurchaseOrderItem))
  347. {
  348. var result = new CoreTable();
  349. result.LoadColumns(typeof(PurchaseOrderItem));
  350. result.LoadRows(SelectedRows);
  351. return result;
  352. }
  353. else
  354. {
  355. return null;
  356. }
  357. }
  358. private static bool EnterBill(Button sender, CoreRow[] rows)
  359. {
  360. if (!rows.Any())
  361. {
  362. MessageBox.Show("Please select a row first");
  363. return false;
  364. }
  365. var bill = new Bill();
  366. bill.SupplierLink.ID = rows.First()
  367. .Get<PurchaseOrderItem, Guid>(x => x.PurchaseOrderLink.SupplierLink.ID);
  368. bill.BillDate = DateTime.Today;
  369. return new DynamicDataGrid<Bill>().EditItems(new[] { bill }, (type) =>
  370. {
  371. return LoadBillLines(type, rows);
  372. }, true);
  373. }
  374. private static CoreTable LoadBillLines(Type type, CoreRow[] rows)
  375. {
  376. var result = new CoreTable();
  377. result.LoadColumns(typeof(BillLine));
  378. foreach (var row in rows)
  379. {
  380. var billrow = result.NewRow();
  381. billrow.Set<BillLine, Guid>(x => x.OrderItem.ID, row.Get<PurchaseOrderItem, Guid>(x => x.ID));
  382. var description = new List<string>();
  383. if (row.Get<PurchaseOrderItem, Guid>(x => x.Product.ID) != Guid.Empty)
  384. description.Add(string.Format("{0} : {1}", row.Get<PurchaseOrderItem, string>(x => x.Product.Code),
  385. row.Get<PurchaseOrderItem, string>(x => x.Product.Name)));
  386. var Description = row.Get<PurchaseOrderItem, string>(x => x.Description);
  387. if (!string.IsNullOrEmpty(Description))
  388. description.Add(Description);
  389. billrow.Set<BillLine, string>(x => x.Description, string.Join("\n", description));
  390. billrow.Set<BillLine, Guid>(x => x.TaxCode.ID, row.Get<PurchaseOrderItem, Guid>(x => x.TaxCode.ID));
  391. billrow.Set<BillLine, string>(x => x.TaxCode.Code, row.Get<PurchaseOrderItem, string>(x => x.TaxCode.Code));
  392. billrow.Set<BillLine, string>(x => x.TaxCode.Description, row.Get<PurchaseOrderItem, string>(x => x.TaxCode.Description));
  393. billrow.Set<BillLine, double>(x => x.TaxCode.Rate, row.Get<PurchaseOrderItem, double>(x => x.TaxCode.Rate));
  394. billrow.Set<BillLine, double>(x => x.TaxRate, row.Get<PurchaseOrderItem, double>(x => x.TaxRate));
  395. billrow.Set<BillLine, double>(x => x.ExTax, row.Get<PurchaseOrderItem, double>(x => x.ExTax));
  396. billrow.Set<BillLine, double>(x => x.Tax, row.Get<PurchaseOrderItem, double>(x => x.Tax));
  397. billrow.Set<BillLine, double>(x => x.IncTax, row.Get<PurchaseOrderItem, double>(x => x.IncTax));
  398. result.Rows.Add(billrow);
  399. }
  400. return result;
  401. }
  402. private bool ReceiveItems(Button sender, CoreRow[] rows)
  403. {
  404. if (!rows.Any())
  405. {
  406. MessageBox.Show("Please select a row first");
  407. return false;
  408. }
  409. var now = DateTime.Now;
  410. using (new WaitCursor())
  411. {
  412. var items = LoadItems(rows);
  413. foreach (var item in items)
  414. item.ReceivedDate = item.ReceivedDate.IsEmpty() ? now : DateTime.MinValue;
  415. now = items.Select(x => x.ReceivedDate).FirstOrDefault();
  416. new Client<PurchaseOrderItem>().Save(items, now.IsEmpty() ? "Cleared Received Date" : $"Updated Received Date to {now:g}");
  417. }
  418. return true;
  419. }
  420. public static bool AssignLocation(Button btn, CoreRow[] rows)
  421. {
  422. if (!rows.Any())
  423. {
  424. MessageBox.Show("Please select at least one row to assign");
  425. return false;
  426. }
  427. var menu = new ContextMenu();
  428. menu.AddItem("Create New Location", null, () =>
  429. {
  430. var grid = new StockLocationGrid();
  431. var location = new StockLocation();
  432. if (grid.EditItems(new StockLocation[] { location }))
  433. AssignLocationToItems(location.ID, rows);
  434. });
  435. menu.AddItem("Choose Existing", null, () =>
  436. {
  437. var popup = new PopupList(typeof(StockLocation), Guid.Empty, new string[] { });
  438. if (popup.ShowDialog() == true)
  439. AssignLocationToItems(popup.ID, rows);
  440. });
  441. menu.IsOpen = true;
  442. return true;
  443. }
  444. private static void AssignLocationToItems(Guid locationID, CoreRow[] rows)
  445. {
  446. var items = new List<PurchaseOrderItem>();
  447. foreach (CoreRow row in rows)
  448. {
  449. var item = row.ToObject<PurchaseOrderItem>();
  450. item.StockLocation.ID = locationID;
  451. items.Add(item);
  452. }
  453. Client.Save(items, "Added stock location from PurchaseOrderItem Grid");
  454. }
  455. /*private StockLocation[] QueryLocations()
  456. {
  457. CoreTable table = new Client<StockLocation>().Query(new Filter<StockLocation>(x => x.Active).IsEqualTo(true), new Columns<StockLocation>(x => x.ID));
  458. List<StockLocation> locations = new List<StockLocation>();
  459. foreach (CoreRow row in table.Rows)
  460. locations.Add(row.ToObject<StockLocation>());
  461. return locations.ToArray();
  462. }*/
  463. protected override void SelectItems(CoreRow[]? rows)
  464. {
  465. // Check if we can actually edit the selected lines
  466. var _editable = !ReadOnly && Security.CanEdit<Consignment>();
  467. // Check to ensure NO selected lines are received
  468. var _allunreceived = rows?.All(r => r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty()) == true;
  469. // Check to ensure ALL select lines are received
  470. var _allreceived = rows?.All(r => !r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty()) == true;
  471. // Check to ensure NO selected lines are already linked to a PO
  472. var _anyconsigmments = rows?.Any(r => r.Get<PurchaseOrderItem,Guid>(c=>c.Consignment.ID) != Guid.Empty) == true;
  473. // Check to Ensure NO selected lines are already linked to a Bill
  474. var _anybills = rows?.Any(r => r.Get<PurchaseOrderItem,Guid>(c=>c.BillLine.ID) != Guid.Empty) == true;
  475. if (createConsignment != null)
  476. {
  477. createConsignment.IsEnabled =
  478. _anyconsigmments == false
  479. && _allunreceived
  480. && _editable;
  481. }
  482. receive.Content = _allreceived
  483. ? "Un-Receive Items"
  484. : "Receive Items";
  485. receive.IsEnabled =
  486. _anyconsigmments == false
  487. && (_allreceived || _allunreceived)
  488. && _editable;
  489. bill.IsEnabled =
  490. _anybills == false
  491. && _editable;
  492. assignLocation.IsEnabled =
  493. rows != null && !ReadOnly && Security.CanEdit<PurchaseOrderItem>();
  494. base.SelectItems(rows);
  495. }
  496. private bool SearchProducts(Button button, CoreRow[] rows)
  497. {
  498. var dlg = new MultiSelectDialog<ProductInstance>(
  499. new Filter<ProductInstance>().All(),
  500. Columns.None<ProductInstance>()
  501. .Add(x => x.ID)
  502. .Add(x => x.Product.ID)
  503. .Add(x => x.Product.Code)
  504. .Add(x => x.Product.Name)
  505. .Add(x => x.Product.TaxCode.ID)
  506. .Add(x => x.Product.TaxCode.Code)
  507. .Add(x => x.Product.TaxCode.Description)
  508. .Add(x => x.Product.TaxCode.Rate)
  509. .Add(x => x.Product.PurchaseGL.ID)
  510. .Add(x => x.Product.PurchaseGL.Code)
  511. .Add(x => x.Product.PurchaseGL.Description)
  512. .Add(x => x.Product.CostCentre.ID)
  513. .Add(x => x.Product.CostCentre.Code)
  514. .Add(x => x.Product.CostCentre.Description)
  515. .Add(x => x.Style.ID)
  516. .Add(x => x.Style.Code)
  517. .Add(x => x.Style.Description)
  518. .AddDimensionsColumns(x => x.Dimensions)
  519. .Add(x => x.NettCost),
  520. false
  521. );
  522. if (dlg.ShowDialog() == true)
  523. {
  524. CreateItems(() =>
  525. {
  526. var result = new List<PurchaseOrderItem>();
  527. var pi = dlg.Data().Rows.FirstOrDefault()?.ToObject<ProductInstance>();
  528. if (pi == null)
  529. return result;
  530. var sp = new Client<SupplierProduct>().Query(
  531. new Filter<SupplierProduct>(x => x.Product.ID).IsEqualTo(pi.Product.ID)
  532. .And(x=>x.Style.ID).IsEqualTo(pi.Style.ID)
  533. .And(x=>x.Dimensions).DimensionEquals(pi.Dimensions),
  534. Columns.None<SupplierProduct>().Add(x => x.ID)
  535. .Add(x => x.Product.ID)
  536. .Add(x => x.Product.Code)
  537. .Add(x => x.Product.Name)
  538. .Add(x => x.Product.TaxCode.ID)
  539. .Add(x => x.Product.TaxCode.Code)
  540. .Add(x => x.Product.TaxCode.Description)
  541. .Add(x => x.Product.TaxCode.Rate)
  542. .Add(x => x.Product.PurchaseGL.ID)
  543. .Add(x => x.Product.PurchaseGL.Code)
  544. .Add(x => x.Product.PurchaseGL.Description)
  545. .Add(x => x.Product.CostCentre.ID)
  546. .Add(x => x.Product.CostCentre.Code)
  547. .Add(x => x.Product.CostCentre.Description)
  548. .Add(x => x.Style.ID)
  549. .Add(x => x.Style.Code)
  550. .Add(x => x.Style.Description)
  551. .Add(x=>x.Job.ID)
  552. .Add(x=>x.Job.JobNumber)
  553. .Add(x=>x.Job.Name)
  554. .AddDimensionsColumns(x => x.Dimensions)
  555. .Add(x => x.CostPrice)
  556. .Add(x => x.ForeignCurrencyPrice)
  557. ).Rows.FirstOrDefault()?.ToObject<SupplierProduct>();
  558. if (sp != null)
  559. {
  560. var poi = CreateItem();
  561. poi.Product.CopyFrom(sp.Product);
  562. poi.Style.CopyFrom(sp.Style);
  563. poi.Dimensions.CopyFrom(sp.Dimensions);
  564. poi.ForeignCurrencyCost = sp.ForeignCurrencyPrice;
  565. poi.Cost = sp.CostPrice;
  566. poi.CostCentre.CopyFrom(sp.Product.CostCentre);
  567. poi.PurchaseGL.CopyFrom(sp.Product.PurchaseGL);
  568. poi.Description = sp.Product.Name;
  569. poi.Job.CopyFrom(sp.Job);
  570. result.Add(poi);
  571. }
  572. else if (pi != null)
  573. {
  574. var poi = CreateItem();
  575. poi.Product.CopyFrom(pi.Product);
  576. poi.Style.CopyFrom(pi.Style);
  577. poi.Dimensions.CopyFrom(pi.Dimensions);
  578. poi.Cost = pi.NettCost;
  579. poi.CostCentre.CopyFrom(pi.Product.CostCentre);
  580. poi.PurchaseGL.CopyFrom(pi.Product.PurchaseGL);
  581. poi.Description = pi.Product.Name;
  582. result.Add(poi);
  583. }
  584. return result;
  585. });
  586. }
  587. return false;
  588. }
  589. protected override void DoAdd(bool OpenEditorOnDirectEdit = false)
  590. {
  591. var dlg = new MultiSelectDialog<SupplierProduct>(
  592. new Filter<SupplierProduct>(x => x.SupplierLink.ID).IsEqualTo(Item.SupplierLink.ID),
  593. Columns.None<SupplierProduct>().Add(x => x.ID)
  594. .Add(x => x.Product.ID)
  595. .Add(x => x.Product.Code)
  596. .Add(x => x.Product.Name)
  597. .Add(x => x.Product.TaxCode.ID)
  598. .Add(x => x.Product.TaxCode.Code)
  599. .Add(x => x.Product.TaxCode.Description)
  600. .Add(x => x.Product.TaxCode.Rate)
  601. .Add(x => x.Product.PurchaseGL.ID)
  602. .Add(x => x.Product.PurchaseGL.Code)
  603. .Add(x => x.Product.PurchaseGL.Description)
  604. .Add(x => x.Product.CostCentre.ID)
  605. .Add(x => x.Product.CostCentre.Code)
  606. .Add(x => x.Product.CostCentre.Description)
  607. .Add(x => x.Style.ID)
  608. .Add(x => x.Style.Code)
  609. .Add(x => x.Style.Description)
  610. .Add(x=>x.Job.ID)
  611. .Add(x=>x.Job.JobNumber)
  612. .Add(x=>x.Job.Name)
  613. .AddDimensionsColumns(x => x.Dimensions)
  614. .Add(x => x.CostPrice)
  615. .Add(x => x.ForeignCurrencyPrice),
  616. false
  617. );
  618. if (dlg.ShowDialog() == true)
  619. {
  620. CreateItems(() =>
  621. {
  622. var result = new List<PurchaseOrderItem>();
  623. var sp = dlg.Data().Rows.FirstOrDefault()?.ToObject<SupplierProduct>();
  624. if (sp != null)
  625. {
  626. var poi = CreateItem();
  627. poi.Product.ID = sp.Product.ID;
  628. poi.Product.Synchronise(sp.Product);
  629. poi.Style.ID = sp.Style.ID;
  630. poi.Style.Synchronise(sp.Style);
  631. poi.Dimensions.CopyFrom(sp.Dimensions);
  632. poi.ForeignCurrencyCost = sp.ForeignCurrencyPrice;
  633. poi.Cost = sp.CostPrice;
  634. poi.CostCentre.CopyFrom(sp.Product.CostCentre);
  635. poi.PurchaseGL.CopyFrom(sp.Product.PurchaseGL);
  636. poi.Job.CopyFrom(sp.Job);
  637. poi.Description = sp.Product.Name;
  638. result.Add(poi);
  639. }
  640. return result;
  641. });
  642. }
  643. }
  644. protected override void ConfigureColumns(DynamicGridColumns columns)
  645. {
  646. base.ConfigureColumns(columns);
  647. var editor = columns
  648. .FirstOrDefault(x => String.Equals(x.ColumnName, nameof(BillLine.ForeignCurrencyCost)))?.Editor as CurrencyEditor;
  649. if (editor != null)
  650. editor.CurrencySymbol = Item.SupplierLink.Currency.Symbol;
  651. }
  652. private readonly Column<PurchaseOrderItem> receivedDateColumn = new Column<PurchaseOrderItem>(x => x.ReceivedDate);
  653. protected override void CustomiseEditor(PurchaseOrderItem[] items, DynamicGridColumn column, BaseEditor editor)
  654. {
  655. base.CustomiseEditor(items, column, editor);
  656. if(items.Any(x => x.ReceivedDate != DateTime.MinValue) && !receivedDateColumn.IsEqualTo(column.ColumnName) && editor.Editable == Editable.Enabled)
  657. {
  658. editor.Editable = editor.Editable.Combine(Editable.Disabled);
  659. }
  660. }
  661. protected override void DoReconfigureEditors(DynamicEditorGrid grid, PurchaseOrderItem[] items)
  662. {
  663. base.DoReconfigureEditors(grid, items);
  664. if(!items.Any(x => x.ReceivedDate != DateTime.MinValue))
  665. {
  666. if (grid.FindEditor(nameof(PurchaseOrderItem.ForeignCurrencyCost)) is CurrencyEditorControl fcp)
  667. {
  668. fcp.SetEnabled(items.All(x => x.PurchaseOrderLink.SupplierLink.Currency.ID != Guid.Empty));
  669. fcp.CurrencySymbol = items.First().PurchaseOrderLink.SupplierLink.Currency.Symbol;
  670. }
  671. var tp = grid.FindEditor(nameof(PurchaseOrderItem.Cost));
  672. tp?.SetEnabled(items.All(x=>x.PurchaseOrderLink.SupplierLink.Currency.ID == Guid.Empty));
  673. }
  674. }
  675. protected override void OnAfterEditorValueChanged(DynamicEditorGrid? grid, PurchaseOrderItem[] items, AfterEditorValueChangedArgs args, Dictionary<string, object?> changes)
  676. {
  677. base.OnAfterEditorValueChanged(grid, items, args, changes);
  678. if (args.ColumnName.Equals("Product.ID") || args.ColumnName.Equals("Job.ID") || args.ColumnName.StartsWith("Dimensions.") || args.ColumnName.Equals("Style.ID"))
  679. {
  680. PurchaseOrder.UpdateCosts(
  681. items,
  682. Item.SupplierLink.ID,
  683. changes,
  684. args.ColumnName
  685. );
  686. }
  687. }
  688. protected override Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm editor, PurchaseOrderItem[] items, string name,
  689. object value)
  690. {
  691. var results = base.EditorValueChanged(editor, items, name, value);
  692. if (name.Equals("ProductLink.TaxCode.ID"))
  693. DynamicGridUtils.UpdateEditorValue(items, "TaxCode.ID", (Guid)value, results);
  694. return results;
  695. }
  696. }