Kenric Nugteren пре 8 месеци
родитељ
комит
0eb81b4f7b

+ 2 - 1
prs.classes/Settings/DFLayoutVideoSettings.cs

@@ -9,7 +9,8 @@ namespace PRSClasses
 {
     public class DFLayoutVideoSettings : IGlobalConfigurationSettings
     {
-        [IntegerEditor(ToolTip = "Maximum video length (sec)")]
+        [Comment("Maximum video length (sec)")]
+        [IntegerEditor]
         public int DefaultMaximumVideoLength { get; set; } = 15;
 
         [EnumLookupEditor(typeof(VideoQuality))]

+ 2 - 1
prs.desktop/Panels/DataEntry/DataEntryPanel.xaml.cs

@@ -23,7 +23,8 @@ public class DataEntryPanelSettings : BaseObject, IUserConfigurationSettings
     [NullEditor]
     public double PreviewWidth { get; set; }
 
-    [DoubleEditor(ToolTip = "Age of cached documents before they are deleted (days)")]
+    [Comment("Age of cached documents before they are deleted (days)")]
+    [DoubleEditor]
     public double CacheAge { get; set; } = DefaultCacheAge;
 }
 

+ 2 - 1
prs.desktop/Panels/Reservation Management/ReservationManagementGlobalSettings.cs

@@ -20,6 +20,7 @@ public class ReservationManagementGlobalSettings : BaseObject, IGlobalConfigurat
     public int DueDateWarning { get; set; } = 2;
 
     [EditorSequence(4)]
-    [CheckBoxEditor(ToolTip = "Automatically issue purchase orders raised for treatment")]
+    [Comment("Automatically issue purchase orders raised for treatment")]
+    [CheckBoxEditor]
     public bool AutoIssueTreatmentPOs { get; set; } = false;
 }

+ 947 - 947
prs.desktop/Panels/Staging/StagingPanel.xaml.cs

@@ -21,35 +21,36 @@ using System.Threading.Tasks;
 using NPOI.SS.Formula.Functions;
 using Columns = InABox.Core.Columns;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+[Caption("Staging Panel Settings")]
+public class StagingPanellSettings : BaseObject, IGlobalConfigurationSettings
 {
-    [Caption("Staging Panel Settings")]
-    public class StagingPanellSettings : BaseObject, IGlobalConfigurationSettings
-    {
-        [Caption("PDF Markup Program Pathway", IncludePath = false)]
-        [FileNameEditor]
-        public string MarkupPathway { get; set; }
+    [Caption("PDF Markup Program Pathway", IncludePath = false)]
+    [FileNameEditor]
+    public string MarkupPathway { get; set; }
 
-        [FolderEditor(Environment.SpecialFolder.CommonDocuments)]
-        public string SetoutsFolder { get; set; }
+    [FolderEditor(Environment.SpecialFolder.CommonDocuments)]
+    public string SetoutsFolder { get; set; }
 
-        [ScriptEditor]
-        public string? Script { get; set; }
+    [ScriptEditor]
+    public string? Script { get; set; }
 
-        [IntegerEditor(Caption = "Maximum Document Size (MB)", ToolTip = "The user will be warned when uploading documents which are larger than this size in megabytes. Set to zero for no maximum.")]
-        public int MaximumDocumentSize { get; set; }
+    [Comment("Maximum Document Size (MB)")]
+    [IntegerEditor(Caption = "Maximum Document Size (MB)", ToolTip = "The user will be warned when uploading documents which are larger than this size in megabytes. Set to zero for no maximum.")]
+    public int MaximumDocumentSize { get; set; }
 
-        public StagingPanellSettings()
-        {
-            MarkupPathway = "";
-            SetoutsFolder = "";
-            Script = null;
-            MaximumDocumentSize = 0;
-        }
+    public StagingPanellSettings()
+    {
+        MarkupPathway = "";
+        SetoutsFolder = "";
+        Script = null;
+        MaximumDocumentSize = 0;
+    }
 
-        public string DefaultScript()
-        {
-            return @"
+    public string DefaultScript()
+    {
+        return @"
 using PRSDesktop;
 using InABox.Core;
 using System.Collections.Generic;
@@ -61,1190 +62,1189 @@ public class Module
         // Perform customisation on the setouts when they are added to the 'Staged Documents' grid.
     }*/
 }";
-        }
     }
+}
 
