Jelajahi Sumber

Adding posted image columns to all the new MYOB posters

Kenric Nugteren 1 tahun lalu
induk
melakukan
db37daa59d

+ 3 - 0
prs.desktop/Panels/Customers/CustomerGrid.cs

@@ -17,6 +17,9 @@ namespace PRSDesktop
             ;
 
             ActionColumns.Add(new DynamicMapColumn<Customer>(this, x => x.Delivery.Location));
+
+            PostUtils.AddPostColumn(this);
+
             //         HiddenColumns.Add(x => x.Delivery.Location.Timestamp);
             //HiddenColumns.Add(x => x.Delivery.Location.Latitude);
             //HiddenColumns.Add(x => x.Delivery.Location.Longitude);

+ 2 - 0
prs.desktop/Panels/Customers/CustomerReceipts.cs

@@ -60,6 +60,8 @@ public class CustomerReceipts : DynamicDataGrid<Receipt>, IPanel<Receipt>
         
         _settings = new UserConfiguration<ReceiptGridSettings>().Load();
 
+        PostUtils.AddPostColumn(this);
+
         FilterComponent.SetSettings(_settings.Filters, refresh: false);
         FilterComponent.OnFiltersSelected += FilterComponent_OnFilterSelected;
     }

+ 2 - 48
prs.desktop/Panels/Invoices/InvoiceGrid.cs

@@ -40,11 +40,6 @@ namespace PRSDesktop
     
     public class InvoiceGrid : DynamicDataGrid<Invoice>, IMasterDetailControl<Job,Invoice>
     {
-        private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
-        private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
-        private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
-        private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
-
         private InvoiceGridSettings _settings;
      
         public Job? Master { get; set; }
@@ -67,12 +62,8 @@ namespace PRSDesktop
             AddButton("Email", PRSDesktop.Resources.email.AsBitmapImage(), EmailInvoice2);
             HiddenColumns.Add(x => x.CustomerLink.ID);
             HiddenColumns.Add(x => x.JobLink.ID);
-            HiddenColumns.Add(x => x.PostedStatus);
-            HiddenColumns.Add(x => x.PostedNote);
-            ActionColumns.Add(new DynamicImageColumn(Posted_Image, Posted_Click)
-            {
-                ToolTip = Posted_ToolTip
-            });
+
+            PostUtils.AddPostColumn(this);
         }
         
         protected override void DoReconfigure(DynamicGridOptions options)
@@ -86,43 +77,6 @@ namespace PRSDesktop
             options.FilterRows = true;
         }
 
-        private bool Posted_Click(CoreRow? arg)
-        {
-            return false;
-        }
-
-        private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)
-        {
-            if (row is null)
-            {
-                return column.TextToolTip("Invoice Processed Status");
-            }
-            return column.TextToolTip(row.Get<Invoice, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => "Post failed: " + row.Get<Invoice, string>(x => x.PostedNote),
-                PostedStatus.RequiresRepost => "Repost required: " + row.Get<Invoice, string>(x => x.PostedNote),
-                PostedStatus.Posted => "Processed",
-                PostedStatus.NeverPosted or _ => "Not posted yet",
-            });
-        }
-
-        private BitmapImage? Posted_Image(CoreRow? arg)
-        {
-            if(arg is null) 
-                return post;
-            return arg.Get<Invoice, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => warning,
-                PostedStatus.Posted => tick,
-                PostedStatus.RequiresRepost => refresh,
-                PostedStatus.NeverPosted or _ => null,
-            };
-        }
-
-
-        
-        //public bool IncludePaidInvoices { get; set; }
-
         protected override void Reload(Filters<Invoice> criteria, Columns<Invoice> columns,
             ref SortOrder<Invoice>? sort,
             Action<CoreTable?, Exception?> action)

+ 0 - 2
prs.desktop/Panels/Invoices/InvoicePanel.xaml.cs

