Forráskód Böngészése

RequisitionViewGrid Now also loads PurchaseOrder reports; added a view stock movements button to Job requisitions panel.
Made other stock movement grids ISpecificGrid.
Disabled editing of received POItems

Kenric Nugteren 1 éve
szülő
commit
50d0c7041d

+ 2 - 0
prs.desktop/Panels/IPanel.cs

@@ -87,6 +87,8 @@ public interface IPanelHost
 {
     void CreatePanelAction(PanelAction action);
 
+    void CreateReport(PanelAction action);
+
     void CreateSetupAction(PanelAction action);
 
     void CreateSetupSeparator();

+ 53 - 54
prs.desktop/Panels/PanelHost.cs

@@ -73,6 +73,11 @@ public class PanelHost : IPanelHost
         HostControl.CreatePanelAction(action);
     }
 
+    void IPanelHost.CreateReport(PanelAction action)
+    {
+        HostControl.CreateReport(action);
+    }
+
     void IPanelHost.CreateSetupAction(PanelAction action)
     {
         SetupActions.Add(action);
@@ -158,20 +163,26 @@ public class PanelHost : IPanelHost
 
     #endregion
 
-    #region Custom Modules
+    #region Actions
 
-    private void ReloadModules(string sectionName, DataModel model)
+    private void ReloadActions(string sectionName, DataModel model)
     {
         SetupActions.Clear();
         HostControl.ClearActions();
+        HostControl.ClearReports();
 
+        CreateModules(sectionName, model);
         if (CurrentPanel != null)
         {
             CurrentPanel.CreateToolbarButtons(this);
-            CreateModules(sectionName, model);
         }
+        CreateReports(sectionName, model);
     }
 
+    #endregion
+
+    #region Custom Modules
+
     private void CreateModules(string section, DataModel model)
     {
         if (ClientFactory.IsSupported<CustomModule>())
@@ -238,7 +249,8 @@ public class PanelHost : IPanelHost
                 DataModel = dataModel
             };
             manager.ShowDialog();
-            ReloadModules(section, dataModel);
+
+            ReloadActions(section, dataModel);
         }
     }
 
@@ -255,72 +267,60 @@ public class PanelHost : IPanelHost
             return EmailUtils.CreateTemplateDefinitions(CurrentPanel.DataModel(Selection.None));
     }
 
-    private void ReloadReports(string section, DataModel model)
+    public static PanelAction CreateReportAction(ReportTemplate template, Func<Selection, DataModel> getDataModel)
     {
-        HostControl.ClearReports();
+        var action = new PanelAction
+        {
+            Caption = template.Name,
+            Image = PRSDesktop.Resources.printer,
+            OnExecute = (action) =>
+            {
+                PrintReport(template.ID, getDataModel);
+            }
+        };
 
-        CreateReports(section, model);
+        if (Security.IsAllowed<CanDesignReports>())
+        {
+            var menu = new ContextMenu();
+            menu.AddItem("Design Report", PRSDesktop.Resources.pencil, () => DesignReport(template.ID, getDataModel));
+            action.Menu = menu;
+        }
+        return action;
     }
 
     private void CreateReports(string section, DataModel model)
     {
+        if (CurrentPanel is null) return;
+
         var client = new Client<ReportTemplate>();
-        var templates = client.Query(
-            new Filter<ReportTemplate>(x => x.DataModel).IsEqualTo(model.Name)
-                .And(x => x.Section).IsEqualTo(section)
-                .And(x => x.Visible).IsEqualTo(true),
-            new Columns<ReportTemplate>(x => x.ID, x => x.Name),
-            new SortOrder<ReportTemplate>(x => x.Name)
-        );
-
-        foreach (var row in templates.Rows)
-        {
-            var action = new PanelAction
-            {
-                Caption = row.Get<ReportTemplate, string>(x => x.Name),
-                Image = PRSDesktop.Resources.printer,
-                OnExecute = (action) =>
-                {
-                    PrintReport(row.Get<ReportTemplate, Guid>(x => x.ID));
-                }
-            };
+        var templates = ReportUtils.LoadReports(section, model, new Columns<ReportTemplate>(x => x.ID, x => x.Name));
 
-            if (Security.IsAllowed<CanDesignReports>())
-            {
-                var menu = new ContextMenu();
-                menu.AddItem("Design Report", PRSDesktop.Resources.pencil, row.Get<ReportTemplate, Guid>(x => x.ID), DesignReport);
-                action.Menu = menu;
-            }
-            HostControl.CreateReport(action);
+        foreach (var template in templates)
+        {
+            HostControl.CreateReport(CreateReportAction(template, CurrentPanel.DataModel));
         }
     }
 
-    private void DesignReport(Guid templateID)
+    private static void DesignReport(Guid templateID, Func<Selection, DataModel> getDataModel)
     {
-        if (CurrentPanel is null)
-            return;
-
         var template = new Client<ReportTemplate>().Load(new Filter<ReportTemplate>(x => x.ID).IsEqualTo(templateID)).FirstOrDefault();
         if (template is null)
         {
             Logger.Send(LogType.Error, "", $"No Report Template with ID '{templateID}'");
-            MessageBox.Show("Report does not exist!");
+            MessageWindow.ShowMessage("Report does not exist!", "Error", image: MessageWindow.WarningImage);
             return;
         }
 
-        ReportUtils.DesignReport(template, CurrentPanel.DataModel(Selection.None));
+        ReportUtils.DesignReport(template, getDataModel(Selection.None));
     }
 
-    private void PrintReport(Guid id)
+    private static void PrintReport(Guid id, Func<Selection, DataModel> getDataModel)
     {
-        if (CurrentPanel is null)
-            return;
-
         var template = new Client<ReportTemplate>().Load(new Filter<ReportTemplate>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
         if (template == null)
         {
             Logger.Send(LogType.Error, "", $"No Report Template with ID '{id}'");
-            MessageBox.Show("Report does not exist!");
+            MessageWindow.ShowMessage("Report does not exist!", "Error", image: MessageWindow.WarningImage);
             return;
         }
 
@@ -333,9 +333,12 @@ public class PanelHost : IPanelHost
         else if (template.AllRecords)
             selection = Selection.All;
         else
-            MessageBox.Show("Report must have either [Selected Records] or [All Records] checked to display!");
+            MessageWindow.ShowMessage(
+                "Report must have either [Selected Records] or [All Records] checked to display!",
+                "Error",
+                image: MessageWindow.WarningImage);
         if (selection != Selection.None)
-            ReportUtils.PreviewReport(template, CurrentPanel.DataModel(selection), false, Security.IsAllowed<CanDesignReports>());
+            ReportUtils.PreviewReport(template, getDataModel(selection), false, Security.IsAllowed<CanDesignReports>());
     }
 
     private void ManageReports(PanelAction action)
@@ -353,7 +356,8 @@ public class PanelHost : IPanelHost
 
         var form = new ReportManager { DataModel = model, Section = section, Populate = true };
         form.ShowDialog();
-        ReloadReports(section, model);
+
+        ReloadActions(section, model);
     }
 
     private void ManageEmailTemplates(PanelAction action)
@@ -464,17 +468,12 @@ public class PanelHost : IPanelHost
         CurrentPanel.Setup();
         CurrentPanel.IsReady = true;
 
-        CurrentPanel.OnUpdateDataModel += (s, m) =>
-        {
-            ReloadModules(s, m);
-            ReloadReports(s, m);
-        };
+        CurrentPanel.OnUpdateDataModel += ReloadActions;
 
         var model = CurrentPanel.DataModel(Selection.None);
         var section = CurrentPanel.SectionName;
 
-        ReloadModules(section, model);
-        ReloadReports(section, model);
+        ReloadActions(section, model);
 
         return panel;
     }

