MYOBPosterEngine.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using InABox.Core;
  2. using InABox.Core.Postable;
  3. using MYOB.AccountRight.SDK;
  4. using MYOB.AccountRight.SDK.Contracts;
  5. using MYOB.AccountRight.SDK.Services;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Text.RegularExpressions;
  11. using System.Threading.Tasks;
  12. using System.Web;
  13. using System.Windows;
  14. using System.Windows.Controls;
  15. namespace InABox.Poster.MYOB;
  16. public class MYOBConnectionData(ApiConfiguration configuration, CompanyFileService cfService, IOAuthKeyService authKey)
  17. {
  18. public ApiConfiguration Configuration { get; set; } = configuration;
  19. public CompanyFileService CompanyFileService { get; set; } = cfService;
  20. public CompanyFile? CompanyFile { get; set; }
  21. public CompanyFileCredentials? CompanyFileCredentials { get; set; }
  22. public CompanyFileWithResources? ActiveCompanyFile { get; set; }
  23. public IOAuthKeyService AuthKey { get; set; } = authKey;
  24. }
  25. public abstract partial class MYOBPosterEngine<TPostable, TSettings> :
  26. PosterEngine<TPostable, IMYOBPoster<TPostable, TSettings>, TSettings>,
  27. IGlobalSettingsPosterEngine<IMYOBPoster<TPostable, TSettings>, MYOBGlobalPosterSettings>
  28. where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
  29. where TSettings : MYOBPosterSettings, new()
  30. {
  31. internal static readonly string DEV_KEY = "f3b27f88-2ef9-4d8e-95c1-d0a045d0afee";
  32. internal static readonly string SECRET_KEY = "ksm0e8yo6oumcPb63A8cduaN";
  33. internal const string AUTH_URL = "https://secure.myob.com/oauth2/account/authorize";
  34. internal const string AUTH_SCOPE = "CompanyFile";
  35. internal const string REDIRECT_URL = "http://desktop";
  36. private static MYOBConnectionData? _connectionData;
  37. private MYOBGlobalPosterSettings GetGlobalSettings() => (this as IGlobalSettingsPosterEngine<IMYOBPoster<TPostable, TSettings>, MYOBGlobalPosterSettings>).GetGlobalSettings();
  38. public override bool BeforePost(IDataModel<TPostable> model)
  39. {
  40. return Poster.BeforePost(model);
  41. }
  42. public static bool GetAuthorisationCode(IApiConfiguration config, out string? code)
  43. {
  44. var url = $"{AUTH_URL}?client_id={config.ClientId}&redirect_uri={HttpUtility.UrlEncode(config.RedirectUrl)}&scope={AUTH_SCOPE}&response_type=code";
  45. var window = new Window
  46. {
  47. Width = 800,
  48. Height = 600,
  49. Title = "Sign in to MYOB"
  50. };
  51. string? resultCode = null;
  52. var browser = new System.Windows.Forms.WebBrowser();
  53. browser.DocumentTitleChanged += (o, e) =>
  54. {
  55. var htmlText = browser.DocumentText;
  56. if(htmlText is not null)
  57. {
  58. var match = CodeRegex().Match(htmlText);
  59. if (match.Success)
  60. {
  61. resultCode = match.Groups[1].Value;
  62. window.DialogResult = true;
  63. window.Close();
  64. }
  65. }
  66. };
  67. browser.Navigate(url);
  68. window.Content = browser;
  69. if(window.ShowDialog() == true)
  70. {
  71. code = resultCode;
  72. return true;
  73. }
  74. else
  75. {
  76. code = null;
  77. return false;
  78. }
  79. }
  80. private static MYOBConnectionData GetConnectionData()
  81. {
  82. if(_connectionData is MYOBConnectionData data)
  83. {
  84. return data;
  85. }
  86. var configuration = new ApiConfiguration(DEV_KEY, SECRET_KEY, REDIRECT_URL);
  87. var authService = new OAuthService(configuration);
  88. if(!GetAuthorisationCode(configuration, out var code))
  89. {
  90. throw new PostCancelledException();
  91. }
  92. if(code is null)
  93. {
  94. throw new PostFailedMessageException("No authorisation code was received.");
  95. }
  96. var keystore = new SimpleOAuthKeyService
  97. {
  98. OAuthResponse = authService.GetTokens(code)
  99. };
  100. var cfService = new CompanyFileService(configuration, null, keystore);
  101. _connectionData = new MYOBConnectionData(configuration, cfService, keystore);
  102. return _connectionData;
  103. }
  104. protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
  105. {
  106. var data = GetConnectionData();
  107. var globalSettings = GetGlobalSettings();
  108. if(data.CompanyFile is null || data.CompanyFile.Id != globalSettings.CompanyFileID)
  109. {
  110. if(globalSettings.CompanyFileID == Guid.Empty)
  111. {
  112. throw new PostFailedMessageException("No CompanyFileID has been set.");
  113. }
  114. else if(globalSettings.CompanyFileUserID.IsNullOrWhiteSpace() || globalSettings.CompanyFilePassword.IsNullOrWhiteSpace())
  115. {
  116. throw new PostFailedMessageException("CompanyFile credentials have not been set.");
  117. }
  118. var companyFile = data.CompanyFileService.GetRange().FirstOrDefault(x => x.Id == globalSettings.CompanyFileID);
  119. var fileCredentials = new CompanyFileCredentials(globalSettings.CompanyFileUserID, globalSettings.CompanyFilePassword);
  120. data.CompanyFile = companyFile;
  121. data.CompanyFileCredentials = fileCredentials;
  122. data.ActiveCompanyFile = data.CompanyFileService.Get(companyFile, fileCredentials);
  123. }
  124. Poster.ConnectionData = data;
  125. return Poster.Process(model);
  126. }
  127. public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
  128. {
  129. }
  130. [GeneratedRegex("code=(.*)<")]
  131. private static partial Regex CodeRegex();
  132. }