Browse Source

Got code building, and fixed job data not being loaded on stock forecast ordering screen.

Kenric Nugteren 11 months ago
parent
commit
30d523fd25

+ 11 - 0
prs.classes/Entities/Job/Requisitions/JobRequisitionItemPurchaseOrderItem.cs

@@ -6,6 +6,17 @@ using System.Text;
 
 namespace Comal.Classes
 {
+    public class PurchaseOrderItemAllocation : Entity, IRemotable, IPersistent, ILicense<ProjectManagementLicense>
+    {
+        public PurchaseOrderItemLink Item { get; set; }
+        
+        public JobLink Job { get; set; }
+        
+        public JobRequisitionItemLink JobRequisitionItem { get; set; }
+        
+        public double Quantity { get; set; }
+        
+    }
     
     public class JobRequisitionItemPurchaseOrderItem : Entity, IRemotable, IPersistent, IOneToMany<JobRequisitionItem>, ILicense<ProjectManagementLicense>
     {

+ 96 - 187
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingGrid.cs

@@ -25,13 +25,13 @@ using Columns = InABox.Core.Columns;
 
 namespace PRSDesktop.Panels.StockForecast.OrderScreen;
 
-public class StockForecastOrderData
+public class StockForecastOrderData(ProductLink product, ProductStyleLink style, StockDimensions dimensions)
 {
-    public ProductLink Product { get; set; }
+    public ProductLink Product { get; set; } = product;
 
-    public ProductStyleLink Style { get; set; }
+    public ProductStyleLink Style { get; set; } = style;
 
-    public StockDimensions Dimensions { get; set; }
+    public StockDimensions Dimensions { get; set; } = dimensions;
 
     public double RequiredQuantity { get; set; }
 
@@ -47,7 +47,6 @@ public class StockForecastOrderData
     }
 }
 
-
 public enum StockForecastOrderingType
 {
     StockOrder,
@@ -70,6 +69,9 @@ public class StockForecastOrderingItemQuantity
     }
 
     private SupplierProduct? _supplierProduct;
+    /// <summary>
+    /// Indicates the Supplier Product that has been selected for this cell. This comes from the combobox column.
+    /// </summary>
     public SupplierProduct? SupplierProduct
     {
         get => _supplierProduct;
@@ -108,13 +110,13 @@ public class StockForecastOrderingItem : BaseObject
     [EnumLookupEditor(typeof(SupplierProductOrderStrategy))]
     public SupplierProductOrderStrategy OrderStrategy { get; set; }
 
-    private StockForecastOrderingItemQuantity[] Quantities = [];
+    public StockForecastOrderingItemQuantity[] Quantities = [];
 
     public StockForecastOrderingItemQuantity GetQuantity(int i) => Quantities[i];
 
     public double GetTotalQuantity(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
-        ? Quantities.Sum(x => x.StockTotal)
-        : Quantities.Sum(x => x.JobTotal);
+        ? Quantities.Sum(x => x.Total)
+        : Quantities.Sum(x => x.Total);
 
     public void SetQuantities(StockForecastOrderingItemQuantity[] quantities)
     {
@@ -177,6 +179,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 {
                     control.UpdateControl(OrderType);
                 }
+
+                UIComponent.UpdateOrderType(OrderType);
+
+                Refresh(true, true);
             }
         }
     }
@@ -191,25 +197,20 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 foreach(var item in Items)
                 {
                     var qty = item.GetQuantity(i);
-                    var supplierProduct = GetSupplierProduct(item, supplier.ID);
-                    if (supplierProduct is null)
+                    if (qty.SupplierProduct is null)
                     {
-                        // If this is true, then the quantities also will have to be true.
                         continue;
                     }
 
-                    if(OrderType == StockForecastOrderingType.StockOrder && qty.StockTotal > 0)
-                    {
-                        yield return new(supplier, null, item, qty.StockTotal, supplierProduct);
-                    }
-                    else
+                    if(qty.Total > 0)
                     {
-                        foreach(var (jobID, q) in qty.JobTotals)
+                        if(OrderType == StockForecastOrderingType.StockOrder && qty.Total > 0)
                         {
-                            if(q > 0)
-                            {
-                                yield return new(supplier, new() { ID = jobID }, item, q, supplierProduct);
-                            }
+                            yield return new(supplier, null, item, qty.Total, qty.SupplierProduct);
+                        }
+                        else
+                        {
+                            yield return new(supplier, item.Job, item, qty.Total, qty.SupplierProduct);
                         }
                     }
                 }
@@ -246,7 +247,12 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             Parent = grid;
             Grid = grid;
 
-            DataGrid.FrozenColumnCount = 7;
+            UpdateOrderType(grid.OrderType);
+        }
+
+        public void UpdateOrderType(StockForecastOrderingType type)
+        {
+            DataGrid.FrozenColumnCount = type == StockForecastOrderingType.StockOrder ? 8 : 9;
         }
 
         protected override Brush? GetCellSelectionBackgroundBrush()