+ 213 - 214
prs.desktop/Panels/Products/Locations/StockMovementGrid.cs

@@ -12,274 +12,273 @@ using InABox.DynamicGrid;
 using InABox.WPF;
 using Syncfusion.UI.Xaml.Diagram.Controls;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class StockMovementGrid : DynamicDataGrid<StockMovement>, IDataModelSource, ISpecificGrid
 {
-    public class StockMovementGrid : DynamicDataGrid<StockMovement>, IDataModelSource
-    {
-        public static readonly DependencyProperty AllowNullLocationProperty =
-            DependencyProperty.Register("AllowNullLocation", typeof(bool), typeof(StockMovementGrid), new UIPropertyMetadata(null));
+    public static readonly DependencyProperty AllowNullLocationProperty =
+        DependencyProperty.Register("AllowNullLocation", typeof(bool), typeof(StockMovementGrid), new UIPropertyMetadata(null));
 
-        public static readonly DependencyProperty AllowNullBatchProperty =
-            DependencyProperty.Register("AllowNullBatch", typeof(bool), typeof(StockMovementGrid), new UIPropertyMetadata(null));
+    public static readonly DependencyProperty AllowNullBatchProperty =
+        DependencyProperty.Register("AllowNullBatch", typeof(bool), typeof(StockMovementGrid), new UIPropertyMetadata(null));
 
-        private Button AllButton;
-        private bool bShowAll = true;
+    private Button AllButton;
+    private bool bShowAll = true;
 
-        private readonly BitmapImage docs = PRSDesktop.Resources.doc_png.AsBitmapImage();
+    private readonly BitmapImage docs = PRSDesktop.Resources.doc_png.AsBitmapImage();
 
-        private int syscolumn = -1;
+    private int syscolumn = -1;
 
-        private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
-        private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
-        private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
-        private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
+    private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
+    private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
+    private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
+    private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
 
-        public StockMovementGrid()
-        {
-            ColumnsTag = "StockMovementGrid";
-        }
-        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
-        {
-            base.DoReconfigure(options);
-
-            options
-                .BeginUpdate()
-                .Add(DynamicGridOption.RecordCount)
-                .Add(DynamicGridOption.SelectColumns)
-                .Add(DynamicGridOption.FilterRows)
-                .EndUpdate();
-        }
-        protected override void Init()
+    public StockMovementGrid()
+    {
+        ColumnsTag = "StockMovementGrid";
+    }
+    protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+    {
+        base.DoReconfigure(options);
+
+        options
+            .BeginUpdate()
+            .Add(DynamicGridOption.RecordCount)
+            .Add(DynamicGridOption.SelectColumns)
+            .Add(DynamicGridOption.FilterRows)
+            .EndUpdate();
+    }
+    protected override void Init()
+    {
+        base.Init();
+        HiddenColumns.Add(x => x.System);
+        HiddenColumns.Add(x => x.Transaction);
+
+        ActionColumns.Add(new DynamicImageColumn(DocumentsImage, DocumentsClick) { Position = DynamicActionColumnPosition.Start });
+        HiddenColumns.Add(x => x.Documents);
+        HiddenColumns.Add(x => x.Batch.ID);
+        HiddenColumns.Add(x => x.Batch.Type);
+        HiddenColumns.Add(x => x.JobRequisitionItem.Requisition.Number);
+        AllButton = AddButton("Hide System", null, ToggleAllTransations);
+
+        HiddenColumns.Add(x => x.PostedStatus);
+        HiddenColumns.Add(x => x.PostedNote);
+
+        ActionColumns.Add(new DynamicImageColumn(Posted_Image, null)
         {
-            base.Init();
-            HiddenColumns.Add(x => x.System);
-            HiddenColumns.Add(x => x.Transaction);
+            ToolTip = Posted_ToolTip
+        });
+    }
 
-            ActionColumns.Add(new DynamicImageColumn(DocumentsImage, DocumentsClick) { Position = DynamicActionColumnPosition.Start });
-            HiddenColumns.Add(x => x.Documents);
-            HiddenColumns.Add(x => x.Batch.ID);
-            HiddenColumns.Add(x => x.Batch.Type);
-            HiddenColumns.Add(x => x.JobRequisitionItem.Requisition.Number);
-            AllButton = AddButton("Hide System", null, ToggleAllTransations);
+    public DateTime StartDate { get; set; } = DateTime.MinValue;
+    public DateTime EndDate { get; set; } = DateTime.MaxValue;
 
-            HiddenColumns.Add(x => x.PostedStatus);
-            HiddenColumns.Add(x => x.PostedNote);
+    public IStockLocation Location { get; set; }
 
-            ActionColumns.Add(new DynamicImageColumn(Posted_Image, null)
-            {
-                ToolTip = Posted_ToolTip
-            });
-        }
+    public bool AllowNullLocation
+    {
+        get => (bool)GetValue(AllowNullLocationProperty);
+        set => SetValue(AllowNullLocationProperty, value);
+    }
+
+    public IStockMovementBatch Batch { get; set; }
 
-        public DateTime StartDate { get; set; } = DateTime.MinValue;
-        public DateTime EndDate { get; set; } = DateTime.MaxValue;
+    public bool AllowNullBatch
+    {
+        get => (bool)GetValue(AllowNullBatchProperty);
+        set => SetValue(AllowNullBatchProperty, value);
+    }
 
-        public IStockLocation Location { get; set; }
+    public event DataModelUpdateEvent? OnUpdateDataModel;
 
-        public bool AllowNullLocation
-        {
-            get => (bool)GetValue(AllowNullLocationProperty);
-            set => SetValue(AllowNullLocationProperty, value);
-        }
+    public string SectionName => "Stock Movements";
 
-        public IStockMovementBatch Batch { get; set; }
+    public DataModel DataModel(Selection selection)
+    {
+        var ids = ExtractValues(x => x.ID, selection).ToArray();
+        return new BaseDataModel<StockMovement>(new Filter<StockMovement>(x => x.ID).InList(ids));
+    }
 
-        public bool AllowNullBatch
+    private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)
+    {
+        if (row is null)
         {
-            get => (bool)GetValue(AllowNullBatchProperty);
-            set => SetValue(AllowNullBatchProperty, value);
+            return column.TextToolTip("Stock Movement Processed Status");
         }
+        return column.TextToolTip(row.Get<StockMovement, PostedStatus>(x => x.PostedStatus) switch
+        {
+            PostedStatus.PostFailed => "Post failed: " + row.Get<StockMovement, string>(x => x.PostedNote),
+            PostedStatus.RequiresRepost => "Repost required: " + row.Get<StockMovement, string>(x => x.PostedNote),
+            PostedStatus.Posted => "Processed",
+            PostedStatus.NeverPosted or _ => "Not posted yet",
+        });
+    }
 