-    public class CustomiseSetoutsArgs
-    {
-        public IReadOnlyList<Tuple<StagingSetout, Document>> Setouts;
+public class CustomiseSetoutsArgs
+{
+    public IReadOnlyList<Tuple<StagingSetout, Document>> Setouts;
 
-        public CustomiseSetoutsArgs(IReadOnlyList<Tuple<StagingSetout, Document>> setouts)
-        {
-            Setouts = setouts;
-        }
+    public CustomiseSetoutsArgs(IReadOnlyList<Tuple<StagingSetout, Document>> setouts)
+    {
+        Setouts = setouts;
     }
+}
 
+/// <summary>
+/// Interaction logic for StagingPanel.xaml
+/// </summary>
+public partial class StagingPanel : UserControl, IPanel<StagingSetout>
+{
+    private StagingPanellSettings _settings = new StagingPanellSettings();
+    
     /// <summary>
-    /// Interaction logic for StagingPanel.xaml
+    /// The currently selected setout.
     /// </summary>
-    public partial class StagingPanel : UserControl, IPanel<StagingSetout>
-    {
-        private StagingPanellSettings _settings = new StagingPanellSettings();
-        
-        /// <summary>
-        /// The currently selected setout.
-        /// </summary>
-        private StagingSetout? selectedSetout;
+    private StagingSetout? selectedSetout;
 
-        /// <summary>
-        /// All currently selected setouts; <see cref="selectedSetout"/> will be a member of this list.
-        /// </summary>
-        private List<StagingSetout> selectedSetouts = new();
+    /// <summary>
+    /// All currently selected setouts; <see cref="selectedSetout"/> will be a member of this list.
+    /// </summary>
+    private List<StagingSetout> selectedSetouts = new();
 
-        private CoreTable _templateGroups = null!;
+    private CoreTable _templateGroups = null!;
 
-        #region Script Stuff
+    #region Script Stuff
 
-        private MethodInfo? _customiseSetoutsMethod;
-        private MethodInfo? CustomiseSetoutsMethod
+    private MethodInfo? _customiseSetoutsMethod;
+    private MethodInfo? CustomiseSetoutsMethod
+    {
+        get
         {
-            get
-            {
-                EnsureScript();
-                return _customiseSetoutsMethod;
-            }
+            EnsureScript();
+            return _customiseSetoutsMethod;
         }
-        private object? _scriptObject;
-        private object? ScriptObject
+    }
+    private object? _scriptObject;
+    private object? ScriptObject
+    {
+        get
         {
-            get
-            {
-                EnsureScript();
-                return _scriptObject;
-            }
+            EnsureScript();
+            return _scriptObject;
         }
-        private ScriptDocument? _script;
-        private ScriptDocument? Script
+    }
+    private ScriptDocument? _script;
+    private ScriptDocument? Script
+    {
+        get
         {
-            get
-            {
-                EnsureScript();
-                return _script;
-            }
+            EnsureScript();
+            return _script;
         }
-        private void EnsureScript()
+    }
+    private void EnsureScript()
+    {
+        if (_script is null && !_settings.Script.IsNullOrWhiteSpace())
         {
-            if (_script is null && !_settings.Script.IsNullOrWhiteSpace())
+            _script = new ScriptDocument(_settings.Script);
+            if (!_script.Compile())
             {
-                _script = new ScriptDocument(_settings.Script);
-                if (!_script.Compile())
-                {
-                    throw new Exception("Script in Staging Panel Settings failed to compile!");
-                }
-                _scriptObject = _script?.GetObject();
-                _customiseSetoutsMethod = _script?.GetMethod(methodName: "CustomiseSetouts");
+                throw new Exception("Script in Staging Panel Settings failed to compile!");
             }
+            _scriptObject = _script?.GetObject();
+            _customiseSetoutsMethod = _script?.GetMethod(methodName: "CustomiseSetouts");
         }
+    }
 
-        #endregion
+    #endregion
 
-        public StagingPanel()
-        {
-            InitializeComponent();
-            SectionName = nameof(StagingPanel);
-        }
+    public StagingPanel()
+    {
+        InitializeComponent();
+        SectionName = nameof(StagingPanel);
+    }
 
-        public void Setup()
-        {
-            _settings = new GlobalConfiguration<StagingPanellSettings>().Load();
-            
-            _templateGroups = new Client<ManufacturingTemplateGroup>().Query();
-            
-            MarkUpButton.Visibility = Security.IsAllowed<CanMarkUpSetouts>() ? Visibility.Visible : Visibility.Hidden;
-            RejectButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
-            ApproveButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
-            ProcessButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
-
-            //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
-            stagingSetoutGrid.PanelSettings = _settings;
-            stagingSetoutGrid.Refresh(true, false);
-            SetoutComponentGrid.Refresh(true, false);
-            
-        }
+    public void Setup()
+    {
+        _settings = new GlobalConfiguration<StagingPanellSettings>().Load();
+        
+        _templateGroups = new Client<ManufacturingTemplateGroup>().Query();
+        
+        MarkUpButton.Visibility = Security.IsAllowed<CanMarkUpSetouts>() ? Visibility.Visible : Visibility.Hidden;
+        RejectButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
+        ApproveButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
+        ProcessButton.Visibility = Security.IsAllowed<CanApproveSetouts>() ? Visibility.Visible : Visibility.Hidden;
+
+        //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
+        stagingSetoutGrid.PanelSettings = _settings;
+        stagingSetoutGrid.Refresh(true, false);
+        SetoutComponentGrid.Refresh(true, false);
+        
+    }
 
-        private bool CanViewPackets() => MainPanel.View != DynamicSplitPanelView.Master && NestedPanel.View != DynamicSplitPanelView.Master;
+    private bool CanViewPackets() => MainPanel.View != DynamicSplitPanelView.Master && NestedPanel.View != DynamicSplitPanelView.Master;
 
-        private void NestedPanel_OnChanged(object sender, DynamicSplitPanelSettings e)
+    private void NestedPanel_OnChanged(object sender, DynamicSplitPanelSettings e)
+    {
+        if(CanViewPackets())
         {
-            if(CanViewPackets())
-            {
-                ManufacturingPacketList.Setout = selectedSetout;
-                SetoutComponentGrid.StagingSetout = selectedSetout;
-            }
+            ManufacturingPacketList.Setout = selectedSetout;
+            SetoutComponentGrid.StagingSetout = selectedSetout;
         }
+    }
+
+    #region Document Viewer
 
-        #region Document Viewer
+    public enum DocumentMode
+    {
+        Markup,
+        Complete,
+        Locked
+    }
+    private DocumentMode _mode;
+    private DocumentMode Mode
+    {
+        get => _mode;
+        set  => SetMode(value);
+    }
 
-        public enum DocumentMode
+    private void SetMode(DocumentMode mode)
+    {
+        _mode = mode;
+        if (_mode == DocumentMode.Markup)
         {
-            Markup,
-            Complete,
-            Locked
+            MarkUpButton.Content = "Mark Up";
+            MarkUpButton.IsEnabled = Document != null && !Document.Approved;
+            UpdateOriginalButton.Visibility =
+                Document != null && !String.Equals(Document.DocumentLink.CRC, selectedSetout?.OriginalCRC)
+                    ? Visibility.Visible
+                    : Visibility.Collapsed;
+            ProcessButton.IsEnabled = Document != null && Document.Approved;
+            RejectButton.IsEnabled = Document != null && !Document.Approved;
+            ApproveButton.IsEnabled = Document != null;
         }
-        private DocumentMode _mode;
-        private DocumentMode Mode
+        else if (_mode == DocumentMode.Complete)
         {
-            get => _mode;
-            set  => SetMode(value);
+            MarkUpButton.Content = "Complete";
+            MarkUpButton.IsEnabled = Document != null;
+            UpdateOriginalButton.Visibility = Visibility.Collapsed;
+            ProcessButton.IsEnabled = false;
+            RejectButton.IsEnabled = false;
+            ApproveButton.IsEnabled = false;
         }
-
-        private void SetMode(DocumentMode mode)
+        else if (_mode == DocumentMode.Locked)
         {
-            _mode = mode;
-            if (_mode == DocumentMode.Markup)
-            {
-                MarkUpButton.Content = "Mark Up";
-                MarkUpButton.IsEnabled = Document != null && !Document.Approved;
-                UpdateOriginalButton.Visibility =
-                    Document != null && !String.Equals(Document.DocumentLink.CRC, selectedSetout?.OriginalCRC)
-                        ? Visibility.Visible
-                        : Visibility.Collapsed;
-                ProcessButton.IsEnabled = Document != null && Document.Approved;
-                RejectButton.IsEnabled = Document != null && !Document.Approved;
-                ApproveButton.IsEnabled = Document != null;
-            }
-            else if (_mode == DocumentMode.Complete)
-            {
-                MarkUpButton.Content = "Complete";
-                MarkUpButton.IsEnabled = Document != null;
-                UpdateOriginalButton.Visibility = Visibility.Collapsed;
-                ProcessButton.IsEnabled = false;
-                RejectButton.IsEnabled = false;
-                ApproveButton.IsEnabled = false;
-            }
-            else if (_mode == DocumentMode.Locked)
-            {
-                MarkUpButton.Content = "Locked";
-                MarkUpButton.IsEnabled = false;
-                UpdateOriginalButton.Visibility = Visibility.Collapsed;
-                ProcessButton.IsEnabled = false;
-                RejectButton.IsEnabled = false;
-                ApproveButton.IsEnabled = false;
-            }
+            MarkUpButton.Content = "Locked";
+            MarkUpButton.IsEnabled = false;
+            UpdateOriginalButton.Visibility = Visibility.Collapsed;
+            ProcessButton.IsEnabled = false;
+            RejectButton.IsEnabled = false;
+            ApproveButton.IsEnabled = false;
         }
+    }
 
-        private StagingSetoutDocument? _document;
+    private StagingSetoutDocument? _document;
 
-        private StagingSetoutDocument? Document
+    private StagingSetoutDocument? Document
+    {
+        get => _document;
+        set
         {
-            get => _document;
-            set
+            _document = value;
+            if(_document is not null)
             {
-                _document = value;
-                if(_document is not null)
-                {
-                    ApproveButton.Content = _document.Approved ? "Unapprove" : "Approve";
-                }
+                ApproveButton.Content = _document.Approved ? "Unapprove" : "Approve";
             }
         }
+    }
 
-        private byte[]? _documentdata = null;
+    private byte[]? _documentdata = null;
 
-        private void ClearDocuments()
-        {
-            Document = null;
-            RenderDocuments(null);
-        }
+    private void ClearDocuments()
+    {
+        Document = null;
+        RenderDocuments(null);
+    }
 
-        private List<byte[]>? GetDocuments(StagingSetoutDocument? document)
+    private List<byte[]>? GetDocuments(StagingSetoutDocument? document)
+    {
+        if(document is null)
         {
-            if(document is null)
-            {
-                return null;
-            }
-            var table = new Client<Document>().Query(
-                new Filter<Document>(x => x.ID).IsEqualTo(document.DocumentLink.ID),
-                Columns.None<Document>().Add(x => x.Data));
-            var first = table.Rows.FirstOrDefault();
-            if (first is null)
-                return null;
-            _documentdata = first.Get<Document, byte[]>(x => x.Data);
-            return ImageUtils.RenderPDFToImageBytes(_documentdata);
+            return null;
         }
-        
-        private void RenderDocuments(List<byte[]>? documents)
+        var table = new Client<Document>().Query(
+            new Filter<Document>(x => x.ID).IsEqualTo(document.DocumentLink.ID),
+            Columns.None<Document>().Add(x => x.Data));
+        var first = table.Rows.FirstOrDefault();
+        if (first is null)
+            return null;
+        _documentdata = first.Get<Document, byte[]>(x => x.Data);
+        return ImageUtils.RenderPDFToImageBytes(_documentdata);
+    }
+    
+    private void RenderDocuments(List<byte[]>? documents)
+    {
+        DocumentViewer.Children.Clear();
+        if(documents is not null)
         {
-            DocumentViewer.Children.Clear();
-            if(documents is not null)
+            foreach (var image in documents)
             {
-                foreach (var image in documents)
+                DocumentViewer.Children.Add(new Image
                 {
-                    DocumentViewer.Children.Add(new Image
-                    {
-                        Source = ImageUtils.LoadImage(image),
-                        Margin = new Thickness(0, 0, 0, 20)
-                    });
-                }
+                    Source = ImageUtils.LoadImage(image),
+                    Margin = new Thickness(0, 0, 0, 20)
+                });
             }
         }
+    }
 
-        private void ProcessButton_Click(object sender, RoutedEventArgs e)
+    private void ProcessButton_Click(object sender, RoutedEventArgs e)
+    {
+        bool bulkApprove = false;
+        if (selectedSetouts.Count > 1)
         {
-            bool bulkApprove = false;
-            if (selectedSetouts.Count > 1)
+            if (MessageBox.Show("Bulk approve? (Skip individual setout approval)", "Continue?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
             {
-                if (MessageBox.Show("Bulk approve? (Skip individual setout approval)", "Continue?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
-                {
-                    bulkApprove = true;
-                    Progress.Show("Approving Setouts..");
-                }
+                bulkApprove = true;
+                Progress.Show("Approving Setouts..");
             }
+        }
 
-            if (selectedSetouts.Any(x => x.UnapprovedDocuments > 0))
-            {
-                MessageBox.Show("Cannot process setouts with unapproved documents.");
-                Progress.Close();
-                return;
-            }
+        if (selectedSetouts.Any(x => x.UnapprovedDocuments > 0))
+        {
+            MessageBox.Show("Cannot process setouts with unapproved documents.");
+            Progress.Close();
+            return;
+        }
 
-            if (selectedSetouts.Any(x => x.JobLink.ID == Guid.Empty))
-            {
-                MessageBox.Show("Cannot process setout without a linked job.");
-                Progress.Close();
-                return;
-            }
+        if (selectedSetouts.Any(x => x.JobLink.ID == Guid.Empty))
+        {
+            MessageBox.Show("Cannot process setout without a linked job.");
+            Progress.Close();
+            return;
+        }
 
-            if (ManufacturingPacketList.Packets.Any(x => x.Template.ID == Guid.Empty))
+        if (ManufacturingPacketList.Packets.Any(x => x.Template.ID == Guid.Empty))
+        {
+            MessageBox.Show("Cannot process manufacturing packets without templates.");
+            Progress.Close();
+            return;
+        }
+
+        if (selectedSetouts.Any(x => x.Packets == 0))
+        {
+            if (MessageBox.Show("Warning: some setouts do not have any manufacturing packets: are you sure you wish to proceed?", "Warning", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
             {
-                MessageBox.Show("Cannot process manufacturing packets without templates.");
                 Progress.Close();
                 return;
             }
-
-            if (selectedSetouts.Any(x => x.Packets == 0))
-            {
-                if (MessageBox.Show("Warning: some setouts do not have any manufacturing packets: are you sure you wish to proceed?", "Warning", MessageBoxButton.YesNoCancel) != MessageBoxResult.Yes)
-                {
-                    Progress.Close();
-                    return;
-                }
-            }
-
+        }
 
 
-            string message = "";
 
-            foreach (var item in selectedSetouts)
-            {
-                if (bulkApprove)
-                    Progress.Show("Working on " + item.Number);
-                var returnstring = ApproveSetout(item, bulkApprove);
-                if (!string.IsNullOrWhiteSpace(returnstring))
-                    message = message + returnstring + Environment.NewLine;
-            }
+        string message = "";
 
+        foreach (var item in selectedSetouts)
+        {
             if (bulkApprove)
-                Progress.Close();
+                Progress.Show("Working on " + item.Number);
+            var returnstring = ApproveSetout(item, bulkApprove);
+            if (!string.IsNullOrWhiteSpace(returnstring))
+                message = message + returnstring + Environment.NewLine;
+        }
 
-            new Client<StagingSetout>().Save(selectedSetouts, "Updated from staging screen");
-            selectedSetout = null;
-            Refresh();
+        if (bulkApprove)
+            Progress.Close();
 
-            if (!message.IsNullOrWhiteSpace())
-            {
-                MessageBox.Show($"Result:\n{message}");
-            }
-            MainPanel.View = DynamicSplitPanelView.Combined;
-            NestedPanel.View = DynamicSplitPanelView.Master;
+        new Client<StagingSetout>().Save(selectedSetouts, "Updated from staging screen");
+        selectedSetout = null;
+        Refresh();
+
+        if (!message.IsNullOrWhiteSpace())
+        {
+            MessageBox.Show($"Result:\n{message}");
         }
+        MainPanel.View = DynamicSplitPanelView.Combined;
+        NestedPanel.View = DynamicSplitPanelView.Master;
+    }
 
-        private string ApproveSetout(StagingSetout item, bool bulkapprove)
+    private string ApproveSetout(StagingSetout item, bool bulkapprove)
+    {
+        if (item.Group.ID == Guid.Empty)
         {
-            if (item.Group.ID == Guid.Empty)
+            var message = "Setout has no group assigned";
+            if (Security.IsAllowed<CanApproveSetoutsWithoutGroup>())
             {
-                var message = "Setout has no group assigned";
-                if (Security.IsAllowed<CanApproveSetoutsWithoutGroup>())
-                {
-                    if (MessageBox.Show(message + ", continue?", "Continue?", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
-                        return "";
-                }
-                else
-                {
-                    MessageBox.Show(message + ", please assign a group!");
+                if (MessageBox.Show(message + ", continue?", "Continue?", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
                     return "";
-                }
             }
+            else
+            {
+                MessageBox.Show(message + ", please assign a group!");
+                return "";
+            }
+        }
 
-            var setoutDocument = new Client<StagingSetoutDocument>()
-                .Query(
-                    new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(item.ID),
-                    Columns.None<StagingSetoutDocument>().Add(x => x.ID, x => x.DocumentLink.ID, x => x.DocumentLink.FileName))
-                .ToObjects<StagingSetoutDocument>().FirstOrDefault();
+        var setoutDocument = new Client<StagingSetoutDocument>()
+            .Query(
+                new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(item.ID),
+                Columns.None<StagingSetoutDocument>().Add(x => x.ID, x => x.DocumentLink.ID, x => x.DocumentLink.FileName))
+            .ToObjects<StagingSetoutDocument>().FirstOrDefault();
 
-            if (setoutDocument is null)
-                return "";
+        if (setoutDocument is null)
+            return "";
 
-            var setout = new Client<Setout>()
-                .Query(
-                    new Filter<Setout>(x => x.Number).IsEqualTo(item.Number),
-                    Columns.Required<Setout>().Add(x => x.ID))
-                .ToObjects<Setout>().FirstOrDefault();
+        var setout = new Client<Setout>()
+            .Query(
+                new Filter<Setout>(x => x.Number).IsEqualTo(item.Number),
+                Columns.Required<Setout>().Add(x => x.ID))
+            .ToObjects<Setout>().FirstOrDefault();
 
-            string result;
-            //setout already exists - create new setoutdoc and supercede old ones
-            if (setout is not null)
-            {
-                if (!bulkapprove)
-                    if (MessageBox.Show("Supercede existing documents?", "Proceed?", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-                        return "";
+        string result;
+        //setout already exists - create new setoutdoc and supercede old ones
+        if (setout is not null)
+        {
+            if (!bulkapprove)
+                if (MessageBox.Show("Supercede existing documents?", "Proceed?", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
+                    return "";
 
-                setout.Group.ID = item.Group.ID;
+            setout.Group.ID = item.Group.ID;
 
-                item.Setout.ID = setout.ID;
+            item.Setout.ID = setout.ID;
 
-                var setoutdoc = new SetoutDocument();
-                setoutdoc.EntityLink.ID = setout.ID;
-                setoutdoc.DocumentLink.ID = setoutDocument.DocumentLink.ID;
+            var setoutdoc = new SetoutDocument();
+            setoutdoc.EntityLink.ID = setout.ID;
+            setoutdoc.DocumentLink.ID = setoutDocument.DocumentLink.ID;
 
-                var setoutdocs = new List<SetoutDocument>
-                {
-                    setoutdoc
-                };
+            var setoutdocs = new List<SetoutDocument>
+            {
+                setoutdoc
+            };
 
-                CoreTable oldDocsTable = new Client<SetoutDocument>().Query(
-                    new Filter<SetoutDocument>(x => x.EntityLink.ID).IsEqualTo((Guid)setout.ID)
-                    .And(x => x.DocumentLink.ID).IsNotEqualTo(item.Group.OptimizationDocument.ID)
-                    );
-                foreach (var row in oldDocsTable.Rows)
+            CoreTable oldDocsTable = new Client<SetoutDocument>().Query(
+                new Filter<SetoutDocument>(x => x.EntityLink.ID).IsEqualTo((Guid)setout.ID)
+                .And(x => x.DocumentLink.ID).IsNotEqualTo(item.Group.OptimizationDocument.ID)
+                );
+            foreach (var row in oldDocsTable.Rows)
+            {
+                var oldDoc = row.ToObject<SetoutDocument>();
+                if (oldDoc.Superceded == DateTime.MinValue)
                 {
-                    var oldDoc = row.ToObject<SetoutDocument>();
-                    if (oldDoc.Superceded == DateTime.MinValue)
-                    {
-                        oldDoc.Superceded = DateTime.Now;
-                        setoutdocs.Add(oldDoc);
-                    }
+                    oldDoc.Superceded = DateTime.Now;
+                    setoutdocs.Add(oldDoc);
                 }
-                new Client<SetoutDocument>().Save(setoutdocs, "Updated from Staging Screen");
-                new Client<Setout>().Save((Setout)setout, "Updated from Staging Screen");
-
-                result = item.Number + " Superceded";
             }
-            //no setout for this pdf - create new
-            else
+            new Client<SetoutDocument>().Save(setoutdocs, "Updated from Staging Screen");
+            new Client<Setout>().Save((Setout)setout, "Updated from Staging Screen");
+
+            result = item.Number + " Superceded";
+        }
+        //no setout for this pdf - create new
+        else
+        {
+            setout = new Setout
             {
-                setout = new Setout
-                {
-                    Number = item.Number
-                };
-                setout.JobLink.ID = item.JobLink.ID;
-                setout.Group.ID = item.Group.ID;
+                Number = item.Number
+            };
+            setout.JobLink.ID = item.JobLink.ID;
+            setout.Group.ID = item.Group.ID;
 
-                var editor = new DynamicDataGrid<Setout>();
-                editor.OnAfterSave += (editor, items) =>
-                {
-                    CreateSetoutDocument(setout, item, setoutDocument);
-                };
+            var editor = new DynamicDataGrid<Setout>();
+            editor.OnAfterSave += (editor, items) =>
+            {
+                CreateSetoutDocument(setout, item, setoutDocument);
+            };
 
-                if (!bulkapprove)
+            if (!bulkapprove)
+            {
+                if (!editor.EditItems(new[] { setout }))
                 {
-                    if (!editor.EditItems(new[] { setout }))
-                    {
-                        MessageBox.Show("Setout Creation Cancelled");
-                        return "";
-                    }
-                    else
-                        result = item.Number + " Created";
+                    MessageBox.Show("Setout Creation Cancelled");
+                    return "";
                 }
                 else
-                {
-                    new Client<Setout>().Save(setout, "Added from staging screen");
-                    CreateSetoutDocument(setout, item, setoutDocument);
                     result = item.Number + " Created";
-                }
             }
-
-            var tuples = new List<Tuple<ManufacturingPacket, StagingManufacturingPacket>>();
-            var stagingPackets = new Client<StagingManufacturingPacket>()
-                .Query(
-                    new Filter<StagingManufacturingPacket>(x => x.StagingSetout.ID).IsEqualTo(item.ID),
-                    Columns.Required<StagingManufacturingPacket>().Add(x => x.ID)
-                        .Add(x => x.Serial)
-                        .Add(x => x.Title)
-                        .Add(x => x.Quantity)
-                        .Add(x => x.BarcodeQuantity)
-                        .Add(x => x.Watermark)
-                        .Add(x => x.Group.Watermark)
-                        .Add(x => x.Location)
-                        .Add(x => x.ITP.ID)
-                        .Add(x => x.Job.ID)
-                        .Add(x => x.Template.ID));
-            foreach(var stagingPacket in stagingPackets.ToObjects<StagingManufacturingPacket>())
+            else
             {
-                if(stagingPacket.ManufacturingPacket.ID != Guid.Empty)
-                {
-                    MessageBox.Show($"A manufacturing packet already exists for {stagingPacket.Serial}; skipping packet.");
-                    continue;
-                }
-
-                var packet = new ManufacturingPacket
-                {
-                    Serial = stagingPacket.Serial,
-                    Title = stagingPacket.Title,
-                    Quantity = stagingPacket.Quantity,
-                    BarcodeQty = string.IsNullOrWhiteSpace(stagingPacket.BarcodeQuantity) 
-                        ? stagingPacket.Quantity 
-                        : int.Parse(stagingPacket.BarcodeQuantity),
-                    WaterMark = string.IsNullOrWhiteSpace(stagingPacket.Watermark) 
-                        ? stagingPacket.Group.Watermark
-                        : stagingPacket.Watermark,
-                    Location = stagingPacket.Location
-                };
-                packet.SetoutLink.ID = setout.ID;
-                packet.ITP.ID = stagingPacket.ITP.ID;
-                packet.JobLink.ID = stagingPacket.Job.ID;
-                packet.ManufacturingTemplateLink.ID = stagingPacket.Template.ID;
-
-                tuples.Add(new(packet, stagingPacket));
+                new Client<Setout>().Save(setout, "Added from staging screen");
+                CreateSetoutDocument(setout, item, setoutDocument);
+                result = item.Number + " Created";
             }
-            new Client<ManufacturingPacket>().Save(tuples.Select(x => x.Item1), "Created from Design Management Panel");
+        }
 
-            var newStages = new List<ManufacturingPacketStage>();
-            var newComponents = new List<ManufacturingPacketComponent>();
-            var newTreatments = new List<ManufacturingTreatment>(); 
-            foreach(var (packet, stagingPacket) in tuples)
+        var tuples = new List<Tuple<ManufacturingPacket, StagingManufacturingPacket>>();
+        var stagingPackets = new Client<StagingManufacturingPacket>()
+            .Query(
+                new Filter<StagingManufacturingPacket>(x => x.StagingSetout.ID).IsEqualTo(item.ID),
+                Columns.Required<StagingManufacturingPacket>().Add(x => x.ID)
+                    .Add(x => x.Serial)
+                    .Add(x => x.Title)
+                    .Add(x => x.Quantity)
+                    .Add(x => x.BarcodeQuantity)
+                    .Add(x => x.Watermark)
+                    .Add(x => x.Group.Watermark)
+                    .Add(x => x.Location)
+                    .Add(x => x.ITP.ID)
+                    .Add(x => x.Job.ID)
+                    .Add(x => x.Template.ID));
+        foreach(var stagingPacket in stagingPackets.ToObjects<StagingManufacturingPacket>())
+        {
+            if(stagingPacket.ManufacturingPacket.ID != Guid.Empty)
             {
-                stagingPacket.ManufacturingPacket.ID = packet.ID;
-                
-                var stages = new Client<StagingManufacturingPacketStage>()
-                    .Query(
-                        new Filter<StagingManufacturingPacketStage>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
-                        IManufacturingPacketGeneratorExtensions.GetPacketGeneratorRequiredColumns<StagingManufacturingPacketStage>());
-                newStages.AddRange(stages.ToObjects<StagingManufacturingPacketStage>()
-                    .Select(x =>
-                    {
-                        var stage = x.CreateManufacturingPacketStage();
-                        stage.Parent.ID = packet.ID;
-                        return stage;
-                    }));
-                
-                var components = new Client<StagingManufacturingPacketComponent>()
-                    .Query(
-                        new Filter<StagingManufacturingPacketComponent>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
-                        Columns.None<StagingManufacturingPacketComponent>().Add(x=>x.Packet.ID)
-                            .Add(x => x.Product.ID)
-                            .Add(x => x.Quantity)
-                            .Add(x => x.Dimensions.Unit.ID)
-                            .Add(x => x.Dimensions.Quantity)
-                            .Add(x => x.Dimensions.Length)
-                            .Add(x => x.Dimensions.Width)
-                            .Add(x => x.Dimensions.Height)
-                            .Add(x => x.Dimensions.Weight)
-                            .Add(x => x.Dimensions.Value)
-                            .Add(x => x.Dimensions.UnitSize)
-                    );
-                
-                newComponents.AddRange(components.ToObjects<StagingManufacturingPacketComponent>()
-                    .Select(x => x.CreateComponent(packet.ID)));
-                
-                var treatments = new Client<StagingManufacturingPacketTreatment>()
-                    .Query(
-                        new Filter<StagingManufacturingPacketTreatment>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
-                        Columns.None<StagingManufacturingPacketTreatment>().Add(x=>x.Packet.ID)
-                            .Add(x=>x.Product.ID)
-                            .Add(x=>x.Parameter)
-                    );
-                
-                newTreatments.AddRange(treatments.ToObjects<StagingManufacturingPacketTreatment>()
-                    .Select(x => x.CreateTreatment(packet.ID)));
-                
+                MessageBox.Show($"A manufacturing packet already exists for {stagingPacket.Serial}; skipping packet.");
+                continue;
             }
-            new Client<ManufacturingPacketStage>().Save(newStages, "Created from Design Management");
-            new Client<ManufacturingPacketComponent>().Save(newComponents, "Created from Design Management");
-            new Client<ManufacturingTreatment>().Save(newTreatments, "Created from Design Management");
-            new Client<StagingManufacturingPacket>().Save(tuples.Select(x => x.Item2), "Created from Design Management");
-
-            //currently not creating packets due to temporary change in requirements - to uncomment and check for validity when required
-            //CreatePackets(setout);
-            return result;
+
+            var packet = new ManufacturingPacket
+            {
+                Serial = stagingPacket.Serial,
+                Title = stagingPacket.Title,
+                Quantity = stagingPacket.Quantity,
+                BarcodeQty = string.IsNullOrWhiteSpace(stagingPacket.BarcodeQuantity) 
+                    ? stagingPacket.Quantity 
+                    : int.Parse(stagingPacket.BarcodeQuantity),
+                WaterMark = string.IsNullOrWhiteSpace(stagingPacket.Watermark) 
+                    ? stagingPacket.Group.Watermark
+                    : stagingPacket.Watermark,
+                Location = stagingPacket.Location
+            };
+            packet.SetoutLink.ID = setout.ID;
+            packet.ITP.ID = stagingPacket.ITP.ID;
+            packet.JobLink.ID = stagingPacket.Job.ID;
+            packet.ManufacturingTemplateLink.ID = stagingPacket.Template.ID;
+
+            tuples.Add(new(packet, stagingPacket));
         }
+        new Client<ManufacturingPacket>().Save(tuples.Select(x => x.Item1), "Created from Design Management Panel");
 
-        private static void CreateSetoutDocument(Setout setout, StagingSetout item, StagingSetoutDocument stagingsetoutdocument)
+        var newStages = new List<ManufacturingPacketStage>();
+        var newComponents = new List<ManufacturingPacketComponent>();
+        var newTreatments = new List<ManufacturingTreatment>(); 
+        foreach(var (packet, stagingPacket) in tuples)
         {
-            item.Setout.ID = setout.ID;
+            stagingPacket.ManufacturingPacket.ID = packet.ID;
+            
+            var stages = new Client<StagingManufacturingPacketStage>()
+                .Query(
+                    new Filter<StagingManufacturingPacketStage>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
+                    IManufacturingPacketGeneratorExtensions.GetPacketGeneratorRequiredColumns<StagingManufacturingPacketStage>());
+            newStages.AddRange(stages.ToObjects<StagingManufacturingPacketStage>()
+                .Select(x =>
+                {
+                    var stage = x.CreateManufacturingPacketStage();
+                    stage.Parent.ID = packet.ID;
+                    return stage;
+                }));
+            
+            var components = new Client<StagingManufacturingPacketComponent>()
+                .Query(
+                    new Filter<StagingManufacturingPacketComponent>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
+                    Columns.None<StagingManufacturingPacketComponent>().Add(x=>x.Packet.ID)
+                        .Add(x => x.Product.ID)
+                        .Add(x => x.Quantity)
+                        .Add(x => x.Dimensions.Unit.ID)
+                        .Add(x => x.Dimensions.Quantity)
+                        .Add(x => x.Dimensions.Length)
+                        .Add(x => x.Dimensions.Width)
+                        .Add(x => x.Dimensions.Height)
+                        .Add(x => x.Dimensions.Weight)
+                        .Add(x => x.Dimensions.Value)
+                        .Add(x => x.Dimensions.UnitSize)
+                );
+            
+            newComponents.AddRange(components.ToObjects<StagingManufacturingPacketComponent>()
+                .Select(x => x.CreateComponent(packet.ID)));
+            
+            var treatments = new Client<StagingManufacturingPacketTreatment>()
+                .Query(
+                    new Filter<StagingManufacturingPacketTreatment>(x => x.Packet.ID).IsEqualTo(stagingPacket.ID),
+                    Columns.None<StagingManufacturingPacketTreatment>().Add(x=>x.Packet.ID)
+                        .Add(x=>x.Product.ID)
+                        .Add(x=>x.Parameter)
+                );
+            
+            newTreatments.AddRange(treatments.ToObjects<StagingManufacturingPacketTreatment>()
+                .Select(x => x.CreateTreatment(packet.ID)));
+            
+        }
+        new Client<ManufacturingPacketStage>().Save(newStages, "Created from Design Management");
+        new Client<ManufacturingPacketComponent>().Save(newComponents, "Created from Design Management");
+        new Client<ManufacturingTreatment>().Save(newTreatments, "Created from Design Management");
+        new Client<StagingManufacturingPacket>().Save(tuples.Select(x => x.Item2), "Created from Design Management");
+
+        //currently not creating packets due to temporary change in requirements - to uncomment and check for validity when required
+        //CreatePackets(setout);
+        return result;
+    }
 
-            var setoutdoc = new SetoutDocument();
-            setoutdoc.EntityLink.ID = setout.ID;
-            setoutdoc.DocumentLink.ID = stagingsetoutdocument.DocumentLink.ID;
+    private static void CreateSetoutDocument(Setout setout, StagingSetout item, StagingSetoutDocument stagingsetoutdocument)
+    {
+        item.Setout.ID = setout.ID;
+
+        var setoutdoc = new SetoutDocument();
+        setoutdoc.EntityLink.ID = setout.ID;
+        setoutdoc.DocumentLink.ID = stagingsetoutdocument.DocumentLink.ID;
+
+        new Client<SetoutDocument>().Save(setoutdoc, "Added from staging screen");
+    }
 
-            new Client<SetoutDocument>().Save(setoutdoc, "Added from staging screen");
+    private void RejectButton_Click(object sender, RoutedEventArgs e)
+    {
+        if (selectedSetout is null || Document is null)
+        {
+            MessageBox.Show("Please select a setout");
+            return;
         }
+        //dont create setout - setout.id remains blank
+        //create kanban and populate task.id - this prevents it from appearing on the stagingsetout grid, and allows a new staging setout to be created when the file is saved to the folder again
+        //attach the document to the task for reference
 
-        private void RejectButton_Click(object sender, RoutedEventArgs e)
+        var task = new Kanban
         {
-            if (selectedSetout is null || Document is null)
-            {
-                MessageBox.Show("Please select a setout");
-                return;
-            }
-            //dont create setout - setout.id remains blank
-            //create kanban and populate task.id - this prevents it from appearing on the stagingsetout grid, and allows a new staging setout to be created when the file is saved to the folder again
-            //attach the document to the task for reference
+            Title = "Setout Review Task (setout rejected)",
+            Description = "Please review the attached document for setout " + selectedSetout.Number
+        };
+        task.ManagerLink.ID = App.EmployeeID;
 
-            var task = new Kanban
-            {
-                Title = "Setout Review Task (setout rejected)",
-                Description = "Please review the attached document for setout " + selectedSetout.Number
-            };
-            task.ManagerLink.ID = App.EmployeeID;
+        var page = new TaskGrid();
 
-            var page = new TaskGrid();
+        if (page.EditItems(new[] { task }))
+        {
+            var doc = new KanbanDocument();
+            doc.EntityLink.ID = task.ID;
+            doc.DocumentLink.ID = Document.DocumentLink.ID;
+            new Client<KanbanDocument>().Save(doc, "Created from staging screen");
 
-            if (page.EditItems(new[] { task }))
-            {
-                var doc = new KanbanDocument();
-                doc.EntityLink.ID = task.ID;
-                doc.DocumentLink.ID = Document.DocumentLink.ID;
-                new Client<KanbanDocument>().Save(doc, "Created from staging screen");
+            selectedSetout.Task.ID = task.ID;
+            new Client<StagingSetout>().Save(selectedSetout, "Updated from staging screen");
 
-                selectedSetout.Task.ID = task.ID;
-                new Client<StagingSetout>().Save(selectedSetout, "Updated from staging screen");
+            MessageBox.Show("Success - Task Created for Document " + selectedSetout.Number + " (Task no. " + task.Number + " assigned to " + task.EmployeeLink.Name + ")");
+            selectedSetout = null;
 
-                MessageBox.Show("Success - Task Created for Document " + selectedSetout.Number + " (Task no. " + task.Number + " assigned to " + task.EmployeeLink.Name + ")");
-                selectedSetout = null;
+            ClearDocuments();
+            refreshing = true;
+            stagingSetoutGrid.Refresh(false, true);
+        }
+        else
+        {
+            MessageBox.Show("Task creation cancelled - setout not rejected");
+        }
+    }
 
-                ClearDocuments();
-                refreshing = true;
-                stagingSetoutGrid.Refresh(false, true);
-            }
-            else
-            {
-                MessageBox.Show("Task creation cancelled - setout not rejected");
-            }
+    private void MarkUpButton_Click(object sender, RoutedEventArgs e)
+    {
+        if (Mode == DocumentMode.Markup)
+        {
+            Mode = DocumentMode.Complete;
+            MessageBox.Show("IMPORTANT - press save in your document editor, then press the Complete Button in PRS");
+            OnMarkupSelected();
+        }
+        else
+        {
+            OnMarkupComplete();
+            Mode = DocumentMode.Markup;
         }
+    }
 
-        private void MarkUpButton_Click(object sender, RoutedEventArgs e)
+    private void UpdateOriginalButton_Click(object sender, RoutedEventArgs e)
+    {
+        if ((_documentdata?.Any() == true) && !String.IsNullOrWhiteSpace(selectedSetout?.OriginalPath))
         {
-            if (Mode == DocumentMode.Markup)
+            try
             {
-                Mode = DocumentMode.Complete;
-                MessageBox.Show("IMPORTANT - press save in your document editor, then press the Complete Button in PRS");
-                OnMarkupSelected();
+                File.WriteAllBytes(selectedSetout.OriginalPath, _documentdata);
+                selectedSetout.OriginalCRC = CoreUtils.CalculateCRC(_documentdata);
+                new Client<StagingSetout>().Save(selectedSetout,"Updated Source File with markups");
+                UpdateOriginalButton.Visibility = Visibility.Collapsed;
             }
-            else
+            catch (Exception _exception)
             {
-                OnMarkupComplete();
-                Mode = DocumentMode.Markup;
+                MessageBox.Show($"Unable to update {selectedSetout?.OriginalPath}!\n\n{_exception.Message}");
             }
+            return;
         }
+        MessageBox.Show("Please select a design first!");
+    }
 
-        private void UpdateOriginalButton_Click(object sender, RoutedEventArgs e)
+    private void ApproveButton_Click(object sender, RoutedEventArgs e)
+    {
+        if (Document is null || selectedSetout is null)
+        {
+            MessageBox.Show("Please select a setout first.");
+            return;
+        }
+        if (selectedSetouts.Count > 1)
         {
-            if ((_documentdata?.Any() == true) && !String.IsNullOrWhiteSpace(selectedSetout?.OriginalPath))
+            var msg = Document.Approved
+                ? "Bulk unapprove?"
+                : "Bulk approve? (Skip individual setout approval)";
+            if (MessageBox.Show(msg, "Continue?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
             {
-                try
+                Progress.Show("Approving Setouts..");
+                var documents = Client.Query(
+                    new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).InList(selectedSetouts.Select(x => x.ID).ToArray()),
+                    Columns.Required<StagingSetoutDocument>().Add(x => x.ID, x => x.Approved)
+                ).ToObjects<StagingSetoutDocument>().ToList();
+                foreach(var document in documents)
                 {
-                    File.WriteAllBytes(selectedSetout.OriginalPath, _documentdata);
-                    selectedSetout.OriginalCRC = CoreUtils.CalculateCRC(_documentdata);
-                    new Client<StagingSetout>().Save(selectedSetout,"Updated Source File with markups");
-                    UpdateOriginalButton.Visibility = Visibility.Collapsed;
+                    document.Approved = !Document.Approved;
                 }
-                catch (Exception _exception)
-                {
-                    MessageBox.Show($"Unable to update {selectedSetout?.OriginalPath}!\n\n{_exception.Message}");
-                }
-                return;
-            }
-            MessageBox.Show("Please select a design first!");
-        }
+                Client.Save(documents, "Approved by user.");
 
-        private void ApproveButton_Click(object sender, RoutedEventArgs e)
-        {
-            if (Document is null || selectedSetout is null)
-            {
-                MessageBox.Show("Please select a setout first.");
-                return;
-            }
-            if (selectedSetouts.Count > 1)
-            {
-                var msg = Document.Approved
-                    ? "Bulk unapprove?"
-                    : "Bulk approve? (Skip individual setout approval)";
-                if (MessageBox.Show(msg, "Continue?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
-                {
-                    Progress.Show("Approving Setouts..");
-                    var documents = Client.Query(
-                        new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).InList(selectedSetouts.Select(x => x.ID).ToArray()),
-                        Columns.Required<StagingSetoutDocument>().Add(x => x.ID, x => x.Approved)
-                    ).ToObjects<StagingSetoutDocument>().ToList();
-                    foreach(var document in documents)
-                    {
-                        document.Approved = !Document.Approved;
-                    }
-                    Client.Save(documents, "Approved by user.");
-
-                    Progress.Close();
+                Progress.Close();
 
-                    refreshing = true;
-                    stagingSetoutGrid.Refresh(false, true);
-                }
-            }
-            else
-            {
-                Document.Approved = !Document.Approved;
-                new Client<StagingSetoutDocument>().Save(Document, "");
                 refreshing = true;
                 stagingSetoutGrid.Refresh(false, true);
             }
         }
-
-        private void OnMarkupSelected()
+        else
         {
-            if (Document is null || selectedSetout is null)
-            {
-                MessageBox.Show("Please select a setout first.");
-                return;
-            }
-
-            var doc = new Client<Document>()
-                .Query(
-                    new Filter<Document>(x => x.ID).IsEqualTo(Document.DocumentLink.ID))
-                .ToObjects<Document>().FirstOrDefault();
-            if (doc is null)
-            {
-                Logger.Send(LogType.Error, "", $"Document with ID {Document.DocumentLink.ID} could not be found.");
-                MessageBox.Show("Error: the selected document could not be found in the database.");
-                return;
-            }
-
-            var tempdocpath = Path.Combine(Path.GetTempPath(), doc.FileName);
-            selectedSetout.SavePath = tempdocpath;
-            selectedSetout.LockedBy.ID = App.EmployeeID;
-            selectedSetout.LockedBy.Name = App.EmployeeName;
-            new Client<StagingSetout>().Save(selectedSetout, "Locked from Staging Screen");
-            File.WriteAllBytes(tempdocpath, doc.Data);
-            using (var p = new Process())
-            {
-                p.StartInfo = new ProcessStartInfo()
-                {
-                    UseShellExecute = true,
-                    FileName = tempdocpath
-                };
-
-                p.Start();
-            }
+            Document.Approved = !Document.Approved;
+            new Client<StagingSetoutDocument>().Save(Document, "");
             refreshing = true;
             stagingSetoutGrid.Refresh(false, true);
         }
-        private void OnMarkupComplete()
+    }
+
+    private void OnMarkupSelected()
+    {
+        if (Document is null || selectedSetout is null)
         {
-            if (selectedSetout is null)
-            {
-                MessageBox.Show("Please select a setout first.");
-                return;
-            }
-            StagingSetoutGrid.ReloadFile(selectedSetout);
-            refreshing = true;
-            stagingSetoutGrid.Refresh(false, true);
+            MessageBox.Show("Please select a setout first.");
+            return;
         }
 
-        #endregion
-
-        private bool refreshing = false;
-
-        private void stagingSetoutGrid_AfterRefresh(object sender, AfterRefreshEventArgs args)
+        var doc = new Client<Document>()
+            .Query(
+                new Filter<Document>(x => x.ID).IsEqualTo(Document.DocumentLink.ID))
+            .ToObjects<Document>().FirstOrDefault();
+        if (doc is null)
         {
-            refreshing = false;
+            Logger.Send(LogType.Error, "", $"Document with ID {Document.DocumentLink.ID} could not be found.");
+            MessageBox.Show("Error: the selected document could not be found in the database.");
+            return;
         }
 
-        private void StagingSetoutGrid_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e)
+        var tempdocpath = Path.Combine(Path.GetTempPath(), doc.FileName);
+        selectedSetout.SavePath = tempdocpath;
+        selectedSetout.LockedBy.ID = App.EmployeeID;
+        selectedSetout.LockedBy.Name = App.EmployeeName;
+        new Client<StagingSetout>().Save(selectedSetout, "Locked from Staging Screen");
+        File.WriteAllBytes(tempdocpath, doc.Data);
+        using (var p = new Process())
         {
-            var newSetouts = new List<StagingSetout>();
-            foreach (var row in e.Rows ?? Enumerable.Empty<CoreRow>())
-                newSetouts.Add(row.ToObject<StagingSetout>());
-
-            if(!refreshing && (selectedSetouts.Count == newSetouts.Count
-                && !selectedSetouts.Any(x => !newSetouts.Any(y => x.ID == y.ID))))
+            p.StartInfo = new ProcessStartInfo()
             {
-                selectedSetouts = newSetouts;
-                selectedSetout = selectedSetouts.FirstOrDefault();
-                return;
-            }
-
-            selectedSetouts = newSetouts;
-
-            selectedSetout = selectedSetouts.FirstOrDefault();
-            AddPacketButton.IsEnabled = selectedSetout is not null;
-            if(selectedSetout is null)
-            {
-                ClearDocuments();
-                ManufacturingPacketList.Setout = null;
-                CollapsePacketsButton.IsEnabled = false;
-                SetoutComponentGrid.StagingSetout = null;
-                SetMode(DocumentMode.Markup);
-                return;
-            }
-
-            var doc = new Client<StagingSetoutDocument>()
-                .Query(
-                    new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(selectedSetout.ID),
-                    Columns.None<StagingSetoutDocument>().Add(x => x.ID)
-                        .Add(x => x.DocumentLink.ID)
-                        .Add(x => x.DocumentLink.FileName)
-                        .Add(x => x.Approved)
-                        .Add(x=>x.DocumentLink.CRC)
-                ).ToObjects<StagingSetoutDocument>().FirstOrDefault();
-            if(doc is null)
-            {
-                MessageBox.Show("No document found for this setout.");
-                ClearDocuments();
-                ManufacturingPacketList.Setout = null;
-                CollapsePacketsButton.IsEnabled = false;
-                SetoutComponentGrid.StagingSetout = null;
-                return;
-            }
+                UseShellExecute = true,
+                FileName = tempdocpath
+            };
 
-            Document = doc;
-            var docTask = Task.Run(() => GetDocuments(doc));
-            if(CanViewPackets())
-            {
-                ManufacturingPacketList.Setout = selectedSetout;
-                SetoutComponentGrid.StagingSetout = selectedSetout;
-            }
-            CollapsePacketsButton.IsEnabled = true;
-            var mode =
-                selectedSetout.LockedBy.ID == Guid.Empty ?
-                    DocumentMode.Markup :
-                    selectedSetout.LockedBy.ID == App.EmployeeID ?
-                        DocumentMode.Complete :
-                        DocumentMode.Locked;
-
-            docTask.Wait();
-            RenderDocuments(docTask.Result);
-
-            SetMode(mode);
+            p.Start();
         }
+        refreshing = true;
+        stagingSetoutGrid.Refresh(false, true);
+    }
+    private void OnMarkupComplete()
+    {
+        if (selectedSetout is null)
+        {
+            MessageBox.Show("Please select a setout first.");
+            return;
+        }
+        StagingSetoutGrid.ReloadFile(selectedSetout);
+        refreshing = true;
+        stagingSetoutGrid.Refresh(false, true);
+    }
 
-        public bool IsReady { get; set; }
-
-        public string SectionName { get; }
+    #endregion
 
+    private bool refreshing = false;
 
-        public event DataModelUpdateEvent? OnUpdateDataModel;
+    private void stagingSetoutGrid_AfterRefresh(object sender, AfterRefreshEventArgs args)
+    {
+        refreshing = false;
+    }
 
-        #region Settings
+    private void StagingSetoutGrid_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e)
+    {
+        var newSetouts = new List<StagingSetout>();
+        foreach (var row in e.Rows ?? Enumerable.Empty<CoreRow>())
+            newSetouts.Add(row.ToObject<StagingSetout>());
 
-        public void CreateToolbarButtons(IPanelHost host)
+        if(!refreshing && (selectedSetouts.Count == newSetouts.Count
+            && !selectedSetouts.Any(x => !newSetouts.Any(y => x.ID == y.ID))))
         {
-            ProjectSetupActions.JobStatuses(host);
-            ProjectSetupActions.DrawingTemplates(host);
-            host.CreateSetupSeparator();
-            ProjectSetupActions.JobSpreadsheetTemplates(host);
-            host.CreateSetupSeparator();
-            ProjectSetupActions.SetoutGroups(host);
-            host.CreateSetupSeparator();
-
-            host.CreateSetupAction(
-                new PanelAction()
-                {
-                    Caption = "Setouts Configuration", 
-                    Image = PRSDesktop.Resources.specifications,
-                    OnExecute = ConfigSettingsClick
-                }
-            );
-            host.CreateSetupAction(
-                new PanelAction()
-                {
-                    Caption = "Component Import Profiles",
-                    Image = PRSDesktop.Resources.doc_xls,
-                    OnExecute = ConfigComponentProfiles
-                });
-
-            host.CreateSetupAction(
-                new PanelAction() 
-                { 
-                    Caption = "Template Products", 
-                    Image = PRSDesktop.Resources.specifications,
-                    OnExecute =
-                        action =>
-                        {
-                            var list = new MasterList(typeof(ManufacturingTemplateGroupProducts));
-                            list.ShowDialog();
-                        }  
-                }
-            );
-            
-            SystemSetupActions.ERPStatuses(host);
-            
+            selectedSetouts = newSetouts;
+            selectedSetout = selectedSetouts.FirstOrDefault();
+            return;
         }
 
-        private void ConfigComponentProfiles(PanelAction obj)
+        selectedSetouts = newSetouts;
+
+        selectedSetout = selectedSetouts.FirstOrDefault();
+        AddPacketButton.IsEnabled = selectedSetout is not null;
+        if(selectedSetout is null)
         {
-            var list = new DynamicImportList(
-                typeof(StagingSetoutComponent),
-                Guid.Empty,
-                canImport: false
-            );
-            list.ShowDialog();
+            ClearDocuments();
+            ManufacturingPacketList.Setout = null;
+            CollapsePacketsButton.IsEnabled = false;
+            SetoutComponentGrid.StagingSetout = null;
+            SetMode(DocumentMode.Markup);
+            return;
         }
 
-        private void ConfigSettingsClick(PanelAction obj)
+        var doc = new Client<StagingSetoutDocument>()
+            .Query(
+                new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(selectedSetout.ID),
+                Columns.None<StagingSetoutDocument>().Add(x => x.ID)
+                    .Add(x => x.DocumentLink.ID)
+                    .Add(x => x.DocumentLink.FileName)
+                    .Add(x => x.Approved)
+                    .Add(x=>x.DocumentLink.CRC)
+            ).ToObjects<StagingSetoutDocument>().FirstOrDefault();
+        if(doc is null)
         {
-            var grid = new DynamicItemsListGrid<StagingPanellSettings>();
-            grid.OnCustomiseEditor += Grid_OnCustomiseEditor;
-            if(grid.EditItems(new[] { _settings }))
-            {
-                new GlobalConfiguration<StagingPanellSettings>().Save(_settings);
-                _script = null;
-            }
+            MessageBox.Show("No document found for this setout.");
+            ClearDocuments();
+            ManufacturingPacketList.Setout = null;
+            CollapsePacketsButton.IsEnabled = false;
+            SetoutComponentGrid.StagingSetout = null;
+            return;
         }
 
-        private void Grid_OnCustomiseEditor(IDynamicEditorForm sender, StagingPanellSettings[]? items, DynamicGridColumn column, BaseEditor editor)
+        Document = doc;
+        var docTask = Task.Run(() => GetDocuments(doc));
+        if(CanViewPackets())
         {
-            if (items?.FirstOrDefault() is not StagingPanellSettings settings) return;
-
-            if (column.ColumnName == nameof(StagingPanellSettings.Script) && editor is ScriptEditor scriptEditor)
-            {
-                scriptEditor.Type = ScriptEditorType.TemplateEditor;
-                scriptEditor.OnEditorClicked += () =>
-                {
-                    var script = settings.Script.NotWhiteSpaceOr()
-                        ?? settings.DefaultScript();
-
-                    var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
-                    if (editor.ShowDialog() == true)
-                    {
-                        sender.SetEditorValue(column.ColumnName, editor.Script);
-                        settings.Script = editor.Script;
-                    }
-                };
-            }
+            ManufacturingPacketList.Setout = selectedSetout;
+            SetoutComponentGrid.StagingSetout = selectedSetout;
         }
+        CollapsePacketsButton.IsEnabled = true;
+        var mode =
+            selectedSetout.LockedBy.ID == Guid.Empty ?
+                DocumentMode.Markup :
+                selectedSetout.LockedBy.ID == App.EmployeeID ?
+                    DocumentMode.Complete :
+                    DocumentMode.Locked;
+
+        docTask.Wait();
+        RenderDocuments(docTask.Result);
+
+        SetMode(mode);
+    }
 
-        #endregion
+    public bool IsReady { get; set; }
 
-        public void Heartbeat(TimeSpan time)
-        {
+    public string SectionName { get; }
 
-        }
 
-        public void Refresh()
-        {
-            //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
-            refreshing = true;
-            stagingSetoutGrid.Refresh(false, true);
-            /*Document = null;
+    public event DataModelUpdateEvent? OnUpdateDataModel;
 
-            selectedSetout = null;
-            ManufacturingPacketList.Setout = null;
-            SetoutComponentGrid.StagingSetout = null;*/
-            CalculateTime();
-        }
+    #region Settings
 
-        private void stagingSetoutGrid_OnRefreshPackets()
-        {
-            if (CanViewPackets())
+    public void CreateToolbarButtons(IPanelHost host)
+    {
+        ProjectSetupActions.JobStatuses(host);
+        ProjectSetupActions.DrawingTemplates(host);
+        host.CreateSetupSeparator();
+        ProjectSetupActions.JobSpreadsheetTemplates(host);
+        host.CreateSetupSeparator();
+        ProjectSetupActions.SetoutGroups(host);
+        host.CreateSetupSeparator();
+
+        host.CreateSetupAction(
+            new PanelAction()
             {
-                ManufacturingPacketList.Refresh();
+                Caption = "Setouts Configuration", 
+                Image = PRSDesktop.Resources.specifications,
+                OnExecute = ConfigSettingsClick
             }
-        }
+        );
+        host.CreateSetupAction(
+            new PanelAction()
+            {
+                Caption = "Component Import Profiles",
+                Image = PRSDesktop.Resources.doc_xls,
+                OnExecute = ConfigComponentProfiles
+            });
+
+        host.CreateSetupAction(
+            new PanelAction() 
+            { 
+                Caption = "Template Products", 
+                Image = PRSDesktop.Resources.specifications,
+                OnExecute =
+                    action =>
+                    {
+                        var list = new MasterList(typeof(ManufacturingTemplateGroupProducts));
+                        list.ShowDialog();
+                    }  
+            }
+        );
+        
+        SystemSetupActions.ERPStatuses(host);
+        
+    }
 
-        public Dictionary<string, object[]> Selected()
+    private void ConfigComponentProfiles(PanelAction obj)
+    {
+        var list = new DynamicImportList(
+            typeof(StagingSetoutComponent),
+            Guid.Empty,
+            canImport: false
+        );
+        list.ShowDialog();
+    }
+
+    private void ConfigSettingsClick(PanelAction obj)
+    {
+        var grid = new DynamicItemsListGrid<StagingPanellSettings>();
+        grid.OnCustomiseEditor += Grid_OnCustomiseEditor;
+        if(grid.EditItems(new[] { _settings }))
         {
-            return new();
+            new GlobalConfiguration<StagingPanellSettings>().Save(_settings);
+            _script = null;
         }
+    }
 
+    private void Grid_OnCustomiseEditor(IDynamicEditorForm sender, StagingPanellSettings[]? items, DynamicGridColumn column, BaseEditor editor)
+    {
+        if (items?.FirstOrDefault() is not StagingPanellSettings settings) return;
 
-        public void Shutdown(CancelEventArgs? cancel)
+        if (column.ColumnName == nameof(StagingPanellSettings.Script) && editor is ScriptEditor scriptEditor)
         {
+            scriptEditor.Type = ScriptEditorType.TemplateEditor;
+            scriptEditor.OnEditorClicked += () =>
+            {
+                var script = settings.Script.NotWhiteSpaceOr()
+                    ?? settings.DefaultScript();
 
+                var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
+                if (editor.ShowDialog() == true)
+                {
+                    sender.SetEditorValue(column.ColumnName, editor.Script);
+                    settings.Script = editor.Script;
+                }
+            };
         }
+    }
+
+    #endregion
 
-        public DataModel DataModel(Selection selection)
+    public void Heartbeat(TimeSpan time)
+    {
+
+    }
+
+    public void Refresh()
+    {
+        //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
+        refreshing = true;
+        stagingSetoutGrid.Refresh(false, true);
+        /*Document = null;
+
+        selectedSetout = null;
+        ManufacturingPacketList.Setout = null;
+        SetoutComponentGrid.StagingSetout = null;*/
+        CalculateTime();
+    }
+
+    private void stagingSetoutGrid_OnRefreshPackets()
+    {
+        if (CanViewPackets())
         {
-            return new AutoDataModel<StagingSetout>(new Filter<StagingSetout>().All());
+            ManufacturingPacketList.Refresh();
         }
+    }
+
+    public Dictionary<string, object[]> Selected()
+    {
+        return new();
+    }
+
+
+    public void Shutdown(CancelEventArgs? cancel)
+    {
+
+    }
 
-        private void AddPacketButton_Click(object sender, RoutedEventArgs e)
+    public DataModel DataModel(Selection selection)
+    {
+        return new AutoDataModel<StagingSetout>(new Filter<StagingSetout>().All());
+    }
+
+    private void AddPacketButton_Click(object sender, RoutedEventArgs e)
+    {
+        if (_templateGroups.Rows.Any() == true)
         {
-            if (_templateGroups.Rows.Any() == true)
+            ContextMenu menu = new ContextMenu();
+            foreach (var row in _templateGroups.Rows)
             {
-                ContextMenu menu = new ContextMenu();
-                foreach (var row in _templateGroups.Rows)
-                {
-                    menu.AddItem(
-                        $"{row.Get<ManufacturingTemplateGroup, String>(x => x.Code)}: {row.Get<ManufacturingTemplateGroup, String>(x => x.Description)}",
-                        null,
-                        () =>
-                        {
-                            ManufacturingPacketList.Add(
-                                selectedSetout?.JobLink.ID ?? Guid.Empty,
-                                row.ToObject<ManufacturingTemplateGroup>()
-                            );
-                            UpdateStagingSetoutGrid();
-                        });
-                }
-
-                menu.AddSeparator();
-                menu.AddItem("Miscellaneous Item", null, () =>
-                {
-                    ManufacturingPacketList.Add(
-                        selectedSetout?.JobLink.ID ?? Guid.Empty,
-                        null
-                    );
-                    UpdateStagingSetoutGrid();
-                });
-                menu.IsOpen = true;
+                menu.AddItem(
+                    $"{row.Get<ManufacturingTemplateGroup, String>(x => x.Code)}: {row.Get<ManufacturingTemplateGroup, String>(x => x.Description)}",
+                    null,
+                    () =>
+                    {
+                        ManufacturingPacketList.Add(
+                            selectedSetout?.JobLink.ID ?? Guid.Empty,
+                            row.ToObject<ManufacturingTemplateGroup>()
+                        );
+                        UpdateStagingSetoutGrid();
+                    });
             }
-            else
+
+            menu.AddSeparator();
+            menu.AddItem("Miscellaneous Item", null, () =>
             {
                 ManufacturingPacketList.Add(
-                    selectedSetout?.JobLink.ID ?? Guid.Empty, 
+                    selectedSetout?.JobLink.ID ?? Guid.Empty,
                     null
                 );
                 UpdateStagingSetoutGrid();
-            }
-
+            });
+            menu.IsOpen = true;
         }
-
-        private void UpdateStagingSetoutGrid()
+        else
         {
-            var selected = stagingSetoutGrid.SelectedRows.FirstOrDefault();
-            if (selected != null)
-            {
-                var packets = ManufacturingPacketList.Packets;
-                selected.Set<StagingSetout, int>(x => x.Packets, packets.Length);
-                selected.Set<StagingSetout, int>(x => x.UnprocessedPackets, packets.Count(x => x.ManufacturingPacket.ID == Guid.Empty));
-                stagingSetoutGrid.InvalidateRow(selected);
-            }
+            ManufacturingPacketList.Add(
+                selectedSetout?.JobLink.ID ?? Guid.Empty, 
+                null
+            );
+            UpdateStagingSetoutGrid();
         }
 
-        private void CollapsePacketsButton_Click(object sender, RoutedEventArgs e)
+    }
+
+    private void UpdateStagingSetoutGrid()
+    {
+        var selected = stagingSetoutGrid.SelectedRows.FirstOrDefault();
+        if (selected != null)
         {
-            if (ManufacturingPacketList.Collapsed())
-            {
-                ManufacturingPacketList.Uncollapse();
-            }
-            else
-            {
-                ManufacturingPacketList.Collapse();
-            }
+            var packets = ManufacturingPacketList.Packets;
+            selected.Set<StagingSetout, int>(x => x.Packets, packets.Length);
+            selected.Set<StagingSetout, int>(x => x.UnprocessedPackets, packets.Count(x => x.ManufacturingPacket.ID == Guid.Empty));
+            stagingSetoutGrid.InvalidateRow(selected);
         }
+    }
 
-        private void ManufacturingPacketList_OnCollapsed(bool collapsed)
+    private void CollapsePacketsButton_Click(object sender, RoutedEventArgs e)
+    {
+        if (ManufacturingPacketList.Collapsed())
         {
-            if (collapsed)
-            {
-                CollapsePacketsButton.Content = "Expand";
-            }
-            else
-            {
-                CollapsePacketsButton.Content = "Collapse";
-            }
+            ManufacturingPacketList.Uncollapse();
         }
-
-        private void stagingSetoutGrid_OnCustomiseSetouts(IReadOnlyList<StagingSetoutGrid.SetoutDocument> setouts)
+        else
         {
-            if(CustomiseSetoutsMethod != null && ScriptObject != null)
-            {
-                CustomiseSetoutsMethod?.Invoke(ScriptObject, new object?[]
-                {
-                    new CustomiseSetoutsArgs(setouts.Select(x => new Tuple<StagingSetout, Document>(x.Setout, x.Document)).ToImmutableList())
-                });
-            }
+            ManufacturingPacketList.Collapse();
         }
+    }
 
-        private void StagingSetoutGrid_OnOnDoubleClick(object sender, HandledEventArgs args)
+    private void ManufacturingPacketList_OnCollapsed(bool collapsed)
+    {
+        if (collapsed)
         {
-            ManufacturingPacketList.Setout = selectedSetout;
-            SetoutComponentGrid.StagingSetout = selectedSetout;
-            MainPanel.View = DynamicSplitPanelView.Detail;
-            NestedPanel.View = DynamicSplitPanelView.Combined;
-            args.Handled = true;
+            CollapsePacketsButton.Content = "Expand";
+        }
+        else
+        {
+            CollapsePacketsButton.Content = "Collapse";
         }
+    }
 
-        private void CalculateTime()
+    private void stagingSetoutGrid_OnCustomiseSetouts(IReadOnlyList<StagingSetoutGrid.SetoutDocument> setouts)
+    {
+        if(CustomiseSetoutsMethod != null && ScriptObject != null)
         {
-            if (selectedSetout != null)
+            CustomiseSetoutsMethod?.Invoke(ScriptObject, new object?[]
             {
-                var time = ManufacturingPacketList.TimeRequired();
-                TimeRequired.Content = $"{time.TotalHours:F2} hours";
-            }
-            else
-                TimeRequired.Content = "N/A";
+                new CustomiseSetoutsArgs(setouts.Select(x => new Tuple<StagingSetout, Document>(x.Setout, x.Document)).ToImmutableList())
+            });
         }
+    }
+
+    private void StagingSetoutGrid_OnOnDoubleClick(object sender, HandledEventArgs args)
+    {
+        ManufacturingPacketList.Setout = selectedSetout;
+        SetoutComponentGrid.StagingSetout = selectedSetout;
+        MainPanel.View = DynamicSplitPanelView.Detail;
+        NestedPanel.View = DynamicSplitPanelView.Combined;
+        args.Handled = true;
+    }
 
-        private void ManufacturingPacketList_OnChanged(object? sender, EventArgs e)
+    private void CalculateTime()
+    {
+        if (selectedSetout != null)
         {
-           CalculateTime();
-           UpdateStagingSetoutGrid();
+            var time = ManufacturingPacketList.TimeRequired();
+            TimeRequired.Content = $"{time.TotalHours:F2} hours";
         }
+        else
+            TimeRequired.Content = "N/A";
+    }
 
+    private void ManufacturingPacketList_OnChanged(object? sender, EventArgs e)
+    {
+       CalculateTime();
+       UpdateStagingSetoutGrid();
+    }
 
-        private void DoImport(Importer importer, string? componentFileName, Guid setoutID)
+
+    private void DoImport(Importer importer, string? componentFileName, Guid setoutID)
+    {
+        var success = DynamicImportGrid.CreateImporter(importer, ref componentFileName, out var iimporter);
+        if (!success)
         {
-            var success = DynamicImportGrid.CreateImporter(importer, ref componentFileName, out var iimporter);
-            if (!success)
-            {
-                return;
-            }
+            return;
+        }
 
-            var errors = new List<string>();
+        var errors = new List<string>();
 
-            var stagingSetoutComponents = new List<StagingSetoutComponent>();
-            iimporter.OnLoad += Iimporter_OnLoad;
-            iimporter.OnSave += (_, entity) => stagingSetoutComponents.Add((entity as StagingSetoutComponent)!);
-            iimporter.OnError += (_, error) => errors.Add(error);
+        var stagingSetoutComponents = new List<StagingSetoutComponent>();
+        iimporter.OnLoad += Iimporter_OnLoad;
+        iimporter.OnSave += (_, entity) => stagingSetoutComponents.Add((entity as StagingSetoutComponent)!);
+        iimporter.OnError += (_, error) => errors.Add(error);
 
-            using var stream = new FileStream(componentFileName!, FileMode.Open, FileAccess.Read);
-            if (iimporter.Open(stream))
+        using var stream = new FileStream(componentFileName!, FileMode.Open, FileAccess.Read);
+        if (iimporter.Open(stream))
+        {
+            if (iimporter.ReadHeader())
             {
-                if (iimporter.ReadHeader())
+                var mismatches = iimporter.Mappings.Where(x =>
+                    !string.IsNullOrWhiteSpace(x.Field) &&
+                    !iimporter.Fields.Contains(x.Field)
+                ).Select(x => x.Field).ToArray();
+                if (!mismatches.Any())
                 {
-                    var mismatches = iimporter.Mappings.Where(x =>
-                        !string.IsNullOrWhiteSpace(x.Field) &&
-                        !iimporter.Fields.Contains(x.Field)
-                    ).Select(x => x.Field).ToArray();
-                    if (!mismatches.Any())
+                    var imported = iimporter.Import();
+                    if (errors.Any())
                     {
-                        var imported = iimporter.Import();
-                        if (errors.Any())
-                        {
-                            MessageBox.Show($"Import for component file {componentFileName} failed:\nSome errors occurred: {string.Join('\n', errors)}", "Import failed");
-                        }
-                        else
+                        MessageBox.Show($"Import for component file {componentFileName} failed:\nSome errors occurred: {string.Join('\n', errors)}", "Import failed");
+                    }
+                    else
+                    {
+                        var valid = true;
+                        var conflicts = false;
+                        if (setoutID != Guid.Empty)
                         {
-                            var valid = true;
-                            var conflicts = false;
-                            if (setoutID != Guid.Empty)
+                            var newComponents = new List<StagingSetoutComponent>();
+                            foreach (var component in stagingSetoutComponents)
                             {
-                                var newComponents = new List<StagingSetoutComponent>();
-                                foreach (var component in stagingSetoutComponents)
+                                if (component.StagingSetout.ID == Guid.Empty)
                                 {
-                                    if (component.StagingSetout.ID == Guid.Empty)
-                                    {
-                                        component.StagingSetout.ID = setoutID;
-                                        newComponents.Add(component);
-                                    }
-                                    else if (component.StagingSetout.ID != setoutID)
-                                    {
-                                        conflicts = true;
-                                        // Ignoring this item.
-                                    }
-                                    else
-                                    {
-                                        newComponents.Add(component);
-                                    }
+                                    component.StagingSetout.ID = setoutID;
+                                    newComponents.Add(component);
                                 }
-                                stagingSetoutComponents = newComponents;
-
-                                if (conflicts)
+                                else if (component.StagingSetout.ID != setoutID)
                                 {
-                                    MessageBox.Show($"Warning: the lines in this file have conflicting setout numbers.", "Warning");
+                                    conflicts = true;
+                                    // Ignoring this item.
                                 }
-                            }
-                            if (valid)
-                            {
-                                foreach (var component in stagingSetoutComponents)
+                                else
                                 {
-                                    if (component.StagingSetout.ID == Guid.Empty)
-                                    {
-                                        MessageBox.Show($"Component with no related setout cannot be imported.");
-                                        valid = false;
-                                        break;
-                                    }
-                                    else if (component.Description.IsNullOrWhiteSpace())
-                                    {
-                                        MessageBox.Show($"Component with no description cannot be imported.");
-                                        valid = false;
-                                        break;
-                                    }
-                                    else if (component.Dimensions.Unit.ID == Guid.Empty)
-                                    {
-                                        MessageBox.Show($"Component with no dimensions unit cannot be imported.");
-                                        valid = false;
-                                        break;
-                                    }
+                                    newComponents.Add(component);
                                 }
                             }
-                            if (valid)
-                            {
-                                new Client<StagingSetoutComponent>().Save(stagingSetoutComponents, $"Imported from {componentFileName}");
+                            stagingSetoutComponents = newComponents;
 
-                                SetoutComponentGrid.Refresh(false, true);
+                            if (conflicts)
+                            {
+                                MessageBox.Show($"Warning: the lines in this file have conflicting setout numbers.", "Warning");
                             }
-                            else
+                        }
+                        if (valid)
+                        {
+                            foreach (var component in stagingSetoutComponents)
                             {
-                                MessageBox.Show($"Import for component file {componentFileName} failed.", "Import failed");
+                                if (component.StagingSetout.ID == Guid.Empty)
+                                {
+                                    MessageBox.Show($"Component with no related setout cannot be imported.");
+                                    valid = false;
+                                    break;
+                                }
+                                else if (component.Description.IsNullOrWhiteSpace())
+                                {
+                                    MessageBox.Show($"Component with no description cannot be imported.");
+                                    valid = false;
+                                    break;
+                                }
+                                else if (component.Dimensions.Unit.ID == Guid.Empty)
+                                {
+                                    MessageBox.Show($"Component with no dimensions unit cannot be imported.");
+                                    valid = false;
+                                    break;
+                                }
                             }
                         }
-                    }
-                    else
-                    {
-                        MessageBox.Show("Import Mappings do not match file headers!\n\n- " + string.Join("\n- ", mismatches),
-                            "Import Failed");
+                        if (valid)
+                        {
+                            new Client<StagingSetoutComponent>().Save(stagingSetoutComponents, $"Imported from {componentFileName}");
+
+                            SetoutComponentGrid.Refresh(false, true);
+                        }
+                        else
+                        {
+                            MessageBox.Show($"Import for component file {componentFileName} failed.", "Import failed");
+                        }
                     }
                 }
                 else
                 {
-                    MessageBox.Show("Unable to Read Headers from {0}", Path.GetFileName(componentFileName));
+                    MessageBox.Show("Import Mappings do not match file headers!\n\n- " + string.Join("\n- ", mismatches),
+                        "Import Failed");
                 }
             }
             else
             {
-                MessageBox.Show("Unable to Open {0}", Path.GetFileName(componentFileName));
+                MessageBox.Show("Unable to Read Headers from {0}", Path.GetFileName(componentFileName));
             }
-
-            iimporter.Close();
         }
-
-        private CoreTable Iimporter_OnLoad(object sender, Type type, string[] fields, string ID)
+        else
         {
-            var result = new CoreTable();
-            result.LoadColumns(Columns.None<StagingSetoutComponent>().Add(fields));
-            return result;
+            MessageBox.Show("Unable to Open {0}", Path.GetFileName(componentFileName));
         }
 
-        private void stagingSetoutGrid_OnParseComponentFile(string componentFileName, Guid setoutID)
+        iimporter.Close();
+    }
+
+    private CoreTable Iimporter_OnLoad(object sender, Type type, string[] fields, string ID)
+    {
+        var result = new CoreTable();
+        result.LoadColumns(Columns.None<StagingSetoutComponent>().Add(fields));
+        return result;
+    }
+
+    private void stagingSetoutGrid_OnParseComponentFile(string componentFileName, Guid setoutID)
+    {
+        try
         {
-            try
+            var entityName = typeof(StagingSetoutComponent).EntityName();
+            var importers = new Client<Importer>()
+                .Query(
+                    new Filter<Importer>(x => x.EntityName).IsEqualTo(entityName),
+                    Columns.None<Importer>().Add(x => x.ID));
+            if (importers.Rows.Count == 0)
             {
-                var entityName = typeof(StagingSetoutComponent).EntityName();
-                var importers = new Client<Importer>()
-                    .Query(
-                        new Filter<Importer>(x => x.EntityName).IsEqualTo(entityName),
-                        Columns.None<Importer>().Add(x => x.ID));
-                if (importers.Rows.Count == 0)
+                var importer = new Importer
                 {
-                    var importer = new Importer
-                    {
-                        EntityName = entityName,
-                        FileName = componentFileName
-                    };
+                    EntityName = entityName,
+                    FileName = componentFileName
+                };
 
-                    var form = new DynamicImportForm(importer);
-                    if (form.ShowDialog() == true)
-                    {
-                        new Client<Importer>().Save(importer, "");
+                var form = new DynamicImportForm(importer);
+                if (form.ShowDialog() == true)
+                {
+                    new Client<Importer>().Save(importer, "");
 
-                        DoImport(importer, componentFileName, setoutID);
+                    DoImport(importer, componentFileName, setoutID);
 
-                        return;
-                    }
+                    return;
                 }
-                else if (importers.Rows.Count == 1)
+            }
+            else if (importers.Rows.Count == 1)
+            {
+                var importer = new Client<Importer>().Load(new Filter<Importer>(x => x.ID).IsEqualTo(importers.Rows[0].Get<Importer, Guid>(x => x.ID))).First();
+                DoImport(importer, componentFileName, setoutID);
+            }
+            else
+            {
+                var list = new PopupList(
+                    typeof(Importer),
+                    Guid.Empty,
+                    Array.Empty<string>());
+                list.OnDefineFilter += t => new Filter<Importer>(x => x.EntityName).IsEqualTo(entityName);
+                if (list.ShowDialog() == true)
                 {
-                    var importer = new Client<Importer>().Load(new Filter<Importer>(x => x.ID).IsEqualTo(importers.Rows[0].Get<Importer, Guid>(x => x.ID))).First();
+                    var importer = new Client<Importer>().Load(new Filter<Importer>(x => x.ID).IsEqualTo(list.ID)).First();
                     DoImport(importer, componentFileName, setoutID);
                 }
-                else
-                {
-                    var list = new PopupList(
-                        typeof(Importer),
-                        Guid.Empty,
-                        Array.Empty<string>());
-                    list.OnDefineFilter += t => new Filter<Importer>(x => x.EntityName).IsEqualTo(entityName);
-                    if (list.ShowDialog() == true)
-                    {
-                        var importer = new Client<Importer>().Load(new Filter<Importer>(x => x.ID).IsEqualTo(list.ID)).First();
-                        DoImport(importer, componentFileName, setoutID);
-                    }
-                }
-            }
-            catch(Exception e)
-            {
-                Logger.Send(LogType.Error, "", $"Error in file {componentFileName}: {CoreUtils.FormatException(e)}");
-                MessageBox.Show($"Error opening {componentFileName}: {e.Message}");
             }
         }
+        catch(Exception e)
+        {
+            Logger.Send(LogType.Error, "", $"Error in file {componentFileName}: {CoreUtils.FormatException(e)}");
+            MessageBox.Show($"Error opening {componentFileName}: {e.Message}");
+        }
     }
 }

+ 988 - 989
prs.desktop/Panels/Tasks/TaskPanel.xaml.cs

@@ -17,1244 +17,1243 @@ using System.Drawing;
 using System.ComponentModel;
 using InABox.Wpf;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class TaskPanelProperties : BaseObject, IGlobalConfigurationSettings
+{
+    [Comment("Require that all tasks are given a task type.")]
+    public bool RequireTaskTypes { get; set; } = false;
+}
+
+public class TaskPanelFilterButton : FilterButton<Kanban>
 {
-    public class TaskPanelProperties : BaseObject, IGlobalConfigurationSettings
+    public TaskPanelFilterButton() : base(
+        new GlobalConfiguration<CoreFilterDefinitions>(nameof(Kanban)),
+        new UserConfiguration<CoreFilterDefinitions>(nameof(Kanban)))
     {
-        [CheckBoxEditor(ToolTip = "Require that all tasks are given a task type.")]
-        public bool RequireTaskTypes { get; set; } = false;
     }
 
-    public class TaskPanelFilterButton : FilterButton<Kanban>
+    public static Filter<Kanban> ConvertFilterToKanbanFilter(Filter<Kanban>? kanbanFilter)
     {
-        public TaskPanelFilterButton() : base(
-            new GlobalConfiguration<CoreFilterDefinitions>(nameof(Kanban)),
-            new UserConfiguration<CoreFilterDefinitions>(nameof(Kanban)))
+        if (kanbanFilter is null)
         {
+            return new Filter<Kanban>().All();
         }
-
-        public static Filter<Kanban> ConvertFilterToKanbanFilter(Filter<Kanban>? kanbanFilter)
+        else if (CoreUtils.TryFindMemberExpression(kanbanFilter.Expression, out var mexp))
         {
-            if (kanbanFilter is null)
+            var prop = CoreUtils.GetFullPropertyName(mexp, ".");
+            var filter = new Filter<Kanban>(prop)
             {
-                return new Filter<Kanban>().All();
-            }
-            else if (CoreUtils.TryFindMemberExpression(kanbanFilter.Expression, out var mexp))
-            {
-                var prop = CoreUtils.GetFullPropertyName(mexp, ".");
-                var filter = new Filter<Kanban>(prop)
-                {
-                    Operator = kanbanFilter.Operator,
-                    Value = kanbanFilter.Value
-                };
+                Operator = kanbanFilter.Operator,
+                Value = kanbanFilter.Value
+            };
 
-                filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToKanbanFilter));
-                filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToKanbanFilter));
+            filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToKanbanFilter));
+            filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToKanbanFilter));
 
-                return filter;
-            }
-            else
-            {
-                return new Filter<Kanban>().None();
-            }
+            return filter;
+        }
+        else
+        {
+            return new Filter<Kanban>().None();
         }
+    }
 
-        public static Filter<KanbanSubscriber> ConvertFilterToSubscriberFilter(Filter<IKanban>? kanbanFilter)
+    public static Filter<KanbanSubscriber> ConvertFilterToSubscriberFilter(Filter<IKanban>? kanbanFilter)
+    {
+        if (kanbanFilter is null)
         {
-            if (kanbanFilter is null)
-            {
-                return new Filter<KanbanSubscriber>().All();
-            }
-            else if (CoreUtils.TryFindMemberExpression(kanbanFilter.Expression, out var mexp))
+            return new Filter<KanbanSubscriber>().All();
+        }
+        else if (CoreUtils.TryFindMemberExpression(kanbanFilter.Expression, out var mexp))
+        {
+            var prop = CoreUtils.GetFullPropertyName(mexp, ".");
+            var filter = new Filter<KanbanSubscriber>(nameof(KanbanSubscriber.Kanban) + "." + prop)
             {
-                var prop = CoreUtils.GetFullPropertyName(mexp, ".");
-                var filter = new Filter<KanbanSubscriber>(nameof(KanbanSubscriber.Kanban) + "." + prop)
-                {
-                    Operator = kanbanFilter.Operator,
-                    Value = kanbanFilter.Value
-                };
+                Operator = kanbanFilter.Operator,
+                Value = kanbanFilter.Value
+            };
 
-                filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToSubscriberFilter));
-                filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToSubscriberFilter));
+            filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToSubscriberFilter));
+            filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToSubscriberFilter));
 
