using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using InABox.Core; using PRSClasses; namespace Comal.Classes { public class StockHoldingLastStocktake : CoreAggregate { public override Expression> Aggregate => x => x.Date; public override Filter Filter => new Filter(x => x.Type) .IsEqualTo(StockMovementType.StockTake); public override Dictionary>, Expression>> Links => new Dictionary>, Expression>>() { { StockMovement => StockMovement.Product.ID, StockHolding => StockHolding.Product.ID }, { StockMovement => StockMovement.Location.ID, StockHolding => StockHolding.Location.ID }, { StockMovement => StockMovement.Style.ID, StockHolding => StockHolding.Style.ID }, { StockMovement => StockMovement.Job.ID, StockHolding => StockHolding.Job.ID }, { StockMovement => StockMovement.Dimensions.Unit.ID, StockHolding => StockHolding.Dimensions.Unit.ID }, { StockMovement => StockMovement.Dimensions.UnitSize, StockHolding => StockHolding.Dimensions.UnitSize }, }; public override AggregateCalculation Calculation => AggregateCalculation.Maximum; } [UserTracking(typeof(StockMovement))] [Unrecoverable] public class StockHolding : StockEntity, IRemotable, IPersistent, IOneToMany, IOneToMany, IStockHolding, ILicense { [Editable(Editable.Disabled)] [EditorSequence(1)] public StockLocationLink Location { get; set; } private class ProductLookupGenerator : LookupDefinitionGenerator { public override Filter? DefineFilter(StockHolding[] items) => LookupFactory.DefineFilter().And(x => x.NonStock).IsEqualTo(false); } [Editable(Editable.Disabled)] [EditorSequence(2)] [LookupDefinition(typeof(ProductLookupGenerator))] public override ProductLink Product { get; set; } [DimensionsEditor(typeof(StockDimensions), AllowEditingUnit = false)] [Editable(Editable.Disabled)] [EditorSequence(3)] public override StockDimensions Dimensions { get; set; } [Editable(Editable.Disabled)] [EditorSequence(4)] public ProductStyleLink Style { get; set; } [Editable(Editable.Disabled)] [EditorSequence(4)] public JobLink Job { get; set; } [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)] [EditorSequence(5)] public double Units { get; set; } [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)] [EditorSequence(6)] public double Qty { get; set; } [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)] [EditorSequence(7)] public double Weight { get; set; } [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)] [EditorSequence(8)] public double Value { get; set; } [DoubleEditor(Editable = Editable.Disabled)] [EditorSequence(9)] public double AverageValue { get; set; } [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)] [EditorSequence(10)] public double Available { get; set; } [Aggregate(typeof(StockHoldingLastStocktake))] [DateEditor(Editable = Editable.Disabled)] [EditorSequence(11)] public DateTime LastStockTake { get; set; } public static Column[] Columns => new Column[] { new Column(x => x.Job.ID), new Column(x => x.Location.ID), new Column(x => x.Product.ID), new Column(x => x.Style.ID), new Column(x => x.Dimensions.Unit.ID), new Column(x => x.Dimensions.Quantity), new Column(x => x.Dimensions.Length), new Column(x => x.Dimensions.Width), new Column(x => x.Dimensions.Height), new Column(x => x.Dimensions.Weight), }; public static Filter? GetFilter(IStockHolding holding) { var filter = new Filters(); foreach(var column in Columns) { filter.Add(new Filter(column.Cast()).IsEqualTo(CoreUtils.GetPropertyValue(holding, column.Property))); } return filter.Combine(); } } 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. /// /// /// public static StockMovement CreateMovement(this IStockHolding holding) { var movement = new StockMovement(); 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 IEnumerable 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.ID = first.Location.ID; holding.Product.ID = first.Product.ID; holding.Style.ID = first.Style.ID; holding.Job.ID = first.Job.ID; holding.Dimensions.CopyFrom(first.Dimensions); } holding.Recalculate(selected); toGroup.RemoveAll(x => selected.Any(s => s.ID == x.ID)); } return grouped; } public static bool IsEqualTo(this IStockHolding h1, IStockHolding h2) { return h1.Product.ID == h2.Product.ID && h1.Location.ID == h2.Location.ID && h1.Job.ID == h2.Job.ID && h1.Style.ID == h2.Style.ID && h1.Dimensions.Unit.ID == h2.Dimensions.Unit.ID && h1.Dimensions.Length.IsEffectivelyEqual(h2.Dimensions.Length) && h1.Dimensions.Width.IsEffectivelyEqual(h2.Dimensions.Width) && h1.Dimensions.Height.IsEffectivelyEqual(h2.Dimensions.Height) && h1.Dimensions.Quantity.IsEffectivelyEqual(h2.Dimensions.Quantity) && h1.Dimensions.Weight.IsEffectivelyEqual(h2.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; } } }