Kenric Nugteren 1 year ago
parent
commit
2083782c2a

+ 16 - 12
prs.classes/Entities/Product/Product.cs

@@ -75,6 +75,10 @@ namespace Comal.Classes
         [EditorSequence("Pricing", 1)]
         public ProductPricingStrategy PricingStrategy { get; set; } = ProductPricingStrategy.Standard;
 
+        [EnumLookupEditor(typeof(SupplierProductOrderStrategy))]
+        [EditorSequence("Pricing", 2)]
+        public SupplierProductOrderStrategy OrderStrategy { get; set; }
+
         private class SupplierProductLookup : LookupDefinitionGenerator<SupplierProduct, Product>
         {
             public override Filter<SupplierProduct> DefineFilter(Product[] items)
@@ -87,53 +91,53 @@ namespace Comal.Classes
                 => Columns.None<Product>().Add(x => x.ID);
         }
         [LookupDefinition(typeof(SupplierProductLookup))]
-        [EditorSequence("Pricing", 2)]
+        [EditorSequence("Pricing", 3)]
         public ProductSupplierLink Supplier { get; set; }
 
         [CheckBoxEditor]
-        [EditorSequence("Pricing", 3)]
+        [EditorSequence("Pricing", 4)]
         public bool UseDefaultSupplierPricing { get; set; } = true;
 
         [CurrencyEditor(Visible = Visible.Optional)]
-        [EditorSequence("Pricing", 4)]
+        [EditorSequence("Pricing", 5)]
         public double BaseCost { get; set; }
 
         [Aggregate(typeof(ProductComponentCost))]
         [CurrencyEditor(Visible = Visible.Optional)] //, Editable = Editable.Disabled)]
-        [EditorSequence("Pricing", 5)]
+        [EditorSequence("Pricing", 6)]
         public double ComponentCost { get; set; }
 
         [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Disabled)]
-        [EditorSequence("Pricing", 6)]
+        [EditorSequence("Pricing", 7)]
         public double NettCost { get; set; }
         
         [Obsolete("Replaced with ProductInstance.AverageCost", true)]
         [CurrencyEditor(Visible = Visible.Optional)]
-        [EditorSequence("Pricing", 7)]
+        [EditorSequence("Pricing", 8)]
         [LoggableProperty]
         public double AverageCost { get; set; }
         
-        [EditorSequence("Pricing", 8)]
+        [EditorSequence("Pricing", 9)]
         [RequiredColumn]
         public TaxCodeLink TaxCode { get; set; }
 
-        [EditorSequence("Pricing", 9)]
+        [EditorSequence("Pricing", 10)]
         [RequiredColumn]
         public PurchaseGLCodeLink PurchaseGL { get; set; }
 
-        [EditorSequence("Pricing", 10)]
+        [EditorSequence("Pricing", 11)]
         [RequiredColumn]
         public SalesGLCodeLink SellGL { get; set; }
 
-        [EditorSequence("Pricing", 11)]
+        [EditorSequence("Pricing", 12)]
         [RequiredColumn]
         public CostCentreLink CostCentre { get; set; }
 
-        [EditorSequence("Pricing", 12)]
+        [EditorSequence("Pricing", 13)]
         public CostSheetSectionLink CostSheetSection { get; set; }
         
         [ProductChargeEditor]
-        [EditorSequence("Pricing", 13)]
+        [EditorSequence("Pricing", 14)]
         public ProductCharge Charge { get; set; }
         
         /// <summary>

+ 3 - 0
prs.classes/Entities/Product/ProductLink.cs

@@ -76,6 +76,9 @@ namespace Comal.Classes
 
         [EnumLookupEditor(typeof(ProductPricingStrategy), Visible = Visible.Optional, Editable = Editable.Hidden)]
         public ProductPricingStrategy PricingStrategy { get; set; }
+
+        [EnumLookupEditor(typeof(SupplierProductOrderStrategy), Visible = Visible.Optional, Editable = Editable.Hidden)]
+        public SupplierProductOrderStrategy OrderStrategy { get; set; }
         
         [NullEditor]
         [RequiredColumn]