@@ -263,7 +269,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 var idx = Math.Max(qIdx, Grid.CostColumns.IndexOf(ac));
                 if(idx != -1)
                 {
-                    var supplierProduct = Grid.GetSupplierProduct(item, Grid.Suppliers[idx].ID);
+                    var supplierProduct = item.GetQuantity(idx).SupplierProduct;
                     if(supplierProduct is null)
                     {
                         return new SolidColorBrush(Colors.Gainsboro);
@@ -324,6 +330,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         Suppliers = SupplierProducts.Select(x => x.SupplierLink).DistinctBy(x => x.ID).ToArray();
 
+        LoadJobData(OrderData.SelectMany(x => x.GetJobRequiredQuantities().Keys).Distinct().Where(x => x != Guid.Empty));
 
         CalculateQuantities();
 
@@ -335,6 +342,8 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         var qty = new StockForecastOrderingItemQuantity();
         qty.Changed += () =>
         {
+            if (!_observing) return;
+
             var row = Data.Rows[itemIdx];
             InvalidateRow(row);
             DoChanged();
@@ -368,6 +377,12 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     item.Style.CopyFrom(dataItem.Style);
                     item.Dimensions.CopyFrom(dataItem.Dimensions);
                     item.Job.ID = id;
+
+                    if(id != Guid.Empty)
+                    {
+                        item.Job.CopyFrom(JobDetails[id]);
+                    }
+
                     item.RequiredQuantity = q;
                     item.OrderStrategy = item.Product.OrderStrategy;
 
@@ -407,7 +422,8 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             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;
+                var supplierIdx = Suppliers.WithIndex()
+                    .FirstOrDefault(x => x.Value.ID == selectedSupplierProduct.SupplierLink.ID, new KeyValuePair<int, SupplierLink>(-1, null)).Key;
                 if(supplierIdx != -1)
                 {
                     item.GetQuantity(supplierIdx).Total = GetRequiredQuantity(item, selectedSupplierProduct);
@@ -416,58 +432,67 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         }
         SetObserving(true);
         DoChanged();
-
-        InvalidateGrid();
     }
 
     private SupplierProduct? SelectSupplierProduct(IEnumerable<SupplierProduct> supplierProducts, StockForecastOrderingItem item)
     {
-
+        return null;
     }
 
     private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct)
     {
-
+        return 0;
     }
 
+    private bool _loadedColumns = false;
     protected override DynamicGridColumns LoadColumns()
     {
         if (!_loadedData)
         {
             LoadData();
         }
-        ActionColumns.Clear();
-
-        ActionColumns.Add(new DynamicImageColumn(Warning_Image) { Position = DynamicActionColumnPosition.Start });
-        ActionColumns.Add(new DynamicImagePreviewColumn<StockForecastOrderingItem>(x => x.Product.Image) { Position = DynamicActionColumnPosition.Start });
 
         var columns = new DynamicGridColumns();
         columns.Add<StockForecastOrderingItem, string>(x => x.Product.Code, 120, "Product Code", "", Alignment.MiddleCenter);
         columns.Add<StockForecastOrderingItem, string>(x => x.Product.Name, 200, "Product Name", "", Alignment.MiddleLeft);
         columns.Add<StockForecastOrderingItem, string>(x => x.Style.Code, 80, "Style", "", Alignment.MiddleCenter);
         columns.Add<StockForecastOrderingItem, string>(x => x.Dimensions.UnitSize, 80, "Size", "", Alignment.MiddleCenter);
+        if(OrderType == StockForecastOrderingType.JobOrder)
+        {
+            columns.Add<StockForecastOrderingItem, string>(x => x.Job.JobNumber, 80, "Job No.", "", Alignment.MiddleCenter);
+        }
         columns.Add<StockForecastOrderingItem, double>(x => x.RequiredQuantity, 80, "Required", "", Alignment.MiddleCenter);
 
-        ActionColumns.Add(new DynamicTemplateColumn(row =>
-        {
-            return null;
-        })
+        if (!_loadedColumns)
         {
-            HeaderText = "Order Strategy.",
-            Width = 120
-        });
+            ActionColumns.Clear();
 
-        SupplierProductColumns = new DynamicActionColumn[Suppliers.Length];
-        QuantityColumns = new DynamicActionColumn[Suppliers.Length];
-        CostColumns = new DynamicActionColumn[Suppliers.Length];
-        QuantityControls.Clear();
+            ActionColumns.Add(new DynamicImageColumn(Warning_Image) { Position = DynamicActionColumnPosition.Start });
+            ActionColumns.Add(new DynamicImagePreviewColumn<StockForecastOrderingItem>(x => x.Product.Image) { Position = DynamicActionColumnPosition.Start });
 
-        for(int i = 0; i < Suppliers.Length; ++i)
-        {
-            InitialiseSupplierColumn(i);
-        }
+            ActionColumns.Add(new DynamicTemplateColumn(row =>
+            {
+                return null;
+            })
+            {
+                HeaderText = "Order Strategy.",
+                Width = 120
+            });
 
-        ActionColumns.Add(new DynamicMenuColumn(BuildMenu));
+            SupplierProductColumns = new DynamicActionColumn[Suppliers.Length];
+            QuantityColumns = new DynamicActionColumn[Suppliers.Length];
+            CostColumns = new DynamicActionColumn[Suppliers.Length];
+            QuantityControls.Clear();
+
+            for(int i = 0; i < Suppliers.Length; ++i)
+            {
+                InitialiseSupplierColumn(i);
+            }
+
+            ActionColumns.Add(new DynamicMenuColumn(BuildMenu));
+
+            _loadedColumns = true;
+        }
 
         return columns;
     }
