Kaynağa Gözat

Overhauled Job Summary Screen

Frank van den Bos 2 yıl önce
ebeveyn
işleme
81145efc9d

+ 25 - 9
prs.classes/Entities/Job/Materials/IJobMaterial.cs

@@ -19,23 +19,39 @@ namespace Comal.Classes
     
     public interface IJobMaterialSummary
     {
+        // Total Required based on Approved BOMs
         double BillOfMaterials { get; set; }
      
-        double TotalRequired { get; set; }
+        // Total Required based on Job Requisitions
+        double Requisitions { get; set; }
         
-        double OnOrder { get; set; }
-        
-        double OrderValue { get; set; }
-        
-        double Received { get; set; }
+        // Total Required based on Stores Picking Lists
+        double PickingLists { get; set; }
         
+        // Total Issued to Job
         double Issued { get; set; }
+
+        // Total reserved for this job
+        double ReservedStock { get; set; }
+
+        // Total ordered for this job
+        double OnOrder { get; set; }
         
-        double BalanceRequired { get; set; }
+        // Not Yet Reserved or Ordered
+        double JobShortage { get; set; }
         
-        double ReservedStock { get; set; }
+        // Stock onhand not reserved for any active job
+        double FreeOnHand { get; set; }
+        
+        // Stock onorder not reserved for any active job
+        double FreeOnOrder { get; set; }     
+
+        // FreeOnHand + FreeOnOrder
+        double FreeStockTotal { get; set; }     
+
+        // JobShortage - FreeStock
+        double FreeStockShortage { get; set; }
         
-        double FreeStock { get; set; }
     }
     
 }

+ 74 - 94
prs.classes/Entities/Job/Materials/JobMaterials.cs

@@ -26,8 +26,8 @@ namespace Comal.Classes
         public override Filter<JobBillOfMaterialsItem> Filter =>
             new Filter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue);
     }
-
-    public class JobMaterialTotalRequiredAggregate : CoreAggregate<JobMaterial, JobRequisitionItem, double>
+    
+    public class JobMaterialRequisitionsAggregate : CoreAggregate<JobMaterial, JobRequisitionItem, double>
     {
         public override Expression<Func<JobRequisitionItem, double>> Aggregate => x => x.Qty;
 
@@ -47,47 +47,29 @@ namespace Comal.Classes
 
     }
     
-    public class JobMaterialOrderAggregate : CoreAggregate<JobMaterial, PurchaseOrderItem, double>
-    {
-        public override Expression<Func<PurchaseOrderItem, double>> Aggregate => x => x.Qty;
-
-        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
-
-        public override Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>> Links =>
-            new Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>>()
-            {
-                { PurchaseOrderItem => PurchaseOrderItem.Job.ID, JobMaterial => JobMaterial.Job.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Product.ID, JobMaterial => JobMaterial.Product.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Style.ID, JobMaterial => JobMaterial.Style.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
-            };
-
-        public override Filter<PurchaseOrderItem> Filter => new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue);
-    }
-
-    public class JobMaterialOrderValue : CoreAggregate<JobMaterial, PurchaseOrderItem, double>
+    public class JobMaterialPickingListsAggregate : CoreAggregate<JobMaterial, RequisitionItem, double>
     {
-        public override Expression<Func<PurchaseOrderItem, double>> Aggregate => x => x.ExTax;
+        public override Expression<Func<RequisitionItem, double>> Aggregate => x => x.Quantity;
 
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
 
-        public override Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>> Links =>
-            new Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>>()
+        public override Dictionary<Expression<Func<RequisitionItem, object>>, Expression<Func<JobMaterial, object>>> Links =>
+            new Dictionary<Expression<Func<RequisitionItem, object>>, Expression<Func<JobMaterial, object>>>()
             {
-                { PurchaseOrderItem => PurchaseOrderItem.Job.ID, JobMaterial => JobMaterial.Job.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Product.ID, JobMaterial => JobMaterial.Product.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Style.ID, JobMaterial => JobMaterial.Style.ID },
-                { PurchaseOrderItem => PurchaseOrderItem.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
+                { RequisitionItem => RequisitionItem.RequisitionLink.JobLink.ID, JobMaterial => JobMaterial.Job.ID },
+                { RequisitionItem => RequisitionItem.Product.ID, JobMaterial => JobMaterial.Product.ID },
+                { RequisitionItem => RequisitionItem.Style.ID, JobMaterial => JobMaterial.Style.ID },
+                { RequisitionItem => RequisitionItem.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
             };
+        
+        public override Filter<RequisitionItem> Filter =>
+            new Filter<RequisitionItem>(x => x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue);
 
-        public override Filter<PurchaseOrderItem> Filter => new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue);
     }
-
-    public class JobMaterialReceivedAggregate : CoreAggregate<JobMaterial, StockMovement, double>
+    
+    public class JobMaterialReservedStockAggregate : CoreAggregate<JobMaterial, StockMovement, double>
     {
-        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Received;
-
-        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(false);
+        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Units;
 
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
 
@@ -101,35 +83,29 @@ namespace Comal.Classes
             };
     }
 
