Kaynağa Gözat

Implemented JobScopeActivity and JobScopeCostCentre classes

frogsoftware 1 hafta önce
ebeveyn
işleme
2ec09cda8d

+ 3 - 0
prs.classes/Entities/Activity/Activity.cs

@@ -23,6 +23,9 @@ namespace Comal.Classes
         [EditorSequence(5)]
         public bool IsDefault { get; set; }
         
+        [EditorSequence(6)]
+        public bool IsJobActity { get; set; }
+        
         [EditorSequence(8)]
         [CodeEditor(Editable = Editable.Enabled)]
         public string PayrollID { get; set; }

+ 2 - 0
prs.classes/Entities/CostCentre/CostCentre.cs

@@ -12,6 +12,8 @@ namespace Comal.Classes
         public string Description { get; set; }
 
         public TaxCodeLink TaxCode { get; set; }
+        
+        public bool IsJobCostCentre { get; set; }
 
         public bool Hidden { get; set; }
 

+ 8 - 8
prs.classes/Entities/Job/JobActivity.cs

@@ -80,15 +80,15 @@ namespace Comal.Classes
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
     }
 
-    public class JobActivityBudgetAggregate : CoreAggregate<JobActivitySummary, JobActivity, TimeSpan>
+    public class JobActivityBudgetAggregate : CoreAggregate<JobActivitySummary, JobScopeActivity, TimeSpan>
     {
-        public override Expression<Func<JobActivity, TimeSpan>> Aggregate => x => x.Budget;
+        public override Expression<Func<JobScopeActivity, TimeSpan>> Aggregate => x => x.Hours;
 
-        public override Dictionary<Expression<Func<JobActivity, object>>, Expression<Func<JobActivitySummary, object>>> Links =>
-            new Dictionary<Expression<Func<JobActivity, object>>, Expression<Func<JobActivitySummary, object>>>()
+        public override Dictionary<Expression<Func<JobScopeActivity, object>>, Expression<Func<JobActivitySummary, object>>> Links =>
+            new Dictionary<Expression<Func<JobScopeActivity, object>>, Expression<Func<JobActivitySummary, object>>>()
             {
-                { JobActivity => JobActivity.JobLink.ID, JobActivitySummary => JobActivitySummary.JobLink.ID },
-                { JobActivity => JobActivity.ActivityLink.ID, JobActivitySummary => JobActivitySummary.ActivityLink.ID }
+                { JobScopeActivity => JobScopeActivity.Job.ID, JobActivitySummary => JobActivitySummary.JobLink.ID },
+                { JobScopeActivity => JobScopeActivity.Activity.ID, JobActivitySummary => JobActivitySummary.ActivityLink.ID }
             };
 
         public override AggregateCalculation Calculation => AggregateCalculation.Sum;
@@ -126,8 +126,8 @@ namespace Comal.Classes
     {
         protected override void Configure()
         {
-            AddTable<JobBillOfMaterialsActivity>(Filter<JobBillOfMaterialsActivity>.Where(x =>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue));
-            AddTable<JobActivity>();
+            AddTable(Filter<JobBillOfMaterialsActivity>.Where(x =>x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue));
+            AddTable<JobScopeActivity>();
             AddTable<Assignment>();
         }
 

+ 180 - 9
prs.classes/Entities/Job/JobScopes/JobScope.cs

@@ -56,6 +56,59 @@ namespace Comal.Classes
         public FormulaType Type => FormulaType.Virtual;
     }
 
+    public class JobScopeActivityCost : CoreAggregate<JobScope, JobScopeActivity, double>
+    {
+        public override Expression<Func<JobScopeActivity, double>> Aggregate => x => x.Cost;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<JobScopeActivity, object?>>, Expression<Func<JobScope, object?>>> Links =>
+            new Dictionary<Expression<Func<JobScopeActivity, object>>, Expression<Func<JobScope, object>>>()
+            {
+                { JobScopeActivity => JobScopeActivity.Scope.ID, JobScope => JobScope.ID }
+            };
+    }
+    
+    public class JobScopeActivitySell : CoreAggregate<JobScope, JobScopeActivity, double>
+    {
+        public override Expression<Func<JobScopeActivity, double>> Aggregate => x => x.Sell;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<JobScopeActivity, object?>>, Expression<Func<JobScope, object?>>> Links =>
+            new Dictionary<Expression<Func<JobScopeActivity, object>>, Expression<Func<JobScope, object>>>()
+            {
+                { JobScopeActivity => JobScopeActivity.Scope.ID, JobScope => JobScope.ID }
+            };
+    }
+    
+    public class JobScopeCostCentreCost : CoreAggregate<JobScope, JobScopeCostCentre, double>
+    {
+        public override Expression<Func<JobScopeCostCentre, double>> Aggregate => x => x.Cost;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<JobScopeCostCentre, object?>>, Expression<Func<JobScope, object?>>> Links =>
+            new Dictionary<Expression<Func<JobScopeCostCentre, object>>, Expression<Func<JobScope, object>>>()
+            {
+                { JobScopeCostCentre => JobScopeCostCentre.Scope.ID, JobScope => JobScope.ID }
+            };
+    }
+    
+    public class JobScopeCostCentreSell : CoreAggregate<JobScope, JobScopeCostCentre, double>
+    {
+        public override Expression<Func<JobScopeCostCentre, double>> Aggregate => x => x.Sell;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<JobScopeCostCentre, object?>>, Expression<Func<JobScope, object?>>> Links =>
+            new Dictionary<Expression<Func<JobScopeCostCentre, object>>, Expression<Func<JobScope, object>>>()
+            {
+                { JobScopeCostCentre => JobScopeCostCentre.Scope.ID, JobScope => JobScope.ID }
+            };
+    }
+
+    
     public interface IJobScopedItem
     {
         JobLink JobLink { get; set; }
@@ -110,35 +163,153 @@ namespace Comal.Classes
         [LookupDefinition(typeof(JobScopeLookup))]
         [EditorSequence(4)]
         public JobScopeLink Parent { get; set; }
+
+        private class MaterialsCostAggregate : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Aggregate<JobScopeCostCentre>(AggregateCalculation.Sum, x => x.Property(p => p.Cost))
+                    .WithLink(x => x.Scope.ID, x => x.ID);
+        }
         
-        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Default)]
+        [ComplexFormula(typeof(MaterialsCostAggregate))]
         [EditorSequence(5)]
