MYOBPosterEngine.cs 5.4 KB

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