Quellcode durchsuchen

Initial Stock Summary Screen
Cleanups / Preparation for PRS 7

Frank van den Bos vor 2 Jahren
Ursprung
Commit
d02cc3999b

+ 1 - 1
prs.classes/Entities/Employee/EmployeeRoster.cs

@@ -3,7 +3,7 @@ using InABox.Core;
 
 namespace Comal.Classes
 {
-    public class EmployeeRoster : Entity, IRemotable, IPersistent
+    public class EmployeeRoster : Entity, IRemotable, IPersistent, ILicense<CoreLicense>
     {
         [EditorSequence(1)]
         [UniqueCodeEditor(Visible = Visible.Default, Editable = Editable.Enabled)]

+ 1 - 1
prs.classes/Entities/Job/DocumentSet/JobDocumentSetTag.cs

@@ -13,7 +13,7 @@ namespace Comal.Classes
         Area
     }
     
-    public class JobDocumentSetTag : Entity, IRemotable, IPersistent
+    public class JobDocumentSetTag : Entity, IRemotable, IPersistent, ILicense<ProjectManagementLicense>
     {
         [EnumLookupEditor(typeof(JobDocumentSetTagType), Visible = Visible.Default, Width = 100)]
         [EditorSequence(1)]

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

@@ -104,7 +104,7 @@ namespace Comal.Classes
     }
 
     [AutoEntity(typeof(JobActivityAssignmentUnionGenerator))]
-    public class JobActivitySummary : Entity, IRemotable, IPersistent, IJobActivity, IOneToMany<Job>
+    public class JobActivitySummary : Entity, IRemotable, IPersistent, IJobActivity, IOneToMany<Job>, ILicense<ProjectManagementLicense>
     {
         [NullEditor]
         public JobLink JobLink { get; set; }

+ 0 - 45
prs.classes/Entities/Job/Materials/JobMaterialLink.cs

@@ -1,45 +0,0 @@
-using System;
-using System.ComponentModel;
-using InABox.Core;
-
-namespace Comal.Classes
-{
-    // Because JobMaterials is now auto-generated (YAY Views)
-    // This class is no longer used (no ID)
-    // [Obsolete("JobMaterials Is Now a View")]
-    // public class JobMaterialLink : EntityLink<JobMaterial>, IJobMaterial/* , IDimensioned */
-    // {
-    //     public override Guid ID { get; set; }
-    //
-    //     public JobLink Job { get; set; }
-    //
-    //     public ProductLink Product { get; set; }
-    //
-    //     public ProductStyleLink Style { get; set; }
-    //
-    //     [NullEditor]
-    //     [Obsolete("Replaced with Dimensions", true)]
-    //     public double UnitSize { get; set; }
-    //
-    //     public StockDimensions Dimensions { get; set; }
-    //     
-    //     protected override void Init()
-    //     {
-    //         base.Init();
-    //         Job = new JobLink();
-    //         Product = new ProductLink();
-    //         //Product.PropertyChanged += Product_PropertyChanged;
-    //         Style = new ProductStyleLink();
-    //         Dimensions = new StockDimensions();
-    //     }
-    //
-    //     // private void Product_PropertyChanged(object sender, PropertyChangedEventArgs e)
-    //     // {
-    //     //     if (new Column<Product>(x=>x.UnitSize).IsEqualTo(e.PropertyName))
-    //     //         UnitSize = Product.UnitSize;
-    //     //     else if (new Column<Product>(x=>x.DefaultStyle).IsEqualTo(e.PropertyName))
-    //     //         Style = Product.DefaultStyle;
-    //     // }
-    //     
-    // }
-}

+ 1 - 1
prs.classes/Entities/LeaveRequest/EmployeeLeaveSummary.cs

@@ -25,7 +25,7 @@ namespace Comal.Classes
     
     [Obsolete("Not Used at this stage", false)]
     [AutoEntity(typeof(EmployeeLeaveSummaryGenerator))]
-    public class EmployeeLeaveSummary : Entity, IRemotable, IPersistent, IEmployeeLeaveRequest
+    public class EmployeeLeaveSummary : Entity, IRemotable, IPersistent, IEmployeeLeaveRequest, ILicense<LeaveManagementLicense>
     {
         public EmployeeLink EmployeeLink { get; set; }
         public LeaveRequestActivityLink LeaveType { get; set; }

+ 1 - 1
prs.classes/Entities/LeaveRequest/EmployeeStandardLeave.cs

@@ -56,7 +56,7 @@ namespace Comal.Classes
     
     [AutoEntity(typeof(EmployeeStandardLeaveGenerator))]
     [Obsolete("Not Used at this stage", false)]
-    public class EmployeeStandardLeave : Entity, IRemotable, IPersistent, IEmployeeLeaveRequest
+    public class EmployeeStandardLeave : Entity, IRemotable, IPersistent, IEmployeeLeaveRequest, ILicense<LeaveManagementLicense>
     {
         public EmployeeLink EmployeeLink { get; set; }
         public LeaveRequestActivityLink LeaveType { get; set; }

+ 1 - 1
prs.classes/Entities/Scan/Scan.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace Comal.Classes
 {
-    public class Scan : Entity, IRemotable, IPersistent
+    public class Scan : Entity, IRemotable, IPersistent, ILicense<DocumentScannerLicense>
     {
         [CheckBoxEditor]
         public bool Processed { get; set; }

+ 1 - 1
prs.classes/Entities/Scan/ScanTag.cs

@@ -14,7 +14,7 @@ namespace Comal.Classes
         public string AppliesTo { get; set; }
     }
 
-    public class ScanTag : Entity, IScanTag, IRemotable, IPersistent
+    public class ScanTag : Entity, IScanTag, IRemotable, IPersistent, ILicense<DocumentScannerLicense>
     {
         [EditorSequence(1)]
         [TextBoxEditor]

+ 2 - 2
prs.classes/Entities/Scan/ScanTagDistributionEmployee.cs

@@ -31,7 +31,7 @@ namespace Comal.Classes
     }
 
     [AutoEntity(typeof(ScanTagRoleEmployeeCrossGenerator))]
-    public class ScanTagRoleEmployee : Entity, IRemotable, IPersistent, IScanTagDistributionEmployee
+    public class ScanTagRoleEmployee : Entity, IRemotable, IPersistent, IScanTagDistributionEmployee, ILicense<DocumentScannerLicense>
     {
         public EmployeeLink Employee { get; set; }
         public ScanTagLink Tag { get; set; }
@@ -70,7 +70,7 @@ namespace Comal.Classes
     }
 
     [AutoEntity(typeof(ScanTagDistributionUnionGenerator))]
-    public class ScanTagDistributionEmployee : Entity, IRemotable, IPersistent, IScanTagDistributionEmployee
+    public class ScanTagDistributionEmployee : Entity, IRemotable, IPersistent, IScanTagDistributionEmployee, ILicense<DocumentScannerLicense>
     {
         public EmployeeLink Employee { get; set; }
         public ScanTagLink Tag { get; set; }

+ 1 - 1
prs.classes/Entities/Scan/ScanTagEmployee.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace Comal.Classes
 {
-    public class ScanTagEmployee : Entity, IRemotable, IPersistent, IManyToMany<ScanTag, Employee>, IScanTagDistributionEmployee
+    public class ScanTagEmployee : Entity, IRemotable, IPersistent, IManyToMany<ScanTag, Employee>, IScanTagDistributionEmployee, ILicense<DocumentScannerLicense>
     {
         [EntityRelationship(DeleteAction.Cascade)]
         public ScanTagLink Tag { get; set; }

+ 1 - 1
prs.classes/Entities/Scan/ScanTagRole.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace Comal.Classes
 {
-    public class ScanTagRole : Entity, IRemotable, IPersistent, IManyToMany<ScanTag, Role>
+    public class ScanTagRole : Entity, IRemotable, IPersistent, IManyToMany<ScanTag, Role>, ILicense<DocumentScannerLicense>
     {
         [EntityRelationship(DeleteAction.Cascade)]
         public ScanTagLink Tag { get; set; }

+ 263 - 0
prs.classes/Entities/Stock/StockSummary.cs

@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class StockSummaryBOMAggregate : CoreAggregate<StockSummary, 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<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<JobBillOfMaterialsItem, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Product.ID, StockSummary => StockSummary.Product.ID },
+                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Style.ID, StockSummary => StockSummary.Style.ID },
+                { JobBillOfMaterialsItem => JobBillOfMaterialsItem.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+
+        public override Filter<JobBillOfMaterialsItem> Filter =>
+            new Filter<JobBillOfMaterialsItem>(x => x.BillOfMaterials.Approved).IsNotEqualTo(DateTime.MinValue);
+    }
+
+    public class StockSummaryTotalRequiredAggregate : CoreAggregate<StockSummary, 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<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<JobRequisitionItem, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { StockSummaryRequisitionItem => StockSummaryRequisitionItem.Product.ID, StockSummary => StockSummary.Product.ID },
+                { StockSummaryRequisitionItem => StockSummaryRequisitionItem.Style.ID, StockSummary => StockSummary.Style.ID },
+                { StockSummaryRequisitionItem => StockSummaryRequisitionItem.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+        
+        public override Filter<JobRequisitionItem> Filter =>
+            new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue);
+
+    }
+    
+    public class StockSummaryOrderAggregate : CoreAggregate<StockSummary, PurchaseOrderItem, double>
+    {
+        public override Expression<Func<PurchaseOrderItem, double>> Aggregate => x => x.Qty;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { PurchaseOrderItem => PurchaseOrderItem.Product.ID, StockSummary => StockSummary.Product.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Style.ID, StockSummary => StockSummary.Style.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+
+        public override Filter<PurchaseOrderItem> Filter => new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue);
+    }
+
+    public class StockSummaryOrderValue : CoreAggregate<StockSummary, PurchaseOrderItem, double>
+    {
+        public override Expression<Func<PurchaseOrderItem, double>> Aggregate => x => x.ExTax;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<PurchaseOrderItem, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { PurchaseOrderItem => PurchaseOrderItem.Product.ID, StockSummary => StockSummary.Product.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Style.ID, StockSummary => StockSummary.Style.ID },
+                { PurchaseOrderItem => PurchaseOrderItem.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+
+        public override Filter<PurchaseOrderItem> Filter => new Filter<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(DateTime.MinValue);
+    }
+
+    public class StockSummaryReceivedAggregate : CoreAggregate<StockSummary, StockMovement, double>
+    {
+        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Received;
+
+        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(false);
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { StockMovement => StockMovement.Product.ID, StockSummary => StockSummary.Product.ID },
+                { StockMovement => StockMovement.Style.ID, StockSummary => StockSummary.Style.ID },
+                { StockMovement => StockMovement.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+    }
+
+    public class StockSummaryIssuedAggregate : CoreAggregate<StockSummary, StockMovement, double>
+    {
+        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Issued;
+
+        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(false);
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { StockMovement => StockMovement.Product.ID, StockSummary => StockSummary.Product.ID },
+                { StockMovement => StockMovement.Style.ID, StockSummary => StockSummary.Style.ID },
+                { StockMovement => StockMovement.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+    }
+
+    public class StockSummaryBalanceRequiredFormula : IFormula<StockSummary, double>
+    {
+        public Expression<Func<StockSummary, double>> Value => x => x.TotalRequired;
+        public Expression<Func<StockSummary, double>>[] Modifiers => new Expression<Func<StockSummary, double>>[] { x => x.Issued };
+        public FormulaOperator Operator => FormulaOperator.Subtract;
+        public FormulaType Type => FormulaType.Virtual;
+    }
+
+    public class StockSummaryReservedStockAggregate : CoreAggregate<StockSummary, 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<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { StockMovement => StockMovement.Product.ID, StockSummary => StockSummary.Product.ID },
+                { StockMovement => StockMovement.Style.ID, StockSummary => StockSummary.Style.ID },
+                { StockMovement => StockMovement.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+    }
+
+    public class StockSummaryFreeStockAggregate : CoreAggregate<StockSummary, StockMovement, double>
+    {
+        public override Expression<Func<StockMovement, double>> Aggregate => x => x.Units;
+
+        public override AggregateCalculation Calculation => AggregateCalculation.Sum;
+
+        public override Filter<StockMovement> Filter => new Filter<StockMovement>(x => x.Job).NotLinkValid().Or(x=>x.Job.JobStatus.Active).IsEqualTo(false);
+
+        public override Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>> Links =>
+            new Dictionary<Expression<Func<StockMovement, object>>, Expression<Func<StockSummary, object>>>()
+            {
+                { StockMovement => StockMovement.Product.ID, StockSummary => StockSummary.Product.ID },
+                //{ StockMovement => StockMovement.Style.ID, StockSummary => StockSummary.Style.ID },
+                //{ StockMovement => StockMovement.Dimensions.UnitSize, StockSummary => StockSummary.Dimensions.UnitSize }
+            };
+    }
+    
+    public interface IStockSummary
+    {
+        
+        ProductLink Product { get; set; }
+
+        ProductStyleLink Style { get; set; }
+
+        StockDimensions Dimensions { get; set; }
+    }
+
+    public class StockSummaryUnionGenerator : AutoEntityUnionGenerator<IStockSummary>
+    {
+        protected override void Configure()
+        {
+            AddTable<StockMovement>();
+            AddTable<JobBillOfMaterialsItem>();
+            AddTable<JobRequisitionItem>();
+            AddTable<PurchaseOrderItem>();
+        }
+
+        public override bool Distinct => true;
+
+        public override Column<IStockSummary>[] IDColumns => new Column<IStockSummary>[]
+        {
+            new Column<IStockSummary>(x => x.Product.ID),
+            new Column<IStockSummary>(x => x.Style.ID),
+            new Column<IStockSummary>(x => x.Dimensions.Unit.ID),
+            new Column<IStockSummary>(x => x.Dimensions.Quantity),
+            new Column<IStockSummary>(x => x.Dimensions.Length),
+            new Column<IStockSummary>(x => x.Dimensions.Width),
+            new Column<IStockSummary>(x => x.Dimensions.Height),
+            new Column<IStockSummary>(x => x.Dimensions.Weight)
+        };
+    }    
+    
+    
+    [UserTracking(typeof(Product))]
+    [AutoEntity(typeof(StockSummaryUnionGenerator))]
+    public class StockSummary : StockEntity, IStockSummary, IRemotable, IPersistent, ILicense<ProductManagementLicense>/* , IDimensioned */
+    {
+
+        [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), AllowEditingUnit = false)]
+        public override StockDimensions Dimensions { get; set; }
+        
+        [EditorSequence(4)]
+        [DoubleEditor]
+        [Aggregate(typeof(StockSummaryBOMAggregate))]
+        public double BillOfMaterials { get; set; }
+
+        [EditorSequence(5)]
+        [DoubleEditor]
+        [Aggregate(typeof(StockSummaryTotalRequiredAggregate))]
+        public double TotalRequired { get; set; }
+
+        [EditorSequence(6)]
+        [DoubleEditor]
+        [Aggregate(typeof(StockSummaryOrderAggregate))]
+        public double OnOrder { get; set; }
+
+        [EditorSequence(7)]
+        [DoubleEditor]
+        [Aggregate(typeof(StockSummaryOrderValue))]
+        public double OrderValue { get; set; }
+
+        [EditorSequence(8)]
+        [Aggregate(typeof(StockSummaryReceivedAggregate))]
+        [DoubleEditor(Editable = Editable.Hidden)]
+        public double Received { get; set; }
+
+        [EditorSequence(9)]
+        [Aggregate(typeof(StockSummaryIssuedAggregate))]
+        [DoubleEditor(Editable = Editable.Hidden)]
+        public double Issued { get; set; }
+
+        [EditorSequence(10)]
+        [DoubleEditor]
+        [Formula(typeof(StockSummaryBalanceRequiredFormula))]
+        public double BalanceRequired { get; set; }
+
+        [EditorSequence(11)]
+        [Aggregate(typeof(StockSummaryReservedStockAggregate))]
+        [DoubleEditor(Editable = Editable.Hidden)]
+        public double ReservedStock { get; set; }
+
+        [EditorSequence(12)]
+        [Aggregate(typeof(StockSummaryFreeStockAggregate))]
+        [DoubleEditor(Editable = Editable.Hidden)]
+        public double FreeStock { get; set; }
+        
+        protected override void Init()
+        {
+            base.Init();
+            Style = new ProductStyleLink();
+        }
+        
+    }
+}

+ 6 - 0
prs.classes/LicenseTokens.cs

@@ -91,4 +91,10 @@ namespace Comal.Classes
     public class DashboardLicense : LicenseToken
     {
     }
+    
+    
+    [Caption("Document Scanner")]
+    public class DocumentScannerLicense : LicenseToken
+    {
+    }
 }

+ 4 - 1
prs.desktop/MainWindow.xaml

@@ -536,7 +536,10 @@
                                    Click="StockLocations_Checked" MinWidth="60" />
                     <fluent:Button x:Name="StockMovementList" Header="Stock Movements"
                                    LargeIcon="pack://application:,,,/Resources/forklift.png"
-                                   Click="StockMovements_Checked" MinWidth="60" />
+                                   Click="StockMovements_Checked" MinWidth="60" />                    
+                    <fluent:Button x:Name="StockSummaryButton" Header="Stock Summary"
+                                   LargeIcon="pack://application:,,,/Resources/kpi.png"
+                                   Click="StockSummaryButton_Clicked" MinWidth="60" />
                 </fluent:RibbonGroupBox>
 
                 <fluent:RibbonGroupBox x:Name="ProductReports" Width="Auto" MinWidth="60" Header="Print"

+ 9 - 4
prs.desktop/MainWindow.xaml.cs

@@ -765,18 +765,19 @@ namespace PRSDesktop
                         SetVisibility(ProductsMasterList, Security.CanView<Product>());
                         SetVisibility(StockLocationList, Security.CanView<StockLocation>());
                         SetVisibility(StockMovementList, Security.CanView<StockMovement>());
+                        SetVisibility(StockSummaryButton, Security.CanView<StockSummary>());
 
                         SetVisibleIfEither(ProductsTaskSeparator,
                             new FrameworkElement[]
                             {
                                 ProductsDashboardButton, ProductsMessagesButton, ProductsTaskButton, ProductsAttendanceButton, ProductsMapButton,
                                 ProductsDailyReportButton
-                            }, new FrameworkElement[] { ProductsMasterList, StockLocationList });
+                            }, new FrameworkElement[] { ProductsMasterList, StockLocationList, StockMovementList, StockSummaryButton });
 
                         SetVisibleIfAny(ProductActions, ProductsDashboardButton, ProductsMessagesButton, ProductsTaskButton, ProductsAttendanceButton,
-                            ProductsDailyReportButton, ProductsMasterList, StockLocationList, StockMovementList);
+                            ProductsDailyReportButton, ProductsMasterList, StockLocationList, StockMovementList, StockSummaryButton);
 
-                        AddSetupAction(ProductTab, "Units of Measure", ProductDimensionUnitsList_Click, PRSDesktop.Resources.unitofmeasure,
+                        AddSetupAction(ProductTab, "Product Dimensions", ProductDimensionUnitsList_Click, PRSDesktop.Resources.unitofmeasure,
                             Security.CanView<ProductDimensionUnit>());
                         AddSetupAction(ProductTab, "Product Groups", ProductGroupsList_Click, PRSDesktop.Resources.productgroup,
                             Security.CanView<ProductGroup>());
@@ -2137,9 +2138,13 @@ namespace PRSDesktop
         private void StockMovements_Checked(object sender, RoutedEventArgs e)
         {
             LoadWindow<StockMovementPanel>((Fluent.Button)sender);
+        }        
+        
+        private void StockSummaryButton_Clicked(object sender, RoutedEventArgs e)
+        {
+            LoadWindow<StockSummaryPanel>((Fluent.Button)sender);
         }
 
-
         private void KitsMasterList_Click(object sender, RoutedEventArgs e)
         {
             LoadWindow<KitPanel>((Fluent.Button)sender);

+ 1 - 1
prs.desktop/Panels/Attendance/AttendancePanel.xaml.cs

@@ -142,7 +142,7 @@ namespace PRSDesktop
                             UpdateKanban(kanban,
                                 TimeSpan.MinValue,
                                 TimeSpan.MinValue,
-                                "",
+                                "Not Rostered On",
                                 "White",
                                 "Gainsboro",
                                 "",

+ 0 - 21
prs.desktop/Panels/EmployeePlanner/EmployeeResourcePlanner.xaml.cs

@@ -25,27 +25,6 @@ using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventA
 
 namespace PRSDesktop
 {
-
-    public class EmployeeResourcePlannerProperties : UserConfigurationSettings, IDashboardProperties
-    {
-        public EmployeeSelectorSettings EmployeeSettings { get; set; }
-        
-        public bool IncludeUnApprovedLeave { get; set; }
-        
-        public EmployeeSelectorData EmployeeSelection { get; set; }
-        public Guid ActivityType { get; set; }
-        public String JobFilter { get; set; }
-
-        public EmployeeResourcePlannerProperties()
-        {
-            EmployeeSettings = new EmployeeSelectorSettings();
-            EmployeeSelection = new EmployeeSelectorData();
-            IncludeUnApprovedLeave = false;
-            ActivityType = Guid.Empty;
-            JobFilter = "";
-        }
-    }
-
     public class EmployeeResourcePlannerValue
     {
         public Guid ID { get; set; }

+ 25 - 0
prs.desktop/Panels/EmployeePlanner/EmployeeResourcePlannerProperties.cs

@@ -0,0 +1,25 @@
+using System;
+using InABox.Configuration;
+
+namespace PRSDesktop
+{
+    public class EmployeeResourcePlannerProperties : UserConfigurationSettings, IDashboardProperties
+    {
+        public EmployeeSelectorSettings EmployeeSettings { get; set; }
+        
+        public bool IncludeUnApprovedLeave { get; set; }
+        
+        public EmployeeSelectorData EmployeeSelection { get; set; }
+        public Guid ActivityType { get; set; }
+        public String JobFilter { get; set; }
+
+        public EmployeeResourcePlannerProperties()
+        {
+            EmployeeSettings = new EmployeeSelectorSettings();
+            EmployeeSelection = new EmployeeSelectorData();
+            IncludeUnApprovedLeave = false;
+            ActivityType = Guid.Empty;
+            JobFilter = "";
+        }
+    }
+}

+ 199 - 0
prs.desktop/Panels/StockSummary/StockSummaryGrid.cs

@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Configuration;
+using InABox.Core;
+using InABox.DynamicGrid;
+using PRSDesktop.WidgetGroups;
+
+namespace PRSDesktop
+{
+    
+    public class StockSummaryProperties : UserConfigurationSettings, IDashboardProperties
+    {
+        
+    }
+    
+    public class SockSummaryElement : DashboardElement<StockSummaryDashboard, Warehousing, StockSummaryProperties>
+    {
+        
+    }
+    
+    public class StockSummaryDashboard : StockSummaryGrid, IDashboardWidget<Warehousing, StockSummaryProperties>
+    {
+        public void Setup()
+        {
+            Refresh(true, false);
+        }
+
+        public void Shutdown()
+        {
+            
+        }
+
+        public void Refresh()
+        {
+            Refresh(false,true);
+        }
+
+        public StockSummaryProperties Properties { get; set; }
+        
+        public event LoadSettings<StockSummaryProperties>? LoadSettings;
+        
+        public event SaveSettings<StockSummaryProperties>? SaveSettings;
+    }
+    
+    public class StockSummaryPanel : StockSummaryGrid, IPanel<StockSummary>
+    {
+        public void Setup()
+        {
+            Refresh(true, false);
+        }
+
+        public void Shutdown()
+        {
+            
+        }
+
+        public void Refresh()
+        {
+            Refresh(false,true);
+        }
+
+        public bool IsReady { get; set; }
+        
+        public void CreateToolbarButtons(IPanelHost host)
+        {
+           
+        }
+
+        public Dictionary<string, object[]> Selected()
+        {
+            return new Dictionary<string, object[]>();
+        }
+
+        public void Heartbeat(TimeSpan time)
+        {
+            
+        }
+    }
+    
+    public class StockSummaryGrid : DynamicDataGrid<StockSummary>, IDataModelSource
+    {
+        public StockSummaryGrid() : base()
+        {
+            Options.AddRange(
+                DynamicGridOption.RecordCount,
+                DynamicGridOption.SelectColumns,
+                DynamicGridOption.FilterRows,
+                DynamicGridOption.ExportData
+            );
+
+            HiddenColumns.Add(x => x.Product.ID);
+            HiddenColumns.Add(x => x.Style.ID);
+            HiddenColumns.Add(x => x.Dimensions.UnitSize);
+
+            HiddenColumns.Add(x => x.BillOfMaterials);
+            HiddenColumns.Add(x => x.TotalRequired);
+            HiddenColumns.Add(x => x.BalanceRequired);
+            HiddenColumns.Add(x => x.ReservedStock);
+            HiddenColumns.Add(x => x.OnOrder);
+            HiddenColumns.Add(x => x.Issued);
+            HiddenColumns.Add(x => x.Received);
+
+            HiddenColumns.Add(x => x.Product.Image.ID);
+            HiddenColumns.Add(x => x.Product.Image.FileName);
+            ActionColumns.Add(new DynamicImagePreviewColumn<StockSummary>(this, x => x.Product.Image, false)
+                { Position = DynamicActionColumnPosition.Start });
+
+            OnCellDoubleClick += StockSummaryGrid_OnCellDoubleClick;
+        }
+
+        private void StockSummaryGrid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
+        {
+            if(args.Column.ColumnName == "BillOfMaterials"
+                || args.Column.ColumnName == "TotalRequired"
+                || args.Column.ColumnName == "ReservedStock"
+                || args.Column.ColumnName == "OnOrder"
+                || args.Column.ColumnName == "Issued"
+                || args.Column.ColumnName == "Received"
+                || args.Column.ColumnName == "FreeStock")
+            {
+                if(DatabaseSchema.Property(typeof(StockSummary), args.Column.ColumnName) is StandardProperty property)
+                {
+                    var aggregate = property.Property.GetCustomAttribute<AggregateAttribute>();
+                    if(aggregate is not null)
+                    {
+                        var entityType = aggregate.Source;
+                        var filter = Filter.Create(entityType).All();
+                        
+                        var aggFilter = aggregate.Filter;
+                        if (aggFilter is not null)
+                            filter.And(aggFilter);
+
+                        var links = aggregate.Links;
+
+                        foreach (var (pLeft, pStockSummary) in aggregate.Links)
+                        {
+                            var value = args.Row[pStockSummary];
+                            filter.And(pLeft).IsEqualTo(value);
+                        }
+
+                        var grid = (Activator.CreateInstance(typeof(DynamicDataGrid<>).MakeGenericType(entityType)) as IDynamicDataGrid)!;
+                        grid.ColumnsTag = $"StockSummaryAggregate.{args.Column.ColumnName}";
+                        //DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), entityType);
+                        grid.Options.BeginUpdate().Clear().AddRange(DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns).EndUpdate();
+                        grid.OnDefineFilter += (o, type) =>
+                        {
+                            return filter;
+                        };
+
+                        var window = DynamicGridUtils.CreateGridWindow($"Viewing {CoreUtils.Neatify(args.Column.ColumnName)} Calculation", (grid as BaseDynamicGrid)!);
+                        window.ShowDialog();
+                    }
+                }
+            }
+            else if(args.Column.ColumnName == "BalanceRequired")
+            {
+                var totalRequired = (double)(args.Row["TotalRequired"] ?? 0.0);
+                var issued = (double)(args.Row["Issued"] ?? 0.0);
+                var balance = (double)(args.Row["BalanceRequired"] ?? 0.0);
+                MessageBox.Show($"BalanceRequired\n    = TotalRequired - Issued\n    = {totalRequired:F2} - {issued:F2} = {balance:F2}");
+            }
+        }
+
+        public event DataModelUpdateEvent OnUpdateDataModel;
+
+        public string SectionName => "Stock Summary";
+
+        public DataModel DataModel(Selection selection)
+        {
+            return new AutoDataModel<StockSummary>(null);
+        }
+        
+        protected override void Reload(Filters<StockSummary> criteria, Columns<StockSummary> columns, ref SortOrder<StockSummary>? sort,
+            Action<CoreTable?, Exception?> action)
+        {
+            criteria.Add(new Filter<StockSummary>(x=>x.Product.ID).IsNotEqualTo(Guid.Empty));
+            base.Reload(criteria, columns, ref sort, action);
+        }
+
+        protected override bool FilterRecord(CoreRow row)
+        {
+            var result = base.FilterRecord(row);
+            if (result)
+                result = (result && row.Get<StockSummary, double>(x => x.BillOfMaterials) != 0.0F) ||
+                         row.Get<StockSummary, double>(x => x.TotalRequired) != 0.0F ||
+                         row.Get<StockSummary, double>(x => x.BalanceRequired) != 0.0F ||
+                         row.Get<StockSummary, double>(x => x.OnOrder) != 0.0F ||
+                         row.Get<StockSummary, double>(x => x.Issued) != 0.0F ||
+                         row.Get<StockSummary, double>(x => x.Received) != 0.0F ||
+                         row.Get<StockSummary, double>(x => x.ReservedStock) != 0.0F;
+            return result;
+        }
+    }
+}

+ 2 - 2
prs.shared/DatabaseUpdateScripts.cs

@@ -20,7 +20,7 @@ namespace PRS.Shared
             DataUpdater.RegisterUpdateScript("6.38", Update_6_38);
             DataUpdater.RegisterUpdateScript("6.39", Update_6_39);
             DataUpdater.RegisterUpdateScript("6.43", Update_6_43);
-            DataUpdater.RegisterUpdateScript("6.46", Update_6_46);
+            DataUpdater.RegisterUpdateScript("7.00", Update_7_00);
         }
 
         private static Dictionary<string, Tuple<string, string>> _6_31_module_map = new()
@@ -203,7 +203,7 @@ namespace PRS.Shared
             }
         }
         
-        private static bool Update_6_46()
+        private static bool Update_7_00()
         {
             
             static void Convert<T>(