+        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double MaterialsCost { get; set; }
+        
+        private class MaterialsSellAggregate : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Aggregate<JobScopeCostCentre>(AggregateCalculation.Sum, x => x.Property(p => p.Sell))
+                    .WithLink(x => x.Scope.ID, x => x.ID);
+        }
+        
+        [ComplexFormula(typeof(MaterialsSellAggregate))]
+        [EditorSequence(6)]
+        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double MaterialsSell { get; set; }
+        
+        private class MaterialsMarginFormula : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Formula(FormulaOperator.Divide,
+                    Formula(FormulaOperator.Subtract, Property(x => x.MaterialsSell), Property(x => x.MaterialsCost)),
+                        Property(x => x.MaterialsSell)
+                    );
+        }
+        
+        [ComplexFormula(typeof(MaterialsMarginFormula))]
+        [EditorSequence(7)]
+        [DoubleEditor(Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double MaterialMargin { get; set; }
+        
+        private class MaterialsMarkupFormula : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Formula(FormulaOperator.Divide,
+                    Formula(FormulaOperator.Subtract, Property(x => x.MaterialsSell), Property(x => x.MaterialsCost)),
+                    Property(x => x.MaterialsCost)
+                );
+        }
+        
+        [ComplexFormula(typeof(MaterialsMarkupFormula))]
+        [EditorSequence(8)]
+        [DoubleEditor(Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double MaterialMarkup { get; set; }
+        
+        private class LabourCostAggregate : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Aggregate<JobScopeActivity>(AggregateCalculation.Sum, x => x.Property(p => p.Cost))
+                    .WithLink(x => x.Scope.ID, x => x.ID);
+        }
+        
+        [ComplexFormula(typeof(LabourCostAggregate))]
+        [EditorSequence(9)]
+        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double LabourCost { get; set; }
+        
+        private class LabourSellAggregate : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Aggregate<JobScopeActivity>(AggregateCalculation.Sum, x => x.Property(p => p.Sell))
+                    .WithLink(x => x.Scope.ID, x => x.ID);
+        }
+        
+        [ComplexFormula(typeof(LabourSellAggregate))]
+        [EditorSequence(10)]
+        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double LabourSell { get; set; }
+        
+        private class LabourMarginFormula : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Formula(FormulaOperator.Divide,
+                    Formula(FormulaOperator.Subtract, Property(x => x.LabourSell), Property(x => x.LabourCost)),
+                        Property(x => x.LabourSell)
+                    );
+        }
+        
+        [ComplexFormula(typeof(LabourMarginFormula))]
+        [EditorSequence(11)]
+        [DoubleEditor(Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double LabourMargin { get; set; }
+        
+        private class LabourMarkupFormula : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Formula(FormulaOperator.Divide,
+                    Formula(FormulaOperator.Subtract, Property(x => x.LabourSell), Property(x => x.LabourCost)),
+                    Property(x => x.LabourCost)
+                );
+        }
+        
+        [ComplexFormula(typeof(LabourMarkupFormula))]
+        [EditorSequence(12)]
+        [DoubleEditor(Visible = Visible.Optional, Editable =  Editable.Hidden)]
+        public double LabourMarkup { get; set; }
+        
+        
+        [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Default)]
+        [EditorSequence(13)]
         public double ExTax { get; set; }
 
