SupplierPurchaseOrderItemOneToMany.cs 29 KB

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