-    public class JobMaterialIssuedAggregate : CoreAggregate<JobMaterial, StockMovement, double>
+    public class JobMaterialOnOrderAggregate : CoreAggregate<JobMaterial, PurchaseOrderItem, double>
     {
-        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Issued;
-
-        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(false);
+        public override Expression<Func<PurchaseOrderItem, double>> Aggregate => x => x.Qty;
 
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
 
-        public override Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<JobMaterial, object>>> Links =>
-            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<JobMaterial, object>>>()
+        public override Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>> Links =>
+            new Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<JobMaterial, object>>>()
             {
-                { StockMovement => StockMovement.Job.ID, JobMaterial => JobMaterial.Job.ID },
-                { StockMovement => StockMovement.Product.ID, JobMaterial => JobMaterial.Product.ID },
-                { StockMovement => StockMovement.Style.ID, JobMaterial => JobMaterial.Style.ID },
-                { StockMovement => StockMovement.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
+                { PurchaseOrderItem => PurchaseOrderItem.Job.ID, JobMaterial => JobMaterial.Job.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Product.ID, JobMaterial => JobMaterial.Product.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Style.ID, JobMaterial => JobMaterial.Style.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
             };
-    }
 
-    public class JobMaterialBalanceRequiredFormula : IFormula<JobMaterial, double>
-    {
-        public Expression<Func<JobMaterial, double>> Value => x => x.TotalRequired;
-        public Expression<Func<JobMaterial, double>>[] Modifiers => new Expression<Func<JobMaterial, double>>[] { x => x.Issued, x=>x.ReservedStock, x=>x.OnOrder };
-        public FormulaOperator Operator => FormulaOperator.Subtract;
-        public FormulaType Type => FormulaType.Virtual;
+        public override Filter<PurchaseOrderItem> Filter => new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue);
     }
-
-    public class JobMaterialReservedStockAggregate : CoreAggregate<JobMaterial, StockMovement, double>
+    
+    public class JobMaterialIssuedAggregate : CoreAggregate<JobMaterial, StockMovement, double>
     {
-        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Units;
+        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Issued;
+
+        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(false);
 
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
 
@@ -142,24 +118,16 @@ namespace Comal.Classes
                 { StockMovement => StockMovement.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
             };
     }
-
-    public class JobMaterialFreeStockAggregate : CoreAggregate<JobMaterial, StockMovement, double>
+    
+    public class JobMaterialCalculatedField : IFormula<JobMaterial, double>
     {
-        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Units;
-
-        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
-
-        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.Job).NotLinkValid().Or(x=>x.Job.JobStatus.Active).IsEqualTo(false);
-
-        public override Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<JobMaterial, object>>> Links =>
-            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<JobMaterial, object>>>()
-            {
-                { StockMovement => StockMovement.Product.ID, JobMaterial => JobMaterial.Product.ID },
-                //{ StockMovement => StockMovement.Style.ID, JobMaterial => JobMaterial.Style.ID },
-                //{ StockMovement => StockMovement.Dimensions.UnitSize, JobMaterial => JobMaterial.Dimensions.UnitSize }
-            };
+        public Expression<Func<JobMaterial, double>> Value => x => 0.0;
+        public Expression<Func<JobMaterial, double>>[] Modifiers => new Expression<Func<JobMaterial, double>>[] {  };
+        public FormulaOperator Operator => FormulaOperator.Constant;
+        public FormulaType Type => FormulaType.Virtual;
     }
