Browse Source

Re-added the StockForecastOrdering breakup

Kenric Nugteren 11 months ago
parent
commit
ec69cb14b3

+ 26 - 0
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderJobScreen.xaml

@@ -0,0 +1,26 @@
+<Window x:Class="PRSDesktop.Panels.StockForecast.OrderScreen.StockForecastOrderJobScreen"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:PRSDesktop.Panels.StockForecast.OrderScreen"
+        mc:Ignorable="d"
+        Title="Select Job Quantities" Height="450" Width="600">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        <local:StockForecastOrderingJobGrid x:Name="Grid" Margin="5,5,5,0"/>
+        <DockPanel Grid.Row="1" LastChildFill="False" x:Name="Buttons">
+            <Button x:Name="CancelButton" Click="CancelButton_Click"
+                    Content="Cancel"
+                    Margin="5" Padding="5" MinWidth="60"
+                    DockPanel.Dock="Right"/>
+            <Button x:Name="OKButton" Click="OKButton_Click"
+                    Content="OK"
+                    Margin="5,5,0,5" Padding="5" MinWidth="60"
+                    DockPanel.Dock="Right"/>
+        </DockPanel>
+    </Grid>
+</Window>

+ 50 - 0
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderJobScreen.xaml.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace PRSDesktop.Panels.StockForecast.OrderScreen;
+
+/// <summary>
+/// Interaction logic for StockForecastOrderJobScreen.xaml
+/// </summary>
+public partial class StockForecastOrderJobScreen : Window
+{
+    public List<StockForecastOrderingJobItem> Items
+    {
+        get => Grid.Items;
+        set
+        {
+            Grid.Items = value;
+            Grid.Refresh(false, true);
+        }
+    }
+
+    public StockForecastOrderJobScreen()
+    {
+        InitializeComponent();
+
+        Grid.Refresh(true, false);
+    }
+
+    private void CancelButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = false;
+        Close();
+    }
+
+    private void OKButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = true;
+        Close();
+    }
+}

+ 233 - 51
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingGrid.cs

@@ -22,9 +22,27 @@ using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using Columns = InABox.Core.Columns;
+using PRSDesktop.Panels.StockForecast.OrderScreen;
 
 namespace PRSDesktop;
 
