Browse Source

Added better interface for selecting company file and inputting credentials data.

Kenric Nugteren 1 year ago
parent
commit
d4aa374345

+ 1 - 1
InABox.Core/Editors/ButtonEditor.cs

@@ -22,7 +22,7 @@ namespace InABox.Core
 
         protected override BaseEditor DoClone()
         {
-            return new ButtonEditor() { OnClick = this.OnClick };
+            return new ButtonEditor() { OnClick = OnClick };
         }
         
     }

+ 9 - 0
InABox.Core/Postable/PosterEngine.cs

@@ -12,6 +12,7 @@ namespace InABox.Core
     public interface IGlobalSettingsPosterEngine
     {
         IGlobalPosterSettings GetGlobalSettings();
+        void SaveGlobalSettings(IGlobalPosterSettings settings);
     }
 
     /// <summary>
@@ -23,8 +24,16 @@ namespace InABox.Core
         where TGlobalSettings : BaseObject, IGlobalPosterSettings, new()
     {
         new TGlobalSettings GetGlobalSettings() => PosterUtils.LoadGlobalPosterSettings<TGlobalSettings>();
+        void SaveGlobalSettings(TGlobalSettings settings) => PosterUtils.SaveGlobalPosterSettings(settings);
 
         IGlobalPosterSettings IGlobalSettingsPosterEngine.GetGlobalSettings() => GetGlobalSettings();
+        void IGlobalSettingsPosterEngine.SaveGlobalSettings(IGlobalPosterSettings settings)
+        {
+            if(settings is TGlobalSettings globalSettings)
+            {
+                SaveGlobalSettings(globalSettings);
+            }
+        }
     }
 
     public interface IPosterEngine<TPostable>

+ 3 - 0
InABox.Poster.MYOB/FodyWeavers.xml

@@ -0,0 +1,3 @@
+<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
+  <PropertyChanged />
+</Weavers>

+ 74 - 0
InABox.Poster.MYOB/FodyWeavers.xsd

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
+  <xs:element name="Weavers">
+    <xs:complexType>
+      <xs:all>
+        <xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="TriggerDependentProperties" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="EventInvokerNames" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="CheckForEquality" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="SuppressWarnings" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+          </xs:complexType>
+        </xs:element>
+      </xs:all>
+      <xs:attribute name="VerifyAssembly" type="xs:boolean">
+        <xs:annotation>
+          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="VerifyIgnoreCodes" type="xs:string">
+        <xs:annotation>
+          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="GenerateXsd" type="xs:boolean">
+        <xs:annotation>
+          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>

+ 5 - 0
InABox.Poster.MYOB/InABox.Poster.MYOB.csproj

@@ -8,8 +8,13 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Fody" Version="6.8.1">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
     <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
     <PackageReference Include="MYOB.AccountRight.API.SDK" Version="2024.4.591" />
+    <PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
   </ItemGroup>
 
   <ItemGroup>

+ 2 - 11
InABox.Poster.MYOB/MYOBGlobalPosterSettings.cs

@@ -10,8 +10,8 @@ namespace InABox.Poster.MYOB;
 public class MYOBGlobalPosterSettings : GlobalPosterSettings
 {
     [EditorSequence(1)]
-    [TextBoxEditor]
-    public string CompanyFileID { get; set; }
+    [MYOBCompanyFileEditor]
+    public MYOBCompanyFile CompanyFile { get; set; }
 
     [EditorSequence(2)]
     [TextBoxEditor]
@@ -20,13 +20,4 @@ public class MYOBGlobalPosterSettings : GlobalPosterSettings
     [EditorSequence(3)]
     [PasswordEditor(ViewButtonVisible = true)]
     public string CompanyFilePassword { get; set; }
-
-    public Guid GetCompanyFileID()
-    {
-        if(Guid.TryParse(CompanyFileID, out var id))
-        {
-            return id;
-        }
-        return Guid.Empty;
-    }
 }

+ 44 - 6
InABox.Poster.MYOB/MYOBPosterEngine.cs

