SupplierPurchaseOrderItemOneToMany.cs 21 KB

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