StockSummaryGrid.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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.Media;
  8. using System.Windows.Media.Imaging;
  9. using Comal.Classes;
  10. using InABox.Clients;
  11. using InABox.Configuration;
  12. using InABox.Core;
  13. using InABox.DynamicGrid;
  14. using InABox.Wpf;
  15. using InABox.WPF;
  16. using PRSDesktop.Utils;
  17. using Syncfusion.Data.Extensions;
  18. using Xceed.Wpf.Toolkit;
  19. namespace PRSDesktop;
  20. public enum StockSummaryMinimumStockBehaviour
  21. {
  22. Cumulative,
  23. Minimum
  24. }
  25. public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
  26. {
  27. private static readonly BitmapImage _warning = InABox.Wpf.Resources.warning.AsBitmapImage();
  28. public Guid[] GroupIDs { get; set; }
  29. public Guid[] JobIDs { get; set; }
  30. public Guid[] SupplierIDs { get; set; }
  31. private StockSummaryMinimumStockBehaviour MinStockBehaviour { get; set; }
  32. public StockSummaryGrid() : base()
  33. {
  34. ColumnsTag = "StockSummaryGrid";
  35. GroupIDs = Array.Empty<Guid>();
  36. JobIDs = Array.Empty<Guid>();
  37. SupplierIDs = Array.Empty<Guid>();
  38. HiddenColumns.Add(x => x.Product.ID);
  39. HiddenColumns.Add(x => x.Product.Issues);
  40. HiddenColumns.Add(x => x.Issues);
  41. HiddenColumns.Add(x => x.Style.ID);
  42. HiddenColumns.Add(x => x.Job.ID);
  43. HiddenColumns.Add(x => x.Dimensions.UnitSize);
  44. HiddenColumns.Add(x => x.Product.DefaultInstance.MinimumStockLevel);
  45. HiddenColumns.Add(x => x.Issued);
  46. HiddenColumns.Add(x => x.TotalRequired);
  47. HiddenColumns.Add(x => x.NettRequired);
  48. HiddenColumns.Add(x => x.AllStock);
  49. HiddenColumns.Add(x => x.OnOrder);
  50. HiddenColumns.Add(x => x.TotalStock);
  51. HiddenColumns.Add(x => x.BalanceAvailable);
  52. HiddenColumns.Add(x => x.Product.Image.ID);
  53. HiddenColumns.Add(x => x.Product.Image.FileName);
  54. //ActionColumns.Add(new DynamicIssuesColumn<Product>(this, CoreUtils.GetFullPropertyName<StockSummary, string>(x => x.Product.Issues, "."), LoadProducts));
  55. ActionColumns.Add(new DynamicImageColumn(Issues_Image, null)
  56. {
  57. ToolTip = Issues_Tooltip
  58. });
  59. ActionColumns.Add(new DynamicImagePreviewColumn<StockSummary>(x => x.Product.Image)
  60. { Position = DynamicActionColumnPosition.Start });
  61. OnCellDoubleClick += StockSummaryGrid_OnCellDoubleClick;
  62. MinStockBehaviour = new GlobalConfiguration<StockSummaryPanelSettings>().Load().MinimumStockBehaviour;
  63. }
  64. private BitmapImage? Issues_Image(CoreRow? row)
  65. {
  66. if (row is null) return _warning;
  67. return row.Get<StockSummary, string>(x => x.Issues).IsNullOrWhiteSpace() ? null : _warning;
  68. }
  69. private FrameworkElement? Issues_Tooltip(DynamicActionColumn column, CoreRow? row)
  70. {
  71. if (row is null) return null;
  72. return column.TextToolTip(row.Get<StockSummary, string>(x => x.Issues));
  73. }
  74. private class UIComponent : DynamicGridGridUIComponent<StockSummary>
  75. {
  76. private StockSummaryGrid Grid;
  77. public UIComponent(StockSummaryGrid grid)
  78. {
  79. Grid = grid;
  80. Parent = grid;
  81. }
  82. protected override Brush? GetCellBackground(CoreRow row, string columnname)
  83. {
  84. if (String.Equals(columnname, Grid._balance))
  85. {
  86. return row.Get<StockSummary, double>(x => x.BalanceAvailable) < 0.0F
  87. ? new SolidColorBrush(Colors.LightSalmon) { Opacity = 0.5 }
  88. : null;
  89. }
  90. return null;
  91. }
  92. }
  93. protected override IDynamicGridUIComponent<StockSummary> CreateUIComponent()
  94. {
  95. return new UIComponent(this);
  96. }
  97. private Product[] LoadProducts(CoreRow[] arg)
  98. {
  99. return Client.Query(
  100. new Filter<Product>(x => x.ID).InList(arg.Select(x => x.Get<StockSummary, Guid>(x => x.Product.ID)).ToArray()),
  101. new Columns<Product>(x => x.ID, x => x.Issues))
  102. .ToObjects<Product>().ToArray();
  103. }
  104. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  105. {
  106. base.DoReconfigure(options);
  107. options.AddRange(
  108. DynamicGridOption.RecordCount,
  109. DynamicGridOption.SelectColumns,
  110. DynamicGridOption.FilterRows,
  111. DynamicGridOption.ExportData,
  112. DynamicGridOption.MultiSelect
  113. );
  114. }
  115. public override DynamicGridColumns GenerateColumns()
  116. {
  117. var columns = new DynamicGridColumns();
  118. columns.Add<StockSummary, string>(x => x.Product.Code, 120, "Product Code", "", Alignment.MiddleCenter);
  119. columns.Add<StockSummary, string>(x => x.Style.Code, 120, "Style Code", "", Alignment.MiddleCenter);
  120. columns.Add<StockSummary, string>(x => x.Dimensions.UnitSize, 120, "Unit Size", "", Alignment.MiddleCenter);
  121. columns.Add<StockSummary, int>(x => x.MinimumStockLevel, 120, "Minimum Stock Level", "", Alignment.MiddleCenter);
  122. columns.Add<StockSummary, double>(x => x.BillOfMaterials, 120, "BOM", "", Alignment.MiddleCenter);
  123. columns.Add<StockSummary, double>(x => x.Issued, 120, "Issued", "", Alignment.MiddleCenter);
  124. columns.Add<StockSummary, double>(x => x.AllStock, 120, "Stock", "", Alignment.MiddleCenter);
  125. columns.Add<StockSummary, double>(x => x.OnOrder, 120, "On Order", "", Alignment.MiddleCenter);
  126. columns.Add<StockSummary, double>(x => x.BalanceAvailable, 120, "Balance Available", "", Alignment.MiddleCenter);
  127. return columns;
  128. }
  129. private void ShowDetailGrid<TEntity>(
  130. String columnname,
  131. Expression<Func<TEntity,object?>> productcol,
  132. Guid productid,
  133. Expression<Func<TEntity,object?>> stylecol,
  134. Guid? styleid,
  135. Expression<Func<TEntity,object?>> unitcol,
  136. String unitsize,
  137. Expression<Func<TEntity,object?>>? jobcol,
  138. Filter<TEntity>? extrafilter,
  139. Func<CoreRow,bool>? rowfilter
  140. )
  141. {
  142. var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
  143. if (grid == null)
  144. {
  145. MessageWindow.ShowError($"Cannot create Grid for [{typeof(TEntity).Name}]", "", shouldLog: false);
  146. return;
  147. }
  148. grid.ColumnsTag = $"{ColumnsTag}.{columnname}";
  149. grid.Reconfigure(options => { options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate(); });
  150. grid.OnDefineFilter += t =>
  151. {
  152. var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
  153. .And(unitcol).IsEqualTo(unitsize);
  154. if (styleid.HasValue)
  155. filter = filter.And(stylecol).IsEqualTo(styleid);
  156. if (jobcol != null)
  157. filter = filter.And(new Filter<TEntity>(jobcol).InList(JobIDs).Or(jobcol).IsEqualTo(Guid.Empty));
  158. if (extrafilter != null)
  159. filter = filter.And(extrafilter);
  160. return filter;
  161. };
  162. grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
  163. var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", grid);
  164. window.ShowDialog();
  165. }
  166. private void StockSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
  167. {
  168. Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
  169. Guid? styleid = HasStyle() ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
  170. String unitsize = args.Row.Get<StockSummary, String>(c => c.Dimensions.UnitSize);
  171. if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BillOfMaterials, ".")))
  172. {
  173. ShowDetailGrid<JobBillOfMaterialsItem>(
  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. new Filter<JobBillOfMaterialsItem>(x=>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
  183. null
  184. );
  185. }
  186. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, int>(x => x.MinimumStockLevel, ".")))
  187. {
  188. ShowDetailGrid<ProductInstance>(
  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. null,
  197. null,
  198. null
  199. );
  200. }
  201. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Issued, ".")))
  202. {
  203. ShowDetailGrid<StockMovement>(
  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. x => x.Job.ID,
  212. new Filter<StockMovement>(x => x.Type).IsEqualTo(StockMovementType.Issue),
  213. null
  214. );
  215. }
  216. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.TotalRequired, ".")))
  217. {
  218. ShowDetailGrid<StockSummary>(
  219. args.Column.ColumnName,
  220. x => x.Product.ID,
  221. productid,
  222. x => x.Style.ID,
  223. styleid,
  224. x=>x.Dimensions.UnitSize,
  225. unitsize,
  226. x => x.Job.ID,
  227. null,
  228. null
  229. );
  230. }
  231. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.NettRequired, ".")))
  232. {
  233. ShowDetailGrid<StockSummary>(
  234. args.Column.ColumnName,
  235. x => x.Product.ID,
  236. productid,
  237. x => x.Style.ID,
  238. styleid,
  239. x=>x.Dimensions.UnitSize,
  240. unitsize,
  241. x => x.Job.ID,
  242. null,
  243. null
  244. );
  245. }
  246. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.AllStock, ".")))
  247. {
  248. ShowDetailGrid<StockHolding>(
  249. args.Column.ColumnName,
  250. x => x.Product.ID,
  251. productid,
  252. x => x.Style.ID,
  253. styleid,
  254. x=>x.Dimensions.UnitSize,
  255. unitsize,
  256. null,
  257. StockHoldingFilter(),
  258. (row) => row.Get<StockHolding,double>(x=>x.Units) != 0.0F
  259. );
  260. }
  261. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.OnOrder, ".")))
  262. {
  263. ShowDetailGrid<PurchaseOrderItem>(
  264. args.Column.ColumnName,
  265. x => x.Product.ID,
  266. productid,
  267. x => x.Style.ID,
  268. styleid,
  269. x=>x.Dimensions.UnitSize,
  270. unitsize,
  271. null,
  272. PurchaseOrderItemFilter(),
  273. null
  274. );
  275. }
  276. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.TotalStock, ".")))
  277. {
  278. ShowDetailGrid<StockSummary>(
  279. args.Column.ColumnName,
  280. x => x.Product.ID,
  281. productid,
  282. x => x.Style.ID,
  283. styleid,
  284. x=>x.Dimensions.UnitSize,
  285. unitsize,
  286. x => x.Job.ID,
  287. null,
  288. null
  289. );
  290. }
  291. else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BalanceAvailable, ".")))
  292. {
  293. ShowDetailGrid<StockSummary>(
  294. args.Column.ColumnName,
  295. x => x.Product.ID,
  296. productid,
  297. x => x.Style.ID,
  298. styleid,
  299. x=>x.Dimensions.UnitSize,
  300. unitsize,
  301. x => x.Job.ID,
  302. null,
  303. null
  304. );
  305. }
  306. }
  307. private bool HasStyle()
  308. {
  309. return DataColumns().ColumnNames().Any(x => x.StartsWith("Style.") && !x.Equals("Style.ID"));
  310. }
  311. private Filters<StockSummary> GetFilters()
  312. {
  313. var filters = new Filters<StockSummary>();
  314. filters.Add(FilterComponent.GetFilter());
  315. Filter<StockSummary>? _groupfilter = !GroupIDs.Any()
  316. ? new Filter<StockSummary>().None()
  317. : !GroupIDs.Contains(CoreUtils.FullGuid)
  318. ? new Filter<StockSummary>(x => x.Product.Group.ID).InList(GroupIDs)
  319. : null;
  320. filters.Add(_groupfilter);
  321. var jobFilter = !JobIDs.Any()
  322. ? new Filter<StockSummary>().None()
  323. : new Filter<StockSummary>(x => x.Job.ID).InList(JobIDs);
  324. filters.Add(jobFilter.Or(x => x.Job.ID).IsEqualTo(Guid.Empty));
  325. var supplierFilter = !SupplierIDs.Any()
  326. ? new Filter<StockSummary>().None()
  327. : new Filter<StockSummary>(x => x.Product.Supplier.SupplierLink.ID).InList(SupplierIDs);
  328. filters.Add(supplierFilter.Or(x => x.Product.Supplier.SupplierLink.ID).IsEqualTo(Guid.Empty));
  329. //Filter<StockSummary> _productfilter = new Filter<StockSummary>(x => x.Product.ID).IsNotEqualTo(Guid.Empty);
  330. //filters.Add(_productfilter);
  331. return filters;
  332. }
  333. private Tuple<Guid,Guid?,String>[] GetKeys(IEnumerable<CoreRow> rows, Columns<StockSummary> columns, bool hasstyle)
  334. {
  335. int productcol = columns.IndexOf(x => x.Product.ID);
  336. int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
  337. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  338. var result = rows.Select(r => new Tuple<Guid, Guid?, String>(
  339. (Guid)(r.Values[productcol] ?? Guid.Empty),
  340. (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
  341. (String)(r.Values[unitcol] ?? ""))
  342. ).Distinct().ToArray();
  343. return result;
  344. }
  345. private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid productid, Guid? styleid, String unitsize) where TSource : IJobMaterial
  346. {
  347. int productcol = columns.IndexOf(x => x.Product.ID);
  348. int stylecol = styleid.HasValue ? columns.IndexOf(x => x.Style.ID) : -1;
  349. int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  350. var subset = rows
  351. .Where(r=>
  352. Guid.Equals(r.Values[productcol], productid)
  353. && (!styleid.HasValue || Guid.Equals(r.Values[stylecol], styleid))
  354. && String.Equals(r.Values[unitcol], unitsize)
  355. );
  356. return subset.ToArray();
  357. }
  358. 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 )
  359. {
  360. int srcol = columns.IndexOf(source);
  361. if (srcol == -1)
  362. return 0.00;
  363. var total = rows.Aggregate(0d, (value, row) => value + (double)(row.Values[srcol] ?? 0.0d));
  364. // int productcol = columns.IndexOf(x => x.Product.ID);
  365. // int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
  366. // int jobcol = hasjob ? columns.IndexOf(x => x.Job.ID) : -1;
  367. // int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
  368. //
  369. // var tuples = rows.Select(r => new Tuple<Guid, Guid?, Guid?, String, double>(
  370. // (Guid)(r.Values[productcol] ?? Guid.Empty),
  371. // (hasstyle ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null),
  372. // (hasjob ? (Guid)(r.Values[jobcol] ?? Guid.Empty) : null),
  373. // (String)(r.Values[unitcol] ?? ""),
  374. // (double)(r.Values[aggcol] ?? 0.0d))
  375. // ).ToArray();
  376. //
  377. // var total = tuples.Aggregate(0d, (value, tuple) => value + tuple.Item5);
  378. target.Set(aggregate, total);
  379. return total;
  380. }
  381. private Filter<StockHolding> StockHoldingFilter()
  382. {
  383. var stockHoldingFilter = new Filter<StockHolding>(x => x.Job.ID).InList(JobIDs)
  384. .Or(x => x.Job.ID).IsEqualTo(Guid.Empty);
  385. return new Filter<StockHolding>(x => x.Units).IsNotEqualTo(0.0)
  386. .And(stockHoldingFilter);
  387. }
  388. private Filter<PurchaseOrderItem> PurchaseOrderItemFilter()
  389. {
  390. var poJobFilter = new Filter<PurchaseOrderItem>(x => x.Job.ID).InList(JobIDs)
  391. .Or(x => x.Job.ID).IsEqualTo(Guid.Empty);
  392. return new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue)
  393. .And(poJobFilter);
  394. }
  395. protected override void Reload(Filters<StockSummary> criteria, Columns<StockSummary> columns, ref SortOrder<StockSummary>? sort,
  396. Action<CoreTable?, Exception?> action)
  397. {
  398. var filter = GetFilters().Combine();
  399. new Client<StockSummary>().Query(filter,columns,sort, (o,e) =>
  400. {
  401. if(o is null)
  402. {
  403. Dispatcher.BeginInvoke(() =>
  404. {
  405. MessageWindow.ShowError("Failed to load data", e);
  406. });
  407. return;
  408. }
  409. var pids = o.ExtractValues<StockSummary, Guid>(x => x.Product.ID).ToArray();
  410. MultiQuery query = new MultiQuery();
  411. query.Add<StockHolding>(
  412. new Filter<StockHolding>(x => x.Product.ID).InList(pids)
  413. .And(StockHoldingFilter()),
  414. new Columns<StockHolding>(x => x.Product.ID)
  415. .Add(x => x.Style.ID)
  416. .Add(x => x.Dimensions.UnitSize)
  417. .Add(x => x.Units)
  418. );
  419. query.Add<PurchaseOrderItem>(
  420. new Filter<PurchaseOrderItem>(x => x.Product.ID).InList(pids)
  421. .And(PurchaseOrderItemFilter()),
  422. new Columns<PurchaseOrderItem>(x => x.Product.ID)
  423. .Add(x => x.Style.ID)
  424. .Add(x => x.Dimensions.UnitSize)
  425. .Add(x => x.Qty)
  426. );
  427. query.Query();
  428. var holdings = query.Get<StockHolding>();
  429. Columns<StockHolding> holdingcolumns = new Columns<StockHolding>(holdings.Columns.Select(x => x.ColumnName));
  430. var orders = query.Get<PurchaseOrderItem>();
  431. Columns<PurchaseOrderItem> ordercolumns = new Columns<PurchaseOrderItem>(orders.Columns.Select(x => x.ColumnName));
  432. CoreTable table = new CoreTable();
  433. table.LoadColumns(columns);
  434. if (o != null)
  435. {
  436. bool bHasStyle = HasStyle();
  437. var keys = GetKeys(o.Rows, columns, bHasStyle);
  438. foreach (var key in keys)
  439. {
  440. var rows = GetRows(o.Rows, columns, key.Item1, key.Item2, key.Item3);
  441. if (rows.Any())
  442. {
  443. CoreRow newrow = table.NewRow();
  444. newrow.LoadValues(rows.First().Values);
  445. Aggregate(rows, columns, bHasStyle, true, x => x.BillOfMaterials, newrow, x => x.BillOfMaterials);
  446. Aggregate(rows, columns, bHasStyle, true, x => x.Issued, newrow, x => x.Issued);
  447. Aggregate(rows, columns, bHasStyle, true, x => x.TotalRequired, newrow, x => x.TotalRequired);
  448. var nettrequired = Aggregate(rows, columns, bHasStyle, true, x => x.NettRequired, newrow, x => x.NettRequired);
  449. var holdingrows = GetRows(holdings.Rows, holdingcolumns, key.Item1, key.Item2, key.Item3);
  450. var allstock = Aggregate(holdingrows, holdingcolumns, bHasStyle, false, x => x.Units, newrow, x => x.AllStock);
  451. var orderrows = GetRows(orders.Rows, ordercolumns, key.Item1, key.Item2, key.Item3);
  452. var onorder = Aggregate(orderrows, ordercolumns, bHasStyle, false, x => x.Qty, newrow, x => x.OnOrder);
  453. newrow.Set<StockSummary, double>(x => x.TotalStock, allstock + onorder);
  454. var minStock = newrow.Get<StockSummary, double>(c => c.MinimumStockLevel);
  455. var balance = MinStockBehaviour switch
  456. {
  457. StockSummaryMinimumStockBehaviour.Cumulative => allstock + onorder -
  458. (minStock + nettrequired),
  459. StockSummaryMinimumStockBehaviour.Minimum or _ => allstock + onorder -
  460. Math.Max(minStock, nettrequired),
  461. };
  462. newrow.Set<StockSummary, double>(x => x.BalanceAvailable, balance);
  463. var productIssues = rows.First().Get<StockSummary, string>(x => x.Product.Issues);
  464. var issues = new List<string>();
  465. if(balance < 0)
  466. {
  467. issues.Add("Not enough stock available.");
  468. }
  469. if (!productIssues.IsNullOrWhiteSpace())
  470. {
  471. issues.Add(productIssues);
  472. }
  473. newrow.Set<StockSummary, string>(x => x.Issues, string.Join('\n', issues));
  474. table.Rows.Add(newrow);
  475. }
  476. }
  477. }
  478. action?.Invoke(table, e);
  479. });
  480. }
  481. protected override bool FilterRecord(CoreRow row)
  482. {
  483. var result = base.FilterRecord(row);
  484. if (result)
  485. result = (result && (
  486. row.Get<StockSummary, double>(x => x.BillOfMaterials) != 0.0F
  487. || row.Get<StockSummary, double>(x => x.MinimumStockLevel) != 0.0F
  488. || row.Get<StockSummary, double>(x => x.Issued) != 0.0F
  489. || row.Get<StockSummary, double>(x => x.AllStock) != 0.0F
  490. || row.Get<StockSummary, double>(x => x.OnOrder) != 0.0F
  491. )
  492. );
  493. return result;
  494. }
  495. // private String _minstock = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Product.MinimumStockLevel, ".");
  496. // private String _bom = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BillOfMaterials, ".");
  497. // private String _issued = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.Issued, ".");
  498. // private String _nettrequired = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.NettRequired, ".");
  499. // private String _stock = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.AllStock, ".");
  500. // private String _ordered = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.OnOrder, ".");
  501. private string _balance = CoreUtils.GetFullPropertyName<StockSummary, double>(x => x.BalanceAvailable, ".");
  502. #region IDataModelSource
  503. public event DataModelUpdateEvent? OnUpdateDataModel;
  504. public string SectionName => "Stock Summary";
  505. public DataModel DataModel(Selection selection)
  506. {
  507. return new AutoDataModel<StockSummary>(null);
  508. }
  509. #endregion
  510. }