-        [EditorSequence(6)]
+        [EditorSequence(14)]
         public TaxCodeLink TaxCode { get; set; }
         
         [CurrencyEditor(Summary = Summary.Sum, Visible = Visible.Optional)]
-        [EditorSequence(7)]
+        [EditorSequence(15)]
         public double IncTax { get; set; }
 
-        [EditorSequence(8)]
+        [EditorSequence(16)]
         public JobScopeStatusLink Status { get; set; }
         
-        [CurrencyEditor(Visible = Visible.Default, Editable = Editable.Hidden, Summary = Summary.Sum)]
+        private class InvoiceExTaxAggregate : ComplexFormulaGenerator<JobScope, double>
+        {
+            public override IComplexFormulaNode<JobScope, double> GetFormula()
+                => Aggregate<InvoiceLine>(AggregateCalculation.Sum, x => x.Property(p => p.ExTax))
+                    .WithLink(x => x.Scope.ID, x => x.ID);
+        }
+        
+        [ComplexFormula(typeof(LabourSellAggregate))]
+        
         [Aggregate(typeof(JobScopeInvoiceExTax))]
+        [EditorSequence(17)]
+        [CurrencyEditor(Visible = Visible.Default, Editable = Editable.Hidden, Summary = Summary.Sum)]
         public double InvoiceExTax { get; set; }
 
-        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(JobScopeInvoiceTax))]
+        [EditorSequence(18)]
+        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         public double InvoiceTax { get; set; }
 
-        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(JobScopeInvoiceIncTax))]
+        [EditorSequence(19)]
+        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         public double InvoiceIncTax { get; set; }
 