@@ -1,5 +1,6 @@
 using InABox.Core;
 using InABox.Core.Postable;
+using InABox.DynamicGrid;
 using Microsoft.Web.WebView2.Wpf;
 using MYOB.AccountRight.SDK;
 using MYOB.AccountRight.SDK.Contracts;
@@ -138,7 +139,11 @@ public abstract class MYOBPosterEngine<TPostable, TSettings> :
     where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
     where TSettings : MYOBPosterSettings, new()
 {
-    private MYOBGlobalPosterSettings GetGlobalSettings() => (this as IGlobalSettingsPosterEngine<IMYOBPoster<TPostable, TSettings>, MYOBGlobalPosterSettings>).GetGlobalSettings();
+    private MYOBGlobalPosterSettings GetGlobalSettings() =>
+        (this as IGlobalSettingsPosterEngine<IMYOBPoster<TPostable, TSettings>, MYOBGlobalPosterSettings>).GetGlobalSettings();
+
+    private void SaveGlobalSettings(MYOBGlobalPosterSettings settings) =>
+        (this as IGlobalSettingsPosterEngine<IMYOBPoster<TPostable, TSettings>, MYOBGlobalPosterSettings>).SaveGlobalSettings(settings);
 
     public override bool BeforePost(IDataModel<TPostable> model)
     {
@@ -151,24 +156,57 @@ public abstract class MYOBPosterEngine<TPostable, TSettings> :
         var data = MYOBPosterEngine.GetConnectionData();
 
         var globalSettings = GetGlobalSettings();
-        var companyFileID = globalSettings.GetCompanyFileID();
-        if(data.CompanyFile is null || data.CompanyFile.Id != companyFileID)
+        if(data.CompanyFile is null || data.CompanyFile.Id != globalSettings.CompanyFile.ID)
         {
             CompanyFile? file;
-            if(companyFileID == Guid.Empty)
+            if(globalSettings.CompanyFile.ID == Guid.Empty)
             {
                 file = MYOBCompanyFileSelectionDialog.SelectCompanyFile();
                 if(file is null)
                 {
                     throw new PostCancelledException();
                 }
+                else
+                {
+                    globalSettings.CompanyFile.ID = file.Id;
+                    globalSettings.CompanyFile.Name = file.Name;
+
+                    SaveGlobalSettings(globalSettings);
+                    globalSettings.CommitChanges();
+                }
             }
 
             if(globalSettings.CompanyFileUserID.IsNullOrWhiteSpace())
             {
-                throw new PostFailedMessageException("CompanyFile credentials have not been set.");
+                var credentials = new MYOBCompanyFileCredentials
+                {
+                    UserID = globalSettings.CompanyFileUserID,
+                    Password = globalSettings.CompanyFilePassword,
+                };
+                if (DynamicGridUtils.EditObject(credentials, customiseGrid: grid =>
+                {
+                    grid.OnValidate += (grid, items, errors) =>
+                    {
+                        if(items.Any(x => x.UserID.IsNullOrWhiteSpace()))
+                        {
+                            errors.Add("[UserID] cannot be blank");
+                        }
+                    };
+                }))
+                {
+                    globalSettings.CompanyFileUserID = credentials.UserID;
+                    globalSettings.CompanyFilePassword = credentials.Password;
+
+                    SaveGlobalSettings(globalSettings);
+                    globalSettings.CommitChanges();
+                }
+                else
+                {
+                    throw new PostCancelledException();
+                }
             }
-            var companyFile = data.CompanyFileService.GetRange().FirstOrDefault(x => x.Id == companyFileID);
+
+            var companyFile = data.CompanyFileService.GetRange().FirstOrDefault(x => x.Id == globalSettings.CompanyFile.ID);
             var fileCredentials = new CompanyFileCredentials(globalSettings.CompanyFileUserID, globalSettings.CompanyFilePassword);
             data.CompanyFile = companyFile;
             data.CompanyFileCredentials = fileCredentials;

+ 19 - 0
InABox.Poster.MYOB/UI/MYOBCompanyFileCredentials.cs

@@ -0,0 +1,19 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace InABox.Poster.MYOB;
+
+public class MYOBCompanyFileCredentials : BaseObject
+{
+    [EditorSequence(1)]
+    [TextBoxEditor]
+    public string UserID { get; set; }
+
+    [EditorSequence(2)]
+    [PasswordEditor(ViewButtonVisible = true)]
+    public string Password { get; set; }
+}

+ 140 - 0
InABox.Poster.MYOB/UI/MYOBCompanyFileEditor.cs

@@ -0,0 +1,140 @@
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.WPF;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace InABox.Poster.MYOB;
+
+public class MYOBCompanyFileEditor : BaseEditor
+{
+    protected override BaseEditor DoClone()
+    {
+        return new MYOBCompanyFileEditor();
+    }
+}
+
+public class MYOBCompanyFileEditorControl : DynamicEnclosedEditorControl<MYOBCompanyFile, MYOBCompanyFileEditor>
+{
+    private Grid Grid = null!;
+    private TextBox TextBox = null!;
+    private Button Select = null!;
+    private Button Clear = null!;
+
+    private readonly MYOBCompanyFile Value = new();
+    private static readonly Column<MYOBCompanyFile> IDColumn = new(x => x.ID);
+    private static readonly Column<MYOBCompanyFile> NameColumn = new(x => x.Name);
+
+    public override int DesiredHeight() => 25;
+
+    public override int DesiredWidth() => int.MaxValue;
+
+    protected override FrameworkElement CreateEditor()
+    {
+        Grid = new Grid
+        {
+            VerticalAlignment = VerticalAlignment.Stretch,
+            HorizontalAlignment = HorizontalAlignment.Stretch
+        };
+        Grid.AddColumn(GridUnitType.Star);
+        Grid.AddColumn(70);
+        Grid.AddColumn(70);
+
+        TextBox = new TextBox
+        {
+            VerticalAlignment = VerticalAlignment.Stretch,
+            VerticalContentAlignment = VerticalAlignment.Center,
+            HorizontalAlignment = HorizontalAlignment.Stretch,
+            IsEnabled = false
+        };
+        Grid.AddChild(TextBox, 0, 0);
+
+        Select = new Button
+        {
+            VerticalAlignment = VerticalAlignment.Stretch,
+            VerticalContentAlignment = VerticalAlignment.Center,
+            HorizontalAlignment = HorizontalAlignment.Stretch,
+            Content = "Select",
+            Margin = new Thickness(5, 0, 0, 0),
+        };
+        Select.Click += Select_Click;
+        Grid.AddChild(Select, 0, 1);
+
+        Clear = new Button
+        {
+            VerticalAlignment = VerticalAlignment.Stretch,
+            VerticalContentAlignment = VerticalAlignment.Center,
+            HorizontalAlignment = HorizontalAlignment.Stretch,
+            Content = "Clear",
+            Margin = new Thickness(5, 0, 0, 0),
+            Focusable = false
+        };
+        Clear.Click += Clear_Click;
+        Grid.AddChild(Clear, 0, 2);
+
+        return Grid;
+    }
+
+    public override void Configure()
+    {
+    }
+
+    private void Select_Click(object sender, RoutedEventArgs e)
+    {
+        var file = MYOBCompanyFileSelectionDialog.SelectCompanyFile();
+        if(file is not null)
+        {
+            Value.ID = file.Id;
+            Value.Name = file.Name;
+            TextBox.Text = Value.Name;
+            CheckChanged();
+        }
+    }
+
+    private void Clear_Click(object sender, RoutedEventArgs e)
+    {
+        Value.ID = Guid.Empty;
+        Value.Name = "";
+        TextBox.Text = "";
+        CheckChanged();
+    }
+
+    protected override object? GetChildValue(string property)
+    {
+        if (IDColumn.IsEqualTo(property)) return Value.ID;
+        if (NameColumn.IsEqualTo(property)) return Value.Name;
+        return null;
+    }
+
+    protected override void SetChildValue(string property, object? value)
+    {
+        if (IDColumn.IsEqualTo(property)) Value.ID = (Guid)(value ?? Guid.Empty);
+        else if (NameColumn.IsEqualTo(property))
+        {
+            Value.Name = (value as string) ?? "";
+            TextBox.Text = Value.Name;
+        }
+    }
+
+    protected override IEnumerable<KeyValuePair<string, object?>> GetChildValues()
+    {
+        yield return new(IDColumn.Property, Value.ID);
+        yield return new(NameColumn.Property, Value.Name);
+    }
+
+    public override void SetColor(Color color)
+    {
+        TextBox.Background = IsEnabled ? new SolidColorBrush(color) : new SolidColorBrush(Colors.WhiteSmoke);
+    }
+
+    public override void SetFocus()
+    {
+        Select.Focus();
+    }
+}

+ 1 - 1
InABox.Poster.MYOB/MYOBCompanyFileGrid.cs → InABox.Poster.MYOB/UI/MYOBCompanyFileGrid.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace InABox.Poster.MYOB;
 
-public class MYOBCompanyFile : BaseObject
+public class MYOBCompanyFile : EnclosedEntity
 {
     [NullEditor]
     public Guid ID { get; set; }

+ 2 - 1
InABox.Poster.MYOB/MYOBCompanyFileSelectionDialog.xaml → InABox.Poster.MYOB/UI/MYOBCompanyFileSelectionDialog.xaml

@@ -6,7 +6,8 @@
         xmlns:local="clr-namespace:InABox.Poster.MYOB"
         mc:Ignorable="d"
         Title="Select Company File:" Height="450" Width="800"
-        x:Name="Window">
+        x:Name="Window"
+        Loaded="Window_Loaded">
     <Grid DataContext="{Binding ElementName=Window}">
         <Grid.RowDefinitions>
             <RowDefinition Height="*"/>

+ 17 - 5
InABox.Poster.MYOB/MYOBCompanyFileSelectionDialog.xaml.cs → InABox.Poster.MYOB/UI/MYOBCompanyFileSelectionDialog.xaml.cs

@@ -68,6 +68,23 @@ public partial class MYOBCompanyFileSelectionDialog : Window, INotifyPropertyCha
         CanSave = e.Rows is not null && e.Rows.Length > 0;
     }
 
+    private void Window_Loaded(object sender, RoutedEventArgs e)
+    {
+        Grid.Refresh(true, true);
+    }
+
+    private void CancelButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = false;
+        Close();
+    }
+
+    private void OKButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = true;
+        Close();
+    }
+
     public static CompanyFile? SelectCompanyFile()
     {
         var data = MYOBPosterEngine.GetConnectionData();
@@ -100,9 +117,4 @@ public partial class MYOBCompanyFileSelectionDialog : Window, INotifyPropertyCha
             return null;
         }
     }
