|  | @@ -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();
 | 
	
		
			
				|  |  | +}
 |