-        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Formula(typeof(JobScopeUninvoicedExTax))]
+        [EditorSequence(20)]
+        [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         public double UninvoiceIncTax { get; set; }
 
         private class MaterialsExTaxFormula : ComplexFormulaGenerator<JobScope, double>
@@ -153,7 +324,7 @@ namespace Comal.Classes
         [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         [ComplexFormula(typeof(MaterialsExTaxFormula))]
         public double MaterialsExTax { get; set; }
-
+        
         private class UninvoicedMaterial : ComplexFormulaGenerator<JobScope, double>
         {
             public override IComplexFormulaNode<JobScope, double> GetFormula() =>

+ 37 - 0
prs.classes/Entities/Job/JobScopes/JobScopeActivity.cs

@@ -0,0 +1,37 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class JobScopeActivity : Entity, IRemotable, IPersistent, IOneToMany<Job>,  IOneToMany<JobScope>, ILicense<ProjectManagementLicense>
+    {
+        
+        [NullEditor]
+        [EntityRelationship(DeleteAction.Cascade)]
+        public JobLink Job { get; set; }
+        
+        [NullEditor]
+        [EntityRelationship(DeleteAction.Cascade)]
+        public JobScopeLink Scope { get; set; }
+        
+        [EditorSequence(1)]
+        [EntityRelationship(DeleteAction.SetNull)]
+        public AssignmentActivityLink Activity { get; set; }
+        
+        [EditorSequence(2)]
+        public TimeSpan Hours { get; set; }
+        
+        [EditorSequence(3)]
+        public double Rate { get; set; }
+        
+        [EditorSequence(4)]
+        public double Cost { get; set; }
+        
+        [EditorSequence(5)]
+        public double Markup { get; set; }
+        
+        [EditorSequence(6)]
+        public double Sell { get; set; }
+        
+    }
+}

+ 30 - 0
prs.classes/Entities/Job/JobScopes/JobScopeCostCentre.cs

@@ -0,0 +1,30 @@
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class JobScopeCostCentre : Entity, IRemotable, IPersistent, IOneToMany<Job>,  IOneToMany<JobScope>, ILicense<ProjectManagementLicense>
+    {
+        
+        [NullEditor]
+        [EntityRelationship(DeleteAction.Cascade)]
+        public JobLink Job { get; set; }
+        
+        [NullEditor]
+        [EntityRelationship(DeleteAction.Cascade)]
+        public JobScopeLink Scope { get; set; }
+    
+        [EditorSequence(1)]
+        [EntityRelationship(DeleteAction.SetNull)]
+        public CostCentreLink CostCentre { get; set; }
+    
+        [EditorSequence(2)]
+        public double Cost { get; set; }
+    
+        [EditorSequence(3)]
+        public double Markup { get; set; }
+    
+        [EditorSequence(4)]
+        public double Sell { get; set; }
+    
+    }
+}

+ 3 - 0
prs.classes/Entities/Job/JobScopes/JobScopeLink.cs

@@ -16,6 +16,9 @@ namespace Comal.Classes
         [EditorSequence(1)]
         public override Guid ID { get; set; }
         
+        [NullEditor]
+        public InternalJobLink Job { get; set; }
+        
         [CodeEditor(Visible = Visible.Default, Editable = Editable.Hidden)]
         [EditorSequence(2)]
         public string Number { get; set; }

+ 108 - 0
prs.desktop/Panels/Jobs/JobScopes/JobScopeActivityGrid.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Windows.Controls;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.DynamicGrid;
+
+namespace PRSDesktop;
+
+public class JobScopeActivityGrid : DynamicDataGrid<JobScopeActivity>, IJobScopeGrid
+{
+    public JobScope? Scope { get; set; }
+
+    protected override void Init()
+    {
+        base.Init();
+        HiddenColumns.Add(x=>x.Activity.ID);
+        HiddenColumns.Add(x => x.Hours);
+        AddButton("Reset", null, ResetActivities);
+        if (Security.CanView<Activity>())
+            AddButton("Manage", null, ManageActivities, DynamicGridButtonPosition.Right);
+    }
+    
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+        options.SelectColumns = true;
+    }
+
+    public override DynamicGridColumns GenerateColumns()
+    {
+        var _columns = new DynamicGridColumns();
+        _columns.Add<JobScopeActivity>(x => x.Activity.Code, 120, "Code", "", Alignment.MiddleLeft);
+        _columns.Add<JobScopeActivity>(x => x.Activity.Description, 0, "Description", "", Alignment.MiddleLeft);
+        _columns.Add<JobScopeActivity>(x => x.Hours, 70, "Hours", "hhh:mm", Alignment.MiddleCenter);
+        _columns.Add<JobScopeActivity>(x => x.Rate, 70, "Rate", "F2", Alignment.MiddleCenter);
+        _columns.Add<JobScopeActivity>(x => x.Cost, 70, "Cost", "F2", Alignment.MiddleCenter);
+        _columns.Add<JobScopeActivity>(x => x.Markup, 70, "M/Up", "F2", Alignment.MiddleCenter);
+        _columns.Add<JobScopeActivity>(x => x.Sell, 70, "Sell", "F2", Alignment.MiddleCenter);
+        return _columns;
+    }
+
+    protected override void Reload(
+        Filters<JobScopeActivity> criteria, Columns<JobScopeActivity> columns, ref SortOrder<JobScopeActivity>? sort,
+        CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        if ((Scope == null) || (Scope.ID == Guid.Empty))
+            criteria.Add(Filter.None<JobScopeActivity>());
+        else
+            criteria.Add(Filter<JobScopeActivity>.Where(x => x.Scope.ID).IsEqualTo(Scope.ID));
+        base.Reload(criteria, columns, ref sort, token, action);
+    }
+
+    protected override bool CanCreateItems()
+    {
+        return base.CanCreateItems() && Scope != null;
+    }
+
+    public override JobScopeActivity CreateItem()
+    {
+        var _result = base.CreateItem();
+        _result.Scope.Synchronise(Scope ?? new JobScope());
+        return _result;
+    }
+    
+    private bool ManageActivities(Button button, CoreRow[] rows)
+    {
+        var list = new MasterList(typeof(Activity));
+        list.ShowDialog();
+        return true;
+    }
+
+    private bool ResetActivities(Button button, CoreRow[] rows)
+    {
+        var _current = ExtractValues(x => x.Activity.ID, Selection.All).ToArray();
+        
+        var _masters = Client.Query(
+                Filter<Activity>.Where(x => x.IsJobActity).IsEqualTo(true),
+                Columns.None<Activity>().Add(x => x.ID)
+            ).Rows
+            .Select(row =>
+                {
+                    var result = new JobScopeActivity();
+                    result.Job.ID = Scope.Job.ID;
+                    result.Scope.ID = Scope.ID;
+                    result.Activity.ID = row.Get<Activity, Guid>(x => x.ID);
+                    return result;
+                }
+            ).ToArray();
+        
+        var _new = _masters.Where(a => _current.All(x => x != a.Activity.ID)).ToList();
+        if (_new.Any())
+            Client.Save(_new, "Updated from Master Activity List");
+        
+        var _unused = Data.ToObjects<JobScopeActivity>()
+            .Where(x => x.Hours == TimeSpan.Zero)
+            .Where(x => _masters.All(m => m.Activity.ID != x.Activity.ID))
+            .ToArray();
+        if (_unused.Any())
+            Client.Delete(_unused, "Deleted from Master Activity List");
+        
+        return _new.Any() || _unused.Any();
+    }
+
+    
+}

