using System;
using System.Collections.Generic;
using System.Linq;
using InABox.Clients;
using InABox.Core;
namespace Comal.Classes
{
public static class StockHoldingExtensions
{
///
/// Create a new stock movement from an , copying across the "key" properties;
/// that is, the job, product, style, location and dimensions.
///
///
/// Also sets the to today.
///
///
///
public static StockMovement CreateMovement(this IStockHolding holding)
{
var movement = new StockMovement();
movement.Date = DateTime.Now;
movement.Job.ID = holding.Job.ID;
movement.Job.Synchronise(holding.Job);
movement.Product.ID = holding.Product.ID;
movement.Product.Synchronise(holding.Product);
movement.Style.ID = holding.Style.ID;
movement.Style.Synchronise(holding.Style);
movement.Location.ID = holding.Location.ID;
movement.Location.Synchronise(holding.Location);
movement.Dimensions.CopyFrom(holding.Dimensions);
return movement;
}
public static List GroupMovements(IEnumerable movements)
{
var grouped = new List();
var toGroup = movements.AsList();
while (toGroup.Count > 0)
{
var first = toGroup.First();
var selected = toGroup.Where(x => x.IsEqualTo(first)).ToList();
var holding = grouped.FirstOrDefault(x => x.IsEqualTo(first));
if (holding == null)
{
holding = new StockHolding();
holding.Location.CopyFrom(first.Location);
holding.Product.CopyFrom(first.Product);
holding.Style.CopyFrom(first.Style);
holding.Job.CopyFrom(first.Job);
holding.Dimensions.CopyFrom(first.Dimensions);
grouped.Add(holding);
}
holding.Recalculate(selected);
toGroup.RemoveAll(x => selected.Any(s => s.ID == x.ID));
}
return grouped;
}
public static bool IsEqualTo(this IStockHolding row1, IStockHolding row2)
{
return row1.Product.ID == row2.Product.ID
&& row1.Location.ID == row2.Location.ID
&& row1.Job.ID == row2.Job.ID
&& row1.Style.ID == row2.Style.ID
&& row1.Dimensions.Unit.ID == row2.Dimensions.Unit.ID
&& row1.Dimensions.Length.IsEffectivelyEqual(row2.Dimensions.Length)
&& row1.Dimensions.Width.IsEffectivelyEqual(row2.Dimensions.Width)
&& row1.Dimensions.Height.IsEffectivelyEqual(row2.Dimensions.Height)
&& row1.Dimensions.Quantity.IsEffectivelyEqual(row2.Dimensions.Quantity)
&& row1.Dimensions.Weight.IsEffectivelyEqual(row2.Dimensions.Weight);
}
public static bool IsEqualTo(this CoreRow row1, CoreRow row2)
where T1 : IStockHolding
where T2 : IStockHolding
{
return row1.Get(x=>x.Product.ID) == row2.Get(x => x.Product.ID)
&& row1.Get(x=>x.Location.ID) == row2.Get(x => x.Location.ID)
&& row1.Get(x=>x.Job.ID) == row2.Get(x => x.Job.ID)
&& row1.Get(x=>x.Style.ID) == row2.Get(x => x.Style.ID)
&& row1.Get(x=>x.Dimensions.Unit.ID) == row2.Get(x => x.Dimensions.Unit.ID)
&& row1.Get(x=>x.Dimensions.Length).IsEffectivelyEqual(row2.Get(x=>x.Dimensions.Length))
&& row1.Get(x=>x.Dimensions.Width).IsEffectivelyEqual(row2.Get(x=>x.Dimensions.Width))
&& row1.Get(x=>x.Dimensions.Height).IsEffectivelyEqual(row2.Get(x=>x.Dimensions.Height))
&& row1.Get(x=>x.Dimensions.Quantity).IsEffectivelyEqual(row2.Get(x=>x.Dimensions.Quantity))
&& row1.Get(x=>x.Dimensions.Weight).IsEffectivelyEqual(row2.Get(x=>x.Dimensions.Weight));
}
public static void Recalculate(this StockHolding holding, IEnumerable movements)
{
movements = movements.AsIList();
var units = movements.Sum(x => x.Units);
var cost = movements.Select(x => x.Units * x.Cost).Sum();
var available = movements.Where(x => x.JobRequisitionItem.ID == Guid.Empty).Sum(x => x.Units);
holding.Units = units;
holding.Available = available;
holding.Qty = movements.Sum(x => x.Units * x.Dimensions.Value);
holding.Value = cost;
holding.AverageValue = units.IsEffectivelyEqual(0.0F) ? 0.0d : cost / units;
holding.Weight = holding.Qty * holding.Dimensions.Weight;
}
public static IEnumerable LoadRequisitionItems(this StockHolding holding, bool alwaysshowunallocated = false, Columns? columns = null)
{
columns ??= Columns.None();
columns.Add(x => x.ID)
.Add(x => x.Product.ID)
.Add(x => x.Style.ID)
.AddDimensionsColumns(x => x.Dimensions)
.Add(x => x.Job.ID)
.Add(x => x.Job.JobNumber)
.Add(x => x.Job.Name)
.Add(x => x.Requisition.Number)
.Add(x => x.Requisition.Description)
.Add(x => x.Qty);
var items = new Client().Query(
new Filter(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID),
columns)
.ToObjects()
.Where(x=>x.Product.ID == holding.Product.ID && x.Style.ID == holding.Style.ID && x.Dimensions.Equals(holding.Dimensions));
if (holding.Available > 0 || alwaysshowunallocated)
{
var requi = new JobRequisitionItem() { Qty = holding.Available };
requi.Requisition.Description = "Unallocated Items";
items = CoreUtils.One(requi).Concat(items);
}
return items;
}
public static IEnumerable AdjustValue(this StockHolding holding, double unitvalue, StockMovementBatch batch)
{
List _result = new List();
var movements = Client.Query(
new Filter(x => x.Location.ID).IsEqualTo(holding.Location.ID)
.And(x=>x.Product.ID).IsEqualTo(holding.Product.ID)
.And(x => x.Style.ID).IsEqualTo(holding.Style.ID)
.And(x => x.Dimensions).DimensionEquals(holding.Dimensions)
.And(x => x.Job.ID).IsEqualTo(holding.Job.ID),
Columns.Required().Add(x=>x.Units)
).Rows.ToObjects().ToArray();
var _allocations = movements.GroupBy(x => x.JobRequisitionItem.ID);
foreach (var _allocation in _allocations)
{
var _units = _allocation.Sum(x => x.Units);
if (!_units.IsEffectivelyEqual(0.0))
{
var _transout = holding.CreateMovement();
_transout.Employee.ID = batch.Employee.ID;
_transout.Issued = _units;
_transout.Cost = holding.AverageValue;
_transout.Type = StockMovementType.TransferOut;
_transout.JobRequisitionItem.ID = _allocation.Key;
_transout.Batch.ID = batch.ID;
_transout.Notes = $"Adjusting Average Value from ${holding.AverageValue:F2} to ${unitvalue:F2}";
_result.Add(_transout);
var _transin = holding.CreateMovement();
_transin.Date = _transout.Date.AddTicks(1);
_transout.Employee.ID = batch.Employee.ID;
_transin.Received = _units;
_transin.Cost = unitvalue;
_transin.Type = StockMovementType.TransferIn;
_transin.Transaction = _transout.Transaction;
_transin.JobRequisitionItem.ID = _allocation.Key;
_transin.Batch.ID = batch.ID;
_transin.Notes = $"Adjusting Average Value from ${holding.AverageValue:F2} to ${unitvalue:F2}";
_result.Add(_transin);
}
}
return _result;
}
public static Dictionary<(Guid product, Guid style, Guid location, Guid job, StockDimensions dimensions), StockHolding> LoadStockHoldings(IEnumerable mvts, Columns columns, Dictionary<(Guid product, Guid style, Guid location, Guid job, StockDimensions dimensions), StockHolding>? holdings = null, IQueryProvider? query = null)
{
query ??= Client.Provider;
columns.Add(x => x.ID);
columns.Add(x => x.Product.ID);
columns.Add(x => x.Location.ID);
columns.Add(x => x.Style.ID);
columns.Add(x => x.Job.ID);
columns.AddDimensionsColumns(x => x.Dimensions, Dimensions.ColumnsType.Local);
if(holdings != null)
{
mvts = mvts.Where(mvt =>
{
var key = (mvt.Product.ID, mvt.Style.ID, mvt.Location.ID, mvt.Job.ID, mvt.Dimensions);
return !holdings.ContainsKey(key);
}).ToArray();
}
else
{
holdings = new Dictionary<(Guid product, Guid style, Guid location, Guid job, StockDimensions dimensions), StockHolding>();
}
var productIDs = mvts.Select(x => x.Product.ID).Distinct().ToArray();
var locationIDs = mvts.Select(x => x.Location.ID).Distinct().ToArray();
var styleIDs = mvts.Select(x => x.Style.ID).Distinct().ToArray();
var jobIDs = mvts.Select(x => x.Job.ID).Distinct().ToArray();
var newHoldings = query.Query(new Filter(x => x.Product.ID).InList(productIDs)
.And(x => x.Location.ID).InList(locationIDs)
.And(x => x.Style.ID).InList(styleIDs)
.And(x => x.Job.ID).InList(jobIDs),
columns
).ToObjects();
foreach(var holding in newHoldings)
{
holdings[(holding.Product.ID, holding.Style.ID, holding.Location.ID, holding.Job.ID, holding.Dimensions)] = holding;
}
return holdings;
}
}
}