-
+    
+    
     public class JobMaterialUnionGenerator : AutoEntityUnionGenerator<IJobMaterial>
     {
         protected override void Configure()
@@ -216,43 +184,55 @@ namespace Comal.Classes
 
         [EditorSequence(5)]
         [DoubleEditor]
-        [Aggregate(typeof(JobMaterialTotalRequiredAggregate))]
-        public double TotalRequired { get; set; }
+        [Aggregate(typeof(JobMaterialRequisitionsAggregate))]
+        public double Requisitions { get; set; }
 
+        
         [EditorSequence(6)]
         [DoubleEditor]
-        [Aggregate(typeof(JobMaterialOrderAggregate))]
-        public double OnOrder { get; set; }
+        [Aggregate(typeof(JobMaterialPickingListsAggregate))]
+        public double PickingLists { get; set; }
 
-        [EditorSequence(7)]
-        [DoubleEditor]
-        [Aggregate(typeof(JobMaterialOrderValue))]
-        public double OrderValue { get; set; }
 
-        [EditorSequence(8)]
-        [Aggregate(typeof(JobMaterialReceivedAggregate))]
-        [DoubleEditor(Editable = Editable.Hidden)]
-        public double Received { get; set; }
-
-        [EditorSequence(9)]
+        [EditorSequence(7)]
         [Aggregate(typeof(JobMaterialIssuedAggregate))]
         [DoubleEditor(Editable = Editable.Hidden)]
         public double Issued { get; set; }
-
-        [EditorSequence(10)]
-        [DoubleEditor]
-        [Formula(typeof(JobMaterialBalanceRequiredFormula))]
-        public double BalanceRequired { get; set; }
-
-        [EditorSequence(12)]
+        
+        [EditorSequence(8)]
         [Aggregate(typeof(JobMaterialReservedStockAggregate))]
         [DoubleEditor(Editable = Editable.Hidden)]
         public double ReservedStock { get; set; }
+        
+        [EditorSequence(9)]
+        [DoubleEditor]
+        [Aggregate(typeof(JobMaterialOnOrderAggregate))]
+        public double OnOrder { get; set; }
 
+        [EditorSequence(10)]
+        [DoubleEditor]
+        [Formula(typeof(JobMaterialCalculatedField))]
+        public double JobShortage { get; set; }
+        
+        [EditorSequence(11)]
+        [DoubleEditor]
+        [Formula(typeof(JobMaterialCalculatedField))]
+        public double FreeOnHand { get; set; }
+        
+        [EditorSequence(12)]
+        [DoubleEditor]
+        [Formula(typeof(JobMaterialCalculatedField))]
+        public double FreeOnOrder { get; set; }
+        
         [EditorSequence(13)]
-        [Aggregate(typeof(JobMaterialFreeStockAggregate))]
-        [DoubleEditor(Editable = Editable.Hidden)]
-        public double FreeStock { get; set; }
+        [DoubleEditor]
+        [Formula(typeof(JobMaterialCalculatedField))]
+        public double FreeStockTotal { get; set; }
+        
+        [EditorSequence(14)]
+        [DoubleEditor]
+        [Formula(typeof(JobMaterialCalculatedField))]
+        public double FreeStockShortage { get; set; }
         
         protected override void Init()
         {

+ 1 - 1
prs.classes/Entities/PurchaseOrder/PurchaseOrderItem.cs

@@ -39,7 +39,7 @@ namespace Comal.Classes
     [UserTracking(typeof(Bill))]
     [Caption("Purchase Order Items")]
     public class PurchaseOrderItem : StockEntity, IRemotable, IPersistent, IOneToMany<PurchaseOrder>, ITaxable, IOneToMany<Consignment>, IOneToMany<Job>,
-        ILicense<AccountsPayableLicense>, IJobMaterial, IStockSummary
+        ILicense<AccountsPayableLicense>, IJobMaterial
     {
         [EntityRelationship(DeleteAction.SetNull)]
         [NullEditor]

+ 1 - 1
prs.classes/Entities/Stock/IStockHolding.cs

@@ -2,7 +2,7 @@
 
 namespace Comal.Classes
 {
-    public interface IStockHolding : IStockSummary
+    public interface IStockHolding : IJobMaterial
     {
         StockLocationLink Location { get; set; }
     }

+ 13 - 24
prs.classes/Entities/Stock/StockSummary.cs

@@ -133,7 +133,7 @@ namespace Comal.Classes
         public FormulaType Type => FormulaType.Virtual;
     }
 
-    public class StockSummaryUnionGenerator : AutoEntityUnionGenerator<IStockSummary>
+    public class StockSummaryUnionGenerator : AutoEntityUnionGenerator<IJobMaterial>
     {
         protected override void Configure()
         {
@@ -147,34 +147,23 @@ namespace Comal.Classes
 
         public override bool Distinct => true;
 
-        public override Column<IStockSummary>[] IDColumns => new Column<IStockSummary>[]
+        public override Column<IJobMaterial>[] IDColumns => new Column<IJobMaterial>[]
         {
-            new Column<IStockSummary>(x => x.Job.ID),
-            new Column<IStockSummary>(x => x.Product.ID),
-            new Column<IStockSummary>(x => x.Style.ID),
-            new Column<IStockSummary>(x => x.Dimensions.Unit.ID),
-            new Column<IStockSummary>(x => x.Dimensions.Quantity),
-            new Column<IStockSummary>(x => x.Dimensions.Length),
-            new Column<IStockSummary>(x => x.Dimensions.Width),
-            new Column<IStockSummary>(x => x.Dimensions.Height),
-            new Column<IStockSummary>(x => x.Dimensions.Weight)
+            new Column<IJobMaterial>(x => x.Job.ID),
+            new Column<IJobMaterial>(x => x.Product.ID),
+            new Column<IJobMaterial>(x => x.Style.ID),
+            new Column<IJobMaterial>(x => x.Dimensions.Unit.ID),
+            new Column<IJobMaterial>(x => x.Dimensions.Quantity),
+            new Column<IJobMaterial>(x => x.Dimensions.Length),
+            new Column<IJobMaterial>(x => x.Dimensions.Width),
+            new Column<IJobMaterial>(x => x.Dimensions.Height),
+            new Column<IJobMaterial>(x => x.Dimensions.Weight)
         };
     }
-
-    public interface IStockSummary
-    {
-        JobLink Job { get; set; }
-
-        ProductLink Product { get; set; }
-
-        ProductStyleLink Style { get; set; }
-
-        StockDimensions Dimensions { get; set; }
-    }
-
+    
     [UserTracking(typeof(Product))]
     [AutoEntity(typeof(StockSummaryUnionGenerator))]
-    public class StockSummary : StockEntity, IStockSummary, IRemotable, IPersistent
+    public class StockSummary : StockEntity, IJobMaterial, IRemotable, IPersistent
     {
 
         [NullEditor]

+ 347 - 112
prs.desktop/Panels/Jobs/JobSummaryGrid.cs

@@ -1,17 +1,25 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
+using System.Linq.Expressions;
 using System.Reflection;
 using System.Windows;
+using System.Windows.Controls;
 using Comal.Classes;
 using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
-using Microsoft.Exchange.WebServices.Data;
+
 
 namespace PRSDesktop
 {
     internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IJobControl, IDataModelSource
     {
+
+        private bool _stylecolumnVisible;
+
+        private bool _includeReserves;
+        
         public JobSummaryGrid() : base()
         {
             Options.AddRange(
@@ -27,74 +35,232 @@ namespace PRSDesktop
             HiddenColumns.Add(x => x.Dimensions.UnitSize);
 
             HiddenColumns.Add(x => x.BillOfMaterials);
-            HiddenColumns.Add(x => x.TotalRequired);
-            HiddenColumns.Add(x => x.BalanceRequired);
+            HiddenColumns.Add(x => x.Requisitions);
+            HiddenColumns.Add(x => x.PickingLists);
+            HiddenColumns.Add(x => x.Issued);
+            
             HiddenColumns.Add(x => x.ReservedStock);
             HiddenColumns.Add(x => x.OnOrder);
-            HiddenColumns.Add(x => x.Issued);
-            HiddenColumns.Add(x => x.Received);
 
+            HiddenColumns.Add(x => x.JobShortage);
+            
+            HiddenColumns.Add(x => x.FreeOnHand);
+            HiddenColumns.Add(x => x.FreeOnOrder);
+            HiddenColumns.Add(x => x.FreeStockTotal);
+            HiddenColumns.Add(x => x.FreeStockShortage);
+            
             HiddenColumns.Add(x => x.Product.Image.ID);
             HiddenColumns.Add(x => x.Product.Image.FileName);
             ActionColumns.Add(new DynamicImageManagerColumn<JobMaterial>(this, x => x.Product.Image, false)
                 { Position = DynamicActionColumnPosition.Start });
+            
+            AddButton("Include Reserves", null, ToggleIncludeReserves);
 
             OnCellDoubleClick += JobSummaryGrid_OnCellDoubleClick;
         }
 
-        private void JobSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
+        private bool ToggleIncludeReserves(Button sender, CoreRow[] arg2)
         {
-            if(args.Column.ColumnName == "BillOfMaterials"
-                || args.Column.ColumnName == "TotalRequired"
-                || args.Column.ColumnName == "ReservedStock"
-                || args.Column.ColumnName == "OnOrder"
-                || args.Column.ColumnName == "Issued"
-                || args.Column.ColumnName == "Received"
-                || args.Column.ColumnName == "FreeStock")
+            _includeReserves = !_includeReserves;
+            UpdateButton(sender, null, _includeReserves ? "Free Stock Only" : "Include Reserves");
+            return true;
+        }
+        
+        protected override void GenerateColumns(DynamicGridColumns columns)
+        {
+            columns.Add<JobMaterial, string>(x => x.Product.Group.Code, 120, "Group Code", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, string>(x => x.Product.Code, 200, "Product Code", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, string>(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, string>(x => x.Dimensions.UnitSize, 120, "UOM", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.BillOfMaterials, 80, "BOM", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.Requisitions, 80, "Req.", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.PickingLists, 80, "P/L", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.Issued, 80, "Issued", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.ReservedStock, 80, "Reserved", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.OnOrder, 80, "Ordered", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.JobShortage, 80, "Shortage", "", Alignment.MiddleCenter);
+            columns.Add<JobMaterial, double>(x => x.FreeStockTotal, 80, "Free Stock", "", Alignment.MiddleCenter);
+        }
+        
+        private void ShowDetailGrid<TEntity>(
+            String columnname, 
+            Expression<Func<TEntity,object?>> productcol, 
+            Guid productid, 
+            Expression<Func<TEntity,object?>> stylecol, 
+            Guid? styleid, 
+            Expression<Func<TEntity,object?>> unitcol, 
+            String unitsize,
+            Expression<Func<TEntity,object?>>? jobcol,
+            Filter<TEntity>? extrafilter,
+            Func<CoreRow,bool>? rowfilter
+        )
+        {
+            var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
+            if (grid == null)
             {
-                if(DatabaseSchema.Property(typeof(JobMaterial), args.Column.ColumnName) is StandardProperty property)
-                {
-                    var aggregate = property.Property.GetCustomAttribute<AggregateAttribute>();
-                    if(aggregate is not null)
-                    {
-                        var entityType = aggregate.Source;
-                        var filter = Filter.Create(entityType).All();
-                        
-                        var aggFilter = aggregate.Filter;
-                        if (aggFilter is not null)
-                            filter.And(aggFilter);
-
-                        var links = aggregate.Links;
-
-                        foreach (var (pLeft, pJobMaterial) in aggregate.Links)
-                        {
-                            var value = pJobMaterial == "Job.ID"
-                                ? ParentID
-                                : args.Row[pJobMaterial];
-
-                            filter.And(pLeft).IsEqualTo(value);
-                        }
-
-                        var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(entityType)) as IDynamicDataGrid)!;
-                        grid.ColumnsTag = $"JobSummaryAggregate.{args.Column.ColumnName}";
-                        //DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), entityType);
-                        grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
-                        grid.OnDefineFilter += t =>
-                        {
-                            return filter;
-                        };
+                MessageBox.Show($"Cannot create Grid for [{typeof(TEntity).Name}]");
+                return;
+            }
+            grid.ColumnsTag = $"{ColumnsTag}.{columnname}";
+            grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
+            grid.OnDefineFilter += t =>
+            {
+                var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
+                    .And(unitcol).IsEqualTo(unitsize);
+                if (styleid.HasValue)
+                    filter = filter.And(stylecol).IsEqualTo(styleid);
+                    
+                if (jobcol != null)
+                    filter = filter.And(jobcol).IsEqualTo(ParentID);
+                    
+                if (extrafilter != null)
+                    filter = filter.And(extrafilter);
+                return filter;
+            };
+            grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
+            var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", (grid as BaseDynamicGrid)!);
+            window.ShowDialog();
+        }
+        
+        private void JobSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
+        {
+            Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
+            Guid? styleid = _stylecolumnVisible ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
+            String unitsize = args.Row.Get<StockSummary, String>(c => c.Dimensions.UnitSize);
 
-                        var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(args.Column.ColumnName)} Calculation", (grid as BaseDynamicGrid)!);
-                        window.ShowDialog();
-                    }
-                }
+            if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.BillOfMaterials, ".")))
+            {
+                ShowDetailGrid<JobBillOfMaterialsItem>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x => x.Job.ID,
+                    new Filter<JobBillOfMaterialsItem>(x=>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue),
+                    null
+                );
             }
-            else if(args.Column.ColumnName == "BalanceRequired")
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.Requisitions, ".")))
+            {
+                ShowDetailGrid<JobRequisitionItem>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x => x.Job.ID,
+                    new Filter<JobRequisitionItem>(x=>x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue),
+                    null
+                );
+            }
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.PickingLists, ".")))
+            {
+                ShowDetailGrid<RequisitionItem>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x => x.Job.ID,
+                    new Filter<RequisitionItem>(x=>x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue),
+                    null
+                );
+            }
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.Issued, ".")))
             {
-                var totalRequired = (double)(args.Row["TotalRequired"] ?? 0.0);
-                var issued = (double)(args.Row["Issued"] ?? 0.0);
-                var balance = (double)(args.Row["BalanceRequired"] ?? 0.0);
-                MessageBox.Show($"BalanceRequired\n    = TotalRequired - Issued\n    = {totalRequired:F2} - {issued:F2} = {balance:F2}");
+                ShowDetailGrid<StockMovement>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x => x.Job.ID,
+                    new Filter<StockMovement>(x=>x.IsTransfer).IsEqualTo(false).And(x=>x.Issued).IsNotEqualTo(0.0F),
+                    null
+                );
+            }
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.ReservedStock, ".")))
+            {
+                ShowDetailGrid<StockHolding>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x=>x.Job.ID,
+                    null,
+                    null
+                );                  
+            }
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.OnOrder, ".")))
+            {
+                ShowDetailGrid<PurchaseOrderItem>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    x=>x.Job.ID,
+                    null,
+                    null
+                );                  
+            }
+            
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.FreeOnHand, ".")))
+            {
+                ShowDetailGrid<StockHolding>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    null,
+                    new Filter<StockHolding>(x=>x.Units).IsNotEqualTo(0.0F)
+                        .And(
+                            _includeReserves 
+                                ?  new Filter<StockHolding>(x=>x.Job.ID).IsNotEqualTo(ParentID) 
+                                : new Filter<StockHolding>(x=>x.Job.JobStatus.Active).IsEqualTo(false)
+                            ),
+                    null
+                );
+            }
+            else if (String.Equals(args.Column.ColumnName, CoreUtils.GetFullPropertyName<JobMaterial, double>(x => x.FreeOnOrder, ".")))
+            {
+                ShowDetailGrid<PurchaseOrderItem>(
+                    args.Column.ColumnName, 
+                    x => x.Product.ID, 
+                    productid, 
+                    x => x.Style.ID, 
+                    styleid, 
+                    x=>x.Dimensions.UnitSize,
+                    unitsize,
+                    null,
+                    
+                    _includeReserves 
+                        ?  new Filter<PurchaseOrderItem>(x=>x.Job.ID).IsNotEqualTo(ParentID)
+                        : new Filter<PurchaseOrderItem>(x=>x.Job.JobStatus.Active).IsEqualTo(false),
+                    null
+                );                
             }
         }
 
