瀏覽代碼

JobSummary grid changes

Kenric Nugteren 11 月之前
父節點
當前提交
daeaa5338e

+ 1 - 1
prs.classes/Entities/Job/Materials/JobMaterials.cs

@@ -42,7 +42,7 @@ namespace Comal.Classes
         
         
         public override Filter<JobRequisitionItem> Filter =>
         public override Filter<JobRequisitionItem> Filter =>
             new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)
             new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)
-                .And(x=>x.Status).IsNotEqualTo(JobRequisitionItemStatus.Cancelled);
+                .And(x => x.Cancelled).IsEqualTo(DateTime.MinValue);
 
 
     }
     }
     
     

+ 203 - 0
prs.desktop/Panels/Jobs/Summary/JobRequisitionItemSummaryGrid.cs

@@ -0,0 +1,203 @@
+using Comal.Classes;
+using InABox.Configuration;
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.WPF;
+using Syncfusion.Data;
+using Syncfusion.UI.Xaml.Grid;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace PRSDesktop.Panels.Jobs.Summary;
+
+public class JobRequisitionItemSummaryGrid : DynamicDataGrid<JobRequisitionItem>, ISpecificGrid
+{
+    public int DueDateAlert { get; set; }
+    public int DueDateWarning { get; set; }
+
+    protected override bool ShowSequenceButtons => false;
+
+    private class UIComponent : DynamicGridGridUIComponent<JobRequisitionItem>
+    {
+        private JobRequisitionItemSummaryGrid Grid;
+
+        public UIComponent(JobRequisitionItemSummaryGrid grid)
+        {
+            Grid = grid;
+            Parent = grid;
+        }
+
+        protected override Brush? GetCellBackground(CoreRow row, DynamicColumnBase column)
+        {
+            if (column is DynamicGridColumn col)
+            {
+                if (String.Equals(col.ColumnName,
+                        CoreUtils.GetFullPropertyName<JobRequisitionItem, DateTime>(x => x.Requisition.DueDate, ".")))
+                {
+                    var due = row.Get<JobRequisitionItem, DateTime>(x => x.Requisition.DueDate);
+                    if (!due.IsEmpty())
+                    {
+                        var background = DateTime.Today > due.Date
+                            ? Colors.LightSalmon
+                            : DateTime.Today.AddDays(Grid.DueDateWarning) >= due.Date
+                                ? Colors.Orange
+                                : DateTime.Today.AddDays(Grid.DueDateAlert) >= due.Date
+                                    ? Colors.LightYellow
+                                    : Colors.LightGreen;
+                        return new SolidColorBrush(background) { Opacity = 0.5 };
+                    }
+                }
+            }
+            else if(column is DynamicActionColumn dac)
+            {
+                if (dac == Grid.InStockColumn
+                    || dac == Grid.OnOrderColumn)
+                {
+                    return Colors.LightBlue.ToBrush(0.5);
+                }
+                else if (dac == Grid.TreatmentRequiredColumn
+                    || dac == Grid.TreatmentOnOrderColumn)
+                {
+                    return Colors.Plum.ToBrush(0.5);
+                }
+            }
+
+            var qty = row.Get<JobRequisitionItem, double>(x => x.Qty);
+            if(row.Get<JobRequisitionItem, double>(x => x.Issued) >= qty)
+            {
+                return Colors.Silver.ToBrush(0.5);
+            }
+            else if(row.Get<JobRequisitionItem, double>(x => x.Allocated) + row.Get<JobRequisitionItem, double>(x => x.Issued) >= qty)
+            {
+                return Colors.LightGreen.ToBrush(0.5);
+            }
+            else if(row.Get<JobRequisitionItem, double>(x => x.InStock) + row.Get<JobRequisitionItem, double>(x => x.Issued) >= qty)
+            {
+                return Colors.Orange.ToBrush(0.5);
+            }
+            else
+            {
+                return Colors.LightSalmon.ToBrush(0.5);
+            }
+        }
+    }
+
+    private DynamicActionColumn InStockColumn;
+    private DynamicActionColumn OnOrderColumn;
+    private DynamicActionColumn TreatmentRequiredColumn;
+    private DynamicActionColumn TreatmentOnOrderColumn;
+    private DynamicActionColumn AllocatedColumn;
+    private DynamicActionColumn IssuedColumn;
+
+    public JobRequisitionItemSummaryGrid()
+    {
+        HiddenColumns.Add(x => x.Qty);
+        HiddenColumns.Add(x => x.InStock);
+        HiddenColumns.Add(x => x.OnOrder);
+        HiddenColumns.Add(x => x.TreatmentOnOrder);
+        HiddenColumns.Add(x => x.TreatmentRequired);
+        HiddenColumns.Add(x => x.Allocated);
+        HiddenColumns.Add(x => x.Issued);
+
+        var qtyColumn = AddDoubleColumn(x => x.Qty, "Qty.");
+        qtyColumn.GetSummary = () =>
+        {
+            return new GridSummaryColumn
+            {
+                Name = "Qty",
+                Format = "{Sum:F2}",
+                MappingName = "Qty",
+                SummaryType = Syncfusion.Data.SummaryType.Custom,
+                CustomAggregate = new QtyAggregate()
+            };
+        };
+
+        InStockColumn = AddDoubleColumn(x => x.InStock, "Stk.");
+        OnOrderColumn = AddDoubleColumn(x => x.OnOrder, "Ord.");
+        TreatmentRequiredColumn = AddDoubleColumn(x => x.TreatmentRequired, "Req.");
+        TreatmentOnOrderColumn = AddDoubleColumn(x => x.TreatmentOnOrder, "Ord.");
+        AllocatedColumn = AddDoubleColumn(x => x.Allocated, "Stk.");
+        IssuedColumn = AddDoubleColumn(x => x.Issued, "Iss.");
+
+        ColumnsTag = "JobRequisitionReview";
+    }
+
+    private class QtyAggregate : ISummaryAggregate
+    {
+        public double Sum { get; private set; }
+
+        public Action<IEnumerable, string, PropertyDescriptor> CalculateAggregateFunc()
+        {
+            return AggregateFunc;
+        }
+
+        private void AggregateFunc(IEnumerable items, string property, PropertyDescriptor args)
+        {
+            if (items is IEnumerable<DataRowView> rows)
+            {
+                Sum = 0;
+                foreach (var dataRow in rows)
+                {
+                    Sum += (double)dataRow["Qty"];
+                }
+            }
+            else
+            {
+                Logger.Send(LogType.Error, "", $"Attempting to calculate aggregate on invalid data type '{items.GetType()}'.");
+            }
+        }
+    }
+
+    private DynamicActionColumn AddDoubleColumn(Expression<Func<JobRequisitionItem, object>> property, string header)
+    {
+        var col = new DynamicTextColumn<JobRequisitionItem>(property) { Format = "F2", HeaderText = header, Width = 50 };
+        ActionColumns.Add(col);
+        return col;
+    }
+
+    protected override IDynamicGridUIComponent<JobRequisitionItem> CreateUIComponent()
+    {
+        return new UIComponent(this);
+    }
+
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+
+        options.Clear();
+        options.FilterRows = true;
+        options.SelectColumns = true;
+        options.RecordCount = true;
+    }
+
+    public override DynamicGridColumns GenerateColumns()
+    {
+        var columns = new DynamicGridColumns();
+        columns.Add<JobRequisitionItem, DateTime>(x => x.Requisition.DueDate, 80, "Due", "", Alignment.MiddleCenter);
+        columns.Add<JobRequisitionItem, string>(x => x.Requisition.Job.JobNumber, 70, "Job", "", Alignment.MiddleCenter);
+        columns.Add<JobRequisitionItem, int>(x => x.Requisition.Number, 50, "Requi", "", Alignment.MiddleCenter);
+        columns.Add<JobRequisitionItem, string>(x => x.Product.Code, 100, "Product Code", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Style.Code, 100, "Style", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Dimensions.UnitSize, 70, "Size", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, JobRequisitionItemStatus>(x => x.Status, 90, "Status", "", Alignment.MiddleCenter);
+
+        return columns;
+    }
+
+    protected override void ConfigureColumnGroups()
+    {
+        GetColumnGrouping()
+            .AddGroup("Stock", InStockColumn, OnOrderColumn)
+            .AddGroup("Treatment", TreatmentRequiredColumn, TreatmentOnOrderColumn)
+            .AddGroup("Allocated", AllocatedColumn, IssuedColumn);
+    }
+}

