|  | @@ -35,6 +35,8 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |      public Guid[] JobIDs { get; set; }
 | 
	
		
			
				|  |  |      public Guid[] SupplierIDs { get; set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    public bool IncludeFreeStock { get; set; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private StockSummaryMinimumStockBehaviour MinStockBehaviour { get; set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private Button OrderButton;
 | 
	
	
		
			
				|  | @@ -63,6 +65,7 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          HiddenColumns.Add(x => x.OnOrder);
 | 
	
		
			
				|  |  |          HiddenColumns.Add(x => x.TotalStock);
 | 
	
		
			
				|  |  |          HiddenColumns.Add(x => x.BalanceAvailable);
 | 
	
		
			
				|  |  | +        //HiddenColumns.Add(x => x.JobBOM);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          HiddenColumns.Add(x => x.Product.Image.ID);
 | 
	
		
			
				|  |  |          HiddenColumns.Add(x => x.Product.Image.FileName);
 | 
	
	
		
			
				|  | @@ -217,6 +220,7 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          columns.Add<StockSummary, double>(x => x.AllStock, 120, "Stock", "", Alignment.MiddleCenter);
 | 
	
		
			
				|  |  |          columns.Add<StockSummary, double>(x => x.OnOrder, 120, "On Order", "", Alignment.MiddleCenter);
 | 
	
		
			
				|  |  |          columns.Add<StockSummary, double>(x => x.BalanceAvailable, 120, "Balance Available", "", Alignment.MiddleCenter);
 | 
	
		
			
				|  |  | +        //columns.Add<StockSummary, double>(x => x.JobBOM, 120, "Job BOM", "", Alignment.MiddleCenter);
 | 
	
		
			
				|  |  |          return columns;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -247,9 +251,9 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |                  .And(unitcol).IsEqualTo(unitsize);
 | 
	
		
			
				|  |  |              if (styleid.HasValue)
 | 
	
		
			
				|  |  |                  filter = filter.And(stylecol).IsEqualTo(styleid);
 | 
	
		
			
				|  |  | -                
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if (jobcol != null)
 | 
	
		
			
				|  |  | -                filter = filter.And(new Filter<TEntity>(jobcol).InList(JobIDs).Or(jobcol).IsEqualTo(Guid.Empty));
 | 
	
		
			
				|  |  | +                filter = filter.And(GetJobFilter<TEntity>(jobcol));
 | 
	
		
			
				|  |  |                  
 | 
	
		
			
				|  |  |              if (extrafilter != null)
 | 
	
		
			
				|  |  |                  filter = filter.And(extrafilter);
 | 
	
	
		
			
				|  | @@ -262,9 +266,6 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |      
 | 
	
		
			
				|  |  |      private void StockSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -       
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  |          Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
 | 
	
		
			
				|  |  |          Guid? styleid = HasStyle() ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
 | 
	
		
			
				|  |  |          String unitsize = args.Row.Get<StockSummary, String>(c => c.Dimensions.UnitSize);
 | 
	
	
		
			
				|  | @@ -412,6 +413,21 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          return DataColumns().ColumnNames().Any(x => x.StartsWith("Style.") && !x.Equals("Style.ID"));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private Filter<T> GetJobFilter<T>(Expression<Func<T, object?>> jobID)
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        var jobFilter = !JobIDs.Any()
 | 
	
		
			
				|  |  | +            ? new Filter<T>().None()
 | 
	
		
			
				|  |  | +            : new Filter<T>(jobID).InList(JobIDs);
 | 
	
		
			
				|  |  | +        if (IncludeFreeStock)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return jobFilter.Or(jobID).IsEqualTo(Guid.Empty);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        else
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return jobFilter;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private Filters<StockSummary> GetFilters() 
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          var filters = new Filters<StockSummary>();
 | 
	
	
		
			
				|  | @@ -423,16 +439,12 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |                  ? new Filter<StockSummary>(x => x.Product.Group.ID).InList(GroupIDs)
 | 
	
		
			
				|  |  |                  : null;
 | 
	
		
			
				|  |  |          filters.Add(_groupfilter);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        var jobFilter = !JobIDs.Any()
 | 
	
		
			
				|  |  | -            ? new Filter<StockSummary>().None()
 | 
	
		
			
				|  |  | -            : new Filter<StockSummary>(x => x.Job.ID).InList(JobIDs);
 | 
	
		
			
				|  |  | -        filters.Add(jobFilter.Or(x => x.Job.ID).IsEqualTo(Guid.Empty));
 | 
	
		
			
				|  |  | +        filters.Add(GetJobFilter<StockSummary>(x => x.Job.ID));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          var supplierFilter = !SupplierIDs.Any()
 | 
	
		
			
				|  |  |              ? new Filter<StockSummary>().None()
 | 
	
		
			
				|  |  |              : new Filter<StockSummary>(x => x.Product.Supplier.SupplierLink.ID).InList(SupplierIDs);
 | 
	
		
			
				|  |  | -        filters.Add(supplierFilter.Or(x => x.Product.Supplier.SupplierLink.ID).IsEqualTo(Guid.Empty));
 | 
	
		
			
				|  |  | +        filters.Add(supplierFilter);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          //Filter<StockSummary> _productfilter = new Filter<StockSummary>(x => x.Product.ID).IsNotEqualTo(Guid.Empty);
 | 
	
		
			
				|  |  |          //filters.Add(_productfilter);
 | 
	
	
		
			
				|  | @@ -455,7 +467,7 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          return result;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid productid, Guid? styleid, String unitsize) where TSource : IJobMaterial
 | 
	
		
			
				|  |  | +    private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid productid, Guid? styleid, string unitsize) where TSource : IJobMaterial
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          int productcol = columns.IndexOf(x => x.Product.ID);
 | 
	
		
			
				|  |  |          int stylecol = styleid.HasValue ? columns.IndexOf(x => x.Style.ID) : -1;
 | 
	
	
		
			
				|  | @@ -471,7 +483,7 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          return subset.ToArray();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    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 )
 | 
	
		
			
				|  |  | +    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)
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          int srcol = columns.IndexOf(source);
 | 
	
		
			
				|  |  |          
 | 
	
	
		
			
				|  | @@ -495,26 +507,23 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |          //
 | 
	
		
			
				|  |  |          // var total = tuples.Aggregate(0d, (value, tuple) => value + tuple.Item5);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        target.Set(aggregate, total);
 | 
	
		
			
				|  |  | +        if(aggregate is not null)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            target?.Set(aggregate, total);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          return total;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private Filter<StockHolding> StockHoldingFilter()
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        var stockHoldingFilter = new Filter<StockHolding>(x => x.Job.ID).InList(JobIDs)
 | 
	
		
			
				|  |  | -            .Or(x => x.Job.ID).IsEqualTo(Guid.Empty);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          return new Filter<StockHolding>(x => x.Units).IsNotEqualTo(0.0)
 | 
	
		
			
				|  |  | -            .And(stockHoldingFilter);
 | 
	
		
			
				|  |  | +            .And(GetJobFilter<StockHolding>(x => x.Job.ID));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private Filter<PurchaseOrderItem> PurchaseOrderItemFilter()
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        var poJobFilter = new Filter<PurchaseOrderItem>(x => x.Job.ID).InList(JobIDs)
 | 
	
		
			
				|  |  | -            .Or(x => x.Job.ID).IsEqualTo(Guid.Empty);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          return new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue)
 | 
	
		
			
				|  |  | -            .And(poJobFilter);
 | 
	
		
			
				|  |  | +            .And(GetJobFilter<PurchaseOrderItem>(x => x.Job.ID));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      
 | 
	
		
			
				|  |  |      protected override void Reload(Filters<StockSummary> criteria, Columns<StockSummary> columns, ref SortOrder<StockSummary>? sort,
 | 
	
	
		
			
				|  | @@ -533,32 +542,50 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |                  return;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              var pids = o.ExtractValues<StockSummary, Guid>(x => x.Product.ID).ToArray();
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | -            MultiQuery query = new MultiQuery();
 | 
	
		
			
				|  |  | -            query.Add<StockHolding>(
 | 
	
		
			
				|  |  | -                new Filter<StockHolding>(x => x.Product.ID).InList(pids)
 | 
	
		
			
				|  |  | -                    .And(StockHoldingFilter()),
 | 
	
		
			
				|  |  | -                new Columns<StockHolding>(x => x.Product.ID)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Dimensions.UnitSize)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Units)
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            query.Add<PurchaseOrderItem>(
 | 
	
		
			
				|  |  | -                new Filter<PurchaseOrderItem>(x => x.Product.ID).InList(pids)
 | 
	
		
			
				|  |  | -                    .And(PurchaseOrderItemFilter()),
 | 
	
		
			
				|  |  | -                new Columns<PurchaseOrderItem>(x => x.Product.ID)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Dimensions.UnitSize)
 | 
	
		
			
				|  |  | -                    .Add(x => x.Qty)
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  | -            query.Query();
 | 
	
		
			
				|  |  | -            var holdings = query.Get<StockHolding>();
 | 
	
		
			
				|  |  | +            var results = Client.QueryMultiple(
 | 
	
		
			
				|  |  | +                new KeyedQueryDef<StockHolding>(
 | 
	
		
			
				|  |  | +                    new Filter<StockHolding>(x => x.Product.ID).InList(pids)
 | 
	
		
			
				|  |  | +                        .And(StockHoldingFilter()),
 | 
	
		
			
				|  |  | +                    new Columns<StockHolding>(x => x.Product.ID)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Dimensions.UnitSize)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Units)),
 | 
	
		
			
				|  |  | +                new KeyedQueryDef<PurchaseOrderItem>(
 | 
	
		
			
				|  |  | +                    new Filter<PurchaseOrderItem>(x => x.Product.ID).InList(pids)
 | 
	
		
			
				|  |  | +                        .And(PurchaseOrderItemFilter()),
 | 
	
		
			
				|  |  | +                    new Columns<PurchaseOrderItem>(x => x.Product.ID)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Dimensions.UnitSize)
 | 
	
		
			
				|  |  | +                        .Add(x => x.Qty))//,
 | 
	
		
			
				|  |  | +                //new KeyedQueryDef<JobBillOfMaterialsItem>(
 | 
	
		
			
				|  |  | +                //    GetJobFilter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Job.ID),
 | 
	
		
			
				|  |  | +                //    new Columns<JobBillOfMaterialsItem>(x => x.BillOfMaterials.ID)
 | 
	
		
			
				|  |  | +                //        .Add(x => x.BillOfMaterials.Job.ID)
 | 
	
		
			
				|  |  | +                //        .Add(x => x.Product.ID)
 | 
	
		
			
				|  |  | +                //        .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | +                //        .Add(x => x.Dimensions.UnitSize)
 | 
	
		
			
				|  |  | +                //        .Add(x => x.TotalCost))
 | 
	
		
			
				|  |  | +                );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var holdings = results.Get<StockHolding>();
 | 
	
		
			
				|  |  |              var holdingcolumns = new Columns<StockHolding>(holdings.Columns.Select(x => x.ColumnName));
 | 
	
		
			
				|  |  |              
 | 
	
		
			
				|  |  | -            var orders = query.Get<PurchaseOrderItem>();
 | 
	
		
			
				|  |  | +            var orders = results.Get<PurchaseOrderItem>();
 | 
	
		
			
				|  |  |              var ordercolumns = new Columns<PurchaseOrderItem>(orders.Columns.Select(x => x.ColumnName));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            //var jobBOMs = results.GetObjects<JobBillOfMaterialsItem>()
 | 
	
		
			
				|  |  | +            //    .GroupBy(x => x.BillOfMaterials.Job.ID)
 | 
	
		
			
				|  |  | +            //    .ToDictionary(x => x.Key, x =>
 | 
	
		
			
				|  |  | +            //    {
 | 
	
		
			
				|  |  | +            //        var items = x.ToArray();
 | 
	
		
			
				|  |  | +            //        return new
 | 
	
		
			
				|  |  | +            //        {
 | 
	
		
			
				|  |  | +            //            Items = items,
 | 
	
		
			
				|  |  | +            //            Total = items.Sum(x => x.TotalCost)
 | 
	
		
			
				|  |  | +            //        };
 | 
	
		
			
				|  |  | +            //    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              var table = new CoreTable();
 | 
	
		
			
				|  |  |              table.LoadColumns(columns);
 | 
	
		
			
				|  |  |              
 | 
	
	
		
			
				|  | @@ -599,6 +626,21 @@ public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                          newrow.Set<StockSummary, double>(x => x.BalanceAvailable, balance);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +                        //var jobBOM = jobBOMs.GetValueOrDefault(newrow.Get<StockSummary, Guid>(x => x.Job.ID));
 | 
	
		
			
				|  |  | +                        //if(jobBOM is not null && jobBOM.Total != 0)
 | 
	
		
			
				|  |  | +                        //{
 | 
	
		
			
				|  |  | +                        //    var itemsCost = jobBOM.Items.Where(x =>
 | 
	
		
			
				|  |  | +                        //        x.Product.ID == key.Item1
 | 
	
		
			
				|  |  | +                        //        && (!key.Item2.HasValue || x.Style.ID == key.Item2.Value)
 | 
	
		
			
				|  |  | +                        //        && string.Equals(x.Dimensions.UnitSize, key.Item3)).Sum(x => x.TotalCost);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        //    newrow.Set<StockSummary, double>(x => x.JobBOM, itemsCost / jobBOM.Total * 100);
 | 
	
		
			
				|  |  | +                        //}
 | 
	
		
			
				|  |  | +                        //else
 | 
	
		
			
				|  |  | +                        //{
 | 
	
		
			
				|  |  | +                        //    newrow.Set<StockSummary, double>(x => x.JobBOM, 0.0);
 | 
	
		
			
				|  |  | +                        //}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |                          var productIssues = rows.First().Get<StockSummary, string>(x => x.Product.Issues);
 | 
	
		
			
				|  |  |                          var issues = new List<string>();
 | 
	
		
			
				|  |  |                          if(balance < 0)
 |