@@ -526,8 +551,6 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
                 foreach (var (itemIdx, item) in Items.WithIndex())
                 {
-                    var populateSupplierProduct = GetSupplierProduct(item);
-
                     var quantities = new StockForecastOrderingItemQuantity[newSuppliers.Length];
                     for (int i = 0; i < Suppliers.Length; ++i)
                     {
@@ -536,17 +559,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     var newQty = CreateQuantity(itemIdx);
 
                     quantities[newIdx] = newQty;
-                    if (OrderType == StockForecastOrderingType.StockOrder)
-                    {
-                        newQty.StockTotal = 0;
-                    }
-                    else
-                    {
-                        foreach (var id in item.GetJobRequiredQuantities().Keys)
-                        {
-                            newQty.JobTotals[id] = 0;
-                        }
-                    }
+                    newQty.Total = 0;
                     item.SetQuantities(quantities);
                 }
 
@@ -614,102 +627,28 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         public void UpdateControl(StockForecastOrderingType mode)
         {
-            var supplierProduct = Parent.GetSupplierProduct(Item, Parent.Suppliers[SupplierIndex].ID);
-            if(supplierProduct is null)
+            // If no supplier product has been selected for this cell, we can't allow the user to select a quantity.
+            if(Item.GetQuantity(SupplierIndex).SupplierProduct is null)
             {
                 Content = null;
                 return;
             }
 
-            if(mode == StockForecastOrderingType.StockOrder)
+            // Otherwise, simple quantity textbox editor.
+            var editor = new DoubleTextBox
             {
-                var editor = new DoubleTextBox
-                {
-                    VerticalAlignment = VerticalAlignment.Stretch,
-                    HorizontalAlignment = HorizontalAlignment.Stretch,
-                    Background = new SolidColorBrush(Colors.LightYellow),
-                    BorderThickness = new Thickness(0.0),
-                    MinValue = 0.0,
-                    Value = Item.GetQuantity(SupplierIndex).StockTotal
-                };
-                editor.ValueChanged += (o, e) =>
-                {
-                    Item.GetQuantity(SupplierIndex).StockTotal = editor.Value ?? default;
-                };
-                Content = editor;
-            }
-            else if(mode == StockForecastOrderingType.JobOrder)
+                VerticalAlignment = VerticalAlignment.Stretch,
+                HorizontalAlignment = HorizontalAlignment.Stretch,
+                Background = new SolidColorBrush(Colors.LightYellow),
+                BorderThickness = new Thickness(0.0),
+                MinValue = 0.0,
+                Value = Item.GetQuantity(SupplierIndex).Total
+            };
+            editor.ValueChanged += (o, e) =>
             {
-                var grid = new Grid();
-                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
-                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(30) });
-
-                var editor = new TextBox
-                {
-                    VerticalAlignment = VerticalAlignment.Stretch,
-                    HorizontalAlignment = HorizontalAlignment.Stretch,
-                    VerticalContentAlignment = VerticalAlignment.Center,
-                    HorizontalContentAlignment = HorizontalAlignment.Center,
-                    Background = new SolidColorBrush(Colors.White),
-                    BorderThickness = new Thickness(0.0),
-                    IsReadOnly = true,
-                    Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).JobTotal)
-                };
-                Grid.SetColumn(editor, 0);
-                grid.Children.Add(editor);
-
-                var btn = new Button
-                {
-                    VerticalAlignment = VerticalAlignment.Stretch,
-                    VerticalContentAlignment = VerticalAlignment.Center,
-                    HorizontalAlignment = HorizontalAlignment.Stretch,
-                    Content = "..",
-                    Margin = new Thickness(1),
-                    Focusable = false
-                };
-                btn.SetValue(Grid.ColumnProperty, 1);
-                btn.SetValue(Grid.RowProperty, 0);
-                btn.Click += (o, e) =>
-                {
-                    var qty = Item.GetQuantity(SupplierIndex);
-
-                    Parent.LoadJobData(qty.JobTotals.Keys);
-
-                    var items = qty.JobTotals.Select(x =>
-                    {
-                        var item = new StockForecastOrderingJobItem
-                        {
-                            JobID = x.Key,
-                            RequiredQuantity = Item.GetJobRequiredQuantities().GetValueOrDefault(x.Key),
-                            Quantity = x.Value
-                        };
-                        if(item.JobID == Guid.Empty)
-                        {
-                            item.Job = "General Stock";
-                        }
-                        else if(Parent.JobDetails.TryGetValue(item.JobID, out var job))
-                        {
-                            item.Job = $"{job.JobNumber}: {job.Name}";
-                        }
-                        return item;
-                    }).ToList();
-
-                    var window = new StockForecastOrderJobScreen();
-                    window.Items = items;
-                    if(window.ShowDialog() == true)
-                    {
-                        foreach(var item in items)
-                        {
-                            qty.JobTotals[item.JobID] = item.Quantity;
-                        }
-                        qty.DoChanged();
-                        editor.Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).JobTotal);
-                    }
-                };
-                grid.Children.Add(btn);
-
-                Content = grid;
-            }
+                Item.GetQuantity(SupplierIndex).Total = editor.Value ?? default;
+            };
+            Content = editor;
         }
     }
 