-                return filter;
-            }
-            else
-            {
-                return new Filter<KanbanSubscriber>().None();
-            }
+            return filter;
+        }
+        else
+        {
+            return new Filter<KanbanSubscriber>().None();
         }
     }
+}
 
-    /// <summary>
-    ///     Interaction logic for TaskPanel.xaml
-    /// </summary>
-    public partial class TaskPanel : UserControl, IPanel<Kanban>, ITaskHost, IMasterDetailControl<Job>, IPropertiesPanel<TaskPanelProperties, CanConfigureTasksPanel>
+/// <summary>
+///     Interaction logic for TaskPanel.xaml
+/// </summary>
+public partial class TaskPanel : UserControl, IPanel<Kanban>, ITaskHost, IMasterDetailControl<Job>, IPropertiesPanel<TaskPanelProperties, CanConfigureTasksPanel>
+{
+    private bool _bTabChanging;
+    private KanbanType[] kanbanTypes = null!; // Initialized in Setup()
+    
+    public IList<KanbanType> KanbanTypes => kanbanTypes;
+    
+    public Job? Master { get; set; }
+    
+    public TaskPanel()
     {
-        private bool _bTabChanging;
-        private KanbanType[] kanbanTypes = null!; // Initialized in Setup()
-        
-        public IList<KanbanType> KanbanTypes => kanbanTypes;
+        InitializeComponent();
         
-        public Job? Master { get; set; }
-        
-        public TaskPanel()
+        foreach (TabItem tab in TaskPanels.Items)
         {
-            InitializeComponent();
-            
-            foreach (TabItem tab in TaskPanels.Items)
-            {
-                var panel = (tab.Content as ITaskControl)!;
-                _viewmap[panel.KanbanViewType] = tab;
-                panel.Host = this;
-            }
+            var panel = (tab.Content as ITaskControl)!;
+            _viewmap[panel.KanbanViewType] = tab;
+            panel.Host = this;
         }
+    }
 
-        #region Menu
+    #region Menu
 
-        private void CompleteTask(ITaskControl control, RoutedEventArgs e, DateTime completed)
+    private void CompleteTask(ITaskControl control, RoutedEventArgs e, DateTime completed)
+    {
+        if (!MessageWindow.ShowYesNo("Are you sure you want to complete the selected tasks?", "Confirm Completion"))
+            return;
+        var tasks = (((FrameworkElement)e.Source).Tag as IEnumerable<TaskModel>)!;
+        Progress.ShowModal("Completing Tasks", progress =>
         {
-            if (!MessageWindow.ShowYesNo("Are you sure you want to complete the selected tasks?", "Confirm Completion"))
-                return;
-            var tasks = (((FrameworkElement)e.Source).Tag as IEnumerable<TaskModel>)!;
-            Progress.ShowModal("Completing Tasks", progress =>
+            var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
+            foreach (var kanban in kanbans)
             {
-                var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
-                foreach (var kanban in kanbans)
-                {
-                    kanban.Completed = completed;
-                    kanban.Status = KanbanStatus.Complete;
-                }
+                kanban.Completed = completed;
+                kanban.Status = KanbanStatus.Complete;
+            }
 
-                Client.Save(kanbans, $"Kanban Marked as Complete");
-            });
-            control.Refresh();
-        }
-        private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, KanbanStatus status)
-        {
-            menu.AddItem(header, null, Tuple.Create(control, models, status), ChangeStatus_Click);
-        }
+            Client.Save(kanbans, $"Kanban Marked as Complete");
+        });
+        control.Refresh();
+    }
+    private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, KanbanStatus status)
+    {
+        menu.AddItem(header, null, Tuple.Create(control, models, status), ChangeStatus_Click);
+    }
 