+ 105 - 0
prs.desktop/Panels/Jobs/JobScopes/JobScopeCostCentreGrid.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Windows.Controls;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.DynamicGrid;
+
+namespace PRSDesktop;
+
+public class JobScopeCostCentreGrid : DynamicDataGrid<JobScopeCostCentre>, IJobScopeGrid
+{
+    public JobScope? Scope { get; set; }
+    
+    protected override void Init()
+    {
+        base.Init();
+        HiddenColumns.Add(x=>x.CostCentre.ID);
+        HiddenColumns.Add(x => x.Cost);
+        HiddenColumns.Add(x => x.Sell);
+        AddButton("Reset", null, ResetGLCodes);
+        if (Security.CanView<CostCentre>())
+            AddButton("Manage", null, ManageCostCentres, DynamicGridButtonPosition.Right);
+    }
+    
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+        options.SelectColumns = true;
+    }
+    
+    public override DynamicGridColumns GenerateColumns()
+    {
+        var _columns = new DynamicGridColumns();
+        _columns.Add<JobScopeCostCentre>(x => x.CostCentre.Code, 120, "Code", "", Alignment.MiddleLeft);
+        _columns.Add<JobScopeCostCentre>(x => x.CostCentre.Description, 0, "Description", "", Alignment.MiddleLeft);
+        _columns.Add<JobScopeCostCentre>(x => x.Cost, 70, "Cost", "F2", Alignment.MiddleCenter);
+        _columns.Add<JobScopeCostCentre>(x => x.Markup, 70, "M/Up", "F2", Alignment.MiddleCenter);
+        _columns.Add<JobScopeCostCentre>(x => x.Sell, 70, "Sell", "F2", Alignment.MiddleCenter);
+        return _columns;
+    }
+
+    protected override void Reload(
+        Filters<JobScopeCostCentre> criteria, Columns<JobScopeCostCentre> columns, ref SortOrder<JobScopeCostCentre>? sort,
+        CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        if ((Scope == null) || (Scope.ID == Guid.Empty))
+            criteria.Add(Filter.None<JobScopeCostCentre>());
+        else
+            criteria.Add(Filter<JobScopeCostCentre>.Where(x => x.Scope.ID).IsEqualTo(Scope.ID));
+        base.Reload(criteria, columns, ref sort, token, action);
+    }
+
+    protected override bool CanCreateItems()
+    {
+        return base.CanCreateItems() && Scope != null;
+    }
+
+    public override JobScopeCostCentre CreateItem()
+    {
+        var _result = base.CreateItem();
+        _result.Scope.Synchronise(Scope ?? new JobScope());
+        return _result;
+    }
+    
+    private bool ManageCostCentres(Button button, CoreRow[] rows)
+    {
+        var list = new MasterList(typeof(CostCentre));
+        list.ShowDialog();
+        return true;
+    }
+
+    private bool ResetGLCodes(Button button, CoreRow[] rows)
+    {
+        var _current = ExtractValues(x => x.CostCentre.ID, Selection.All).ToArray();
+        
+        var _masters = Client.Query(
+                Filter<CostCentre>.Where(x => x.IsJobCostCentre).IsEqualTo(true),
+                Columns.None<CostCentre>().Add(x => x.ID)
+            ).Rows
+            .Select(row =>
+                {
+                    var result = new JobScopeCostCentre();
+                    result.Job.ID = Scope.Job.ID;
+                    result.Scope.ID = Scope.ID;
+                    result.CostCentre.ID = row.Get<CostCentre, Guid>(x => x.ID);
+                    return result;
+                }
+            ).ToArray();
+        
+        var _new = _masters.Where(a => _current.All(x => x != a.CostCentre.ID)).ToList();
+        if (_new.Any())
+            Client.Save(_new, "Updated from Master CostCentre List");
+        
+        var _unused = Data.ToObjects<JobScopeCostCentre>()
+            .Where(x => x.Cost.IsEffectivelyEqual(0.0F) && x.Sell.IsEffectivelyEqual(0.0F))
+            .Where(x => _masters.All(m => m.CostCentre.ID != x.CostCentre.ID))
+            .ToArray();
+        if (_unused.Any())
+            Client.Delete(_unused, "Deleted from Master CostCentre List");
+        
+        return _new.Any() || _unused.Any();
+    }
+}