@@ -117,7 +283,55 @@ namespace PRSDesktop
             result.Job.ID = ParentID;
             return result;
         }
+        
+        private Tuple<Guid,Guid,Guid?,String>[] GetKeys(IEnumerable<CoreRow> rows, Columns<JobMaterial> columns, bool hasstyle)
+        {
+            int jobcol = columns.IndexOf(x => x.Job.ID);
+            int productcol = columns.IndexOf(x => x.Product.ID);
+            int stylecol = hasstyle ? columns.IndexOf(x => x.Style.ID) : -1;
+            int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
+            
+            var result = rows.Select(r => new Tuple<Guid, Guid, Guid?, String>(
+                (Guid)(r.Values[jobcol] ?? Guid.Empty),
+                (Guid)(r.Values[productcol] ?? Guid.Empty),
+                (stylecol != -1) ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : null,
+                (String)(r.Values[unitcol] ?? ""))
+            ).Distinct().ToArray();
+            
+            return result;
+        }
+
+        private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid? jobid, Guid productid, Guid? styleid, String unitsize) where TSource : IJobMaterial
+        {
+            int jobcol = columns.IndexOf(x => x.Job.ID);
+            int productcol = columns.IndexOf(x => x.Product.ID);
+            int stylecol = styleid.HasValue ? columns.IndexOf(x => x.Style.ID) : -1;
+            int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize); 
+            
+            var subset = rows
+                .Where(r=> 
+                    (!jobid.HasValue || Guid.Equals(r.Values[jobcol], jobid))
+                    && Guid.Equals(r.Values[productcol], productid)
+                    && (!styleid.HasValue || Guid.Equals(r.Values[stylecol], styleid))
+                    && String.Equals(r.Values[unitcol], unitsize)
+                );
+            
+            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<JobMaterial, object>> aggregate )
+        {
+            int srcol = columns.IndexOf(source);
+            
+            if (srcol == -1)
+                return 0.00;
 
+            var total =  rows.Aggregate(0d, (value, row) => value + (double)(row.Values[srcol] ?? 0.0d));
+            
+            target.Set(aggregate, total);
+            return total;
+        }
+        
         protected override void Reload(Filters<JobMaterial> criteria, Columns<JobMaterial> columns, ref SortOrder<JobMaterial>? sort,
             Action<CoreTable?, Exception?> action)
         {
@@ -129,73 +343,94 @@ namespace PRSDesktop
             
             var orderby = sort != null ? Serialization.Deserialize<SortOrder<JobMaterial>>(Serialization.Serialize(sort)) : null;
 
-            bool bHasStyle  = columns.ColumnNames().Any(x => x.StartsWith("Style.") && !x.Equals("Style.ID"));
-            
             new Client<JobMaterial>().Query(filter,columns,orderby, (o,e) =>
             {
                 
-                void UpdateColumn(CoreRow srcrow, CoreRow tgtrow, int colno)
-                {
-                    if (colno != -1)
-                        tgtrow.Set(colno, tgtrow.Get<double>(colno) + (double)(srcrow.Values[colno] ?? default(double)));
-                }
+                var pids = o.ExtractValues<JobMaterial, Guid>(x => x.Product.ID).ToArray();
+                
+                MultiQuery query = new MultiQuery();
+                
+                query.Add<StockHolding>(
+                    new Filter<StockHolding>(x => x.Product.ID).InList(pids)
+                        .And(x=>x.Units).IsNotEqualTo(0.0F)
+                        .And(
+                            _includeReserves
+                                ? new Filter<StockHolding>(x => x.Job.ID).IsNotEqualTo(ParentID)
+                                : new Filter<StockHolding>(x => x.Job.JobStatus.Active).IsEqualTo(false)
+                        ),
+                    new Columns<StockHolding>(x => x.Product.ID)
+                        .Add(x => x.Style.ID)
+                        .Add(x => x.Dimensions.UnitSize)
+                        .Add(x => x.Units)
+                        .Add(x=>x.Job.ID)
+                );
+                
+                query.Add<PurchaseOrderItem>(
+                    new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue)
+                        .And(x => x.Product.ID).InList(pids)
+                        .And(
+                            _includeReserves
+                                ? new Filter<PurchaseOrderItem>(x => x.Job.ID).IsNotEqualTo(ParentID)
+                                : new Filter<PurchaseOrderItem>(x => x.Job.JobStatus.Active).IsEqualTo(false)
+                        ),
+                    new Columns<PurchaseOrderItem>(x => x.Product.ID)
+                        .Add(x => x.Style.ID)
+                        .Add(x => x.Dimensions.UnitSize)
+                        .Add(x => x.Qty)
+                );
+                
+                query.Query();
+                
+                var freestock = query.Get<StockHolding>();
+                Columns<StockHolding> freestockcolumns = new Columns<StockHolding>(freestock.Columns.Select(x => x.ColumnName));
+                
+                var freeorders = query.Get<PurchaseOrderItem>();
+                Columns<PurchaseOrderItem> freeordercolumns = new Columns<PurchaseOrderItem>(freeorders.Columns.Select(x => x.ColumnName));
                 
                 CoreTable table = new CoreTable();
                 table.LoadColumns(columns);
                 
                 if (o != null)
                 {
-
-                    int productcol = columns.IndexOf(x => x.Product.ID);
-                    int stylecol = columns.IndexOf(x => x.Style.ID);
-                    int unitcol = columns.IndexOf(x => x.Dimensions.UnitSize);
                     
-                    int bomcol = columns.IndexOf(x => x.BillOfMaterials);
-                    int requiredcol = columns.IndexOf(x => x.TotalRequired);
-                    int reservedcol = columns.IndexOf(x => x.ReservedStock);
-                    int orderedcol = columns.IndexOf(x => x.OnOrder);
-                    int receivedcol = columns.IndexOf(x => x.Received);
-                    int issuedcol = columns.IndexOf(x => x.Issued);
-                    int balancecol = columns.IndexOf(x => x.BalanceRequired);
-
-
-                    Tuple<Guid, Guid, String>[] keys = o.Rows
-                        .Select(r => new Tuple<Guid, Guid, String>(
-                            (Guid)(r.Values[productcol] ?? Guid.Empty),
-                            bHasStyle ? (Guid)(r.Values[stylecol] ?? Guid.Empty) : Guid.Empty,
-                            (String)(r.Values[unitcol] ?? ""))
-                        ).Distinct().ToArray();
+                    
+                    var keys = GetKeys(o.Rows, columns, _stylecolumnVisible);
                     
                     foreach (var key in keys)
                     {
-                        var rows = o.Rows.Where(r =>
-                            Guid.Equals(r.Values[productcol], key.Item1) 
-                            && (bHasStyle ? Guid.Equals(r.Values[stylecol], key.Item2) : true)
-                            && String.Equals(r.Values[unitcol], key.Item3)
-                        ).ToArray();
                         
-                        CoreRow newrow = null;
-                        foreach (var row in rows)
+                        var rows = GetRows(o.Rows, columns, key.Item1, key.Item2, key.Item3, key.Item4);
+                        if (rows.Any())
                         {
-                            if (newrow == null)
-                            {
-                                newrow = table.NewRow();
-                                newrow.LoadValues(row.Values);
-                            }
-                            else
-                            {
-                                UpdateColumn(row, newrow, bomcol);
-                                UpdateColumn(row, newrow, requiredcol);
-                                UpdateColumn(row, newrow, orderedcol);
-                                UpdateColumn(row, newrow, receivedcol);
-                                UpdateColumn(row, newrow, issuedcol);
-                                UpdateColumn(row, newrow, balancecol);
-                                UpdateColumn(row, newrow, reservedcol);
-                            }
-                        }
+                            
+                            CoreRow newrow = table.NewRow();
+                            newrow.LoadValues(rows.First().Values);
+                            
+                            var bom = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.BillOfMaterials, newrow, x => x.BillOfMaterials);
+                            var requi = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.Requisitions, newrow, x => x.Requisitions);
+                            var picklist = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.PickingLists, newrow, x => x.PickingLists);
+                            var issued = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.Issued, newrow, x => x.Issued);
+                            
+                            var reserved = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.ReservedStock, newrow, x => x.ReservedStock);
+                            var ordered = Aggregate(rows, columns, _stylecolumnVisible, true, x => x.OnOrder, newrow, x => x.OnOrder);
 