@@ -22,8 +22,6 @@ namespace PRSDesktop
     public partial class InvoicePanel : UserControl, IPanel<Invoice>, IMasterDetailControl<Job>
     {
         
-        
-        
         public InvoicePanel()
         {
             InitializeComponent();

+ 175 - 213
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrders.cs

@@ -12,251 +12,213 @@ using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.WPF;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+
+public class SupplierPurchaseOrdersSettings : IUserConfigurationSettings
 {
-    
-    public class SupplierPurchaseOrdersSettings : IUserConfigurationSettings
-    {
-        [Obsolete]
-        private CoreFilterDefinition? _currentFilter;
+    [Obsolete]
+    private CoreFilterDefinition? _currentFilter;
 
-        [Obsolete]
-        public CoreFilterDefinition? CurrentFilter
+    [Obsolete]
+    public CoreFilterDefinition? CurrentFilter
+    {
+        get => _currentFilter;
+        set
         {
-            get => _currentFilter;
-            set
+            if (value is not null)
             {
-                if (value is not null)
-                {
-                    Filters = new DynamicGridSelectedFilterSettings(new List<CoreFilterDefinition> { value }, false, null);
-                }
+                Filters = new DynamicGridSelectedFilterSettings(new List<CoreFilterDefinition> { value }, false, null);
             }
         }
-
-        public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
     }
+
+    public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
+}
+
+public class SupplierPurchaseOrders : DynamicDataGrid<PurchaseOrder>
+{
+    private readonly Button close;
     
-    public class SupplierPurchaseOrders : DynamicDataGrid<PurchaseOrder>
+    private SupplierPurchaseOrdersSettings _settings;
+
+    public SupplierPurchaseOrders()
     {
-        private readonly Button close;
-        private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
-        private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
-        private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
-        private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
-        
-        private SupplierPurchaseOrdersSettings _settings;
+        _settings = new UserConfiguration<SupplierPurchaseOrdersSettings>().Load();
+        FilterComponent.SetSettings(_settings.Filters, false);
+        FilterComponent.OnFiltersSelected += FilterComponent_OnFilterSelected;
 
-        public SupplierPurchaseOrders()
-        {
-            _settings = new UserConfiguration<SupplierPurchaseOrdersSettings>().Load();
-            FilterComponent.SetSettings(_settings.Filters, false);
-            FilterComponent.OnFiltersSelected += FilterComponent_OnFilterSelected;
+        OnEditorValueChanged += SupplierPurchaseOrders_OnEditorValueChanged;
+        OnCustomiseEditor += SupplierPurchaseOrders_OnCustomiseEditor;
+        
+        HiddenColumns.Add(x => x.ClosedDate);
+        HiddenColumns.Add(x => x.Balance);
 
-            OnEditorValueChanged += SupplierPurchaseOrders_OnEditorValueChanged;
-            OnCustomiseEditor += SupplierPurchaseOrders_OnCustomiseEditor;
-            
-            HiddenColumns.Add(x => x.ClosedDate);
-            HiddenColumns.Add(x => x.Balance);
-            HiddenColumns.Add(x => x.PostedStatus);
-            HiddenColumns.Add(x => x.PostedNote);
-            
-            ActionColumns.Add(new DynamicImageColumn(Posted_Image, null)
-            {
-                ToolTip = Posted_ToolTip
-            });
+        PostUtils.AddPostColumn(this);
 
-            close = AddButton("Close Order", null, CloseOrder);
-            close.IsEnabled = false;
-        }
-        protected override void DoReconfigure(DynamicGridOptions options)
-        {
-            base.DoReconfigure(options);
-            options.RecordCount = true;
-            options.FilterRows = true;
-            options.SelectColumns = true;
-        }
+        close = AddButton("Close Order", null, CloseOrder);
+        close.IsEnabled = false;
+    }
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+        options.RecordCount = true;
+        options.FilterRows = true;
+        options.SelectColumns = true;
+    }
 
-        private void SupplierPurchaseOrders_OnCustomiseEditor(IDynamicEditorForm sender, PurchaseOrder[]? items, DynamicGridColumn column, BaseEditor editor)
+    private void SupplierPurchaseOrders_OnCustomiseEditor(IDynamicEditorForm sender, PurchaseOrder[]? items, DynamicGridColumn column, BaseEditor editor)
+    {
+        if(column.ColumnName == nameof(PurchaseOrder.ClosedDate))
         {
-            if(column.ColumnName == nameof(PurchaseOrder.ClosedDate))
-            {
-                editor.Editable = Security.CanEdit<PurchaseOrder>() && Security.IsAllowed<CanOpenAndClosePurchaseOrders>()
-                    ? Editable.Enabled : Editable.Disabled;
-            }
+            editor.Editable = Security.CanEdit<PurchaseOrder>() && Security.IsAllowed<CanOpenAndClosePurchaseOrders>()
+                ? Editable.Enabled : Editable.Disabled;
         }
+    }
 
-        protected override void DoReconfigureEditors(DynamicEditorGrid grid, PurchaseOrder[] items)
-        {
-            base.DoReconfigureEditors(grid, items);
-
-            var closedEditor = grid.FindEditor(nameof(PurchaseOrder.ClosedDate));
-            if(closedEditor is not null)
-            {
-                closedEditor.IsEnabled = Security.CanEdit<PurchaseOrder>() && Security.IsAllowed<CanOpenAndClosePurchaseOrders>();
-            }
-        }
+    protected override void DoReconfigureEditors(DynamicEditorGrid grid, PurchaseOrder[] items)
+    {
+        base.DoReconfigureEditors(grid, items);
 
-        protected override void BeforeLoad(IDynamicEditorForm form, PurchaseOrder[] items)
+        var closedEditor = grid.FindEditor(nameof(PurchaseOrder.ClosedDate));
+        if(closedEditor is not null)
         {
-            base.BeforeLoad(form, items);
-
-            foreach (var page in form.Pages ?? Enumerable.Empty<IDynamicEditorPage>())
-            {
-                page.ReadOnly = items.Any(x => !x.ClosedDate.IsEmpty());
-            }
+            closedEditor.IsEnabled = Security.CanEdit<PurchaseOrder>() && Security.IsAllowed<CanOpenAndClosePurchaseOrders>();
         }
+    }
 
-        private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)
-        {
-            if (row is null)
-            {
-                return column.TextToolTip("Purchase Order Processed Status");
-            }
-            return column.TextToolTip(row.Get<PurchaseOrder, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => "Post failed: " + row.Get<PurchaseOrder, string>(x => x.PostedNote),
-                PostedStatus.RequiresRepost => "Repost required: " + row.Get<PurchaseOrder, string>(x => x.PostedNote),
-                PostedStatus.Posted => "Processed",
-                PostedStatus.NeverPosted or _ => "Not posted yet",
-            });
-        }
+    protected override void BeforeLoad(IDynamicEditorForm form, PurchaseOrder[] items)
+    {
+        base.BeforeLoad(form, items);
 
-        private BitmapImage? Posted_Image(CoreRow? row)
+        foreach (var page in form.Pages ?? Enumerable.Empty<IDynamicEditorPage>())
         {
-            if (row is null) 
-                return post;
-            return row.Get<PurchaseOrder, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => warning,
-                PostedStatus.Posted => tick,
-                PostedStatus.RequiresRepost => refresh,
-                PostedStatus.NeverPosted or _ => null,
-            };
+            page.ReadOnly = items.Any(x => !x.ClosedDate.IsEmpty());
         }
+    }
 
