|
|
@@ -172,13 +172,13 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
Position = DynamicActionColumnPosition.Start
|
|
|
});
|
|
|
|
|
|
- var _averageColumn = CreateColumn(GetAverage, ColumnTag.Average,"Avg.","F2");
|
|
|
+ var _averageColumn = CreateColumn(GetAverage, ColumnTag.Average,"Avg.","");
|
|
|
_averageColumn.ContextMenu = Average_Menu;
|
|
|
|
|
|
- var _periodColumn = CreateColumn(GetPeriod, ColumnTag.Period, "Days", "F0");
|
|
|
- _periodColumn.ContextMenu = MinimumStock_Menu;
|
|
|
+ var _periodColumn = CreateColumn(GetPeriod, ColumnTag.Period, "Days", "");
|
|
|
+ _periodColumn.ContextMenu = MinimumDays_Menu;
|
|
|
|
|
|
- var _minStockColumn = CreateColumn(GetMinimumStockLevel, ColumnTag.MinimumStockRequired,"Min.","F2");
|
|
|
+ var _minStockColumn = CreateColumn(GetMinimumStockLevel, ColumnTag.MinimumStockRequired,"Min.","");
|
|
|
_minStockColumn.ContextMenu = MinimumStock_Menu;
|
|
|
CreateColumn(GetGeneralStockLevel, ColumnTag.GeneralStockHoldings,"Hld.","F2");
|
|
|
CreateColumn(GetGeneralPurchaseOrder, ColumnTag.GeneralPurchaseOrders, "PO.","F2");
|
|
|
@@ -294,7 +294,7 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
var item = Grid.LoadItem(row);
|
|
|
|
|
|
var periods = (Parent as StockForecastGrid)?.Properties?.AveragePeriods ?? 1;
|
|
|
- var avg = item.MinStock.IsEffectivelyGreaterThan(0.0) && item.MinStockDays > 0
|
|
|
+ var avg = item.MinStockDays > 0
|
|
|
? !item.MinStock.IsEffectivelyLessThan(item.Average)
|
|
|
? Colors.LightGreen.ToBrush(0.5)
|
|
|
: Colors.LightSalmon.ToBrush(0.5)
|
|
|
@@ -399,12 +399,37 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
private DynamicTextColumn GetColumn(ColumnTag tag) => (ActionColumns.First(x => Equals(x.Tag, tag)) as DynamicTextColumn)!;
|
|
|
|
|
|
private object GetAverage(CoreRow? row)
|
|
|
- => row is not null ? LoadItem(row).Average : 0.0;
|
|
|
+ {
|
|
|
+ if (row is not null)
|
|
|
+ {
|
|
|
+ var item = LoadItem(row);
|
|
|
+ if (item.IsProductInstance)
|
|
|
+ return $"{item.Average:F2}";
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
|
|
|
private object GetPeriod(CoreRow? row)
|
|
|
- => row is not null ? LoadItem(row).MinStockDays : 0.0;
|
|
|
-
|
|
|
- private object GetMinimumStockLevel(CoreRow? row) => row is not null ? LoadItem(row).MinStock : 0.0;
|
|
|
+ {
|
|
|
+ if (row is not null)
|
|
|
+ {
|
|
|
+ var item = LoadItem(row);
|
|
|
+ if (item.IsProductInstance)
|
|
|
+ return $"{item.MinStockDays}";
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ private object GetMinimumStockLevel(CoreRow? row)
|
|
|
+ {
|
|
|
+ if (row is not null)
|
|
|
+ {
|
|
|
+ var item = LoadItem(row);
|
|
|
+ if (item.IsProductInstance)
|
|
|
+ return $"{item.MinStock}";
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
|
|
|
private object GetGeneralStockLevel(CoreRow? row)
|
|
|
=> row is not null ? LoadItem(row).GenStock : 0.0;
|
|
|
@@ -518,6 +543,30 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
|
|
|
switch (tag)
|
|
|
{
|
|
|
+ case ColumnTag.Average :
|
|
|
+ ShowDetailGrid(
|
|
|
+ "Stock Usage",
|
|
|
+ () => BuildDetailGrid<StockMovement>(
|
|
|
+ ColumnTag.Average.ToString(),
|
|
|
+ x => x.Product.ID,
|
|
|
+ item.Product.ID,
|
|
|
+ x => x.Style.ID,
|
|
|
+ styleid,
|
|
|
+ x => x.Dimensions,
|
|
|
+ item.Dimensions,
|
|
|
+ null,
|
|
|
+ new Filter<StockMovement>(x=>x.Job.ID).IsEqualTo(Guid.Empty)
|
|
|
+ .And(x=>x.Date).IsGreaterThanOrEqualTo(DateTime.Today.AddDays(0-item.MinStockDays * this.Properties.AveragePeriods))
|
|
|
+ .And(x=>x.Type).InList(
|
|
|
+ StockMovementType.Issue,
|
|
|
+ StockMovementType.TransferOut,
|
|
|
+ StockMovementType.TransferIn,
|
|
|
+ StockMovementType.StockTake),
|
|
|
+ null
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ break;
|
|
|
case ColumnTag.GeneralStockHoldings:
|
|
|
ShowDetailGrid(
|
|
|
"Stock Holdings",
|
|
|
@@ -644,6 +693,37 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private ContextMenu? MinimumDays_Menu(CoreRow[]? rows)
|
|
|
+ {
|
|
|
+ if (rows is null || rows.Length == 0) return null;
|
|
|
+ var items = LoadItems(rows);
|
|
|
+
|
|
|
+ var menu = new ContextMenu();
|
|
|
+ if (Security.CanEdit<ProductInstance>())
|
|
|
+ {
|
|
|
+ if(items.Any(x => x.IsProductInstance))
|
|
|
+ {
|
|
|
+ menu.AddItem("Adjust minimum stock days", null, items, AdjustMinStockDays_Click);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(menu.Items.Count > 0)
|
|
|
+ return menu;
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AdjustMinStockDays_Click(StockForecastItem[] sfItems)
|
|
|
+ {
|
|
|
+ var items = sfItems.SelectMany(x=>x.ProductInstances).ToArray();
|
|
|
+ var dayValues = items.Select(x => x.MinimumStockDays).Distinct().ToArray();
|
|
|
+ int days = dayValues.Length == 1 ? dayValues[0] : 0;
|
|
|
+ if (NumberEdit.Execute("Number of Days", 0, int.MaxValue, ref days))
|
|
|
+ {
|
|
|
+ foreach (var item in items)
|
|
|
+ item.MinimumStockDays = days;
|
|
|
+ Client.Save(items.Where(x=>x.IsChanged()),"Minimum Stock Days adjusted");
|
|
|
+ Refresh(false,true);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
private ContextMenu? MinimumStock_Menu(CoreRow[]? rows)
|
|
|
{
|
|
|
@@ -660,13 +740,8 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
}
|
|
|
}
|
|
|
if(menu.Items.Count > 0)
|
|
|
- {
|
|
|
return menu;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
private void AdjustMinStock_Click(StockForecastItem[] items)
|
|
|
@@ -954,7 +1029,11 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
),
|
|
|
|
|
|
GetQuery<StockMovement>(
|
|
|
- filter: new Filter<StockMovement>(x => x.Type).InList(StockMovementType.Issue, StockMovementType.StockTake),
|
|
|
+ filter: new Filter<StockMovement>(x => x.Type).InList(
|
|
|
+ StockMovementType.Issue,
|
|
|
+ StockMovementType.TransferOut,
|
|
|
+ StockMovementType.TransferIn,
|
|
|
+ StockMovementType.StockTake),
|
|
|
columns: Columns.None<StockMovement>()
|
|
|
.Add(x=>x.Date)
|
|
|
.Add(x => x.Units)
|
|
|
@@ -999,7 +1078,9 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
{
|
|
|
var dimensions = new StockDimensions();
|
|
|
dimensions.CopyFrom(instance.Dimensions);
|
|
|
- var minStock = DimensionUtils.ConvertDimensions(dimensions, instance.MinimumStockLevel, (f,c) => Client.Query(f,c));
|
|
|
+ var minStock = (double)instance.MinimumStockLevel;
|
|
|
+ var minCost = instance.AverageCost;
|
|
|
+ DimensionUtils.ConvertDimensions(dimensions, ref minStock, ref minCost, Client<ProductDimensionUnit>.Provider);
|
|
|
|
|
|
var item = GetItem(new(instance.Product.ID, instance.Style.ID, dimensions));
|
|
|
item.MinStockDays = instance.MinimumStockDays;
|
|
|
@@ -1011,7 +1092,9 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
foreach(var holdingrow in holdings.Rows)
|
|
|
{
|
|
|
var holding = holdingrow.ToObject<StockHolding>();
|
|
|
- holding.Units = DimensionUtils.ConvertDimensions(holding.Dimensions, holding.Units, (f,c) => Client.Query(f,c));
|
|
|
+ holding.ConvertDimensions(x => x.Dimensions, x => x.Units, x => x.AverageValue,
|
|
|
+ Client<ProductDimensionUnit>.Provider);
|
|
|
+ //holding.Units = DimensionUtils.ConvertDimensions(holding.Dimensions, holding.Units, (f,c) => Client.Query(f,c));
|
|
|
|
|
|
var item = GetItem(GetKey(holding.Product.ID, holding.Style.ID, holding.Dimensions));
|
|
|
if(holding.Job.ID == Guid.Empty)
|
|
|
@@ -1029,15 +1112,19 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
.ToDictionary(x => x.ID);
|
|
|
foreach (var poi in purchaseOrderItems.Values)
|
|
|
{
|
|
|
- poi.Qty = DimensionUtils.ConvertDimensions(poi.Dimensions, poi.Qty, (f,c) => Client.Query(f,c));
|
|
|
+ poi.ConvertDimensions(x => x.Dimensions, x => x.Qty, x => x.Cost,
|
|
|
+ Client<ProductDimensionUnit>.Provider);
|
|
|
+ //poi.Qty = DimensionUtils.ConvertDimensions(poi.Dimensions, poi.Qty, (f,c) => Client.Query(f,c));
|
|
|
}
|
|
|
|
|
|
var allocations = results.Get<PurchaseOrderItemAllocation>();
|
|
|
foreach(var allocationrow in allocations.Rows)
|
|
|
{
|
|
|
var allocation = allocationrow.ToObject<PurchaseOrderItemAllocation>();
|
|
|
+ var d = allocation.Item.Dimensions.Copy();
|
|
|
var q = allocation.Quantity;
|
|
|
- DimensionUtils.ConvertDimensions(allocation.Item.Dimensions, ref q, (f,c) => Client.Query(f,c));
|
|
|
+ var c = allocation.Item.Cost;
|
|
|
+ DimensionUtils.ConvertDimensions(d, ref q, ref c, Client<ProductDimensionUnit>.Provider);
|
|
|
// POIAs are already converted where necessary, so we don't have to update the quantities again, just update the dimensions
|
|
|
|
|
|
var key = new ItemKey(
|
|
|
@@ -1083,7 +1170,8 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
foreach(var bomItemRow in jobBOMItems.Rows)
|
|
|
{
|
|
|
var bomItem = bomItemRow.ToObject<JobBillOfMaterialsItem>();
|
|
|
- bomItem.Quantity = DimensionUtils.ConvertDimensions(bomItem.Dimensions, bomItem.Quantity, (f,c) => Client.Query(f,c));
|
|
|
+ bomItem.ConvertDimensions(x => x.Dimensions, x => x.Quantity, x => x.UnitCost, Client<ProductDimensionUnit>.Provider);
|
|
|
+ //bomItem.Quantity = DimensionUtils.ConvertDimensions(bomItem.Dimensions, bomItem.Quantity, (f,c) => Client.Query(f,c));
|
|
|
var key = GetKey(bomItem.Product.ID, bomItem.Style.ID, bomItem.Dimensions);
|
|
|
var item = GetItem(key);
|
|
|
|
|
|
@@ -1094,16 +1182,19 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
|
|
|
foreach(var mvtrow in movements.Rows)
|
|
|
{
|
|
|
var movement = mvtrow.ToObject<StockMovement>();
|
|
|
- movement.Units = DimensionUtils.ConvertDimensions(movement.Dimensions, movement.Units, (f,c) => Client.Query(f,c));
|
|
|
-
|
|
|
+ movement.ConvertDimensions(x => x.Dimensions, x => x.Units, x => x.Cost,
|
|
|
+ Client<ProductDimensionUnit>.Provider);
|
|
|
+ //movement.Units = DimensionUtils.ConvertDimensions(movement.Dimensions, movement.Units, (f,c) => Client.Query(f,c));
|
|
|
+
|
|
|
var item = GetItem(GetKey(movement.Product.ID, movement.Style.ID, movement.Dimensions));
|
|
|
-
|
|
|
- var periods = (Properties?.AveragePeriods ?? 1);
|
|
|
- var days = item.MinStockDays * periods;
|
|
|
- if (days != 0 && DateTime.Today.Subtract(movement.Date) <= TimeSpan.FromDays(days))
|
|
|
- item.Average += (0.0 - movement.Units)/periods;
|
|
|
-
|
|
|
- if(movement.Job.ID != Guid.Empty)
|
|
|
+ if (movement.Job.ID == Guid.Empty)
|
|
|
+ {
|
|
|
+ var periods = (Properties?.AveragePeriods ?? 1);
|
|
|
+ var days = item.MinStockDays * periods;
|
|
|
+ if (days != 0 && DateTime.Today.Subtract(movement.Date) <= TimeSpan.FromDays(days))
|
|
|
+ item.Average += (0.0 - movement.Units) / periods;
|
|
|
+ }
|
|
|
+ else
|
|
|
item.AddJobBOM(movement.Job.ID, movement.Units);
|
|
|
}
|
|
|
|