MYOBPosterEngine.cs 5.9 KB

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