-        private Dictionary<string, object?> SupplierPurchaseOrders_OnEditorValueChanged(IDynamicEditorForm sender, string name, object value)
+    private Dictionary<string, object?> SupplierPurchaseOrders_OnEditorValueChanged(IDynamicEditorForm sender, string name, object value)
+    {
+        var result = new Dictionary<string, object?>();
+        if(name == nameof(PurchaseOrder.ClosedDate) && value is DateTime closed)
         {
-            var result = new Dictionary<string, object?>();
-            if(name == nameof(PurchaseOrder.ClosedDate) && value is DateTime closed)
+            foreach (var page in sender.Pages ?? Enumerable.Empty<IDynamicEditorPage>())
             {
-                foreach (var page in sender.Pages ?? Enumerable.Empty<IDynamicEditorPage>())
-                {
-                    page.ReadOnly = !closed.IsEmpty();
-                }
+                page.ReadOnly = !closed.IsEmpty();
             }
-            //try
-            //{               
-            //    var form = sender as DynamicEditorForm;
-            //    var itemspage = form?.Pages.FirstOrDefault(x => x is DynamicOneToManyGrid<PurchaseOrder, PurchaseOrderItem>) as DynamicOneToManyGrid<PurchaseOrder, PurchaseOrderItem>;
-            //    if ((itemspage == null) || (itemspage.Items.Count == 0))
-            //        return result;
-
-            //    if (name.Equals("DueDate") && (MessageBox.Show("Update Due Date on existing items?", "Alert", MessageBoxButton.YesNo) == MessageBoxResult.Yes))
-            //    {
-            //        foreach (var item in itemspage.Items)
-            //            item.DueDate = Convert.ToDateTime(value);
-            //        itemspage.Refresh(false, true);
-            //    }
-            //    else if (name.Equals("SupplierLink.ID") &&
-            //             (MessageBox.Show("Update Supplier Pricing to existihg items?", "Alert", MessageBoxButton.YesNo) == MessageBoxResult.Yes))
-            //    {
-            //        PurchaseOrder.UpdateCosts(itemspage.Items, (Guid)value, null);
-            //        // var productids = itemspage.Items.Where(x => x.Product.ID != Guid.Empty).Select(x => x.Product.ID).ToArray();
-            //        // MultiQuery query = new MultiQuery();
-            //        // query.Add(
-            //        //     new Filter<SupplierProduct>(x=>x.SupplierLink.ID).IsEqualTo(value).And(x=>x.ProductLink.ID).InList(productids),
-            //        //     new Columns<SupplierProduct>(x=>x.ProductLink.ID).Add(x=>x.Job.ID).Add(x=>x.CostPrice)
-            //        // );
-            //        // query.Add(
-            //        //     new Filter<Product>(x=>x.ID).InList(productids),
-            //        //     new Columns<Product>(x=>x.ID).Add(x=>x.NettCost)
-            //        // );
-            //        // query.Query();
-            //        //
-            //        // foreach (var item in itemspage.Items)
-            //        // {
-            //        //     CoreRow? row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
-            //        //         (r.Get<SupplierProduct, Guid>(c => c.ProductLink.ID) == item.Product.ID)
-            //        //         && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == item.Job.ID)
-            //        //     );
-            //        //     if (row == null)
-            //        //         row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
-            //        //             (r.Get<SupplierProduct, Guid>(c => c.ProductLink.ID) == item.Product.ID)
-            //        //             && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == Guid.Empty)
-            //        //         );
-            //        //     if (row != null)
-            //        //         item.Cost = row.Get<SupplierProduct, double>(c => c.CostPrice);
-            //        //     else
-            //        //     {
-            //        //         row = query.Get<Product>()?.Rows.FirstOrDefault(r =>
-            //        //             (r.Get<Product, Guid>(c => c.ID) == item.Product.ID)
-            //        //         );
-            //        //         if (row != null)
-            //        //             item.Cost = row.Get<Product, double>(c => c.NettCost);
-            //        //     }
-            //        //
-            //        // }
-
-            //        itemspage.Refresh(false, true);
-            //    }
-            //}
-            //catch
-            //{ }
-            return result;
-            
         }
-
-        // private List<SupplierProduct> GetSupplierProducts(object value)
-        // {
-        //     List<SupplierProduct> supplierProducts = new List<SupplierProduct>();
-        //     CoreTable supplierProductstable = new Client<SupplierProduct>().Query(new Filter<SupplierProduct>(x => x.SupplierLink.ID).IsEqualTo(Guid.Parse(value.ToString())),
-        //     new Columns<SupplierProduct>(
-        //     x => x.ID,
-        //     x => x.SupplierLink.ID,
-        //     x => x.ProductLink.ID,
-        //     x => x.Job.ID,
-        //     x => x.TradePrice));
-        //     if (supplierProductstable.Rows.Any())
-        //     {
-        //         foreach (CoreRow row in supplierProductstable.Rows)
-        //         {
-        //             SupplierProduct supplierProduct = row.ToObject<SupplierProduct>();
-        //             supplierProducts.Add(supplierProduct);
-        //         }
-        //     }
-        //     return supplierProducts;
-        // }
+        //try
+        //{               
+        //    var form = sender as DynamicEditorForm;
+        //    var itemspage = form?.Pages.FirstOrDefault(x => x is DynamicOneToManyGrid<PurchaseOrder, PurchaseOrderItem>) as DynamicOneToManyGrid<PurchaseOrder, PurchaseOrderItem>;
+        //    if ((itemspage == null) || (itemspage.Items.Count == 0))
+        //        return result;
+
+        //    if (name.Equals("DueDate") && (MessageBox.Show("Update Due Date on existing items?", "Alert", MessageBoxButton.YesNo) == MessageBoxResult.Yes))
+        //    {
+        //        foreach (var item in itemspage.Items)
+        //            item.DueDate = Convert.ToDateTime(value);
+        //        itemspage.Refresh(false, true);
+        //    }
+        //    else if (name.Equals("SupplierLink.ID") &&
+        //             (MessageBox.Show("Update Supplier Pricing to existihg items?", "Alert", MessageBoxButton.YesNo) == MessageBoxResult.Yes))
+        //    {
+        //        PurchaseOrder.UpdateCosts(itemspage.Items, (Guid)value, null);
+        //        // var productids = itemspage.Items.Where(x => x.Product.ID != Guid.Empty).Select(x => x.Product.ID).ToArray();
+        //        // MultiQuery query = new MultiQuery();
+        //        // query.Add(
+        //        //     new Filter<SupplierProduct>(x=>x.SupplierLink.ID).IsEqualTo(value).And(x=>x.ProductLink.ID).InList(productids),
+        //        //     new Columns<SupplierProduct>(x=>x.ProductLink.ID).Add(x=>x.Job.ID).Add(x=>x.CostPrice)
+        //        // );
+        //        // query.Add(
+        //        //     new Filter<Product>(x=>x.ID).InList(productids),
+        //        //     new Columns<Product>(x=>x.ID).Add(x=>x.NettCost)
+        //        // );
+        //        // query.Query();
+        //        //
+        //        // foreach (var item in itemspage.Items)
+        //        // {
+        //        //     CoreRow? row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
+        //        //         (r.Get<SupplierProduct, Guid>(c => c.ProductLink.ID) == item.Product.ID)
+        //        //         && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == item.Job.ID)
+        //        //     );
+        //        //     if (row == null)
+        //        //         row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
+        //        //             (r.Get<SupplierProduct, Guid>(c => c.ProductLink.ID) == item.Product.ID)
+        //        //             && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == Guid.Empty)
+        //        //         );
+        //        //     if (row != null)
+        //        //         item.Cost = row.Get<SupplierProduct, double>(c => c.CostPrice);
+        //        //     else
+        //        //     {
+        //        //         row = query.Get<Product>()?.Rows.FirstOrDefault(r =>
+        //        //             (r.Get<Product, Guid>(c => c.ID) == item.Product.ID)
+        //        //         );
+        //        //         if (row != null)
+        //        //             item.Cost = row.Get<Product, double>(c => c.NettCost);
+        //        //     }
+        //        //
+        //        // }
+
+        //        itemspage.Refresh(false, true);
+        //    }
+        //}
+        //catch
+        //{ }
+        return result;
         
-        protected override void SelectItems(CoreRow[]? rows)
-        {
-            close.IsEnabled = rows != null && rows.Any(r =>
-                r.Get<PurchaseOrder, DateTime>(c => c.ClosedDate).IsEmpty() && r.Get<PurchaseOrder, double>(x => x.Balance).Equals(0.0F));
-            base.SelectItems(rows);
-        }
+    }
 
