StockSummaryGrid.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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.Media;
  7. using Comal.Classes;
  8. using InABox.Clients;
  9. using InABox.Core;
  10. using InABox.DynamicGrid;
  11. using PRSDesktop.Utils;
  12. using Syncfusion.Data.Extensions;
  13. using Xceed.Wpf.Toolkit;
  14. namespace PRSDesktop
  15. {
  16. public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
  17. {
  18. public Guid[] GroupIDs { get; set; }
  19. public Guid[] JobIDs { get; set; }
  20. public StockSummaryGrid() : base()
  21. {
  22. ColumnsTag = "StockSummaryGrid";
  23. GroupIDs = new Guid[] { };
  24. JobIDs = new Guid[] { };
  25. HiddenColumns.Add(x => x.Product.ID);
  26. HiddenColumns.Add(x => x.Style.ID);
  27. HiddenColumns.Add(x => x.Job.ID);
  28. HiddenColumns.Add(x => x.Dimensions.UnitSize);
  29. HiddenColumns.Add(x => x.Product.MinimumStockLevel);
  30. HiddenColumns.Add(x => x.Issued);
  31. HiddenColumns.Add(x => x.TotalRequired);
  32. HiddenColumns.Add(x => x.NettRequired);
  33. HiddenColumns.Add(x => x.AllStock);
  34. HiddenColumns.Add(x => x.OnOrder);
  35. HiddenColumns.Add(x => x.TotalStock);
  36. HiddenColumns.Add(x => x.BalanceAvailable);
  37. HiddenColumns.Add(x => x.Product.Image.ID);
  38. HiddenColumns.Add(x => x.Product.Image.FileName);
  39. ActionColumns.Add(new DynamicImagePreviewColumn<StockSummary>(x => x.Product.Image)
  40. { Position = DynamicActionColumnPosition.Start });
  41. OnCellDoubleClick += StockSummaryGrid_OnCellDoubleClick;
  42. }
  43. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  44. {
  45. base.DoReconfigure(options);
  46. options.AddRange(
  47. DynamicGridOption.RecordCount,
  48. DynamicGridOption.SelectColumns,
  49. DynamicGridOption.FilterRows,
  50. DynamicGridOption.ExportData,
  51. DynamicGridOption.MultiSelect
  52. );
  53. }
  54. protected override void GenerateColumns(DynamicGridColumns columns)
  55. {
  56. columns.Add<StockSummary, string>(x => x.Product.Code, 120, "Product Code", "", Alignment.MiddleCenter);
  57. columns.Add<StockSummary, string>(x => x.Style.Code, 120, "Style Code", "", Alignment.MiddleCenter);
  58. columns.Add<StockSummary, string>(x => x.Dimensions.UnitSize, 120, "Unit Size", "", Alignment.MiddleCenter);
  59. columns.Add<StockSummary, int>(x => x.Product.MinimumStockLevel, 120, "Minimum Stock Level", "", Alignment.MiddleCenter);
  60. columns.Add<StockSummary, double>(x => x.BillOfMaterials, 120, "BOM", "", Alignment.MiddleCenter);
  61. columns.Add<StockSummary, double>(x => x.Issued, 120, "Issued", "", Alignment.MiddleCenter);
  62. columns.Add<StockSummary, double>(x => x.AllStock, 120, "Stock", "", Alignment.MiddleCenter);
  63. columns.Add<StockSummary, double>(x => x.OnOrder, 120, "On Order", "", Alignment.MiddleCenter);
  64. columns.Add<StockSummary, double>(x => x.BalanceAvailable, 120, "Balance Available", "", Alignment.MiddleCenter);
  65. }
  66. private void ShowDetailGrid<TEntity>(
  67. String columnname,
  68. Expression<Func<TEntity,object?>> productcol,
  69. Guid productid,
  70. Expression<Func<TEntity,object?>> stylecol,
  71. Guid? styleid,
  72. Expression<Func<TEntity,object?>> unitcol,
  73. String unitsize,
  74. Expression<Func<TEntity,object?>>? jobcol,
  75. Filter<TEntity>? extrafilter,
  76. Func<CoreRow,bool>? rowfilter
  77. )
  78. {
  79. var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
  80. if (grid == null)
  81. {
  82. MessageBox.Show($"Cannot create Grid for [{typeof(TEntity).Name}]");
  83. return;
  84. }
  85. grid.ColumnsTag = $"{ColumnsTag}.{columnname}";
  86. grid.Reconfigure(options => { options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate(); });
  87. grid.OnDefineFilter += t =>
  88. {
  89. var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
  90. .And(unitcol).IsEqualTo(unitsize);
  91. if (styleid.HasValue)
  92. filter = filter.And(stylecol).IsEqualTo(styleid);
  93. if (jobcol != null)
  94. filter = filter.And(jobcol).InList(JobIDs);
  95. if (extrafilter != null)
  96. filter = filter.And(extrafilter);
  97. return filter;
  98. };
  99. grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
  100. var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", (grid as BaseDynamicGrid)!);
  101. window.ShowDialog();
  102. }
  103. private void StockSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
  104. {
  105. Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
  106. Guid? styleid = HasStyle() ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
  107. String unitsize = args.Row.Get<StockSummary, String>(c => c.Dimensions.UnitSize);
  108. if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BillOfMaterials, ".")))
  109. {
  110. ShowDetailGrid<JobBillOfMaterialsItem>(
  111. args.Column.ColumnName,
  112. x => x.Product.ID,
  113. productid,
  114. x => x.Style.ID,
  115. styleid,
  116. x=>x.Dimensions.UnitSize,
  117. unitsize,
  118. x => x.Job.ID,
  119. new Filter<JobBillOfMaterialsItem>(x=>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
  120. null
  121. );
  122. }
  123. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Issued, ".")))
  124. {
  125. ShowDetailGrid<StockMovement>(
  126. args.Column.ColumnName,
  127. x => x.Product.ID,
  128. productid,
  129. x => x.Style.ID,
  130. styleid,
  131. x=>x.Dimensions.UnitSize,
  132. unitsize,
  133. x => x.Job.ID,
  134. new Filter<StockMovement>(x=>x.IsTransfer).IsEqualTo(false).And(x=>x.Issued).IsNotEqualTo(0.0F),
  135. null
  136. );
  137. }
  138. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.TotalRequired, ".")))
  139. {
  140. ShowDetailGrid<StockSummary>(
  141. args.Column.ColumnName,
  142. x => x.Product.ID,
  143. productid,
  144. x => x.Style.ID,
  145. styleid,
  146. x=>x.Dimensions.UnitSize,
  147. unitsize,
  148. x => x.Job.ID,
  149. null,
  150. null
  151. );
  152. }
  153. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.NettRequired, ".")))
  154. {
  155. ShowDetailGrid<StockSummary>(
  156. args.Column.ColumnName,
  157. x => x.Product.ID,
  158. productid,
  159. x => x.Style.ID,
  160. styleid,
  161. x=>x.Dimensions.UnitSize,
  162. unitsize,
  163. x => x.Job.ID,
  164. null,
  165. null
  166. );
  167. }
  168. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.AllStock, ".")))
  169. {
  170. ShowDetailGrid<StockHolding>(
  171. args.Column.ColumnName,
  172. x => x.Product.ID,
  173. productid,
  174. x => x.Style.ID,
  175. styleid,
  176. x=>x.Dimensions.UnitSize,
  177. unitsize,
  178. null,
  179. null,
  180. (row) => row.Get<StockHolding,double>(x=>x.Units) != 0.0F
  181. );
  182. }
  183. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.OnOrder, ".")))
  184. {
  185. ShowDetailGrid<PurchaseOrderItem>(
  186. args.Column.ColumnName,
  187. x => x.Product.ID,
  188. productid,
  189. x => x.Style.ID,
  190. styleid,
  191. x=>x.Dimensions.UnitSize,
  192. unitsize,
  193. null,
  194. null,
  195. null
  196. );
  197. }
  198. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.TotalStock, ".")))
  199. {
  200. ShowDetailGrid<StockSummary>(
  201. args.Column.ColumnName,
  202. x => x.Product.ID,
  203. productid,
  204. x => x.Style.ID,
  205. styleid,
  206. x=>x.Dimensions.UnitSize,
  207. unitsize,
  208. x => x.Job.ID,
  209. null,
  210. null
  211. );
  212. }
  213. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BalanceAvailable, ".")))
  214. {
  215. ShowDetailGrid<StockSummary>(
  216. args.Column.ColumnName,
  217. x => x.Product.ID,
  218. productid,
  219. x => x.Style.ID,
  220. styleid,
  221. x=>x.Dimensions.UnitSize,
  222. unitsize,
  223. x => x.Job.ID,
  224. null,
  225. null
  226. );
  227. }
  228. }
  229. private bool HasStyle()
  230. {
  231. return DataColumns().ColumnNames().Any(x => x.StartsWith("Style.") && !x.Equals("Style.ID"));
  232. }
  233. private Filters<StockSummary> GetFilters()
  234. {
  235. var filters = new Filters<StockSummary>();
  236. if (SelectedFilter?.Item2 != null)
  237. filters.Add(SelectedFilter.Item2);
  238. Filter<StockSummary>? _groupfilter = !GroupIDs.Any()
  239. ? new Filter<StockSummary>().None()
  240. : !GroupIDs.Contains(CoreUtils.FullGuid)
  241. ? new Filter<StockSummary>(x => x.Product.Group.ID).InList(GroupIDs)
  242. : null;
  243. filters.Add(_groupfilter);
  244. var jobFilter = !JobIDs.Any()
  245. ? new Filter<StockSummary>().None()
  246. : new Filter<StockSummary>(x => x.Job.ID).InList(JobIDs);
  247. filters.Add(jobFilter);
  248. //Filter<StockSummary> _productfilter = new Filter<StockSummary>(x => x.Product.ID).IsNotEqualTo(Guid.Empty);
  249. //filters.Add(_productfilter);
  250. return filters;
  251. }
  252. private Tuple<Guid,Guid?,String>[] GetKeys(IEnumerable<CoreRow> rows, Columns<StockSummary> columns, bool hasstyle)
  253. {
  254. int productcol = columns.IndexOf(x => x.Product.ID);
  255. int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
  256. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  257. var result = rows.Select(r => new Tuple<Guid, Guid?, String>(
  258. (Guid)(r.Values[productcol] ?? Guid.Empty),
  259. (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
  260. (String)(r.Values[unitcol] ?? ""))
  261. ).Distinct().ToArray();
  262. return result;
  263. }
  264. private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid productid, Guid? styleid, String unitsize) where TSource : IJobMaterial
  265. {
  266. int productcol = columns.IndexOf(x => x.Product.ID);
  267. int stylecol = styleid.HasValue ? columns.IndexOf(x => x.Style.ID) : -1;
  268. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  269. var subset = rows
  270. .Where(r=>
  271. Guid.Equals(r.Values[productcol], productid)
  272. && (!styleid.HasValue || Guid.Equals(r.Values[stylecol], styleid))
  273. && String.Equals(r.Values[unitcol], unitsize)
  274. );
  275. return subset.ToArray();
  276. }
  277. private double Aggregate<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, bool hasstyle, bool hasjob, Expression<Func<TSource, object>> source, CoreRow target, Expression<Func<StockSummary, object>> aggregate )
  278. {
  279. int srcol = columns.IndexOf(source);
  280. if (srcol == -1)
  281. return 0.00;
  282. var total = rows.Aggregate(0d, (value, row) => value + (double)(row.Values[srcol] ?? 0.0d));
  283. // int productcol = columns.IndexOf(x => x.Product.ID);
  284. // int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
  285. // int jobcol = hasjob ? columns.IndexOf(x => x.Job.ID) : -1;
  286. // int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  287. //
  288. // var tuples = rows.Select(r => new Tuple<Guid, Guid?, Guid?, String, double>(
  289. // (Guid)(r.Values[productcol] ?? Guid.Empty),
  290. // (hasstyle ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null),
  291. // (hasjob ? (Guid)(r.Values[jobcol] ?? Guid.Empty) : null),
  292. // (String)(r.Values[unitcol] ?? ""),
  293. // (double)(r.Values[aggcol] ?? 0.0d))
  294. // ).ToArray();
  295. //
  296. // var total = tuples.Aggregate(0d, (value, tuple) => value + tuple.Item5);
  297. target.Set(aggregate, total);
  298. return total;
  299. }
  300. protected override void Reload(Filters<StockSummary> criteria, Columns<StockSummary> columns, ref SortOrder<StockSummary>? sort,
  301. Action<CoreTable?, Exception?> action)
  302. {
  303. var filter = GetFilters().Combine();
  304. new Client<StockSummary>().Query(filter,columns,sort, (o,e) =>
  305. {
  306. var pids = o.ExtractValues<StockSummary, Guid>(x => x.Product.ID).ToArray();
  307. MultiQuery query = new MultiQuery();
  308. query.Add<StockHolding>(
  309. new Filter<StockHolding>(x => x.Product.ID).InList(pids),
  310. new Columns<StockHolding>(x => x.Product.ID)
  311. .Add(x => x.Style.ID)
  312. .Add(x => x.Dimensions.UnitSize)
  313. .Add(x => x.Units)
  314. );
  315. query.Add<PurchaseOrderItem>(
  316. new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue)
  317. .And(x => x.Product.ID).InList(pids),
  318. new Columns<PurchaseOrderItem>(x => x.Product.ID)
  319. .Add(x => x.Style.ID)
  320. .Add(x => x.Dimensions.UnitSize)
  321. .Add(x => x.Qty)
  322. );
  323. query.Query();
  324. var holdings = query.Get<StockHolding>();
  325. Columns<StockHolding> holdingcolumns = new Columns<StockHolding>(holdings.Columns.Select(x => x.ColumnName));
  326. var orders = query.Get<PurchaseOrderItem>();
  327. Columns<PurchaseOrderItem> ordercolumns = new Columns<PurchaseOrderItem>(orders.Columns.Select(x => x.ColumnName));
  328. CoreTable table = new CoreTable();
  329. table.LoadColumns(columns);
  330. if (o != null)
  331. {
  332. bool bHasStyle = HasStyle();
  333. var keys = GetKeys(o.Rows, columns, bHasStyle);
  334. foreach (var key in keys)
  335. {
  336. var rows = GetRows(o.Rows, columns, key.Item1, key.Item2, key.Item3);
  337. if (rows.Any())
  338. {
  339. CoreRow newrow = table.NewRow();
  340. newrow.LoadValues(rows.First().Values);
  341. Aggregate(rows, columns, bHasStyle, true, x => x.BillOfMaterials, newrow, x => x.BillOfMaterials);
  342. Aggregate(rows, columns, bHasStyle, true, x => x.Issued, newrow, x => x.Issued);
  343. Aggregate(rows, columns, bHasStyle, true, x => x.TotalRequired, newrow, x => x.TotalRequired);
  344. var nettrequired = Aggregate(rows, columns, bHasStyle, true, x => x.NettRequired, newrow, x => x.NettRequired);
  345. var holdingrows = GetRows(holdings.Rows, holdingcolumns, key.Item1, key.Item2, key.Item3);
  346. var allstock = Aggregate(holdingrows, holdingcolumns, bHasStyle, false, x => x.Units, newrow, x => x.AllStock);
  347. var orderrows = GetRows(orders.Rows, ordercolumns, key.Item1, key.Item2, key.Item3);
  348. var onorder = Aggregate(orderrows, ordercolumns, bHasStyle, false, x => x.Qty, newrow, x => x.OnOrder);
  349. newrow.Set<StockSummary, double>(x => x.TotalStock, allstock + onorder);
  350. newrow.Set<StockSummary, double>(x => x.BalanceAvailable, allstock + onorder - (newrow.Get<StockSummary,double>(c=>c.Product.MinimumStockLevel) + nettrequired));
  351. table.Rows.Add(newrow);
  352. }
  353. }
  354. }
  355. action?.Invoke(table, e);
  356. });
  357. }
  358. protected override bool FilterRecord(CoreRow row)
  359. {
  360. var result = base.FilterRecord(row);
  361. if (result)
  362. result = (result && (
  363. row.Get<StockSummary, double>(x => x.BillOfMaterials) != 0.0F
  364. || row.Get<StockSummary, double>(x => x.Product.MinimumStockLevel) != 0.0F
  365. || row.Get<StockSummary, double>(x => x.Issued) != 0.0F
  366. || row.Get<StockSummary, double>(x => x.AllStock) != 0.0F
  367. || row.Get<StockSummary, double>(x => x.OnOrder) != 0.0F
  368. )
  369. );
  370. return result;
  371. }
  372. // private String _minstock = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Product.MinimumStockLevel, ".");
  373. // private String _bom = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BillOfMaterials, ".");
  374. // private String _issued = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Issued, ".");
  375. // private String _nettrequired = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.NettRequired, ".");
  376. // private String _stock = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.AllStock, ".");
  377. // private String _ordered = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.OnOrder, ".");
  378. private String _balance = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BalanceAvailable, ".");
  379. protected override Brush? GetCellBackground(CoreRow row, string columnname)
  380. {
  381. if (String.Equals(columnname, _balance))
  382. {
  383. return row.Get<StockSummary, double>(x => x.BalanceAvailable) < 0.0F
  384. ? new SolidColorBrush(Colors.LightSalmon) { Opacity = 0.5 }
  385. : null;
  386. }
  387. return null;
  388. }
  389. #region IDataModelSource
  390. public event DataModelUpdateEvent OnUpdateDataModel;
  391. public string SectionName => "Stock Summary";
  392. public DataModel DataModel(Selection selection)
  393. {
  394. return new AutoDataModel<StockSummary>(null);
  395. }
  396. #endregion
  397. }
  398. }