@@ -765,13 +704,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             }
 
             var instance = LoadItem(row);
-            var qty = OrderType == StockForecastOrderingType.StockOrder
-                ? instance.GetQuantity(idx).StockTotal
-                : instance.GetQuantity(idx).JobTotal;
-            var supplierProduct = GetSupplierProduct(instance, Suppliers[idx].ID);
-            if(supplierProduct is not null)
+            var qty = instance.GetQuantity(idx);//.Total;
+            if(qty.SupplierProduct is not null)
             {
-                return $"{qty * supplierProduct.CostPrice:C2}";
+                return $"{qty.Total * qty.SupplierProduct.CostPrice:C2}";
             }
             else
             {
@@ -821,37 +757,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             && item.Style.ID == supplierProduct.Style.ID
             && item.Dimensions.Equals(supplierProduct.Dimensions);
     }
-    private static bool Matches(ProductInstance instance, SupplierProduct supplierProduct)
-    {
-        return instance.Product.ID == supplierProduct.Product.ID
-            && instance.Style.ID == supplierProduct.Style.ID
-            && instance.Dimensions.Equals(supplierProduct.Dimensions);
-    }
-
-    private SupplierProduct? GetSupplierProduct(StockForecastOrderingItem item)
-    {
-        var defaultSupplierProduct = SupplierProducts.FirstOrDefault(x => x.ID == item.Product.Supplier.ID);
-        if(defaultSupplierProduct is not null && Matches(item, defaultSupplierProduct))
-        {
-            return defaultSupplierProduct;
-        }
-        else
-        {
-            return SupplierProducts.FirstOrDefault(x => Matches(item, x));
-        }
-    }
-    private SupplierProduct? GetSupplierProduct(ProductInstance instance, Guid supplierID)
-    {
-        return SupplierProducts.FirstOrDefault(x => x.SupplierLink.ID == supplierID && Matches(instance, x));
-    }
     private SupplierProduct? GetSupplierProduct(StockForecastOrderingItem item, Guid supplierID)
     {
         return SupplierProducts.FirstOrDefault(x => x.SupplierLink.ID == supplierID && Matches(item, x));
     }