-        private bool CloseOrder(Button arg1, CoreRow[] arg2)
-        {
-            var orders = new List<PurchaseOrder>();
-            foreach (var row in arg2)
-            {
-                var order = row.ToObject<PurchaseOrder>();
-                order.ClosedDate = DateTime.Now;
-                orders.Add(order);
-            }
+    // private List<SupplierProduct> GetSupplierProducts(object value)
+    // {
+    //     List<SupplierProduct> supplierProducts = new List<SupplierProduct>();
+    //     CoreTable supplierProductstable = new Client<SupplierProduct>().Query(new Filter<SupplierProduct>(x => x.SupplierLink.ID).IsEqualTo(Guid.Parse(value.ToString())),
+    //     new Columns<SupplierProduct>(
+    //     x => x.ID,
+    //     x => x.SupplierLink.ID,
+    //     x => x.ProductLink.ID,
+    //     x => x.Job.ID,
+    //     x => x.TradePrice));
+    //     if (supplierProductstable.Rows.Any())
+    //     {
+    //         foreach (CoreRow row in supplierProductstable.Rows)
+    //         {
+    //             SupplierProduct supplierProduct = row.ToObject<SupplierProduct>();
+    //             supplierProducts.Add(supplierProduct);
+    //         }
+    //     }
+    //     return supplierProducts;
+    // }
+    
+    protected override void SelectItems(CoreRow[]? rows)
+    {
+        close.IsEnabled = rows != null && rows.Any(r =>
+            r.Get<PurchaseOrder, DateTime>(c => c.ClosedDate).IsEmpty() && r.Get<PurchaseOrder, double>(x => x.Balance).Equals(0.0F));
+        base.SelectItems(rows);
+    }
 
-            new Client<PurchaseOrder>().Save(orders, "Marking Order as Closed");
-            return true;
-        }
-        
-        public PurchaseOrder[] LoadOrders(CoreRow[] rows)
-        {
-            return LoadItems(rows);
-        }
-        
-        private void FilterComponent_OnFilterSelected(DynamicGridSelectedFilterSettings settings)
+    private bool CloseOrder(Button arg1, CoreRow[] arg2)
+    {
+        var orders = new List<PurchaseOrder>();
+        foreach (var row in arg2)
         {
-            _settings.Filters = settings;
-            new UserConfiguration<SupplierPurchaseOrdersSettings>().Save(_settings);
+            var order = row.ToObject<PurchaseOrder>();
+            order.ClosedDate = DateTime.Now;
+            orders.Add(order);
         }
+
+        new Client<PurchaseOrder>().Save(orders, "Marking Order as Closed");
+        return true;
+    }
+    
+    public PurchaseOrder[] LoadOrders(CoreRow[] rows)
+    {
+        return LoadItems(rows);
+    }
+    
+    private void FilterComponent_OnFilterSelected(DynamicGridSelectedFilterSettings settings)
+    {
+        _settings.Filters = settings;
+        new UserConfiguration<SupplierPurchaseOrdersSettings>().Save(_settings);
     }
 }