-        private void ChangeStatus_Click(Tuple<ITaskControl, TaskModel[], KanbanStatus> obj)
+    private void ChangeStatus_Click(Tuple<ITaskControl, TaskModel[], KanbanStatus> obj)
+    {
+        var (control, tasks, status) = obj;
+        if (!MessageWindow.ShowYesNo($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status"))
+            return;
+        Progress.ShowModal("Changing Status", progress =>
         {
-            var (control, tasks, status) = obj;
-            if (!MessageWindow.ShowYesNo($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status"))
-                return;
-            Progress.ShowModal("Changing Status", progress =>
+            var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
+            foreach (var kanban in kanbans)
             {
-                var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
-                foreach (var kanban in kanbans)
+                if (status == KanbanStatus.Complete)
                 {
-                    if (status == KanbanStatus.Complete)
-                    {
-                        kanban.Completed = DateTime.Now;
-                    }
-                    kanban.Status = status;
+                    kanban.Completed = DateTime.Now;
                 }
+                kanban.Status = status;
+            }
 
-                new Client<Kanban>().Save(kanbans, $"Kanban Marked as {status}");
-            });
-            control.Refresh();
-        }
+            new Client<Kanban>().Save(kanbans, $"Kanban Marked as {status}");
+        });
+        control.Refresh();
+    }
 
-        public bool CanChangeTasks(IEnumerable<TaskModel> models)
+    public bool CanChangeTasks(IEnumerable<TaskModel> models)
+    {
+        foreach (var task in models)
         {
-            foreach (var task in models)
+            if (!App.EmployeeID.Equals(task.ManagerID) && !App.EmployeeID.Equals(task.EmployeeID))
             {
-                if (!App.EmployeeID.Equals(task.ManagerID) && !App.EmployeeID.Equals(task.EmployeeID))
-                {
-                    // If you can change others tasks, IsFullControl is true - but we don't check at the beginning of the function
-                    // to save checking security tokens every time.
-                    return Security.IsAllowed<CanChangeOthersTasks>();
-                }
+                // If you can change others tasks, IsFullControl is true - but we don't check at the beginning of the function
+                // to save checking security tokens every time.
+                return Security.IsAllowed<CanChangeOthersTasks>();
             }
-            return true;
         }
+        return true;
+    }
 
-        public void PopulateMenu(ITaskControl control, TaskModel task, ContextMenu menu)
+    public void PopulateMenu(ITaskControl control, TaskModel task, ContextMenu menu)
+    {
+        menu.Items.Clear();
+        
+        var models = control.SelectedModels(task).ToArray();
+        var references = GetReferences(models);
+        var bLinks = references.Any(x => x.ReferenceType() != null);
+        var referencetypes = references.Select(x => x.ReferenceType()).Distinct().ToArray();
+        var bSingle = models.Length == 1;
+        var canChange = CanChangeTasks(models);
+
+        var edit = new MenuItem
         {
-            menu.Items.Clear();
-            
-            var models = control.SelectedModels(task).ToArray();
-            var references = GetReferences(models);
-            var bLinks = references.Any(x => x.ReferenceType() != null);
-            var referencetypes = references.Select(x => x.ReferenceType()).Distinct().ToArray();
-            var bSingle = models.Length == 1;
-            var canChange = CanChangeTasks(models);
-
-            var edit = new MenuItem
-            {
-                Tag = models,
-                Header = referencetypes.SingleOrDefault() == typeof(Requisition)
-                    ? "Edit Requisition Details"
-                    : referencetypes.SingleOrDefault() == typeof(Setout)
-                        ? "Edit Setout Details"
-                        : referencetypes.SingleOrDefault() == typeof(Delivery)
-                            ? "Edit Delivery Details"
-                            : referencetypes.SingleOrDefault() == typeof(PurchaseOrder)
-                                ? "Edit Order Details"
-                                : "Edit Task" + (bSingle ? "" : "s")
-            };
-            edit.Click += (o, e) =>
-            {
-                var tasks = (((MenuItem)e.Source).Tag as IEnumerable<TaskModel>)!;
-                if (EditReferences(tasks))
-                    control.Refresh();
-                e.Handled = true;
-            };
-            edit.IsEnabled = referencetypes.Length == 1;
-            menu.Items.Add(edit);
+            Tag = models,
+            Header = referencetypes.SingleOrDefault() == typeof(Requisition)
+                ? "Edit Requisition Details"
+                : referencetypes.SingleOrDefault() == typeof(Setout)
+                    ? "Edit Setout Details"
+                    : referencetypes.SingleOrDefault() == typeof(Delivery)
+                        ? "Edit Delivery Details"
+                        : referencetypes.SingleOrDefault() == typeof(PurchaseOrder)
+                            ? "Edit Order Details"
+                            : "Edit Task" + (bSingle ? "" : "s")
+        };
+        edit.Click += (o, e) =>
+        {
+            var tasks = (((MenuItem)e.Source).Tag as IEnumerable<TaskModel>)!;
+            if (EditReferences(tasks))
+                control.Refresh();
+            e.Handled = true;
+        };
+        edit.IsEnabled = referencetypes.Length == 1;
+        menu.Items.Add(edit);
+
+        if (!bLinks && models.Length == 1)
+        {
+            var digitalForms = new MenuItem { Header = "Digital Forms" };
 
-            if (!bLinks && models.Length == 1)
-            {
-                var digitalForms = new MenuItem { Header = "Digital Forms" };
+            var model = models.First();
 
-                var model = models.First();
+            DynamicGridUtils.PopulateFormMenu<KanbanForm, Kanban, KanbanLink>(
+                digitalForms,
+                model.ID,
+                () => new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(model.ID)).First(),
+                model.EmployeeID == App.EmployeeID);
 
-                DynamicGridUtils.PopulateFormMenu<KanbanForm, Kanban, KanbanLink>(
-                    digitalForms,
-                    model.ID,
-                    () => new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(model.ID)).First(),
-                    model.EmployeeID == App.EmployeeID);
+            menu.Items.Add(digitalForms);
+        }
 
-                menu.Items.Add(digitalForms);
-            }
+        if (!models.Any(x => !x.CompletedDate.IsEmpty()) && !bLinks)
+        {
+            menu.Items.Add(new Separator());
+
+            var job = new MenuItem
+            {
+                Tag = models,
+                Header = "Link to Job"
+            };
+            job.SubmenuOpened += (o, e) => CreateJobSubMenu(control, job, models);
+            menu.Items.Add(job);
 
-            if (!models.Any(x => !x.CompletedDate.IsEmpty()) && !bLinks)
+            if (bSingle)
             {
-                menu.Items.Add(new Separator());
 
-                var job = new MenuItem
+                menu.AddItem("Create Setout from Task", null, models.First(), task =>
                 {
-                    Tag = models,
-                    Header = "Link to Job"
-                };
-                job.SubmenuOpened += (o, e) => CreateJobSubMenu(control, job, models);
-                menu.Items.Add(job);
+                    if (!MessageWindow.ShowYesNo("This will convert this task into a Setout.\n\nDo you wish to continue?", "Confirmation"))
+                        return;
 
-                if (bSingle)
-                {
+                    ManufacturingTemplate? template = new Client<ManufacturingTemplate>()
+                        .Load(new Filter<ManufacturingTemplate>(x => x.Code).IsEqualTo("PRS")).FirstOrDefault();
+                    if (template == null)
+                    {
+                        MessageWindow.ShowMessage("[Pressing] Template does not exist!", "No template");
+                        return;
+                    }
 
-                    menu.AddItem("Create Setout from Task", null, models.First(), task =>
+                    string? setoutNumber = null;
+                    Kanban? kanban = null;
+                    ManufacturingTemplateStage[] tstages = Array.Empty<ManufacturingTemplateStage>();
+                    Progress.ShowModal("Creating Setout", (progress) =>
                     {
-                        if (!MessageWindow.ShowYesNo("This will convert this task into a Setout.\n\nDo you wish to continue?", "Confirmation"))
-                            return;
+                        var kanbanFilter = new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID);
+                        var tables = Client.QueryMultiple(new Dictionary<string, IQueryDef>
+                        {
+                            { "ManufacturingTemplateStage", new QueryDef<ManufacturingTemplateStage>(
+                                new Filter<ManufacturingTemplateStage>(x => x.Template.ID).IsEqualTo(template.ID),
+                                null,
+                                new SortOrder<ManufacturingTemplateStage>(x => x.Sequence)) },
+                            { "Kanban", new QueryDef<Kanban>(
+                                kanbanFilter,
+                                null,
+                                null) },
+                            { "Setout", new QueryDef<Setout>(
+                                new Filter<Setout>(x => x.JobLink.ID)
+                                    .InQuery(new SubQuery<Kanban>(kanbanFilter, new Column<Kanban>(x => x.JobLink.ID))),
+                                Columns.None<Setout>().Add(x => x.JobLink.JobNumber, x => x.Number),
+                                null) }
+                        });
 
-                        ManufacturingTemplate? template = new Client<ManufacturingTemplate>()
-                            .Load(new Filter<ManufacturingTemplate>(x => x.Code).IsEqualTo("PRS")).FirstOrDefault();
-                        if (template == null)
+                        tstages = tables["ManufacturingTemplateStage"].Rows
+                           .Select(x => x.ToObject<ManufacturingTemplateStage>()).ToArray();
+                        kanban = tables["Kanban"].Rows.FirstOrDefault()?.ToObject<Kanban>();
+                        if (kanban == null)
                         {
-                            MessageWindow.ShowMessage("[Pressing] Template does not exist!", "No template");
+                            MessageWindow.ShowMessage("Task does not exist!", "No task");
                             return;
                         }
 
-                        string? setoutNumber = null;
-                        Kanban? kanban = null;
-                        ManufacturingTemplateStage[] tstages = Array.Empty<ManufacturingTemplateStage>();
-                        Progress.ShowModal("Creating Setout", (progress) =>
-                        {
-                            var kanbanFilter = new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID);
-                            var tables = Client.QueryMultiple(new Dictionary<string, IQueryDef>
-                            {
-                                { "ManufacturingTemplateStage", new QueryDef<ManufacturingTemplateStage>(
-                                    new Filter<ManufacturingTemplateStage>(x => x.Template.ID).IsEqualTo(template.ID),
-                                    null,
-                                    new SortOrder<ManufacturingTemplateStage>(x => x.Sequence)) },
-                                { "Kanban", new QueryDef<Kanban>(
-                                    kanbanFilter,
-                                    null,
-                                    null) },
-                                { "Setout", new QueryDef<Setout>(
-                                    new Filter<Setout>(x => x.JobLink.ID)
-                                        .InQuery(new SubQuery<Kanban>(kanbanFilter, new Column<Kanban>(x => x.JobLink.ID))),
-                                    Columns.None<Setout>().Add(x => x.JobLink.JobNumber, x => x.Number),
-                                    null) }
-                            });
-
-                            tstages = tables["ManufacturingTemplateStage"].Rows
-                               .Select(x => x.ToObject<ManufacturingTemplateStage>()).ToArray();
-                            kanban = tables["Kanban"].Rows.FirstOrDefault()?.ToObject<Kanban>();
-                            if (kanban == null)
-                            {
-                                MessageWindow.ShowMessage("Task does not exist!", "No task");
-                                return;
-                            }
+                        progress.Report("Creating Setouts");
+                        CoreTable setouts = tables["Setout"];
 
-                            progress.Report("Creating Setouts");
-                            CoreTable setouts = tables["Setout"];
+                        int ireq = 0;
+                        string sreq = "";
+                        while (true)
+                        {
+                            ireq++;
+                            sreq = string.Format("{0}-{1:yyMMdd}-{2}", kanban.JobLink.JobNumber, DateTime.Now, ireq);
+                            if (!setouts.Rows.Any(r => sreq.Equals(r.Get<Setout, String>(c => c.Number))))
+                                break;
+                        }
+                        setoutNumber = sreq;
+                    });
+                    if (setoutNumber == null || kanban == null)
+                    {
+                        return;
+                    }
+                    var result = CreateSetout(
+                        task,
+                        s =>
+                        {
+                            s.Number = setoutNumber;
+                            s.JobLink.ID = task.JobID;
 
-                            int ireq = 0;
-                            string sreq = "";
-                            while (true)
+                            var notes = kanban.Notes.ToList();
+                            var description = kanban.Summary;
+                            if (string.IsNullOrWhiteSpace(description))
                             {
-                                ireq++;
-                                sreq = string.Format("{0}-{1:yyMMdd}-{2}", kanban.JobLink.JobNumber, DateTime.Now, ireq);
-                                if (!setouts.Rows.Any(r => sreq.Equals(r.Get<Setout, String>(c => c.Number))))
-                                    break;
+                                description = CoreUtils.StripHTML(kanban.Description);
                             }
-                            setoutNumber = sreq;
-                        });
-                        if (setoutNumber == null || kanban == null)
-                        {
-                            return;
-                        }
-                        var result = CreateSetout(
-                            task,
-                            s =>
+                            if (!string.IsNullOrWhiteSpace(description))
                             {
-                                s.Number = setoutNumber;
-                                s.JobLink.ID = task.JobID;
-
-                                var notes = kanban.Notes.ToList();
-                                var description = kanban.Summary;
-                                if (string.IsNullOrWhiteSpace(description))
-                                {
-                                    description = CoreUtils.StripHTML(kanban.Description);
-                                }
-                                if (!string.IsNullOrWhiteSpace(description))
-                                {
-                                    notes.Insert(0, description);
-                                }
-                                s.Description = string.Join("\n==========================================\n", notes);
+                                notes.Insert(0, description);
                             }
-                        );
-                        if (result != null)
+                            s.Description = string.Join("\n==========================================\n", notes);
+                        }
+                    );
+                    if (result != null)
+                    {
+                        Progress.ShowModal("Creating Manufacturing Packet", progress =>
                         {
-                            Progress.ShowModal("Creating Manufacturing Packet", progress =>
+                            ManufacturingPacket packet = new ManufacturingPacket()
                             {
-                                ManufacturingPacket packet = new ManufacturingPacket()
+                                Serial = template.Code,
+                                Title = kanban.Title,
+                                Quantity = 1,
+                                BarcodeQty = 1,
+                                DueDate = kanban.DueDate
+                            };
+                            packet.ManufacturingTemplateLink.ID = template.ID;
+                            packet.ManufacturingTemplateLink.Code = template.Code;
+                            packet.ManufacturingTemplateLink.Factory.ID = template.Factory.ID;
+                            packet.SetoutLink.ID = result.ID;
+                            new Client<ManufacturingPacket>().Save(packet, "Created from Task");
+
+                            DoLink<ManufacturingPacketKanban, ManufacturingPacket, ManufacturingPacketLink>(task, packet.ID);
+
+                            List<ManufacturingPacketStage> pstages = new List<ManufacturingPacketStage>();
+                            foreach (var tstage in tstages)
+                            {
+                                var pstage = new ManufacturingPacketStage()
                                 {
-                                    Serial = template.Code,
-                                    Title = kanban.Title,
-                                    Quantity = 1,
-                                    BarcodeQty = 1,
-                                    DueDate = kanban.DueDate
+                                    Time = tstage.Time,
+                                    Sequence = tstage.Sequence,
+                                    SequenceType = tstage.SequenceType,
+                                    Started = DateTime.MinValue,
+                                    PercentageComplete = 0.0F,
+                                    Completed = DateTime.MinValue,
+                                    QualityChecks = tstage.QualityChecks,
+                                    QualityStatus = QualityStatus.NotChecked,
+                                    QualityNotes = "",
                                 };
-                                packet.ManufacturingTemplateLink.ID = template.ID;
-                                packet.ManufacturingTemplateLink.Code = template.Code;
-                                packet.ManufacturingTemplateLink.Factory.ID = template.Factory.ID;
-                                packet.SetoutLink.ID = result.ID;
-                                new Client<ManufacturingPacket>().Save(packet, "Created from Task");
-
-                                DoLink<ManufacturingPacketKanban, ManufacturingPacket, ManufacturingPacketLink>(task, packet.ID);
-
-                                List<ManufacturingPacketStage> pstages = new List<ManufacturingPacketStage>();
-                                foreach (var tstage in tstages)
-                                {
-                                    var pstage = new ManufacturingPacketStage()
-                                    {
-                                        Time = tstage.Time,
-                                        Sequence = tstage.Sequence,
-                                        SequenceType = tstage.SequenceType,
-                                        Started = DateTime.MinValue,
-                                        PercentageComplete = 0.0F,
-                                        Completed = DateTime.MinValue,
-                                        QualityChecks = tstage.QualityChecks,
-                                        QualityStatus = QualityStatus.NotChecked,
-                                        QualityNotes = "",
-                                    };
-                                    pstage.Parent.ID = packet.ID;
-                                    pstage.ManufacturingSectionLink.ID = tstage.Section.ID;
-                                    pstage.ManufacturingSectionLink.Name = tstage.Section.Name;
-                                    pstages.Add(pstage);
-                                }
-                                new Client<ManufacturingPacketStage>().Save(pstages, "Created from Task", (_, __) => { });
-
-                                progress.Report("Processing Documents");
-                                List<SetoutDocument> _setoutdocuments = new List<SetoutDocument>();
-                                List<KanbanDocument> _kanbandocuments = new List<KanbanDocument>();
-                                KanbanDocument[] docrefs = new Client<KanbanDocument>()
-                                    .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
-                                foreach (var docref in docrefs)
-                                {
-                                    // Convert the document to a PDF
-                                    var docid = ProcessKanbanDocument(docref);
-                                    var newdoc = new SetoutDocument();
-                                    newdoc.EntityLink.ID = result.ID;
-                                    newdoc.DocumentLink.ID = docid;
-                                    _setoutdocuments.Add(newdoc);
-
-                                    if (docid != docref.DocumentLink.ID)
-                                    {
-                                        docref.DocumentLink.ID = docid;
-                                        _kanbandocuments.Add(docref);
-                                    }
-                                }
-                                new Client<SetoutDocument>().Save(_setoutdocuments, "Converted from Task", (_, __) => { });
-                                new Client<KanbanDocument>().Save(_kanbandocuments, "Converted to PDF", (_, __) => { });
-
-                                progress.Report("Updating Task");
-                                kanban.Title = kanban.Title + " (" + result.Number + ")";
-                                new Client<Kanban>().Save(kanban, "Converting Kanban to Setout");
-                            });
-                            control.Refresh();
-                        }
-                    });
-                    menu.AddItem("Create Requisition from Task", null, models, tasks =>
-                    {
-                        var taskModel = tasks.First();
-                        
-                        var kanbanTable = new Client<Kanban>().Query(new Filter<Kanban>(x => x.ID).IsEqualTo(taskModel.ID));
-                        var kanban = kanbanTable.Rows.First().ToObject<Kanban>();
-
-                        var result = CreateRequisition(
-                            taskModel,
-                            r =>
-                            {
-                                r.RequestedBy.ID = kanban.ManagerLink.ID;
-                                r.Employee.ID = Guid.Empty;
-                                r.Title = kanban.Title;
-
-                                r.Request = string.IsNullOrWhiteSpace(kanban.Summary)
-                                    ? String.IsNullOrWhiteSpace(kanban.Description)
-                                        ? String.Join("\n", kanban.Notes)
-                                        : CoreUtils.StripHTML(kanban.Description)
-                                    : kanban.Summary;
-
-                                r.Notes = kanban.Notes;
-                                r.Due = kanban.DueDate;
-                                r.JobLink.ID = taskModel.JobID;
+                                pstage.Parent.ID = packet.ID;
+                                pstage.ManufacturingSectionLink.ID = tstage.Section.ID;
+                                pstage.ManufacturingSectionLink.Name = tstage.Section.Name;
+                                pstages.Add(pstage);
                             }
-                        );
-                        if (result != null)
-                        {
-                            Progress.ShowModal("Updating Documents", progress =>
+                            new Client<ManufacturingPacketStage>().Save(pstages, "Created from Task", (_, __) => { });
+
+                            progress.Report("Processing Documents");
+                            List<SetoutDocument> _setoutdocuments = new List<SetoutDocument>();
+                            List<KanbanDocument> _kanbandocuments = new List<KanbanDocument>();
+                            KanbanDocument[] docrefs = new Client<KanbanDocument>()
+                                .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
+                            foreach (var docref in docrefs)
                             {
-                                progress.Report("Updating Documents");
-                                List<RequisitionDocument> requiDocuments = new();
-                                KanbanDocument[] kanbanDocuments = new Client<KanbanDocument>()
-                                    .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
-                                foreach (var document in kanbanDocuments)
+                                // Convert the document to a PDF
+                                var docid = ProcessKanbanDocument(docref);
+                                var newdoc = new SetoutDocument();
+                                newdoc.EntityLink.ID = result.ID;
+                                newdoc.DocumentLink.ID = docid;
+                                _setoutdocuments.Add(newdoc);
+
+                                if (docid != docref.DocumentLink.ID)
                                 {
-                                    var newdoc = new RequisitionDocument();
-                                    newdoc.EntityLink.ID = result.ID;
-                                    newdoc.DocumentLink.ID = document.DocumentLink.ID;
-                                    requiDocuments.Add(newdoc);
+                                    docref.DocumentLink.ID = docid;
+                                    _kanbandocuments.Add(docref);
                                 }
-                                new Client<RequisitionDocument>().Save(requiDocuments, "Converted from Task", (_, __) => { });
-
-                                /*RequisitionKanban link = new();
-                                link.Entity.ID = result.ID;
-                                link.Kanban.ID = kanban.ID;
-                                new Client<RequisitionKanban>().Save(link, "Converting Task -> Requisition", (_, __) => { });*/
-
-                                progress.Report("Updating Task");
-                                kanban.Status = KanbanStatus.Open;
-                                kanban.Completed = DateTime.MinValue;
-                                kanban.Title += $" (Requi #{result.Number})";
-                                new Client<Kanban>().Save(kanban, "Converted to Requisition", (_, __) => { });
-                            });
-                            MessageWindow.ShowMessage($"Created Requisition {result.Number}", "Success");
-                            control.Refresh();
-                        }
-                    });
-                    menu.AddItem("Create Delivery from Task", null, models, tasks =>
-                    {
-                        var result = CreateDelivery(
-                            tasks.First(),
-                            d =>
-                            {
-                                // Post-Process Requi Here
                             }
-                        );
-                        if (result != null)
-                            control.Refresh();
-                    });
-                    menu.AddItem("Create Purchase Order from Task", null, models, tasks =>
+                            new Client<SetoutDocument>().Save(_setoutdocuments, "Converted from Task", (_, __) => { });
+                            new Client<KanbanDocument>().Save(_kanbandocuments, "Converted to PDF", (_, __) => { });
+
+                            progress.Report("Updating Task");
+                            kanban.Title = kanban.Title + " (" + result.Number + ")";
+                            new Client<Kanban>().Save(kanban, "Converting Kanban to Setout");
+                        });
+                        control.Refresh();
+                    }
+                });
+                menu.AddItem("Create Requisition from Task", null, models, tasks =>
+                {
+                    var taskModel = tasks.First();
+                    
+                    var kanbanTable = new Client<Kanban>().Query(new Filter<Kanban>(x => x.ID).IsEqualTo(taskModel.ID));
+                    var kanban = kanbanTable.Rows.First().ToObject<Kanban>();
+
+                    var result = CreateRequisition(
+                        taskModel,
+                        r =>
+                        {
+                            r.RequestedBy.ID = kanban.ManagerLink.ID;
+                            r.Employee.ID = Guid.Empty;
+                            r.Title = kanban.Title;
+
+                            r.Request = string.IsNullOrWhiteSpace(kanban.Summary)
+                                ? String.IsNullOrWhiteSpace(kanban.Description)
+                                    ? String.Join("\n", kanban.Notes)
+                                    : CoreUtils.StripHTML(kanban.Description)
+                                : kanban.Summary;
+
+                            r.Notes = kanban.Notes;
+                            r.Due = kanban.DueDate;
+                            r.JobLink.ID = taskModel.JobID;
+                        }
+                    );
+                    if (result != null)
                     {
-                        var result = CreateOrder(
-                            tasks.First(),
-                            p =>
+                        Progress.ShowModal("Updating Documents", progress =>
+                        {
+                            progress.Report("Updating Documents");
+                            List<RequisitionDocument> requiDocuments = new();
+                            KanbanDocument[] kanbanDocuments = new Client<KanbanDocument>()
+                                .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
+                            foreach (var document in kanbanDocuments)
                             {
-                                // Post-Process Requi Here
+                                var newdoc = new RequisitionDocument();
+                                newdoc.EntityLink.ID = result.ID;
+                                newdoc.DocumentLink.ID = document.DocumentLink.ID;
+                                requiDocuments.Add(newdoc);
                             }
-                        );
-                        if (result != null)
-                            control.Refresh();
-                    });
-                }
+                            new Client<RequisitionDocument>().Save(requiDocuments, "Converted from Task", (_, __) => { });
+
+                            /*RequisitionKanban link = new();
+                            link.Entity.ID = result.ID;
+                            link.Kanban.ID = kanban.ID;
+                            new Client<RequisitionKanban>().Save(link, "Converting Task -> Requisition", (_, __) => { });*/
+
+                            progress.Report("Updating Task");
+                            kanban.Status = KanbanStatus.Open;
+                            kanban.Completed = DateTime.MinValue;
+                            kanban.Title += $" (Requi #{result.Number})";
+                            new Client<Kanban>().Save(kanban, "Converted to Requisition", (_, __) => { });
+                        });
+                        MessageWindow.ShowMessage($"Created Requisition {result.Number}", "Success");
+                        control.Refresh();
+                    }
+                });
+                menu.AddItem("Create Delivery from Task", null, models, tasks =>
+                {
+                    var result = CreateDelivery(
+                        tasks.First(),
+                        d =>
+                        {
+                            // Post-Process Requi Here
+                        }
+                    );
+                    if (result != null)
+                        control.Refresh();
+                });
+                menu.AddItem("Create Purchase Order from Task", null, models, tasks =>
+                {
+                    var result = CreateOrder(
+                        tasks.First(),
+                        p =>
+                        {
+                            // Post-Process Requi Here
+                        }
+                    );
+                    if (result != null)
+                        control.Refresh();
+                });
             }