+ 4 - 2
prs.desktop/Panels/Jobs/JobScopes/JobScopePanel.xaml

@@ -7,7 +7,7 @@
              xmlns:dynamicGrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
              mc:Ignorable="d"
              d:DesignHeight="300" d:DesignWidth="300">
-    <dynamicGrid:DynamicSplitPanel Anchor="Detail" AnchorWidth="500" AllowableViews="Combined" View="Combined">
+    <dynamicGrid:DynamicSplitPanel Anchor="Master" AnchorWidth="600"  AllowableViews="Combined" View="Combined">
         
         <dynamicGrid:DynamicSplitPanel.Header>
             <Border BorderBrush="Gray" BorderThickness="0.75">
@@ -33,10 +33,12 @@
         <dynamicGrid:DynamicSplitPanel.Detail>
             <dynamicGrid:DynamicTabControl TabStripPlacement="Bottom" x:Name="_pages" SelectionChanged="_pages_OnSelectionChanged">
                 <dynamicGrid:DynamicTabControl.Items>
+                    <dynamicGrid:DynamicTabItem Header="Activities" x:Name="_activities" />
+                    <dynamicGrid:DynamicTabItem Header="Cost Centres" x:Name="_costCentres" />
                     <dynamicGrid:DynamicTabItem Header="Picking Lists" x:Name="_requisitionItems" />
                     <dynamicGrid:DynamicTabItem Header="Manufacturing" x:Name="_manufacturing" />
                     <dynamicGrid:DynamicTabItem Header="Delivery Items" x:Name="_deliveryItems" />
-                    <dynamicGrid:DynamicTabItem Header="Activities" x:Name="_assignments" />
+                    <dynamicGrid:DynamicTabItem Header="Assignments" x:Name="_assignments" />
                     <dynamicGrid:DynamicTabItem Header="Documents" x:Name="_documents" />
                     <dynamicGrid:DynamicTabItem Header="Tasks" x:Name="_kanbans" />
                     <dynamicGrid:DynamicTabItem Header="Forms" x:Name="_forms" />

+ 12 - 2
prs.desktop/Panels/Jobs/JobScopes/JobScopePanel.xaml.cs

@@ -16,7 +16,9 @@ public partial class JobScopePanel : UserControl, IPanel<JobScope>, IMasterDetai
     
     private enum PageIndex
     {
-        RequisitionItems = 00,
+        Activities = 00,
+        GLCodes,
+        RequisitionItems,
         ManufacturingPackets,
         DeliveryItems,
         Assignments,
@@ -26,7 +28,9 @@ public partial class JobScopePanel : UserControl, IPanel<JobScope>, IMasterDetai
     }
             
     private int _currentPage = -1;
-    
+
+    private JobScopeActivityGrid? _activityGrid;
+    private JobScopeCostCentreGrid? _costCentreGrid;
     private JobScopeRequisitionItemGrid? _requisitionItemsGrid;
     private JobScopeAssignmentGrid? _assignmentGrid;
     private JobScopeDocumentGrid? _documentGrid;