-
-    private void Window_Loaded(object sender, RoutedEventArgs e)
-    {
-        Grid.Refresh(true, true);
-    }
 }

+ 2 - 2
inabox.wpf/DynamicGrid/DynamicGrid.cs

@@ -1557,12 +1557,12 @@ public abstract class DynamicGrid<T> : DynamicGrid, IDynamicGridUIComponentParen
         }*/
     }
 
-    private string[]? ValidateData(T[] items)
+    private List<string>? ValidateData(T[] items)
     {
         var errors = new List<string>();
         DoValidate(items, errors);
         OnValidate?.Invoke(this, items, errors);
-        return errors.Any() ? errors.ToArray() : null;
+        return errors.Count != 0 ? errors : null;
     }
 
     protected virtual void DoValidate(T[] items, List<string> errors)

+ 3 - 2
inabox.wpf/DynamicGrid/DynamicGridCommon.cs

@@ -334,7 +334,8 @@ public delegate void OnGridCustomiseEditor(DynamicEditorGrid sender, DynamicGrid
 
 public delegate void OnFormCustomiseEditor(IDynamicEditorForm sender, object[] items, DynamicGridColumn column, BaseEditor editor);
 
-public delegate void ValidateEvent<T>(object sender, T[] items, List<string> errors);
+public delegate void ValidateEvent<T>(DynamicGrid<T> sender, T[] items, List<string> errors)
+	where T : BaseObject, new();
 
 /// <summary>
 /// 
@@ -383,7 +384,7 @@ public delegate IFilter? OnDefineLookupFilter(Type type, string column);
 
 public delegate IFilter? OnDefineFilter(Type type);
 
-public delegate IList<string>? OnValidateData(object sender, BaseObject[] items);
+public delegate IList<string>? OnValidateData(IDynamicEditorForm sender, BaseObject[] items);
 
 public delegate void OnPrintData(object sender);
 

+ 26 - 0
inabox.wpf/DynamicGrid/Editors/DynamicEnclosedEditorControl.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Linq;
 using System.Windows;
 using System.Windows.Media;
 using InABox.Core;
@@ -67,5 +68,30 @@ namespace InABox.DynamicGrid
             Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
         }
 