+public class StockForecastBreakupKey(Guid jobID, Guid requiID)
+{
+    public Guid JobID { get; set; } = jobID != Guid.Empty ? jobID : throw new ArgumentException("jobID cannot be Guid.Empty!", "jobID");
+
+    public Guid JobRequiItemID { get; set; } = requiID;
+
+    public override bool Equals(object? obj)
+    {
+        return obj is StockForecastBreakupKey key && key.JobID == JobID && key.JobRequiItemID == JobRequiItemID;
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(JobID, JobRequiItemID);
+    }
+}
+
 public class StockForecastOrderData(ProductLink product, ProductStyleLink style, StockDimensions dimensions)
 {
     public ProductLink Product { get; set; } = product;
@@ -66,17 +84,19 @@ public class StockForecastOrderingItemQuantity
 {
     public event Action? Changed;
 
-    private double _total;
-    public double Total
+    private double _stockTotal;
+    public double StockTotal
     {
-        get => _total;
+        get => _stockTotal;
         set
         {
-            _total = value;
+            _stockTotal = value;
             Changed?.Invoke();
         }
     }
 
+    public Dictionary<StockForecastBreakupKey, double> JobTotals { get; init; } = [];
+
     private SupplierProduct? _supplierProduct;
     /// <summary>
     /// Indicates the Supplier Product that has been selected for this cell. This comes from the combobox column.
@@ -95,6 +115,12 @@ public class StockForecastOrderingItemQuantity
     {
         Changed?.Invoke();
     }
+
+    public double JobTotal => JobTotals.Values.Sum();
+
+    public double GetTotal(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
+        ? StockTotal
+        : JobTotal;
 }
 
 public class StockForecastOrderingItem : BaseObject
@@ -118,6 +144,16 @@ public class StockForecastOrderingItem : BaseObject
     [EnumLookupEditor(typeof(SupplierProductOrderStrategy))]
     public SupplierProductOrderStrategy OrderStrategy { get; set; }
 
+    private Dictionary<StockForecastBreakupKey, double> JobRequiredQuantities { get; set; } = [];
+    public Dictionary<StockForecastBreakupKey, double> GetJobRequiredQuantities()
+    {
+        return JobRequiredQuantities;
+    }
+    public void SetJobRequiredQuantity(Guid jobID, Guid requiID, double requiredQuantity)
+    {
+        JobRequiredQuantities[new(jobID, requiID)] = requiredQuantity;
+    }
+
     public bool CustomStrategy { get; set; } = false;
 
     public StockForecastOrderingItemQuantity[] Quantities = [];
@@ -125,8 +161,8 @@ public class StockForecastOrderingItem : BaseObject
     public StockForecastOrderingItemQuantity GetQuantity(int i) => Quantities[i];
 
     public double GetTotalQuantity(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
-        ? Quantities.Sum(x => x.Total)
-        : Quantities.Sum(x => x.Total);
+        ? Quantities.Sum(x => x.StockTotal)
+        : Quantities.Sum(x => x.JobTotal);
 
     public void SetQuantities(StockForecastOrderingItemQuantity[] quantities)
     {
@@ -175,7 +211,8 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
     private DynamicActionColumn[] QuantityColumns = [];
     private DynamicActionColumn[] CostColumns = [];
 
-    //private readonly Dictionary<Guid, Job> JobDetails = [];
+    private readonly Dictionary<Guid, Job> JobDetails = [];
+    private readonly Dictionary<Guid, JobRequisitionItem> JobRequiDetails = [];
 
     private static BitmapImage _warning = PRSDesktop.Resources.warning.AsBitmapImage();
 
@@ -192,6 +229,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 {
                     CalculateQuantities(true);
                     UIComponent.UpdateOrderType(OrderType);
+                    foreach(var control in QuantityControls)
+                    {
+                        control.UpdateControl(OrderType);
+                    }
 
                     Refresh(true, true);
                 }
@@ -248,16 +289,22 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                         continue;
                     }
 
-                    if(qty.Total > 0)
+                    if(OrderType == StockForecastOrderingType.StockOrder && qty.StockTotal > 0)
                     {
-                        if(OrderType == StockForecastOrderingType.StockOrder && qty.Total > 0)
-                        {
-                            yield return new(supplier, new List<StockForecastOrderData.QuantityBreakup>() , item, qty.Total, qty.SupplierProduct);
-                        }
-                        else
+                        yield return new(supplier, new(), item, qty.StockTotal, qty.SupplierProduct);
+                    }
+                    else
+                    {
+                        var breakups = new List<StockForecastOrderData.QuantityBreakup>();
+                        foreach(var (key, q) in qty.JobTotals)
                         {
-                            yield return new(supplier, item.Breakups, item, qty.Total, qty.SupplierProduct);
+                            // Check JobID because we are to skip the empty job (this is just the difference between all the allocations and the quantity on the PO).
+                            if(q > 0 && key.JobID != Guid.Empty)
+                            {
+                                breakups.Add(new(key.JobID, key.JobRequiItemID, "", q));
+                            }
                         }
+                        yield return new(supplier, breakups, item, qty.JobTotal, qty.SupplierProduct);
                     }
                 }
             }
@@ -394,8 +441,6 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         Suppliers = SupplierProducts.Select(x => x.SupplierLink).DistinctBy(x => x.ID).ToArray();
 
-        //LoadJobData(OrderData.SelectMany(x => x.GetRequiredQuantities().Keys).Distinct().Where(x => x != Guid.Empty));
-
         CalculateQuantities(true);
 
         _loadedData = true;
@@ -415,13 +460,19 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         return qty;
     }
 
-    private SupplierProduct CalculateSupplierProduct(StockForecastOrderingItem item, int supplierIdx)
+    private SupplierProduct? CalculateSupplierProduct(StockForecastOrderingItem item, int supplierIdx)
     {
         var supplierProduct = SelectSupplierProduct(SupplierProducts.Where(x => x.Product.ID == item.Product.ID && x.Style.ID == item.Style.ID && x.SupplierLink.ID == Suppliers[supplierIdx].ID), item);
 
         var qty = item.GetQuantity(supplierIdx);
         qty.SupplierProduct = supplierProduct;
-        qty.Total = 0;
+        qty.StockTotal = 0;
+        qty.JobTotals.Clear();
+        foreach(var id in item.GetJobRequiredQuantities().Keys)
+        {
+            qty.JobTotals[id] = 0;
+        }
+
         return supplierProduct;
     }
 
@@ -444,7 +495,19 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 .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);
+                var qty = item.GetQuantity(supplierIdx);
+                if(OrderType == StockForecastOrderingType.StockOrder)
+                {
+                    qty.StockTotal = GetRequiredQuantity(item, selectedSupplierProduct, item.RequiredQuantity);
+                }
+                else
+                {
+                    qty.JobTotals.Clear();
+                    foreach(var (id, q) in item.GetJobRequiredQuantities())
+                    {
+                        qty.JobTotals[id] = GetRequiredQuantity(item, selectedSupplierProduct, q);
+                    }
+                }
             }
         }
     }
@@ -463,18 +526,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 item.Style.CopyFrom(dataItem.Style);
                 item.Dimensions.CopyFrom(dataItem.Dimensions);
                 item.OrderStrategy = ForecastOrderStrategyToProductOrderStrategy(OrderStrategy, item.Product.OrderStrategy);
