Browse Source

Stock Forecast screen improvements

Kenric Nugteren 1 year ago
parent
commit
a27cba430d

+ 6 - 1
prs.classes/Entities/Stock/StockSummary.cs

@@ -260,7 +260,12 @@ namespace Comal.Classes
         [Formula(typeof(StockSummaryCalculatedField))]
         public double BalanceAvailable { get; set; }
 
-        [EditorSequence(13)]
+        //[EditorSequence(13)]
+        //[DoubleEditor]
+        //[Formula(typeof(StockSummaryCalculatedField))]
+        //public double JobBOM { get; set; }
+
+        [EditorSequence(14)]
         [NullEditor]
         [Formula(typeof(StockSummaryCalculatedStringField))]
         public string Issues { get; set; }

+ 10 - 1
prs.desktop/Panels/StockSummary/StockSummaryControl.xaml

@@ -35,7 +35,16 @@
 
                 <dynamicGrid:DynamicSplitPanel.Header>
                     <Border BorderBrush="DimGray" BorderThickness="0.75">
-                        <Label Content="Stock Summary" HorizontalContentAlignment="Center"/>
+                        <DockPanel>
+                            <CheckBox x:Name="FreeStockCheckBox" Content="Include Free Stock"
+                                      DockPanel.Dock="Right"
+                                      VerticalContentAlignment="Center"
+                                      Checked="FreeStockCheckBox_Checked"
+                                      Unchecked="FreeStockCheckBox_Checked"
+                                      Margin="5,0,5,0"/>
+                            <Label Content="Stock Summary" HorizontalContentAlignment="Center"
+                                   DockPanel.Dock="Left"/>
+                        </DockPanel>
                     </Border>
                 </dynamicGrid:DynamicSplitPanel.Header>
         

+ 14 - 0
prs.desktop/Panels/StockSummary/StockSummaryControl.xaml.cs

@@ -38,6 +38,9 @@ public partial class StockSummaryControl : UserControl
             SplitPanel.AnchorWidth = Properties.SplitPanelSettings.AnchorWidth;
             SplitPanel.DetailHeight = Properties.SplitPanelSettings.DetailHeight;
 
+            FreeStockCheckBox.IsChecked = Properties.IncludeFreeStock;
+            SummaryGrid.IncludeFreeStock = Properties.IncludeFreeStock;
+
             InitialiseSelectors();
             
             SummaryGrid.Refresh(true, false);
@@ -184,4 +187,15 @@ public partial class StockSummaryControl : UserControl
             Refresh();
         }
     }
+
+    private void FreeStockCheckBox_Checked(object sender, RoutedEventArgs e)
+    {
+        if (EventSuppressor.IsSet(Suppress.This))
+            return;
+
+        Properties.IncludeFreeStock = FreeStockCheckBox.IsChecked == true;
+        SummaryGrid.IncludeFreeStock = Properties.IncludeFreeStock;
+
+        SummaryGrid.Refresh(false, true);
+    }
 }

+ 85 - 43
prs.desktop/Panels/StockSummary/StockSummaryGrid.cs

@@ -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)

+ 2 - 0
prs.desktop/Panels/StockSummary/StockSummaryProperties.cs

@@ -19,6 +19,8 @@ public class StockSummaryProperties : IUserConfigurationSettings, IDashboardProp
     public DynamicGridSelectedFilterSettings ProductGroupFilter { get; set; } = new();
 
     public DynamicSplitPanelSettings SplitPanelSettings { get; set; } = new();
+
+    public bool IncludeFreeStock { get; set; } = true;
     
     public StockSummaryProperties()
     {