-        public event DataModelUpdateEvent? OnUpdateDataModel;
+    private BitmapImage? Posted_Image(CoreRow? row)
+    {
+        if (row is null)
+            return post;
+        return row.Get<StockMovement, PostedStatus>(x => x.PostedStatus) switch
+        {
+            PostedStatus.PostFailed => warning,
+            PostedStatus.Posted => tick,
+            PostedStatus.RequiresRepost => refresh,
+            PostedStatus.NeverPosted or _ => null,
+        };
+    }
 
-        public string SectionName => "Stock Movements";
+    private bool DocumentsClick(CoreRow? arg)
+    {
+        if (arg == null || arg.Get<StockMovement, int>(x => x.Documents) == 0)
+            return false;
 
-        public DataModel DataModel(Selection selection)
+        var docs = new List<IEntityDocument>();
+        using (new WaitCursor())
         {
-            var ids = ExtractValues(x => x.ID, selection).ToArray();
-            return new BaseDataModel<StockMovement>(new Filter<StockMovement>(x => x.ID).InList(ids));
+            var batchid = arg.Get<StockMovement, Guid>(x => x.Batch.ID);
+            var table = new Client<StockMovementBatchDocument>().Query(
+                new Filter<StockMovementBatchDocument>(x => x.EntityLink.ID).IsEqualTo(batchid)
+            );
+            foreach (var row in table.Rows)
+                docs.Add(row.ToObject<StockMovementBatchDocument>());
         }
 
-        private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)
+        if (docs.Any())
         {
-            if (row is null)
-            {
-                return column.TextToolTip("Stock Movement Processed Status");
-            }
-            return column.TextToolTip(row.Get<StockMovement, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => "Post failed: " + row.Get<StockMovement, string>(x => x.PostedNote),
-                PostedStatus.RequiresRepost => "Repost required: " + row.Get<StockMovement, string>(x => x.PostedNote),
-                PostedStatus.Posted => "Processed",
-                PostedStatus.NeverPosted or _ => "Not posted yet",
-            });
+            var editor = new DocumentEditor(docs.ToArray());
+            //editor.PrintAllowed = Security.IsAllowed<CanPrintFactoryFloorDrawings>();
+            editor.SaveAllowed = Security.IsAllowed<CanSaveFactoryFloorDrawings>();
+            editor.ShowDialog();
         }
-
-        private BitmapImage? Posted_Image(CoreRow? row)
+        else
         {
-            if (row is null)
-                return post;
-            return row.Get<StockMovement, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => warning,
-                PostedStatus.Posted => tick,
-                PostedStatus.RequiresRepost => refresh,
-                PostedStatus.NeverPosted or _ => null,
-            };
+            MessageBox.Show("No Documents Available!");
         }
 
-        private bool DocumentsClick(CoreRow? arg)
-        {
-            if (arg == null || arg.Get<StockMovement, int>(x => x.Documents) == 0)
-                return false;
+        return false;
+    }
 
-            var docs = new List<IEntityDocument>();
-            using (new WaitCursor())
-            {
-                var batchid = arg.Get<StockMovement, Guid>(x => x.Batch.ID);
-                var table = new Client<StockMovementBatchDocument>().Query(
-                    new Filter<StockMovementBatchDocument>(x => x.EntityLink.ID).IsEqualTo(batchid)
-                );
-                foreach (var row in table.Rows)
-                    docs.Add(row.ToObject<StockMovementBatchDocument>());
-            }
-
-            if (docs.Any())
-            {
-                var editor = new DocumentEditor(docs.ToArray());
-                //editor.PrintAllowed = Security.IsAllowed<CanPrintFactoryFloorDrawings>();
-                editor.SaveAllowed = Security.IsAllowed<CanSaveFactoryFloorDrawings>();
-                editor.ShowDialog();
-            }
-            else
-            {
-                MessageBox.Show("No Documents Available!");
-            }
+    private BitmapImage? DocumentsImage(CoreRow? arg)
+    {
+        if (arg == null)
+            return docs;
+        return arg.Get<Delivery, int>(x => x.Documents) > 0 ? docs : null;
+    }
 
-            return false;
-        }
+    private bool ToggleAllTransations(Button arg1, CoreRow[] arg2)
+    {
+        bShowAll = !bShowAll;
+        AllButton.Content = bShowAll ? "Hide System" : "Show All";
+        return true;
+    }
 
-        private BitmapImage? DocumentsImage(CoreRow? arg)
+    protected override void Reload(Filters<StockMovement> criteria, Columns<StockMovement> columns, ref SortOrder<StockMovement>? sort,
+        Action<CoreTable, Exception> action)
+    {
+        if (!AllowNullLocation && (Location == null || Location.ID == Guid.Empty))
         {
-            if (arg == null)
-                return docs;
-            return arg.Get<Delivery, int>(x => x.Documents) > 0 ? docs : null;
+            criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(CoreUtils.FullGuid));
+            criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(Guid.Empty));
         }
-
-        private bool ToggleAllTransations(Button arg1, CoreRow[] arg2)
+        else
         {
-            bShowAll = !bShowAll;
-            AllButton.Content = bShowAll ? "Hide System" : "Show All";
-            return true;
+            if (Location != null)
+                criteria.Add(new Filter<StockMovement>(x => x.Location.ID).IsEqualTo(Location.ID));
         }
 
-        protected override void Reload(Filters<StockMovement> criteria, Columns<StockMovement> columns, ref SortOrder<StockMovement>? sort,
-            Action<CoreTable, Exception> action)
+        if (!AllowNullBatch && (Batch == null || Batch.ID == Guid.Empty))
         {
-            if (!AllowNullLocation && (Location == null || Location.ID == Guid.Empty))
-            {
-                criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(CoreUtils.FullGuid));
-                criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(Guid.Empty));
-            }
-            else
-            {
-                if (Location != null)
-                    criteria.Add(new Filter<StockMovement>(x => x.Location.ID).IsEqualTo(Location.ID));
-            }
+            criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(CoreUtils.FullGuid));
+            criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(Guid.Empty));
+        }
+        else
+        {
+            if (Batch != null)
+                criteria.Add(new Filter<StockMovement>(x => x.Batch.ID).IsEqualTo(Batch.ID));
+        }
 
-            if (!AllowNullBatch && (Batch == null || Batch.ID == Guid.Empty))
-            {
-                criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(CoreUtils.FullGuid));
-                criteria.Add(new Filter<StockMovement>(x => x.ID).IsEqualTo(Guid.Empty));
-            }
-            else
-            {
-                if (Batch != null)
-                    criteria.Add(new Filter<StockMovement>(x => x.Batch.ID).IsEqualTo(Batch.ID));
-            }
+        if (!bShowAll)
+            criteria.Add(new Filter<StockMovement>(x => x.System).IsEqualTo(false));
 