+ 78 - 36
prs.desktop/Panels/Jobs/Summary/JobSummaryGrid.cs

@@ -13,6 +13,7 @@ using System.Linq.Expressions;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media;
+using PRSDesktop.Panels.Jobs.Summary;
 
 
 namespace PRSDesktop;
 namespace PRSDesktop;
 
 
@@ -66,10 +67,13 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         foreach (var row in rows)
         foreach (var row in rows)
         {
         {
             var material = row.ToObject<JobMaterial>();
             var material = row.ToObject<JobMaterial>();
-            var filter = new Filter<StockHolding>(x => x.Product.ID).IsEqualTo(material.Product.ID)
-                .And(x => x.Dimensions).DimensionEquals(material.Dimensions);
+            var filter = new Filter<StockHolding>(x => x.Product.ID).IsEqualTo(material.Product.ID);
             if (StyleColumnVisible())
             if (StyleColumnVisible())
                 filter = filter.And(x => x.Style.ID).IsEqualTo(material.Style.ID);
                 filter = filter.And(x => x.Style.ID).IsEqualTo(material.Style.ID);
+
+            if(DimensionsColumnVisible())
+                filter = filter.And(x => x.Dimensions).DimensionEquals(material.Dimensions);
+
             var columns = Columns.None<StockHolding>().Add(x => x.Location.ID)
             var columns = Columns.None<StockHolding>().Add(x => x.Location.ID)
                 .Add(x => x.Product.ID)
                 .Add(x => x.Product.ID)
                 .Add(x => x.Style.ID)
                 .Add(x => x.Style.ID)
@@ -248,6 +252,13 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         return VisibleColumns.Any(x => x.ColumnName.StartsWith(styleColumn));
         return VisibleColumns.Any(x => x.ColumnName.StartsWith(styleColumn));
     }
     }
 
 