-
-                if(OrderType == StockForecastOrderingType.StockOrder)
+                item.RequiredQuantity = dataItem.RequiredQuantity;
+                foreach(var breakup in dataItem.GetRequiredQuantities())
                 {
-                    item.RequiredQuantity = dataItem.RequiredQuantity;
-                }
-                else
-                {
-                    foreach(var breakup in dataItem.GetRequiredQuantities())
-                    {
-                        item.RequiredQuantity += breakup.Quantity;
-                        item.Breakups.Add(breakup);
-                    }
+                    item.SetJobRequiredQuantity(breakup.JobID, breakup.JobRequiItemID, breakup.Quantity);
                 }
                 Items.Add(item);
             }
@@ -536,18 +591,18 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         }
     }
 
-    private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct)
+    private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct, double requiredQuantity)
     {
         switch (item.OrderStrategy)
         {
             case SupplierProductOrderStrategy.Exact:
-                return item.RequiredQuantity;
+                return requiredQuantity;
             case SupplierProductOrderStrategy.RoundUp:
-                return Math.Ceiling(item.RequiredQuantity);
+                return Math.Ceiling(requiredQuantity);
             case SupplierProductOrderStrategy.LowestOverallPrice:
             case SupplierProductOrderStrategy.LowestUnitPrice:
             case SupplierProductOrderStrategy.LowestOverstock:
-                return Math.Ceiling(item.RequiredQuantity * item.Dimensions.Value / (supplierProduct.Dimensions.Value.IsEffectivelyEqual(0.0) ? (item.Dimensions.Value.IsEffectivelyEqual(0.0) ? 1.0 : item.Dimensions.Value) : supplierProduct.Dimensions.Value));
+                return Math.Ceiling(requiredQuantity * item.Dimensions.Value / (supplierProduct.Dimensions.Value.IsEffectivelyEqual(0.0) ? (item.Dimensions.Value.IsEffectivelyEqual(0.0) ? 1.0 : item.Dimensions.Value) : supplierProduct.Dimensions.Value));
             default:
                 return 0.0;
         }
@@ -684,7 +739,17 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     var newQty = CreateQuantity(itemIdx);
 
                     quantities[newIdx] = newQty;
-                    newQty.Total = 0;
+                    if(OrderType == StockForecastOrderingType.StockOrder)
+                    {
+                        newQty.StockTotal = 0;
+                    }
+                    else
+                    {
+                        foreach(var id in item.GetJobRequiredQuantities().Keys)
+                        {
+                            newQty.JobTotals[id] = 0;
+                        }
+                    }
                     item.SetQuantities(quantities);
                 }
 
@@ -718,6 +783,39 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         }
     }
 
+    private void LoadJobData(IEnumerable<Guid> ids)
+    {
+        var neededIDs = ids.Where(x => x != Guid.Empty && !JobDetails.ContainsKey(x)).ToArray();
+        if(neededIDs.Length > 0)
+        {
+            var details = Client.Query(
+                new Filter<Job>(x => x.ID).InList(neededIDs),
+                Columns.None<Job>().Add(x => x.ID)
+                    .Add(x => x.JobNumber)
+                    .Add(x => x.Name));
+            foreach(var job in details.ToObjects<Job>())
+            {
+                JobDetails[job.ID] = job;
+            }
+        }
+    }
+    private void LoadJobRequiData(IEnumerable<Guid> ids)
+    {
+        var neededIDs = ids.Where(x => x != Guid.Empty && !JobRequiDetails.ContainsKey(x)).ToArray();
+        if(neededIDs.Length > 0)
+        {
+            var details = Client.Query(
+                new Filter<JobRequisitionItem>(x => x.ID).InList(neededIDs),
+                Columns.None<JobRequisitionItem>().Add(x => x.ID)
+                    .Add(x => x.Requisition.Number)
+                    .Add(x => x.Requisition.Description));
+            foreach(var requi in details.ToObjects<JobRequisitionItem>())
+            {
+                JobRequiDetails[requi.ID] = requi;
+            }
+        }
+    }
+
     private class QuantityControl : ContentControl
     {
         private readonly StockForecastOrderingItem Item;
@@ -742,21 +840,105 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 return;
             }
 