-            if (!bShowAll)
-                criteria.Add(new Filter<StockMovement>(x => x.System).IsEqualTo(false));
+        if (!DateTime.Equals(StartDate, DateTime.MinValue))
+            criteria.Add(new Filter<StockMovement>(x => x.Date).IsGreaterThanOrEqualTo(StartDate));
 
-            if (!DateTime.Equals(StartDate, DateTime.MinValue))
-                criteria.Add(new Filter<StockMovement>(x => x.Date).IsGreaterThanOrEqualTo(StartDate));
+        if (!DateTime.Equals(EndDate, DateTime.MaxValue))
+            criteria.Add(new Filter<StockMovement>(x => x.Date).IsLessThan(EndDate.Date.AddDays(1)));
 
-            if (!DateTime.Equals(EndDate, DateTime.MaxValue))
-                criteria.Add(new Filter<StockMovement>(x => x.Date).IsLessThan(EndDate.Date.AddDays(1)));
+        sort = new SortOrder<StockMovement>(x => x.Date, SortDirection.Descending).ThenBy(x => x.System);
+        base.Reload(criteria, columns, ref sort, action);
+    }
 
-            sort = new SortOrder<StockMovement>(x => x.Date, SortDirection.Descending).ThenBy(x => x.System);
-            base.Reload(criteria, columns, ref sort, action);
-        }
+    protected override StockMovement CreateItem()
+    {
+        var result = base.CreateItem();
+        result.Location.ID = Location != null ? Location.ID : Guid.Empty;
+        return result;
+    }
 
-        protected override StockMovement CreateItem()
-        {
-            var result = base.CreateItem();
-            result.Location.ID = Location != null ? Location.ID : Guid.Empty;
-            return result;
-        }
+    protected override BaseEditor? GetEditor(object item, DynamicGridColumn column)
+    {
+        if (column.ColumnName.Equals("Location.ID"))
+            return new NullEditor();
+        return base.GetEditor(item, column);
+    }
 
-        protected override BaseEditor? GetEditor(object item, DynamicGridColumn column)
+    protected override void DeleteItems(params CoreRow[] rows)
+    {
+        if (!rows.Any())
         {
-            if (column.ColumnName.Equals("Location.ID"))
-                return new NullEditor();
-            return base.GetEditor(item, column);
+            MessageBox.Show("Please select an item first!");
+            return;
         }
 
-        protected override void DeleteItems(params CoreRow[] rows)
+        var txnid = rows.First().Get<StockMovement, Guid>(x => x.Transaction);
+        var allrecords = new Client<StockMovement>().Query(
+            new Filter<StockMovement>(x => x.Transaction).IsEqualTo(txnid),
+            new Columns<StockMovement>(x => x.ID)
+        );
+        base.DeleteItems(allrecords.Rows.ToArray());
+    }
+
+    //int typecolumn = -1;
+    protected override DynamicGridStyle GetRowStyle(CoreRow row, DynamicGridStyle style)
+    {
+        var result = base.GetRowStyle(row, style);
+        if (syscolumn == -1)
         {
-            if (!rows.Any())
-            {
-                MessageBox.Show("Please select an item first!");
-                return;
-            }
-
-            var txnid = rows.First().Get<StockMovement, Guid>(x => x.Transaction);
-            var allrecords = new Client<StockMovement>().Query(
-                new Filter<StockMovement>(x => x.Transaction).IsEqualTo(txnid),
-                new Columns<StockMovement>(x => x.ID)
-            );
-            base.DeleteItems(allrecords.Rows.ToArray());
+            var col = row.Table.Columns.FirstOrDefault(x => x.ColumnName.Equals("System"));
+            syscolumn = row.Table.Columns.IndexOf(col);
         }
 
-        //int typecolumn = -1;
-        protected override DynamicGridStyle GetRowStyle(CoreRow row, DynamicGridStyle style)
+        if (row.Values[syscolumn].Equals(true)) //row.Get<StockMovement, bool>(x => x.System))
         {
-            var result = base.GetRowStyle(row, style);
-            if (syscolumn == -1)
+            result = new DynamicGridRowStyle
             {
-                var col = row.Table.Columns.FirstOrDefault(x => x.ColumnName.Equals("System"));
-                syscolumn = row.Table.Columns.IndexOf(col);
-            }
-
-            if (row.Values[syscolumn].Equals(true)) //row.Get<StockMovement, bool>(x => x.System))
-            {
-                result = new DynamicGridRowStyle
-                {
-                    Foreground = new SolidColorBrush(Colors.Gray),
-                    FontStyle = FontStyles.Italic,
-                    Background = new SolidColorBrush(Colors.Gainsboro)
-                };
-                return result;
-            }
-
-            //if (typecolumn == -1)
-            //{
-            //    CoreColumn col = row.Table.Columns.FirstOrDefault(x => x.ColumnName.Equals("Batch.Type"));
-            //    typecolumn = row.Table.Columns.IndexOf(col);
-            //}
-
-            //var type = (StockMovementBatchType)row.Values[typecolumn];
-            //result = new NewDynamicGridStyle()
-            //{
-            //    Background = new SolidColorBrush(
-            //        type == StockMovementBatchType.Stocktake
-            //            ? Colors.LightGreen
-            //            : type == StockMovementBatchType.Receipt
-            //                ? Colors.LightYellow
-            //                : Colors.LightSalmon
-            //    )
-            //};
+                Foreground = new SolidColorBrush(Colors.Gray),
+                FontStyle = FontStyles.Italic,
+                Background = new SolidColorBrush(Colors.Gainsboro)
+            };
             return result;
         }
+
+        //if (typecolumn == -1)
+        //{
+        //    CoreColumn col = row.Table.Columns.FirstOrDefault(x => x.ColumnName.Equals("Batch.Type"));
+        //    typecolumn = row.Table.Columns.IndexOf(col);
+        //}
+
+        //var type = (StockMovementBatchType)row.Values[typecolumn];
+        //result = new NewDynamicGridStyle()
+        //{
+        //    Background = new SolidColorBrush(
+        //        type == StockMovementBatchType.Stocktake
+        //            ? Colors.LightGreen
+        //            : type == StockMovementBatchType.Receipt
+        //                ? Colors.LightYellow
+        //                : Colors.LightSalmon
+        //    )
+        //};
+        return result;
     }
 }

+ 32 - 33
prs.desktop/Panels/Products/Master List/ProductMovementSummary.cs