-                        if (newrow != null)
+                            var shortage = Math.Max(0, Math.Max(0,(requi - issued)) - (reserved + ordered));
+                            newrow.Set<JobMaterial, double>(x => x.JobShortage, shortage);
+                            
+                            var freestockrows = GetRows(freestock.Rows, freestockcolumns, null, key.Item2, key.Item3, key.Item4);
+                            var freeonhand = Aggregate(freestockrows, freestockcolumns, _stylecolumnVisible, false, x => x.Units, newrow, x => x.FreeOnHand);
+                            newrow.Set<JobMaterial, double>(x => x.FreeOnHand, freeonhand);
+                            
+                            var freeorderrows = GetRows(freeorders.Rows, freeordercolumns, null, key.Item2, key.Item3, key.Item4);
+                            var freeonorder = Aggregate(freeorderrows, freeordercolumns, _stylecolumnVisible, false, x => x.Qty, newrow, x => x.FreeOnOrder);
+                            newrow.Set<JobMaterial, double>(x => x.FreeOnOrder, freeonorder);
+                            
+                            newrow.Set<JobMaterial, double>(x => x.FreeStockTotal, freeonhand+freeonorder);
+                            newrow.Set<JobMaterial, double>(x => x.FreeStockShortage, Math.Max(0,shortage-(freeonhand+freeonorder)));
                             table.Rows.Add(newrow);
