Sfoglia il codice sorgente

Added code for working out exact and round up strategies

Kenric Nugteren 10 mesi fa
parent
commit
112da888c3

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

@@ -6,7 +6,13 @@ namespace Comal.Classes
 {
     public enum SupplierProductOrderStrategy
     {
+        /// <summary>
+        /// Find the supplier product that has exactly the right dimensions.
+        /// </summary>
         Exact,
+        /// <summary>
+        /// Same as <see cref="SupplierProductOrderStrategy.Exact"/>, but round up the quantity.
+        /// </summary>
         RoundUp,
         LowestUnitPrice,
         LowestOverallPrice,

+ 14 - 0
prs.desktop/Panels/Products/Master List/ProductSuppliersControl.cs

@@ -44,6 +44,20 @@ namespace PRSDesktop
                 editor.CurrencySymbol = " "; // Non-empty, should blnak out "$" symbol
         }
 
+        public override DynamicGridColumns GenerateColumns()
+        {
+            var columns = new DynamicGridColumns();
+
+            columns.Add<SupplierProduct, string>(x => x.SupplierLink.Code, 120, "Supplier Code", "", Alignment.MiddleCenter);
+            columns.Add<SupplierProduct, string>(x => x.SupplierLink.Name, 0, "Supplier", "", Alignment.MiddleLeft);
+            columns.Add<SupplierProduct, string>(x => x.Dimensions.UnitSize, 120, "Size", "", Alignment.MiddleCenter);
+            columns.Add<SupplierProduct, string>(x => x.Style.Code, 120, "Style", "", Alignment.MiddleCenter);
+            columns.Add<SupplierProduct, string>(x => x.Job.JobNumber, 80, "Job", "", Alignment.MiddleCenter);
+            columns.Add<SupplierProduct, double>(x => x.CostPrice, 80, "Cost Price", "C2", Alignment.MiddleCenter);
+
+            return columns;
+        }
+
         public Product Product { get; set; }
 
         private bool SetDefaultClick(Button arg1, CoreRow[] arg2)

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

@@ -1,26 +0,0 @@
-<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>

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

@@ -1,50 +0,0 @@
-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();
-    }
-}

+ 1 - 0
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderScreen.xaml

@@ -25,6 +25,7 @@
                 <ComboBoxItem Content="Job Order" Tag="{x:Static local:StockForecastOrderingType.JobOrder}"/>
                 <ComboBoxItem Content="Stock Order" Tag="{x:Static local:StockForecastOrderingType.StockOrder}"/>
             </ComboBox>
+            <ComboBox x:Name="OrderStrategyBox" DockPanel.Dock="Left" Margin="0,5,5,5"/>
             <Button x:Name="CancelButton" Click="CancelButton_Click"
                     Content="Cancel"
                     Margin="5" Padding="5" MinWidth="60"

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

@@ -1,4 +1,7 @@
 using Comal.Classes;
+using InABox.Core;
+using InABox.Wpf;
+using InABox.WPF;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -39,12 +42,25 @@ public partial class StockForecastOrderScreen : Window, INotifyPropertyChanged
         set => Grid.OrderType = value;
     }
 
+    public StockForecastOrderingStrategy Strategy
+    {
+        get => Grid.OrderStrategy;
+        set => Grid.OrderStrategy = value;
+    }
+
     public IEnumerable<StockForecastOrderingResult> Results => Grid.Results;
 
     public StockForecastOrderScreen(List<StockForecastOrderData> items)
     {
         InitializeComponent();
 
+        OrderStrategyBox.ItemsSource = Enum.GetValues<StockForecastOrderingStrategy>()
+            .Select(x => new KeyValuePair<StockForecastOrderingStrategy, string>(x, CoreUtils.Neatify(x.ToString())));
+        OrderStrategyBox.SelectedValuePath = "Key";
+        OrderStrategyBox.DisplayMemberPath = "Value";
+        OrderStrategyBox.VerticalContentAlignment = VerticalAlignment.Center;
+        OrderStrategyBox.Bind(ComboBox.SelectedValueProperty, this, x => x.Strategy);
+
         Grid.OrderData = items;
         Grid.Refresh(true, true);
     }