+        }
 
-            if (!bLinks && canChange)
-            {
-                menu.Items.Add(new Separator());
+        if (!bLinks && canChange)
+        {
+            menu.Items.Add(new Separator());
 
-                var changeStatus = new MenuItem { Header = "Change Status" };
+            var changeStatus = new MenuItem { Header = "Change Status" };
 
-                AddChangeStatusButton(control, models, changeStatus, "Open", KanbanStatus.Open);
-                AddChangeStatusButton(control, models, changeStatus, "In Progress", KanbanStatus.InProgress);
-                AddChangeStatusButton(control, models, changeStatus, "Waiting", KanbanStatus.Waiting);
+            AddChangeStatusButton(control, models, changeStatus, "Open", KanbanStatus.Open);
+            AddChangeStatusButton(control, models, changeStatus, "In Progress", KanbanStatus.InProgress);
+            AddChangeStatusButton(control, models, changeStatus, "Waiting", KanbanStatus.Waiting);
 
-                if (models.Any(x => x.CompletedDate.IsEmpty()))
+            if (models.Any(x => x.CompletedDate.IsEmpty()))
+            {
+                var complete = new MenuItem
+                {
+                    Tag = models,
+                    Header = models.Length > 1 ? "Complete Tasks" : "Complete Task"
+                };
+                complete.Click += (o, e) =>
                 {
-                    var complete = new MenuItem
+                    CompleteTask(control, e, DateTime.Now);
+                };
+                menu.Items.Add(complete);
+
+                if (Security.IsAllowed<CanSetKanbanCompletedDate>())
+                {
+                    var completeDate = new MenuItem
                     {
                         Tag = models,
-                        Header = models.Length > 1 ? "Complete Tasks" : "Complete Task"
-                    };
-                    complete.Click += (o, e) =>
-                    {
-                        CompleteTask(control, e, DateTime.Now);
+                        Header = "Set Completed Date"
                     };
-                    menu.Items.Add(complete);
 
-                    if (Security.IsAllowed<CanSetKanbanCompletedDate>())
-                    {
-                        var completeDate = new MenuItem
-                        {
-                            Tag = models,
-                            Header = "Set Completed Date"
-                        };
-
-                        var dateItem = new MenuItem();
+                    var dateItem = new MenuItem();
 
-                        var dateCalendar = new System.Windows.Controls.Calendar { SelectedDate = DateTime.MinValue };
-                        dateCalendar.Tag = models;
-                        dateCalendar.SelectedDatesChanged += (o, e) =>
-                        {
-                            if (e.Source is not System.Windows.Controls.Calendar calendar) return;
-                            menu.IsOpen = false;
+                    var dateCalendar = new System.Windows.Controls.Calendar { SelectedDate = DateTime.MinValue };
+                    dateCalendar.Tag = models;
+                    dateCalendar.SelectedDatesChanged += (o, e) =>
+                    {
+                        if (e.Source is not System.Windows.Controls.Calendar calendar) return;
+                        menu.IsOpen = false;
 
-                            var selectedDate = calendar.SelectedDate ?? DateTime.Now;
-                            CompleteTask(control, e, selectedDate);
-                        };
+                        var selectedDate = calendar.SelectedDate ?? DateTime.Now;
+                        CompleteTask(control, e, selectedDate);
+                    };
 
-                        dateItem.Header = dateCalendar;
-                        dateItem.Style = Resources["calendarItem"] as Style;
+                    dateItem.Header = dateCalendar;
+                    dateItem.Style = Resources["calendarItem"] as Style;
 
-                        completeDate.Items.Add(dateItem);
+                    completeDate.Items.Add(dateItem);
 
-                        menu.Items.Add(completeDate);
-                    }
+                    menu.Items.Add(completeDate);
                 }
-                else
+            }
+            else
+            {
+                menu.AddItem(models.Length > 1 ? "Archive Tasks" : "Archive Task", null, models, tasks =>
                 {
-                    menu.AddItem(models.Length > 1 ? "Archive Tasks" : "Archive Task", null, models, tasks =>
-                    {
-                        if (!MessageWindow.ShowYesNo("Are you sure you want to remove the selected tasks from the list?", "Confirm removal"))
-                            return;
+                    if (!MessageWindow.ShowYesNo("Are you sure you want to remove the selected tasks from the list?", "Confirm removal"))
+                        return;
 
-                        Progress.ShowModal("Closing Kanbans", progress =>
-                        {
-                            var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Closed));
-                            foreach (var kanban in kanbans)
-                                kanban.Closed = DateTime.Now;
-                            new Client<Kanban>().Save(kanbans, "Kanban Marked as Closed");
-                        });
-                        control.Refresh();
+                    Progress.ShowModal("Closing Kanbans", progress =>
+                    {
+                        var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Closed));
+                        foreach (var kanban in kanbans)
+                            kanban.Closed = DateTime.Now;
+                        new Client<Kanban>().Save(kanbans, "Kanban Marked as Closed");
                     });
-                }
+                    control.Refresh();
+                });
+            }
 