@@ -3,44 +3,43 @@ using Comal.Classes;
 using InABox.Core;
 using InABox.DynamicGrid;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class ProductMovementSummaryControl : DynamicDataGrid<StockMovement>, IProductControl, ISpecificGrid
 {
-    public class ProductMovementSummaryControl : DynamicDataGrid<StockMovement>, IProductControl
+    public ProductMovementSummaryControl()
     {
-        public ProductMovementSummaryControl()
-        {
-            ColumnsTag = "ProductMovementSummary";
-            HiddenColumns.Add(x => x.System);
-        }
-        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
-        {
-            base.DoReconfigure(options);
-            options.AddRange(DynamicGridOption.SelectColumns, DynamicGridOption.FilterRows);
-        }
+        ColumnsTag = "ProductMovementSummary";
+        HiddenColumns.Add(x => x.System);
+    }
+    protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+    {
+        base.DoReconfigure(options);
+        options.AddRange(DynamicGridOption.SelectColumns, DynamicGridOption.FilterRows);
+    }
 
-        public Product Product { get; set; }
+    public Product Product { get; set; }
 
-        protected override void Reload(Filters<StockMovement> criteria, Columns<StockMovement> columns, ref SortOrder<StockMovement>? sort,
-            Action<CoreTable?, Exception?> action)
+    protected override void Reload(Filters<StockMovement> criteria, Columns<StockMovement> columns, ref SortOrder<StockMovement>? sort,
+        Action<CoreTable?, Exception?> action)
+    {
+        if (Product.ID == Guid.Empty)
         {
-            if (Product.ID == Guid.Empty)
-            {
-                criteria.Add(new Filter<StockMovement>().None());
-            }
-            else
-            {
-                criteria.Add(new Filter<StockMovement>(x => x.Product.ID).IsEqualTo(Product.ID));
-            }
-            criteria.Add(new Filter<StockMovement>(x => x.System).IsEqualTo(false));
-            base.Reload(criteria, columns, ref sort, action);
+            criteria.Add(new Filter<StockMovement>().None());
         }
-
-        //protected override bool FilterRecord(CoreRow row)
-        //{
-        //    bool result = base.FilterRecord(row);
-        //    if (result)
-        //        result = !row.Get<StockMovement, bool>(x => x.Hidden);
-        //    return result;
-        //}
+        else
+        {
+            criteria.Add(new Filter<StockMovement>(x => x.Product.ID).IsEqualTo(Product.ID));
+        }
+        criteria.Add(new Filter<StockMovement>(x => x.System).IsEqualTo(false));
+        base.Reload(criteria, columns, ref sort, action);
     }
+
+    //protected override bool FilterRecord(CoreRow row)
+    //{
+    //    bool result = base.FilterRecord(row);
+    //    if (result)
+    //        result = !row.Get<StockMovement, bool>(x => x.Hidden);
+    //    return result;
+    //}
 }

+ 25 - 9
prs.desktop/Panels/Products/Reservation Management/JobRequisitionReviewGrid.cs

@@ -147,6 +147,18 @@ public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
 
     #region Action Column Buttons
 
+    private void BuildMenu(DynamicMenuColumn column, CoreRow? row)
+    {
+        column.AddItem("Order Required", PRSDesktop.Resources.purchase, OrderRequired_Clicked);
+        column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine_Clicked);
+        column.AddItem("Archive", PRSDesktop.Resources.archive, Archive_Clicked);
+        if (Security.CanView<StockMovement>())
+        {
+            column.AddSeparator();
+            column.AddItem("View Stock Movements", PRSDesktop.Resources.forklift, ViewStockMovements);
+        }
+    }
+
     private bool CheckValidAction(JobRequisitionItem item)
     {
         bool valid = true;
@@ -165,8 +177,8 @@ public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
 
     private void SplitLine(JobRequisitionItem item, double oldItemQty, double newItemQty, string notes)
     {
-        List<JobRequisitionItem> items = new List<JobRequisitionItem>();
-        JobRequisitionItem newItem = new JobRequisitionItem();
+        var items = new List<JobRequisitionItem>();
+        var newItem = new JobRequisitionItem();
         newItem.Requisition.ID = item.Requisition.ID;
         newItem.Requisition.Job.ID = item.Requisition.Job.ID;
         newItem.Requisition.Job.JobNumber = item.Requisition.Job.JobNumber;
@@ -293,6 +305,17 @@ public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
         }
     }
 
+    private void ViewStockMovements(CoreRow? row)
+    {
+        if (row is null) return;
+
+        var requiID = row.Get<StockMovement, Guid>(x => x.ID);
+        var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(StockMovement)) as DynamicDataGrid<StockMovement>)!;
+        grid.OnDefineFilter += (t) => new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(requiID);
+        var window = DynamicGridUtils.CreateGridWindow("Stock movements", grid);
+        window.ShowDialog();
+    }
+
     #endregion
 
     protected override void Reload(Filters<JobRequisitionItem> criteria, Columns<JobRequisitionItem> columns, ref SortOrder<JobRequisitionItem>? sort, Action<CoreTable?, Exception?> action)
@@ -305,13 +328,6 @@ public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
         base.Reload(criteria, columns, ref sort, action);
     }
 