+ 152 - 62
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingGrid.cs

@@ -146,6 +146,16 @@ public class StockForecastOrderingResult
     }
 }
 
+public enum StockForecastOrderingStrategy
+{
+    PerProduct,
+    Exact,
+    RoundUp,
+    LowestUnitPrice,
+    LowestOverallPrice,
+    LowestOverstock
+}
+
 public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrderingItem>, ISpecificGrid
 {
     private List<SupplierProduct> SupplierProducts = [];
@@ -173,13 +183,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             {
                 _orderType = value;
 
-                CalculateQuantities();
-
-                foreach(var control in QuantityControls)
-                {
-                    control.UpdateControl(OrderType);
-                }
-
+                CalculateQuantities(true);
                 UIComponent.UpdateOrderType(OrderType);
 
                 Refresh(true, true);
@@ -187,6 +191,32 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         }
     }
 
+    private StockForecastOrderingStrategy orderStrategy;
+    public StockForecastOrderingStrategy OrderStrategy
+    {
+        get => orderStrategy;
+        set
+        {
+            orderStrategy = value;
+
+            foreach(var item in Items)
+            {
+                item.OrderStrategy = value switch
+                {
+                    StockForecastOrderingStrategy.Exact => SupplierProductOrderStrategy.Exact,
+                    StockForecastOrderingStrategy.LowestOverallPrice => SupplierProductOrderStrategy.LowestOverallPrice,
+                    StockForecastOrderingStrategy.LowestUnitPrice => SupplierProductOrderStrategy.LowestUnitPrice,
+                    StockForecastOrderingStrategy.LowestOverstock => SupplierProductOrderStrategy.LowestOverstock,
+                    StockForecastOrderingStrategy.RoundUp => SupplierProductOrderStrategy.RoundUp,
+                    StockForecastOrderingStrategy.PerProduct or _ => item.Product.OrderStrategy
+                };
+            }
+
+            CalculateQuantities(false);
+            Refresh(false, true);
+        }
+    }
+
     public IEnumerable<StockForecastOrderingResult> Results
     {
         get
@@ -316,6 +346,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             .Add(x => x.SupplierLink.ID)
             .Add(x => x.Product.ID)
             .Add(x => x.Style.ID)
+            .Add(x => x.Style.Code)
             .Add(x => x.ForeignCurrencyPrice)
             .Add(x => x.CostPrice)
             .AddDimensionsColumns(x => x.Dimensions)
@@ -332,7 +363,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         LoadJobData(OrderData.SelectMany(x => x.GetJobRequiredQuantities().Keys).Distinct().Where(x => x != Guid.Empty));
 
-        CalculateQuantities();
+        CalculateQuantities(true);
 
         _loadedData = true;
     }
@@ -351,42 +382,74 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         return qty;
     }
 
-    private void CalculateQuantities()
+    private void CalculateSupplierProduct(StockForecastOrderingItem item)
     {
-        SetObserving(false);
+        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;
+
+            if(supplierProduct is not null)
+            {
+                selectedSupplierProducts.Add(supplierProduct);
+            }
+        }
 
-        Items.Clear();
-        foreach(var dataItem in OrderData)
+        var selectedSupplierProduct = SelectSupplierProduct(selectedSupplierProducts, item);
+        if(selectedSupplierProduct is not null)
         {
-            if(OrderType == StockForecastOrderingType.StockOrder)
+            var supplierIdx = Suppliers.WithIndex()
+                .FirstOrDefault(x => x.Value.ID == selectedSupplierProduct.SupplierLink.ID, new KeyValuePair<int, SupplierLink>(-1, null)).Key;
+            if(supplierIdx != -1)
             {
-                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);
+                item.GetQuantity(supplierIdx).Total = GetRequiredQuantity(item, selectedSupplierProduct);
             }
