SupplierPurchaseOrderItemOneToMany.cs 29 KB

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