+        protected abstract IEnumerable<KeyValuePair<string, object?>> GetChildValues();
+        public override Dictionary<string, object?> GetValues()
+        {
+            return GetChildValues()
+                .Select(x => new KeyValuePair<string, object?>($"{ColumnName}.{x.Key}", x.Value))
+                .ToDictionary();
+        }
+
+        protected abstract object? GetChildValue(string property);
+
+        public override object? GetValue(string property)
+        {
+            if (!property.StartsWith($"{ColumnName}.")) return null;
+            property = property[(ColumnName.Length + 1)..];
+
+            return GetChildValue(property);
+        }
+
+        protected abstract void SetChildValue(string property, object? value);
+        public override void SetValue(string property, object? value)
+        {
+            if (!property.StartsWith($"{ColumnName}.")) return;
+            property = property[(ColumnName.Length + 1)..];
+            SetChildValue(property, value);
+        }
     }
 }

+ 36 - 0
inabox.wpf/WPFUtils.cs

@@ -103,6 +103,12 @@ namespace InABox.WPF
             element.SetValue(Grid.RowProperty, row);
             element.SetValue(Grid.RowSpanProperty, Math.Max(1, rowspan));
         }
+        public static Grid AddChild(this Grid grid, FrameworkElement element, int row, int column, int rowSpan = 1, int colSpan = 1)
+        {
+            element.SetGridPosition(row, column, rowSpan, colSpan);
+            grid.Children.Add(element);
+            return grid;
+        }
 
         public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject depObj)
         {
@@ -132,6 +138,36 @@ namespace InABox.WPF
                 }
         }
 
+        #region Grid Columns + Rows
+
+        public static ColumnDefinition AddColumn(this Grid grid, GridUnitType type, double value = 1)
+        {
+            var colDef = new ColumnDefinition { Width = new GridLength(value, type) };
+            grid.ColumnDefinitions.Add(colDef);
+            return colDef;
+        }
+        public static ColumnDefinition AddColumn(this Grid grid, double value)
+        {
+            var colDef = new ColumnDefinition { Width = new GridLength(value) };
+            grid.ColumnDefinitions.Add(colDef);
+            return colDef;
+        }
+
+        public static RowDefinition AddRow(this Grid grid, GridUnitType type, double value = 1)
+        {
+            var rowDef = new RowDefinition { Height = new GridLength(value, type) };
+            grid.RowDefinitions.Add(rowDef);
+            return rowDef;
+        }
+        public static RowDefinition AddRow(this Grid grid, double value)
+        {
+            var rowDef = new RowDefinition { Height = new GridLength(value) };
+            grid.RowDefinitions.Add(rowDef);
+            return rowDef;
+        }
+
+        #endregion
+
         #region Menu Utils
 
         private static void ItemsControlInsert(ItemsControl menu, FrameworkElement item, int index)