-            else
+        }
+    }
+
+    private void CalculateQuantities(bool recreateItems)
+    {
+        SetObserving(false);
+
+        if (recreateItems)
+        {
+            Items.Clear();
+            foreach(var dataItem in OrderData)
             {
-                foreach(var (id, q) in dataItem.GetJobRequiredQuantities())
+                if(OrderType == StockForecastOrderingType.StockOrder)
                 {
                     var item = new StockForecastOrderingItem();
                     item.Product.CopyFrom(dataItem.Product);
                     item.Style.CopyFrom(dataItem.Style);
                     item.Dimensions.CopyFrom(dataItem.Dimensions);
-                    item.Job.ID = id;
-
-                    if(id != Guid.Empty)
+                    item.RequiredQuantity = dataItem.RequiredQuantity;
+                    item.OrderStrategy = item.Product.OrderStrategy;
+                    Items.Add(item);
+                }
+                else
+                {
+                    foreach(var (id, q) in dataItem.GetJobRequiredQuantities())
                     {
-                        item.Job.CopyFrom(JobDetails[id]);
-                    }
+                        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;
+                        if(id != Guid.Empty)
+                        {
+                            item.Job.CopyFrom(JobDetails[id]);
+                        }
 
-                    Items.Add(item);
+                        item.RequiredQuantity = q;
+                        item.OrderStrategy = item.Product.OrderStrategy;
+
+                        Items.Add(item);
+                    }
                 }
             }
         }
@@ -404,31 +467,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         foreach(var item in Items)
         {
-            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;
-
-                if(supplierProduct is not null)
-                {
-                    selectedSupplierProducts.Add(supplierProduct);
-                }
-            }
-
-            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)
-                {
-                    item.GetQuantity(supplierIdx).Total = GetRequiredQuantity(item, selectedSupplierProduct);
-                }
-            }
+            CalculateSupplierProduct(item);
         }
         SetObserving(true);
         DoChanged();
@@ -436,12 +475,30 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
     private SupplierProduct? SelectSupplierProduct(IEnumerable<SupplierProduct> supplierProducts, StockForecastOrderingItem item)
     {
-        return null;
+        switch (item.OrderStrategy)
+        {
+            case SupplierProductOrderStrategy.Exact:
+            case SupplierProductOrderStrategy.RoundUp:
+                // First, find the cheapest in the right style and dimensions.
+                return supplierProducts.Where(x => x.Dimensions.Equals(item.Dimensions) && x.Style.ID == item.Style.ID).MinBy(x => x.CostPrice)
+                // Otherwise, find the cheapest in the right dimensions.
+                    ?? supplierProducts.Where(x => x.Dimensions.Equals(item.Dimensions)).MinBy(x => x.CostPrice);
+            default:
+                return null;
+        }
     }
 
     private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct)
     {
-        return 0;
+        switch (item.OrderStrategy)
+        {
+            case SupplierProductOrderStrategy.Exact:
+                return item.RequiredQuantity;
+            case SupplierProductOrderStrategy.RoundUp:
+                return Math.Ceiling(item.RequiredQuantity);
+            default:
+                return 0.0;
+        }
     }
 
     private bool _loadedColumns = false;