+                        }
+                        
+
 
                     }
                 }
@@ -205,15 +440,15 @@ namespace PRSDesktop
 
         protected override bool FilterRecord(CoreRow row)
         {
-            var result = base.FilterRecord(row);
-            if (result)
-                result = (result && row.Get<JobMaterial, double>(x => x.BillOfMaterials) != 0.0F) ||
-                         row.Get<JobMaterial, double>(x => x.TotalRequired) != 0.0F ||
-                         row.Get<JobMaterial, double>(x => x.BalanceRequired) != 0.0F ||
-                         row.Get<JobMaterial, double>(x => x.OnOrder) != 0.0F ||
-                         row.Get<JobMaterial, double>(x => x.Issued) != 0.0F ||
-                         row.Get<JobMaterial, double>(x => x.Received) != 0.0F ||
-                         row.Get<JobMaterial, double>(x => x.ReservedStock) != 0.0F;
+            var result = base.FilterRecord(row)
+                && (
+                    row.Get<JobMaterial, double>(x => x.BillOfMaterials) != 0.0F ||
+                    row.Get<JobMaterial, double>(x => x.Requisitions) != 0.0F ||
+                    row.Get<JobMaterial, double>(x => x.PickingLists) != 0.0F ||
+                    row.Get<JobMaterial, double>(x => x.Issued) != 0.0F ||
+                    row.Get<JobMaterial, double>(x => x.ReservedStock) != 0.0F ||
+                    row.Get<JobMaterial, double>(x => x.OnOrder) != 0.0F
+                );
             return result;
         }
     }