+    private bool DimensionsColumnVisible()
+    {
+        var dimColumn = CoreUtils.GetFullPropertyName<JobMaterial, StockDimensions>(x => x.Dimensions, ".");
+
+        return VisibleColumns.Any(x => x.ColumnName.StartsWith(dimColumn));
+    }
+
     public override DynamicGridColumns GenerateColumns()
     public override DynamicGridColumns GenerateColumns()
     {
     {
         var columns = new DynamicGridColumns();
         var columns = new DynamicGridColumns();
@@ -285,7 +296,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
 
 
         var productID = item.Product.ID;
         var productID = item.Product.ID;
         Guid? styleID = StyleColumnVisible() ? item.Style.ID : null;
         Guid? styleID = StyleColumnVisible() ? item.Style.ID : null;
-        var dimensions = item.Dimensions;
+        var dimensions = DimensionsColumnVisible() ? item.Dimensions : null;
 
 
         /*ShowDetailGrid<StockMovement>(
         /*ShowDetailGrid<StockMovement>(
             args.Column.ColumnName,
             args.Column.ColumnName,
@@ -324,8 +335,9 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
 
 
         grid.OnDefineFilter += t =>
         grid.OnDefineFilter += t =>
         {
         {
-            var filter = new Filter<StockMovement>(x => x.Product.ID).IsEqualTo(productID)
-                .And(x => x.Dimensions).DimensionEquals(dimensions);
+            var filter = new Filter<StockMovement>(x => x.Product.ID).IsEqualTo(productID);
+            if(dimensions is not null)
+                filter = filter.And(x => x.Dimensions).DimensionEquals(dimensions);
             if (styleID.HasValue)
             if (styleID.HasValue)
                 filter = filter.And(x => x.Style.ID).IsEqualTo(styleID);
                 filter = filter.And(x => x.Style.ID).IsEqualTo(styleID);
             filter = filter.And(x => x.Job.ID).IsEqualTo(Master?.ID ?? Guid.Empty);
             filter = filter.And(x => x.Job.ID).IsEqualTo(Master?.ID ?? Guid.Empty);
@@ -343,7 +355,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         Expression<Func<TEntity, object?>> stylecol,
         Expression<Func<TEntity, object?>> stylecol,
         Guid? styleid,
         Guid? styleid,
         Expression<Func<TEntity, IDimensions>> dimcol,
         Expression<Func<TEntity, IDimensions>> dimcol,
-        IDimensions dimensions,
+        IDimensions? dimensions,
         Expression<Func<TEntity, object?>>? jobcol,
         Expression<Func<TEntity, object?>>? jobcol,
         Filter<TEntity>? extrafilter,
         Filter<TEntity>? extrafilter,
         Func<CoreRow, bool>? rowfilter
         Func<CoreRow, bool>? rowfilter
@@ -364,8 +376,9 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         });
         });
         grid.OnDefineFilter += t =>
         grid.OnDefineFilter += t =>
         {
         {
-            var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
-                .And(CoreUtils.GetFullPropertyName(dimcol, ".")).DimensionEquals(dimensions);
+            var filter = new Filter<TEntity>(productcol).IsEqualTo(productid);
+            if(dimensions is not null)
+                filter = filter.And(CoreUtils.GetFullPropertyName(dimcol, ".")).DimensionEquals(dimensions);
             if (styleid.HasValue)
             if (styleid.HasValue)
                 filter = filter.And(stylecol).IsEqualTo(styleid);
                 filter = filter.And(stylecol).IsEqualTo(styleid);
 
 
@@ -400,7 +413,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             x => x.Job.ID,
             x => x.Job.ID,
             new Filter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
             new Filter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
             null
             null
@@ -409,18 +422,31 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
     private void ViewRequisitions(CoreRow row)
     private void ViewRequisitions(CoreRow row)
     {
     {
         var item = row.ToObject<JobMaterial>();
         var item = row.ToObject<JobMaterial>();
-        ShowDetailGrid<JobRequisitionItem>(
-            RequisitionsColumn.Property,
-            x => x.Product.ID,
-            item.Product.ID,
-            x => x.Style.ID,
-            StyleColumnVisible() ? item.Style.ID : null,
-            x => x.Dimensions,
-            item.Dimensions,
-            x => x.Job.ID,
-            new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue),
-            null
-        );
+
+        Guid? styleID = StyleColumnVisible() ? item.Style.ID : null;
+        var dimensions = DimensionsColumnVisible() ? item.Dimensions : null;
+
+        var grid = new JobRequisitionItemSummaryGrid();
+        grid.OnDefineFilter += t =>
+        {
+            var filter = new Filter<JobRequisitionItem>(x => x.Product.ID).IsEqualTo(item.Product.ID)
+                .And(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)
+                .And(x => x.Cancelled).IsEqualTo(DateTime.MinValue);
+
+            if(dimensions is not null)
+            {
+                filter = filter.And(x => x.Dimensions).DimensionEquals(item.Dimensions);
+            }
+            if (styleID.HasValue)
+            {
+                filter = filter.And(x => x.Style.ID).IsEqualTo(styleID.Value);
+            }
+            filter = filter.And(x => x.Requisition.Job.ID).IsEqualTo(Master?.ID ?? Guid.Empty);
+
+            return filter;
+        };
+        var window = DynamicGridUtils.CreateGridWindow($"Viewing Requisition Calculation", grid);
+        window.ShowDialog();
     }
     }
     private void ViewPickingLists(CoreRow row)
     private void ViewPickingLists(CoreRow row)
     {
     {
@@ -432,7 +458,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             x => x.JobLink.ID,
             x => x.JobLink.ID,
             new Filter<RequisitionItem>(x => x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue),
             new Filter<RequisitionItem>(x => x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue),
             null
             null
@@ -448,7 +474,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             x => x.Job.ID,
             x => x.Job.ID,
             new Filter<StockMovement>(x => x.Type).IsEqualTo(StockMovementType.Issue),
             new Filter<StockMovement>(x => x.Type).IsEqualTo(StockMovementType.Issue),
             null
             null
@@ -464,7 +490,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             x => x.Job.ID,
             x => x.Job.ID,
             new Filter<StockHolding>(x => x.Units).IsGreaterThan(0.1),
             new Filter<StockHolding>(x => x.Units).IsGreaterThan(0.1),
             null
             null
@@ -480,7 +506,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             x => x.Job.ID,
             x => x.Job.ID,
             null,
             null,
             null
             null
@@ -496,7 +522,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             null,
             null,
             new Filter<StockHolding>(x => x.Units).IsNotEqualTo(0.0F)
             new Filter<StockHolding>(x => x.Units).IsNotEqualTo(0.0F)
                 .And(
                 .And(
@@ -517,7 +543,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             x => x.Style.ID,
             x => x.Style.ID,
             StyleColumnVisible() ? item.Style.ID : null,
             StyleColumnVisible() ? item.Style.ID : null,
             x => x.Dimensions,
             x => x.Dimensions,
-            item.Dimensions,
+            DimensionsColumnVisible() ? item.Dimensions : null,
             null,
             null,
 
 
             IncludeReserves
             IncludeReserves
@@ -595,7 +621,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         return result;
         return result;
     }
     }
 
 
-    private class Key(Guid jobID, Guid productID, Guid? styleID, IDimensions dimensions)
+    private class Key(Guid jobID, Guid productID, Guid? styleID, IDimensions? dimensions)
     {
     {
         public Guid JobID { get; set; } = jobID;
         public Guid JobID { get; set; } = jobID;
 
 
@@ -603,21 +629,35 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
 
 
         public Guid? StyleID { get; set; } = styleID;
         public Guid? StyleID { get; set; } = styleID;
 
 
-        public IDimensions Dimensions { get; set; } = dimensions;
+        public IDimensions? Dimensions { get; set; } = dimensions;
+
+        public override bool Equals(object? obj)
+        {
+            return obj is Key key
+                && JobID == key.JobID
+                && ProductID == key.ProductID
+                && StyleID == key.StyleID
+                && Equals(Dimensions, key.Dimensions);
+        }
+        public override int GetHashCode()
+        {
+            return HashCode.Combine(
+                JobID, ProductID, StyleID, Dimensions);
+        }
     }
     }
 
 
-    private Key[] GetKeys(IEnumerable<CoreRow> rows, Columns<JobMaterial> columns, bool hasstyle)
+    private Key[] GetKeys(IEnumerable<CoreRow> rows, Columns<JobMaterial> columns, bool hasstyle, bool hasDimensions)
     {
     {
         int jobcol = columns.IndexOf(x => x.Job.ID);
         int jobcol = columns.IndexOf(x => x.Job.ID);
         int productcol = columns.IndexOf(x => x.Product.ID);
         int productcol = columns.IndexOf(x => x.Product.ID);
         int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
         int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
-        var dimCols = Dimensions.GetFilterColumnIndices<JobMaterial>(columns, x => x.Dimensions);
+        var dimCols = hasDimensions ? Dimensions.GetFilterColumnIndices<JobMaterial>(columns, x => x.Dimensions) : null;
 
 
         var result = rows.Select(r => new Key(
         var result = rows.Select(r => new Key(
             (Guid)(r.Values[jobcol] ?? Guid.Empty),
             (Guid)(r.Values[jobcol] ?? Guid.Empty),
             (Guid)(r.Values[productcol] ?? Guid.Empty),
             (Guid)(r.Values[productcol] ?? Guid.Empty),
             (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
             (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
-            r.ToDimensions<StockDimensions>(dimCols))
+            dimCols is not null ? r.ToDimensions<StockDimensions>(dimCols) : null)
         ).Distinct().ToArray();
         ).Distinct().ToArray();
 
 
         return result;
         return result;
@@ -637,7 +677,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
                 (!jobID.HasValue || Guid.Equals(jobID, r.Values[jobcol]))
                 (!jobID.HasValue || Guid.Equals(jobID, r.Values[jobcol]))
                 && Guid.Equals(key.ProductID, r.Values[productcol])
                 && Guid.Equals(key.ProductID, r.Values[productcol])
                 && (!key.StyleID.HasValue || Guid.Equals(key.StyleID, r.Values[stylecol]))
                 && (!key.StyleID.HasValue || Guid.Equals(key.StyleID, r.Values[stylecol]))
-                && key.Dimensions.Equals(r.ToDimensions<StockDimensions>(dimCols))
+                && (key.Dimensions is null || key.Dimensions.Equals(r.ToDimensions<StockDimensions>(dimCols)))
                 && ((extrafilter == null) || extrafilter(r))
                 && ((extrafilter == null) || extrafilter(r))
             );
             );
 
 
@@ -661,7 +701,8 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
         Action<CoreTable?, Exception?> action)
         Action<CoreTable?, Exception?> action)
     {
     {
 
 
-        var filter = MasterDetailFilter;
+        criteria.Add(MasterDetailFilter);
+        criteria.Add(FilterComponent.GetFilter());
 
 
         var orderby = sort;
         var orderby = sort;
 
 
@@ -671,7 +712,7 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
                 var table = new CoreTable();
                 var table = new CoreTable();
                 table.LoadColumns(columns);
                 table.LoadColumns(columns);
 
 
-                var data = new Client<JobMaterial>().Query(filter, columns, orderby);
+                var data = new Client<JobMaterial>().Query(criteria.Combine(), columns, orderby);
                 var pids = data.ExtractValues<JobMaterial, Guid>(x => x.Product.ID).ToArray();
                 var pids = data.ExtractValues<JobMaterial, Guid>(x => x.Product.ID).ToArray();
 
 
                 if (pids.Any())
                 if (pids.Any())
@@ -705,7 +746,8 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
                     var freeordercolumns = Columns.None<PurchaseOrderItem>().Add(freeorders.Columns.Select(x => x.ColumnName));
                     var freeordercolumns = Columns.None<PurchaseOrderItem>().Add(freeorders.Columns.Select(x => x.ColumnName));
 
 
                     var hasStyle = StyleColumnVisible();
                     var hasStyle = StyleColumnVisible();
-                    var keys = GetKeys(data.Rows, columns, hasStyle);
+                    var hasDimensions = DimensionsColumnVisible();
+                    var keys = GetKeys(data.Rows, columns, hasStyle, hasDimensions);
 
 
                     foreach (var key in keys)
                     foreach (var key in keys)
                     {
                     {