+ 2 - 39
prs.desktop/Panels/Suppliers/Bills/SupplierBills.cs

@@ -16,9 +16,6 @@ namespace PRSDesktop
     public class SupplierBills : DynamicDataGrid<Bill>
     {
         private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
-        private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
-        private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
-        private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
         private static readonly BitmapImage? data = PRSDesktop.Resources.pencil.AsBitmapImage();
         private static readonly BitmapImage? check = PRSDesktop.Resources.checklist.AsBitmapImage();
 
@@ -29,9 +26,6 @@ namespace PRSDesktop
             
             _settings = new GlobalConfiguration<SupplierBillPanelProperties>().Load();
 
-            HiddenColumns.Add(x => x.PostedStatus);
-            HiddenColumns.Add(x => x.PostedStatus);
-            HiddenColumns.Add(x => x.PostedNote);
             HiddenColumns.Add(x => x.Approved);
             HiddenColumns.Add(x => x.DataEntered);
             HiddenColumns.Add(x => x.Checked);
@@ -39,11 +33,8 @@ namespace PRSDesktop
             ActionColumns.Add(new DynamicImageColumn(DataEntered_Image, null) { ToolTip = DataEntered_ToolTip });
             ActionColumns.Add(new DynamicImageColumn(Checked_Image, null) { ToolTip = Checked_ToolTip });
             ActionColumns.Add(new DynamicImageColumn(Approved_Image, null) { ToolTip = Approved_ToolTip });
-            
-            ActionColumns.Add(new DynamicImageColumn(Posted_Image, null)
-            {
-                ToolTip = Posted_ToolTip
-            });
+
+            PostUtils.AddPostColumn(this);
         }
 
         private FrameworkElement? Approved_ToolTip(DynamicActionColumn column, CoreRow? row)
@@ -92,21 +83,6 @@ namespace PRSDesktop
                 : check;
         }
 
-        private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)
-        {
-            if (row is null)
-            {
-                return column.TextToolTip("Bill Processed Status");
-            }
-            return column.TextToolTip(row.Get<Bill, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => "Post failed: " + row.Get<Bill, string>(x => x.PostedNote),
-                PostedStatus.RequiresRepost => "Repost required: " + row.Get<Bill, string>(x => x.PostedNote),
-                PostedStatus.Posted => "Processed",
-                PostedStatus.NeverPosted or _ => "Not posted yet",
-            });
-        }
-        
         private BitmapImage? Approved_Image(CoreRow? row)
         {
             return (row != null) && row.Get<Bill, DateTime>(x => x.Approved).IsEmpty()
@@ -114,19 +90,6 @@ namespace PRSDesktop
                 : tick;
         }
 