-                menu.Items.Add(changeStatus);
+            menu.Items.Add(changeStatus);
 
-                var changeType = new MenuItem { Header = "Change Task Type", Tag = models };
+            var changeType = new MenuItem { Header = "Change Task Type", Tag = models };
 
-                foreach(var type in KanbanTypes)
+            foreach(var type in KanbanTypes)
+            {
+                changeType.AddItem($"{type.Code}: {type.Description}", null, type, type =>
                 {
-                    changeType.AddItem($"{type.Code}: {type.Description}", null, type, type =>
+                    Progress.ShowModal("Changing Task Type", progress =>
                     {
-                        Progress.ShowModal("Changing Task Type", progress =>
+                        var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.Type.ID));
+                        foreach (var kanban in kanbans)
                         {
-                            var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.Type.ID));
-                            foreach (var kanban in kanbans)
-                            {
-                                kanban.Type.ID = type.ID;
-                            }
+                            kanban.Type.ID = type.ID;
+                        }
 
-                            new Client<Kanban>().Save(kanbans, $"Kanban Task Type changed to {type}");
-                        });
-                        control.Refresh();
+                        new Client<Kanban>().Save(kanbans, $"Kanban Task Type changed to {type}");
                     });
-                }
+                    control.Refresh();
+                });
+            }
 
-                menu.Items.Add(changeType);
+            menu.Items.Add(changeType);
 
 
-                var changeDueDate = new MenuItem { Header = "Change Due Date" };
+            var changeDueDate = new MenuItem { Header = "Change Due Date" };
 
-                var calendarItem = new MenuItem();
+            var calendarItem = new MenuItem();
 
-                var calendar = new System.Windows.Controls.Calendar { SelectedDate = models.Length == 1 ? models[0].DueDate : DateTime.Today };
-                calendar.Tag = models;
-                calendar.SelectedDatesChanged += (o, e) =>
-                {
-                    if (e.Source is not System.Windows.Controls.Calendar calendar) return;
+            var calendar = new System.Windows.Controls.Calendar { SelectedDate = models.Length == 1 ? models[0].DueDate : DateTime.Today };
+            calendar.Tag = models;
+            calendar.SelectedDatesChanged += (o, e) =>
+            {
+                if (e.Source is not System.Windows.Controls.Calendar calendar) return;
 
-                    var selectedDate = calendar.SelectedDate ?? DateTime.Now;
-                    var models = (calendar.Tag as IList<TaskModel>)!;
-                    Progress.ShowModal("Changing Due Date", progress =>
+                var selectedDate = calendar.SelectedDate ?? DateTime.Now;
+                var models = (calendar.Tag as IList<TaskModel>)!;
+                Progress.ShowModal("Changing Due Date", progress =>
+                {
+                    var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.DueDate));
+                    foreach (var kanban in kanbans)
                     {
-                        var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.DueDate));
-                        foreach (var kanban in kanbans)
-                        {
-                            kanban.DueDate = selectedDate;
-                        }
+                        kanban.DueDate = selectedDate;
+                    }
 
-                        new Client<Kanban>().Save(kanbans, $"Kanban Due Date changed to {selectedDate:dd MMM yyyy}");
-                    });
-                    control.Refresh();
+                    new Client<Kanban>().Save(kanbans, $"Kanban Due Date changed to {selectedDate:dd MMM yyyy}");
+                });
+                control.Refresh();
 
-                    menu.IsOpen = false;
-                };
+                menu.IsOpen = false;
+            };
 
-                calendarItem.Header = calendar;
-                calendarItem.Style = Resources["calendarItem"] as Style;
+            calendarItem.Header = calendar;
+            calendarItem.Style = Resources["calendarItem"] as Style;
 
-                changeDueDate.Items.Add(calendarItem);
+            changeDueDate.Items.Add(calendarItem);
 
-                menu.Items.Add(changeDueDate);
-            }
+            menu.Items.Add(changeDueDate);
         }
+    }
 
-        /// <summary>
-        /// Takes a <see cref="KanbanDocument"/>, and if it is a .txt or an image (".png", ".jpg", ".jpeg" or ".bmp"), converts to a PDF
-        /// with the content of the document, saving a new document with extension changed to ".pdf".
-        /// </summary>
-        /// <param name="docref">The original document.</param>
-        /// <returns>
-        /// The ID of the new <see cref="Document"/> or,
-        /// if not one of the given types, the original document ID.
-        /// </returns>
-        private static Guid ProcessKanbanDocument(KanbanDocument docref)
-        {
-            var result = docref.DocumentLink.ID;
+    /// <summary>
+    /// Takes a <see cref="KanbanDocument"/>, and if it is a .txt or an image (".png", ".jpg", ".jpeg" or ".bmp"), converts to a PDF
+    /// with the content of the document, saving a new document with extension changed to ".pdf".
+    /// </summary>
+    /// <param name="docref">The original document.</param>
+    /// <returns>
+    /// The ID of the new <see cref="Document"/> or,
+    /// if not one of the given types, the original document ID.
+    /// </returns>
+    private static Guid ProcessKanbanDocument(KanbanDocument docref)
+    {
+        var result = docref.DocumentLink.ID;
 
-            var ext = System.IO.Path.GetExtension(docref.DocumentLink.FileName).ToLower();
+        var ext = System.IO.Path.GetExtension(docref.DocumentLink.FileName).ToLower();
 
-            if (ext.EndsWith("txt"))
+        if (ext.EndsWith("txt"))
+        {
+            var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
+            if (doc is null)
             {
-                var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
-                if (doc is null)
-                {
-                    Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
-                    return docref.DocumentLink.ID;
-                }
-
-                PdfDocument pdf = new PdfDocument();
-                PdfPage page = pdf.Pages.Add();
-                PdfGraphics graphics = page.Graphics;
-                PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 12);
-                String text = System.Text.Encoding.UTF8.GetString(doc.Data);
-                graphics.DrawString(text, font, PdfBrushes.Black, new PointF(0, 0));
-                MemoryStream ms = new MemoryStream();
-                pdf.Save(ms);
-                pdf.Close(true);
-                byte[] data = ms.ToArray();
-                var newdoc = new Document()
-                {
-                    Data = data,
-                    FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
-                    CRC = CoreUtils.CalculateCRC(data),
-                    TimeStamp = DateTime.Now,
-                };
-                new Client<Document>().Save(newdoc, "Converted from Text");
-                return newdoc.ID;
+                Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
+                return docref.DocumentLink.ID;
             }
 
-            else if (ext.EndsWith("png") || ext.EndsWith("bmp") || ext.EndsWith("jpg") || ext.EndsWith("jpeg"))
+            PdfDocument pdf = new PdfDocument();
+            PdfPage page = pdf.Pages.Add();
+            PdfGraphics graphics = page.Graphics;
+            PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 12);
+            String text = System.Text.Encoding.UTF8.GetString(doc.Data);
+            graphics.DrawString(text, font, PdfBrushes.Black, new PointF(0, 0));
+            MemoryStream ms = new MemoryStream();
+            pdf.Save(ms);
+            pdf.Close(true);
+            byte[] data = ms.ToArray();
+            var newdoc = new Document()
             {
-                var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
-                if (doc is null)
-                {
-                    Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
-                    return docref.DocumentLink.ID;
-                }
+                Data = data,
+                FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
+                CRC = CoreUtils.CalculateCRC(data),
+                TimeStamp = DateTime.Now,
+            };
+            new Client<Document>().Save(newdoc, "Converted from Text");
+            return newdoc.ID;
+        }
 