-    private void BuildMenu(DynamicMenuColumn column, CoreRow? row)
-    {
-        column.AddItem("Order Required", PRSDesktop.Resources.purchase, OrderRequired_Clicked);
-        column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine_Clicked);
-        column.AddItem("Archive", PRSDesktop.Resources.archive, Archive_Clicked);
-    }
-
     protected override DragDropEffects OnRowsDragStart(CoreRow[] rows)
     {
         // Only allow dragging the selected rows.

+ 13 - 8
prs.desktop/Panels/Products/Reservation Management/JobRequisitionsPanel.xaml

@@ -7,14 +7,19 @@
              mc:Ignorable="d" 
              d:DesignHeight="450" d:DesignWidth="900"
              x:Name="Panel">
-    <dynamicgrid:DynamicSplitPanel 
-        MasterCaption="Job Requisition Items" 
-        DetailCaption="Available Stock" 
-        Anchor="Detail" 
-        AnchorWidth="600" 
-        AllowableViews="Master,Combined" 
-        View="Combined"
-        DataContext="{Binding ElementName=Panel}">
+    <dynamicgrid:DynamicSplitPanel x:Name="SplitPanel"
+                                   MasterCaption="Job Requisition Items" DetailCaption="Available Stock"
+                                   Anchor="Detail" AnchorWidth="600"
+                                   AllowableViews="Master,Combined" View="Combined"
+                                   OnChanged="SplitPanel_OnChanged"
+                                   DataContext="{Binding ElementName=Panel}">
+
+        <dynamicgrid:DynamicSplitPanel.Header>
+            <Border BorderBrush="Gray" BorderThickness="0.75" Background="WhiteSmoke" Height="25">
+                <Label Content="Job Requisition Items" HorizontalContentAlignment="Center"
+                       VerticalContentAlignment="Center" />
+            </Border>
+        </dynamicgrid:DynamicSplitPanel.Header>
        
         <dynamicgrid:DynamicSplitPanel.Master>
             <local:JobRequisitionReviewGrid  x:Name="JobRequiItems" Margin="0,2,0,0"/>

+ 251 - 196
prs.desktop/Panels/Products/Reservation Management/JobRequisitionsPanel.xaml.cs

@@ -17,279 +17,334 @@ using InABox.WPF.Themes;
 using NPOI.SS.Formula.Functions;
 using System.Collections.ObjectModel;
 using InABox.Clients;
+using InABox.Wpf;
+using InABox.Wpf.Reports;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class JobRequisitionPanelSettings : BaseObject, IGlobalConfigurationSettings
 {
-    public class JobRequisitionPanelSettings : BaseObject, IGlobalConfigurationSettings
-    {
-        [Caption("Default Style for Company", IncludePath = false)]
-        public ProductStyleLink ProductStyle { get; set; }
-    }
+    [Caption("Default Style for Company", IncludePath = false)]
+    public ProductStyleLink ProductStyle { get; set; }
+}
 
-    /// <summary>
-    /// Interaction logic for JobRequisitionsPanel.xaml
-    /// </summary>
-    public partial class JobRequisitionsPanel : UserControl, IPanel<JobRequisitionItem>
-    {
-        private JobRequisitionPanelSettings _settings = null!; // Initialised in Setup()
+/// <summary>
+/// Interaction logic for JobRequisitionsPanel.xaml
+/// </summary>
+public partial class JobRequisitionsPanel : UserControl, IPanel<JobRequisitionItem>
+{
+    private JobRequisitionPanelSettings _settings = null!; // Initialised in Setup()
 
-        public JobRequisitionsPanel()
-        {
-            InitializeComponent();
+    private DynamicSplitPanelView CurrentView;
 
-            JobRequiItems.Reconfigure(options =>
-            {
-                if(Mode == PanelMode.Reserve)
-                {
-                    options.Remove(DynamicGridOption.MultiSelect);
-                }
-                else
-                {
-                    options.Add(DynamicGridOption.MultiSelect);
-                }
-            });
-        }
+    private List<PurchaseOrder> PurchaseOrders = new List<PurchaseOrder>();
 
-        enum PanelMode
-        {
-            Reserve,
-            Purchase
-        }
+    public JobRequisitionsPanel()
+    {
+        InitializeComponent();
 
-        private PanelMode Mode { get; set; }
+        CurrentView = SplitPanel.View;
 
-        private void SelectDetailButton(Button button)
+        JobRequiItems.Reconfigure(options =>
         {
-            reserveBtn.Background = new SolidColorBrush(Colors.WhiteSmoke);
-            reserveBtn.Foreground = new SolidColorBrush(Colors.Black);
-            foreach(var btn in PurchaseOrderButtons.Children.OfType<Button>())
+            var canMultiSelect = Mode != PanelMode.Reserve || SplitPanel.View == DynamicSplitPanelView.Master;
+            if(canMultiSelect)
             {
-                btn.Background = new SolidColorBrush(Colors.WhiteSmoke);
-                btn.Foreground = new SolidColorBrush(Colors.Black);
+                options.Add(DynamicGridOption.MultiSelect);
             }
-            AddPOButton.Background = new SolidColorBrush(Colors.WhiteSmoke);
-            AddPOButton.Foreground = new SolidColorBrush(Colors.Black);
-            button.Background = ThemeManager.SelectedTabItemBackgroundBrush;
-            button.Foreground = ThemeManager.SelectedTabItemForegroundBrush;
-        }
-        private void SelectReserved()
-        {
-            SelectDetailButton(reserveBtn);
-
-            reserveCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
-            purchaseCol.Width = new System.Windows.GridLength(0);
-
-            if(Mode != PanelMode.Reserve)
+            else
             {
-                Mode = PanelMode.Reserve;
-                JobRequiItems.Reconfigure();
+                options.Remove(DynamicGridOption.MultiSelect);
             }
-        }
-        private void SelectNewPurchaseOrder()
-        {
-            SelectDetailButton(AddPOButton);
+        });
+    }
 
-            reserveCol.Width = new System.Windows.GridLength(0);
-            purchaseCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
+    enum PanelMode
+    {
+        Reserve,
+        Purchase
+    }
 
-            if (Mode != PanelMode.Purchase)
-            {
-                Mode = PanelMode.Purchase;
-                JobRequiItems.Reconfigure();
-            }
+    private PanelMode Mode { get; set; }
 
-            purchasing.LoadFromRequiLine(Guid.Empty);
-        }
-        private void SelectPurchaseOrder(Button button, PurchaseOrder order)
+    private void SelectDetailButton(Button button)
+    {
+        reserveBtn.Background = new SolidColorBrush(Colors.WhiteSmoke);
+        reserveBtn.Foreground = new SolidColorBrush(Colors.Black);
+        foreach(var btn in PurchaseOrderButtons.Children.OfType<Button>())
         {
-            SelectDetailButton(button);
+            btn.Background = new SolidColorBrush(Colors.WhiteSmoke);
+            btn.Foreground = new SolidColorBrush(Colors.Black);
+        }
+        AddPOButton.Background = new SolidColorBrush(Colors.WhiteSmoke);
+        AddPOButton.Foreground = new SolidColorBrush(Colors.Black);
+        button.Background = ThemeManager.SelectedTabItemBackgroundBrush;
+        button.Foreground = ThemeManager.SelectedTabItemForegroundBrush;
+    }
+
+    private void Reconfigure()
+    {
+        JobRequiItems.Reconfigure();
+        OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
+    }
 
-            reserveCol.Width = new System.Windows.GridLength(0);
-            purchaseCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
+    private void SelectReserved()
+    {
+        SelectDetailButton(reserveBtn);
 
-            if (Mode != PanelMode.Purchase)
-            {
-                Mode = PanelMode.Purchase;
-                JobRequiItems.Reconfigure();
-            }
+        reserveCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
+        purchaseCol.Width = new System.Windows.GridLength(0);
 
-            purchasing.LoadFromRequiLine(order.ID);
+        if(Mode != PanelMode.Reserve)
+        {
+            Mode = PanelMode.Reserve;
+            Reconfigure();
         }
+    }
+    private void SelectNewPurchaseOrder()
+    {
+        SelectDetailButton(AddPOButton);
+
+        reserveCol.Width = new System.Windows.GridLength(0);
+        purchaseCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
 
-        public bool IsReady { get; set; }
+        if (Mode != PanelMode.Purchase)
+        {
+            Mode = PanelMode.Purchase;
+            Reconfigure();
+        }
 
-        public string SectionName => "Job Requisitions";
+        purchasing.LoadFromRequiLine(Guid.Empty);
+    }
+    private void SelectPurchaseOrder(Button button, PurchaseOrder order)
+    {
+        SelectDetailButton(button);
 
-        public event DataModelUpdateEvent? OnUpdateDataModel;
+        reserveCol.Width = new System.Windows.GridLength(0);
+        purchaseCol.Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
 
-        public void CreateToolbarButtons(IPanelHost host)
+        if (Mode != PanelMode.Purchase)
         {
-            ProductSetupActions.Standard(host);
-            host.CreateSetupAction(new PanelAction() { Caption = "Product Configuration Settings", Image = PRSDesktop.Resources.specifications, OnExecute = ConfigSettingsClick });
+            Mode = PanelMode.Purchase;
+            Reconfigure();
         }
 
-        private void ConfigSettingsClick(PanelAction obj)
+        purchasing.LoadFromRequiLine(order.ID);
+    }
+
+    public bool IsReady { get; set; }
+
+    public string SectionName => "Job Requisitions";
+
+    public event DataModelUpdateEvent? OnUpdateDataModel;
+
+    public void CreateToolbarButtons(IPanelHost host)
+    {
+        ProductSetupActions.Standard(host);
+        host.CreateSetupAction(new PanelAction() { Caption = "Product Configuration Settings", Image = PRSDesktop.Resources.specifications, OnExecute = ConfigSettingsClick });
+
+        if(Mode == PanelMode.Purchase && SplitPanel.IsDetailVisible())
         {
-            var grid = new DynamicItemsListGrid<JobRequisitionPanelSettings>();
-            if(grid.EditItems(new JobRequisitionPanelSettings[] { _settings }))
+            var sectionName = SupplierPurchaseOrderPanel.SectionName;
+            var dataModel = SupplierPurchaseOrderPanel.DataModel(Array.Empty<Guid>());
+            var reports = ReportUtils.LoadReports(sectionName, dataModel);
+            foreach(var report in reports)
             {
-                new GlobalConfiguration<JobRequisitionPanelSettings>().Save(_settings);
-                holdings.CompanyDefaultStyle = _settings.ProductStyle;
+                host.CreateReport(PanelHost.CreateReportAction(report, (selection) =>
+                {
+                    Guid[] ids;
+                    if(selection == Selection.None)
+                    {
+                        ids = Array.Empty<Guid>();
+                    }
+                    else
+                    {
+                        ids = PurchaseOrders.Select(x => x.ID).ToArray();
+                    }
+                    return SupplierPurchaseOrderPanel.DataModel(ids);
+                }));
             }
         }
+    }
 
-        public DataModel DataModel(Selection selection)
+    private void ConfigSettingsClick(PanelAction obj)
+    {
+        var grid = new DynamicItemsListGrid<JobRequisitionPanelSettings>();
+        if(grid.EditItems(new JobRequisitionPanelSettings[] { _settings }))
         {
-            var ids = JobRequiItems.ExtractValues(x => x.ID, selection).ToArray();
-            return new BaseDataModel<JobRequisitionItem>(new Filter<JobRequisitionItem>(x => x.ID).InList(ids));
+            new GlobalConfiguration<JobRequisitionPanelSettings>().Save(_settings);
+            holdings.CompanyDefaultStyle = _settings.ProductStyle;
         }
+    }
 
-        public void Heartbeat(TimeSpan time)
-        {
+    public DataModel DataModel(Selection selection)
+    {
+        var ids = JobRequiItems.ExtractValues(x => x.ID, selection).ToArray();
+        return new BaseDataModel<JobRequisitionItem>(new Filter<JobRequisitionItem>(x => x.ID).InList(ids));
+    }
 
-        }
+    public void Heartbeat(TimeSpan time)
+    {
 
-        public void Refresh()
-        {
-            JobRequiItems.Refresh(false, true);
-        }
+    }
 
-        public Dictionary<string, object[]> Selected()
-        {
-            return new Dictionary<string, object[]>
-            {
-                [typeof(JobRequisitionItem).EntityName()] = JobRequiItems.SelectedRows
-            };
-        }
+    public void Refresh()
+    {
+        JobRequiItems.Refresh(false, true);
+    }
 
-        public void Setup()
+    public Dictionary<string, object[]> Selected()
+    {
+        return new Dictionary<string, object[]>
         {
-            SelectReserved();
+            [typeof(JobRequisitionItem).EntityName()] = JobRequiItems.SelectedRows
+        };
+    }
 
-            JobRequiItems.OnSelectItem += OnJobRequiItemSelected;
-            JobRequiItems.Refresh(true, false);
+    public void Setup()
+    {
+        SelectReserved();
 
-            _settings = new GlobalConfiguration<JobRequisitionPanelSettings>().Load();
-            holdings.CompanyDefaultStyle = _settings.ProductStyle;
+        JobRequiItems.OnSelectItem += OnJobRequiItemSelected;
+        JobRequiItems.Refresh(true, false);
 
-            holdings.OnHoldingsReviewRefresh += Holdings_OnHoldingsReviewRefresh;
+        _settings = new GlobalConfiguration<JobRequisitionPanelSettings>().Load();
+        holdings.CompanyDefaultStyle = _settings.ProductStyle;
 
-            purchasing.OnPurchaseOrderSaved += Refresh;
-        }
+        holdings.OnHoldingsReviewRefresh += Holdings_OnHoldingsReviewRefresh;
+
+        purchasing.OnPurchaseOrderSaved += Refresh;
+    }
 
-        private void OnJobRequiItemSelected(object sender, DynamicGridSelectionEventArgs e)
+    private void OnJobRequiItemSelected(object sender, DynamicGridSelectionEventArgs e)
+    {
+        if (SplitPanel.IsDetailVisible())
         {
             holdings.Item = e.Rows?.FirstOrDefault()?.ToObject<JobRequisitionItem>();
             RefreshPurchaseOrderButtons();
         }
+    }
 
-        private void RefreshPurchaseOrderButtons()
+    private void RefreshPurchaseOrderButtons()
+    {
+        var requiIDs = JobRequiItems.SelectedRows
+            .Select(x => x.Get<JobRequisitionItem, Guid>(x => x.ID))
+            .ToArray();
+
+        var pos = Client.Query(
+            new Filter<PurchaseOrder>(x => x.ID).InQuery(
+                new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID)
+                    .InList(requiIDs),
+                x => x.PurchaseOrderItem.PurchaseOrderLink.ID),
+            new Columns<PurchaseOrder>(x => x.ID)
+                .Add(x => x.PONumber))
+            .ToObjects<PurchaseOrder>().ToList();
+        PurchaseOrders = pos;
+
+        PurchaseOrderButtons.Children.Clear();
+        var buttons = new List<(Button btn, PurchaseOrder po)>();
+        foreach (var po in pos)
         {
-            var requiIDs = JobRequiItems.SelectedRows
-                .Select(x => x.Get<JobRequisitionItem, Guid>(x => x.ID))
-                .ToArray();
-
-            var pos = Client.Query(
-                new Filter<PurchaseOrder>(x => x.ID).InQuery(
-                    new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID)
-                        .InList(requiIDs),
-                    x => x.PurchaseOrderItem.PurchaseOrderLink.ID),
-                new Columns<PurchaseOrder>(x => x.ID)
-                    .Add(x => x.PONumber))
-                .ToObjects<PurchaseOrder>().ToList();
-
-            PurchaseOrderButtons.Children.Clear();
-            var buttons = new List<(Button btn, PurchaseOrder po)>();
-            foreach (var po in pos)
+            var button = new Button
             {
-                var button = new Button
-                {
-                    Content = po.PONumber,
-                    Height = 30,
-                    FontWeight = FontWeights.DemiBold,
-                    BorderBrush = new SolidColorBrush(Colors.DarkGray),
-                    BorderThickness = new Thickness(1.25),
-                    Margin = new Thickness(1, 2, 0, 0),
-                    Padding = new Thickness(13, 3, 13, 3)
-                };
-                button.Click += (o, e) =>
-                {
-                    SelectPurchaseOrder(button, po);
-                };
-                buttons.Add((button, po));
-                PurchaseOrderButtons.Children.Add(button);
-            }
-            if (Mode == PanelMode.Purchase)
+                Content = po.PONumber,
+                Height = 30,
+                FontWeight = FontWeights.DemiBold,
+                BorderBrush = new SolidColorBrush(Colors.DarkGray),
+                BorderThickness = new Thickness(1.25),
+                Margin = new Thickness(1, 2, 0, 0),
+                Padding = new Thickness(13, 3, 13, 3)
+            };
+            button.Click += (o, e) =>
             {
-                if (buttons.Count == 0)
-                {
-                    SelectNewPurchaseOrder();
-                }
-                else
-                {
-                    var btn = buttons[0];
-                    SelectPurchaseOrder(btn.btn, btn.po);
-                }
+                SelectPurchaseOrder(button, po);
+            };
+            buttons.Add((button, po));
+            PurchaseOrderButtons.Children.Add(button);
+        }
+        if (Mode == PanelMode.Purchase)
+        {
+            if (buttons.Count == 0)
+            {
+                SelectNewPurchaseOrder();
             }
-            else if (Mode == PanelMode.Reserve)
+            else
             {
-                SelectReserved();
+                var btn = buttons[0];
+                SelectPurchaseOrder(btn.btn, btn.po);
             }
         }