+ 32 - 93
prs.desktop/Panels/StockSummary/StockSummaryGrid.cs

@@ -70,105 +70,50 @@ namespace PRSDesktop
             columns.Add<StockSummary, double>(x => x.BalanceAvailable, 120, "Balance Available", "", Alignment.MiddleCenter);
         }
 
-        private void ShowAggregateScreen(CoreRow row, DynamicGridColumn column, AggregateAttribute aggregate)
+        private void ShowDetailGrid<TEntity>(
+            String columnname, 
+            Expression<Func<TEntity,object?>> productcol, 
+            Guid productid, 
+            Expression<Func<TEntity,object?>> stylecol, 
+            Guid? styleid, 
+            Expression<Func<TEntity,object?>> unitcol, 
+            String unitsize,
+            Expression<Func<TEntity,object?>>? jobcol,
+            Filter<TEntity>? extrafilter,
+            Func<CoreRow,bool>? rowfilter
+        )
         {
-            var entityType = aggregate.Source;
-            var filter = Filter.Create(entityType).All();
-
-            var aggFilter = aggregate.Filter;
-            if (aggFilter is not null)
-                filter.And(aggFilter);
-
-            var links = aggregate.Links;
-
-            foreach (var (pLeft, pStockSummary) in aggregate.Links)
+            var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
+            if (grid == null)
             {
-                if(string.Equals(pStockSummary, CoreUtils.GetFullPropertyName<StockSummary, Guid>(x => x.Job.ID, ".")))
-                {
-                    var jobFilter = filter.And(pLeft);
-                    if (!JobIDs.Any())
-                    {
-                        jobFilter.None();
-                    }
-                    else if (!JobIDs.Contains(CoreUtils.FullGuid))
-                    {
-                        jobFilter.InList(JobIDs);
-                    }
-                    else
-                    {
-                        jobFilter.All();
-                    }
-                }
-                else if (string.Equals(pStockSummary, CoreUtils.GetFullPropertyName<StockSummary, Guid>(x => x.Style.ID, ".")))
-                {
-                    if (HasStyle())
-                    {
-                        var value = row[pStockSummary];
-                        filter.And(pLeft).IsEqualTo(value);
-                    }
-                }
-                else
-                {
-                    var value = row[pStockSummary];
-                    filter.And(pLeft).IsEqualTo(value);
-                }
+                MessageBox.Show($"Cannot create Grid for [{typeof(TEntity).Name}]");
+                return;
             }
-
-            var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(entityType)) as IDynamicDataGrid)!;
-            grid.ColumnsTag = $"StockSummaryAggregate.{column.ColumnName}";
-            //DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), entityType);
+            grid.ColumnsTag = $"{ColumnsTag}.{columnname}";
             grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
             grid.OnDefineFilter += t =>
             {
+                var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
+                    .And(unitcol).IsEqualTo(unitsize);
+                if (styleid.HasValue)
+                    filter = filter.And(stylecol).IsEqualTo(styleid);
+                    
+                if (jobcol != null)
+                    filter = filter.And(jobcol).InList(JobIDs);
+                    
+                if (extrafilter != null)
+                    filter = filter.And(extrafilter);
                 return filter;
             };