-                PdfBitmap image = new PdfBitmap(new MemoryStream(doc.Data));
-                PdfDocument pdf = new PdfDocument();
-                pdf.PageSettings.Orientation = image.Height > image.Width ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
-                pdf.PageSettings.Size = new SizeF(image.Width, image.Height);
-                PdfPage page = pdf.Pages.Add();
-                PdfGraphics graphics = page.Graphics;
-                graphics.DrawImage(image, 0.0F, 0.0F);
-                MemoryStream ms = new MemoryStream();
-                pdf.Save(ms);
-                pdf.Close(true);
-                byte[] data = ms.ToArray();
-                var newdoc = new Document()
-                {
-                    Data = data,
-                    FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
-                    CRC = CoreUtils.CalculateCRC(data),
-                    TimeStamp = DateTime.Now,
-                };
-                new Client<Document>().Save(newdoc, "Converted from Image");
-                return newdoc.ID;
+        else if (ext.EndsWith("png") || ext.EndsWith("bmp") || ext.EndsWith("jpg") || ext.EndsWith("jpeg"))
+        {
+            var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
+            if (doc is null)
+            {
+                Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
+                return docref.DocumentLink.ID;
             }
 
-            return result;
+            PdfBitmap image = new PdfBitmap(new MemoryStream(doc.Data));
+            PdfDocument pdf = new PdfDocument();
+            pdf.PageSettings.Orientation = image.Height > image.Width ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
+            pdf.PageSettings.Size = new SizeF(image.Width, image.Height);
+            PdfPage page = pdf.Pages.Add();
+            PdfGraphics graphics = page.Graphics;
+            graphics.DrawImage(image, 0.0F, 0.0F);
+            MemoryStream ms = new MemoryStream();
+            pdf.Save(ms);
+            pdf.Close(true);
+            byte[] data = ms.ToArray();
+            var newdoc = new Document()
+            {
+                Data = data,
+                FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
+                CRC = CoreUtils.CalculateCRC(data),
+                TimeStamp = DateTime.Now,
+            };
+            new Client<Document>().Save(newdoc, "Converted from Image");
+            return newdoc.ID;
         }
-        private void CreateJobSubMenu(ITaskControl control, MenuItem job, IEnumerable<TaskModel> tasks)
+
+        return result;
+    }
+    private void CreateJobSubMenu(ITaskControl control, MenuItem job, IEnumerable<TaskModel> tasks)
+    {
+        job.Items.Clear();
+        job.Items.Add(new MenuItem { Header = "Loading...", IsEnabled = false });
+        using (new WaitCursor())
         {
             job.Items.Clear();
-            job.Items.Add(new MenuItem { Header = "Loading...", IsEnabled = false });
-            using (new WaitCursor())
+            var jobs = new Client<Job>().Query(
+                LookupFactory.DefineFilter<Job>(),
+                LookupFactory.DefineColumns<Job>(),
+                LookupFactory.DefineSort<Job>()
+            );
+            foreach (var row in jobs.Rows)
             {
-                job.Items.Clear();
-                var jobs = new Client<Job>().Query(
-                    LookupFactory.DefineFilter<Job>(),
-                    LookupFactory.DefineColumns<Job>(),
-                    LookupFactory.DefineSort<Job>()
-                );
-                foreach (var row in jobs.Rows)
+                var jobNumber = row.Get<Job, string>(x => x.JobNumber);
+                var jobName = row.Get<Job, string>(x => x.Name);
+                job.AddItem($"{jobNumber}: {jobName}", null, tasks, tasks =>
                 {
-                    var jobNumber = row.Get<Job, string>(x => x.JobNumber);
-                    var jobName = row.Get<Job, string>(x => x.Name);
-                    job.AddItem($"{jobNumber}: {jobName}", null, tasks, tasks =>
+                    using (new WaitCursor())
                     {
-                        using (new WaitCursor())
-                        {
-                            var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.JobLink.ID));
-                            foreach (var kanban in kanbans)
-                                kanban.JobLink.ID = row.Get<Job, Guid>(x => x.ID);
-                            new Client<Kanban>().Save(kanbans, "Updated Job Number");
-                            control.Refresh();
-                        }
-                    });
-                }
+                        var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.JobLink.ID));
+                        foreach (var kanban in kanbans)
+                            kanban.JobLink.ID = row.Get<Job, Guid>(x => x.ID);
+                        new Client<Kanban>().Save(kanbans, "Updated Job Number");
+                        control.Refresh();
+                    }
+                });
             }
         }
+    }
 
-        #endregion
+    #endregion
 
-        private void TaskPanels_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (!IsReady)
-                return;
+    private void TaskPanels_SelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
+        if (!IsReady)
+            return;
 
-            if (e.Source is not TabControl)
-                return;
+        if (e.Source is not TabControl)
+            return;
 
-            if (_bTabChanging)
-                return;
-            try
-            {
-                _bTabChanging = true;
-                var panel = GetCurrentPanel();
-                if(panel is not null)
-                {
-                    KanbanSettings.ViewType = panel.KanbanViewType;
-                    new UserConfiguration<KanbanSettings>().Save(KanbanSettings);
-                    panel.Refresh();
-                }
-            }
-            finally
+        if (_bTabChanging)
+            return;
+        try
+        {
+            _bTabChanging = true;
+            var panel = GetCurrentPanel();
+            if(panel is not null)
             {
-                _bTabChanging = false;
+                KanbanSettings.ViewType = panel.KanbanViewType;
+                new UserConfiguration<KanbanSettings>().Save(KanbanSettings);
+                panel.Refresh();
             }
         }
+        finally
+        {
+            _bTabChanging = false;
+        }
+    }
 
-        #region Get/Save Settings
+    #region Get/Save Settings
 
-        private KanbanSettings? _settings;
+    private KanbanSettings? _settings;
 
-        public KanbanSettings KanbanSettings
+    public KanbanSettings KanbanSettings
+    {
+        get
         {
-            get
-            {
-                _settings ??= new UserConfiguration<KanbanSettings>().Load();
-                return _settings;
-            }
+            _settings ??= new UserConfiguration<KanbanSettings>().Load();
+            return _settings;
         }
+    }
 
-        public void SaveSettings()
-        {
-            if(_settings != null)
-                new UserConfiguration<KanbanSettings>().Save(_settings);
-        }
+    public void SaveSettings()
+    {
+        if(_settings != null)
+            new UserConfiguration<KanbanSettings>().Save(_settings);
+    }
 
-        #endregion
+    #endregion
 
-        #region IPanel Stuff
+    #region IPanel Stuff
 
-        public event DataModelUpdateEvent? OnUpdateDataModel;
+    public event DataModelUpdateEvent? OnUpdateDataModel;
 
-        public bool IsReady { get; set; }
+    public bool IsReady { get; set; }
 
-        public void CreateToolbarButtons(IPanelHost host)
+    public void CreateToolbarButtons(IPanelHost host)
+    {
+        host.CreatePanelAction(
+            new PanelAction
+            {
+                Caption = "New Task",
+                OnExecute = a => {
+                    if(CreateKanban(k => { }) != null)
+                    {
+                        Refresh();
+                    }
+                },
+                Image = InABox.Wpf.Resources.add
+            }
+        );
+        if (Security.CanView<KanbanType>())
         {
-            host.CreatePanelAction(
+            host.CreateSetupAction(
                 new PanelAction
                 {
-                    Caption = "New Task",
-                    OnExecute = a => {
-                        if(CreateKanban(k => { }) != null)
-                        {
-                            Refresh();
-                        }
-                    },
-                    Image = InABox.Wpf.Resources.add
-                }
-            );
-            if (Security.CanView<KanbanType>())
-            {
-                host.CreateSetupAction(
-                    new PanelAction
+                    Caption = "Task Types",
+                    Image = PRSDesktop.Resources.kanbantype,
+                    OnExecute = a =>
                     {
-                        Caption = "Task Types",
-                        Image = PRSDesktop.Resources.kanbantype,
-                        OnExecute = a =>
-                        {
-                            var list = new MasterList(typeof(KanbanType));
-                            list.ShowDialog();
-                        }
-                    });
-            }
+                        var list = new MasterList(typeof(KanbanType));
+                        list.ShowDialog();
+                    }
+                });
         }
+    }
 
-        public Dictionary<string, object[]> Selected()
-        {
-            return new Dictionary<string, object[]>();
-        }
+    public Dictionary<string, object[]> Selected()
+    {
+        return new Dictionary<string, object[]>();
+    }
 
-        public void Heartbeat(TimeSpan time)
-        {
-        }
+    public void Heartbeat(TimeSpan time)
+    {
+    }
 
-        private readonly Dictionary<KanbanViewType, TabItem> _viewmap = new();
+    private readonly Dictionary<KanbanViewType, TabItem> _viewmap = new();
 
-        private readonly List<ITaskControl> _initialized = new();
+    private readonly List<ITaskControl> _initialized = new();
 
-        private ITaskControl GetCurrentPanel()
+    private ITaskControl GetCurrentPanel()
+    {
+        var result = (TaskPanels.SelectedContent as ITaskControl)!;
+        if (result == null)
+            result = (TaskPanels.Items[0] as DynamicTabItem)?.Content as ITaskControl;
+        try
         {
-            var result = (TaskPanels.SelectedContent as ITaskControl)!;
-            if (result == null)
-                result = (TaskPanels.Items[0] as DynamicTabItem)?.Content as ITaskControl;
-            try
-            {
-                //if (result != null)
-                if (!_initialized.Contains(result))
-                {
-                    result.Setup();
-                    result.IsReady = true;
-                    _initialized.Add(result);
-                }
-            }
-            catch (Exception e)
+            //if (result != null)
+            if (!_initialized.Contains(result))
             {
-                Logger.Send(LogType.Error, "", $"Error in TaskPanel.GetCurrentPanel: {CoreUtils.FormatException(e)}");
+                result.Setup();
+                result.IsReady = true;
+                _initialized.Add(result);
             }
-
-            return result;
         }
-
-        public void Setup()
+        catch (Exception e)
         {
-            _settings = new UserConfiguration<KanbanSettings>().Load();
-
+            Logger.Send(LogType.Error, "", $"Error in TaskPanel.GetCurrentPanel: {CoreUtils.FormatException(e)}");
+        }
 
-            TaskPanels.SelectedItem = _viewmap[_settings.ViewType];
+        return result;
+    }
 
-            kanbanTypes = new Client<KanbanType>()
-                .Query(
-                    new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
-                    Columns.None<KanbanType>().Add(x => x.ID, x => x.Code, x => x.Description))
-                .Rows.Select(x => x.ToObject<KanbanType>()).ToArray();
-        }
+    public void Setup()
+    {
+        _settings = new UserConfiguration<KanbanSettings>().Load();
 
-        public void Shutdown(CancelEventArgs? cancel)
-        {
-        }
 
-        public void Refresh()
-        {
-            if (Master == null)
-            {
-                if (Equals(TaskPanels.SelectedItem, TasksPlannerTabItem))
-                    TaskPanels.SelectedItem = _viewmap[KanbanViewType.Status];
-                TasksPlannerTabItem.Visibility = Visibility.Collapsed;
-            }
-            else
-                TasksPlannerTabItem.Visibility = Visibility.Visible;
-            
-            GetCurrentPanel()?.Refresh();
-        }
+        TaskPanels.SelectedItem = _viewmap[_settings.ViewType];
 
-        public string SectionName => GetCurrentPanel().SectionName;
+        kanbanTypes = new Client<KanbanType>()
+            .Query(
+                new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
+                Columns.None<KanbanType>().Add(x => x.ID, x => x.Code, x => x.Description))
+            .Rows.Select(x => x.ToObject<KanbanType>()).ToArray();
+    }
 
-        public TaskPanelProperties Properties { get; set; }
+    public void Shutdown(CancelEventArgs? cancel)
+    {
+    }
 
-        public DataModel DataModel(Selection selection)
+    public void Refresh()
+    {
+        if (Master == null)
         {
-            return GetCurrentPanel().DataModel(selection);
-            //return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).IsEqualTo(Guid.Empty));
+            if (Equals(TaskPanels.SelectedItem, TasksPlannerTabItem))
+                TaskPanels.SelectedItem = _viewmap[KanbanViewType.Status];
+            TasksPlannerTabItem.Visibility = Visibility.Collapsed;
         }
+        else
+            TasksPlannerTabItem.Visibility = Visibility.Visible;
+        
+        GetCurrentPanel()?.Refresh();
+    }
 
-        #endregion
+    public string SectionName => GetCurrentPanel().SectionName;
 
-        #region CRUD Functionality
+    public TaskPanelProperties Properties { get; set; }
 
-        private TEntity? DoCreate<TEntity>(Action<TEntity> customise)
-            where TEntity : Entity, IRemotable, IPersistent, new()
-        {
-            var result = new TEntity();
-            customise?.Invoke(result);
-            if (DoEdit(new[] { result }, null))
-                return result;
-            return null;
-        }
+    public DataModel DataModel(Selection selection)
+    {
+        return GetCurrentPanel().DataModel(selection);
+        //return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).IsEqualTo(Guid.Empty));
+    }
 
-        private readonly Dictionary<Type, IDynamicGrid> _grids = new();
+    #endregion
 
-        private readonly List<Tuple<Guid, Entity>> _entitycache = new();
+    #region CRUD Functionality
 
-        private DynamicDataGrid<TEntity> GetGrid<TEntity>() where TEntity : Entity, IRemotable, IPersistent, new()
-        {
-            if(!_grids.TryGetValue(typeof(TEntity), out var grid))
-            {
-                grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(TEntity)) as DynamicDataGrid<TEntity>)!;
-                _grids[typeof(TEntity)] = grid;
+    private TEntity? DoCreate<TEntity>(Action<TEntity> customise)
+        where TEntity : Entity, IRemotable, IPersistent, new()
+    {
+        var result = new TEntity();
+        customise?.Invoke(result);
+        if (DoEdit(new[] { result }, null))
+            return result;
+        return null;
+    }
 
-                if (typeof(TEntity) == typeof(Kanban))
-                {
-                    CustomiseKanbanGrid((grid as DynamicDataGrid<Kanban>)!);
-                }
-            }
+    private readonly Dictionary<Type, IDynamicGrid> _grids = new();
 
-            return (grid as DynamicDataGrid<TEntity>)!;
-        }
+    private readonly List<Tuple<Guid, Entity>> _entitycache = new();
 
-        private IEnumerable<TEntity> DoLoad<TEntity>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
-            where TEntity : Entity, IRemotable, IPersistent, new()
+    private DynamicDataGrid<TEntity> GetGrid<TEntity>() where TEntity : Entity, IRemotable, IPersistent, new()
+    {
+        if(!_grids.TryGetValue(typeof(TEntity), out var grid))
         {
-            var result = new List<TEntity>();
-            var load = new List<Guid>();
-            foreach (var model in models)
-            {
-                var entity = _entitycache.FirstOrDefault(x => Equals(x.Item1, model.ID) && x.Item2 is TEntity) as TEntity;
-                if (entity is not null)
-                    result.Add(entity);
-                else
-                    load.Add(model.ID);
-            }
+            grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(TEntity)) as DynamicDataGrid<TEntity>)!;
+            _grids[typeof(TEntity)] = grid;
 
-            if (load.Any())
+            if (typeof(TEntity) == typeof(Kanban))
             {
-                var entities = new Client<TEntity>()
-                    .Query(new Filter<TEntity>(x => x.ID).InList(load.ToArray()), columns)
-                    .Rows.Select(x => x.ToObject<TEntity>()).ToList();
-                foreach (var entity in entities)
-                    _entitycache.Add(new Tuple<Guid, Entity>(entity.ID, entity));
-                result.AddRange(entities);
+                CustomiseKanbanGrid((grid as DynamicDataGrid<Kanban>)!);
             }
-
-            return result;
         }
 
-        private IEnumerable<TEntity> DoLoad<TEntityKanban, TEntity, TLink>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
-            where TEntityKanban : EntityKanban<TEntity, TLink>, new()
-            where TEntity : Entity, IRemotable, IPersistent, new()
-            where TLink : IEntityLink<TEntity>, new()
-        {
-            var result = DoLoad(models, columns);
-            if (!result.Any())
-                foreach (var model in models)
-                {
-                    result = new Client<TEntity>().Load(
-                        new Filter<TEntity>(x => x.ID).InQuery(new Filter<TEntityKanban>(x => x.Kanban.ID).IsEqualTo(model.ID),
-                            x => x.Entity.ID));
-                    foreach (var r in result)
-                        _entitycache.Add(new Tuple<Guid, Entity>(model.ID, r));
-                }
-
-            return result;
-        }
+        return (grid as DynamicDataGrid<TEntity>)!;
+    }
 
-        private void DoCache<TEntity>(Guid kanbanid, TEntity entity) where TEntity : Entity
+    private IEnumerable<TEntity> DoLoad<TEntity>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
+        where TEntity : Entity, IRemotable, IPersistent, new()
+    {
+        var result = new List<TEntity>();
+        var load = new List<Guid>();
+        foreach (var model in models)
         {
-            if (!_entitycache.Any(x => Equals(x.Item1, kanbanid) && x.Item2 is TEntity && Equals(x.Item2.ID, entity.ID)))
-                _entitycache.Add(new Tuple<Guid, Entity>(kanbanid, entity));
+            var entity = _entitycache.FirstOrDefault(x => Equals(x.Item1, model.ID) && x.Item2 is TEntity) as TEntity;
+            if (entity is not null)
+                result.Add(entity);
+            else
+                load.Add(model.ID);
         }
 
-        private bool DoEdit<TEntity>(IEnumerable<TEntity> entities, Action<TEntity>? action = null)
-            where TEntity : Entity, IRemotable, IPersistent, new()
+        if (load.Any())
         {
-            if (entities == null || !entities.Any())
-                return false;
-
+            var entities = new Client<TEntity>()
+                .Query(new Filter<TEntity>(x => x.ID).InList(load.ToArray()), columns)
+                .Rows.Select(x => x.ToObject<TEntity>()).ToList();
             foreach (var entity in entities)
-                action?.Invoke(entity);
-
-            return GetGrid<TEntity>().EditItems(entities.ToArray());
+                _entitycache.Add(new Tuple<Guid, Entity>(entity.ID, entity));
+            result.AddRange(entities);
         }
 
-        private void DoLink<TEntityKanban, TEntity, TLink>(TaskModel model, Guid entityid)
-            where TEntityKanban : EntityKanban<TEntity, TLink>, new()
-            where TEntity : Entity, IRemotable, IPersistent, new()
-            where TLink : IEntityLink<TEntity>, new()
-        {
-            var linktask = Task.Run(() =>
-            {
-                var link = new TEntityKanban();
-                link.Kanban.ID = model.ID;
-                link.Entity.ID = entityid;
-                new Client<TEntityKanban>().Save(link, "");
-            });
-            var kanbantask = Task.Run(() =>
+        return result;
+    }
+
+    private IEnumerable<TEntity> DoLoad<TEntityKanban, TEntity, TLink>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
+        where TEntityKanban : EntityKanban<TEntity, TLink>, new()
+        where TEntity : Entity, IRemotable, IPersistent, new()
+        where TLink : IEntityLink<TEntity>, new()
+    {
+        var result = DoLoad(models, columns);
+        if (!result.Any())
+            foreach (var model in models)
             {
-                var kanban = LoadKanbans(
-                    new[] { model },
-                    Columns.Required<Kanban>().Add(x => x.ID, x => x.Locked)).FirstOrDefault();
-                if (kanban is not null)
-                {
-                    kanban.Locked = true;
-                    new Client<Kanban>().Save(kanban, "Locked because of linked " + typeof(TEntity).EntityName().Split('.').Last());
-                }
-            });
-            Task.WaitAll(linktask, kanbantask);
-        }
+                result = new Client<TEntity>().Load(
+                    new Filter<TEntity>(x => x.ID).InQuery(new Filter<TEntityKanban>(x => x.Kanban.ID).IsEqualTo(model.ID),
+                        x => x.Entity.ID));
+                foreach (var r in result)
+                    _entitycache.Add(new Tuple<Guid, Entity>(model.ID, r));
+            }
 
