MYOBPosterEngine.cs 5.2 KB

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