-        private BitmapImage? Posted_Image(CoreRow? row)
-        {
-            if (row is null) 
-                return post;
-            return row.Get<Bill, PostedStatus>(x => x.PostedStatus) switch
-            {
-                PostedStatus.PostFailed => warning,
-                PostedStatus.Posted => tick,
-                PostedStatus.RequiresRepost => refresh,
-                PostedStatus.NeverPosted or _ => null,
-            };
-        }
-
         public Bill[] LoadBills(CoreRow[] rows)
         {
             return LoadItems(rows);

+ 16 - 10
prs.desktop/Panels/Suppliers/SupplierGrid.cs

@@ -2,18 +2,24 @@
 using InABox.Core;
 using InABox.DynamicGrid;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class SupplierGrid : DynamicDataGrid<Supplier>
 {
-    public class SupplierGrid : DynamicDataGrid<Supplier>
+    protected override void Init()
     {
-        protected override void DoReconfigure(DynamicGridOptions options)
-        {
-            base.DoReconfigure(options);
-            options.FilterRows = true;
-            options.SelectColumns = true;
-            options.MultiSelect = true;
-        }
+        base.Init();
 
-        public bool IsReady { get; set; }
+        PostUtils.AddPostColumn(this);
     }
+
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+        options.FilterRows = true;
+        options.SelectColumns = true;
+        options.MultiSelect = true;
+    }
+
+    public bool IsReady { get; set; }
 }

+ 159 - 111
prs.desktop/Utils/PostUtils.cs

@@ -4,6 +4,7 @@ using InABox.Core;
 using InABox.Core.Postable;
 using InABox.DynamicGrid;
 using InABox.Wpf;