@@ -75,6 +79,12 @@ public partial class JobScopePanel : UserControl, IPanel<JobScope>, IMasterDetai
         var page = (PageIndex)_pages.SelectedIndex;
         switch (page)
         {
+            case PageIndex.Activities :
+                RefreshGrid(_activities, ref _activityGrid, scope);
+                break;
+            case PageIndex.GLCodes :
+                RefreshGrid(_costCentres, ref _costCentreGrid, scope);
+                break;
             case PageIndex.RequisitionItems :
                 RefreshGrid(_requisitionItems, ref _requisitionItemsGrid, scope);
                 break;

+ 48 - 24
prs.stores/ActivityStore.cs

@@ -1,33 +1,57 @@
 using Comal.Classes;
+using InABox.Core;
 
 namespace Comal.Stores
 {
     public class ActivityStore : BaseStore<Activity>
     {
-        //protected override void AfterSave(Activity entity)
-        //{
-        //    base.AfterSave(entity);
 
-        //    if (true) //entity.HasOriginalValue<Activity, Guid>(x => x.ID))
-        //    {
-        //        CoreTable roles = Provider.Query<Role>(null, new InABox.Core.Columns<Role>(x => x.ID), null);
-        //        CoreTable maps = Provider.Query<RoleActivity>(null, new Columns<Classes.RoleActivity>(x => x.ID, x => x.Role.ID, x => x.Activity.ID), null);
-        //        List<RoleActivity> updates = new List<RoleActivity>();
-        //        foreach (var role in roles.Rows)
-        //        {
-        //            Guid rid = role.Get<Role, Guid>(c => c.ID);
-        //            if (!maps.Rows.Any(r => r.Get<RoleActivity, Guid>(c => c.Activity.ID).Equals(entity.ID) && r.Get<RoleActivity, Guid>(c => c.Role.ID).Equals(rid)))
-        //            {
-        //                RoleActivity map = new RoleActivity();
-        //                map.Role.ID = role.Get<Role, Guid>(c => c.ID);
-        //                map.Activity.ID = entity.ID;
-        //                map.Enabled = true;
-        //                updates.Add(map);
-        //            }
-        //        }
-        //        if (updates.Any())
-        //            FindSubStore<RoleActivity>().Save(updates, "");
-        //    }
-        //}
+        private void UpdateJobScopeActivities(IEnumerable<Activity> entities)
+        {
+            var _activities = entities as Activity[] ?? entities.ToArray();
+            
+            var _adds = _activities
+                .Where(x => x.HasOriginalValue(x => x.IsJobActity))
+                .Where(x => x.IsJobActity).ToArray();
+            if (_adds.Any())
+            {
+                var _new = Provider.Query(
+                    Filter<JobScope>.Where(x => x.ID).NotInQuery(Filter<JobScopeActivity>.Where(x => x.Activity.ID).InList(_adds.Select(x=>x.ID).ToArray()), x => x.Scope.ID),
+                    Columns.None<JobScope>().Add(x => x.ID).Add(x=>x.Job.ID)
+                ).Rows.Select(r =>
+                {
+                    return _activities.Select(e =>
+                    {
+                        var _insert = new JobScopeActivity();
+                        _insert.Job.ID = r.Get<JobScope, Guid>(x => x.ID);
+                        _insert.Scope.ID = r.Get<JobScope, Guid>(x => x.ID);
+                        _insert.Activity.ID = e.ID;
+                        return _insert;
+                    });
+                }).SelectMany(x=>x).ToArray();
+                Provider.Save(_new);
+            }
+            
+            var _removes = _activities
+                .Where(x => x.HasOriginalValue(x => x.IsJobActity))
+                .Where(x => !x.IsJobActity).ToArray();
+            if (_removes.Any())
+            {
+                var _deletes = Provider.Query(
+                    Filter<JobScopeActivity>.Where(x => x.Activity.ID).InList(_removes.Select(x => x.ID).ToArray())
+                        .And(x => x.Hours).IsEqualTo(TimeSpan.Zero),
+                    Columns.None<JobScopeActivity>().Add(x => x.ID)
+                ).ToArray<JobScopeActivity>();
+                Provider.Delete(_deletes,this.UserID);
+            }
+            
+        }
+        
+        protected override void AfterSave(IEnumerable<Activity> entities)
+        {
+            base.AfterSave(entities);
+            UpdateJobScopeActivities(entities);
+        }
+        
     }
 }

+ 57 - 0
prs.stores/CostCentreStore.cs

@@ -0,0 +1,57 @@
+using Comal.Classes;
+using InABox.Core;
+
+namespace Comal.Stores;
+
+public class CostCentreStore : BaseStore<CostCentre>
+{
+
+    private void UpdateJobScopeCostCentres(IEnumerable<CostCentre> entities)
+    {
+        var _entities = entities as CostCentre[] ?? entities.ToArray();
+            
+        var _adds = _entities
+            .Where(x => x.HasOriginalValue(x => x.IsJobCostCentre))
+            .Where(x => x.IsJobCostCentre).ToArray();
+        if (_adds.Any())
+        {
+            var _new = Provider.Query(
+                Filter<JobScope>.Where(x => x.ID).NotInQuery(Filter<JobScopeCostCentre>.Where(x => x.CostCentre.ID).InList(_adds.Select(x=>x.ID).ToArray()), x => x.Scope.ID),
+                Columns.None<JobScope>().Add(x => x.ID).Add(x=>x.Job.ID)
+            ).Rows.Select(r =>
+            {
+                return _entities.Select(e =>
+                {
+                    var _insert = new JobScopeCostCentre();
+                    _insert.Job.ID = r.Get<JobScope, Guid>(x => x.ID);
+                    _insert.Scope.ID = r.Get<JobScope, Guid>(x => x.ID);
+                    _insert.CostCentre.ID = e.ID;
+                    return _insert;
+                });
+            }).SelectMany(x=>x).ToArray();
+            Provider.Save(_new);
+        }
+            
+        var _removes = _entities
+            .Where(x => x.HasOriginalValue(x => x.IsJobCostCentre))
+            .Where(x => !x.IsJobCostCentre).ToArray();
+        if (_removes.Any())
+        {
+            var _deletes = Provider.Query(
+                Filter<JobScopeCostCentre>.Where(x => x.CostCentre.ID).InList(_removes.Select(x => x.ID).ToArray())
+                    .And(x => x.Cost).IsEqualTo(0.0F)
+                    .And(x=>x.Sell).IsEqualTo(0.0F),
+                Columns.None<JobScopeCostCentre>().Add(x => x.ID)
+            ).ToArray<JobScopeCostCentre>();
+            Provider.Delete(_deletes,this.UserID);
+        }
+            
+    }
+        
+    protected override void AfterSave(IEnumerable<CostCentre> entities)
+    {
+        base.AfterSave(entities);
+        UpdateJobScopeCostCentres(entities);
+    }
+    
+}

+ 37 - 0
prs.stores/JobScopeStore.cs

@@ -26,4 +26,41 @@ public class JobScopeStore : BaseStore<JobScope>
                 entity.TaxCode.CopyFrom(tax);
         }
     }