-            // Otherwise, simple quantity textbox editor.
-            var editor = new DoubleTextBox
+            if(mode == StockForecastOrderingType.StockOrder)
             {
-                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) =>
+                // Otherwise, simple quantity textbox editor.
+                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.Breakup)
             {
-                Item.GetQuantity(SupplierIndex).Total = editor.Value ?? default;
-            };
-            Content = editor;
+                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.Select(x => x.JobID));
+                    Parent.LoadJobRequiData(qty.JobTotals.Keys.Select(x => x.JobRequiItemID));
+
+                    var items = qty.JobTotals.Select(x =>
+                    {
+                        var item = new StockForecastOrderingJobItem
+                        {
+                            JobID = x.Key.JobID,
+                            JobRequiID = x.Key.JobRequiItemID,
+                            RequiredQuantity = Item.GetJobRequiredQuantities().GetValueOrDefault(x.Key),
+                            Quantity = x.Value
+                        };
+                        if(item.JobID == Guid.Empty)
+                        {
+                            item.Description = "General Stock";
+                        }
+                        else if(Parent.JobDetails.TryGetValue(item.JobID, out var job))
+                        {
+                            if(Parent.JobRequiDetails.TryGetValue(item.JobRequiID, out var requi))
+                            {
+                                item.Description = $"{job.JobNumber}: Requi #{requi.Requisition.Number} ({requi.Requisition.Description})";
+                            }
+                            else
+                            {
+                                item.Description = $"{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[new(item.JobID, item.JobRequiID)] = item.Quantity;
+                        }
+                        qty.DoChanged();
+                        editor.Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).JobTotal);
+                    }
+                };
+                grid.Children.Add(btn);
+
+                Content = grid;
+            }
         }
     }
 
@@ -835,10 +1017,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             }
 
             var instance = LoadItem(row);
-            var qty = instance.GetQuantity(idx);//.Total;
+            var qty = instance.GetQuantity(idx);
             if(qty.SupplierProduct is not null)
             {
-                return $"{qty.Total * qty.SupplierProduct.CostPrice:C2}";
+                return $"{qty.GetTotal(OrderType) * qty.SupplierProduct.CostPrice:C2}";
             }
             else
             {
@@ -919,7 +1101,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     var qty = item.GetQuantity(SupplierIndex);
                     if(qty.SupplierProduct is not null)
                     {
-                        Sum += qty.Total * qty.SupplierProduct.CostPrice;
+                        Sum += qty.GetTotal(Grid.OrderType) * qty.SupplierProduct.CostPrice;
                     }
                 }
             }

+ 86 - 0
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingJobGrid.cs

@@ -0,0 +1,86 @@
+using Comal.Classes;
+using InABox.Core;
+using InABox.DynamicGrid;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace PRSDesktop.Panels.StockForecast.OrderScreen;
+
+public class StockForecastOrderingJobItem : BaseObject
+{
+    [EditorSequence(1)]
+    public string Description { get; set; }
+
+    [NullEditor]
+    public Guid JobID { get; set; }
+
+    [NullEditor]
+    public Guid JobRequiID { get; set; }
+
+    [EditorSequence(2)]
+    [DoubleEditor]
+    public double RequiredQuantity { get; set; }
+
+    [EditorSequence(2)]
+    [DoubleEditor]
+    public double Quantity { get; set; }
+}
+
+
+public class StockForecastOrderingJobGrid : DynamicItemsListGrid<StockForecastOrderingJobItem>
+{
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+
+        options.Clear();
+        options.DirectEdit = true;
+    }
+
+    private class UIComponent : DynamicGridGridUIComponent<StockForecastOrderingJobItem>
+    {
+        public StockForecastOrderingJobGrid Grid { get; set; }
+
+        public UIComponent(StockForecastOrderingJobGrid grid)
+        {
+            Grid = grid;
+            Parent = grid;
+        }
+
+        protected override Brush? GetCellBackground(CoreRow row, DynamicColumnBase column)
+        {
+            return base.GetCellBackground(row, column);
+        }
+    }
+    protected override IDynamicGridUIComponent<StockForecastOrderingJobItem> CreateUIComponent()
+    {
+        return new UIComponent(this);
+    }
+
+    protected override DynamicGridColumns LoadColumns()
+    {
+        var columns = new DynamicGridColumns();
+        columns.Add<StockForecastOrderingJobItem, string>(x => x.Description, 0, "Description", "", Alignment.MiddleLeft);
+        columns.Add<StockForecastOrderingJobItem, double>(x => x.RequiredQuantity, 70, "Req. Qty.", "F2", Alignment.MiddleCenter);
+        columns.Add<StockForecastOrderingJobItem, double>(x => x.Quantity, 70, "Qty.", "F2", Alignment.MiddleCenter);
+        return columns;
+    }
+
+    protected override void CustomiseEditor(StockForecastOrderingJobItem[] items, DynamicGridColumn column, BaseEditor editor)
+    {
+        base.CustomiseEditor(items, column, editor);
+
+        if(new Column<StockForecastOrderingJobItem>(x => x.Quantity).IsEqualTo(column.ColumnName))
+        {
+            column.Editor.Editable = Editable.Enabled;
+        }
+        else
+        {
+            column.Editor.Editable = Editable.Disabled;
+        }
+    }
+}