|
|
@@ -148,6 +148,12 @@ public class StockOrderingItem : BaseObject
|
|
|
[EnumLookupEditor(typeof(SupplierProductOrderStrategy))]
|
|
|
public SupplierProductOrderStrategy OrderStrategy { get; set; }
|
|
|
|
|
|
+ public JobLink Job => InitializeField(ref _job, nameof(Job));
|
|
|
+ private JobLink? _job;
|
|
|
+
|
|
|
+ public JobRequisitionItemLink JRI => InitializeField(ref _jri, nameof(JRI));
|
|
|
+ private JobRequisitionItemLink? _jri;
|
|
|
+
|
|
|
private Dictionary<StockForecastBreakupKey, double> JobRequiredQuantities { get; set; } = new()
|
|
|
{
|
|
|
{ new(Guid.Empty, Guid.Empty), 0.0 }
|
|
|
@@ -210,6 +216,13 @@ public enum StockForecastOrderingStrategy
|
|
|
LowestOverstock
|
|
|
}
|
|
|
|
|
|
+public enum StockForecastCombineMode
|
|
|
+{
|
|
|
+ Combined,
|
|
|
+ SplitJobs,
|
|
|
+ SplitRequisitionItems
|
|
|
+}
|
|
|
+
|
|
|
public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>, ISpecificGrid
|
|
|
{
|
|
|
#region Internal Data + Caches
|
|
|
@@ -244,7 +257,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
if (OrderData != null)
|
|
|
{
|
|
|
CalculateQuantities(true);
|
|
|
- UIComponent.UpdateOrderType(OrderType);
|
|
|
+ UIComponent.Update();
|
|
|
foreach(var control in QuantityControls)
|
|
|
{
|
|
|
control.UpdateControl(OrderType);
|
|
|
@@ -276,6 +289,22 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private StockForecastCombineMode _combineMode = StockForecastCombineMode.Combined;
|
|
|
+ public StockForecastCombineMode CombineMode
|
|
|
+ {
|
|
|
+ get => _combineMode;
|
|
|
+ set
|
|
|
+ {
|
|
|
+ _combineMode = value;
|
|
|
+ if(OrderData != null)
|
|
|
+ {
|
|
|
+ CalculateQuantities(true);
|
|
|
+ UIComponent.Update();
|
|
|
+ Refresh(true, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public double TotalQuantity => Items.Sum(x => x.GetTotalQuantity(OrderType));
|
|
|
public IEnumerable<StockForecastOrderingResult> Results
|
|
|
{
|
|
|
@@ -361,12 +390,17 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
Parent = grid;
|
|
|
Grid = grid;
|
|
|
|
|
|
- UpdateOrderType(grid.OrderType);
|
|
|
+ Update();
|
|
|
}
|
|
|
|
|
|
- public void UpdateOrderType(StockForecastOrderingType type)
|
|
|
+ public void Update()
|
|
|
{
|
|
|
- DataGrid.FrozenColumnCount = 8;
|
|
|
+ DataGrid.FrozenColumnCount = 8 + (Grid.CombineMode switch
|
|
|
+ {
|
|
|
+ StockForecastCombineMode.SplitJobs => 1,
|
|
|
+ StockForecastCombineMode.SplitRequisitionItems => 3,
|
|
|
+ StockForecastCombineMode.Combined or _ => 0
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
protected override Brush? GetCellBackground(CoreRow row, DynamicColumnBase column)
|
|
|
@@ -467,20 +501,101 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
|
|
|
if (recreateItems)
|
|
|
{
|
|
|
- Items.Clear();
|
|
|
- foreach(var dataItem in OrderData)
|
|
|
+ StockOrderingItem CreateItem(StockForecastOrderData dataItem)
|
|
|
{
|
|
|
var item = new StockOrderingItem();
|
|
|
item.Product.CopyFrom(dataItem.Product);
|
|
|
item.Style.CopyFrom(dataItem.Style);
|
|
|
item.Dimensions.CopyFrom(dataItem.Dimensions);
|
|
|
item.OrderStrategy = CastOrderStrategyToProductOrderStrategy(OrderStrategy, item.Product.OrderStrategy);
|
|
|
- item.RequiredQuantity = dataItem.RequiredQuantity;
|
|
|
- foreach(var breakup in dataItem.GetRequiredQuantities())
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+
|
|
|
+ Items.Clear();
|
|
|
+ foreach(var dataItem in OrderData)
|
|
|
+ {
|
|
|
+ if(CombineMode == StockForecastCombineMode.Combined)
|
|
|
{
|
|
|
- item.SetJobRequiredQuantity(breakup.JobID, breakup.JobRequiItemID, breakup.Quantity);
|
|
|
+ var item = CreateItem(dataItem);
|
|
|
+ item.RequiredQuantity = dataItem.RequiredQuantity;
|
|
|
+ foreach(var breakup in dataItem.GetRequiredQuantities())
|
|
|
+ {
|
|
|
+ item.SetJobRequiredQuantity(breakup.JobID, breakup.JobRequiItemID, breakup.Quantity);
|
|
|
+ }
|
|
|
+ Items.Add(item);
|
|
|
+ }
|
|
|
+ else if(CombineMode == StockForecastCombineMode.SplitJobs)
|
|
|
+ {
|
|
|
+ var jobItems = new Dictionary<Guid, StockOrderingItem>();
|
|
|
+ foreach(var breakup in dataItem.GetRequiredQuantities())
|
|
|
+ {
|
|
|
+ if(!jobItems.TryGetValue(breakup.JobID, out var item))
|
|
|
+ {
|
|
|
+ item = CreateItem(dataItem);
|
|
|
+ item.Job.ID = breakup.JobID;
|
|
|
+ Items.Add(item);
|
|
|
+ jobItems.Add(breakup.JobID, item);
|
|
|
+ }
|
|
|
+ item.RequiredQuantity += breakup.Quantity;
|
|
|
+ item.SetJobRequiredQuantity(breakup.JobID, breakup.JobRequiItemID, breakup.Quantity);
|
|
|
+ }
|
|
|
+ LoadJobData(jobItems.Keys);
|
|
|
+ foreach(var item in Items)
|
|
|
+ {
|
|
|
+ if(JobDetails.TryGetValue(item.Job.ID, out var jobDetails))
|
|
|
+ {
|
|
|
+ item.Job.Synchronise(jobDetails);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Items.Sort((a, b) =>
|
|
|
+ {
|
|
|
+ var ret = a.Product.Code.CompareTo(b.Product.Code);
|
|
|
+ if (ret == 0)
|
|
|
+ {
|
|
|
+ ret = a.Job.JobNumber.CompareTo(b.Job.JobNumber);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ foreach(var breakup in dataItem.GetRequiredQuantities())
|
|
|
+ {
|
|
|
+ var item = CreateItem(dataItem);
|
|
|
+ item.RequiredQuantity = breakup.Quantity;
|
|
|
+ item.Job.ID = breakup.JobID;
|
|
|
+ item.JRI.ID = breakup.JobRequiItemID;
|
|
|
+ item.SetJobRequiredQuantity(breakup.JobID, breakup.JobRequiItemID, breakup.Quantity);
|
|
|
+ Items.Add(item);
|
|
|
+ }
|
|
|
+ LoadJobData(Items.Select(x => x.Job.ID));
|
|
|
+ LoadJobRequiData(Items.Select(x => x.JRI.ID));
|
|
|
+ foreach(var item in Items)
|
|
|
+ {
|
|
|
+ if(JobDetails.TryGetValue(item.Job.ID, out var jobDetails))
|
|
|
+ {
|
|
|
+ item.Job.Synchronise(jobDetails);
|
|
|
+ }
|
|
|
+ if(JobRequiDetails.TryGetValue(item.JRI.ID, out var jriDetails))
|
|
|
+ {
|
|
|
+ item.JRI.Synchronise(jriDetails);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Items.Sort((a, b) =>
|
|
|
+ {
|
|
|
+ var ret = a.Product.Code.CompareTo(b.Product.Code);
|
|
|
+ if (ret == 0)
|
|
|
+ {
|
|
|
+ ret = a.Job.JobNumber.CompareTo(b.Job.JobNumber);
|
|
|
+ }
|
|
|
+ if (ret == 0)
|
|
|
+ {
|
|
|
+ ret = a.JRI.Requisition.Number.CompareTo(b.JRI.Requisition.Number);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ });
|
|
|
}
|
|
|
- Items.Add(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -507,12 +622,22 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
|
|
|
#region Order Strategy
|
|
|
|
|
|
- private SupplierProduct? CalculateSupplierProduct(StockOrderingItem item, int supplierIdx)
|
|
|
+ private IEnumerable<SupplierProduct> AvailableSupplierProducts(StockOrderingItem item, int supplierIdx)
|
|
|
{
|
|
|
var supplierProducts = string.IsNullOrWhiteSpace(item.Dimensions.Unit.Conversion)
|
|
|
? SupplierProducts.Where(x => x.Dimensions.Equals(item.Dimensions))
|
|
|
: SupplierProducts;
|
|
|
- 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);
|
|
|
+ supplierProducts = supplierProducts.Where(x => x.Product.ID == item.Product.ID && x.Style.ID == item.Style.ID && x.SupplierLink.ID == Suppliers[supplierIdx].ID);
|
|
|
+ if(CombineMode == StockForecastCombineMode.SplitJobs || CombineMode == StockForecastCombineMode.SplitRequisitionItems)
|
|
|
+ {
|
|
|
+ supplierProducts = supplierProducts.Where(x => x.Job.ID == item.Job.ID || x.Job.ID == Guid.Empty);
|
|
|
+ }
|
|
|
+ return supplierProducts;
|
|
|
+ }
|
|
|
+
|
|
|
+ private SupplierProduct? CalculateSupplierProduct(StockOrderingItem item, int supplierIdx)
|
|
|
+ {
|
|
|
+ var supplierProduct = SelectSupplierProduct(AvailableSupplierProducts(item, supplierIdx), item);
|
|
|
|
|
|
var qty = item.GetQuantity(supplierIdx);
|
|
|
qty.SupplierProduct = supplierProduct;
|
|
|
@@ -646,6 +771,15 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
var columns = new DynamicGridColumns();
|
|
|
columns.Add<StockOrderingItem, string>(x => x.Product.Code, 120, "Product Code", "", Alignment.MiddleCenter);
|
|
|
columns.Add<StockOrderingItem, string>(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleLeft);
|
|
|
+ if(CombineMode == StockForecastCombineMode.SplitJobs || CombineMode == StockForecastCombineMode.SplitRequisitionItems)
|
|
|
+ {
|
|
|
+ columns.Add<StockOrderingItem, string>(x => x.Job.JobNumber, width: 70);
|
|
|
+ }
|
|
|
+ if(CombineMode == StockForecastCombineMode.SplitRequisitionItems)
|
|
|
+ {
|
|
|
+ columns.Add<StockOrderingItem, int>(x => x.JRI.Requisition.Number, width: 50, alignment: Alignment.MiddleCenter);
|
|
|
+ columns.Add<StockOrderingItem, string>(x => x.JRI.Requisition.Description, width: 200);
|
|
|
+ }
|
|
|
columns.Add<StockOrderingItem, string>(x => x.Dimensions.UnitSize, 80, "Size", "", Alignment.MiddleCenter);
|
|
|
columns.Add<StockOrderingItem, string>(x => x.Style.Code, 80, "Style", "", Alignment.MiddleCenter);
|
|
|
columns.Add<StockOrderingItem, double>(x => x.RequiredQuantity, 80, "Required", "", Alignment.MiddleCenter);
|
|
|
@@ -900,11 +1034,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
var comboBox = new ComboBox();
|
|
|
comboBox.Tag = idx;
|
|
|
|
|
|
- var supplierProducts = string.IsNullOrWhiteSpace(instance.Dimensions.Unit.Conversion)
|
|
|
- ? SupplierProducts.Where(x => x.Dimensions.Equals(instance.Dimensions))
|
|
|
- : SupplierProducts;
|
|
|
-
|
|
|
- var items = supplierProducts.Where(x => x.SupplierLink.ID == Suppliers[idx].ID && x.Product.ID == instance.Product.ID && x.Style.ID == instance.Style.ID)
|
|
|
+ var items = AvailableSupplierProducts(instance, idx)
|
|
|
.Select(x => new KeyValuePair<SupplierProduct?, string>(x, x.Job.ID == Guid.Empty ? x.Dimensions.UnitSize : $"Job {x.Job.JobNumber}: {x.Dimensions.UnitSize}"));
|
|
|
if (items.Any())
|
|
|
items = items.Prepend(new KeyValuePair<SupplierProduct?, string>(null, ""));
|
|
|
@@ -1127,7 +1257,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockOrderingItem>
|
|
|
|
|
|
private void LoadJobData(IEnumerable<Guid> ids)
|
|
|
{
|
|
|
- var neededIDs = ids.Where(x => x != Guid.Empty && !JobDetails.ContainsKey(x)).ToArray();
|
|
|
+ var neededIDs = ids.Where(x => x != Guid.Empty && !JobDetails.ContainsKey(x)).Distinct().ToArray();
|
|
|
if(neededIDs.Length > 0)
|
|
|
{
|
|
|
var details = Client.Query(
|