-
-            var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(column.ColumnName)} Calculation", (grid as BaseDynamicGrid)!);
+            grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
+            var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", (grid as BaseDynamicGrid)!);
             window.ShowDialog();
         }
-
+        
         private void StockSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
         {
 
-            void ShowDetailGrid<TEntity>(
-                String columnname, 
-                Expression<Func<TEntity,object?>> productcol, 
-                Guid productid, 
-                Expression<Func<TEntity,object?>> stylecol, 
-                Guid? styleid, 
-                Expression<Func<TEntity,object?>> unitcol, 
-                String unitsize,
-                Expression<Func<TEntity,object?>>? jobcol,
-                Filter<TEntity>? extrafilter,
-                Func<CoreRow,bool>? rowfilter
-            )
-            {
-                var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(typeof(TEntity))) as IDynamicDataGrid);
-                if (grid == null)
-                {
-                    MessageBox.Show($"Cannot create Grid for [{typeof(TEntity).Name}]");
-                    return;
-                }
-                grid.ColumnsTag = $"StockSummaryAggregate.{columnname}";
-                grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
-                grid.OnDefineFilter += t =>
-                {
-                    var filter = new Filter<TEntity>(productcol).IsEqualTo(productid)
-                        .And(unitcol).IsEqualTo(unitsize);
-                    if (styleid.HasValue)
-                        filter = filter.And(stylecol).IsEqualTo(styleid);
-                    
-                    if (jobcol != null)
-                        filter = filter.And(jobcol).InList(JobIDs);
-                    
-                    if (extrafilter != null)
-                        filter = filter.And(extrafilter);
-                    return filter;
-                };
-                grid.OnFilterRecord += row => rowfilter?.Invoke(row) ?? true;
-                var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(columnname)} Calculation", (grid as BaseDynamicGrid)!);
-                window.ShowDialog();
-            }
+           
             
             Guid productid = args.Row.Get<StockSummary, Guid>(c => c.Product.ID);
             Guid? styleid = HasStyle() ? args.Row.Get<StockSummary, Guid>(c => c.Style.ID) : null;
@@ -342,7 +287,7 @@ namespace PRSDesktop
             return result;
         }
 
-        private CoreRow[] GetRows<TSource>(IEnumerable<CoreRow> rows, Columns<TSource> columns, Guid productid, Guid? styleid, String unitsize) where TSource : IStockSummary
+        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;
@@ -394,12 +339,6 @@ namespace PRSDesktop
             new Client<StockSummary>().Query(filter,columns,sort, (o,e) =>
             {
                 
-                static void UpdateColumn(CoreRow srcrow, CoreRow tgtrow, int colno)
-                {
-                    if (colno != -1)
-                        tgtrow.Set(colno, tgtrow.Get<double>(colno) + (double)(srcrow.Values[colno] ?? default(double)));
-                }
-
                 var pids = o.ExtractValues<StockSummary, Guid>(x => x.Product.ID).ToArray();
                 
                 MultiQuery query = new MultiQuery();