-
-        private void Holdings_OnHoldingsReviewRefresh()
+        else if (Mode == PanelMode.Reserve)
         {
-            Refresh();
+            SelectReserved();
         }
+    }
 
-        private void CheckSaved(CancelEventArgs cancel)
+    private void Holdings_OnHoldingsReviewRefresh()
+    {
+        Refresh();
+    }
+
+    private void CheckSaved(CancelEventArgs cancel)
+    {
+        if (!SplitPanel.IsDetailVisible() || !purchasing.EditorChanged)
         {
-            if (!purchasing.EditorChanged)
-            {
-                return;
-            }
-            var result = MessageBox.Show("You have changes that have not been saved; do you wish to save these changes?", "Save Changes?", MessageBoxButton.YesNoCancel);
-            if (result == MessageBoxResult.Yes)
-            {
-                purchasing.Editor.SaveItem(cancel);
-                if (!cancel.Cancel)
-                {
-                    MessageBox.Show("Purchase Order saved.");
-                }
-            }
-            else if (result == MessageBoxResult.Cancel)
-            {
-                cancel.Cancel = true;
-            }
+            return;
         }
-
-        public void Shutdown(CancelEventArgs? cancel)
+        var result = MessageWindow.ShowYesNoCancel("You have changes that have not been saved; do you wish to save these changes?", "Save Changes?");
+        if (result == MessageWindowResult.Yes)
         {
-            if(cancel is not null && purchasing.EditorChanged)
+            purchasing.Editor.SaveItem(cancel);
+            if (!cancel.Cancel)
             {
-                CheckSaved(cancel);
+                MessageWindow.ShowMessage("Purchase Order saved.", "Success");
             }
         }
+        else if (result == MessageWindowResult.Cancel)
+        {
+            cancel.Cancel = true;
+        }
+    }
 