@@ -472,7 +529,23 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
             ActionColumns.Add(new DynamicTemplateColumn(row =>
             {
-                return null;
+                var item = LoadItem(row);
+
+                var box = new ComboBox();
+                box.ItemsSource = Enum.GetValues<SupplierProductOrderStrategy>()
+                    .Select(x => new KeyValuePair<SupplierProductOrderStrategy, string>(x, CoreUtils.Neatify(x.ToString())));
+                box.DisplayMemberPath = "Value";
+                box.SelectedValuePath = "Key";
+                box.SelectedValue = item.OrderStrategy;
+                box.SelectionChanged += (o, e) =>
+                {
+                    item.OrderStrategy = (SupplierProductOrderStrategy)box.SelectedValue;
+                    CalculateSupplierProduct(item);
+                    InvalidateRow(row);
+                };
+                box.Margin = new Thickness(2);
+                box.VerticalContentAlignment = VerticalAlignment.Center;
+                return box;
             })
             {
                 HeaderText = "Order Strategy.",
@@ -589,7 +662,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
     {
         for(int idx = 0; idx < Suppliers.Length; ++idx)
         {
-            GetColumnGrouping().AddGroup(Suppliers[idx].Code, QuantityColumns[idx], CostColumns[idx]);
+            GetColumnGrouping().AddGroup(Suppliers[idx].Code, SupplierProductColumns[idx], CostColumns[idx]);
         }
     }
 
@@ -656,8 +729,6 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
     private void InitialiseSupplierColumn(int idx)
     {
-        var supplierProducts = SupplierProducts.Where(x => x.SupplierLink.ID == Suppliers[idx].ID).ToArray();
-
         var contextMenuFunc = (CoreRow[]? rows) =>
         {
             var row = rows?.FirstOrDefault();
@@ -678,7 +749,26 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
         var qtyColumn = new Tuple<DynamicActionColumn, QuantityControl?>(null!, null);
         SupplierProductColumns[idx] = new DynamicTemplateColumn(row =>
         {
-            return null;
+            var instance = LoadItem(row);
+
+            var comboBox = new ComboBox();
+
+            comboBox.ItemsSource =
+                SupplierProducts.Where(x => x.SupplierLink.ID == Suppliers[idx].ID && x.Product.ID == instance.Product.ID)
+                .Select(x => new KeyValuePair<SupplierProduct?, string>(
+                    x,
+                    x.Style.ID != Guid.Empty ? $"{x.Dimensions.UnitSize}/{x.Style.Code}" : $"{x.Dimensions.UnitSize}"));
+            comboBox.SelectedValuePath = "Key";
+            comboBox.DisplayMemberPath = "Value";
+
+            var qty = instance.GetQuantity(idx);
+
+            comboBox.Bind(ComboBox.SelectedValueProperty, qty, x => x.SupplierProduct);
+
+            comboBox.VerticalContentAlignment = VerticalAlignment.Center;
+            comboBox.Margin = new Thickness(2);
+
+            return comboBox;
         })
         {
             HeaderText = "Supplier Product.",

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

@@ -1,83 +0,0 @@
-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 Job { get; set; }
-
-    [NullEditor]
-    public Guid JobID { 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.Job, 0, "Job", "", 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;
-        }
-    }
-}

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

@@ -114,6 +114,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
         HiddenColumns.Add(x => x.Product.Image.FileName);
         HiddenColumns.Add(x => x.Product.Supplier.ID);
         HiddenColumns.Add(x => x.Product.Supplier.SupplierLink.ID);
+        HiddenColumns.Add(x => x.Product.OrderStrategy);
 
         HiddenColumns.Add(x => x.Required);
         HiddenColumns.Add(x => x.Optimised);
@@ -892,10 +893,16 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
             var item = new StockForecastOrderData(forecastItem.Product, forecastItem.Style, forecastItem.Dimensions);
             item.RequiredQuantity = Optimise ? forecastItem.Optimised : forecastItem.Required;
 
-            item.SetJobRequiredQuantity(Guid.Empty, forecastItem.StockRequired);
+            if(forecastItem.StockRequired > 0)
+            {
+                item.SetJobRequiredQuantity(Guid.Empty, forecastItem.StockRequired);
+            }
             foreach(var (id, jobInfo) in forecastItem.JobInfo)
             {
-                item.SetJobRequiredQuantity(id, jobInfo.Required);
+                if (jobInfo.Required > 0)
+                {
+                    item.SetJobRequiredQuantity(id, jobInfo.Required);
+                }
             }
 
             items.Add(item);