From e214a39efdd6743078d37d6eae1484865aeb5176 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 26 Nov 2024 13:14:48 +0100 Subject: [PATCH] Updated to new E-Doc Core --- .../Tietoevry/app/app.json | 8 +- .../app/src/Authenticator.Codeunit.al | 139 ++++++ ...ctionSetup.al => ConnectionSetup.Table.al} | 64 ++- ...tupCard.al => ConnectionSetupCard.Page.al} | 77 ++-- ...ConnectionEvents.al => Events.Codeunit.al} | 67 +-- .../EDocFormat.EnumExt.al} | 6 +- .../EDocument.TableExt.al} | 16 +- .../app/src/Formats/Cod6398.FormatEvents.al | 91 ++++ .../EDocumentImpl.al} | 39 +- .../TietoevryEDocImport.al} | 94 ++-- .../app/src/HttpExecutor.Codeunit.al | 119 ++++++ .../Tietoevry/app/src/Integration.EnumExt.al | 18 + .../app/src/IntegrationImpl.Codeunit.al | 47 ++ ...ntSendMode.al => IntegrationV2.EnumExt.al} | 15 +- .../Edit.PermissionSet.al} | 7 +- .../src/Permissions/Objects.PermissionSet.al | 24 ++ .../Read.PermissionSet.al} | 6 +- ...evryEDocConnectorEdit.PermissionSetExt.al} | 5 +- ...evryEDocConnectorRead.PermissionSetExt.al} | 7 +- .../Tietoevry/app/src/Processing.Codeunit.al | 280 ++++++++++++ .../Tietoevry/app/src/Requests.Codeunit.al | 242 +++++++++++ .../Tietoevry/app/src/Upgrade.Codeunit.al | 64 +++ .../app/src/codeunit/Cod6381.Connection.al | 127 ------ .../src/codeunit/Cod6382.IntegrationImpl.al | 59 --- .../app/src/codeunit/Cod6383.APIRequests.al | 174 -------- .../app/src/codeunit/Cod6384.Authenticator.al | 371 ---------------- .../app/src/codeunit/Cod6387.Processing.al | 401 ------------------ .../Enum-Ext6380.EDocFormatExt.al | 16 - .../PermissionSet6382.TEEDocConnObjects.al | 19 - 29 files changed, 1220 insertions(+), 1382 deletions(-) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Authenticator.Codeunit.al rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{table/Tab6380.ConnectionSetup.al => ConnectionSetup.Table.al} (54%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{page/Pag6380.ConnectionSetupCard.al => ConnectionSetupCard.Page.al} (59%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{codeunit/Cod6380.ConnectionEvents.al => Events.Codeunit.al} (62%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{enumextension/Enum-Ext6381.EDocExtIntegration.al => Extensions/EDocFormat.EnumExt.al} (72%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{tableextension/Tab-Ext6382.EDocTEExtEDocument.al => Extensions/EDocument.TableExt.al} (64%) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{codeunit/Cod6385.EDocTEPEPPOLBIS30.al => Formats/EDocumentImpl.al} (78%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{codeunit/Cod6386.EDocImportTietoevry.al => Formats/TietoevryEDocImport.al} (82%) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{enum/Enum6380.EDocumentSendMode.al => IntegrationV2.EnumExt.al} (58%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{permissionset/PermissionSet6381.TEEDocConnRead.al => Permissions/Edit.PermissionSet.al} (70%) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{permissionset/PermissionSet6380.TEEDocConnEdit.al => Permissions/Read.PermissionSet.al} (76%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al => Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al} (73%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/{permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al => Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al} (69%) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json index b11d4fa14..c1f1f7470 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json @@ -16,13 +16,13 @@ "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", "name": "E-Document Core", "publisher": "Microsoft", - "version": "26.0.0.0" + "version": "26.0.27172.0" }, { "id": "d852a468-263e-49e5-bfda-f09e33342b89", "name": "E-Documents Connector with External Endpoints", "publisher": "Microsoft", - "version": "26.0.0.0" + "version": "26.0.27172.0" } ], "internalsVisibleTo": [ @@ -38,8 +38,8 @@ "platform": "26.0.0.0", "idRanges": [ { - "from": 6380, - "to": 6389 + "from": 6390, + "to": 6399 } ], "resourceExposurePolicy": { diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Authenticator.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Authenticator.Codeunit.al new file mode 100644 index 000000000..31266f6c8 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Authenticator.Codeunit.al @@ -0,0 +1,139 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using System.Security.Authentication; +codeunit 6394 "Authenticator" +{ + Access = Internal; + Permissions = tabledata "OAuth 2.0 Setup" = im, + tabledata "Connection Setup" = rim; + + procedure CreateConnectionSetupRecord() + var + ConnectionSetup: Record "Connection Setup"; + begin + if not ConnectionSetup.Get() then begin + ConnectionSetup."Authentication URL" := this.AuthURLTxt; + ConnectionSetup."API URL" := this.APIURLTxt; + ConnectionSetup."Sandbox Authentication URL" := this.SandboxAuthURLTxt; + ConnectionSetup."Sandbox API URL" := this.SandboxAPIURLTxt; + ConnectionSetup."Send Mode" := ConnectionSetup."Send Mode"::Test; //Sandbox + ConnectionSetup.Insert(); + end; + end; + + procedure SetClientId(var ClientIdKey: Guid; ClientID: SecretText) + begin + this.SetIsolatedStorageValue(ClientIdKey, ClientID, DataScope::Company); + end; + + procedure SetClientSecret(var ClienSecretKey: Guid; ClientSecret: SecretText) + begin + this.SetIsolatedStorageValue(ClienSecretKey, ClientSecret, DataScope::Company); + end; + + procedure GetAccessToken() Token: SecretText + var + ConnectionSetup: Record "Connection Setup"; + Requests: Codeunit Requests; + ExpiresIn: Integer; + ClientId, ClientSecret, TokenTxt, Response : SecretText; + TokenKey: Guid; + begin + ConnectionSetup.Get(); + + // Reuse token if it lives longer than 1 min in future + if (ConnectionSetup."Token Expiry" > CurrentDateTime() + 60 * 1000) and (not IsNullGuid(ConnectionSetup."Token - Key")) then + if this.GetTokenValue(ConnectionSetup."Token - Key", Token, DataScope::Company) then + exit; + + if not this.GetTokenValue(ConnectionSetup."Client ID - Key", ClientId, DataScope::Company) then + Error(this.TietoevryClientIdErr, ConnectionSetup.TableCaption); + + if not this.GetTokenValue(ConnectionSetup."Client Secret - Key", ClientSecret, DataScope::Company) then + Error(this.TietoevryClientSecretErr, ConnectionSetup.TableCaption); + + Requests.Init(); + Requests.CreateAuthenticateRequest(ClientId, ClientSecret); + this.ExecuteResponse(Requests, Response); + if not this.ParseResponse(Response, TokenTxt, ExpiresIn) then + Error(this.TietoevryParseTokenErr); + + // Save token for reuse + this.SetIsolatedStorageValue(TokenKey, TokenTxt, DataScope::Company); + // Read again as we want fresh record to modify + ConnectionSetup.Get(); + ConnectionSetup."Token - Key" := TokenKey; + ConnectionSetup."Token Expiry" := CurrentDateTime() + ExpiresIn * 1000; + ConnectionSetup.Modify(); + Commit(); + exit(TokenTxt); + end; + + [NonDebuggable] + local procedure ExecuteResponse(var Request: Codeunit Requests; var Response: SecretText) + var + HttpExecutor: Codeunit "Http Executor"; + begin + Response := HttpExecutor.ExecuteHttpRequest(Request); + end; + + [NonDebuggable] + [TryFunction] + local procedure ParseResponse(Response: SecretText; var Token: SecretText; var ExpiresIn: Integer) + var + ResponseJson: JsonObject; + TokenJson, ExpiryJson : JsonToken; + begin + ResponseJson.ReadFrom(Response.Unwrap()); + ResponseJson.Get('access_token', TokenJson); + Token := TokenJson.AsValue().AsText(); + ResponseJson.Get('expires_in', ExpiryJson); + ExpiresIn := ExpiryJson.AsValue().AsInteger(); + end; + + procedure IsClientCredsSet(var ClientId: Text; var ClientSecret: Text): Boolean + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + + if this.HasToken(ConnectionSetup."Client ID - Key", DataScope::Company) then + ClientId := '*'; + if this.HasToken(ConnectionSetup."Client Secret - Key", DataScope::Company) then + ClientSecret := '*'; + end; + + procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText; TokenDataScope: DataScope) + begin + if IsNullGuid(ValueKey) then + ValueKey := CreateGuid(); + + IsolatedStorage.Set(ValueKey, Value, TokenDataScope); + end; + + local procedure GetTokenValue(TokenKey: Text; var TokenValueAsSecret: SecretText; TokenDataScope: DataScope): Boolean + begin + if not this.HasToken(TokenKey, TokenDataScope) then + exit(false); + + exit(IsolatedStorage.Get(TokenKey, TokenDataScope, TokenValueAsSecret)); + end; + + local procedure HasToken(TokenKey: Text; TokenDataScope: DataScope): Boolean + begin + exit(IsolatedStorage.Contains(TokenKey, TokenDataScope)); + end; + + var + AuthURLTxt: Label 'https://auth.infotorg.no/auth/realms/fms-realm/protocol/openid-connect', Locked = true; + APIURLTxt: Label 'https://accesspoint-api.dataplatfor.ms', Locked = true; + SandboxAuthURLTxt: Label 'https://auth-qa.infotorg.no/auth/realms/fms-realm/protocol/openid-connect', Locked = true; + SandboxAPIURLTxt: Label 'https://accesspoint-api.qa.dataplatfor.ms', Locked = true; + TietoevryClientIdErr: Label 'Tietoevry Client Id is not set in %1', Comment = '%1 - Client id'; + TietoevryClientSecretErr: Label 'Tietoevry Client Secret is not set in %1', Comment = '%1 - Client secret'; + TietoevryParseTokenErr: Label 'Failed to parse response for Tietoevry Access token request'; +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/table/Tab6380.ConnectionSetup.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al similarity index 54% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/table/Tab6380.ConnectionSetup.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index f26e7d490..bd3f5eaff 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/table/Tab6380.ConnectionSetup.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -4,68 +4,86 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -table 6380 "Connection Setup" +using Microsoft.EServices.EDocumentConnector; + +table 6392 "Connection Setup" { fields { - field(1; PK; Code[10]) + field(1; Id; Code[10]) { DataClassification = CustomerContent; } - field(3; "OAuth Feature GUID"; GUID) + field(2; "Client Id - Key"; Guid) { - Caption = 'OAuth 2.0 Code'; - DataClassification = CustomerContent; + Caption = 'Client Id'; + DataClassification = EndUserIdentifiableInformation; + } + field(3; "Client Secret - Key"; Guid) + { + Caption = 'Client Secret'; + DataClassification = EndUserIdentifiableInformation; } field(4; "Authentication URL"; Text[250]) { Caption = 'Authentication URL'; DataClassification = CustomerContent; + Editable = false; } - field(7; "Outbound API URL"; Text[250]) + field(5; "API URL"; Text[250]) { - Caption = 'Outbound API URL'; + Caption = 'API URL'; DataClassification = CustomerContent; + Editable = false; } - field(8; "Inbound API URL"; Text[250]) + field(6; "Sandbox Authentication URL"; Text[250]) { - Caption = 'Inbound API URL'; + Caption = 'Sandbox Authentication URL'; DataClassification = CustomerContent; + Editable = false; } - field(9; "Company Id"; Text[100]) + field(7; "Sandbox API URL"; Text[250]) { - Caption = 'Company ID'; + Caption = 'Sandbox Authentication URL'; DataClassification = CustomerContent; + Editable = false; } - field(10; "Client ID"; Guid) + field(8; "Token - Key"; Guid) { - Caption = 'Client ID'; - DataClassification = EndUserIdentifiableInformation; + Caption = 'Token'; + DataClassification = CustomerContent; } - field(11; "Client Secret"; Guid) + field(9; "Token Expiry"; DateTime) { - Caption = 'Client Secret'; - DataClassification = EndUserIdentifiableInformation; + Caption = 'Token Expiry'; + DataClassification = CustomerContent; } - field(12; "Send Mode"; Enum "E-Document Send Mode") + field(10; "Company Id"; Text[250]) { - Caption = 'Send Mode'; - DataClassification = EndUserIdentifiableInformation; + Caption = 'Company ID'; + DataClassification = CustomerContent; trigger OnValidate() var - TietoevryAuth: Codeunit Authenticator; + TietoevryProcessing: Codeunit Processing; begin - TietoevryAuth.SetDefaultEndpoints(Rec, "Send Mode"); + if not TietoevryProcessing.IsValidSchemeId(Rec."Company Id") then + Rec.FieldError(Rec."Company Id"); end; } + field(13; "Send Mode"; Enum "E-Doc. Ext. Send Mode") + { + Caption = 'Send Mode'; + DataClassification = EndUserIdentifiableInformation; + } } keys { - key(Key1; PK) + key(Key1; Id) { Clustered = true; } } + } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/page/Pag6380.ConnectionSetupCard.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al similarity index 59% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/page/Pag6380.ConnectionSetupCard.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al index bb81462c0..7560ea236 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/page/Pag6380.ConnectionSetupCard.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al @@ -5,15 +5,17 @@ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using System.Telemetry; -using System.Environment; -page 6380 "Connection Setup Card" +page 6392 "Connection Setup Card" { PageType = Card; SourceTable = "Connection Setup"; ApplicationArea = Basic, Suite; UsageCategory = None; Caption = 'Tietoevry Connection Setup'; + Permissions = tabledata "Connection Setup" = rm; + DeleteAllowed = false; + InsertAllowed = false; layout { @@ -21,59 +23,56 @@ page 6380 "Connection Setup Card" { group(General) { - field(ClientID; ClientID) + field(ClientID; this.ClientID) { Caption = 'Client ID'; - ToolTip = 'Specifies the client ID token.'; + ToolTip = 'Specifies the client ID.'; ApplicationArea = Basic, Suite; ExtendedDatatype = Masked; -#if not DOCKER - Visible = not IsSaaS; -#endif ShowMandatory = true; trigger OnValidate() begin - TietoevryAuth.SetClientId(Rec."Client ID", ClientID); + this.TietoevryAuth.SetClientId(Rec."Client ID - Key", this.ClientID); end; } - field(ClientSecret; ClientSecret) + field(ClientSecret; this.ClientSecret) { Caption = 'Client Secret'; - ToolTip = 'Specifies the client secret token.'; + ToolTip = 'Specifies the client secret.'; ApplicationArea = Basic, Suite; ExtendedDatatype = Masked; -#if not DOCKER - Visible = not IsSaaS; -#endif ShowMandatory = true; trigger OnValidate() begin - TietoevryAuth.SetClientSecret(Rec."Client Secret", ClientSecret); + this.TietoevryAuth.SetClientSecret(Rec."Client Secret - Key", this.ClientSecret); end; } field("Authentication URL"; Rec."Authentication URL") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the URL to connect to Tietoevry Online.'; - Visible = not IsSaaS; + ToolTip = 'Specifies the URL to connect to Avalara.'; } - field("Inbound API Url"; Rec."Inbound API URL") + field("API URL"; Rec."API URL") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the inbound API URL.'; + ToolTip = 'Specifies the URL to connect to Avalara''s api.'; } - field("Outbound API Url"; Rec."Outbound API URL") + field("Sandbox Authentication URL"; Rec."Sandbox Authentication URL") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the outbound API URL.'; + ToolTip = 'Specifies the URL to connect to Avalara sandbox.'; + } + field("Sandbox API URL"; Rec."Sandbox API URL") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the URL to connect to Avalara sandbox api.'; } field("Company Id"; Rec."Company Id") { ApplicationArea = Basic, Suite; ToolTip = 'Specifies the company ID.'; - ShowMandatory = true; } field("Send Mode"; Rec."Send Mode") { @@ -84,57 +83,53 @@ page 6380 "Connection Setup Card" } } } - actions { area(processing) { - action(TestOAuthSetup) + action(Authenticate) { ApplicationArea = Basic, Suite; - Caption = 'Test OAuth 2.0 setup'; + Caption = 'Authenticate'; Image = Setup; Promoted = true; PromotedCategory = Process; PromotedOnly = true; - ToolTip = 'Tests the OAuth 2.0 setup.'; + ToolTip = 'Verify the Authentication Details'; trigger OnAction() var TietoevryAuth: Codeunit Authenticator; + [NonDebuggable] + Token: SecretText; begin - TietoevryAuth.TestOAuth2Setup(); - FeatureTelemetry.LogUptake('0000MSD', ExternalServiceTok, Enum::"Feature Uptake Status"::"Set up"); + Token := TietoevryAuth.GetAccessToken(); + if not Token.IsEmpty() then + Message(this.AuthSuccessMsg); end; } } } - trigger OnOpenPage() var - EnvironmentInfo: Codeunit "Environment Information"; - + FeatureTelemetry: Codeunit "Feature Telemetry"; begin - IsSaaS := EnvironmentInfo.IsSaaS(); - - TietoevryAuth.InitConnectionSetup(); - TietoevryAuth.IsClientCredsSet(ClientID, ClientSecret); - - FeatureTelemetry.LogUptake('0000LST', ExternalServiceTok, Enum::"Feature Uptake Status"::Discovered); + FeatureTelemetry.LogUptake('0000NHL', this.TietoevryProcessing.GetTietoevryTok(), Enum::"Feature Uptake Status"::Discovered); + this.TietoevryAuth.CreateConnectionSetupRecord(); + this.TietoevryAuth.IsClientCredsSet(this.ClientID, this.ClientSecret); end; trigger OnClosePage() - var begin Rec.TestField("Company Id"); Rec.TestField("Send Mode"); end; var - TietoevryAuth: Codeunit Authenticator; - FeatureTelemetry: Codeunit "Feature Telemetry"; + + TietoevryAuth: Codeunit "Authenticator"; + TietoevryProcessing: Codeunit Processing; + AuthSuccessMsg: Label 'Authenticated successfully'; [NonDebuggable] ClientID, ClientSecret : Text; - IsSaaS: Boolean; - ExternalServiceTok: Label 'ExternalServiceConnector', Locked = true; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al similarity index 62% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al index 3f4e1c282..b9e6811c2 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al @@ -9,10 +9,8 @@ using Microsoft.Sales.Document; using Microsoft.Foundation.Company; using Microsoft.Sales.Customer; using Microsoft.eServices.EDocument; -using System.Utilities; using Microsoft.eServices.EDocument.Service.Participant; - -codeunit 6380 "Connection Events" +codeunit 6395 Events { SingleInstance = true; EventSubscriberInstance = StaticAutomatic; @@ -27,7 +25,7 @@ codeunit 6380 "Connection Events" IsHandled := true; if CompanyInformation."VAT Registration No." = '' then - Error(MissingCompInfVATRegNoErr, CompanyInformation.TableCaption()); + Error(this.MissingCompInfVATRegNoErr, CompanyInformation.TableCaption()); end; [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Validation", OnCheckSalesDocumentOnBeforeCheckCustomerVATRegNo, '', false, false)] @@ -39,7 +37,7 @@ codeunit 6380 "Connection Events" begin if not EDocExtConnectionSetup.Get() then exit; - EDocumentService.SetRange("Service Integration", EDocumentService."Service Integration"::Tietoevry); + EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); if not EDocumentService.FindFirst() then exit; @@ -48,48 +46,13 @@ codeunit 6380 "Connection Events" Customer.Get(SalesHeader."Bill-to Customer No.") then if Customer."VAT Registration No." = '' then - Error(MissingCustInfoErr, Customer.FieldCaption("VAT Registration No."), Customer."No."); + Error(this.MissingCustInfoErr, Customer.FieldCaption("VAT Registration No."), Customer."No."); if not ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No.") then ServiceParticipant.Init(); if ServiceParticipant."Participant Identifier" = '' then - Error(MissingCustInfoErr, ServiceParticipant.FieldCaption("Participant Identifier"), Customer."No."); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Import", 'OnAfterInsertImportedEdocument', '', false, false)] - local procedure OnAfterInsertEdocument(var EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; var TempBlob: Codeunit "Temp Blob"; EDocCount: Integer; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage) - var - LocalHttpRequest: HttpRequestMessage; - LocalHttpResponse: HttpResponseMessage; - DocumentOutStream: OutStream; - ContentData, MessageId : Text; - begin - if EDocumentService."Service Integration" <> EDocumentService."Service Integration"::Tietoevry then - exit; - - HttpResponse.Content.ReadAs(ContentData); - if not TietoevryProcessing.ParseReceivedDocument(ContentData, EDocument."Index In Batch", MessageId) then begin - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, DocumentIdNotFoundErr); - exit; - end; - - TietoevryConnection.HandleGetTargetDocumentRequest(MessageId, LocalHttpRequest, LocalHttpResponse, false); - EDocumentLogHelper.InsertIntegrationLog(EDocument, EDocumentService, LocalHttpRequest, LocalHttpResponse); - - LocalHttpResponse.Content.ReadAs(ContentData); - if ContentData = '' then - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(CouldNotRetrieveDocumentErr, MessageId)); - - Clear(TempBlob); - TempBlob.CreateOutStream(DocumentOutStream, TextEncoding::UTF8); - DocumentOutStream.WriteText(ContentData); - - TietoevryProcessing.AcknowledgeEDocument(EDocument, EDocumentService, MessageId); - - EDocument."Message Id" := CopyStr(MessageId, 1, MaxStrLen(EDocument."Message Id")); - - EDocumentLogHelper.InsertLog(EDocument, EDocumentService, TempBlob, "E-Document Service Status"::Imported); + Error(this.MissingCustInfoErr, ServiceParticipant.FieldCaption("Participant Identifier"), Customer."No."); end; [EventSubscriber(ObjectType::Table, Database::"E-Document Service", 'OnAfterValidateEvent', 'Document Format', false, false)] @@ -97,7 +60,7 @@ codeunit 6380 "Connection Events" var EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; begin - if Rec."Document Format" <> Rec."Document Format"::"TE PEPPOL BIS 3.0" then + if Rec."Document Format" <> Rec."Document Format"::"Tietoevry PEPPOL BIS 3.0" then exit; EDocServiceSupportedType.SetRange("E-Document Service Code", Rec.Code); @@ -118,6 +81,15 @@ codeunit 6380 "Connection Events" end; end; + [EventSubscriber(ObjectType::Table, Database::"E-Document Service", 'OnAfterValidateEvent', "Service Integration V2", false, false)] + local procedure OnAfterValidateServiceIntegrationV2(var Rec: Record "E-Document Service"; var xRec: Record "E-Document Service"; CurrFieldNo: Integer) + begin + if Rec."Service Integration V2" <> Rec."Service Integration V2"::Tietoevry then + exit; + Rec.Validate("Document Format", Rec."Document Format"::"Tietoevry PEPPOL BIS 3.0"); + Rec.Modify(true); + end; + [EventSubscriber(ObjectType::Table, Database::"Service Participant", 'OnAfterValidateEvent', 'Participant Identifier', false, false)] local procedure OnAfterValidateServiceParticipant(var Rec: Record "Service Participant"; var xRec: Record "Service Participant"; CurrFieldNo: Integer) var @@ -125,24 +97,19 @@ codeunit 6380 "Connection Events" begin if not EDocumentService.Get(Rec.Service) then exit; - if EDocumentService."Service Integration" <> EDocumentService."Service Integration"::Tietoevry then + if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then exit; if Rec."Participant Identifier" <> '' then - if not TietoevryProcessing.IsValidSchemeId(Rec."Participant Identifier") then + if not this.TietoevryProcessing.IsValidSchemeId(Rec."Participant Identifier") then Rec.FieldError(Rec."Participant Identifier"); end; var - TietoevryConnection: Codeunit "Connection"; TietoevryProcessing: Codeunit "Processing"; - EDocumentLogHelper: Codeunit "E-Document Log Helper"; - EDocumentErrorHelper: Codeunit "E-Document Error Helper"; #pragma warning disable AA0470 MissingCompInfVATRegNoErr: Label 'You must specify VAT Registration No. in %1.', Comment = '%1=Company Information'; #pragma warning restore AA0470 #pragma warning disable AA0470 MissingCustInfoErr: Label 'You must specify %1 for Customer %2.', Comment = '%1=Fieldcaption %2=Customer No.'; #pragma warning restore AA0470 - CouldNotRetrieveDocumentErr: Label 'Could not retrieve document with id: %1 from the service', Comment = '%1 - Document ID'; - DocumentIdNotFoundErr: Label 'Document ID not found in response'; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al similarity index 72% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al index e022ca745..a12921e2b 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al @@ -6,10 +6,10 @@ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.EServices.EDocument; -enumextension 6381 "E-Doc. Ext. Integration" extends "E-Document Integration" +enumextension 6390 "E-Doc. Format Ext." extends "E-Document Format" { - value(6381; "Tietoevry") + value(6390; "Tietoevry PEPPOL BIS 3.0") { - Implementation = "E-Document Integration" = "Integration Impl."; + Implementation = "E-Document" = "Tietoevry E-Document"; } } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/tableextension/Tab-Ext6382.EDocTEExtEDocument.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al similarity index 64% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/tableextension/Tab-Ext6382.EDocTEExtEDocument.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al index 24e0ae642..bd5acc039 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/tableextension/Tab-Ext6382.EDocTEExtEDocument.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al @@ -2,31 +2,31 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector; +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocument; +using Microsoft.eServices.EDocument; -tableextension 6382 "E-Doc. TE Ext. EDocument" extends "E-Document" +tableextension 6390 "E-Document" extends "E-Document" { fields { - field(6380; "Bill-to/Pay-to Id"; Text[100]) + field(6390; "Bill-to/Pay-to Id"; Text[100]) { DataClassification = CustomerContent; } - field(6381; "Message Id"; Text[50]) + field(6391; "Message Id"; Text[50]) { DataClassification = CustomerContent; } - field(6382; "Message Profile Id"; Text[50]) + field(6392; "Message Profile Id"; Text[50]) { DataClassification = CustomerContent; } - field(6383; "Message Document Id"; Text[200]) + field(6393; "Message Document Id"; Text[200]) { DataClassification = CustomerContent; } - field(6384; "Receiving Company Id"; Text[100]) + field(6394; "Receiving Company Id"; Text[100]) { DataClassification = CustomerContent; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al new file mode 100644 index 000000000..2aa1e8f37 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using Microsoft.Sales.Peppol; +using Microsoft.Sales.Document; +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Service.Participant; + +codeunit 6398 "Format Events" +{ + + SingleInstance = true; + EventSubscriberInstance = StaticAutomatic; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingSupplierPartyInfoByFormat, '', false, false)] + local procedure "PEPPOL Management_OnAfterGetAccountingSupplierPartyInfoByFormat"(var SupplierEndpointID: Text; var SupplierSchemeID: Text; var SupplierName: Text; IsBISBilling: Boolean) + var + EDocExtConnectionSetup: Record "Connection Setup"; + begin + if not IsBISBilling then + exit; + if not EDocExtConnectionSetup.Get() then + exit; + + this.SplitId(EDocExtConnectionSetup."Company Id", SupplierSchemeID, SupplierEndpointID); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingSupplierPartyLegalEntityByFormat, '', false, false)] + local procedure "PEPPOL Management_OnAfterGetAccountingSupplierPartyLegalEntityByFormat"(var PartyLegalEntityRegName: Text; var PartyLegalEntityCompanyID: Text; var PartyLegalEntitySchemeID: Text; var SupplierRegAddrCityName: Text; var SupplierRegAddrCountryIdCode: Text; var SupplRegAddrCountryIdListId: Text; IsBISBilling: Boolean) + var + EDocExtConnectionSetup: Record "Connection Setup"; + begin + if not IsBISBilling then + exit; + if not EDocExtConnectionSetup.Get() then + exit; + + this.SplitId(EDocExtConnectionSetup."Company Id", PartyLegalEntitySchemeID, PartyLegalEntityCompanyID); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingCustomerPartyInfoByFormat, '', false, false)] + local procedure "PEPPOL Management_OnAfterGetAccountingCustomerPartyInfoByFormat"(SalesHeader: Record "Sales Header"; var CustomerEndpointID: Text; var CustomerSchemeID: Text; var CustomerPartyIdentificationID: Text; var CustomerPartyIDSchemeID: Text; var CustomerName: Text; IsBISBilling: Boolean) + var + ServiceParticipant: Record "Service Participant"; + EDocumentService: Record "E-Document Service"; + EDocExtConnectionSetup: Record "Connection Setup"; + begin + if not IsBISBilling then + exit; + if not EDocExtConnectionSetup.Get() then + exit; + EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); + if not EDocumentService.FindFirst() then + exit; + ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No."); + this.SplitId(ServiceParticipant."Participant Identifier", CustomerSchemeID, CustomerEndpointID); + this.SplitId(ServiceParticipant."Participant Identifier", CustomerPartyIDSchemeID, CustomerPartyIdentificationID); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingCustomerPartyLegalEntityByFormat, '', false, false)] + local procedure "PEPPOL Management_OnAfterGetAccountingCustomerPartyLegalEntityByFormat"(SalesHeader: Record "Sales Header"; var CustPartyLegalEntityRegName: Text; var CustPartyLegalEntityCompanyID: Text; var CustPartyLegalEntityIDSchemeID: Text; IsBISBilling: Boolean) + var + ServiceParticipant: Record "Service Participant"; + EDocumentService: Record "E-Document Service"; + EDocExtConnectionSetup: Record "Connection Setup"; + begin + if not IsBISBilling then + exit; + if not EDocExtConnectionSetup.Get() then + exit; + EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); + if not EDocumentService.FindFirst() then + exit; + + ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No."); + this.SplitId(ServiceParticipant."Participant Identifier", CustPartyLegalEntityIDSchemeID, CustPartyLegalEntityCompanyID); + end; + + + local procedure SplitId(Input: Text; var SchemeId: Text; var EndpointId: Text) + var + Parts: List of [Text]; + begin + Parts := Input.Split(':'); + SchemeId := Parts.Get(1); + EndpointId := Parts.Get(2); + end; +} diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.al similarity index 78% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.al index 620f6ad75..089b0ebcd 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.al @@ -1,18 +1,21 @@ -namespace Microsoft.eServices.EDocument.IO.Peppol; +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.eServices.EDocument; -using System.Utilities; -using Microsoft.Sales.Peppol; -using Microsoft.Purchases.Document; -using Microsoft.Service.History; using Microsoft.Sales.Document; using Microsoft.Sales.History; +using Microsoft.Service.History; +using Microsoft.Sales.Peppol; using System.IO; using Microsoft.eServices.EDocument.Service.Participant; +using Microsoft.Purchases.Document; -codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" +codeunit 6391 "Tietoevry E-Document" implements "E-Document" { - procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: Enum "E-Document Processing Phase") + procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: enum Microsoft.eServices.EDocument."E-Document Processing Phase") var SalesHeader: Record "Sales Header"; SalesInvoiceHeader: Record "Sales Invoice Header"; @@ -51,7 +54,7 @@ codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" end; end; - procedure Create(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + procedure Create(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: codeunit System.Utilities."Temp Blob") var ServiceParticipant: Record "Service Participant"; TempXMLBuffer: Record "XML Buffer" temporary; @@ -63,11 +66,11 @@ codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" TempBlob.CreateOutStream(DocOutStream); case EDocument."Document Type" of EDocument."Document Type"::"Sales Invoice", EDocument."Document Type"::"Service Invoice": - GenerateInvoiceXMLFile(SourceDocumentHeader, DocOutStream); + this.GenerateInvoiceXMLFile(SourceDocumentHeader, DocOutStream); EDocument."Document Type"::"Sales Credit Memo", EDocument."Document Type"::"Service Credit Memo": - GenerateCrMemoXMLFile(SourceDocumentHeader, DocOutStream); + this.GenerateCrMemoXMLFile(SourceDocumentHeader, DocOutStream); else - EDocErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(DocumentTypeNotSupportedErr, EDocument.FieldCaption("Document Type"), EDocument."Document Type")); + EDocErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(this.DocumentTypeNotSupportedErr, EDocument.FieldCaption("Document Type"), EDocument."Document Type")); end; EDocument.Find(); @@ -100,22 +103,22 @@ codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" EDocument.Modify(); end; - procedure CreateBatch(EDocService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeaders: RecordRef; var SourceDocumentsLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + procedure CreateBatch(EDocumentService: Record "E-Document Service"; var EDocuments: Record "E-Document"; var SourceDocumentHeaders: RecordRef; var SourceDocumentsLines: RecordRef; var TempBlob: codeunit System.Utilities."Temp Blob") begin end; - procedure GetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") + procedure GetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: codeunit System.Utilities."Temp Blob") begin - ImportTEPeppol.ParseBasicInfo(EDocument, TempBlob); + this.ImportTEPeppol.ParseBasicInfo(EDocument, TempBlob); end; - procedure GetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + procedure GetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: codeunit System.Utilities."Temp Blob") var TempPurchaseHeader: Record "Purchase Header" temporary; TempPurchaseLine: Record "Purchase Line" temporary; begin - ImportTEPeppol.ParseCompleteInfo(EDocument, TempPurchaseHeader, TempPurchaseLine, TempBlob); + this.ImportTEPeppol.ParseCompleteInfo(EDocument, TempPurchaseHeader, TempPurchaseLine, TempBlob); CreatedDocumentHeader.GetTable(TempPurchaseHeader); CreatedDocumentLines.GetTable(TempPurchaseLine); @@ -140,6 +143,6 @@ codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" end; var - ImportTEPeppol: Codeunit "EDoc Import Tietoevry"; + ImportTEPeppol: Codeunit "Tietoevry E-Document Import"; DocumentTypeNotSupportedErr: Label '%1 %2 is not supported by PEPPOL BIS30 Format', Comment = '%1 - Document Type caption, %2 - Document Type'; -} \ No newline at end of file +} diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al similarity index 82% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al index 38c87f79a..ca954b654 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al @@ -1,7 +1,10 @@ -namespace Microsoft.eServices.EDocument.IO.Peppol; +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.eServices.EDocument; -using Microsoft.Sales.Customer; using System.Utilities; using Microsoft.Purchases.Document; using System.IO; @@ -11,9 +14,8 @@ using Microsoft.Purchases.Vendor; using Microsoft.Finance.GeneralLedger.Setup; using Microsoft.eServices.EDocument.Service.Participant; -codeunit 6386 "EDoc Import Tietoevry" +codeunit 6393 "Tietoevry E-Document Import" { - procedure ParseBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") var TempXMLBuffer: Record "XML Buffer" temporary; @@ -25,15 +27,15 @@ codeunit 6386 "EDoc Import Tietoevry" TempXMLBuffer.LoadFromStream(DocStream); GLSetup.Get(); - LCYCode := GLSetup."LCY Code"; + this.LCYCode := GLSetup."LCY Code"; EDocument.Direction := EDocument.Direction::Incoming; case UpperCase(GetDocumentType(TempXMLBuffer)) of 'INVOICE': - ParseInvoiceBasicInfo(EDocument, TempXMLBuffer); + this.ParseInvoiceBasicInfo(EDocument, TempXMLBuffer); 'CREDITNOTE': - ParseCreditMemoBasicInfo(EDocument, TempXMLBuffer); + this.ParseCreditMemoBasicInfo(EDocument, TempXMLBuffer); end; end; @@ -51,9 +53,9 @@ codeunit 6386 "EDoc Import Tietoevry" case UpperCase(GetDocumentType(TempXMLBuffer)) of 'INVOICE': - CreateInvoice(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + this.CreateInvoice(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); 'CREDITNOTE': - CreateCreditMemo(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + this.CreateCreditMemo(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); end; end; @@ -64,23 +66,23 @@ codeunit 6386 "EDoc Import Tietoevry" begin EDocument."Document Type" := EDocument."Document Type"::"Purchase Invoice"; EDocument."Incoming E-Document No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); - ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'Invoice'); - ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'Invoice'); + this.ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'Invoice'); + this.ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'Invoice'); - DueDate := GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DueDate'); + DueDate := this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DueDate'); if DueDate <> '' then Evaluate(EDocument."Due Date", DueDate, 9); - IssueDate := GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:IssueDate'); + IssueDate := this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:IssueDate'); if IssueDate <> '' then Evaluate(EDocument."Document Date", IssueDate, 9); - Evaluate(EDocument."Amount Excl. VAT", GetNodeByPath(TempXMLBuffer, '/Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); - Evaluate(EDocument."Amount Incl. VAT", GetNodeByPath(TempXMLBuffer, '/Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); + Evaluate(EDocument."Amount Excl. VAT", this.GetNodeByPath(TempXMLBuffer, '/Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); + Evaluate(EDocument."Amount Incl. VAT", this.GetNodeByPath(TempXMLBuffer, '/Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); - EDocument."Order No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/Invoice/cac:OrderReference/cbc:ID'), 1, MaxStrLen(EDocument."Order No.")); + EDocument."Order No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cac:OrderReference/cbc:ID'), 1, MaxStrLen(EDocument."Order No.")); - Currency := CopyStr(GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); - if LCYCode <> Currency then + Currency := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); + if this.LCYCode <> Currency then EDocument."Currency Code" := Currency; end; @@ -91,13 +93,13 @@ codeunit 6386 "EDoc Import Tietoevry" begin EDocument."Document Type" := EDocument."Document Type"::"Purchase Credit Memo"; EDocument."Incoming E-Document No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); - ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'CreditNote'); - ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'CreditNote'); + this.ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'CreditNote'); + this.ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'CreditNote'); - DueDate := GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate'); + DueDate := this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate'); if DueDate <> '' then Evaluate(EDocument."Due Date", DueDate, 9); - IssueDate := GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:IssueDate'); + IssueDate := this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:IssueDate'); if IssueDate <> '' then Evaluate(EDocument."Document Date", IssueDate, 9); @@ -105,7 +107,7 @@ codeunit 6386 "EDoc Import Tietoevry" Evaluate(EDocument."Amount Incl. VAT", GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); Currency := CopyStr(GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); - if LCYCode <> Currency then + if this.LCYCode <> Currency then EDocument."Currency Code" := Currency; end; @@ -117,21 +119,21 @@ codeunit 6386 "EDoc Import Tietoevry" VendorId: Text[50]; VendorNo: Code[20]; begin - VendorId := CopyStr(GetNodeAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID'), 1, MaxStrLen(VendorId)) + ':'; - VendorId += CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(VendorId) - StrLen(VendorId)); + VendorId := CopyStr(this.GetNodeAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID'), 1, MaxStrLen(VendorId)) + ':'; + VendorId += CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(VendorId) - StrLen(VendorId)); ServiceParticipant.SetRange("Participant Type", ServiceParticipant."Participant Type"::Vendor); ServiceParticipant.SetRange("Participant Identifier", VendorId); if ServiceParticipant.FindFirst() then VendorNo := ServiceParticipant.Participant; if VendorNo = '' then begin - VendorName := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name'), 1, MaxStrLen(VendorName)); - VendorAddress := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(VendorAddress)); - VendorNo := EDocumentImportHelper.FindVendorByNameAndAddress(VendorName, VendorAddress); + VendorName := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name'), 1, MaxStrLen(VendorName)); + VendorAddress := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(VendorAddress)); + VendorNo := this.EDocumentImportHelper.FindVendorByNameAndAddress(VendorName, VendorAddress); EDocument."Bill-to/Pay-to Name" := CopyStr(VendorName, 1, MaxStrLen(EDocument."Bill-to/Pay-to Name")); end; - Vendor := EDocumentImportHelper.GetVendor(EDocument, VendorNo); + Vendor := this.EDocumentImportHelper.GetVendor(EDocument, VendorNo); if Vendor."No." <> '' then begin EDocument."Bill-to/Pay-to No." := Vendor."No."; EDocument."Bill-to/Pay-to Name" := Vendor.Name; @@ -142,12 +144,12 @@ codeunit 6386 "EDoc Import Tietoevry" var ReceivingId: Text[50]; begin - EDocument."Receiving Company Name" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name'), 1, MaxStrLen(EDocument."Receiving Company Name")); - EDocument."Receiving Company Address" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(EDocument."Receiving Company Address")); - ReceivingId := CopyStr(GetNodeAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID'), 1, MaxStrLen(EDocument."Receiving Company Id")) + ':'; - ReceivingId += CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(EDocument."Receiving Company Id") - StrLen(ReceivingId)); + EDocument."Receiving Company Name" := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name'), 1, MaxStrLen(EDocument."Receiving Company Name")); + EDocument."Receiving Company Address" := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(EDocument."Receiving Company Address")); + ReceivingId := CopyStr(this.GetNodeAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID'), 1, MaxStrLen(EDocument."Receiving Company Id")) + ':'; + ReceivingId += CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(EDocument."Receiving Company Id") - StrLen(ReceivingId)); EDocument."Receiving Company Id" := ReceivingId; - EDocument."Receiving Company VAT Reg. No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID'), 1, MaxStrLen(EDocument."Receiving Company VAT Reg. No.")); + EDocument."Receiving Company VAT Reg. No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID'), 1, MaxStrLen(EDocument."Receiving Company VAT Reg. No.")); end; local procedure CreateInvoice(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary) @@ -157,15 +159,15 @@ codeunit 6386 "EDoc Import Tietoevry" InStream: InStream; begin PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::Invoice; - PurchaseHeader."No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); - PurchaseHeader."Vendor Order No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/Invoice/cac:OrderReference/cbc:ID'), 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); + PurchaseHeader."No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); + PurchaseHeader."Vendor Order No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cac:OrderReference/cbc:ID'), 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); // Currency PurchaseHeader.Insert(); TempXMLBuffer.Reset(); if TempXMLBuffer.FindSet() then repeat - ParseInvoice(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); + this.ParseInvoice(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); until TempXMLBuffer.Next() = 0; // Insert last document attachment @@ -180,7 +182,7 @@ codeunit 6386 "EDoc Import Tietoevry" PurchaseHeader.Modify(); // Allowance charge - CreateInvoiceAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + this.CreateInvoiceAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); end; local procedure CreateCreditMemo(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary) @@ -196,7 +198,7 @@ codeunit 6386 "EDoc Import Tietoevry" TempXMLBuffer.Reset(); if TempXMLBuffer.FindSet() then repeat - ParseCreditMemo(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); + this.ParseCreditMemo(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); until TempXMLBuffer.Next() = 0; // Insert last document attachment @@ -211,7 +213,7 @@ codeunit 6386 "EDoc Import Tietoevry" PurchaseHeader.Modify(); // Allowance charge - CreateInvoiceAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + this.CreateInvoiceAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); end; local procedure CreateInvoiceAllowanceChargeLines(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary) @@ -237,7 +239,7 @@ codeunit 6386 "EDoc Import Tietoevry" case TempXMLBuffer.Path of DocumentText + '/cac:AllowanceCharge/cbc:ChargeIndicator': if TempXMLBuffer.Value = 'true' then begin - SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + this.SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); PurchaseLine.Init(); PurchaseLine."Document Type" := PurchaseHeader."Document Type"; @@ -257,7 +259,7 @@ codeunit 6386 "EDoc Import Tietoevry" end; until TempXMLBuffer.Next() = 0; - SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + this.SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); end; local procedure SetGLAccountAndInsertLine(var EDocument: Record "E-Document"; var PurchaseLine: record "Purchase Line" temporary; var LineNo: Integer) @@ -266,7 +268,7 @@ codeunit 6386 "EDoc Import Tietoevry" begin if PurchaseLine."Line No." = LineNo then begin RecRef.GetTable(PurchaseLine); - EDocumentImportHelper.FindGLAccountForLine(EDocument, RecRef); + this.EDocumentImportHelper.FindGLAccountForLine(EDocument, RecRef); PurchaseLine."No." := RecRef.Field(PurchaseLine.FieldNo("No.")).Value; PurchaseLine.Insert(); LineNo += 10000; @@ -382,7 +384,7 @@ codeunit 6386 "EDoc Import Tietoevry" if Value <> '' then Evaluate(PurchaseLine."Quantity (Base)", Value, 9); '/CreditNote/cac:CreditNoteLine/cbc:Note': - setlineType(PurchaseLine, Value); + this.SetlineType(PurchaseLine, Value); end end; @@ -431,7 +433,7 @@ codeunit 6386 "EDoc Import Tietoevry" begin if DocumentAttachment."No." <> '' then begin DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + //EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); Clear(DocumentAttachment); end; @@ -494,7 +496,7 @@ codeunit 6386 "EDoc Import Tietoevry" if Value <> '' then Evaluate(PurchaseLine."Quantity (Base)", Value, 9); '/Invoice/cac:InvoiceLine/cbc:Note': - setlineType(PurchaseLine, Value); + this.SetlineType(PurchaseLine, Value); end; end; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al new file mode 100644 index 000000000..638bd7eaa --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al @@ -0,0 +1,119 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using System.Telemetry; + +/// +/// Execute http requests for Tietoevry API +/// +codeunit 6397 "Http Executor" +{ + + Access = Internal; + + /// + /// Execute http calls. Handle response with error logging. + /// + procedure ExecuteHttpRequest(var Request: Codeunit Requests) Response: Text + var + HttpResponse: HttpResponseMessage; + begin + exit(ExecuteHttpRequest(Request, HttpResponse)); + end; + + /// + /// Execute http calls. Handle response with error logging and store response in HttpResponse + /// + procedure ExecuteHttpRequest(var Request: Codeunit Requests; HttpResponse: HttpResponseMessage) Response: Text + var + FeatureTelemetry: Codeunit "Feature Telemetry"; + HttpClient: HttpClient; + begin + FeatureTelemetry.LogUptake('0000NH9', this.AvalaraProcessing.GetTietoevryTok(), Enum::"Feature Uptake Status"::Used); + FeatureTelemetry.LogUsage('0000NHA', this.AvalaraProcessing.GetTietoevryTok(), 'Tietoevry request.'); + + HttpClient.Send(Request.GetRequest(), this.HttpResponseMessage); + HttpResponse := this.HttpResponseMessage; + HandleHttpResponse(this.HttpResponseMessage, Response); + end; + + /// + /// Return response from last http call + /// + procedure GetResponse(): HttpResponseMessage + begin + exit(this.HttpResponseMessage); + end; + + /// + /// Throw error for requests not of status 200 and 201. + /// + local procedure HandleHttpResponse(LocalHttpResponseMessage: HttpResponseMessage; var Response: Text) + var + FriendlyErrorMsg: Text; + begin + GetContent(LocalHttpResponseMessage, Response); + case LocalHttpResponseMessage.HttpStatusCode() of + 200: + begin + Session.LogMessage('0000NHB', HTTPSuccessMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.AvalaraProcessing.GetTietoevryTok()); + exit; + end; + 201: + begin + Session.LogMessage('0000NHC', HTTPSuccessAndCreatedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.AvalaraProcessing.GetTietoevryTok()); + exit; + end; + 400: + FriendlyErrorMsg := HTTPBadRequestMsg; + 401: + FriendlyErrorMsg := HTTPUnauthorizedMsg; + 402 .. 499: + if not Parse400Messages(Response, FriendlyErrorMsg) then + FriendlyErrorMsg := HTTPBadRequestMsg; + 500: + FriendlyErrorMsg := HTTPInternalServerErrorMsg; + 503: + FriendlyErrorMsg := HTTPServiceUnavailableMsg; + else + FriendlyErrorMsg := HTTPGeneralErrMsg; + end; + + FriendlyErrorMsg := StrSubstNo(HttpErrorMsg, LocalHttpResponseMessage.HttpStatusCode(), FriendlyErrorMsg); + Session.LogMessage('0000NHD', StrSubstNo(HttpErrorMsg, LocalHttpResponseMessage.HttpStatusCode(), Response), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.AvalaraProcessing.GetTietoevryTok()); + Error(FriendlyErrorMsg); + end; + + [TryFunction] + local procedure Parse400Messages(Content: Text; var Message: Text) + var + ResponseJson: JsonObject; + JsonToken: JsonToken; + begin + ResponseJson.ReadFrom(Content); + ResponseJson.Get('message', JsonToken); + Message := JsonToken.AsValue().AsText(); + end; + + [TryFunction] + local procedure GetContent(HttpResponseMsg: HttpResponseMessage; var Response: Text) + begin + HttpResponseMsg.Content.ReadAs(Response); + end; + + var + AvalaraProcessing: Codeunit Processing; + HttpResponseMessage: HttpResponseMessage; + HTTPSuccessMsg: Label 'The HTTP request was successful and the body contains the resource fetched.'; // 200 + HTTPSuccessAndCreatedMsg: Label 'The HTTP request was successful and a new resource was created.'; //201 + HTTPBadRequestMsg: Label 'The HTTP request was incorrectly formed or invalid.'; // 400 + HTTPUnauthorizedMsg: Label 'The HTTP request is not authorized. Authentication credentials are not valid.'; // 401 + HTTPInternalServerErrorMsg: Label 'The HTTP request is not successful. An internal server error occurred.'; // 500 + HTTPServiceUnavailableMsg: Label 'The HTTP request is not successful. The service is unavailable.'; // 503 + HTTPGeneralErrMsg: Label 'Something went wrong, try again later.'; + HttpErrorMsg: Label 'Error Code: %1, Error Message: %2', Comment = '%1 = Error Code, %2 = Error Message'; + +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al new file mode 100644 index 000000000..e2e3d6fce --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al @@ -0,0 +1,18 @@ +#if not CLEAN26 +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +// +// Intentionally left as documentation for the upgrade codeunit +// +// enumextension 6390 Integration extends "E-Document Integration" +// { +// // Leave commented out as 6390 is used in Upgrade Codeunit +// // value(6390; "Tietoevry") +// // { +// // Implementation = "E-Document Integration" = "E-Document No Integration"; +// // } +// } +#endif \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al new file mode 100644 index 000000000..b9623c460 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using System.Utilities; +using Microsoft.EServices.EDocument; +using Microsoft.eServices.EDocument.Integration.Send; +using Microsoft.eServices.EDocument.Integration.Receive; +using Microsoft.eServices.EDocument.Integration.Interfaces; + +codeunit 6392 "Integration Impl." implements IDocumentSender, IDocumentResponseHandler, IDocumentReceiver +{ + Access = Internal; + + procedure Send(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext) + begin + this.TietoevryProcessing.SendEDocument(EDocument, EDocumentService, SendContext); + end; + + procedure GetResponse(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext): Boolean + begin + exit(this.TietoevryProcessing.GetDocumentStatus(EDocument, SendContext)); + end; + + procedure ReceiveDocuments(var EDocumentService: Record "E-Document Service"; ReceivedEDocuments: Codeunit "Temp Blob List"; ReceiveContext: Codeunit ReceiveContext) + begin + this.TietoevryProcessing.ReceiveDocuments(EDocumentService, ReceivedEDocuments, ReceiveContext); + end; + + procedure DownloadDocument(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; DocumentMetadataBlob: codeunit "Temp Blob"; ReceiveContext: Codeunit ReceiveContext) + begin + this.TietoevryProcessing.DownloadDocument(EDocument, EDocumentService, DocumentMetadataBlob, ReceiveContext); + this.TietoevryProcessing.AcknowledgeDocument(EDocument, EDocumentService, DocumentMetadataBlob, ReceiveContext); + end; + + [EventSubscriber(ObjectType::Page, Page::"E-Document Service", OnBeforeOpenServiceIntegrationSetupPage, '', false, false)] + local procedure OnBeforeOpenServiceIntegrationSetupPage(EDocumentService: Record "E-Document Service"; var SetupPage: Integer) + begin + if EDocumentService."Service Integration V2" = EDocumentService."Service Integration V2"::Tietoevry then + SetupPage := Page::"Connection Setup Card"; + end; + + var + TietoevryProcessing: Codeunit Processing; +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationV2.EnumExt.al similarity index 58% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationV2.EnumExt.al index 6d16b11ba..135d0f64f 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationV2.EnumExt.al @@ -4,16 +4,13 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -enum 6380 "E-Document Send Mode" -{ - Extensible = true; +using Microsoft.eServices.EDocument.Integration.Interfaces; +using Microsoft.eServices.EDocument.Integration; - value(0; Production) - { - Caption = 'Production'; - } - value(1; Certification) +enumextension 6391 IntegrationV2 extends "Service Integration" +{ + value(6390; "Tietoevry") { - Caption = 'Certification'; + Implementation = IDocumentSender = "Integration Impl.", IDocumentReceiver = "Integration Impl."; } } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6381.TEEDocConnRead.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Edit.PermissionSet.al similarity index 70% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6381.TEEDocConnRead.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Edit.PermissionSet.al index fc87ec1c2..9e40cbeed 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6381.TEEDocConnRead.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Edit.PermissionSet.al @@ -4,11 +4,12 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -permissionset 6381 "TE EDocConn. - Read" +permissionset 6392 "Tietoevry Edit" { Access = Public; Assignable = true; - IncludedPermissionSets = "TE EDoc. Conn. Objects"; + IncludedPermissionSets = "Tietoevry Read"; + Caption = 'Tietoevry E-Document Connector - Edit'; - Permissions = tabledata "Connection Setup" = R; + Permissions = tabledata "Connection Setup" = imd; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al new file mode 100644 index 000000000..b97ebb349 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +permissionset 6390 "Tietoevry Objects" +{ + Access = Public; + Assignable = false; + Caption = 'Tietoevry E-Document Connector - Objects'; + + Permissions = + table "Connection Setup" = X, + page "Connection Setup Card" = X, + codeunit "Integration Impl." = X, + codeunit Processing = X, + codeunit Authenticator = X, + codeunit Requests = X, + codeunit Events = X, + codeunit "Http Executor" = X, + codeunit "Tietoevry E-Document" = X, + codeunit "Tietoevry E-Document Import" = X; +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Read.PermissionSet.al similarity index 76% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Read.PermissionSet.al index 954c5a147..01cadadaf 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Read.PermissionSet.al @@ -4,11 +4,11 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -permissionset 6380 "TE EDocConn. - Edit" +permissionset 6391 "Tietoevry Read" { Access = Public; Assignable = true; - IncludedPermissionSets = "TE EDocConn. - Read"; + Caption = 'Tietoevry E-Document Connector - Read'; - Permissions = tabledata "Connection Setup" = IM; + Permissions = tabledata "Connection Setup" = r; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al similarity index 73% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al index 61be74307..39e1ff0e2 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al @@ -4,10 +4,9 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using System.Security.AccessControl; using Microsoft.EServices.EDocumentConnector; -permissionsetextension 6381 "D365 Read - EDocument Connector" extends "D365 READ" +permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "EDocConnector - Edit" { - IncludedPermissionSets = "TE EDocConn. - Edit"; + IncludedPermissionSets = "Tietoevry Edit"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al similarity index 69% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al index ad69d987d..fa29a1114 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al @@ -3,10 +3,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; +using Microsoft.EServices.EDocumentConnector; -using System.Security.AccessControl; - -permissionsetextension 6380 "D365 Basic - EDocument Connector" extends "D365 BASIC" +permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "EDocConnector - Read" { - IncludedPermissionSets = "TE EDocConn. - Edit"; + IncludedPermissionSets = "Tietoevry Read"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al new file mode 100644 index 000000000..c3d25f9e8 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al @@ -0,0 +1,280 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using Microsoft.EServices.EDocument; +using System.Utilities; +using Microsoft.eServices.EDocument.Integration.Send; +using Microsoft.eServices.EDocument.Integration.Receive; + +codeunit 6399 Processing +{ + Access = Internal; + Permissions = tabledata "E-Document" = m, + tabledata "E-Document Service Status" = m, + tabledata "Connection Setup" = rm; + + + /// + /// Calls Tietoevry API for SubmitDocument. + /// + procedure SendEDocument(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext) + var + Request: Codeunit Requests; + HttpExecutor: Codeunit "Http Executor"; + TempBlob: Codeunit "Temp Blob"; + InStream: InStream; + RequestContent: Text; + ResponseContent: Text; + begin + TempBlob := SendContext.GetTempBlob(); + + TempBlob.CreateInStream(InStream, TextEncoding::UTF8); + InStream.Read(RequestContent); + + Request.Init(); + Request.Authenticate().CreateSubmitDocumentRequest(EDocument, RequestContent); + ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); + SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); + SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); + + EDocument.Get(EDocument."Entry No"); + EDocument."Document Id" := this.ParseDocumentId(ResponseContent); + EDocument.Modify(true); + end; + + /// + /// Calls Tietoevry API for GetDocumentStatus. + /// If request is successfull, but status is Error, then errors are logged and error is thrown to set document to Sending Error state + /// + /// False if status is Pending, True if status is Complete. + procedure GetDocumentStatus(var EDocument: Record "E-Document"; SendContext: Codeunit SendContext): Boolean + var + Request: Codeunit Requests; + HttpExecutor: Codeunit "Http Executor"; + ResponseContent: Text; + begin + EDocument.TestField("Document Id"); + + Request.Init(); + Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Document Id"); + ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); + SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); + SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); + exit(this.ParseGetDocumentStatusResponse(EDocument, ResponseContent)); + end; + + /// + /// Get a list of messages to collect. + /// + procedure ReceiveDocuments(var EDocumentService: Record "E-Document Service"; ReceivedEDocuments: Codeunit "Temp Blob List"; ReceiveContext: Codeunit ReceiveContext) + var + TempBlob: Codeunit "Temp Blob"; + Request: Codeunit Requests; + HttpExecutor: Codeunit "Http Executor"; + ResponseContent: Text; + OutStream: OutStream; + HttpRequest: HttpRequestMessage; + HttpResponse: HttpResponseMessage; + Response: JsonArray; + ValueObject: JsonToken; + begin + Request.Init(); + Request.Authenticate().CreateReceiveDocumentsRequest(); + HttpRequest := Request.GetRequest(); + ResponseContent := HttpExecutor.ExecuteHttpRequest(Request, HttpResponse); + ReceiveContext.Http().SetHttpRequestMessage(HttpRequest); + ReceiveContext.Http().SetHttpResponseMessage(HttpResponse); + + Response.ReadFrom(ResponseContent); + this.RemoveExistingDocumentsFromResponse(Response); + + foreach ValueObject in Response do begin + Clear(TempBlob); + TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8); + OutStream.Write(ValueObject.AsValue().AsText()); + ReceivedEDocuments.Add(TempBlob); + end; + end; + + /// + /// Download document XML from Tietoevry API + /// + procedure DownloadDocument(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; DocumentMetadataBlob: codeunit "Temp Blob"; ReceiveContext: Codeunit ReceiveContext) + var + Request: Codeunit Requests; + HttpExecutor: Codeunit "Http Executor"; + ResponseContent: Text; + InStream: InStream; + DocumentId: Text; + OutStream: OutStream; + begin + DocumentMetadataBlob.CreateInStream(InStream, TextEncoding::UTF8); + InStream.ReadText(DocumentId); + + if DocumentId = '' then begin + this.EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, this.DocumentIdNotFoundErr); + exit; + end; + + EDocument."Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id")); + EDocument.Modify(); + + Request.Init(); + Request.Authenticate().CreateDownloadRequest(DocumentId); + ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); + ReceiveContext.Http().SetHttpRequestMessage(Request.GetRequest()); + ReceiveContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); + + ReceiveContext.GetTempBlob().CreateOutStream(OutStream, TextEncoding::UTF8); + OutStream.WriteText(ResponseContent); + end; + + /// + /// Mark document as read from Tietoevry API + /// + procedure AcknowledgeDocument(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; DocumentMetadataBlob: codeunit "Temp Blob"; ReceiveContext: Codeunit ReceiveContext) + var + Request: Codeunit Requests; + HttpExecutor: Codeunit "Http Executor"; + ResponseContent: Text; + begin + Request.Init(); + Request.Authenticate().CreateAcknowledgeRequest(EDocument."Document Id"); + ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); + ReceiveContext.Http().SetHttpRequestMessage(Request.GetRequest()); + ReceiveContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); + end; + + + /// + /// Remove document ids from array that are already created as E-Documents. + /// + local procedure RemoveExistingDocumentsFromResponse(var Documents: JsonArray) + var + DocumentId: Text; + I: Integer; + NewArray: JsonArray; + begin + for I := 0 to Documents.Count() - 1 do begin + DocumentId := this.GetDocumentIdFromArray(Documents, I); + if not this.DocumentExists(DocumentId) then + NewArray.Add(DocumentId); + end; + Documents := NewArray; + end; + + /// + /// Check if E-Document with Document Id exists in E-Document table + /// + local procedure DocumentExists(DocumentId: Text): Boolean + var + EDocument: Record "E-Document"; + begin + EDocument.SetRange("Document Id", DocumentId); + exit(not EDocument.IsEmpty()); + end; + + /// + /// Parse company id + /// + local procedure ParseDocumentId(ResponseMsg: Text): Text[50] + var + EDocument: Record "E-Document"; + DocumentId: Text; + ResponseJson: JsonObject; + ValueJson: JsonToken; + begin + ResponseJson.ReadFrom(ResponseMsg); + ResponseJson.Get('id', ValueJson); + + DocumentId := ValueJson.AsValue().AsText(); + if StrLen(DocumentId) > MaxStrLen(EDocument."Document Id") then + Error(this.TietoevryIdLongerErr); + + exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id"))); + end; + + /// + /// Parse Document Response. If erros log all events + /// + local procedure ParseGetDocumentStatusResponse(var EDocument: Record "E-Document"; ResponseMsg: Text): Boolean + var + ResponseJson: JsonObject; + ValueJson, EventToken : JsonToken; + begin + ResponseJson.ReadFrom(ResponseMsg); + ResponseJson.Get('id', ValueJson); + if EDocument."Document Id" <> ValueJson.AsValue().AsText() then + Error(this.IncorrectDocumentIdInResponseErr); + + ResponseJson.Get('status', ValueJson); + case UpperCase(ValueJson.AsValue().AsText()) of + 'PROCESSED': + exit(true); + 'PENDING': + exit(false); + 'FAILED': + begin + if not EventToken.ReadFrom(ResponseMsg) then begin + this.EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, this.TietoevryProcessingDocFailedErr); + exit(false); + end; + if ResponseJson.Get('details', ValueJson) then + this.EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, ValueJson.AsValue().AsText()); + + exit(false); + end; + else + exit(false); + end; + end; + + /// + /// Returns id from json array + /// + local procedure GetDocumentIdFromArray(DocumentArray: JsonArray; Index: Integer): Text + var + DocumentJsonToken, IdToken : JsonToken; + begin + DocumentArray.Get(Index, DocumentJsonToken); + DocumentJsonToken.AsObject().Get('id', IdToken); + exit(IdToken.AsValue().AsText()); + end; + + procedure GetTietoevryTok(): Text + begin + exit(this.TietoevryTok); + end; + + internal procedure IsValidSchemeId(PeppolId: Text[50]) Result: Boolean; + var + ValidSchemeId: Text; + ValidSchemeIdList: List of [Text]; + SplitSeparator: Text; + SchemeId: Text; + begin + SplitSeparator := ' '; + ValidSchemeId := ValidSchemeIdTxt; + ValidSchemeIdList := ValidSchemeId.Split(SplitSeparator); + + foreach SchemeId in ValidSchemeIdList do + if PeppolId.StartsWith(SchemeId) then + exit(true); + exit(false); + end; + + var + EDocumentErrorHelper: Codeunit "E-Document Error Helper"; + IncorrectDocumentIdInResponseErr: Label 'Document ID returned by API does not match E-Document.'; + DocumentIdNotFoundErr: Label 'Document ID not found in response.'; + TietoevryProcessingDocFailedErr: Label 'An error has been identified in the submitted document.'; + TietoevryIdLongerErr: Label 'Tietoevry returned id longer than supported by framework.'; + TietoevryTok: Label 'E-Document - Tietoevry', Locked = true; +#pragma warning disable AA0240 + ValidSchemeIdTxt: Label '0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0147 0151 0170 0183 0184 0188 0190 0191 0192 0193 0194 0195 0196 0198 0199 0200 0201 0202 0203 0204 0205 0208 0209 0210 0211 0212 0213 0215 0216 0217 0218 0219 0220 0221 0225 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959 AN AQ AS AU EM', Locked = true; +#pragma warning restore AA0240 + +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al new file mode 100644 index 000000000..edd74fadd --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -0,0 +1,242 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using Microsoft.EServices.EDocumentConnector; +using Microsoft.eServices.EDocument; +using System.Text; +using System.Reflection; + + +/// +/// Construct meta data object for Tietoevry request +/// +codeunit 6396 Requests +{ + + Access = Internal; + Permissions = tabledata "Connection Setup" = r; + + var + TietoevryAuth: Codeunit "Authenticator"; + HttpRequestMessage: HttpRequestMessage; + BaseUrl, AuthUrl, CompanyId : Text; + AccessToken: SecretText; + + /// + /// Create request for /outbound API + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Outbound%20Resource/post_outbound + /// + /// The data object is the details of the invoice. + /// A request object that can be used for the endpoint. + procedure CreateSubmitDocumentRequest(EDocument: Record "E-Document"; Data: Text): Codeunit Requests + var + Base64Convert: Codeunit "Base64 Convert"; + HttpHeaders, HttpContentHeaders : HttpHeaders; + Content: Text; + ContentJson: JsonObject; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/outbound'); + this.HttpRequestMessage.Method := 'POST'; + + this.HttpRequestMessage.GetHeaders(HttpHeaders); + HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + + EDocument.Get(EDocument."Entry No"); //Refresh + ContentJson.Add('payload', Base64Convert.ToBase64(Data)); + ContentJson.Add('sender', CompanyId); + ContentJson.Add('receiver', EDocument."Bill-to/Pay-to Id"); + ContentJson.Add('profileId', EDocument."Message Profile Id"); + ContentJson.Add('documentId', EDocument."Message Document Id"); + ContentJson.Add('channel', 'PEPPOL'); + ContentJson.Add('reference', Format(EDocument."Entry No")); + ContentJson.WriteTo(Content); + + + this.HttpRequestMessage.Content.WriteFrom(Content); + + this.HttpRequestMessage.Content.GetHeaders(HttpContentHeaders); + if HttpContentHeaders.Contains('Content-Type') then + HttpContentHeaders.Remove('Content-Type'); + HttpContentHeaders.Add('Content-Type', 'application/json'); + + exit(this); + end; + + /// + /// Create request for /outbound/:id API + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Outbound%20Resource/get_outbound__id_ + /// + /// A request object that can be used for the endpoint. + procedure CreateGetDocumentStatusRequest(Id: Text): Codeunit Requests + var + HttpHeaders: HttpHeaders; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/outbound/' + Id); + this.HttpRequestMessage.Method := 'GET'; + + this.HttpRequestMessage.GetHeaders(HttpHeaders); + HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + HttpHeaders.Add('Accept', 'application/json'); + + exit(this); + end; + + /// + /// Create request for /inbound?receiver=$companyid + /// Takes a path as query parameters are computed for each request. + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Inbound%20Resource/get_inbound + /// + /// A request object that can be used for the endpoint. + procedure CreateReceiveDocumentsRequest(): Codeunit Requests + var + TypeHelper: Codeunit "Type Helper"; + HttpHeaders: HttpHeaders; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/inbound?receiver=' + TypeHelper.UrlEncode(this.CompanyId)); + this.HttpRequestMessage.Method := 'GET'; + + this.HttpRequestMessage.GetHeaders(HttpHeaders); + HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + HttpHeaders.Add('Accept', 'application/json'); + + exit(this); + end; + + /// + /// Create request for /inbound/$id + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Inbound%20Resource/get_inbound__id_ + /// + /// Document Id + /// A request object that can be used for the endpoint. + procedure CreateDownloadRequest(Id: Text): Codeunit Requests + var + HttpHeaders: HttpHeaders; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/inbound/' + Id); + this.HttpRequestMessage.Method := 'GET'; + + this.HttpRequestMessage.GetHeaders(HttpHeaders); + HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + HttpHeaders.Add('Accept', 'application/json'); + + exit(this); + end; + + /// + /// Create request for /inbound/$id/read + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Inbound%20Resource/post_inbound__id__read + /// + /// Document Id + /// A request object that can be used for the endpoint. + procedure CreateAcknowledgeRequest(Id: Text): Codeunit Requests + var + HttpHeaders: HttpHeaders; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/inbound/' + Id + '/read'); + this.HttpRequestMessage.Method := 'POST'; + + this.HttpRequestMessage.GetHeaders(HttpHeaders); + HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + HttpHeaders.Add('Accept', 'application/json'); + + exit(this); + end; + + /// + /// Create request to get access token for Tietoevry API + /// + /// A request object that can be used for the endpoint. + [NonDebuggable] + procedure CreateAuthenticateRequest(ClientId: SecretText; ClientSecret: SecretText): Codeunit Requests; + var + HttpContentHeaders: HttpHeaders; + begin + Clear(this.HttpRequestMessage); + this.HttpRequestMessage.SetRequestUri(this.AuthUrl + '/token'); + this.HttpRequestMessage.Method := 'POST'; + this.HttpRequestMessage.Content.WriteFrom('grant_type=client_credentials&client_id=' + ClientId.Unwrap() + '&client_secret=' + ClientSecret.Unwrap()); + + this.HttpRequestMessage.Content.GetHeaders(HttpContentHeaders); + if HttpContentHeaders.Contains('Content-Type') then + HttpContentHeaders.Remove('Content-Type'); + HttpContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded'); + + exit(this); + end; + + procedure GetRequest(): HttpRequestMessage + begin + exit(this.HttpRequestMessage); + end; + + procedure Init() + begin + this.TietoevryAuth.CreateConnectionSetupRecord(); + this.BaseUrl := this.GetBaseUrl(); + this.AuthUrl := this.GetAuthUrl(); + this.CompanyId := this.GetCompanyId(); + end; + + /// + /// Set access token on request. + /// + procedure Authenticate(): Codeunit Requests + begin + this.AccessToken := this.TietoevryAuth.GetAccessToken(); + exit(this); + end; + + [NonDebuggable] + local procedure AddBearer(Token: SecretText): SecretText + begin + exit('Bearer ' + Token.Unwrap()); + end; + + procedure GetBaseUrl(): Text + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + + case ConnectionSetup."Send Mode" of + "E-Doc. Ext. Send Mode"::Production: + exit(ConnectionSetup."API URL"); + "E-Doc. Ext. Send Mode"::Test: + exit(ConnectionSetup."Sandbox API URL"); + else + Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); + end; + end; + + local procedure GetAuthUrl(): Text + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + + case ConnectionSetup."Send Mode" of + "E-Doc. Ext. Send Mode"::Production: + exit(ConnectionSetup."Authentication URL"); + "E-Doc. Ext. Send Mode"::Test: + exit(ConnectionSetup."Sandbox Authentication URL"); + else + Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); + end; + end; + + procedure GetCompanyId(): Text + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + exit(ConnectionSetup."Company Id"); + end; +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al new file mode 100644 index 000000000..026fcfe12 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al @@ -0,0 +1,64 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; + +using System.Upgrade; +#if not CLEAN26 +using Microsoft.eServices.EDocument.Integration; +using Microsoft.eServices.EDocument; +#endif + +codeunit 6390 Upgrade +{ + Access = Internal; + Subtype = Upgrade; + + trigger OnUpgradePerCompany() + var + + begin +#if not CLEAN26 + // Upgrade code per company + this.UpdateServiceIntegration(); +#endif + + end; + +#if not CLEAN26 + local procedure UpdateServiceIntegration() + var + EDocumentService: Record "E-Document Service"; + UpgradeTag: Codeunit "Upgrade Tag"; + begin + if UpgradeTag.HasUpgradeTag(UpgradeServiceIntegrationTag()) then + exit; + + // 6390 - Tietoevry Integration + EDocumentService.SetRange("Service Integration", 6370); + if EDocumentService.FindSet() then + repeat + EDocumentService."Service Integration V2" := Enum::"Service Integration"::Tietoevry; + EDocumentService."Service Integration" := Enum::"E-Document Integration"::"No Integration"; + EDocumentService.Modify(); + until EDocumentService.Next() = 0; + + UpgradeTag.SetUpgradeTag(UpgradeServiceIntegrationTag()); + end; +#endif + + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Upgrade Tag", 'OnGetPerCompanyUpgradeTags', '', false, false)] + local procedure RegisterPerCompanyTags(var PerCompanyUpgradeTags: List of [Code[250]]) + begin + PerCompanyUpgradeTags.Add(UpgradeServiceIntegrationTag()); + end; + + local procedure UpgradeServiceIntegrationTag(): Code[250] + begin + exit('MS-000000-UpdateServiceIntegrationTietoevry-20241125'); + end; + + +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al deleted file mode 100644 index 7443ac33a..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al +++ /dev/null @@ -1,127 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using Microsoft.EServices.EDocument; -using System.Utilities; -using Microsoft.Purchases.Posting; -using Microsoft.Purchases.Document; - -codeunit 6381 "Connection" -{ - Access = Internal; - Permissions = tabledata "E-Document" = m; - - procedure HandleSendDocumentRequest(var TempBlob: Codeunit "Temp Blob"; var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage; Retry: Boolean): Boolean - begin - if not TietoevryAPIRequests.SendDocumentRequest(TempBlob, EDocument, HttpRequest, HttpResponse) then - if Retry then - TietoevryAPIRequests.SendDocumentRequest(TempBlob, EDocument, HttpRequest, HttpResponse); - - exit(CheckIfSuccessfulRequest(EDocument, HttpResponse)); - end; - - procedure CheckDocumentStatus(var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage; Retry: Boolean): Boolean - begin - if not TietoevryAPIRequests.GetDocumentStatusRequest(EDocument, HttpRequest, HttpResponse) then - if Retry then - TietoevryAPIRequests.GetDocumentStatusRequest(EDocument, HttpRequest, HttpResponse); - - exit(CheckIfSuccessfulRequest(EDocument, HttpResponse)); - end; - - procedure GetReceivedDocuments(var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage; Retry: Boolean): Boolean - var - InputTxt: Text; - begin - if not TietoevryAPIRequests.GetReceivedDocumentsRequest(HttpRequest, HttpResponse) then - if Retry then - TietoevryAPIRequests.GetReceivedDocumentsRequest(HttpRequest, HttpResponse); - - if not HttpResponse.IsSuccessStatusCode then - exit(false); - - InputTxt := ParseAsJsonArray(HttpResponse.Content); - if InputTxt <> '' then - exit(true); - end; - - procedure HandleGetTargetDocumentRequest(DocumentId: Text; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage; Retry: Boolean): Boolean - begin - if not TietoevryAPIRequests.GetTargetDocumentRequest(DocumentId, HttpRequest, HttpResponse) then - if Retry then - TietoevryAPIRequests.GetTargetDocumentRequest(DocumentId, HttpRequest, HttpResponse); - - if HttpResponse.IsSuccessStatusCode then - exit(true); - end; - - procedure HandleSendFetchDocumentRequest(DocumentId: Text; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage; Retry: Boolean): Boolean - begin - if not TietoevryAPIRequests.SendAcknowledgeDocumentRequest(DocumentId, HttpRequest, HttpResponse) then - if Retry then - TietoevryAPIRequests.SendAcknowledgeDocumentRequest(DocumentId, HttpRequest, HttpResponse); - - if HttpResponse.IsSuccessStatusCode then - exit(true); - end; - - procedure ParseAsJsonArray(HttpContentResponse: HttpContent): Text - var - ResponseJArray: JsonArray; - ResponseJson: Text; - Result: Text; - IsJsonResponse: Boolean; - begin - HttpContentResponse.ReadAs(Result); - IsJsonResponse := ResponseJArray.ReadFrom(Result); - if IsJsonResponse then - ResponseJArray.WriteTo(ResponseJson) - else - exit(''); - - exit(Result); - end; - - local procedure CheckIfSuccessfulRequest(EDocument: Record "E-Document"; HttpResponse: HttpResponseMessage): Boolean - var - EDocumentErrorHelper: Codeunit "E-Document Error Helper"; - begin - if HttpResponse.IsSuccessStatusCode then - exit(true); - - if HttpResponse.IsBlockedByEnvironment then - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, EnvironmentBlocksErr) - else - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(UnsuccessfulResponseErr, HttpResponse.HttpStatusCode, HttpResponse.ReasonPhrase)); - end; - - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", 'OnAfterCheckAndUpdate', '', false, false)] - local procedure CheckOnPosting(var PurchaseHeader: Record "Purchase Header"; CommitIsSuppressed: Boolean; PreviewMode: Boolean) - var - EDocument: Record "E-Document"; - EDocumentService: Record "E-Document Service"; - EDocumentServiceStatus: Record "E-Document Service Status"; - begin - EDocument.SetRange("Document Record ID", PurchaseHeader.RecordId); - if not EDocument.FindFirst() then - exit; - - EDocumentService.SetRange("Service Integration", EDocumentService."Service Integration"::Tietoevry); - if EDocumentService.FindFirst() then; - EDocumentServiceStatus.SetRange("E-Document Entry No", EDocument."Entry No"); - EDocumentServiceStatus.SetRange("E-Document Service Code", EDocumentService.Code); - if EDocumentServiceStatus.FindSet() then - repeat - EDocumentServiceStatus.TestField(EDocumentServiceStatus.Status, EDocumentServiceStatus.Status::Approved); - until EDocumentServiceStatus.Next() = 0; - end; - - var - TietoevryAPIRequests: Codeunit "API Requests"; - UnsuccessfulResponseErr: Label 'There was an error sending the request. Response code: %1 and error message: %2', Comment = '%1 - http response status code, e.g. 400, %2- error message'; - EnvironmentBlocksErr: Label 'The request to send documents has been blocked. To resolve the problem, enable outgoing HTTP requests for the E-Document apps on the Extension Management page.'; -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al deleted file mode 100644 index 9927a8be5..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al +++ /dev/null @@ -1,59 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using System.Utilities; -using Microsoft.EServices.EDocument; - -codeunit 6382 "Integration Impl." implements "E-Document Integration" -{ - Access = Internal; - - procedure Send(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob"; var IsAsync: Boolean; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage) - var - begin - TietoevryProcessing.SendEDocument(EDocument, TempBlob, IsAsync, HttpRequest, HttpResponse); - end; - - procedure SendBatch(var EDocuments: Record "E-Document"; var TempBlob: Codeunit "Temp Blob"; var IsAsync: Boolean; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage) - begin - IsAsync := false; - Error('Batch sending is not supported in this version'); - end; - - procedure GetResponse(var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage): Boolean - begin - exit(TietoevryProcessing.GetDocumentResponse(EDocument, HttpRequest, HttpResponse)); - end; - - procedure GetApproval(var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage): Boolean - begin - Error('Get Approval is not supported in this version'); - end; - - procedure Cancel(var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage): Boolean - begin - exit(TietoevryProcessing.CancelEDocument(EDocument, HttpRequest, HttpResponse)); - end; - - procedure ReceiveDocument(var TempBlob: Codeunit "Temp Blob"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage) - begin - TietoevryProcessing.ReceiveDocument(TempBlob, HttpRequest, HttpResponse); - end; - - procedure GetDocumentCountInBatch(var TempBlob: Codeunit "Temp Blob"): Integer - begin - exit(TietoevryProcessing.GetDocumentCountInBatch(TempBlob)); - end; - - procedure GetIntegrationSetup(var SetupPage: Integer; var SetupTable: Integer) - begin - SetupPage := page::"Connection Setup Card"; - SetupTable := Database::"Connection Setup"; - end; - - var - TietoevryProcessing: Codeunit "Processing"; -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al deleted file mode 100644 index ea024b687..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al +++ /dev/null @@ -1,174 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using Microsoft.EServices.EDocument; -using System.Utilities; -using System.Text; -using System.Xml; -using System.Reflection; - -codeunit 6383 "API Requests" -{ - Access = Internal; - - procedure SendDocumentRequest(var TempBlob: Codeunit "Temp Blob"; EDocument: Record "E-Document"; var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean - var - ExternalConnectionSetup: Record "Connection Setup"; - TietoevryAuthMgt: Codeunit "Authenticator"; - Base64Convert: Codeunit "Base64 Convert"; - Payload: Text; - Content: Text; - HttpClient: HttpClient; - HttpHeaders: HttpHeaders; - HttpContent: HttpContent; - ContentJson: JsonObject; - begin - InitRequest(ExternalConnectionSetup, HttpRequestMessage, HttpResponseMessage); - - Payload := TempBlobToTxt(TempBlob); - if Payload = '' then - exit(false); - - EDocument.Get(EDocument."Entry No"); //Refresh - ContentJson.Add('payload', Base64Convert.ToBase64(Payload)); - ContentJson.Add('sender', ExternalConnectionSetup."Company Id"); - ContentJson.Add('receiver', EDocument."Bill-to/Pay-to Id"); - ContentJson.Add('profileId', EDocument."Message Profile Id"); - ContentJson.Add('documentId', EDocument."Message Document Id"); - ContentJson.Add('channel', 'PEPPOL'); - ContentJson.Add('reference', Format(EDocument."Entry No")); - ContentJson.WriteTo(Content); - - if HttpClient.DefaultRequestHeaders.Contains('Authorization') then - HttpClient.DefaultRequestHeaders.Remove('Authorization'); - HttpClient.DefaultRequestHeaders.Add('Authorization', TietoevryAuthMgt.GetAuthBearerTxt()); - - HttpRequestMessage.Method('POST'); - HttpRequestMessage.SetRequestUri(ExternalConnectionSetup."Outbound API URL"); - - HttpContent.WriteFrom(Content); - - HttpContent.GetHeaders(HttpHeaders); - if HttpHeaders.Contains('Content-Type') then - HttpHeaders.Remove('Content-Type'); - HttpHeaders.Add('Content-Type', 'application/json'); - - HttpRequestMessage.Content := HttpContent; - - exit(HttpClient.Send(HttpRequestMessage, HttpResponseMessage)); - end; - - procedure GetDocumentStatusRequest(EDocument: Record "E-Document"; var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean - var - ExternalConnectionSetup: Record "Connection Setup"; - TietoevryAuth: Codeunit "Authenticator"; - HttpClient: HttpClient; - HttpHeaders: HttpHeaders; - EndpointURL: Text; - begin - InitRequest(ExternalConnectionSetup, HttpRequestMessage, HttpResponseMessage); - - HttpRequestMessage.GetHeaders(HttpHeaders); - HttpHeaders.Add('Authorization', TietoevryAuth.GetAuthBearerTxt()); - HttpHeaders.Add('Accept', 'application/json'); - HttpRequestMessage.Method('GET'); - - EndpointURL := ExternalConnectionSetup."Outbound API URL" + '/' + EDocument."Message Id"; - HttpRequestMessage.SetRequestUri(EndpointURL); - - exit(HttpClient.Send(HttpRequestMessage, HttpResponseMessage)); - end; - - procedure GetReceivedDocumentsRequest(var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean - var - ExternalConnectionSetup: Record "Connection Setup"; - TypeHelper: Codeunit "Type Helper"; - TietoevryAuth: Codeunit "Authenticator"; - HttpClient: HttpClient; - HttpHeaders: HttpHeaders; - EndpointURL: Text; - begin - InitRequest(ExternalConnectionSetup, HttpRequestMessage, HttpResponseMessage); - - HttpRequestMessage.GetHeaders(HttpHeaders); - HttpHeaders.Add('Authorization', TietoevryAuth.GetAuthBearerTxt()); - HttpHeaders.Add('Accept', 'application/json'); - - HttpRequestMessage.Method('GET'); - EndpointURL := - ExternalConnectionSetup."Inbound API URL" + '?receiver=' + TypeHelper.UrlEncode(ExternalConnectionSetup."Company Id"); - HttpRequestMessage.SetRequestUri(EndpointURL); - - exit(HttpClient.Send(HttpRequestMessage, HttpResponseMessage)); - end; - - procedure GetTargetDocumentRequest(DocumentId: Text; var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean - var - ExternalConnectionSetup: Record "Connection Setup"; - TietoevryAuth: Codeunit "Authenticator"; - HttpClient: HttpClient; - HttpHeaders: HttpHeaders; - EndpointURL: Text; - begin - InitRequest(ExternalConnectionSetup, HttpRequestMessage, HttpResponseMessage); - - HttpRequestMessage.GetHeaders(HttpHeaders); - HttpHeaders.Add('Authorization', TietoevryAuth.GetAuthBearerTxt()); - HttpHeaders.Add('Accept', 'application/octet-stream'); - - HttpRequestMessage.Method('GET'); - EndpointURL := ExternalConnectionSetup."Inbound API URL" + '/' + DocumentId + '/PAYLOAD/document'; - HttpRequestMessage.SetRequestUri(EndpointURL); - - exit(HttpClient.Send(HttpRequestMessage, HttpResponseMessage)); - end; - - procedure SendAcknowledgeDocumentRequest(DocumentId: Text; var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage): Boolean - var - ExternalConnectionSetup: Record "Connection Setup"; - TietoevryAuthMgt: Codeunit "Authenticator"; - HttpClient: HttpClient; - HttpHeaders: HttpHeaders; - EndpointUrl: Text; - begin - InitRequest(ExternalConnectionSetup, HttpRequestMessage, HttpResponseMessage); - - HttpRequestMessage.GetHeaders(HttpHeaders); - HttpHeaders.Add('Authorization', TietoevryAuthMgt.GetAuthBearerTxt()); - HttpHeaders.Add('Accept', '*/*'); - HttpRequestMessage.Method('POST'); - - EndpointURL := ExternalConnectionSetup."Inbound API URL" + '/' + DocumentId + '/read'; - HttpRequestMessage.SetRequestUri(EndpointUrl); - - exit(HttpClient.Send(HttpRequestMessage, HttpResponseMessage)); - end; - - local procedure InitRequest(var ExternalConnectionSetup: Record "Connection Setup"; var HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - Clear(HttpRequestMessage); - Clear(HttpResponseMessage); - if not ExternalConnectionSetup.Get() then - Error(MissingSetupErr); - ExternalConnectionSetup.TestField("Send Mode"); - ExternalConnectionSetup.TestField("Company Id"); - end; - - local procedure TempBlobToTxt(var TempBlob: Codeunit "Temp Blob"): Text - var - XMLDOMManagement: Codeunit "XML DOM Management"; - InStr: InStream; - Content: Text; - begin - TempBlob.CreateInStream(InStr, TextEncoding::UTF8); - XMLDOMManagement.TryGetXMLAsText(InStr, Content); - exit(Content); - end; - - var - MissingSetupErr: Label 'You must set up service integration in the E-Document service card.'; - -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al deleted file mode 100644 index 5a4ac1382..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al +++ /dev/null @@ -1,371 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using System.Security.Authentication; -using System.Azure.KeyVault; -using System.Environment; -using System.Integration; - -codeunit 6384 "Authenticator" -{ - Access = Internal; - Permissions = tabledata "OAuth 2.0 Setup" = im; - - procedure InitConnectionSetup() - var - OAuth20Setup: Record "OAuth 2.0 Setup"; - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not EDocExtConnectionSetup.Get() then begin - EDocExtConnectionSetup."OAuth Feature GUID" := CreateGuid(); - EDocExtConnectionSetup."Send Mode" := EDocExtConnectionSetup."Send Mode"::Certification; - SetDefaultEndpoints(EDocExtConnectionSetup, EDocExtConnectionSetup."Send Mode"); - EDocExtConnectionSetup.Insert(); - end; - InitOAuthSetup(OAuth20Setup); - end; - - procedure SetDefaultEndpoints(var EDocExtConnectionSetup: Record "Connection Setup"; SendMode: Enum "E-Document Send Mode") - begin - case SendMode of - SendMode::Production: - begin - EDocExtConnectionSetup."Authentication URL" := ProdAuthURLTxt; - EDocExtConnectionSetup."Inbound API URL" := ProdInboundAPITxt; - EDocExtConnectionSetup."Outbound API URL" := ProdOutboundAPITxt; - end; - SendMode::Certification: - begin - EDocExtConnectionSetup."Authentication URL" := CertAuthURLTxt; - EDocExtConnectionSetup."Inbound API URL" := CertInboundAPITxt; - EDocExtConnectionSetup."Outbound API URL" := CertOutboundAPITxt; - end; - end; - end; - - [NonDebuggable] - procedure SetClientId(var ClienId: Guid; ClientID: Text) - var - begin - SetIsolatedStorageValue(ClienId, ClientID, DataScope::Company); - end; - - procedure SetClientSecret(var ClienSecret: Guid; ClientSecret: SecretText) - begin - SetIsolatedStorageValue(ClienSecret, ClientSecret, DataScope::Company); - end; - - procedure IsClientCredsSet(var ClientId: Text; var ClientSecret: Text): Boolean - var - EDocExtConnectionSetup: Record "Connection Setup"; - begin - EDocExtConnectionSetup.Get(); -#if not DOCKER - if EnvironmentInfo.IsSaaS() then - exit(true); -#endif - if HasToken(EDocExtConnectionSetup."Client ID", DataScope::Company) then - ClientId := '*'; - if HasToken(EDocExtConnectionSetup."Client Secret", DataScope::Company) then - ClientSecret := '*'; - end; - - procedure OpenOAuthSetupPage() - var - OAuth20Setup: Record "OAuth 2.0 Setup"; - begin - InitOAuthSetup(OAuth20Setup); - Commit(); - Page.RunModal(Page::"OAuth 2.0 Setup", OAuth20Setup); - end; - - procedure GetAuthBearerTxt(): SecretText; - var - OAuth20Setup: Record "OAuth 2.0 Setup"; - HttpError: Text; - begin - GetOAuth2Setup(OAuth20Setup); - if OAuth20Setup."Access Token Due DateTime" < CurrentDateTime() + 60 * 1000 then - if not RefreshAccessToken(HttpError) then - Error(HttpError); - - exit(SecretStrSubstNo(BearerTxt, GetToken(OAuth20Setup."Access Token", OAuth20Setup.GetTokenDataScope()))); - end; - - local procedure ParseAccessTokens(ResponseJson: Text; var AccessToken: SecretText; var ExpireInSec: BigInteger): Boolean - var - JToken: JsonToken; - NewAccessToken: Text; - begin - NewAccessToken := ''; - - AccessToken := NewAccessToken; - - ExpireInSec := 0; - - if JToken.ReadFrom(ResponseJson) then - foreach JToken in JToken.AsObject().Values() do - case JToken.Path() of - 'access_token': - NewAccessToken := JToken.AsValue().AsText(); - 'expires_in': - ExpireInSec := JToken.AsValue().AsBigInteger(); - end; - if (NewAccessToken = '') then - exit(false); - - AccessToken := NewAccessToken; - exit(true); - end; - - procedure TestOAuth2Setup() - var - OAuth20Setup: Record "OAuth 2.0 Setup"; - HttpError: Text; - begin - GetOAuth2Setup(OAuth20Setup); - OAuth20Setup.RequestAccessToken(HttpError, ''); - end; - - [NonDebuggable] - local procedure AcquireTokenWithClientCredentials(ClientId: Text; ClientSecret: SecretText; OAuthAuthorityUrl: Text; var AccessToken: SecretText; var ExpireInSec: BigInteger): Boolean - var - HttpClient: HttpClient; - HttpContent: HttpContent; - HttpResponse: HttpResponseMessage; - HttpHeaders: HttpHeaders; - RequestContent: Text; - HttpResponseBodyText: Text; - begin - RequestContent := StrSubstNo(TokenRequestContentTxt, ClientId, ClientSecret.Unwrap()); - HttpClient.Clear(); - HttpContent.WriteFrom(RequestContent); - HttpContent.GetHeaders(HttpHeaders); - if HttpHeaders.Contains('Content-Type') then - HttpHeaders.Remove('Content-Type'); - HttpHeaders.Add('Content-Type', 'application/x-www-form-urlencoded'); - - HttpClient.Post(OAuthAuthorityUrl, HttpContent, HttpResponse); - if HttpResponse.IsSuccessStatusCode then begin - HttpResponse.Content().ReadAs(HttpResponseBodyText); - exit(ParseAccessTokens(HttpResponseBodyText, AccessToken, ExpireInSec)); - end; - end; - - [NonDebuggable] - local procedure RefreshAccessToken(var HttpError: Text): Boolean; - var - OAuth20Setup: Record "OAuth 2.0 Setup"; - begin - GetOAuth2Setup(OAuth20Setup); - exit(OAuth20Setup.RefreshAccessToken(HttpError)); - end; - - [NonDebuggable] - local procedure InitOAuthSetup(var OAuth20Setup: Record "OAuth 2.0 Setup") - var - EDocExtConnectionSetup: Record "Connection Setup"; - Exists: Boolean; - begin - EDocExtConnectionSetup.Get(); - - if OAuth20Setup.Get(GetAuthSetupCode()) then - Exists := true; - - OAuth20Setup.Code := GetAuthSetupCode(); - OAuth20Setup."Client ID" := CreateGuid(); - OAuth20Setup."Client Secret" := CreateGuid(); - OAuth20Setup."Service URL" := EDocExtConnectionSetup."Authentication URL"; - OAuth20Setup.Description := 'Tietoevry Online'; - OAuth20Setup.Scope := 'all'; - OAuth20Setup."Access Token URL Path" := AccessTokenURLPathTxt; - OAuth20Setup."Token DataScope" := OAuth20Setup."Token DataScope"::Company; - OAuth20Setup."Daily Limit" := 1000; - OAuth20Setup."Feature GUID" := EDocExtConnectionSetup."OAuth Feature GUID"; - OAuth20Setup."User ID" := CopyStr(UserId(), 1, MaxStrLen(OAuth20Setup."User ID")); - if not Exists then - OAuth20Setup.Insert() - else - OAuth20Setup.Modify(); - end; - - [NonDebuggable] - local procedure GetOAuth2Setup(var OAuth20Setup: Record "OAuth 2.0 Setup"): Boolean; - var - ExternalConnectionSetup: Record "Connection Setup"; - begin - if not ExternalConnectionSetup.Get() then - Error(MissingAuthErr); - - ExternalConnectionSetup.TestField("OAuth Feature GUID"); - - OAuth20Setup.Get(GetAuthSetupCode()); - exit(true); - end; - - [NonDebuggable] - local procedure CheckOAuthConsistencySetup(OAuth20Setup: Record "OAuth 2.0 Setup") - begin - OAuth20Setup.TestField("Access Token URL Path", AccessTokenURLPathTxt); - OAuth20Setup.TestField("Daily Limit"); - end; - - local procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText; TokenDataScope: DataScope) NewToken: Boolean - begin - if IsNullGuid(ValueKey) then - NewToken := true; - if NewToken then - ValueKey := CreateGuid(); - - IsolatedStorage.Set(ValueKey, Value, TokenDataScope); - end; - - local procedure GetToken(TokenKey: Text; TokenDataScope: DataScope) TokenValueAsSecret: SecretText - begin - if not HasToken(TokenKey, TokenDataScope) then - exit(TokenValueAsSecret); - - IsolatedStorage.Get(TokenKey, TokenDataScope, TokenValueAsSecret); - end; - - [NonDebuggable] - local procedure HasToken(TokenKey: Text; TokenDataScope: DataScope): Boolean - begin - exit(IsolatedStorage.Contains(TokenKey, TokenDataScope)); - end; - - [NonDebuggable] - local procedure GetAuthSetupCode(): Code[20] - begin - exit(TietoevryOAuthCodeLbl); - end; - - [NonDebuggable] - local procedure GetClientId(): Text - var - EDocExtConnectionSetup: Record "Connection Setup"; -#if not DOCKER - AzureKeyVault: Codeunit "Azure Key Vault"; - Secret: Text; -#endif - begin -#if not DOCKER - if EnvironmentInfo.IsSaaS() then begin - AzureKeyVault.GetAzureKeyVaultSecret('tietoevry-client-id', Secret); - exit(Secret); - end; -#endif - if EDocExtConnectionSetup.Get() then - exit(GetToken(EDocExtConnectionSetup."Client ID", DataScope::Company).Unwrap()); - end; - - local procedure GetClientSecret(): SecretText - var - EDocExtConnectionSetup: Record "Connection Setup"; -#if not DOCKER - AzureKeyVault: Codeunit "Azure Key Vault"; - Secret: SecretText; -#endif - begin -#if not DOCKER - if EnvironmentInfo.IsSaaS() then begin - AzureKeyVault.GetAzureKeyVaultSecret('tietoevry-client-secret', Secret); - exit(Secret); - end; -#endif - if EDocExtConnectionSetup.Get() then - exit(GetToken(EDocExtConnectionSetup."Client Secret", DataScope::Company)); - end; - - local procedure SaveToken(var OAuth20Setup: Record "OAuth 2.0 Setup"; TokenDataScope: DataScope; AccessToken: SecretText) - begin - SetIsolatedStorageValue(OAuth20Setup."Access Token", AccessToken, TokenDataScope); - OAuth20Setup.Modify(); - end; - - [NonDebuggable] - [EventSubscriber(ObjectType::Table, Database::"OAuth 2.0 Setup", 'OnBeforeRequestAccessToken', '', true, true)] - local procedure OnBeforeRequestAccessToken(var OAuth20Setup: Record "OAuth 2.0 Setup"; AuthorizationCode: Text; var Result: Boolean; var MessageText: Text; var Processed: Boolean) - var - EDocExtConnectionSetup: Record "Connection Setup"; - NewAccessToken: SecretText; - ExpireInSec: BigInteger; - TokenDataScope: DataScope; - begin - if not EDocExtConnectionSetup.Get() then - exit; - - CheckOAuthConsistencySetup(OAuth20Setup); - - Processed := true; - - TokenDataScope := OAuth20Setup.GetTokenDataScope(); - - Result := AcquireTokenWithClientCredentials(GetClientId(), GetClientSecret(), OAuth20Setup."Service URL" + OAuth20Setup."Access Token URL Path", NewAccessToken, ExpireInSec); - - if not Result then - Error(AuthenticationFailedErr); - - OAuth20Setup."Access Token Due DateTime" := CurrentDateTime() + ExpireInSec * 1000; - SaveToken(OAuth20Setup, TokenDataScope, NewAccessToken); - - Message(AuthorizationSuccessfulTxt); - end; - - [NonDebuggable] - [EventSubscriber(ObjectType::Table, Database::"OAuth 2.0 Setup", 'OnBeforeRefreshAccessToken', '', true, true)] - local procedure OnBeforeRefreshAccessToken(var OAuth20Setup: Record "OAuth 2.0 Setup"; var Result: Boolean; var MessageText: Text; var Processed: Boolean) - var - EDocExtConnectionSetup: Record "Connection Setup"; - NewAccessToken: SecretText; - ExpireInSec: BigInteger; - TokenDataScope: DataScope; - begin - if not EDocExtConnectionSetup.Get() then - exit; - if not GetOAuth2Setup(OAuth20Setup) or Processed then - exit; - - CheckOAuthConsistencySetup(OAuth20Setup); - - Processed := true; - - TokenDataScope := OAuth20Setup.GetTokenDataScope(); - - Result := AcquireTokenWithClientCredentials(GetClientId(), GetClientSecret(), OAuth20Setup."Service URL" + OAuth20Setup."Access Token URL Path", NewAccessToken, ExpireInSec); - if not Result then - Error(AuthenticationFailedErr); - - OAuth20Setup."Access Token Due DateTime" := CurrentDateTime() + ExpireInSec * 1000; - SaveToken(OAuth20Setup, TokenDataScope, NewAccessToken); - end; - - [EventSubscriber(ObjectType::Table, Database::"OAuth 2.0 Setup", 'OnBeforeRequestAuthoizationCode', '', true, true)] - [NonDebuggable] - local procedure OnBeforeRequestAuthoizationCode(OAuth20Setup: Record "OAuth 2.0 Setup"; var Processed: Boolean) - var - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not EDocExtConnectionSetup.Get() or Processed then - exit; - Processed := true; - end; - - var - AccessTokenURLPathTxt: Label '/token', Locked = true; - BearerTxt: Label 'Bearer %1', Comment = '%1 = text value', Locked = true; - TokenRequestContentTxt: Label 'grant_type=client_credentials&client_id=%1&client_secret=%2', Comment = '%1 = Client Id, %2 = Client Secret', Locked = true; - ProdAuthURLTxt: Label 'https://auth.infotorg.no/auth/realms/fms-realm/protocol/openid-connect', Locked = true; - ProdInboundAPITxt: Label 'https://accesspoint-api.dataplatfor.ms/inbound', Locked = true; - ProdOutboundAPITxt: Label 'https://accesspoint-api.dataplatfor.ms/outbound', Locked = true; - CertAuthURLTxt: Label 'https://auth-qa.infotorg.no/auth/realms/fms-realm/protocol/openid-connect', Locked = true; - CertInboundAPITxt: Label 'https://accesspoint-api.qa.dataplatfor.ms/inbound', Locked = true; - CertOutboundAPITxt: Label 'https://accesspoint-api.qa.dataplatfor.ms/outbound', Locked = true; - TietoevryOAuthCodeLbl: Label 'EDocTietoevry', Locked = true; - AuthorizationSuccessfulTxt: Label 'Authorization successful.'; - MissingAuthErr: Label 'You must set up authentication to the service integration in the E-Document service card.'; - AuthenticationFailedErr: Label 'Authentication failed, check your credentials in the E-Document service card.'; -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al deleted file mode 100644 index 93e353233..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al +++ /dev/null @@ -1,401 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using Microsoft.EServices.EDocument; -using Microsoft.Sales.Document; -using Microsoft.Sales.Peppol; -using System.Telemetry; -using System.Text; -using System.Utilities; -using Microsoft.eServices.EDocument.Service.Participant; - -codeunit 6387 "Processing" -{ - Access = Internal; - Permissions = tabledata "E-Document" = m, - tabledata "E-Document Service Status" = m; - - //Send outbound document - procedure SendEDocument(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob"; var IsAsync: Boolean; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage) - var - EDocumentServiceStatus: Record "E-Document Service Status"; - EdocumentService: Record "E-Document Service"; - FeatureTelemetry: Codeunit "Feature Telemetry"; - begin - IsAsync := true; - - EDocumentHelper.GetEdocumentService(EDocument, EdocumentService); - EDocumentServiceStatus.Get(EDocument."Entry No", EdocumentService.Code); - - case EDocumentServiceStatus.Status of - EDocumentServiceStatus.Status::Exported, - EDocumentServiceStatus.Status::"Sending Error": - SendEDocument(EDocument, TempBlob, HttpRequest, HttpResponse); - end; - - FeatureTelemetry.LogUptake('0000MSC', ExternalServiceTok, Enum::"Feature Uptake Status"::Used); - end; - - //Get status of sent document - procedure GetDocumentResponse(var EDocument: Record "E-Document"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage): Boolean - begin - if not CheckIfDocumentStatusSuccessful(EDocument, HttpRequest, HttpResponse) then - exit(false); - - exit(true); - end; - - procedure CancelEDocument(EDocument: Record "E-Document"; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage): Boolean - var - EDocumentService: Record "E-Document Service"; - EDocumentServiceStatus: Record "E-Document Service Status"; - begin - EDocumentHelper.GetEdocumentService(EDocument, EdocumentService); - if EDocumentService."Service Integration" <> EDocumentService."Service Integration"::Tietoevry then - exit; - - EDocumentServiceStatus.Get(EDocument."Entry No", EdocumentService.Code); - - if not (EDocumentServiceStatus.Status = EDocumentServiceStatus.Status::Created) then - Error(CancelCheckStatusErr, EDocumentServiceStatus.Status); - - EDocumentServiceStatus.Status := EDocumentServiceStatus.Status::Canceled; - EDocumentServiceStatus.Modify(); - exit(true); - end; - - procedure RestartEDocument(EDocument: Record "E-Document"; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage): Boolean - var - EDocumentService: Record "E-Document Service"; - begin - EDocumentHelper.GetEdocumentService(EDocument, EdocumentService); - if EDocumentService."Service Integration" <> EDocumentService."Service Integration"::Tietoevry then - exit; - - // if TietoevryConnection.HandleSendActionRequest(EDocument, HttpRequest, HttpResponse, 'Restart', false) then - exit(true); - end; - - // Mark document as collected - procedure AcknowledgeEDocument(EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; MessageId: Text) - var - LocalHttpRequest: HttpRequestMessage; - LocalHttpResponse: HttpResponseMessage; - begin - if MessageId = '' then - exit; - - TietoevryConnection.HandleSendFetchDocumentRequest(MessageId, LocalHttpRequest, LocalHttpResponse, false); - EDocumentLogHelper.InsertIntegrationLog(EDocument, EDocumentService, LocalHttpRequest, LocalHttpResponse); - end; - - //Get a list of messages to collect - procedure ReceiveDocument(var TempBlob: Codeunit "Temp Blob"; var HttpRequest: HttpRequestMessage; var HttpResponse: HttpResponseMessage) - var - ContentData: Text; - OutStream: OutStream; - begin - if not TietoevryConnection.GetReceivedDocuments(HttpRequest, HttpResponse, true) then - exit; - - HttpResponse.Content.ReadAs(ContentData); - - TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8); - OutStream.WriteText(ContentData); - end; - - procedure GetDocumentCountInBatch(var TempBlob: Codeunit "Temp Blob"): Integer - var - ResponseInstream: InStream; - ResponseTxt: Text; - begin - TempBlob.CreateInStream(ResponseInstream); - ResponseInstream.ReadText(ResponseTxt); - - exit(GetNumberOfReceivedDocuments(ResponseTxt)); - end; - - procedure ParseReceivedDocument(InputTxt: Text; Index: Integer; var MessageId: Text): Boolean - var - JsonArray: JsonArray; - JsonToken: JsonToken; - JsonObject: JsonObject; - JsonTokenValue: JsonToken; - begin - if not JsonArray.ReadFrom(InputTxt) then - exit(false); - - if Index > JsonArray.Count() then - exit(false); - - if not JsonArray.Get(Index, JsonToken) then - exit(false); - - JsonObject := JsonToken.AsObject(); - if not JsonObject.Get('id', JsonTokenValue) then - exit(false); - - MessageId := JsonTokenValue.AsValue().AsText(); - exit(true); - end; - - local procedure GetNumberOfReceivedDocuments(InputTxt: Text): Integer - var - JsonToken: JsonToken; - begin - if not JsonToken.ReadFrom(InputTxt) then - exit(0); - - exit(JsonToken.AsArray().Count()); - end; - - local procedure SendEDocument(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage); - var - HttpContentResponse: HttpContent; - begin - TietoevryConnection.HandleSendDocumentRequest(TempBlob, EDocument, HttpRequest, HttpResponse, true); - HttpContentResponse := HttpResponse.Content; - SetEMessageID(EDocument."Entry No", ParseSendDocumentResponse(HttpContentResponse)); - end; - - local procedure CheckIfDocumentStatusSuccessful(EDocument: Record "E-Document"; var HttpRequestMessage: HttpRequestMessage; var HttpResponse: HttpResponseMessage): Boolean - var - EDocumentService: Record "E-Document Service"; - EDocumentServiceStatus: Record "E-Document Service Status"; - Telemetry: Codeunit Telemetry; - Status, ErrorDescription : Text; - ContentData: Text; - JToken: JsonToken; - begin - if not TietoevryConnection.CheckDocumentStatus(EDocument, HttpRequestMessage, HttpResponse, true) then - exit(false); - - if IsDocumentStatusProcessed(HttpResponse, Status) then begin - EDocumentHelper.GetEdocumentService(EDocument, EDocumentService); - EDocumentServiceStatus.Get(EDocument."Entry No", EDocumentService.Code); - EDocumentServiceStatus.Status := "E-Document Status"::Processed; - EDocumentServiceStatus.Modify(); - EDocumentLogHelper.InsertIntegrationLog(EDocument, EDocumentService, HttpRequestMessage, HttpResponse); - exit(true); - end; - - case Status of - TietoevryPendingStatusLbl: - exit(false); - TietoevryFailedStatusLbl: - begin - HttpResponse.Content.ReadAs(ContentData); - if not JToken.ReadFrom(ContentData) then begin - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, ParseErr); - exit(false); - end; - foreach JToken in JToken.AsObject().Values() do - case JToken.Path() of - 'details': - ErrorDescription := JToken.AsValue().AsText(); - end; - EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, ErrorDescription); - exit(false); - end; - else begin - Telemetry.LogMessage('0000MSB', StrSubstNo(WrongParseStatusErr, Status), Verbosity::Error, DataClassification::SystemMetadata); - exit(false); - end; - end; - end; - - local procedure IsDocumentStatusProcessed(HttpResponse: HttpResponseMessage; var Status: Text): Boolean - var - HttpContentResponse: HttpContent; - Result: Text; - JToken: JsonToken; - begin - HttpContentResponse := HttpResponse.Content; - Result := ParseJsonString(HttpContentResponse); - if Result = '' then - Error(ParseErr); - - if not JToken.ReadFrom(Result) then - Error(ParseErr); - - foreach JToken in JToken.AsObject().Values() do - case JToken.Path() of - 'status': - Status := JToken.AsValue().AsText(); - end; - - if Status = TietoevryProcessedStatusLbl then - exit(true); - - exit(false); - end; - - local procedure ParseSendDocumentResponse(HttpContentResponse: HttpContent): Text - var - JsonManagement: Codeunit "JSON Management"; - Result: Text; - Value: Text; - begin - Result := ParseJsonString(HttpContentResponse); - if Result = '' then - exit(''); - - if not JsonManagement.InitializeFromString(Result) then - exit(''); - - JsonManagement.GetStringPropertyValueByName('id', Value); - exit(Value); - end; - - local procedure SetEMessageID(EDocEntryNo: Integer; MessageId: Text) - var - EDocument: Record "E-Document"; - begin - if MessageId = '' then - exit; - - if not EDocument.Get(EDocEntryNo) then - exit; - - EDocument."Message Id" := CopyStr(MessageId, 1, MaxStrLen(EDocument."Message Id")); - EDocument.Modify(); - end; - - procedure ParseJsonString(HttpContentResponse: HttpContent): Text - var - ResponseJObject: JsonObject; - ResponseJson: Text; - Result: Text; - IsJsonResponse: Boolean; - begin - HttpContentResponse.ReadAs(Result); - IsJsonResponse := ResponseJObject.ReadFrom(Result); - if IsJsonResponse then - ResponseJObject.WriteTo(ResponseJson) - else - exit(''); - - if not TryInitJson(ResponseJson) then - exit(''); - - exit(Result); - end; - - [TryFunction] - local procedure TryInitJson(JsonTxt: Text) - var - JsonManagement: Codeunit "JSON Management"; - begin - JSONManagement.InitializeObject(JsonTxt); - end; - - local procedure SplitId(Input: Text; var SchemeId: Text; var EndpointId: Text) - var - Parts: List of [Text]; - begin - Parts := Input.Split(':'); - SchemeId := Parts.Get(1); - EndpointId := Parts.Get(2); - end; - - internal procedure IsValidSchemeId(PeppolId: Text[50]) Result: Boolean; - var - ValidSchemeId: Text; - ValidSchemeIdList: List of [Text]; - SplitSeparator: Text; - SchemeId: Text; - begin - SplitSeparator := ' '; - ValidSchemeId := ValidSchemeIdTxt; - ValidSchemeIdList := ValidSchemeId.Split(SplitSeparator); - - foreach SchemeId in ValidSchemeIdList do - if PeppolId.StartsWith(SchemeId) then - exit(true); - exit(false); - end; - - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingSupplierPartyInfoByFormat, '', false, false)] - local procedure "PEPPOL Management_OnAfterGetAccountingSupplierPartyInfoByFormat"(var SupplierEndpointID: Text; var SupplierSchemeID: Text; var SupplierName: Text; IsBISBilling: Boolean) - var - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not IsBISBilling then - exit; - if not EDocExtConnectionSetup.Get() then - exit; - - SplitId(EDocExtConnectionSetup."Company Id", SupplierSchemeID, SupplierEndpointID); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingSupplierPartyLegalEntityByFormat, '', false, false)] - local procedure "PEPPOL Management_OnAfterGetAccountingSupplierPartyLegalEntityByFormat"(var PartyLegalEntityRegName: Text; var PartyLegalEntityCompanyID: Text; var PartyLegalEntitySchemeID: Text; var SupplierRegAddrCityName: Text; var SupplierRegAddrCountryIdCode: Text; var SupplRegAddrCountryIdListId: Text; IsBISBilling: Boolean) - var - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not IsBISBilling then - exit; - if not EDocExtConnectionSetup.Get() then - exit; - - SplitId(EDocExtConnectionSetup."Company Id", PartyLegalEntitySchemeID, PartyLegalEntityCompanyID); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingCustomerPartyInfoByFormat, '', false, false)] - local procedure "PEPPOL Management_OnAfterGetAccountingCustomerPartyInfoByFormat"(SalesHeader: Record "Sales Header"; var CustomerEndpointID: Text; var CustomerSchemeID: Text; var CustomerPartyIdentificationID: Text; var CustomerPartyIDSchemeID: Text; var CustomerName: Text; IsBISBilling: Boolean) - var - ServiceParticipant: Record "Service Participant"; - EDocumentService: Record "E-Document Service"; - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not IsBISBilling then - exit; - if not EDocExtConnectionSetup.Get() then - exit; - EDocumentService.SetRange("Service Integration", EDocumentService."Service Integration"::Tietoevry); - if not EDocumentService.FindFirst() then - exit; - ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No."); - SplitId(ServiceParticipant."Participant Identifier", CustomerSchemeID, CustomerEndpointID); - SplitId(ServiceParticipant."Participant Identifier", CustomerPartyIDSchemeID, CustomerPartyIdentificationID); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingCustomerPartyLegalEntityByFormat, '', false, false)] - local procedure "PEPPOL Management_OnAfterGetAccountingCustomerPartyLegalEntityByFormat"(SalesHeader: Record "Sales Header"; var CustPartyLegalEntityRegName: Text; var CustPartyLegalEntityCompanyID: Text; var CustPartyLegalEntityIDSchemeID: Text; IsBISBilling: Boolean) - var - ServiceParticipant: Record "Service Participant"; - EDocumentService: Record "E-Document Service"; - EDocExtConnectionSetup: Record "Connection Setup"; - begin - if not IsBISBilling then - exit; - if not EDocExtConnectionSetup.Get() then - exit; - EDocumentService.SetRange("Service Integration", EDocumentService."Service Integration"::Tietoevry); - if not EDocumentService.FindFirst() then - exit; - - ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No."); - SplitId(ServiceParticipant."Participant Identifier", CustPartyLegalEntityIDSchemeID, CustPartyLegalEntityCompanyID); - end; - - var - TietoevryConnection: Codeunit Connection; - EDocumentHelper: Codeunit "E-Document Helper"; - EDocumentLogHelper: Codeunit "E-Document Log Helper"; - EDocumentErrorHelper: Codeunit "E-Document Error Helper"; - CancelCheckStatusErr: Label 'You cannot ask for cancel with the E-Document in this current status %1. You can request for cancel when E-document status is ''Created''.', Comment = '%1 - Status'; - ParseErr: Label 'Failed to parse document from Tietoevry API'; - WrongParseStatusErr: Label 'Got unexected status from Tietoevry API: %1', Comment = '%1 - Status that we received from API', Locked = true; - TietoevryFailedStatusLbl: Label 'FAILED', Locked = true; - TietoevryPendingStatusLbl: Label 'PENDING', Locked = true; - TietoevryProcessedStatusLbl: Label 'PROCESSED', Locked = true; - ExternalServiceTok: Label 'ExternalServiceConnector', Locked = true; -#pragma warning disable AA0240 - ValidSchemeIdTxt: Label '0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0147 0151 0170 0183 0184 0188 0190 0191 0192 0193 0194 0195 0196 0198 0199 0200 0201 0202 0203 0204 0205 0208 0209 0210 0211 0212 0213 0215 0216 0217 0218 0219 0220 0221 0225 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959 AN AQ AS AU EM', Locked = true; -#pragma warning restore AA0240 -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al deleted file mode 100644 index bbe24cd5b..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al +++ /dev/null @@ -1,16 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -using Microsoft.EServices.EDocument; -using Microsoft.eServices.EDocument.IO.PEPPOL; - -enumextension 6380 "E-Doc. Format Ext." extends "E-Document Format" -{ - value(6381; "TE PEPPOL BIS 3.0") - { - Implementation = "E-Document" = "EDoc TE PEPPOL BIS 3.0"; - } -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al deleted file mode 100644 index 7c2f5df96..000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.EServices.EDocumentConnector.Tietoevry; - -permissionset 6382 "TE EDoc. Conn. Objects" -{ - Access = Public; - Assignable = false; - - Permissions = table "Connection Setup" = X, - page "Connection Setup Card" = X, - codeunit "API Requests" = X, - codeunit "Authenticator" = X, - codeunit "Connection" = X, - codeunit "Integration Impl." = X, - codeunit "Processing" = X; -} \ No newline at end of file