-    //private double GetQuantity(SupplierProduct product)
-    //{
-    //    var instance = ProductInstances.WithIndex().Where(x => x.Value.Product.ID == product.ID)
-    //}
 
     private class CostAggregate : ISummaryAggregate
     {
@@ -881,11 +790,11 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 {
                     var rowIdx = dataRow.Row.Table.Rows.IndexOf(dataRow.Row);
                     var item = Grid.LoadItem(Grid.Data.Rows[rowIdx]);
-                    var supplierProduct = Grid.GetSupplierProduct(item, Grid.Suppliers[SupplierIndex].ID);
-                    if(supplierProduct is not null)
+
+                    var qty = item.GetQuantity(SupplierIndex);
+                    if(qty.SupplierProduct is not null)
                     {
-                        var qty = item.GetQuantity(SupplierIndex);
-                        Sum += qty.GetTotal(Grid.OrderType) * supplierProduct.CostPrice;
+                        Sum += qty.Total * qty.SupplierProduct.CostPrice;
                     }
                 }
             }

+ 11 - 17
prs.desktop/Panels/Stock Forecast/StockForecastGrid.cs

@@ -97,7 +97,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
     public HashSet<Guid> SupplierIDs { get; set; } = [];
 
     private readonly Button OrderButton;
-    private HashSet<Guid> SelectedForOrder = [];
+    private HashSet<CoreRow> SelectedForOrder = [];
 
     private DynamicGridCustomColumnsComponent<StockForecastItem> ColumnsComponent;
     private string ColumnsTag => "StockForecastGrid";
@@ -207,7 +207,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
     {
         return (row is null)
             ? _warning
-            : row.Get<ProductInstance, string>(x => x.Product.Issues).IsNullOrWhiteSpace() 
+            : row.Get<StockForecastItem, string>(x => x.Product.Issues).IsNullOrWhiteSpace() 
                 ? null 
                 : _warning;
     }
@@ -216,7 +216,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
     {
         return (row is null)
             ? null
-            : column.TextToolTip(row.Get<ProductInstance, string>(x => x.Product.Issues));
+            : column.TextToolTip(row.Get<StockForecastItem, string>(x => x.Product.Issues));
     }
 
     #region UIComponent