+using InABox.WPF;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
@@ -12,153 +13,200 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
+using System.Windows.Media.Imaging;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public static class PostUtils
 {
-    public static class PostUtils
-    {
-        private static readonly Inflector.Inflector inflector = new(new CultureInfo("en"));
+    private static readonly Inflector.Inflector inflector = new(new CultureInfo("en"));
 
-        public static void PostEntities<T>(IDataModel<T> model, Action refresh, Action? configurePost = null)
-            where T : Entity, IPostable, IRemotable, IPersistent, new()
+    public static void PostEntities<T>(IDataModel<T> model, Action refresh, Action? configurePost = null)
+        where T : Entity, IPostable, IRemotable, IPersistent, new()
+    {
+        try
         {
-            try
-            {
-                var result = PosterUtils.Process(model);
-                if(result is null)
-                {
-                    MessageWindow.ShowMessage($"Processing failed", "Processing failed");
-                    refresh();
-                }
-                else
-                {
-                    var failedMessages = new List<string>();
-                    var successCount = 0;
-                    foreach(var entity in result.PostedEntities)
-                    {
-                        if(entity.PostedStatus == PostedStatus.PostFailed)
-                        {
-                            failedMessages.Add(entity.PostedNote);
-                        }
-                        else
-                        {
-                            successCount++;
-                        }
-                    }
-                    if(successCount == 0)
-                    {
-                        MessageWindow.ShowMessage($"Processing failed:\n - {string.Join("\n - ", failedMessages)}", "Processing failed.");
-                    }
-                    else if(failedMessages.Count == 0)
-                    {
-                        MessageWindow.ShowMessage($"Processing successful; {successCount} items processed", "Processing successful.");
-                    }
-                    else
-                    {
-                        MessageWindow.ShowMessage($"{successCount} items succeeded, but {failedMessages.Count} failed:\n - {string.Join("\n - ", failedMessages)}", "Partial success");
-                    }
-                    refresh();
-                }
-            }
-            catch (EmptyPostException)
-            {
-                MessageWindow.ShowMessage($"Please select at least one {typeof(T).Name}.", "Select items");
-            }
-            catch (PostFailedMessageException e)
-            {
-                MessageWindow.ShowMessage(e.Message, "Post failed");
-            }
-            catch (RepostedException)
-            {
-                MessageWindow.ShowMessage("At least one of the items you selected has already been processed. Processing cancelled.", "Already processed");
-            }
-            catch (PostCancelledException)
+            var result = PosterUtils.Process(model);
+            if(result is null)
             {
-                MessageWindow.ShowMessage("Processing cancelled.", "Cancelled");
+                MessageWindow.ShowMessage($"Processing failed", "Processing failed");
+                refresh();
             }
-            catch (MissingSettingsException)
+            else
             {
-                if (configurePost is not null && Security.CanConfigurePost<T>())
+                var failedMessages = new List<string>();
+                var successCount = 0;
+                foreach(var entity in result.PostedEntities)
                 {
-                    if (MessageWindow.ShowYesNo($"Processing has not been configured for {inflector.Pluralize(typeof(T).Name)}. Would you like to configure this now?",
-                        "Configure Processing?"))
+                    if(entity.PostedStatus == PostedStatus.PostFailed)
                     {
-                        configurePost();
+                        failedMessages.Add(entity.PostedNote);
                     }
                     else
                     {
-                        MessageWindow.ShowMessage("Processing cancelled.", "Cancelled");
+                        successCount++;
                     }
                 }
+                if(successCount == 0)
+                {
+                    MessageWindow.ShowMessage($"Processing failed:\n - {string.Join("\n - ", failedMessages)}", "Processing failed.");
+                }
+                else if(failedMessages.Count == 0)
+                {
+                    MessageWindow.ShowMessage($"Processing successful; {successCount} items processed", "Processing successful.");
+                }
                 else
                 {
-                    MessageWindow.ShowMessage($"Processing has not been configured for {inflector.Pluralize(typeof(T).Name)}!", "Unconfigured");
+                    MessageWindow.ShowMessage($"{successCount} items succeeded, but {failedMessages.Count} failed:\n - {string.Join("\n - ", failedMessages)}", "Partial success");
                 }
-            }
-            catch (Exception e)
-            {
-                MessageWindow.ShowError("Processing failed.", e);
                 refresh();
             }
         }
-
-        public static void CreateToolbarButtons<T>(IPanelHost host, Func<IDataModel<T>> model, Action refresh, Action? configurePost = null)
-            where T : Entity, IPostable, IRemotable, IPersistent, new()
+        catch (EmptyPostException)
         {
-            var postSettings = PosterUtils.LoadPostableSettings<T>();
-            if (Security.CanPost<T>() && !postSettings.PosterType.IsNullOrWhiteSpace())
+            MessageWindow.ShowMessage($"Please select at least one {typeof(T).Name}.", "Select items");
+        }
+        catch (PostFailedMessageException e)
+        {
+            MessageWindow.ShowMessage(e.Message, "Post failed");
+        }
+        catch (RepostedException)
+        {
+            MessageWindow.ShowMessage("At least one of the items you selected has already been processed. Processing cancelled.", "Already processed");
+        }
+        catch (PostCancelledException)
+        {
+            MessageWindow.ShowMessage("Processing cancelled.", "Cancelled");
+        }
+        catch (MissingSettingsException)
+        {
+            if (configurePost is not null && Security.CanConfigurePost<T>())
             {
-                Bitmap? image = null;
-                if (postSettings.Thumbnail.ID != Guid.Empty)
+                if (MessageWindow.ShowYesNo($"Processing has not been configured for {inflector.Pluralize(typeof(T).Name)}. Would you like to configure this now?",
+                    "Configure Processing?"))
                 {
-                    var icon = new Client<Document>()
-                        .Load(new Filter<Document>(x => x.ID).IsEqualTo(postSettings.Thumbnail.ID)).FirstOrDefault();
-                    if (icon?.Data?.Any() == true)
-                        image = new ImageConverter().ConvertFrom(icon.Data) as Bitmap;
+                    configurePost();
                 }
-                host.CreatePanelAction(new PanelAction
+                else
                 {
-                    Caption = postSettings.ButtonName.NotWhiteSpaceOr($"Process {inflector.Pluralize(typeof(T).Name)}"),
-                    Image = image ?? PRSDesktop.Resources.edit,
-                    OnExecute = action =>
-                    {
-                        PostEntities(
-                            model(),
-                            refresh,
-                            configurePost);
-                    }
-                });
+                    MessageWindow.ShowMessage("Processing cancelled.", "Cancelled");
+                }
             }
-
-            if (configurePost is not null && Security.CanConfigurePost<T>())
+            else
             {
-                host.CreateSetupAction(new PanelAction
-                {
-                    Caption = $"Configure {CoreUtils.Neatify(typeof(T).Name)} Processing",
-                    OnExecute = action =>
-                    {
-                        configurePost();
-                    }
-                });
+                MessageWindow.ShowMessage($"Processing has not been configured for {inflector.Pluralize(typeof(T).Name)}!", "Unconfigured");
             }
         }
-
-        public static void ConfigurePost<T>()
-            where T : Entity, IPostable, IRemotable, IPersistent, new()
+        catch (Exception e)
         {
-            var postSettings = PosterUtils.LoadPostableSettings<T>();
+            MessageWindow.ShowError("Processing failed.", e);
+            refresh();
+        }
+    }
 
-            var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>), typeof(PostableSettings)) as DynamicGrid<PostableSettings>)!;
-            if (grid.EditItems(new PostableSettings[] { postSettings }))
+    public static void CreateToolbarButtons<T>(IPanelHost host, Func<IDataModel<T>> model, Action refresh, Action? configurePost = null)
+        where T : Entity, IPostable, IRemotable, IPersistent, new()
+    {
+        var postSettings = PosterUtils.LoadPostableSettings<T>();
+        if (Security.CanPost<T>() && !postSettings.PosterType.IsNullOrWhiteSpace())
+        {
+            Bitmap? image = null;
+            if (postSettings.Thumbnail.ID != Guid.Empty)
             {
-                PosterUtils.SavePostableSettings<T>(postSettings);
+                var icon = new Client<Document>()
+                    .Load(new Filter<Document>(x => x.ID).IsEqualTo(postSettings.Thumbnail.ID)).FirstOrDefault();
+                if (icon?.Data?.Any() == true)
+                    image = new ImageConverter().ConvertFrom(icon.Data) as Bitmap;
             }
+            host.CreatePanelAction(new PanelAction
+            {
+                Caption = postSettings.ButtonName.NotWhiteSpaceOr($"Process {inflector.Pluralize(typeof(T).Name)}"),
+                Image = image ?? PRSDesktop.Resources.edit,
+                OnExecute = action =>
+                {
+                    PostEntities(
+                        model(),
+                        refresh,
+                        configurePost);
+                }
+            });
+        }
+
+        if (configurePost is not null && Security.CanConfigurePost<T>())
+        {
+            host.CreateSetupAction(new PanelAction
+            {
+                Caption = $"Configure {CoreUtils.Neatify(typeof(T).Name)} Processing",
+                OnExecute = action =>
+                {
+                    configurePost();
+                }
+            });
         }
+    }
+
+    public static void ConfigurePost<T>()
+        where T : Entity, IPostable, IRemotable, IPersistent, new()
+    {
+        var postSettings = PosterUtils.LoadPostableSettings<T>();
 
-        public static void CreateToolbarButtons<T>(IPanelHost host, Func<IDataModel<T>> model, Action refresh, bool allowConfig)
-            where T : Entity, IPostable, IRemotable, IPersistent, new()
+        var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>), typeof(PostableSettings)) as DynamicGrid<PostableSettings>)!;
+        if (grid.EditItems(new PostableSettings[] { postSettings }))
         {
-            CreateToolbarButtons(host, model, refresh, allowConfig ? ConfigurePost<T> : null);
+            PosterUtils.SavePostableSettings<T>(postSettings);
         }
     }
