|
@@ -0,0 +1,155 @@
|
|
|
+using InABox.Core;
|
|
|
+using InABox.Core.Postable;
|
|
|
+using MYOB.AccountRight.SDK;
|
|
|
+using MYOB.AccountRight.SDK.Contracts;
|
|
|
+using MYOB.AccountRight.SDK.Services;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+using System.Text.RegularExpressions;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using System.Web;
|
|
|
+using System.Windows;
|
|
|
+using System.Windows.Controls;
|
|
|
+
|
|
|
+namespace InABox.Poster.MYOB;
|
|
|
+
|
|
|
+public class MYOBConnectionData(ApiConfiguration configuration, CompanyFileService cfService)
|
|
|
+{
|
|
|
+ public ApiConfiguration Configuration { get; set; } = configuration;
|
|
|
+
|
|
|
+ public CompanyFileService CompanyFileService { get; set; } = cfService;
|
|
|
+
|
|
|
+ public CompanyFile? CompanyFile { get; set; }
|
|
|
+
|
|
|
+ public CompanyFileCredentials? CompanyFileCredentials { get; set; }
|
|
|
+
|
|
|
+ public CompanyFileWithResources? ActiveCompanyFile { get; set; }
|
|
|
+}
|
|
|
+
|
|
|
+public partial class MYOBPosterEngine<TPostable> :
|
|
|
+ PosterEngine<TPostable, IMYOBPoster<TPostable>, MYOBPosterSettings>,
|
|
|
+ IGlobalSettingsPosterEngine<IMYOBPoster<TPostable>, MYOBGlobalPosterSettings>
|
|
|
+
|
|
|
+ where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
|
|
|
+{
|
|
|
+ internal static readonly string DEV_KEY = "f3b27f88-2ef9-4d8e-95c1-d0a045d0afee";
|
|
|
+ internal static readonly string SECRET_KEY = "ksm0e8yo6oumcPb63A8cduaN";
|
|
|
+ internal const string AUTH_URL = "https://secure.myob.com/oauth2/account/authorize";
|
|
|
+ internal const string AUTH_SCOPE = "CompanyFile";
|
|
|
+ internal const string REDIRECT_URL = "http://desktop";
|
|
|
+
|
|
|
+ private static MYOBConnectionData? _connectionData;
|
|
|
+
|
|
|
+ private MYOBGlobalPosterSettings GetGlobalSettings() => (this as IGlobalSettingsPosterEngine<IMYOBPoster<TPostable>, MYOBGlobalPosterSettings>).GetGlobalSettings();
|
|
|
+
|
|
|
+ public override bool BeforePost(IDataModel<TPostable> model)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static bool GetAuthorisationCode(IApiConfiguration config, out string? code)
|
|
|
+ {
|
|
|
+ var url = $"{AUTH_URL}?client_id={config.ClientId}&redirect_uri={HttpUtility.UrlEncode(config.RedirectUrl)}&scope={AUTH_SCOPE}&response_type=code";
|
|
|
+
|
|
|
+ var window = new Window
|
|
|
+ {
|
|
|
+ Width = 800,
|
|
|
+ Height = 600
|
|
|
+ };
|
|
|
+
|
|
|
+ string? resultCode = null;
|
|
|
+ var browser = new WebBrowser();
|
|
|
+ browser.Navigated += (o, e) =>
|
|
|
+ {
|
|
|
+ dynamic doc = browser.Document;
|
|
|
+ var htmlText = doc.documentElement.InnerHtml as string;
|
|
|
+ if(htmlText is not null)
|
|
|
+ {
|
|
|
+ var match = CodeRegex().Match(htmlText);
|
|
|
+ if (match.Success)
|
|
|
+ {
|
|
|
+ resultCode = match.Groups[1].Value;
|
|
|
+ window.Close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ browser.Navigate(url);
|
|
|
+ window.Content = browser;
|
|
|
+ if(window.ShowDialog() == true)
|
|
|
+ {
|
|
|
+ code = resultCode;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ code = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MYOBConnectionData GetConnectionData()
|
|
|
+ {
|
|
|
+ if(_connectionData is MYOBConnectionData data)
|
|
|
+ {
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ var configuration = new ApiConfiguration(DEV_KEY, SECRET_KEY, REDIRECT_URL);
|
|
|
+ var authService = new OAuthService(configuration);
|
|
|
+
|
|
|
+ if(!GetAuthorisationCode(configuration, out var code))
|
|
|
+ {
|
|
|
+ throw new PostCancelledException();
|
|
|
+ }
|
|
|
+ if(code is null)
|
|
|
+ {
|
|
|
+ throw new PostFailedMessageException("No authorisation code was received.");
|
|
|
+ }
|
|
|
+
|
|
|
+ var keystore = new SimpleOAuthKeyService
|
|
|
+ {
|
|
|
+ OAuthResponse = authService.GetTokens(code)
|
|
|
+ };
|
|
|
+
|
|
|
+ var cfService = new CompanyFileService(configuration, null, keystore);
|
|
|
+
|
|
|
+ _connectionData = new MYOBConnectionData(configuration, cfService);
|
|
|
+
|
|
|
+ return _connectionData;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
|
|
|
+ {
|
|
|
+ var data = GetConnectionData();
|
|
|
+
|
|
|
+ var globalSettings = GetGlobalSettings();
|
|
|
+ if(data.CompanyFile is null || data.CompanyFile.Id != globalSettings.CompanyFileID)
|
|
|
+ {
|
|
|
+ if(globalSettings.CompanyFileID == Guid.Empty)
|
|
|
+ {
|
|
|
+ throw new PostFailedMessageException("No CompanyFileID has been set.");
|
|
|
+ }
|
|
|
+ else if(globalSettings.CompanyFileUserID.IsNullOrWhiteSpace() || globalSettings.CompanyFilePassword.IsNullOrWhiteSpace())
|
|
|
+ {
|
|
|
+ throw new PostFailedMessageException("CompanyFile credentials have not been set.");
|
|
|
+ }
|
|
|
+ var companyFile = data.CompanyFileService.GetRange().FirstOrDefault(x => x.Id == globalSettings.CompanyFileID);
|
|
|
+ var fileCredentials = new CompanyFileCredentials(globalSettings.CompanyFileUserID, globalSettings.CompanyFilePassword);
|
|
|
+ data.CompanyFile = companyFile;
|
|
|
+ data.CompanyFileCredentials = fileCredentials;
|
|
|
+ data.ActiveCompanyFile = data.CompanyFileService.Get(companyFile, fileCredentials);
|
|
|
+ }
|
|
|
+ Poster.ConnectionData = data;
|
|
|
+
|
|
|
+ return Poster.Process(model);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ [GeneratedRegex("code=(.*)<")]
|
|
|
+ private static partial Regex CodeRegex();
|
|
|
+}
|