|  | @@ -1,41 +1,13 @@
 | 
	
		
			
				|  |  | -using System;
 | 
	
		
			
				|  |  | -using System.Collections.Generic;
 | 
	
		
			
				|  |  | -using System.Linq;
 | 
	
		
			
				|  |  | -using System.Linq.Expressions;
 | 
	
		
			
				|  |  | -using InABox.Clients;
 | 
	
		
			
				|  |  | +using System;
 | 
	
		
			
				|  |  |  using InABox.Core;
 | 
	
		
			
				|  |  | -using PRSClasses;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace Comal.Classes
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    using HoldingDictionary = Dictionary<(Guid product, Guid style, Guid location, Guid job, StockDimensions dimensions), StockHolding>;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    public class StockHoldingLastStocktake : CoreAggregate<StockHolding, StockMovement, DateTime>
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        public override Expression<Func<StockMovement, DateTime>> Aggregate => x => x.Date;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.Type)
 | 
	
		
			
				|  |  | -            .IsEqualTo(StockMovementType.StockTake);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public override Dictionary<Expression<Func<StockMovement, object?>>, Expression<Func<StockHolding, object?>>> Links =>
 | 
	
		
			
				|  |  | -            new Dictionary<Expression<Func<StockMovement, object?>>, Expression<Func<StockHolding, object?>>>()
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                { 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 },
 | 
	
		
			
				|  |  | -            }.AddRange(Dimensions.GetLinks<StockMovement, StockHolding>(x => x.Dimensions, x => x.Dimensions));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public override AggregateCalculation Calculation => AggregateCalculation.Maximum;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +    [AutoEntity(typeof(StockHoldingSummaryGenerator))]
 | 
	
		
			
				|  |  |      [UserTracking(typeof(StockMovement))]
 | 
	
		
			
				|  |  | -    [Unrecoverable]
 | 
	
		
			
				|  |  |      public class StockHolding : StockEntity, IRemotable, IPersistent, IOneToMany<StockLocation>, IOneToMany<Product>, 
 | 
	
		
			
				|  |  |          IStockHolding, ILicense<WarehouseLicense>
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  |          [Editable(Editable.Disabled)]
 | 
	
		
			
				|  |  |          [EditorSequence(1)]
 | 
	
		
			
				|  |  |          public StockLocationLink Location { get; set; }
 | 
	
	
		
			
				|  | @@ -77,22 +49,31 @@ namespace Comal.Classes
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  |          [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
 | 
	
		
			
				|  |  |          [EditorSequence(8)]
 | 
	
		
			
				|  |  | -        public double Value { get; set; }
 | 
	
		
			
				|  |  | +        public double Available { get; set; }
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  | -        [DoubleEditor(Editable = Editable.Disabled)]
 | 
	
		
			
				|  |  | +        [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
 | 
	
		
			
				|  |  |          [EditorSequence(9)]
 | 
	
		
			
				|  |  | -        public double AverageValue { get; set; }
 | 
	
		
			
				|  |  | +        public double Allocated { get; set; }
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  |          [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
 | 
	
		
			
				|  |  |          [EditorSequence(10)]
 | 
	
		
			
				|  |  | -        public double Available { get; set; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        [Formula(typeof(StockHoldingAllocatedFormula))]
 | 
	
		
			
				|  |  | -        [DoubleEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
 | 
	
		
			
				|  |  | +        public double Value { get; set; }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        private class AverageValueFormula : ComplexFormulaGenerator<StockHolding, double>
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            public override IComplexFormulaNode<StockHolding, double> GetFormula() =>
 | 
	
		
			
				|  |  | +                Formula(
 | 
	
		
			
				|  |  | +                    FormulaOperator.Divide,
 | 
	
		
			
				|  |  | +                    Property(x => x.Value),
 | 
	
		
			
				|  |  | +                    Property(x => x.Units)
 | 
	
		
			
				|  |  | +                );
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        [ComplexFormula(typeof(AverageValueFormula))]
 | 
	
		
			
				|  |  | +        [DoubleEditor(Editable = Editable.Disabled)]
 | 
	
		
			
				|  |  |          [EditorSequence(11)]
 | 
	
		
			
				|  |  | -        public double Allocated { get; set; }
 | 
	
		
			
				|  |  | +        public double AverageValue { get; set; }
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  | -        [Aggregate(typeof(StockHoldingLastStocktake))]
 | 
	
		
			
				|  |  |          [DateEditor(Editable = Editable.Disabled)]
 | 
	
		
			
				|  |  |          [EditorSequence(11)]
 | 
	
		
			
				|  |  |          public DateTime LastStockTake { get; set; }
 | 
	
	
		
			
				|  | @@ -122,280 +103,7 @@ namespace Comal.Classes
 | 
	
		
			
				|  |  |          
 | 
	
		
			
				|  |  |              return filter.Combine();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    internal class StockHoldingAllocatedFormula : IFormula<StockHolding, double>
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        public Expression<Func<StockHolding, double>> Value => x => x.Qty;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public FormulaOperator Operator => FormulaOperator.Subtract;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public Expression<Func<StockHolding, double>>[] Modifiers => new Expression<Func<StockHolding, double>>[]
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            x => x.Available
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public FormulaType Type => FormulaType.Virtual;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    public static class StockHoldingExtensions
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Create a new stock movement from an <see cref="IStockHolding"/>, copying across the "key" properties;
 | 
	
		
			
				|  |  | -        /// that is, the job, product, style, location and dimensions.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <remarks>
 | 
	
		
			
				|  |  | -        /// Also sets the <see cref="StockMovement.Date"/> to today.
 | 
	
		
			
				|  |  | -        /// </remarks>
 | 
	
		
			
				|  |  | -        /// <param name="holding"></param>
 | 
	
		
			
				|  |  | -        /// <returns></returns>
 | 
	
		
			
				|  |  | -        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<StockHolding> GroupMovements(IEnumerable<StockMovement> movements)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var grouped = new List<StockHolding>();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            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<T1,T2>(this CoreRow row1, CoreRow row2)
 | 
	
		
			
				|  |  | -            where T1 : IStockHolding
 | 
	
		
			
				|  |  | -            where T2 : IStockHolding
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return row1.Get<T1,Guid>(x=>x.Product.ID) == row2.Get<T2,Guid>(x => x.Product.ID)
 | 
	
		
			
				|  |  | -                && row1.Get<T1,Guid>(x=>x.Location.ID) == row2.Get<T2,Guid>(x => x.Location.ID)
 | 
	
		
			
				|  |  | -                && row1.Get<T1,Guid>(x=>x.Job.ID) == row2.Get<T2,Guid>(x => x.Job.ID)
 | 
	
		
			
				|  |  | -                && row1.Get<T1,Guid>(x=>x.Style.ID) == row2.Get<T2,Guid>(x => x.Style.ID)
 | 
	
		
			
				|  |  | -                && row1.Get<T1,Guid>(x=>x.Dimensions.Unit.ID) == row2.Get<T2,Guid>(x => x.Dimensions.Unit.ID)
 | 
	
		
			
				|  |  | -                && row1.Get<T1,double>(x=>x.Dimensions.Length).IsEffectivelyEqual(row2.Get<T2,double>(x=>x.Dimensions.Length))
 | 
	
		
			
				|  |  | -                && row1.Get<T1,double>(x=>x.Dimensions.Width).IsEffectivelyEqual(row2.Get<T2,double>(x=>x.Dimensions.Width))
 | 
	
		
			
				|  |  | -                && row1.Get<T1,double>(x=>x.Dimensions.Height).IsEffectivelyEqual(row2.Get<T2,double>(x=>x.Dimensions.Height))
 | 
	
		
			
				|  |  | -                && row1.Get<T1,double>(x=>x.Dimensions.Quantity).IsEffectivelyEqual(row2.Get<T2,double>(x=>x.Dimensions.Quantity))
 | 
	
		
			
				|  |  | -                && row1.Get<T1,double>(x=>x.Dimensions.Weight).IsEffectivelyEqual(row2.Get<T2,double>(x=>x.Dimensions.Weight));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public static void Recalculate(this StockHolding holding, IEnumerable<StockMovement> 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<JobRequisitionItem> LoadRequisitionItems(this StockHolding holding, bool alwaysshowunallocated = false, Columns<JobRequisitionItem>? columns = null)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            columns ??= Columns.None<JobRequisitionItem>();
 | 
	
		
			
				|  |  | -            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<JobRequisitionItem>().Query(
 | 
	
		
			
				|  |  | -                    new Filter<JobRequisitionItem>(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID),
 | 
	
		
			
				|  |  | -                    columns)
 | 
	
		
			
				|  |  | -                .ToObjects<JobRequisitionItem>()
 | 
	
		
			
				|  |  | -                .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<StockMovement> AdjustValue(this StockHolding holding, double unitvalue, StockMovementBatch batch)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            List<StockMovement> _result = new List<StockMovement>();
 | 
	
		
			
				|  |  | -            var movements = Client.Query(
 | 
	
		
			
				|  |  | -                new Filter<StockMovement>(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<StockMovement>().Add(x=>x.Units)
 | 
	
		
			
				|  |  | -            ).Rows.ToObjects<StockMovement>().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 HoldingDictionary LoadStockHoldings(IEnumerable<IStockHolding> mvts, Columns<StockHolding> columns, HoldingDictionary? holdings = null, IQueryProvider<StockHolding>? query = null)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            query ??= Client<StockHolding>.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 HoldingDictionary();
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            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<StockHolding>(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<StockHolding>();
 | 
	
		
			
				|  |  | -            foreach(var holding in newHoldings)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                holdings[(holding.Product.ID, holding.Style.ID, holding.Location.ID, holding.Job.ID, holding.Dimensions)] = holding;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return holdings;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | -    //     public static IEnumerable<Tuple<Guid,double>> GetAllocations(this StockHolding holding, bool alwaysshowunallocated)
 | 
	
		
			
				|  |  | -    //     {
 | 
	
		
			
				|  |  | -    //         var table = new Client<StockMovement>().Query(
 | 
	
		
			
				|  |  | -    //             StockHolding.GetFilter(holding),
 | 
	
		
			
				|  |  | -    //             new Columns<StockMovement>(x => x.Units)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.Location.ID)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.Product.ID)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.Style.ID)
 | 
	
		
			
				|  |  | -    //                 .AddDimensionsColumns(x => x.Dimensions)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.Cost)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.OrderItem.ID)
 | 
	
		
			
				|  |  | -    //                 .Add(x => x.JobRequisitionItem.ID)
 | 
	
		
			
				|  |  | -    //             );
 | 
	
		
			
				|  |  | -    //         
 | 
	
		
			
				|  |  | -    //         var movements = table
 | 
	
		
			
				|  |  | -    //             .ToObjects<StockMovement>();
 | 
	
		
			
				|  |  | -    //         
 | 
	
		
			
				|  |  | -    //         var groups = movements
 | 
	
		
			
				|  |  | -    //             .GroupBy(x => new
 | 
	
		
			
				|  |  | -    //             {
 | 
	
		
			
				|  |  | -    //                 Location = x.Location.ID,
 | 
	
		
			
				|  |  | -    //                 Product = x.Product.ID,
 | 
	
		
			
				|  |  | -    //                 Style = x.Style.ID,
 | 
	
		
			
				|  |  | -    //                 x.Dimensions,
 | 
	
		
			
				|  |  | -    //                 x.Cost,
 | 
	
		
			
				|  |  | -    //                 OrderItem = x.OrderItem.ID,
 | 
	
		
			
				|  |  | -    //                 JobRequisitionItem = x.JobRequisitionItem.ID
 | 
	
		
			
				|  |  | -    //             });
 | 
	
		
			
				|  |  | -    //
 | 
	
		
			
				|  |  | -    //         var result = groups
 | 
	
		
			
				|  |  | -    //             .Select(x => new Tuple<Guid, double>(
 | 
	
		
			
				|  |  | -    //                 x.Key.JobRequisitionItem,
 | 
	
		
			
				|  |  | -    //                 x.Sum(x => x.Units))
 | 
	
		
			
				|  |  | -    //             ).ToList();
 | 
	
		
			
				|  |  | -    //         
 | 
	
		
			
				|  |  | -    //         if (alwaysshowunallocated || !holding.Available.IsEffectivelyEqual(0))
 | 
	
		
			
				|  |  | -    //             result.Add(new Tuple<Guid, double>(Guid.Empty,holding.Available));
 | 
	
		
			
				|  |  | -    //         
 | 
	
		
			
				|  |  | -    //         return result;
 | 
	
		
			
				|  |  | -    //
 | 
	
		
			
				|  |  | -    //     }
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  |  }
 |