+
+    public static void CreateToolbarButtons<T>(IPanelHost host, Func<IDataModel<T>> model, Action refresh, bool allowConfig)
+        where T : Entity, IPostable, IRemotable, IPersistent, new()
+    {
+        CreateToolbarButtons(host, model, refresh, allowConfig ? ConfigurePost<T> : null);
+    }
+
+    #region PostColumn
+
+    private static readonly BitmapImage? post = PRSDesktop.Resources.post.AsBitmapImage();
+    private static readonly BitmapImage? tick = PRSDesktop.Resources.tick.AsBitmapImage();
+    private static readonly BitmapImage? warning = PRSDesktop.Resources.warning.AsBitmapImage();
+    private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();
+
+    public static void AddPostColumn<T>(DynamicGrid<T> grid)
+        where T : Entity, IPostable, IRemotable, IPersistent, new()
+    {
+        grid.HiddenColumns.Add(x => x.PostedStatus);
+        grid.HiddenColumns.Add(x => x.PostedNote);
+        
+        grid.ActionColumns.Add(new DynamicImageColumn(
+            row =>
+            {
+                if (row is null) 
+                    return post;
+                return row.Get<T, PostedStatus>(x => x.PostedStatus) switch
+                {
+                    PostedStatus.PostFailed => warning,
+                    PostedStatus.Posted => tick,
+                    PostedStatus.RequiresRepost => refresh,
+                    PostedStatus.NeverPosted or _ => null,
+                };
+            },
+            null)
+        {
+            ToolTip = (column, row) =>
+            {
+                if (row is null)
+                {
+                    return column.TextToolTip($"{CoreUtils.Neatify(typeof(T).Name)} Processed Status");
+                }
+                return column.TextToolTip(row.Get<T, PostedStatus>(x => x.PostedStatus) switch
+                {
+                    PostedStatus.PostFailed => "Post failed: " + row.Get<T, string>(x => x.PostedNote),
+                    PostedStatus.RequiresRepost => "Repost required: " + row.Get<T, string>(x => x.PostedNote),
+                    PostedStatus.Posted => "Processed",
+                    PostedStatus.NeverPosted or _ => "Not posted yet",
+                });
+            }
+        });
+    }
+
+    #endregion
 }

+ 25 - 0
prs.shared/Posters/MYOB/MYOBGlobalPosterSettingsGrid.cs

@@ -0,0 +1,25 @@
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.Poster.MYOB;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRS.Shared.Posters.MYOB;
+
+public class MYOBGlobalPosterSettingsGrid : DynamicItemsListGrid<MYOBGlobalPosterSettings>
+{
+    protected override void DoAfterSave(IDynamicEditorForm editor, MYOBGlobalPosterSettings[] items)
+    {
+        base.DoAfterSave(editor, items);
+
+        if(items.Any(x => x.HasOriginalValue(x => x.CompanyFileID) || x.HasOriginalValue(x => x.CompanyFilePassword) || x.HasOriginalValue(x => x.CompanyFileUserID)))
+        {
+            var data = MYOBPosterEngine.GetConnectionData();
+            // Reset authentication if it's changed.
+            data.CompanyFile = null;
+        }
+    }
+}