+ 15 - 0
prs.classes/Entities/Supplier/SupplierProductOrderStrategy.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Comal.Classes
+{
+    public enum SupplierProductOrderStrategy
+    {
+        Exact,
+        RoundUp,
+        LowestUnitPrice,
+        LowestOverallPrice,
+        LowestOverstock
+    }
+}

+ 2 - 2
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderScreen.xaml.cs

@@ -41,11 +41,11 @@ public partial class StockForecastOrderScreen : Window, INotifyPropertyChanged
 
     public IEnumerable<StockForecastOrderingResult> Results => Grid.Results;
 
-    public StockForecastOrderScreen(List<StockForecastOrderingItem> items)
+    public StockForecastOrderScreen(List<StockForecastOrderData> items)
     {
         InitializeComponent();
 
-        Grid.Items = items;
+        Grid.OrderData = items;
         Grid.Refresh(true, true);
     }
 

+ 133 - 60
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingGrid.cs

@@ -25,6 +25,29 @@ using Columns = InABox.Core.Columns;
 
 namespace PRSDesktop.Panels.StockForecast.OrderScreen;
 
+public class StockForecastOrderData
+{
+    public ProductLink Product { get; set; }
+
+    public ProductStyleLink Style { get; set; }
+
+    public StockDimensions Dimensions { get; set; }
+
+    public double RequiredQuantity { get; set; }
+
+    private Dictionary<Guid, double> JobRequiredQuantities { get; set; } = [];
+
+    public Dictionary<Guid, double> GetJobRequiredQuantities()
+    {
+        return JobRequiredQuantities;
+    }
+    public void SetJobRequiredQuantity(Guid jobID, double requiredQty)
+    {
+        JobRequiredQuantities[jobID] = requiredQty;
+    }
+}
+
+
 public enum StockForecastOrderingType
 {
     StockOrder,
@@ -35,29 +58,32 @@ public class StockForecastOrderingItemQuantity
 {
     public event Action? Changed;
 
-    private double _stockTotal;
-    public double StockTotal
+    private double _total;
+    public double Total
     {
-        get => _stockTotal;
+        get => _total;
         set
         {
-            _stockTotal = value;
+            _total = value;
             Changed?.Invoke();
         }
     }
 
-    public Dictionary<Guid, double> JobTotals { get; init; } = [];
+    private SupplierProduct? _supplierProduct;
+    public SupplierProduct? SupplierProduct
+    {
+        get => _supplierProduct;
+        set
+        {
+            _supplierProduct = value;
+            Changed?.Invoke();
+        }
+    }
 
     public void DoChanged()
     {
         Changed?.Invoke();
     }
-
-    public double JobTotal => JobTotals.Sum(x => x.Value);
-
-    public double GetTotal(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
-        ? StockTotal
-        : JobTotal;
 }
 
 public class StockForecastOrderingItem : BaseObject
@@ -72,19 +98,15 @@ public class StockForecastOrderingItem : BaseObject
     public StockDimensions Dimensions { get; set; }
 
     [EditorSequence(4)]
+    public JobLink Job { get; set; }
+
+    [EditorSequence(5)]
     [DoubleEditor]
     public double RequiredQuantity { get; set; }
 
-    private Dictionary<Guid, double> JobRequiredQuantities { get; set; } = [];
-
-    public Dictionary<Guid, double> GetJobRequiredQuantities()
-    {
-        return JobRequiredQuantities;
-    }
-    public void SetJobRequiredQuantity(Guid jobID, double requiredQty)
-    {
-        JobRequiredQuantities[jobID] = requiredQty;
-    }
+    [EditorSequence(6)]
+    [EnumLookupEditor(typeof(SupplierProductOrderStrategy))]
+    public SupplierProductOrderStrategy OrderStrategy { get; set; }
 
     private StockForecastOrderingItemQuantity[] Quantities = [];
 
@@ -127,8 +149,11 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
     private List<SupplierProduct> SupplierProducts = [];
     private SupplierLink[] Suppliers = [];
 
+    public IList<StockForecastOrderData> OrderData { get; set; }
+
     public double TotalQuantity => Items.Sum(x => x.GetTotalQuantity(OrderType));
 
+    private DynamicActionColumn[] SupplierProductColumns = [];
     private DynamicActionColumn[] QuantityColumns = [];
     private DynamicActionColumn[] CostColumns = [];
 
@@ -291,7 +316,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             .Add(x => x.SupplierLink.Code);
 
         SupplierProducts = Client.Query(
-            new Filter<SupplierProduct>(x => x.Product.ID).InList(Items.Select(x => x.Product.ID).ToArray())
+            new Filter<SupplierProduct>(x => x.Product.ID).InList(OrderData.Select(x => x.Product.ID).ToArray())
                 .And(x => x.SupplierLink.ID).IsNotEqualTo(Guid.Empty),
             supplierColumns,
             new SortOrder<SupplierProduct>(x => x.SupplierLink.Code))
@@ -299,16 +324,6 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         Suppliers = SupplierProducts.Select(x => x.SupplierLink).DistinctBy(x => x.ID).ToArray();
 
-        foreach(var (itemIdx, item) in Items.WithIndex())
-        {
-            var quantities = new StockForecastOrderingItemQuantity[Suppliers.Length];
-            for(int i = 0; i < Suppliers.Length; ++i)
-            {
-                quantities[i] = CreateQuantity(itemIdx);
-            }
-
-            item.SetQuantities(quantities);
-        }
 
         CalculateQuantities();
 
@@ -330,42 +345,72 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
     private void CalculateQuantities()
     {
         SetObserving(false);
+
+        Items.Clear();
+        foreach(var dataItem in OrderData)
+        {
+            if(OrderType == StockForecastOrderingType.StockOrder)
+            {
+                var item = new StockForecastOrderingItem();
+                item.Product.CopyFrom(dataItem.Product);
+                item.Style.CopyFrom(dataItem.Style);
+                item.Dimensions.CopyFrom(dataItem.Dimensions);
+                item.RequiredQuantity = dataItem.RequiredQuantity;
+                item.OrderStrategy = item.Product.OrderStrategy;
+                Items.Add(item);
+            }
+            else
+            {
+                foreach(var (id, q) in dataItem.GetJobRequiredQuantities())
+                {
+                    var item = new StockForecastOrderingItem();
+                    item.Product.CopyFrom(dataItem.Product);
+                    item.Style.CopyFrom(dataItem.Style);
+                    item.Dimensions.CopyFrom(dataItem.Dimensions);
+                    item.Job.ID = id;
+                    item.RequiredQuantity = q;
+                    item.OrderStrategy = item.Product.OrderStrategy;
+
+                    Items.Add(item);
+                }
+            }
+        }
+
+        foreach(var (itemIdx, item) in Items.WithIndex())
+        {
+            var quantities = new StockForecastOrderingItemQuantity[Suppliers.Length];
+            for(int i = 0; i < Suppliers.Length; ++i)
+            {
+                quantities[i] = CreateQuantity(itemIdx);
+            }
+
+            item.SetQuantities(quantities);
+        }
+
         foreach(var item in Items)
         {
-            var supplierProduct = GetSupplierProduct(item);
+            var selectedSupplierProducts = new List<SupplierProduct>();
             for(int i = 0; i < Suppliers.Length; ++i)
             {
+                var supplierProduct = SelectSupplierProduct(SupplierProducts.Where(x => x.Product.ID == item.Product.ID && x.Style.ID == item.Style.ID && x.SupplierLink.ID == Suppliers[i].ID), item);
+
                 var qty = item.GetQuantity(i);
+                qty.SupplierProduct = supplierProduct;
+                qty.Total = 0;
 
-                var supplier = Suppliers[i];
-                if(supplierProduct is not null && supplier.ID == supplierProduct.SupplierLink.ID)
+                if(supplierProduct is not null)
                 {
-                    if(OrderType == StockForecastOrderingType.StockOrder)
-                    {
-                        qty.StockTotal = qty.JobTotal;
-                    }
-                    else
-                    {
-                        qty.JobTotals.Clear();
-                        foreach(var (id, q) in item.GetJobRequiredQuantities())
-                        {
-                            qty.JobTotals[id] = q;
-                        }
-                    }
+                    selectedSupplierProducts.Add(supplierProduct);
                 }
-                else
+            }
+
+            var selectedSupplierProduct = SelectSupplierProduct(selectedSupplierProducts, item);
+            if(selectedSupplierProduct is not null)
+            {
+                var supplierIdx = Suppliers.WithIndex().FirstOrDefault(x => x.Value.ID == selectedSupplierProduct.SupplierLink.ID, new KeyValuePair<int, SupplierLink>(-1, null)).Key;
+                if(supplierIdx != -1)
                 {
-                    if(OrderType == StockForecastOrderingType.StockOrder)
-                    {
-                        qty.StockTotal = 0;
-                    }
-                    else
-                    {
-                        foreach(var id in item.GetJobRequiredQuantities().Keys)
-                        {
-                            qty.JobTotals[id] = 0;
-                        }
-                    }
+                    item.GetQuantity(supplierIdx).Total = GetRequiredQuantity(item, selectedSupplierProduct);
                 }
             }
         }
@@ -375,6 +420,16 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         InvalidateGrid();
     }
 
+    private SupplierProduct? SelectSupplierProduct(IEnumerable<SupplierProduct> supplierProducts, StockForecastOrderingItem item)
+    {
+
+    }
+
+    private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct)
+    {
+
+    }
+
     protected override DynamicGridColumns LoadColumns()
     {
         if (!_loadedData)
@@ -393,6 +448,16 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         columns.Add<StockForecastOrderingItem, string>(x => x.Dimensions.UnitSize, 80, "Size", "", Alignment.MiddleCenter);
         columns.Add<StockForecastOrderingItem, double>(x => x.RequiredQuantity, 80, "Required", "", Alignment.MiddleCenter);
 
+        ActionColumns.Add(new DynamicTemplateColumn(row =>
+        {
+            return null;
+        })
+        {
+            HeaderText = "Order Strategy.",
+            Width = 120
+        });
+
+        SupplierProductColumns = new DynamicActionColumn[Suppliers.Length];
         QuantityColumns = new DynamicActionColumn[Suppliers.Length];
         CostColumns = new DynamicActionColumn[Suppliers.Length];
         QuantityControls.Clear();
@@ -671,8 +736,15 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             return menu;
         };
 
-        // Making local copy of index so that the lambda can use it, and not the changed value of 'i'.
         var qtyColumn = new Tuple<DynamicActionColumn, QuantityControl?>(null!, null);
+        SupplierProductColumns[idx] = new DynamicTemplateColumn(row =>
+        {
+            return null;
+        })
+        {
+            HeaderText = "Supplier Product.",
+            Width = 80
+        };
         QuantityColumns[idx] = new DynamicTemplateColumn(row =>
         {
             var instance = LoadItem(row);
@@ -721,6 +793,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 return summary;
             }
         };
+        ActionColumns.Add(SupplierProductColumns[idx]);
         ActionColumns.Add(QuantityColumns[idx]);
         ActionColumns.Add(CostColumns[idx]);
     }

+ 2 - 2
prs.desktop/Panels/Stock Forecast/StockForecastGrid.cs

@@ -889,10 +889,10 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
             return false;
         }
 
-        var items = new List<StockForecastOrderingItem>();
+        var items = new List<StockForecastOrderData>();
         foreach(var forecastItem in LoadItems(rows))
         {
-            var item = new StockForecastOrderingItem();
+            var item = new StockForecastOrderData();
             item.Product.CopyFrom(forecastItem.Product);
             item.Style.CopyFrom(forecastItem.Style);
             item.Dimensions.CopyFrom(forecastItem.Dimensions);