+    
+    protected override void AfterSave(IEnumerable<JobScope> entities)
+    {
+        var _entities = entities as JobScope[] ?? entities.ToArray();
+        
+        base.AfterSave(_entities);
+        
+        var _activities = Provider.Query(Filter<Activity>.Where(x=>x.IsJobActity).IsEqualTo(true), Columns.None<Activity>().Add(x=>x.ID))
+            .Rows.Select(row =>
+            {
+                return _entities.Where(x=>x.HasOriginalValue(e=>e.ID)).Select(e =>
+                {
+                    var _result = new JobScopeActivity();                
+                    _result.Job.ID = e.Job.ID;
+                    _result.Scope.ID = e.ID;
+                    _result.Activity.ID = row.Get<Activity, Guid>(x => x.ID);
+                    return _result;
+                });
+            });
+        Provider.Save(_activities.SelectMany(x=>x));
+        
+        var _costCentres = Provider.Query(Filter<CostCentre>.Where(x => x.IsJobCostCentre).IsEqualTo(true),
+                Columns.None<CostCentre>().Add(x => x.ID))
+            .Rows.Select(row =>
+            {
+                return _entities.Where(x=>x.HasOriginalValue(e=>e.ID)).Select(e =>
+                {
+                    var _result = new JobScopeCostCentre();
+                    _result.Job.ID = e.Job.ID;
+                    _result.Scope.ID = e.ID;
+                    _result.CostCentre.ID = row.Get<GLCode, Guid>(x => x.ID);
+                    return _result;
+                });
+
+            });
+        Provider.Save(_costCentres.SelectMany(x=>x));
+    }
 }