-        private static void DoDelete<TEntity>(IList<TEntity> entities, string auditnote)
-            where TEntity : Entity, IRemotable, IPersistent, new()
-        {
-            new Client<TEntity>().Delete(entities, auditnote);
-        }
+        return result;
+    }
 
-        public Kanban? CreateKanban(Action<Kanban> customise)
-        {
-            var result = DoCreate<Kanban>(
-                kanban =>
-                {
-                    kanban.Title = "New Task";
-                    kanban.Description = "";
-                    kanban.Status = KanbanStatus.Open;
-                    kanban.DueDate = DateTime.Today;
-                    kanban.Private = false;
-                    kanban.JobLink.ID = Master?.ID ?? Guid.Empty;
-                    kanban.JobLink.Synchronise(Master ?? new Job());
-                    kanban.EmployeeLink.ID = App.EmployeeID;
-                    kanban.ManagerLink.ID = App.EmployeeID;
-                    customise?.Invoke(kanban);
-                });
-            if (result != null)
-                DoCache(result.ID, result);
-            return result;
-        }
+    private void DoCache<TEntity>(Guid kanbanid, TEntity entity) where TEntity : Entity
+    {
+        if (!_entitycache.Any(x => Equals(x.Item1, kanbanid) && x.Item2 is TEntity && Equals(x.Item2.ID, entity.ID)))
+            _entitycache.Add(new Tuple<Guid, Entity>(kanbanid, entity));
+    }
 
-        public IEnumerable<Kanban> LoadKanbans(IEnumerable<TaskModel> models, Columns<Kanban> columns)
-        {
-            columns.Add(x => x.ID);
-            columns.Add(x => x.Number);
-            columns.Add(x => x.Title);
-            columns.Add(x => x.Notes);
-            columns.Add(x => x.Summary);
-            columns.Add(x => x.Completed);
-            columns.Add(x => x.DueDate);
-            columns.Add(x => x.ManagerLink.ID);
-            columns.Add(x => x.EmployeeLink.ID);
-            return DoLoad(models, columns);
-        }
+    private bool DoEdit<TEntity>(IEnumerable<TEntity> entities, Action<TEntity>? action = null)
+        where TEntity : Entity, IRemotable, IPersistent, new()
+    {
+        if (entities == null || !entities.Any())
+            return false;
+
+        foreach (var entity in entities)
+            action?.Invoke(entity);
 
-        public void OnValidateKanban(object sender, Kanban[] items, List<string> errors)
+        return GetGrid<TEntity>().EditItems(entities.ToArray());
+    }
+
+    private void DoLink<TEntityKanban, TEntity, TLink>(TaskModel model, Guid entityid)
+        where TEntityKanban : EntityKanban<TEntity, TLink>, new()
+        where TEntity : Entity, IRemotable, IPersistent, new()
+        where TLink : IEntityLink<TEntity>, new()
+    {
+        var linktask = Task.Run(() =>
+        {
+            var link = new TEntityKanban();
+            link.Kanban.ID = model.ID;
+            link.Entity.ID = entityid;
+            new Client<TEntityKanban>().Save(link, "");
+        });
+        var kanbantask = Task.Run(() =>
         {
-            if (Properties.RequireTaskTypes && items.Any(x => x.Type.ID == Guid.Empty))
+            var kanban = LoadKanbans(
+                new[] { model },
+                Columns.Required<Kanban>().Add(x => x.ID, x => x.Locked)).FirstOrDefault();
+            if (kanban is not null)
             {
-                errors.Add("[Task Type] may not be blank!");
+                kanban.Locked = true;
+                new Client<Kanban>().Save(kanban, "Locked because of linked " + typeof(TEntity).EntityName().Split('.').Last());
             }
-        }
+        });
+        Task.WaitAll(linktask, kanbantask);
+    }
 
-        public void CustomiseKanbanGrid(DynamicDataGrid<Kanban> grid)
-        {
-            grid.OnValidate += OnValidateKanban;
-        }
+    private static void DoDelete<TEntity>(IList<TEntity> entities, string auditnote)
+        where TEntity : Entity, IRemotable, IPersistent, new()
+    {
+        new Client<TEntity>().Delete(entities, auditnote);
+    }
 
-        public bool EditKanbans(IEnumerable<TaskModel> models, Action<Kanban>? customise = null)
-        {
-            var entities = LoadKanbans(models, GetGrid<Kanban>().LoadEditorColumns());
-            return DoEdit(entities, customise);
-        }
+    public Kanban? CreateKanban(Action<Kanban> customise)
+    {
+        var result = DoCreate<Kanban>(
+            kanban =>
+            {
+                kanban.Title = "New Task";
+                kanban.Description = "";
+                kanban.Status = KanbanStatus.Open;
+                kanban.DueDate = DateTime.Today;
+                kanban.Private = false;
+                kanban.JobLink.ID = Master?.ID ?? Guid.Empty;
+                kanban.JobLink.Synchronise(Master ?? new Job());
+                kanban.EmployeeLink.ID = App.EmployeeID;
+                kanban.ManagerLink.ID = App.EmployeeID;
+                customise?.Invoke(kanban);
+            });
+        if (result != null)
+            DoCache(result.ID, result);
+        return result;
+    }
+
+    public IEnumerable<Kanban> LoadKanbans(IEnumerable<TaskModel> models, Columns<Kanban> columns)
+    {
+        columns.Add(x => x.ID);
+        columns.Add(x => x.Number);
+        columns.Add(x => x.Title);
+        columns.Add(x => x.Notes);
+        columns.Add(x => x.Summary);
+        columns.Add(x => x.Completed);
+        columns.Add(x => x.DueDate);
+        columns.Add(x => x.ManagerLink.ID);
+        columns.Add(x => x.EmployeeLink.ID);
+        return DoLoad(models, columns);
+    }
 
-        public void DeleteKanbans(IEnumerable<TaskModel> models, string auditnote)
+    public void OnValidateKanban(object sender, Kanban[] items, List<string> errors)
+    {
+        if (Properties.RequireTaskTypes && items.Any(x => x.Type.ID == Guid.Empty))
         {
-            var kanbans = models.Select(x => new Kanban { ID = x.ID }).ToList();
-            DoDelete(kanbans, auditnote);
+            errors.Add("[Task Type] may not be blank!");
         }
+    }
 
-        public Requisition? CreateRequisition(TaskModel model, Action<Requisition>? customise)
-        {
-            var result = DoCreate<Requisition>(
-                requi =>
-                {
-                    requi.JobLink.ID = Master?.ID ?? Guid.Empty;
-                    requi.JobLink.Synchronise(Master ?? new Job());
-                    customise?.Invoke(requi);
-                });
-            if (result != null)
-            {
-                DoCache(model.ID, result);
-                DoLink<RequisitionKanban, Requisition, RequisitionLink>(model, result.ID);
-            }
+    public void CustomiseKanbanGrid(DynamicDataGrid<Kanban> grid)
+    {
+        grid.OnValidate += OnValidateKanban;
+    }
 
-            return result;
-        }
+    public bool EditKanbans(IEnumerable<TaskModel> models, Action<Kanban>? customise = null)
+    {
+        var entities = LoadKanbans(models, GetGrid<Kanban>().LoadEditorColumns());
+        return DoEdit(entities, customise);
+    }
 
+    public void DeleteKanbans(IEnumerable<TaskModel> models, string auditnote)
+    {
+        var kanbans = models.Select(x => new Kanban { ID = x.ID }).ToList();
+        DoDelete(kanbans, auditnote);
+    }
 
-        public bool EditRequisitions(IEnumerable<TaskModel> models, Action<Requisition>? customise = null)
+    public Requisition? CreateRequisition(TaskModel model, Action<Requisition>? customise)
+    {
+        var result = DoCreate<Requisition>(
+            requi =>
+            {
+                requi.JobLink.ID = Master?.ID ?? Guid.Empty;
+                requi.JobLink.Synchronise(Master ?? new Job());
+                customise?.Invoke(requi);
+            });
+        if (result != null)
         {
-            var requis = DoLoad<RequisitionKanban, Requisition, RequisitionLink>(models, GetGrid<Requisition>().LoadEditorColumns());
-            if (requis.Any())
-                return DoEdit(requis, customise);
-            return false;
+            DoCache(model.ID, result);
+            DoLink<RequisitionKanban, Requisition, RequisitionLink>(model, result.ID);
         }
 
-        public Setout? CreateSetout(TaskModel model, Action<Setout> customise)
-        {
-            var result = DoCreate<Setout>(
-                setout =>
-                {
-                    setout.JobLink.ID = Master?.ID ?? Guid.Empty;
-                    setout.JobLink.Synchronise(Master ?? new Job());
-                    customise?.Invoke(setout);
-                });
-            if (result != null)
-            {
-                DoCache(model.ID, result);
-                //DoLink<SetoutKanban, Setout, SetoutLink>(model, result.ID);
-            }
+        return result;
+    }
 
-            return result;
-        }
 
-        public bool EditSetouts(IEnumerable<TaskModel> models, Action<Setout>? customise = null)
+    public bool EditRequisitions(IEnumerable<TaskModel> models, Action<Requisition>? customise = null)
+    {
+        var requis = DoLoad<RequisitionKanban, Requisition, RequisitionLink>(models, GetGrid<Requisition>().LoadEditorColumns());
+        if (requis.Any())
+            return DoEdit(requis, customise);
+        return false;
+    }
+
+    public Setout? CreateSetout(TaskModel model, Action<Setout> customise)
+    {
+        var result = DoCreate<Setout>(
+            setout =>
+            {
+                setout.JobLink.ID = Master?.ID ?? Guid.Empty;
+                setout.JobLink.Synchronise(Master ?? new Job());
+                customise?.Invoke(setout);
+            });
+        if (result != null)
         {
-            var setouts = DoLoad<SetoutKanban, Setout, SetoutLink>(models, GetGrid<Setout>().LoadEditorColumns());
-            if (setouts.Any())
-                return DoEdit(setouts, customise);
-            return false;
+            DoCache(model.ID, result);
+            //DoLink<SetoutKanban, Setout, SetoutLink>(model, result.ID);
         }
 
-        public Delivery? CreateDelivery(TaskModel model, Action<Delivery> customise)
-        {
-            var result = DoCreate<Delivery>(
-                delivery =>
-                {
-                    delivery.Job.ID = Master?.ID ?? Guid.Empty;
-                    delivery.Job.Synchronise(Master ?? new Job());
-                    customise?.Invoke(delivery);
-                });
-            if (result != null)
-            {
-                DoCache(model.ID, result);
-                DoLink<DeliveryKanban, Delivery, DeliveryLink>(model, result.ID);
-            }
+        return result;
+    }
 
-            return result;
-        }
+    public bool EditSetouts(IEnumerable<TaskModel> models, Action<Setout>? customise = null)
+    {
+        var setouts = DoLoad<SetoutKanban, Setout, SetoutLink>(models, GetGrid<Setout>().LoadEditorColumns());
+        if (setouts.Any())
+            return DoEdit(setouts, customise);
+        return false;
+    }
 
-        public bool EditDeliveries(IEnumerable<TaskModel> models, Action<Delivery>? customise = null)
+    public Delivery? CreateDelivery(TaskModel model, Action<Delivery> customise)
+    {
+        var result = DoCreate<Delivery>(
+            delivery =>
+            {
+                delivery.Job.ID = Master?.ID ?? Guid.Empty;
+                delivery.Job.Synchronise(Master ?? new Job());
+                customise?.Invoke(delivery);
+            });
+        if (result != null)
         {
-            var deliveries = DoLoad<DeliveryKanban, Delivery, DeliveryLink>(models, GetGrid<Delivery>().LoadEditorColumns());
-            if (deliveries.Any())
-                return DoEdit(deliveries, customise);
-            return false;
+            DoCache(model.ID, result);
+            DoLink<DeliveryKanban, Delivery, DeliveryLink>(model, result.ID);
         }
 
+        return result;
+    }
 
-        public PurchaseOrder? CreateOrder(TaskModel model, Action<PurchaseOrder> customise)
-        {
-            var result = DoCreate<PurchaseOrder>(
-                order => { customise?.Invoke(order); });
-            if (result != null)
-            {
-                DoCache(model.ID, result);
-                DoLink<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(model, result.ID);
-            }
+    public bool EditDeliveries(IEnumerable<TaskModel> models, Action<Delivery>? customise = null)
+    {
+        var deliveries = DoLoad<DeliveryKanban, Delivery, DeliveryLink>(models, GetGrid<Delivery>().LoadEditorColumns());
+        if (deliveries.Any())
+            return DoEdit(deliveries, customise);
+        return false;
+    }
 
-            return result;
-        }
 
-        public bool EditPurchaseOrders(IEnumerable<TaskModel> models, Action<PurchaseOrder>? customise = null)
+    public PurchaseOrder? CreateOrder(TaskModel model, Action<PurchaseOrder> customise)
+    {
+        var result = DoCreate<PurchaseOrder>(
+            order => { customise?.Invoke(order); });
+        if (result != null)
         {
-            var orders = DoLoad<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(models, GetGrid<PurchaseOrder>().LoadEditorColumns());
-            if (orders.Any())
-                return DoEdit(orders, customise);
-            return false;
+            DoCache(model.ID, result);
+            DoLink<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(model, result.ID);
         }
 
-        #endregion
+        return result;
+    }
 
-        #region EntityReferences
+    public bool EditPurchaseOrders(IEnumerable<TaskModel> models, Action<PurchaseOrder>? customise = null)
+    {
+        var orders = DoLoad<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(models, GetGrid<PurchaseOrder>().LoadEditorColumns());
+        if (orders.Any())
+            return DoEdit(orders, customise);
+        return false;
+    }
 
-        private static void AddQuery<TEntityKanban, TEntity, TLink>(MultiQuery query, Guid[] taskids)
-            where TEntityKanban : EntityKanban<TEntity, TLink>, new()
-            where TEntity : Entity
-            where TLink : IEntityLink<TEntity>, new()
-        {
-            query.Add(
-                new Filter<TEntityKanban>(x => x.Kanban.ID).InList(taskids),
-                Columns.None<TEntityKanban>().Add(x => x.Entity.ID).Add(x => x.Kanban.ID)
-            );
-        }
+    #endregion
 
-        private static Guid[] ExtractIDs<TEntityKanban, TEntity, TLink>(MultiQuery query)
-            where TEntityKanban : EntityKanban<TEntity, TLink>, new()
-            where TEntity : Entity
-            where TLink : IEntityLink<TEntity>, new()
-        {
-            var lookup = query.Get<TEntityKanban>().ToLookup<TEntityKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
-            return query.Get<TEntityKanban>().ExtractValues<TEntityKanban, Guid>(x => x.Entity.ID).ToArray();
-        }
+    #region EntityReferences
 
-        public KanbanReferences[] GetReferences(IEnumerable<TaskModel> models)
-        {
-            var result = new List<KanbanReferences>();
+    private static void AddQuery<TEntityKanban, TEntity, TLink>(MultiQuery query, Guid[] taskids)
+        where TEntityKanban : EntityKanban<TEntity, TLink>, new()
+        where TEntity : Entity
+        where TLink : IEntityLink<TEntity>, new()
+    {
+        query.Add(
+            new Filter<TEntityKanban>(x => x.Kanban.ID).InList(taskids),
+            Columns.None<TEntityKanban>().Add(x => x.Entity.ID).Add(x => x.Kanban.ID)
+        );
+    }
 
-            var ids = models.Select(x => x.ID).ToArray();
+    private static Guid[] ExtractIDs<TEntityKanban, TEntity, TLink>(MultiQuery query)
+        where TEntityKanban : EntityKanban<TEntity, TLink>, new()
+        where TEntity : Entity
+        where TLink : IEntityLink<TEntity>, new()
+    {
+        var lookup = query.Get<TEntityKanban>().ToLookup<TEntityKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
+        return query.Get<TEntityKanban>().ExtractValues<TEntityKanban, Guid>(x => x.Entity.ID).ToArray();
+    }
 
-            var query = new MultiQuery();
+    public KanbanReferences[] GetReferences(IEnumerable<TaskModel> models)
+    {
+        var result = new List<KanbanReferences>();
 
-            AddQuery<RequisitionKanban, Requisition, RequisitionLink>(query, ids);
-            AddQuery<SetoutKanban, Setout, SetoutLink>(query, ids);
-            AddQuery<DeliveryKanban, Delivery, DeliveryLink>(query, ids);
-            AddQuery<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(query, ids);
+        var ids = models.Select(x => x.ID).ToArray();
 
-            query.Query();
+        var query = new MultiQuery();
 
-            var requis = query.Get<RequisitionKanban>().ToLookup<RequisitionKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
-            var setouts = query.Get<SetoutKanban>().ToLookup<SetoutKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
-            var deliveries = query.Get<DeliveryKanban>().ToLookup<DeliveryKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
-            var orders = query.Get<PurchaseOrderKanban>().ToLookup<PurchaseOrderKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
+        AddQuery<RequisitionKanban, Requisition, RequisitionLink>(query, ids);
+        AddQuery<SetoutKanban, Setout, SetoutLink>(query, ids);
+        AddQuery<DeliveryKanban, Delivery, DeliveryLink>(query, ids);
+        AddQuery<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(query, ids);
 
-            foreach (var id in ids)
-            {
-                var references = new KanbanReferences
-                {
-                    Kanban = id,
-                    Requisitions = requis.Contains(id) ? requis[id].ToArray() : Array.Empty<Guid>(),
-                    Setouts = setouts.Contains(id) ? setouts[id].ToArray() : Array.Empty<Guid>(),
-                    Deliveries = deliveries.Contains(id) ? deliveries[id].ToArray() : Array.Empty<Guid>(),
-                    Orders = orders.Contains(id) ? orders[id].ToArray() : Array.Empty<Guid>()
-                };
-                result.Add(references);
-            }
+        query.Query();
 
-            return result.ToArray();
-        }
+        var requis = query.Get<RequisitionKanban>().ToLookup<RequisitionKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
+        var setouts = query.Get<SetoutKanban>().ToLookup<SetoutKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
+        var deliveries = query.Get<DeliveryKanban>().ToLookup<DeliveryKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
+        var orders = query.Get<PurchaseOrderKanban>().ToLookup<PurchaseOrderKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
 
-        public bool EditReferences(IEnumerable<TaskModel> models)
+        foreach (var id in ids)
         {
-            var result = false;
-            var refs = GetReferences(models).First();
-            if (refs.ReferenceType() == typeof(Requisition))
-                result = EditRequisitions(
-                    models,
-                    requi =>
-                    {
-                        requi.Notes = Utility.ProcessNotes(requi.Notes, requi.Request);
-                        requi.Request = "";
-                    }
-                );
-            else if (refs.ReferenceType() == typeof(Setout))
-                result = EditSetouts(models);
-            else if (refs.ReferenceType() == typeof(Delivery))
-                result = EditDeliveries(models);
-            else if (refs.ReferenceType() == typeof(PurchaseOrder))
-                result = EditPurchaseOrders(models);
-            else
-                result = EditKanbans(models);
-            return result;
+            var references = new KanbanReferences
+            {
+                Kanban = id,
+                Requisitions = requis.Contains(id) ? requis[id].ToArray() : Array.Empty<Guid>(),
+                Setouts = setouts.Contains(id) ? setouts[id].ToArray() : Array.Empty<Guid>(),
+                Deliveries = deliveries.Contains(id) ? deliveries[id].ToArray() : Array.Empty<Guid>(),
+                Orders = orders.Contains(id) ? orders[id].ToArray() : Array.Empty<Guid>()
+            };
+            result.Add(references);
         }
 
-        #endregion
+        return result.ToArray();
+    }
+
+    public bool EditReferences(IEnumerable<TaskModel> models)
+    {
+        var result = false;
+        var refs = GetReferences(models).First();
+        if (refs.ReferenceType() == typeof(Requisition))
+            result = EditRequisitions(
+                models,
+                requi =>
+                {
+                    requi.Notes = Utility.ProcessNotes(requi.Notes, requi.Request);
+                    requi.Request = "";
+                }
+            );
+        else if (refs.ReferenceType() == typeof(Setout))
+            result = EditSetouts(models);
+        else if (refs.ReferenceType() == typeof(Delivery))
+            result = EditDeliveries(models);
+        else if (refs.ReferenceType() == typeof(PurchaseOrder))
+            result = EditPurchaseOrders(models);
+        else
+            result = EditKanbans(models);
+        return result;
     }
+
+    #endregion
 }

+ 1 - 1
prs.desktop/Panels/Users/UserGrid.cs

@@ -65,7 +65,7 @@ namespace PRSDesktop
             [EditorSequence(3)]
             public string To { get; set; } = "";
 
-            [IntegerEditor(ToolTip = "Enter link expiry time (mins)")]
+            [Comment("Link expiry time (mins)")]
             [EditorSequence(4)]
             public int ExpiryTime { get; set; } = 10;
         }

+ 2 - 1
prs.shared/Posters/MYOB/ReceiptMYOBPoster.cs

@@ -20,7 +20,8 @@ namespace PRS.Shared.Posters.MYOB;
 public class ReceiptMYOBPosterSettings : MYOBPosterSettings
 {
     [EditorSequence(1)]
-    [TextBoxEditor(ToolTip = "The MYOB account code for receipts")]
+    [Comment("The MYOB account code for receipts")]
+    [TextBoxEditor]
     public string Account { get; set; }
 
     public override string DefaultScript(Type TPostable)