JobSummaryGrid.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using Comal.Classes;
  9. using InABox.Clients;
  10. using InABox.Core;
  11. using InABox.DynamicGrid;
  12. namespace PRSDesktop
  13. {
  14. internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IJobControl, IDataModelSource
  15. {
  16. private bool _stylecolumnVisible;
  17. private bool _includeReserves;
  18. public JobSummaryGrid() : base()
  19. {
  20. Options.AddRange(
  21. DynamicGridOption.RecordCount,
  22. DynamicGridOption.SelectColumns,
  23. DynamicGridOption.FilterRows,
  24. DynamicGridOption.ExportData,
  25. DynamicGridOption.MultiSelect
  26. );
  27. HiddenColumns.Add(x => x.Product.ID);
  28. HiddenColumns.Add(x => x.Style.ID);
  29. HiddenColumns.Add(x => x.Dimensions.UnitSize);
  30. HiddenColumns.Add(x => x.BillOfMaterials);
  31. HiddenColumns.Add(x => x.Requisitions);
  32. HiddenColumns.Add(x => x.PickingLists);
  33. HiddenColumns.Add(x => x.Issued);
  34. HiddenColumns.Add(x => x.ReservedStock);
  35. HiddenColumns.Add(x => x.OnOrder);
  36. HiddenColumns.Add(x => x.JobShortage);
  37. HiddenColumns.Add(x => x.FreeOnHand);
  38. HiddenColumns.Add(x => x.FreeOnOrder);
  39. HiddenColumns.Add(x => x.FreeStockTotal);
  40. HiddenColumns.Add(x => x.FreeStockShortage);
  41. HiddenColumns.Add(x => x.Product.Image.ID);
  42. HiddenColumns.Add(x => x.Product.Image.FileName);
  43. ActionColumns.Add(new DynamicImageManagerColumn<JobMaterial>(this, x => x.Product.Image, false)
  44. { Position = DynamicActionColumnPosition.Start });
  45. AddButton("Include Reserves", null, ToggleIncludeReserves);
  46. OnCellDoubleClick += JobSummaryGrid_OnCellDoubleClick;
  47. }
  48. private bool ToggleIncludeReserves(Button sender, CoreRow[] arg2)
  49. {
  50. _includeReserves = !_includeReserves;
  51. UpdateButton(sender, null, _includeReserves ? "Free Stock Only" : "Include Reserves");
  52. return true;
  53. }
  54. protected override void GenerateColumns(DynamicGridColumns columns)
  55. {
  56. columns.Add<JobMaterial, string>(x => x.Product.Group.Code, 120, "Group Code", "", Alignment.MiddleCenter);
  57. columns.Add<JobMaterial, string>(x => x.Product.Code, 200, "Product Code", "", Alignment.MiddleCenter);
  58. columns.Add<JobMaterial, string>(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleCenter);
  59. columns.Add<JobMaterial, string>(x => x.Dimensions.UnitSize, 120, "UOM", "", Alignment.MiddleCenter);
  60. columns.Add<JobMaterial, double>(x => x.BillOfMaterials, 80, "BOM", "", Alignment.MiddleCenter);
  61. columns.Add<JobMaterial, double>(x => x.Requisitions, 80, "Req.", "", Alignment.MiddleCenter);
  62. columns.Add<JobMaterial, double>(x => x.PickingLists, 80, "P/L", "", Alignment.MiddleCenter);
  63. columns.Add<JobMaterial, double>(x => x.Issued, 80, "Issued", "", Alignment.MiddleCenter);
  64. columns.Add<JobMaterial, double>(x => x.ReservedStock, 80, "Reserved", "", Alignment.MiddleCenter);
  65. columns.Add<JobMaterial, double>(x => x.OnOrder, 80, "Ordered", "", Alignment.MiddleCenter);
  66. columns.Add<JobMaterial, double>(x => x.JobShortage, 80, "Shortage", "", Alignment.MiddleCenter);
  67. columns.Add<JobMaterial, double>(x => x.FreeStockTotal, 80, "Free Stock", "", Alignment.MiddleCenter);
  68. }
  69. private void ShowDetailGrid<TEntity>(
  70. String columnname,
  71. Expression<Func<TEntity,object?>> productcol,
  72. Guid productid,
  73. Expression<Func<TEntity,object?>> stylecol,
  74. Guid? styleid,
  75. Expression<Func<TEntity,object?>> unitcol,
  76. String unitsize,
  77. Expression<Func<TEntity,object?>>? jobcol,
  78. Filter<TEntity>? extrafilter,
  79. Func<CoreRow,bool>? rowfilter
  80. )
  81. {
  82. var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
  83. if (grid == null)
  84. {
  85. MessageBox.Show($"Cannot create Grid for [{typeof(TEntity).Name}]");
  86. return;
  87. }
  88. grid.ColumnsTag = $"{ColumnsTag}.{columnname}";
  89. grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
  90. grid.OnDefineFilter += t =>
  91. {
  92. var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
  93. .And(unitcol).IsEqualTo(unitsize);
  94. if (styleid.HasValue)
  95. filter = filter.And(stylecol).IsEqualTo(styleid);
  96. if (jobcol != null)
  97. filter = filter.And(jobcol).IsEqualTo(ParentID);
  98. if (extrafilter != null)
  99. filter = filter.And(extrafilter);
  100. return filter;
  101. };
  102. grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
  103. var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", (grid as BaseDynamicGrid)!);
  104. window.ShowDialog();
  105. }
  106. private void JobSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
  107. {
  108. Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
  109. Guid? styleid = _stylecolumnVisible ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
  110. String unitsize = args.Row.Get<StockSummary, String>(c => c.Dimensions.UnitSize);
  111. if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.BillOfMaterials, ".")))
  112. {
  113. ShowDetailGrid<JobBillOfMaterialsItem>(
  114. args.Column.ColumnName,
  115. x => x.Product.ID,
  116. productid,
  117. x => x.Style.ID,
  118. styleid,
  119. x=>x.Dimensions.UnitSize,
  120. unitsize,
  121. x => x.Job.ID,
  122. new Filter<JobBillOfMaterialsItem>(x=>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
  123. null
  124. );
  125. }
  126. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.Requisitions, ".")))
  127. {
  128. ShowDetailGrid<JobRequisitionItem>(
  129. args.Column.ColumnName,
  130. x => x.Product.ID,
  131. productid,
  132. x => x.Style.ID,
  133. styleid,
  134. x=>x.Dimensions.UnitSize,
  135. unitsize,
  136. x => x.Job.ID,
  137. new Filter<JobRequisitionItem>(x=>x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue),
  138. null
  139. );
  140. }
  141. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.PickingLists, ".")))
  142. {
  143. ShowDetailGrid<RequisitionItem>(
  144. args.Column.ColumnName,
  145. x => x.Product.ID,
  146. productid,
  147. x => x.Style.ID,
  148. styleid,
  149. x=>x.Dimensions.UnitSize,
  150. unitsize,
  151. x => x.Job.ID,
  152. new Filter<RequisitionItem>(x=>x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue),
  153. null
  154. );
  155. }
  156. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.Issued, ".")))
  157. {
  158. ShowDetailGrid<StockMovement>(
  159. args.Column.ColumnName,
  160. x => x.Product.ID,
  161. productid,
  162. x => x.Style.ID,
  163. styleid,
  164. x=>x.Dimensions.UnitSize,
  165. unitsize,
  166. x => x.Job.ID,
  167. new Filter<StockMovement>(x=>x.IsTransfer).IsEqualTo(false).And(x=>x.Issued).IsNotEqualTo(0.0F),
  168. null
  169. );
  170. }
  171. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.ReservedStock, ".")))
  172. {
  173. ShowDetailGrid<StockHolding>(
  174. args.Column.ColumnName,
  175. x => x.Product.ID,
  176. productid,
  177. x => x.Style.ID,
  178. styleid,
  179. x=>x.Dimensions.UnitSize,
  180. unitsize,
  181. x=>x.Job.ID,
  182. null,
  183. null
  184. );
  185. }
  186. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.OnOrder, ".")))
  187. {
  188. ShowDetailGrid<PurchaseOrderItem>(
  189. args.Column.ColumnName,
  190. x => x.Product.ID,
  191. productid,
  192. x => x.Style.ID,
  193. styleid,
  194. x=>x.Dimensions.UnitSize,
  195. unitsize,
  196. x=>x.Job.ID,
  197. null,
  198. null
  199. );
  200. }
  201. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.FreeOnHand, ".")))
  202. {
  203. ShowDetailGrid<StockHolding>(
  204. args.Column.ColumnName,
  205. x => x.Product.ID,
  206. productid,
  207. x => x.Style.ID,
  208. styleid,
  209. x=>x.Dimensions.UnitSize,
  210. unitsize,
  211. null,
  212. new Filter<StockHolding>(x=>x.Units).IsNotEqualTo(0.0F)
  213. .And(
  214. _includeReserves
  215. ? new Filter<StockHolding>(x=>x.Job.ID).IsNotEqualTo(ParentID)
  216. : new Filter<StockHolding>(x=>x.Job.JobStatus.Active).IsEqualTo(false)
  217. ),
  218. null
  219. );
  220. }
  221. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.FreeOnOrder, ".")))
  222. {
  223. ShowDetailGrid<PurchaseOrderItem>(
  224. args.Column.ColumnName,
  225. x => x.Product.ID,
  226. productid,
  227. x => x.Style.ID,
  228. styleid,
  229. x=>x.Dimensions.UnitSize,
  230. unitsize,
  231. null,
  232. _includeReserves
  233. ? new Filter<PurchaseOrderItem>(x=>x.Job.ID).IsNotEqualTo(ParentID)
  234. : new Filter<PurchaseOrderItem>(x=>x.Job.JobStatus.Active).IsEqualTo(false),
  235. null
  236. );
  237. }
  238. }
  239. public event DataModelUpdateEvent OnUpdateDataModel;
  240. public string SectionName => "Job Summary";
  241. public DataModel DataModel(Selection selection)
  242. {
  243. return new AutoDataModel<JobMaterial>(new Filter<JobMaterial>(x => x.Job.ID).IsEqualTo(ParentID));
  244. }
  245. public Guid ParentID { get; set; }
  246. public JobPanelSettings Settings { get; set; }
  247. protected override JobMaterial CreateItem()
  248. {
  249. var result = base.CreateItem();
  250. result.Job.ID = ParentID;
  251. return result;
  252. }
  253. private Tuple<Guid,Guid,Guid?,String>[] GetKeys(IEnumerable<CoreRow> rows, Columns<JobMaterial> columns, bool hasstyle)
  254. {
  255. int jobcol = columns.IndexOf(x => x.Job.ID);
  256. int productcol = columns.IndexOf(x => x.Product.ID);
  257. int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
  258. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  259. var result = rows.Select(r => new Tuple<Guid, Guid, Guid?, String>(
  260. (Guid)(r.Values[jobcol] ?? Guid.Empty),
  261. (Guid)(r.Values[productcol] ?? Guid.Empty),
  262. (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
  263. (String)(r.Values[unitcol] ?? ""))
  264. ).Distinct().ToArray();
  265. return result;
  266. }
  267. private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid? jobid, Guid productid, Guid? styleid, String unitsize) where TSource : IJobMaterial
  268. {
  269. int jobcol = columns.IndexOf(x => x.Job.ID);
  270. int productcol = columns.IndexOf(x => x.Product.ID);
  271. int stylecol = styleid.HasValue ? columns.IndexOf(x => x.Style.ID) : -1;
  272. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  273. var subset = rows
  274. .Where(r=>
  275. (!jobid.HasValue || Guid.Equals(r.Values[jobcol], jobid))
  276. && Guid.Equals(r.Values[productcol], productid)
  277. && (!styleid.HasValue || Guid.Equals(r.Values[stylecol], styleid))
  278. && String.Equals(r.Values[unitcol], unitsize)
  279. );
  280. return subset.ToArray();
  281. }
  282. private double Aggregate<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, bool hasstyle, bool hasjob, Expression<Func<TSource, object>> source, CoreRow target, Expression<Func<JobMaterial, object>> aggregate )
  283. {
  284. int srcol = columns.IndexOf(source);
  285. if (srcol == -1)
  286. return 0.00;
  287. var total = rows.Aggregate(0d, (value, row) => value + (double)(row.Values[srcol] ?? 0.0d));
  288. target.Set(aggregate, total);
  289. return total;
  290. }
  291. protected override void Reload(Filters<JobMaterial> criteria, Columns<JobMaterial> columns, ref SortOrder<JobMaterial>? sort,
  292. Action<CoreTable?, Exception?> action)
  293. {
  294. Filter<JobMaterial>? filter = ParentID == Guid.Empty
  295. ? new Filter<JobMaterial>().None()
  296. : new Filter<JobMaterial>(x => x.Job.ID).IsEqualTo(ParentID)
  297. .And(x => x.Product.ID).IsNotEqualTo(Guid.Empty);
  298. var orderby = sort != null ? Serialization.Deserialize<SortOrder<JobMaterial>>(Serialization.Serialize(sort)) : null;
  299. new Client<JobMaterial>().Query(filter,columns,orderby, (o,e) =>
  300. {
  301. var pids = o.ExtractValues<JobMaterial, Guid>(x => x.Product.ID).ToArray();
  302. MultiQuery query = new MultiQuery();
  303. query.Add<StockHolding>(
  304. new Filter<StockHolding>(x => x.Product.ID).InList(pids)
  305. .And(x=>x.Units).IsNotEqualTo(0.0F)
  306. .And(
  307. _includeReserves
  308. ? new Filter<StockHolding>(x => x.Job.ID).IsNotEqualTo(ParentID)
  309. : new Filter<StockHolding>(x => x.Job.JobStatus.Active).IsEqualTo(false)
  310. ),
  311. new Columns<StockHolding>(x => x.Product.ID)
  312. .Add(x => x.Style.ID)
  313. .Add(x => x.Dimensions.UnitSize)
  314. .Add(x => x.Units)
  315. .Add(x=>x.Job.ID)
  316. );
  317. query.Add<PurchaseOrderItem>(
  318. new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue)
  319. .And(x => x.Product.ID).InList(pids)
  320. .And(
  321. _includeReserves
  322. ? new Filter<PurchaseOrderItem>(x => x.Job.ID).IsNotEqualTo(ParentID)
  323. : new Filter<PurchaseOrderItem>(x => x.Job.JobStatus.Active).IsEqualTo(false)
  324. ),
  325. new Columns<PurchaseOrderItem>(x => x.Product.ID)
  326. .Add(x => x.Style.ID)
  327. .Add(x => x.Dimensions.UnitSize)
  328. .Add(x => x.Qty)
  329. );
  330. query.Query();
  331. var freestock = query.Get<StockHolding>();
  332. Columns<StockHolding> freestockcolumns = new Columns<StockHolding>(freestock.Columns.Select(x => x.ColumnName));
  333. var freeorders = query.Get<PurchaseOrderItem>();
  334. Columns<PurchaseOrderItem> freeordercolumns = new Columns<PurchaseOrderItem>(freeorders.Columns.Select(x => x.ColumnName));
  335. CoreTable table = new CoreTable();
  336. table.LoadColumns(columns);
  337. if (o != null)
  338. {
  339. var keys = GetKeys(o.Rows, columns, _stylecolumnVisible);
  340. foreach (var key in keys)
  341. {
  342. var rows = GetRows(o.Rows, columns, key.Item1, key.Item2, key.Item3, key.Item4);
  343. if (rows.Any())
  344. {
  345. CoreRow newrow = table.NewRow();
  346. newrow.LoadValues(rows.First().Values);
  347. var bom = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.BillOfMaterials, newrow, x => x.BillOfMaterials);
  348. var requi = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.Requisitions, newrow, x => x.Requisitions);
  349. var picklist = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.PickingLists, newrow, x => x.PickingLists);
  350. var issued = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.Issued, newrow, x => x.Issued);
  351. var reserved = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.ReservedStock, newrow, x => x.ReservedStock);
  352. var ordered = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.OnOrder, newrow, x => x.OnOrder);
  353. var shortage = Math.Max(0, Math.Max(0,(requi - issued)) - (reserved + ordered));
  354. newrow.Set<JobMaterial, double>(x => x.JobShortage, shortage);
  355. var freestockrows = GetRows(freestock.Rows, freestockcolumns, null, key.Item2, key.Item3, key.Item4);
  356. var freeonhand = Aggregate(freestockrows, freestockcolumns, _stylecolumnVisible, false, x => x.Units, newrow, x => x.FreeOnHand);
  357. newrow.Set<JobMaterial, double>(x => x.FreeOnHand, freeonhand);
  358. var freeorderrows = GetRows(freeorders.Rows, freeordercolumns, null, key.Item2, key.Item3, key.Item4);
  359. var freeonorder = Aggregate(freeorderrows, freeordercolumns, _stylecolumnVisible, false, x => x.Qty, newrow, x => x.FreeOnOrder);
  360. newrow.Set<JobMaterial, double>(x => x.FreeOnOrder, freeonorder);
  361. newrow.Set<JobMaterial, double>(x => x.FreeStockTotal, freeonhand+freeonorder);
  362. newrow.Set<JobMaterial, double>(x => x.FreeStockShortage, Math.Max(0,shortage-(freeonhand+freeonorder)));
  363. table.Rows.Add(newrow);
  364. }
  365. }
  366. }
  367. action?.Invoke(table, e);
  368. });
  369. }
  370. protected override bool FilterRecord(CoreRow row)
  371. {
  372. var result = base.FilterRecord(row)
  373. && (
  374. row.Get<JobMaterial, double>(x => x.BillOfMaterials) != 0.0F ||
  375. row.Get<JobMaterial, double>(x => x.Requisitions) != 0.0F ||
  376. row.Get<JobMaterial, double>(x => x.PickingLists) != 0.0F ||
  377. row.Get<JobMaterial, double>(x => x.Issued) != 0.0F ||
  378. row.Get<JobMaterial, double>(x => x.ReservedStock) != 0.0F ||
  379. row.Get<JobMaterial, double>(x => x.OnOrder) != 0.0F
  380. );
  381. return result;
  382. }
  383. }
  384. }