| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 | using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;using System.Runtime.CompilerServices;using InABox.Core;using PRSClasses;namespace Comal.Classes{    public class JobMaterialBOMAggregate : CoreAggregate<JobMaterial, JobBillOfMaterialsItem, double>    {        public override Expression<Func<JobBillOfMaterialsItem, double>> Aggregate => x => x.Quantity;        public override AggregateCalculation Calculation => AggregateCalculation.Sum;        public override Dictionary<Expression<Func<JobBillOfMaterialsItem, object?>>, Expression<Func<JobMaterial, object?>>> Links =>            new Dictionary<Expression<Func<JobBillOfMaterialsItem, object?>>, Expression<Func<JobMaterial, object?>>>()            {                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Job.ID, JobMaterial => JobMaterial.Job.ID },                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Product.ID, JobMaterial => JobMaterial.Product.ID },                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Style.ID, JobMaterial => JobMaterial.Style.ID },            }.AddRange(Dimensions.GetLinks<JobBillOfMaterialsItem, JobMaterial>(x => x.Dimensions, x => x.Dimensions));        public override Filter<JobBillOfMaterialsItem> Filter =>            new Filter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue);    }        public class JobMaterialRequisitionsAggregate : CoreAggregate<JobMaterial, JobRequisitionItem, double>    {        public override Expression<Func<JobRequisitionItem, double>> Aggregate => x => x.Qty;        public override AggregateCalculation Calculation => AggregateCalculation.Sum;        public override Dictionary<Expression<Func<JobRequisitionItem, object?>>, Expression<Func<JobMaterial, object?>>> Links =>            new Dictionary<Expression<Func<JobRequisitionItem, object?>>, Expression<Func<JobMaterial, object?>>>()            {                { JobMaterialRequisitionItem => JobMaterialRequisitionItem.Requisition.Job.ID, JobMaterial => JobMaterial.Job.ID },                { JobMaterialRequisitionItem => JobMaterialRequisitionItem.Product.ID, JobMaterial => JobMaterial.Product.ID },                { JobMaterialRequisitionItem => JobMaterialRequisitionItem.Style.ID, JobMaterial => JobMaterial.Style.ID },            }.AddRange(Dimensions.GetLinks<JobRequisitionItem, JobMaterial>(x => x.Dimensions, x => x.Dimensions));                public override Filter<JobRequisitionItem> Filter =>            new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)                .And(x => x.Cancelled).IsEqualTo(DateTime.MinValue);    }        public class JobMaterialPickingListsAggregate : CoreAggregate<JobMaterial, RequisitionItem, double>    {        public override Expression<Func<RequisitionItem, double>> Aggregate => x => x.Quantity;        public override AggregateCalculation Calculation => AggregateCalculation.Sum;        public override Dictionary<Expression<Func<RequisitionItem, object?>>, Expression<Func<JobMaterial, object?>>> Links =>            new Dictionary<Expression<Func<RequisitionItem, object?>>, Expression<Func<JobMaterial, object?>>>()            {                { RequisitionItem => RequisitionItem.RequisitionLink.JobLink.ID, JobMaterial => JobMaterial.Job.ID },                { RequisitionItem => RequisitionItem.Product.ID, JobMaterial => JobMaterial.Product.ID },                { RequisitionItem => RequisitionItem.Style.ID, JobMaterial => JobMaterial.Style.ID },            }.AddRange(Dimensions.GetLinks<RequisitionItem, JobMaterial>(x => x.Dimensions, x => x.Dimensions));                public override Filter<RequisitionItem> Filter =>            new Filter<RequisitionItem>(x => x.RequisitionLink.Filled).IsEqualTo(DateTime.MinValue);    }        public class JobMaterialReservedStockAggregate : CoreAggregate<JobMaterial, StockMovement, double>    {        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Units;        public override AggregateCalculation Calculation => AggregateCalculation.Sum;        public override Dictionary<Expression<Func<StockMovement, object?>>, Expression<Func<JobMaterial, object?>>> Links =>            new Dictionary<Expression<Func<StockMovement, object?>>, Expression<Func<JobMaterial, object?>>>()            {                { StockMovement => StockMovement.Job.ID, JobMaterial => JobMaterial.Job.ID },                { StockMovement => StockMovement.Product.ID, JobMaterial => JobMaterial.Product.ID },                { StockMovement => StockMovement.Style.ID, JobMaterial => JobMaterial.Style.ID },            }.AddRange(Dimensions.GetLinks<StockMovement, JobMaterial>(x => x.Dimensions, x => x.Dimensions));    }        public class JobMaterialUnionGenerator : AutoEntityUnionGenerator<IJobMaterial>    {        protected override void Configure()        {            AddTable<StockMovement>();            AddTable<JobBillOfMaterialsItem>();            AddTable<JobRequisitionItem>();            AddTable<PurchaseOrderItem>();            var alloc = AddTable<PurchaseOrderItemAllocation>()                .AliasField(x => x.Product.ID, x => x.Item.Product.ID)                .AliasField(x => x.Style.ID, x => x.Item.Style.ID);            foreach(var column in Dimensions.LocalColumns<StockDimensions>())            {                alloc.AliasField(                    Column<IJobMaterial>.SubColumn(x => x.Dimensions, column),                    Column<PurchaseOrderItemAllocation>.SubColumn(x => x.Item.Dimensions, column));            }        }        public override bool Distinct => true;        public override Column<IJobMaterial>[] IDColumns => new Column<IJobMaterial>[]        {            new Column<IJobMaterial>(x => x.Job.ID),            new Column<IJobMaterial>(x => x.Product.ID),            new Column<IJobMaterial>(x => x.Style.ID),            new Column<IJobMaterial>(x => x.Dimensions.Unit.ID),            new Column<IJobMaterial>(x => x.Dimensions.Quantity),            new Column<IJobMaterial>(x => x.Dimensions.Length),            new Column<IJobMaterial>(x => x.Dimensions.Width),            new Column<IJobMaterial>(x => x.Dimensions.Height),            new Column<IJobMaterial>(x => x.Dimensions.Weight)        };    }        [UserTracking(typeof(Job))]    [AutoEntity(typeof(JobMaterialUnionGenerator))]    public class JobMaterial : StockEntity, IJobMaterial, IJobMaterialSummary, IRemotable, IPersistent, IManyToMany<Job, Product>, ILicense<ProjectManagementLicense> /* , IDimensioned */    {                [NullEditor]        public JobLink Job { get; set; }        [EditorSequence(1)]        public override ProductLink Product { get; set; }        [EditorSequence(2)]        public ProductStyleLink Style { get; set; }        [NullEditor]        [Obsolete("Replaced with Dimensions", true)]        public double UnitSize { get; set; }        [EditorSequence(3)]        [RequiredColumn]        [DimensionsEditor(typeof(StockDimensions))]        public override StockDimensions Dimensions { get; set; }                [EditorSequence(4)]        [DoubleEditor]        [Aggregate(typeof(JobMaterialBOMAggregate))]        public double BillOfMaterials { get; set; }        [EditorSequence(5)]        [DoubleEditor]        [Aggregate(typeof(JobMaterialRequisitionsAggregate))]        public double Requisitions { get; set; }                [EditorSequence(6)]        [DoubleEditor]        [Aggregate(typeof(JobMaterialPickingListsAggregate))]        public double PickingLists { get; set; }        private class IssuedAggregate : ComplexFormulaGenerator<JobMaterial, double>        {            public override IComplexFormulaNode<JobMaterial, double> GetFormula() =>                Aggregate<StockMovement>(                    AggregateCalculation.Sum,                    x => x.Property(x => x.Issued),                    new Filter<StockMovement>(x => x.Type).IsEqualTo(StockMovementType.Issue))                .WithLink(x => x.Job.ID, x => x.Job.ID)                .WithLink(x => x.Product.ID, x => x.Product.ID)                .WithLink(x => x.Style.ID, x => x.Style.ID)                .WithLinks(Classes.Dimensions.GetLinks<StockMovement, JobMaterial>());        }        [EditorSequence(7)]        [ComplexFormula(typeof(IssuedAggregate))]        [DoubleEditor(Editable = Editable.Hidden)]        public double Issued { get; set; }                [EditorSequence(8)]        [Aggregate(typeof(JobMaterialReservedStockAggregate))]        [DoubleEditor(Editable = Editable.Hidden)]        public double ReservedStock { get; set; }                private class OnOrderAggregate : ComplexFormulaGenerator<JobMaterial, double>        {            public override IComplexFormulaNode<JobMaterial, double> GetFormula() =>                Formula(FormulaOperator.Add,                    TotalPO(),                    If<JobMaterial, string, double>(                        Property<JobMaterial, string>(x => x.Dimensions.Unit.Conversion),                        Condition.Equals,                        Constant<JobMaterial, string>(""),                        "")                    .Then(TotalExplodedAllocations())                    .Else(Formula(FormulaOperator.Divide,                        TotalExplodedAllocations(),                        Property(x => x.Dimensions.Value))));            /// <summary>            /// Add up order item in "exploded" units.            /// </summary>            private IComplexFormulaNode<JobMaterial, double> TotalPO() =>                // All PO items                Aggregate(AggregateCalculation.Sum,                    x => x.Property(x => x.Unallocated),                    new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue))                .WithLink(x => x.Job.ID, x => x.Job.ID)                .WithLink(x => x.Product.ID, x => x.Product.ID)                .WithLink(x => x.Style.ID, x => x.Style.ID)                .WithLinks(Classes.Dimensions.GetLinks<PurchaseOrderItem, JobMaterial>(x => x.Dimensions, x => x.Dimensions));            /// <summary>            /// Add up order item in "exploded" units.            /// </summary>            private IComplexFormulaNode<JobMaterial, double> TotalExplodedAllocations() =>                // All allocations.                Aggregate(AggregateCalculation.Sum,                    x => x.Property(x => x.Quantity),                    new Filter<PurchaseOrderItemAllocation>(x => x.Item.ReceivedDate).IsEqualTo(DateTime.MinValue))                .WithLink(x => x.Job.ID, x => x.Job.ID)                .WithLink(x => x.Item.Product.ID, x => x.Product.ID)                .WithLink(x => x.Item.Style.ID, x => x.Style.ID)                .WithLinks(Classes.Dimensions.GetLinks<PurchaseOrderItemAllocation, JobMaterial>(x => x.Item.Dimensions, x => x.Dimensions));        }        [EditorSequence(9)]        [DoubleEditor]        [ComplexFormula(typeof(OnOrderAggregate))]        public double OnOrder { get; set; }        /// <summary>        /// Calculated by JobSummary Grid.        /// </summary>        [EditorSequence(10)]        [DoubleEditor]        [ComplexFormula(typeof(CalculatedField))]        public double JobShortage { get; set; }                /// <summary>        /// Calculated by JobSummary Grid.        /// </summary>        [EditorSequence(11)]        [DoubleEditor]        [ComplexFormula(typeof(CalculatedField))]        public double FreeOnHand { get; set; }                /// <summary>        /// Calculated by JobSummary Grid.        /// </summary>        [EditorSequence(12)]        [DoubleEditor]        [ComplexFormula(typeof(CalculatedField))]        public double FreeOnOrder { get; set; }                /// <summary>        /// Calculated by JobSummary Grid.        /// </summary>        [EditorSequence(13)]        [DoubleEditor]        [ComplexFormula(typeof(CalculatedField))]        public double FreeStockTotal { get; set; }                /// <summary>        /// Calculated by JobSummary Grid.        /// </summary>        [EditorSequence(14)]        [DoubleEditor]        [ComplexFormula(typeof(CalculatedField))]        public double FreeStockShortage { get; set; }        private class CalculatedField : ComplexFormulaGenerator<JobMaterial, double>        {            public override IComplexFormulaNode<JobMaterial, double> GetFormula()                => Constant(0.0);        }    }}
 |