-        private void ReserveStock_Clicked(object sender, System.Windows.RoutedEventArgs e)
+    public void Shutdown(CancelEventArgs? cancel)
+    {
+        if(cancel is not null && purchasing.EditorChanged)
         {
-            SelectReserved();
+            CheckSaved(cancel);
         }
+    }
+
+    private void ReserveStock_Clicked(object sender, System.Windows.RoutedEventArgs e)
+    {
+        SelectReserved();
+    }
 
-        private void Purchasing_Drop(object sender, DragEventArgs e)
+    private void Purchasing_Drop(object sender, DragEventArgs e)
+    {
+        if(DynamicGridUtils.TryGetDropData(e, out var type, out var table))
         {
-            if(DynamicGridUtils.TryGetDropData(e, out var type, out var table))
+            if(type == typeof(JobRequisitionItem))
             {
-                if(type == typeof(JobRequisitionItem))
-                {
-                    purchasing.DropItems(table.Rows);
-                }
+                purchasing.DropItems(table.Rows);
             }
         }
+    }
+
+    private void AddPOButton_Click(object sender, RoutedEventArgs e)
+    {
+        SelectNewPurchaseOrder();
+    }
 
-        private void AddPOButton_Click(object sender, RoutedEventArgs e)
+    private void SplitPanel_OnChanged(object sender, DynamicSplitPanelSettings e)
+    {
+        JobRequiItems.Reconfigure();
+        if(CurrentView != e.View)
         {
-            SelectNewPurchaseOrder();
+            CurrentView = e.View;
+            if (e.View != DynamicSplitPanelView.Master)
+            {
+                Refresh();
+            }
         }
     }
 }

+ 11 - 1
prs.desktop/Panels/Suppliers/PurchaseOrders/SupplierPurchaseOrderItemOneToMany.cs

@@ -469,7 +469,17 @@ namespace PRSDesktop
 
             base.SelectItems(rows);
         }
-        
+
+        protected override void CustomiseEditor(PurchaseOrderItem[] items, DynamicGridColumn column, BaseEditor editor)
+        {
+            base.CustomiseEditor(items, column, editor);
+
+            if(items.Any(x => x.ReceivedDate != DateTime.MinValue) && !new Column<PurchaseOrderItem>(x => x.ReceivedDate).IsEqualTo(column.ColumnName))
+            {
+                editor.Editable = editor.Editable.Combine(Editable.Disabled);
+            }
+        }
+
         protected override void OnAfterEditorValueChanged(DynamicEditorGrid? grid, PurchaseOrderItem[] items, AfterEditorValueChangedArgs args, Dictionary<string, object?> changes)
         {
             base.OnAfterEditorValueChanged(grid, items, args, changes);

+ 10 - 2
prs.desktop/Panels/Suppliers/PurchaseOrders/SupplierPurchaseOrderPanel.xaml.cs

@@ -53,6 +53,7 @@ public partial class SupplierPurchaseOrderPanel : UserControl, IPanel<PurchaseOr
 
     public bool IsReady { get; set; }
 
+
     public event DataModelUpdateEvent? OnUpdateDataModel;
 
     public Dictionary<string, object[]> Selected()
@@ -70,12 +71,19 @@ public partial class SupplierPurchaseOrderPanel : UserControl, IPanel<PurchaseOr
             true);
     }
 
-    public string SectionName => "Purchase Orders";
+    string IDataModelSource.SectionName => SectionName;
+
+    public static string SectionName => "Purchase Orders";
 
     public DataModel DataModel(Selection selection)
     {
         var ids = Orders.ExtractValues(x => x.ID, selection).ToArray();
-        return new BaseDataModel<PurchaseOrder>(new Filter<PurchaseOrder>(x => x.ID).InList(ids));
+        return DataModel(ids);
+    }
+
+    public static DataModel DataModel(Guid[] poIDs)
+    {
+        return new BaseDataModel<PurchaseOrder>(new Filter<PurchaseOrder>(x => x.ID).InList(poIDs));
     }
 
     public void Refresh()