@@ -593,12 +593,10 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
 
     private string[] GetColumnFilterItems(ColumnTag tag)
     {
-        var selectedIDs = Data.Rows.Select(x => x.Get<ProductInstance, Guid>(x => x.ID));
-
         var items = new HashSet<string>();
-        foreach(var item in Items)
+        foreach(var row in Data.Rows)
         {
-            var value = GetColumnCalculatedData(tag, item);
+            var value = GetColumnCalculatedData(tag, row);
             if (value.HasValue)
             {
                 items.Add(value.Value.ToString("F2"));
@@ -838,7 +836,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
             {
                 foreach (var row in FilterRows(Data.Rows))
                 {
-                    SelectedForOrder.Add(row.Get<ProductInstance, Guid>(x => x.ID));
+                    SelectedForOrder.Add(row);
                     InvalidateRow(row);
                 }
                 OrderButton.IsEnabled = SelectedForOrder.Count > 0;
@@ -854,10 +852,9 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
         }
         else
         {
-            var id = row.Get<ProductInstance, Guid>(x => x.ID);
-            if (!SelectedForOrder.Remove(id))
+            if (!SelectedForOrder.Remove(row))
             {
-                SelectedForOrder.Add(id);
+                SelectedForOrder.Add(row);
             }
             OrderButton.IsEnabled = SelectedForOrder.Count > 0;
             InvalidateRow(row);
@@ -871,7 +868,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
         {
             return _cart;
         }
-        else if(SelectedForOrder.Contains(row.Get<ProductInstance, Guid>(x => x.ID)))
+        else if(SelectedForOrder.Contains(row))
         {
             return _cart;
         }
@@ -883,7 +880,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
     
     private bool OrderStock_Click(Button button, CoreRow[] rows)
     {
-        rows = FilterRows(Data.Rows.Where(x => SelectedForOrder.Contains(x.Get<ProductInstance, Guid>(x => x.ID)))).ToArray();
+        rows = FilterRows(Data.Rows.Where(x => SelectedForOrder.Contains(x))).ToArray();
         if(rows.Length == 0)
         {
             return false;
@@ -892,10 +889,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
         var items = new List<StockForecastOrderData>();
         foreach(var forecastItem in LoadItems(rows))
         {
-            var item = new StockForecastOrderData();
-            item.Product.CopyFrom(forecastItem.Product);
-            item.Style.CopyFrom(forecastItem.Style);
-            item.Dimensions.CopyFrom(forecastItem.Dimensions);
+            var item = new StockForecastOrderData(forecastItem.Product, forecastItem.Style, forecastItem.Dimensions);
             item.RequiredQuantity = Optimise ? forecastItem.Optimised : forecastItem.Required;
 
             item.SetJobRequiredQuantity(Guid.Empty, forecastItem.StockRequired);

+ 117 - 0
prs.shared/Database Update Scripts/Update_8_14.cs

@@ -0,0 +1,117 @@
+using Comal.Classes;
+using InABox.Core;
+using InABox.Database;
+
+namespace PRS.Shared.Database_Update_Scripts;
+
+internal class Update_8_14 : DatabaseUpdateScript
+{
+
+    private static readonly int CHUNK_SIZE = 500;
+    private static readonly bool DESTRUCTIVE = false;
+    
+    public override VersionNumber Version => new(8, 14);
+    
+    public override bool Update()
+    {
+        var _provider = DbFactory.NewProvider(Logger.Main);
+        Clear_POIAs(_provider);
+        var _poias = Migrate_JRIPOIs(_provider);
+        Migrate_POIs(_provider, _poias);
+        return true;
+    }
+
+    private void Clear_POIAs(IProvider provider)
+    {
+        return;
+        Logger.Send(LogType.Information, "", "Clearing Existing Allocations");
+        var _queue = provider.Query(
+            new Filter<PurchaseOrderItemAllocation>().All(),
+            Columns.None<PurchaseOrderItemAllocation>().Add(x => x.ID)
+        ).Rows.ToQueue();
+
+        while (_queue.Any())
+        {
+            List<PurchaseOrderItemAllocation> _poias = _queue
+                .Dequeue(CHUNK_SIZE)
+                .Select(x => x.ToObject<PurchaseOrderItemAllocation>())
+                .ToList();
+            provider.Delete(_poias, "");
+
+            Logger.Send(LogType.Information, "", $"- Deleted {_poias.Count} Allocations ({_queue.Count} remaining)");
+        }
+    }
+
+    private List<PurchaseOrderItemAllocation> Migrate_JRIPOIs(IProvider provider)
+    {
+        var _result = new List<PurchaseOrderItemAllocation>();
+        return _result;
+        
+        Logger.Send(LogType.Information,"","Migrating JobRequisitionItems");
+        var _queue = provider.Query(
+            new Filter<JobRequisitionItemPurchaseOrderItem>().All(),
+            Columns.None<JobRequisitionItemPurchaseOrderItem>()
+                .Add(x=>x.PurchaseOrderItem.ID)
+                .Add(x=>x.PurchaseOrderItem.Qty)
+                .Add(x=>x.JobRequisitionItem.ID)
+                .Add(x=>x.JobRequisitionItem.Job.ID)
+        ).Rows.ToQueue();
+        
+        while (_queue.Any())
+        {
+            List<PurchaseOrderItemAllocation> _poias = new();
+            var _jripois = _queue.Dequeue(CHUNK_SIZE).Select(x=>x.ToObject<JobRequisitionItemPurchaseOrderItem>()).ToList();
+            foreach (var _jripoi in _jripois)
+            {
+                var _poia = new PurchaseOrderItemAllocation();
+                _poia.Item.ID = _jripoi.ID;
+                _poia.Job.ID = _jripoi.JobRequisitionItem.Job.ID;
+                _poia.JobRequisitionItem.ID = _jripoi.JobRequisitionItem.ID;
+                _poia.Quantity = _jripoi.PurchaseOrderItem.Qty;
+                _poias.Add(_poia);
+                CoreUtils.SetPropertyValue(_jripoi,"Job.ID",Guid.Empty);
+            }
+            provider.Save(_poias);
+            if (DESTRUCTIVE)
+                provider.Delete(_jripois,"");
+            Logger.Send(LogType.Information, "", $"- Created {_poias.Count} Allocations ({_queue.Count} remaining)");
+            _result.AddRange(_poias);
+        }
+
+        return _result;
+    }
+    
+    private void Migrate_POIs(IProvider provider, List<PurchaseOrderItemAllocation> poias)
+    {
+        Logger.Send(LogType.Information,"","Migrating PurchaseOrderItems");
+        var _ids = poias.Select(x => x.Item.ID).Distinct().ToArray();
+        var _queue = provider.Query(
+            new Filter<PurchaseOrderItem>("Job.ID").IsNotEqualTo(Guid.Empty),
+            Columns.Required<PurchaseOrderItem>().Add("Job.ID")
+        ).Rows.ToQueue();
+        
+        while (_queue.Any())
+        {
+            List<PurchaseOrderItemAllocation> _poias = new();
+            var _pois = _queue.Dequeue(CHUNK_SIZE)
+                .Where(r => !_ids.Contains(r.Get<PurchaseOrderItemAllocation,Guid>(c=>c.ID)))
+                .Select(x=>x.ToObject<PurchaseOrderItem>())
+                .ToList();
+            foreach (var _poi in _pois)
+            {
+                var _poia = new PurchaseOrderItemAllocation();
+                _poia.Item.ID = _poi.ID;
+                _poia.Job.ID = (Guid)(CoreUtils.GetPropertyValue(_poi, "Job.ID") ?? Guid.Empty);
+                _poia.Quantity = _poi.Qty;
+                _poias.Add(_poia);
+                CoreUtils.SetPropertyValue(_poi,"Job.ID",Guid.Empty);
+            }
+            provider.Save(_poias);
+            if(DESTRUCTIVE)
+                provider.Save(_pois);
+            Logger.Send(LogType.Information, "", $"- Created {_poias.Count} Allocations ({_queue.Count} remaining)");
+        }
+    }
+
+
+}