From 2395fb46db558bfd988e72c5e4c9b6ecebe47029 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 29 Oct 2024 13:48:03 +0100 Subject: [PATCH 01/21] app checkin --- .../Tietoevry/app/ExtensionLogo.png | Bin 0 -> 5446 bytes .../Tietoevry/app/app.json | 48 ++ .../src/codeunit/Cod6380.ConnectionEvents.al | 148 ++++ .../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 ++++++++++ .../src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al | 145 ++++ .../codeunit/Cod6386.EDocImportTietoevry.al | 577 +++++++++++++++ .../app/src/codeunit/Cod6387.Processing.al | 401 +++++++++++ .../src/enum/Enum6380.EDocumentSendMode.al | 19 + .../Enum-Ext6380.EDocFormatExt.al | 16 + .../Enum-Ext6381.EDocExtIntegration.al | 15 + .../src/page/Pag6380.ConnectionSetupCard.al | 140 ++++ .../PermissionSet6380.TEEDocConnEdit.al | 14 + .../PermissionSet6381.TEEDocConnRead.al | 14 + .../PermissionSet6382.TEEDocConnObjects.al | 19 + ...Ext-Ext6380.D365BasicEDocumentConnector.al | 12 + ...tExt-Ext6381.D365ReadEDocumentConnector.al | 13 + .../app/src/table/Tab6380.ConnectionSetup.al | 71 ++ .../Tab-Ext6382.EDocTEExtEDocument.al | 34 + .../Tietoevry/test/ExtensionLogo.png | Bin 0 -> 5446 bytes .../Tietoevry/test/app.json | 84 +++ .../test/src/IntegrationTests.Codeunit.al | 656 ++++++++++++++++++ 24 files changed, 3157 insertions(+) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/ExtensionLogo.png create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/app.json create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/page/Pag6380.ConnectionSetupCard.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6381.TEEDocConnRead.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/table/Tab6380.ConnectionSetup.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/tableextension/Tab-Ext6382.EDocTEExtEDocument.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/test/ExtensionLogo.png create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/test/app.json create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/ExtensionLogo.png b/Apps/W1/EDocumentConnectors/Tietoevry/app/ExtensionLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2c9a626cb9617350617c40cd73904129d4c108 GIT binary patch literal 5446 zcma)=S5VVywD$iAMIcgC2u+$uktV%J6$Dhev_NQrfC^Fsq!|cJ=^|Z`P&U*^N-uuwi#_w5i!*aB*89x5SkKKnv*ua91anhEW+omc005Zp+`e`1 zOsW4C1O3^nWxbYuCX9Z!?E(M*a`E2+jm<_J0|5K}om)4pLZ&wJ&3t(K(|ffcq#?ky z#^aeQO#|lO9vyUeb0ezqQtpipl3Sj#-xy!eh7lu@5+BnW zNhL-~3Zpw&1u=bMN*Q(sgYksq4dM>Iw7p&Qk_Su~b*PgEs#LK~^K}aDaTG_6Q?_tM<8wOS}`Z+?~Et8GB>T%(k7$9`DL!d5)f!ZoXco-vj+s_QLEs2cf zKM&F>#c9w|TmM9MFtl8L*cYQgl9khf5CYMR)DJOUf;M~a9|+ys@RYR zCusNC(CSlUk|r`qdS&ZKh$O=@#&e0>;W~S#|KjHdfLx!-J9r1JtP4RGIhS|Rm0eZ6 z7eOE~Zfo4Li~K^|&)d^-r?8Rh2Q}#ZjL=?VJZ7~hlp4(!U!0K%679I`OR&x54*0&4 znho|hKu)WR)4PUVA1}N;jXHg}AG+gSKQ6O_fEP^Y51!LwBERH09|t!GNx2KH4co>r zA%cgSHxh2Sezx-w!S5DTG#0zVCbnLM6BP}2P-G{8 zh**wJHj<652FS05bSQNx-0fS7^(wREYvZwpt;$!!k4H0U*iyhS8(syBDMv>L<)~LI zPl!Y^-cM{_J@{hY1=XJ#T=Ef(FD!I^r1^lca3c0ftVuvo-(%!Zn)C1bK{}-i*Jc); zIIc+o&iMgvboj&4`@5sF23MV!*zIVmA0>{1;*H*faMAG6EZ7XydTfaGyABAGx>)yl z@Y+|)SVxCx@!GWqspay7GBetK*s2@CJ?s{8v!(b|ShLb|O;3T1rAMB?DJ?Z`@013q zoyIvV84eYiS+?kRJOz`3AFcR~ZQ1Uq7wCnbSJ%-HZwhAnJ^4zDp2W8I)~WI7ush5> z&f3O)rj~2ZGr!c@=p3!n>jG-O#9`$7&WyF7bB}(rq4ldokUp5TY?E62r+YJbJp8Jf znDW3fYZ^nBQ9O}3?zH_*mZ9+G#HHnwop1Vfm!Df~{Z%D?5KzMN&RA>&#q8iCzTfAt zV#TyMeyyh8=M$8tyA|KeUwo_Q6Si)P)%n(W-*QE~08BG|>J!sQPq?IF;;%1ypP?Z` zK_0Un>p;9=9d675ELHboC0+fNMY&(;k(|=0TS>ka)BKI3q#)zx!Jp@zv0QfeEAjU< z=vI5@-d^A^-*#|P+b2QFiGxk4z<8Tp4p6{aOp88x>SQEa0M`VxX%IUb$bya!5EgRf6$fFw zp}jNTKUXjNe0x(;)Nu)Ij5K?QD0u6~mRHQ-!;6m#VP>)}=irAqy;f$e{W-EWnR75~ zm2b0u@r7ASk4x0oTqs9{f&F|eAmD*Gf^A;te7f}J{dXqLaH_4%D_(mnp0VmWhq>^E z&7>5*-mh>FX{w5SJf^#th&GrpOQk58U-+4 zq3$q~C4ySH7@lr>W+|c0`UF*ieC+3vC1$4m}F(ic|G7}QDt(t z7`#>$c4U-4LU_;nWHhdN9Fcv~L8h6M_}nW&EGTjgW(=c}uD9>eU^rDOrkNg_effOV z^8z_y=vNIt{`wOfgG2o^3ey`R!aP1=t7Mz@&MKK3>_BH_QkgNO@4IoQ-2d8EqsDg) zTMb-5lqlubRot-7!RD@+udO?O9_Da3XV5bvjW zXTb2psHUdeiIaI(lknQE_<+YlY31}R!VfoM_BuILQ{>Q89=LB5j;V|-yAW2gY82+~ zYlu~#*R(cHw2NO1h5xaiAD2oiIEQ-aQyA-D^y^z2ZHNfM{o(3M#SbqOP3>k9FOdDO z(t%c9hk)NCPe_8>=Y^U-_-6IwS-D0cE=pwdyLp!;r-fWiXtbUS$<dl!~WV$TR8 zP$KU?K>m?*O)mSGccn&kn|nj7NXFeo<0D=ue8s^~BK#P?J~gB}v5<0nK9GPipjT#9 zkm6yXFyLlgoUIDEVxw*0Z-WDqp8swCs(bcjAqdDLl1oUqYf#a`NjT6IO3?=P`FvUZ zlWC&lWb9_dexSz%N~-oscM`oC%b#KS|KS7AptwRX5h&1VDCKWzP{&??TFdF3h53&c zU(v)WhOr)#!V6Y6d7CzOO-@KF%@67>kh34@Exj7Rh}p5_0?yUeyC7@c7DHf+mW=~wpLeLYDA9#W-Ri*S|M@g zjPHH@qHrPuzq(+5y$V*UoFEg(g$$mRNUEF!C{IN3Rig{tU54W|OD_`M0G3u)B{WhC z*D?hTF7J+YdF8-Z-Uuw{3jBx`_!aus`uDDBecwuu&tsVpj2~DZJb2-!a2l??m{}er}lR6Lqu)-2+Vm)jr(g{nfQPx9-<^1d;k-d zkU{E^g7qwp+D`b+QtU5@+swaVKp9<`>sT~U)O!EEMBo!*)~s_<`6Yl z7fX2;ki>kVDfdietW1k;TYvaY({>?5X)&(d&_y<-J7Qa@b z(zwGCI=`P#^b>1>2#Y!9T5|AdtaU|zXxw9^KpIu6CAmQf$GzaeOJmYVsc3eh5%6lb z)t~(Ak2J`;KW_L6psME-h?xF6ryr4d{q;>-b`Q$L43T{r`{N?U6cqP(Q3f%kA8`c@ z<82KXjte|7u_Lo~MV!d%y$tYi(hzU$6t+*ml~Z&Mg{eK?@}^XEBK+-&j`Uv95x)=_ zZLs=Mpg_IuZenjm(~}b8Aggaaje8NX$A_7^G%-)!xtu)C{N|S<3hVOmU;{|i+q6zn zfr(1Ua*jF!%-dU3L}O2fvWAe%-4kxtXo_vJHF(AxSx)4AI8-$^uBQO_86Z_y%RZX4 zJpu5`pOAztxv?jXv9yx|r>#9!0|`71C-fli@v${6r+V$hgvcr|W_I`{=7*0s(PKQH zzn8r2+tSeD15stz|DIJ3%X%8EkyN?bsHhuq4(5D0Oewn_)-o)Nx$eNs{0V*ZTSVt4 z3ifXGGw5fBv+9b6d~Nl+08L4VbbZqf3DL^e?l@!uZVdWkdOpJPaE?{zF!ZI?c(vF3 zvX~OK4vktvm&R$MgNpiKA~&zT!1#H7!q1h7AQiuSNG9<=$64)Zym(UQ``(j#^hDzt}{aur0pS?mmBi&z4I0Jfieqh%Pa_A%N?_1OZHm-S{ zQ*)4(N_J;y7tRh0o>xs25-s9!M-)i;@I68#SGXB2XgS}N zx_r3%V)z1jLA_M&?)E^DT$kzdHMJF%e2w6BH@iI5tKWM+zcuhCsz@N0a_1RBvrdZx zjzD>V%;c4*$RkEv{zHuVyaB+ANl(iT8w{pJdziC7YcO2&(ciqGLhs@q-dNh! zkV_V_(_~$*>ND}j1yozMedYnu-_GKMh?IpP<@D+edeB4M%3@xr3oj{@mdFKoBVpm^)1_}Y^}rOWBSB|Uv)*-pTdiU ztW9~{qq5@iB+$QpbeJVKH^n^9vV})i>Z@2CHoY2$PC888c;#Yz-pHRK@EVheWhE!> zZzjPmy?0Ni8#=o_k6_s3DY7nS^&Bm}BW&ZfAuF7bQbDgAGM$dE)RM6RvdobKb&MhsYD4exRm9*jcHPjbz#rI?vj$u zPLF5Gjv|8}?ta9`&^H}Va3H;llghU-BC7pxo6?-eTP`7CUZHJrw{5 zhkDYeIYlhL%brQJ1X#<#fz#E}Z87Kj=Hde*f{l|A`9E my8jz0{9hgZgN;Rh%;ug!HJ{lE_@04L;EulOt!iDD=>G@$cU!Ii literal 0 HcmV?d00001 diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json new file mode 100644 index 0000000000..7c5a39ae22 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json @@ -0,0 +1,48 @@ +{ + "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "name": "E-Document Connector - Tietoevry", + "publisher": "Microsoft", + "brief": "E-Document Connector - Tietoevry", + "description": "E-Document Connector - Tietoevry", + "version": "26.0.0.0", + "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", + "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", + "help": "https://go.microsoft.com/fwlink/?linkid=2204541", + "url": "https://go.microsoft.com/fwlink/?LinkId=724011", + "logo": "ExtensionLogo.png", + "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2206603", + "dependencies": [ + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", + "name": "E-Document Core", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "name": "E-Documents Connector with External Endpoints", + "publisher": "Microsoft", + "version": "26.0.0.0" + } + ], + "screenshots": [ + + ], + "platform": "26.0.0.0", + "idRanges": [ + { + "from": 6380, + "to": 6389 + } + ], + "resourceExposurePolicy": { + "allowDebugging": true, + "allowDownloadingSource": true, + "includeSourceInSymbolFile": true + }, + "application": "26.0.0.0", + "target": "OnPrem", + "features": [ + "TranslationFile" + ] +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al new file mode 100644 index 0000000000..ecb771ed38 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al @@ -0,0 +1,148 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Foundation.Company; +using Microsoft.Sales.Customer; +using Microsoft.eServices.EDocument; +using System.Utilities; +using Microsoft.eServices.EDocument.Service; + +codeunit 6380 "Connection Events" +{ + SingleInstance = true; + EventSubscriberInstance = StaticAutomatic; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Validation", OnCheckSalesDocumentOnBeforeCheckCompanyVATRegNo, '', false, false)] + local procedure "PEPPOL Validation_OnCheckSalesDocumentOnBeforeCheckCompanyVATRegNo"(SalesHeader: Record "Sales Header"; CompanyInformation: Record "Company Information"; var IsHandled: Boolean) + var + ExternalConnectionSetup: Record "Connection Setup"; + begin + if not ExternalConnectionSetup.Get() then + exit; + + IsHandled := true; + if CompanyInformation."VAT Registration No." = '' then + Error(MissingCompInfVATRegNoErr, CompanyInformation.TableCaption()); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Validation", OnCheckSalesDocumentOnBeforeCheckCustomerVATRegNo, '', false, false)] + local procedure "PEPPOL Validation_OnCheckSalesDocumentOnBeforeCheckCustomerVATRegNo"(SalesHeader: Record "Sales Header"; Customer: Record Customer; var IsHandled: Boolean) + var + ServiceParticipant: Record "Service Participant"; + EDocumentService: Record "E-Document Service"; + EDocExtConnectionSetup: Record "Connection Setup"; + begin + if not EDocExtConnectionSetup.Get() then + exit; + EDocumentService.SetRange("Service Integration", EDocumentService."Service Integration"::Tietoevry); + if not EDocumentService.FindFirst() then + exit; + + IsHandled := true; + if (SalesHeader."Document Type" in [SalesHeader."Document Type"::Invoice, SalesHeader."Document Type"::Order, SalesHeader."Document Type"::"Credit Memo"]) and + Customer.Get(SalesHeader."Bill-to Customer No.") + then + if Customer."VAT Registration No." = '' then + Error(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); + end; + + [EventSubscriber(ObjectType::Table, Database::"E-Document Service", 'OnAfterValidateEvent', 'Document Format', false, false)] + local procedure OnAfterValidateDocumentFormat(var Rec: Record "E-Document Service"; var xRec: Record "E-Document Service"; CurrFieldNo: Integer) + var + EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; + begin + if Rec."Document Format" <> Rec."Document Format"::"TE PEPPOL BIS 3.0" then + exit; + + EDocServiceSupportedType.SetRange("E-Document Service Code", Rec.Code); + if EDocServiceSupportedType.IsEmpty() then begin + EDocServiceSupportedType.Init(); + EDocServiceSupportedType."E-Document Service Code" := Rec.Code; + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Credit Memo"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Credit Memo"; + EDocServiceSupportedType.Insert(); + end; + 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 + EDocumentService: Record "E-Document Service"; + begin + if not EDocumentService.Get(Rec.Service) then + exit; + if EDocumentService."Service Integration" <> EDocumentService."Service Integration"::Tietoevry then + exit; + if Rec."Participant Identifier" <> '' then + if not 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/codeunit/Cod6381.Connection.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al new file mode 100644 index 0000000000..602f7890cd --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al @@ -0,0 +1,127 @@ +// ------------------------------------------------------------------------------------------------ +// 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 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 new file mode 100644 index 0000000000..9927a8be59 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6382.IntegrationImpl.al @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------------------------ +// 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 new file mode 100644 index 0000000000..ea024b687f --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6383.APIRequests.al @@ -0,0 +1,174 @@ +// ------------------------------------------------------------------------------------------------ +// 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 new file mode 100644 index 0000000000..5a4ac1382f --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6384.Authenticator.al @@ -0,0 +1,371 @@ +// ------------------------------------------------------------------------------------------------ +// 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/Cod6385.EDocTEPEPPOLBIS30.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al new file mode 100644 index 0000000000..92530e2cf1 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al @@ -0,0 +1,145 @@ +namespace Microsoft.eServices.EDocument.IO.Peppol; + +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 System.IO; +using Microsoft.eServices.EDocument.Service; + +codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" +{ + procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: Enum "E-Document Processing Phase") + var + SalesHeader: Record "Sales Header"; + SalesInvoiceHeader: Record "Sales Invoice Header"; + SalesCrMemoHeader: Record "Sales Cr.Memo Header"; + ServiceInvoiceHeader: Record "Service Invoice Header"; + ServiceCrMemoHeader: Record "Service Cr.Memo Header"; + PEPPOLValidation: Codeunit "PEPPOL Validation"; + PEPPOLServiceValidation: Codeunit "PEPPOL Service Validation"; + begin + case SourceDocumentHeader.Number of + Database::"Sales Header": + begin + SourceDocumentHeader.SetTable(SalesHeader); + PEPPOLValidation.Run(SalesHeader); + end; + Database::"Sales Invoice Header": + begin + SourceDocumentHeader.SetTable(SalesInvoiceHeader); + PEPPOLValidation.CheckSalesInvoice(SalesInvoiceHeader); + end; + Database::"Sales Cr.Memo Header": + begin + SourceDocumentHeader.SetTable(SalesCrMemoHeader); + PEPPOLValidation.CheckSalesCreditMemo(SalesCrMemoHeader); + end; + Database::"Service Invoice Header": + begin + SourceDocumentHeader.SetTable(ServiceInvoiceHeader); + PEPPOLServiceValidation.CheckServiceInvoice(ServiceInvoiceHeader); + end; + Database::"Service Cr.Memo Header": + begin + SourceDocumentHeader.SetTable(ServiceCrMemoHeader); + PEPPOLServiceValidation.CheckServiceCreditMemo(ServiceCrMemoHeader); + end; + 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") + var + ServiceParticipant: Record "Service Participant"; + TempXMLBuffer: Record "XML Buffer" temporary; + EDocErrorHelper: Codeunit "E-Document Error Helper"; + DocOutStream: OutStream; + DocInStream: InStream; + MessageDocumentId: Text; + begin + TempBlob.CreateOutStream(DocOutStream); + case EDocument."Document Type" of + EDocument."Document Type"::"Sales Invoice", EDocument."Document Type"::"Service Invoice": + GenerateInvoiceXMLFile(SourceDocumentHeader, DocOutStream); + EDocument."Document Type"::"Sales Credit Memo", EDocument."Document Type"::"Service Credit Memo": + GenerateCrMemoXMLFile(SourceDocumentHeader, DocOutStream); + else + EDocErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(DocumentTypeNotSupportedErr, EDocument.FieldCaption("Document Type"), EDocument."Document Type")); + end; + + EDocument.Find(); + ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No."); + EDocument."Bill-to/Pay-to Id" := ServiceParticipant."Participant Identifier"; + + TempBlob.CreateInStream(DocInStream); + TempXMLBuffer.LoadFromStream(DocInStream); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'ProfileID'); + if TempXMLBuffer.FindFirst() then + EDocument."Message Profile Id" := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); + TempXMLBuffer.SetRange(Name, 'xmlns'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '::' + TempXMLBuffer.Name; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'CustomizationID'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '##' + TempXMLBuffer.Value + '::2.1'; + + EDocument."Message Document Id" := MessageDocumentId; + 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"); + begin + + end; + + procedure GetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") + begin + ImportTEPeppol.ParseBasicInfo(EDocument, TempBlob); + end; + + procedure GetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + var + TempPurchaseHeader: Record "Purchase Header" temporary; + TempPurchaseLine: Record "Purchase Line" temporary; + begin + ImportTEPeppol.ParseCompleteInfo(EDocument, TempPurchaseHeader, TempPurchaseLine, TempBlob); + + CreatedDocumentHeader.GetTable(TempPurchaseHeader); + CreatedDocumentLines.GetTable(TempPurchaseLine); + end; + + local procedure GenerateInvoiceXMLFile(VariantRec: Variant; var OutStr: OutStream) + var + SalesInvoicePEPPOLBIS30: XMLport "Sales Invoice - PEPPOL BIS 3.0"; + begin + SalesInvoicePEPPOLBIS30.Initialize(VariantRec); + SalesInvoicePEPPOLBIS30.SetDestination(OutStr); + SalesInvoicePEPPOLBIS30.Export(); + end; + + local procedure GenerateCrMemoXMLFile(VariantRec: Variant; var OutStr: OutStream) + var + SalesCrMemoPEPPOLBIS30: XMLport "Sales Cr.Memo - PEPPOL BIS 3.0"; + begin + SalesCrMemoPEPPOLBIS30.Initialize(VariantRec); + SalesCrMemoPEPPOLBIS30.SetDestination(OutStr); + SalesCrMemoPEPPOLBIS30.Export(); + end; + + var + ImportTEPeppol: Codeunit "EDoc Import Tietoevry"; + 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/codeunit/Cod6386.EDocImportTietoevry.al new file mode 100644 index 0000000000..2f7fcaef69 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al @@ -0,0 +1,577 @@ +namespace Microsoft.eServices.EDocument.IO.Peppol; + +using Microsoft.eServices.EDocument; +using Microsoft.Sales.Customer; +using System.Utilities; +using Microsoft.Purchases.Document; +using System.IO; +using System.Text; +using Microsoft.Foundation.Attachment; +using Microsoft.Purchases.Vendor; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.eServices.EDocument.Service; + +codeunit 6386 "EDoc Import Tietoevry" +{ + + procedure ParseBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") + var + TempXMLBuffer: Record "XML Buffer" temporary; + GLSetup: Record "General Ledger Setup"; + DocStream: InStream; + begin + TempXMLBuffer.DeleteAll(); + TempBlob.CreateInStream(DocStream, TextEncoding::UTF8); + TempXMLBuffer.LoadFromStream(DocStream); + + GLSetup.Get(); + LCYCode := GLSetup."LCY Code"; + + EDocument.Direction := EDocument.Direction::Incoming; + + case UpperCase(GetDocumentType(TempXMLBuffer)) of + 'INVOICE': + ParseInvoiceBasicInfo(EDocument, TempXMLBuffer); + 'CREDITNOTE': + ParseCreditMemoBasicInfo(EDocument, TempXMLBuffer); + end; + end; + + procedure ParseCompleteInfo(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempBlob: Codeunit "Temp Blob") + var + TempXMLBuffer: Record "XML Buffer" temporary; + DocStream: InStream; + begin + TempXMLBuffer.DeleteAll(); + TempBlob.CreateInStream(DocStream, TextEncoding::UTF8); + TempXMLBuffer.LoadFromStream(DocStream); + + PurchaseHeader."Buy-from Vendor No." := EDocument."Bill-to/Pay-to No."; + PurchaseHeader."Currency Code" := EDocument."Currency Code"; + + case UpperCase(GetDocumentType(TempXMLBuffer)) of + 'INVOICE': + CreateInvoice(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + 'CREDITNOTE': + CreateCreditMemo(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); + end; + end; + + local procedure ParseInvoiceBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary) + var + DueDate, IssueDate : Text; + Currency: Text[10]; + 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'); + + DueDate := GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DueDate'); + if DueDate <> '' then + Evaluate(EDocument."Due Date", DueDate, 9); + IssueDate := 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); + + EDocument."Order No." := CopyStr(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 + EDocument."Currency Code" := Currency; + end; + + local procedure ParseCreditMemoBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary) + var + DueDate, IssueDate : Text; + Currency: Text[10]; + 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'); + + DueDate := GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate'); + if DueDate <> '' then + Evaluate(EDocument."Due Date", DueDate, 9); + IssueDate := GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:IssueDate'); + if IssueDate <> '' then + Evaluate(EDocument."Document Date", IssueDate, 9); + + Evaluate(EDocument."Amount Excl. VAT", GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); + 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 + EDocument."Currency Code" := Currency; + end; + + local procedure ParseAccountingSupplierParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + Vendor: Record Vendor; + ServiceParticipant: Record "Service Participant"; + VendorName, VendorAddress : Text; + 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)); + 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); + EDocument."Bill-to/Pay-to Name" := CopyStr(VendorName, 1, MaxStrLen(EDocument."Bill-to/Pay-to Name")); + end; + + Vendor := 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; + end; + end; + + local procedure ParseAccountingCustomerParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + 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 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.")); + 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) + var + DocumentAttachment: Record "Document Attachment"; + DocumentAttachmentData: Codeunit "Temp Blob"; + 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.")); + // Currency + PurchaseHeader.Insert(); + + TempXMLBuffer.Reset(); + if TempXMLBuffer.FindSet() then + repeat + ParseInvoice(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); + until TempXMLBuffer.Next() = 0; + + // Insert last document attachment + if DocumentAttachment."No." <> '' then begin + DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + Clear(DocumentAttachment); + end; + + // Insert last line + PurchaseLine.Insert(); + PurchaseHeader.Modify(); + + // Allowance charge + 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) + var + DocumentAttachment: Record "Document Attachment"; + DocumentAttachmentData: Codeunit "Temp Blob"; + InStream: InStream; + begin + PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::"Credit Memo"; + PurchaseHeader."No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); + PurchaseHeader.Insert(); + + TempXMLBuffer.Reset(); + if TempXMLBuffer.FindSet() then + repeat + ParseCreditMemo(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); + until TempXMLBuffer.Next() = 0; + + // Insert last document attachment + if DocumentAttachment."No." <> '' then begin + DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + Clear(DocumentAttachment); + end; + + // Insert last line + PurchaseLine.Insert(); + PurchaseHeader.Modify(); + + // Allowance charge + 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) + var + LineNo: Integer; + DocumentText: Text; + begin + case EDocument."Document Type" of + EDocument."Document Type"::"Purchase Invoice": + DocumentText := '/Invoice'; + EDocument."Document Type"::"Purchase Credit Memo": + DocumentText := '/CreditNote'; + end; + + TempXMLBuffer.Reset(); + TempXMLBuffer.SetFilter(Path, DocumentText + '/cac:AllowanceCharge*'); + + PurchaseLine.FindLast(); + LineNo := PurchaseLine."Line No." + 10000; + + if TempXMLBuffer.FindSet() then + repeat + case TempXMLBuffer.Path of + DocumentText + '/cac:AllowanceCharge/cbc:ChargeIndicator': + if TempXMLBuffer.Value = 'true' then begin + SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." := LineNo; + PurchaseLine.Quantity := 1; + PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; + end; + DocumentText + '/cac:AllowanceCharge/cbc:Amount': + if TempXMLBuffer.Value <> '' then begin + Evaluate(PurchaseLine."Direct Unit Cost", TempXMLBuffer.Value, 9); + Evaluate(PurchaseLine.Amount, TempXMLBuffer.Value, 9); + end; + DocumentText + '/cac:AllowanceCharge/cbc:AllowanceChargeReason': + PurchaseLine.Description := CopyStr(TempXMLBuffer.Value, 1, MaxStrLen(PurchaseLine.Description)); + + end; + until TempXMLBuffer.Next() = 0; + + SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + end; + + local procedure SetGLAccountAndInsertLine(var EDocument: Record "E-Document"; var PurchaseLine: record "Purchase Line" temporary; var LineNo: Integer) + var + RecRef: RecordRef; + begin + if PurchaseLine."Line No." = LineNo then begin + RecRef.GetTable(PurchaseLine); + EDocumentImportHelper.FindGLAccountForLine(EDocument, RecRef); + PurchaseLine."No." := RecRef.Field(PurchaseLine.FieldNo("No.")).Value; + PurchaseLine.Insert(); + LineNo += 10000; + end; + end; + + /// + /// Parses credit memo information line by line from TempXMLBuffer. + /// We handle the insert of Purchase Order Line and Document Attachment after the call to this function. + /// + local procedure ParseCreditMemo(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var DocumentAttachment: Record "Document Attachment"; DocumentAttachmentData: Codeunit "Temp Blob"; var TempXMLBuffer: Record "XML Buffer" temporary) + var + Base64Convert: Codeunit "Base64 Convert"; + OutStream: OutStream; + InStream: InStream; + Path, Value : Text; + begin + Path := TempXMLBuffer.Path; + Value := TempXMLBuffer.Value; + case Path of + '/CreditNote/cbc:ID': + PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); + '/CreditNote/cbc:IssueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Document Date", Value, 9); + '/CreditNote/cac:OrderReference/cbc:ID': + PurchaseHeader."Vendor Order No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); + '/CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID': + PurchaseHeader."Applies-to Doc. No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Applies-to Doc. No.")); + '/CreditNote/cac:PayeeParty/cac:PartyName/cbc:Name': + PurchaseHeader."Pay-to Name" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Pay-to Name")); + '/CreditNote/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); + '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Amount Including VAT", Value, 9); + '/CreditNote/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ID': + PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); + '/CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName': + PurchaseHeader."Buy-from Address" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Address")); + '/CreditNote/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID', '/CreditNote/cac:PayeeParty/cac:PartyIdentification/cbc:ID': + PurchaseHeader."VAT Registration No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."VAT Registration No.")); + '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Due Date", Value, 9); + '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount': + if Value <> '' then + Evaluate(PurchaseHeader.Amount, Value, 9); + '/CreditNote/cac:AdditionalDocumentReference/cbc:ID': + begin + if DocumentAttachment."No." <> '' then begin + DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + Clear(DocumentAttachment); + end; + + DocumentAttachment.Init(); + DocumentAttachment."No." := CopyStr(PurchaseHeader."Vendor Invoice No.", 1, MaxStrLen(DocumentAttachment."No.")); + end; + '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject': + begin + DocumentAttachmentData.CreateOutStream(OutStream, TextEncoding::UTF8); + TempXMLBuffer.CalcFields("Value BLOB"); + TempXMLBuffer."Value BLOB".CreateInStream(InStream); + InStream.Read(Value, InStream.Length); + Base64Convert.FromBase64(Value, OutStream); + end; + '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@mimeCode': + DocumentAttachment.Validate("File Extension", DetermineFileType(Value)); + '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@filename': + DocumentAttachment."File Name" := CopyStr(Value.Split('.').Get(1), 1, MaxStrLen(DocumentAttachment."File Name")); + '/CreditNote/cac:CreditNoteLine': + begin + if PurchaseLine."Document No." <> '' then + PurchaseLine.Insert(); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + end; + '/CreditNote/cac:CreditNoteLine/cbc:CreditedQuantity': + if Value <> '' then + Evaluate(PurchaseLine.Quantity, Value, 9); + '/CreditNote/cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode': + PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + '/CreditNote/cac:CreditNoteLine/cbc:LineExtensionAmount': + if Value <> '' then + Evaluate(PurchaseLine.Amount, Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:AllowanceCharge/cbc:Amount': + if Value <> '' then + Evaluate(PurchaseLine."Line Discount Amount", Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:TaxTotal/cbc:TaxAmount': + if Value <> '' then + Evaluate(PurchaseLine."Amount Including VAT", Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:Item/cbc:Description': + PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); + '/CreditNote/cac:CreditNoteLine/cac:Item/cbc:Name': + PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); + '/CreditNote/cac:CreditNoteLine/cac:Item/cac:SellersItemIdentification/cbc:ID': + PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); + '/CreditNote/cac:CreditNoteLine/cac:Item/cac:StandardItemIdentification/cbc:ID': + PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); + '/CreditNote/cac:CreditNoteLine/cbc:ID': + Evaluate(PurchaseLine."Line No.", Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': + if Value <> '' then + Evaluate(PurchaseLine."VAT %", Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:Price/cbc:PriceAmount': + if Value <> '' then + Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); + '/CreditNote/cac:CreditNoteLine/cac:Price/cbc:BaseQuantity': + if Value <> '' then + Evaluate(PurchaseLine."Quantity (Base)", Value, 9); + '/CreditNote/cac:CreditNoteLine/cbc:Note': + setlineType(PurchaseLine, Value); + end + end; + + /// + /// Parses invoice information line by line from TempXMLBuffer. + /// We handle the insert of Purchase Order Line and Document Attachment after the call to this function. + /// + local procedure ParseInvoice(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; var DocumentAttachment: Record "Document Attachment"; DocumentAttachmentData: Codeunit "Temp Blob"; var TempXMLBuffer: Record "XML Buffer" temporary) + var + Base64Convert: Codeunit "Base64 Convert"; + OutStream: OutStream; + InStream: InStream; + Path, Value : Text; + begin + Path := TempXMLBuffer.Path; + Value := TempXMLBuffer.Value; + case Path of + '/Invoice/cbc:ID': + PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); + '/Invoice/cac:OrderReference/cbc:ID': + PurchaseHeader."Vendor Order No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); + '/Invoice/cac:PayeeParty/cac:PartyName/cbc:Name': + PurchaseHeader."Pay-to Name" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Pay-to Name")); + '/Invoice/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); + '/Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Amount Including VAT", Value, 9); + '/Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ID': + PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); + '/Invoice/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName': + PurchaseHeader."Buy-from Address" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Address")); + '/Invoice/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID': + PurchaseHeader."VAT Registration No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."VAT Registration No.")); + '/Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount': + if Value <> '' then + Evaluate(PurchaseHeader.Amount, Value, 9); + '/Invoice/cbc:DueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Due Date", Value, 9); + '/Invoice/cbc:IssueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Document Date", Value, 9); + '/Invoice/cac:AdditionalDocumentReference/cbc:ID': + begin + if DocumentAttachment."No." <> '' then begin + DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + Clear(DocumentAttachment); + end; + + DocumentAttachment.Init(); + DocumentAttachment."No." := CopyStr(Value, 1, MaxStrLen(DocumentAttachment."No.")); + end; + '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject': + begin + DocumentAttachmentData.CreateOutStream(OutStream, TextEncoding::UTF8); + TempXMLBuffer.CalcFields("Value BLOB"); + TempXMLBuffer."Value BLOB".CreateInStream(InStream); + InStream.Read(Value, InStream.Length); + Base64Convert.FromBase64(Value, OutStream); + end; + '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@mimeCode': + DocumentAttachment.Validate("File Extension", DetermineFileType(Value)); + '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@filename': + DocumentAttachment."File Name" := CopyStr(Value.Split('.').Get(1), 1, MaxStrLen(DocumentAttachment."File Name")); + '/Invoice/cac:InvoiceLine': + begin + if PurchaseLine."Document No." <> '' then + PurchaseLine.Insert(); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + end; + '/Invoice/cac:InvoiceLine/cbc:InvoicedQuantity': + if Value <> '' then + Evaluate(PurchaseLine.Quantity, Value, 9); + '/Invoice/cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode': + PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + '/Invoice/cac:InvoiceLine/cbc:LineExtensionAmount': + if Value <> '' then + Evaluate(PurchaseLine.Amount, Value, 9); + + '/Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount': + if Value <> '' then + Evaluate(PurchaseLine."Line Discount Amount", Value, 9); + '/Invoice/cac:InvoiceLine/cac:TaxTotal/cbc:TaxAmount': + if Value <> '' then + Evaluate(PurchaseLine."Amount Including VAT", Value, 9); + '/Invoice/cac:InvoiceLine/cac:Item/cbc:Description': + PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); + '/Invoice/cac:InvoiceLine/cac:Item/cbc:Name': + PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); + '/Invoice/cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID': + PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); + '/Invoice/cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID': + PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); + '/Invoice/cac:InvoiceLine/cbc:ID': + Evaluate(PurchaseLine."Line No.", Value, 9); + '/Invoice/cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': + if Value <> '' then + Evaluate(PurchaseLine."VAT %", Value, 9); + '/Invoice/cac:InvoiceLine/cac:Price/cbc:PriceAmount': + if Value <> '' then + Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); + '/Invoice/cac:InvoiceLine/cac:Price/cbc:BaseQuantity': + if Value <> '' then + Evaluate(PurchaseLine."Quantity (Base)", Value, 9); + '/Invoice/cac:InvoiceLine/cbc:Note': + setlineType(PurchaseLine, Value); + end; + end; + + procedure DetermineFileType(MimeType: Text) FileExension: Text + begin + case MimeType of + 'image/jpeg': + exit('jpeg'); + 'image/png': + exit('png'); + 'application/pdf': + exit('pdf'); + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.oasis.opendocument.spreadsheet': + exit('xlsx'); + else + exit(''); + end; + end; + + local procedure GetNodeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Path, XPath); + + if TempXMLBuffer.FindFirst() then + exit(TempXMLBuffer.Value); + end; + + local procedure GetNodeAttributeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); + TempXMLBuffer.SetRange(Path, XPath); + + if TempXMLBuffer.FindFirst() then + exit(TempXMLBuffer.Value); + end; + + local procedure GetDocumentType(var TempXMLBuffer: Record "XML Buffer" temporary): Text + var + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange("Parent Entry No.", 0); + + if not TempXMLBuffer.FindFirst() then + Error('Invalid XML file'); + + TempXMLBuffer.Reset(); + exit(TempXMLBuffer.Name); + end; + + local procedure SetLineType(var PurchaseLine: record "Purchase Line" temporary; Value: Text): Text + var + begin + case UpperCase(Value) of + 'ITEM': + PurchaseLine.Type := PurchaseLine.Type::Item; + 'CHARGE (ITEM)': + PurchaseLine.Type := PurchaseLine.Type::"Charge (Item)"; + 'RESOURCE': + PurchaseLine.Type := PurchaseLine.Type::Resource; + 'G/L ACCOUNT': + PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; + end; + end; + + [EventSubscriber(ObjectType::Table, Database::"E-Document Log", 'OnBeforeExportDataStorage', '', false, false)] + local procedure SetFileExt(EDocumentLog: Record "E-Document Log"; var FileName: Text) + begin + FileName += '.xml'; + end; + + var + EDocumentAttachmentGen: Codeunit "E-Doc. Attachment Processor"; + EDocumentImportHelper: Codeunit "E-Document Import Helper"; + LCYCode: Code[10]; +} \ 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 new file mode 100644 index 0000000000..5f0df6b5fe --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al @@ -0,0 +1,401 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +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/enum/Enum6380.EDocumentSendMode.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al new file mode 100644 index 0000000000..6d16b11bab --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enum/Enum6380.EDocumentSendMode.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +enum 6380 "E-Document Send Mode" +{ + Extensible = true; + + value(0; Production) + { + Caption = 'Production'; + } + value(1; Certification) + { + Caption = 'Certification'; + } +} \ 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 new file mode 100644 index 0000000000..bbe24cd5b6 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6380.EDocFormatExt.al @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------------------------ +// 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/enumextension/Enum-Ext6381.EDocExtIntegration.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al new file mode 100644 index 0000000000..e022ca7457 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/enumextension/Enum-Ext6381.EDocExtIntegration.al @@ -0,0 +1,15 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +enumextension 6381 "E-Doc. Ext. Integration" extends "E-Document Integration" +{ + value(6381; "Tietoevry") + { + Implementation = "E-Document Integration" = "Integration Impl."; + } +} \ 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/page/Pag6380.ConnectionSetupCard.al new file mode 100644 index 0000000000..bb81462c0f --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/page/Pag6380.ConnectionSetupCard.al @@ -0,0 +1,140 @@ +// ------------------------------------------------------------------------------------------------ +// 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; +using System.Environment; + +page 6380 "Connection Setup Card" +{ + PageType = Card; + SourceTable = "Connection Setup"; + ApplicationArea = Basic, Suite; + UsageCategory = None; + Caption = 'Tietoevry Connection Setup'; + + layout + { + area(Content) + { + group(General) + { + field(ClientID; ClientID) + { + Caption = 'Client ID'; + ToolTip = 'Specifies the client ID token.'; + ApplicationArea = Basic, Suite; + ExtendedDatatype = Masked; +#if not DOCKER + Visible = not IsSaaS; +#endif + ShowMandatory = true; + + trigger OnValidate() + begin + TietoevryAuth.SetClientId(Rec."Client ID", ClientID); + end; + } + field(ClientSecret; ClientSecret) + { + Caption = 'Client Secret'; + ToolTip = 'Specifies the client secret token.'; + ApplicationArea = Basic, Suite; + ExtendedDatatype = Masked; +#if not DOCKER + Visible = not IsSaaS; +#endif + ShowMandatory = true; + + trigger OnValidate() + begin + TietoevryAuth.SetClientSecret(Rec."Client Secret", ClientSecret); + end; + } + field("Authentication URL"; Rec."Authentication URL") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the URL to connect to Tietoevry Online.'; + Visible = not IsSaaS; + } + field("Inbound API Url"; Rec."Inbound API URL") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the inbound API URL.'; + } + field("Outbound API Url"; Rec."Outbound API URL") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the outbound API URL.'; + } + field("Company Id"; Rec."Company Id") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the company ID.'; + ShowMandatory = true; + } + field("Send Mode"; Rec."Send Mode") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the send mode.'; + ShowMandatory = true; + } + } + } + } + + actions + { + area(processing) + { + action(TestOAuthSetup) + { + ApplicationArea = Basic, Suite; + Caption = 'Test OAuth 2.0 setup'; + Image = Setup; + Promoted = true; + PromotedCategory = Process; + PromotedOnly = true; + ToolTip = 'Tests the OAuth 2.0 setup.'; + + trigger OnAction() + var + TietoevryAuth: Codeunit Authenticator; + begin + TietoevryAuth.TestOAuth2Setup(); + FeatureTelemetry.LogUptake('0000MSD', ExternalServiceTok, Enum::"Feature Uptake Status"::"Set up"); + end; + } + } + } + + trigger OnOpenPage() + var + EnvironmentInfo: Codeunit "Environment Information"; + + begin + IsSaaS := EnvironmentInfo.IsSaaS(); + + TietoevryAuth.InitConnectionSetup(); + TietoevryAuth.IsClientCredsSet(ClientID, ClientSecret); + + FeatureTelemetry.LogUptake('0000LST', ExternalServiceTok, Enum::"Feature Uptake Status"::Discovered); + end; + + trigger OnClosePage() + var + begin + Rec.TestField("Company Id"); + Rec.TestField("Send Mode"); + end; + + var + TietoevryAuth: Codeunit Authenticator; + FeatureTelemetry: Codeunit "Feature Telemetry"; + [NonDebuggable] + ClientID, ClientSecret : Text; + IsSaaS: Boolean; + ExternalServiceTok: Label 'ExternalServiceConnector', Locked = true; +} diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al new file mode 100644 index 0000000000..954c5a147d --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6380.TEEDocConnEdit.al @@ -0,0 +1,14 @@ +// ------------------------------------------------------------------------------------------------ +// 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 6380 "TE EDocConn. - Edit" +{ + Access = Public; + Assignable = true; + IncludedPermissionSets = "TE EDocConn. - Read"; + + Permissions = tabledata "Connection Setup" = IM; +} \ 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/permissionset/PermissionSet6381.TEEDocConnRead.al new file mode 100644 index 0000000000..fc87ec1c29 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6381.TEEDocConnRead.al @@ -0,0 +1,14 @@ +// ------------------------------------------------------------------------------------------------ +// 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 6381 "TE EDocConn. - Read" +{ + Access = Public; + Assignable = true; + IncludedPermissionSets = "TE EDoc. Conn. Objects"; + + Permissions = tabledata "Connection Setup" = R; +} \ 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 new file mode 100644 index 0000000000..7c2f5df964 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionset/PermissionSet6382.TEEDocConnObjects.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// 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 diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al new file mode 100644 index 0000000000..ad69d987dc --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6380.D365BasicEDocumentConnector.al @@ -0,0 +1,12 @@ +// ------------------------------------------------------------------------------------------------ +// 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.AccessControl; + +permissionsetextension 6380 "D365 Basic - EDocument Connector" extends "D365 BASIC" +{ + IncludedPermissionSets = "TE EDocConn. - Edit"; +} \ 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/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al new file mode 100644 index 0000000000..61be743078 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/permissionsetextension/PermissionSetExt-Ext6381.D365ReadEDocumentConnector.al @@ -0,0 +1,13 @@ +// ------------------------------------------------------------------------------------------------ +// 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.AccessControl; +using Microsoft.EServices.EDocumentConnector; + +permissionsetextension 6381 "D365 Read - EDocument Connector" extends "D365 READ" +{ + IncludedPermissionSets = "TE EDocConn. - Edit"; +} \ 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/table/Tab6380.ConnectionSetup.al new file mode 100644 index 0000000000..f26e7d4905 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/table/Tab6380.ConnectionSetup.al @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +table 6380 "Connection Setup" +{ + fields + { + field(1; PK; Code[10]) + { + DataClassification = CustomerContent; + } + field(3; "OAuth Feature GUID"; GUID) + { + Caption = 'OAuth 2.0 Code'; + DataClassification = CustomerContent; + } + field(4; "Authentication URL"; Text[250]) + { + Caption = 'Authentication URL'; + DataClassification = CustomerContent; + } + field(7; "Outbound API URL"; Text[250]) + { + Caption = 'Outbound API URL'; + DataClassification = CustomerContent; + } + field(8; "Inbound API URL"; Text[250]) + { + Caption = 'Inbound API URL'; + DataClassification = CustomerContent; + } + field(9; "Company Id"; Text[100]) + { + Caption = 'Company ID'; + DataClassification = CustomerContent; + } + field(10; "Client ID"; Guid) + { + Caption = 'Client ID'; + DataClassification = EndUserIdentifiableInformation; + } + field(11; "Client Secret"; Guid) + { + Caption = 'Client Secret'; + DataClassification = EndUserIdentifiableInformation; + } + field(12; "Send Mode"; Enum "E-Document Send Mode") + { + Caption = 'Send Mode'; + DataClassification = EndUserIdentifiableInformation; + + trigger OnValidate() + var + TietoevryAuth: Codeunit Authenticator; + begin + TietoevryAuth.SetDefaultEndpoints(Rec, "Send Mode"); + end; + } + } + + keys + { + key(Key1; PK) + { + Clustered = true; + } + } +} \ 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/tableextension/Tab-Ext6382.EDocTEExtEDocument.al new file mode 100644 index 0000000000..24e0ae642d --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/tableextension/Tab-Ext6382.EDocTEExtEDocument.al @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +using Microsoft.EServices.EDocument; + +tableextension 6382 "E-Doc. TE Ext. EDocument" extends "E-Document" +{ + fields + { + field(6380; "Bill-to/Pay-to Id"; Text[100]) + { + DataClassification = CustomerContent; + } + field(6381; "Message Id"; Text[50]) + { + DataClassification = CustomerContent; + } + field(6382; "Message Profile Id"; Text[50]) + { + DataClassification = CustomerContent; + } + field(6383; "Message Document Id"; Text[200]) + { + DataClassification = CustomerContent; + } + field(6384; "Receiving Company Id"; Text[100]) + { + DataClassification = CustomerContent; + } + } +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/ExtensionLogo.png b/Apps/W1/EDocumentConnectors/Tietoevry/test/ExtensionLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2c9a626cb9617350617c40cd73904129d4c108 GIT binary patch literal 5446 zcma)=S5VVywD$iAMIcgC2u+$uktV%J6$Dhev_NQrfC^Fsq!|cJ=^|Z`P&U*^N-uuwi#_w5i!*aB*89x5SkKKnv*ua91anhEW+omc005Zp+`e`1 zOsW4C1O3^nWxbYuCX9Z!?E(M*a`E2+jm<_J0|5K}om)4pLZ&wJ&3t(K(|ffcq#?ky z#^aeQO#|lO9vyUeb0ezqQtpipl3Sj#-xy!eh7lu@5+BnW zNhL-~3Zpw&1u=bMN*Q(sgYksq4dM>Iw7p&Qk_Su~b*PgEs#LK~^K}aDaTG_6Q?_tM<8wOS}`Z+?~Et8GB>T%(k7$9`DL!d5)f!ZoXco-vj+s_QLEs2cf zKM&F>#c9w|TmM9MFtl8L*cYQgl9khf5CYMR)DJOUf;M~a9|+ys@RYR zCusNC(CSlUk|r`qdS&ZKh$O=@#&e0>;W~S#|KjHdfLx!-J9r1JtP4RGIhS|Rm0eZ6 z7eOE~Zfo4Li~K^|&)d^-r?8Rh2Q}#ZjL=?VJZ7~hlp4(!U!0K%679I`OR&x54*0&4 znho|hKu)WR)4PUVA1}N;jXHg}AG+gSKQ6O_fEP^Y51!LwBERH09|t!GNx2KH4co>r zA%cgSHxh2Sezx-w!S5DTG#0zVCbnLM6BP}2P-G{8 zh**wJHj<652FS05bSQNx-0fS7^(wREYvZwpt;$!!k4H0U*iyhS8(syBDMv>L<)~LI zPl!Y^-cM{_J@{hY1=XJ#T=Ef(FD!I^r1^lca3c0ftVuvo-(%!Zn)C1bK{}-i*Jc); zIIc+o&iMgvboj&4`@5sF23MV!*zIVmA0>{1;*H*faMAG6EZ7XydTfaGyABAGx>)yl z@Y+|)SVxCx@!GWqspay7GBetK*s2@CJ?s{8v!(b|ShLb|O;3T1rAMB?DJ?Z`@013q zoyIvV84eYiS+?kRJOz`3AFcR~ZQ1Uq7wCnbSJ%-HZwhAnJ^4zDp2W8I)~WI7ush5> z&f3O)rj~2ZGr!c@=p3!n>jG-O#9`$7&WyF7bB}(rq4ldokUp5TY?E62r+YJbJp8Jf znDW3fYZ^nBQ9O}3?zH_*mZ9+G#HHnwop1Vfm!Df~{Z%D?5KzMN&RA>&#q8iCzTfAt zV#TyMeyyh8=M$8tyA|KeUwo_Q6Si)P)%n(W-*QE~08BG|>J!sQPq?IF;;%1ypP?Z` zK_0Un>p;9=9d675ELHboC0+fNMY&(;k(|=0TS>ka)BKI3q#)zx!Jp@zv0QfeEAjU< z=vI5@-d^A^-*#|P+b2QFiGxk4z<8Tp4p6{aOp88x>SQEa0M`VxX%IUb$bya!5EgRf6$fFw zp}jNTKUXjNe0x(;)Nu)Ij5K?QD0u6~mRHQ-!;6m#VP>)}=irAqy;f$e{W-EWnR75~ zm2b0u@r7ASk4x0oTqs9{f&F|eAmD*Gf^A;te7f}J{dXqLaH_4%D_(mnp0VmWhq>^E z&7>5*-mh>FX{w5SJf^#th&GrpOQk58U-+4 zq3$q~C4ySH7@lr>W+|c0`UF*ieC+3vC1$4m}F(ic|G7}QDt(t z7`#>$c4U-4LU_;nWHhdN9Fcv~L8h6M_}nW&EGTjgW(=c}uD9>eU^rDOrkNg_effOV z^8z_y=vNIt{`wOfgG2o^3ey`R!aP1=t7Mz@&MKK3>_BH_QkgNO@4IoQ-2d8EqsDg) zTMb-5lqlubRot-7!RD@+udO?O9_Da3XV5bvjW zXTb2psHUdeiIaI(lknQE_<+YlY31}R!VfoM_BuILQ{>Q89=LB5j;V|-yAW2gY82+~ zYlu~#*R(cHw2NO1h5xaiAD2oiIEQ-aQyA-D^y^z2ZHNfM{o(3M#SbqOP3>k9FOdDO z(t%c9hk)NCPe_8>=Y^U-_-6IwS-D0cE=pwdyLp!;r-fWiXtbUS$<dl!~WV$TR8 zP$KU?K>m?*O)mSGccn&kn|nj7NXFeo<0D=ue8s^~BK#P?J~gB}v5<0nK9GPipjT#9 zkm6yXFyLlgoUIDEVxw*0Z-WDqp8swCs(bcjAqdDLl1oUqYf#a`NjT6IO3?=P`FvUZ zlWC&lWb9_dexSz%N~-oscM`oC%b#KS|KS7AptwRX5h&1VDCKWzP{&??TFdF3h53&c zU(v)WhOr)#!V6Y6d7CzOO-@KF%@67>kh34@Exj7Rh}p5_0?yUeyC7@c7DHf+mW=~wpLeLYDA9#W-Ri*S|M@g zjPHH@qHrPuzq(+5y$V*UoFEg(g$$mRNUEF!C{IN3Rig{tU54W|OD_`M0G3u)B{WhC z*D?hTF7J+YdF8-Z-Uuw{3jBx`_!aus`uDDBecwuu&tsVpj2~DZJb2-!a2l??m{}er}lR6Lqu)-2+Vm)jr(g{nfQPx9-<^1d;k-d zkU{E^g7qwp+D`b+QtU5@+swaVKp9<`>sT~U)O!EEMBo!*)~s_<`6Yl z7fX2;ki>kVDfdietW1k;TYvaY({>?5X)&(d&_y<-J7Qa@b z(zwGCI=`P#^b>1>2#Y!9T5|AdtaU|zXxw9^KpIu6CAmQf$GzaeOJmYVsc3eh5%6lb z)t~(Ak2J`;KW_L6psME-h?xF6ryr4d{q;>-b`Q$L43T{r`{N?U6cqP(Q3f%kA8`c@ z<82KXjte|7u_Lo~MV!d%y$tYi(hzU$6t+*ml~Z&Mg{eK?@}^XEBK+-&j`Uv95x)=_ zZLs=Mpg_IuZenjm(~}b8Aggaaje8NX$A_7^G%-)!xtu)C{N|S<3hVOmU;{|i+q6zn zfr(1Ua*jF!%-dU3L}O2fvWAe%-4kxtXo_vJHF(AxSx)4AI8-$^uBQO_86Z_y%RZX4 zJpu5`pOAztxv?jXv9yx|r>#9!0|`71C-fli@v${6r+V$hgvcr|W_I`{=7*0s(PKQH zzn8r2+tSeD15stz|DIJ3%X%8EkyN?bsHhuq4(5D0Oewn_)-o)Nx$eNs{0V*ZTSVt4 z3ifXGGw5fBv+9b6d~Nl+08L4VbbZqf3DL^e?l@!uZVdWkdOpJPaE?{zF!ZI?c(vF3 zvX~OK4vktvm&R$MgNpiKA~&zT!1#H7!q1h7AQiuSNG9<=$64)Zym(UQ``(j#^hDzt}{aur0pS?mmBi&z4I0Jfieqh%Pa_A%N?_1OZHm-S{ zQ*)4(N_J;y7tRh0o>xs25-s9!M-)i;@I68#SGXB2XgS}N zx_r3%V)z1jLA_M&?)E^DT$kzdHMJF%e2w6BH@iI5tKWM+zcuhCsz@N0a_1RBvrdZx zjzD>V%;c4*$RkEv{zHuVyaB+ANl(iT8w{pJdziC7YcO2&(ciqGLhs@q-dNh! zkV_V_(_~$*>ND}j1yozMedYnu-_GKMh?IpP<@D+edeB4M%3@xr3oj{@mdFKoBVpm^)1_}Y^}rOWBSB|Uv)*-pTdiU ztW9~{qq5@iB+$QpbeJVKH^n^9vV})i>Z@2CHoY2$PC888c;#Yz-pHRK@EVheWhE!> zZzjPmy?0Ni8#=o_k6_s3DY7nS^&Bm}BW&ZfAuF7bQbDgAGM$dE)RM6RvdobKb&MhsYD4exRm9*jcHPjbz#rI?vj$u zPLF5Gjv|8}?ta9`&^H}Va3H;llghU-BC7pxo6?-eTP`7CUZHJrw{5 zhkDYeIYlhL%brQJ1X#<#fz#E}Z87Kj=Hde*f{l|A`9E my8jz0{9hgZgN;Rh%;ug!HJ{lE_@04L;EulOt!iDD=>G@$cU!Ii literal 0 HcmV?d00001 diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json new file mode 100644 index 0000000000..18c1bd8fbe --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json @@ -0,0 +1,84 @@ +{ + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8e", + "name": "E-Document Connector - Avalara Tests", + "publisher": "Microsoft", + "brief": "E-Document Connector - Avalara Tests", + "description": "E-Document Connector - Avalara Tests", + "version": "26.0.0.0", + "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", + "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", + "help": "https://go.microsoft.com/fwlink/?linkid=2204541", + "url": "https://go.microsoft.com/fwlink/?LinkId=724011", + "logo": "ExtensionLogo.png", + "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2206603", + "dependencies": [ + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", + "name": "E-Document Core", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "f35c56a6-7c5f-4dbe-89c4-fef5145d00f4", + "name": "E-Document Connector - Avalara", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "name": "E-Documents Connector with External Endpoints", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8c", + "name": "E-Document Core Tests", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "5d86850b-0d76-4eca-bd7b-951ad998e997", + "name": "Tests-TestLibraries", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "9856ae4f-d1a7-46ef-89bb-6ef056398228", + "name": "System Application Test Library", + "publisher": "Microsoft", + "version": "26.0.0.0" + }, + { + "id": "5095f467-0a01-4b99-99d1-9ff1237d286f", + "publisher": "Microsoft", + "name": "Library Variable Storage", + "version": "26.0.0.0" + }, + { + "id": "40860557-a18d-42ad-aecb-22b7dd80dc80", + "name": "Permissions Mock", + "publisher": "Microsoft", + "version": "26.0.0.0" + } + ], + "screenshots": [ + + ], + "platform": "26.0.0.0", + "idRanges": [ + { + "from": 148191, + "to": 148192 + } + ], + "resourceExposurePolicy": { + "allowDebugging": true, + "allowDownloadingSource": true, + "includeSourceInSymbolFile": true + }, + "application": "26.0.0.0", + "target": "OnPrem", + "features": [ + "TranslationFile" + ] +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al new file mode 100644 index 0000000000..da2436a2d3 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al @@ -0,0 +1,656 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Avalara; + +using Microsoft.eServices.EDocument; +using Microsoft.Sales.Customer; +using Microsoft.Purchases.Document; +using Microsoft.Foundation.Company; +using Microsoft.Purchases.Vendor; +using System.Threading; +codeunit 148191 "Integration Tests" +{ + + Subtype = Test; + Permissions = tabledata "Connection Setup" = rimd, + tabledata "E-Document" = r; + + /// + /// Test needs MockService running to work. + /// + [Test] + procedure SubmitDocument() + var + EDocument: Record "E-Document"; + JobQueueEntry: Record "Job Queue Entry"; + EDocumentPage: TestPage "E-Document"; + EDocLogList: List of [Enum "E-Document Service Status"]; + begin + // Steps: + // Pending response -> Sent + Initialize(); + + // [Given] Team member + LibraryPermission.SetTeamMember(); + + // [When] Posting invoice and EDocument is created + LibraryEDocument.PostInvoice(Customer); + EDocument.FindLast(); + LibraryEDocument.RunEDocumentJobQueue(EDocument); + + // [When] EDocument is fetched after running Avalara SubmitDocument + EDocument.FindLast(); + + // [Then] Document Id has been correctly set on E-Document, parsed from Integration response. + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has "Pending Response" + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Pending Response"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('2', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + + // [WHEN] Executing Get Response succesfully + SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); + LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); + + // [When] EDocument is fetched after running Avalara GetResponse + EDocument.FindLast(); + + // [Then] E-Document is considered processed + Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be set to processed'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has Sent + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::Sent), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('3', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Sent"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + end; + + /// + /// Test needs MockService running to work. + /// + [Test] + procedure SubmitDocument_Pending_Sent() + var + EDocument: Record "E-Document"; + JobQueueEntry: Record "Job Queue Entry"; + EDocumentPage: TestPage "E-Document"; + EDocLogList: List of [Enum "E-Document Service Status"]; + begin + // Steps: + // Pending response -> Pending response -> Sent + Initialize(); + SetAPIWith200Code(); + + // [Given] Team member + LibraryPermission.SetTeamMember(); + + // [When] Posting invoice and EDocument is created + LibraryEDocument.PostInvoice(Customer); + EDocument.FindLast(); + LibraryEDocument.RunEDocumentJobQueue(EDocument); + + // [When] EDocument is fetched after running Avalara SubmitDocument + EDocument.FindLast(); + + // [Then] Document Id has been correctly set on E-Document, parsed from Integration response + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Pending Response"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('2', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + + // [WHEN] Executing Get Response succesfully + SetAvalaraConnectionBaseUrl('/avalara/response-pending'); + JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); + LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); + + // [When] EDocument is fetched after running Avalara GetResponse + EDocument.FindLast(); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Pending Response"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('3', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + + // [WHEN] Executing Get Response succesfully + SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); + LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); + + // [When] EDocument is fetched after running Avalara GetResponse + EDocument.FindLast(); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be set to processed'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::Sent), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('4', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::Sent); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + end; + + /// + /// Test needs MockService running to work. + /// + [Test] + [HandlerFunctions('EDocServicesPageHandler')] + procedure SubmitDocument_Error_Sent() + var + EDocument: Record "E-Document"; + JobQueueEntry: Record "Job Queue Entry"; + EDocumentPage: TestPage "E-Document"; + EDocLogList: List of [Enum "E-Document Service Status"]; + begin + // Steps: + // Pending response -> Error -> Pending response -> Sent + Initialize(); + SetAPIWith200Code(); + + // [Given] Team member + LibraryPermission.SetTeamMember(); + + // [When] Posting invoice and EDocument is created + LibraryEDocument.PostInvoice(Customer); + EDocument.FindLast(); + LibraryEDocument.RunEDocumentJobQueue(EDocument); + + // [When] EDocument is fetched after running Avalara SubmitDocument + EDocument.FindLast(); + + // [Then] Document Id has been correctly set on E-Document, parsed from Integration response + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Pending Response"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('2', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + + // [WHEN] Executing Get Response succesfully + SetAvalaraConnectionBaseUrl('/avalara/response-error'); + JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); + LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); + + // [When] EDocument is fetched after running Avalara GetResponse + EDocument.FindLast(); + + // [Then] E-Document is in error state + Assert.AreEqual(Enum::"E-Document Status"::Error, EDocument.Status, 'E-Document should be set to error'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has sending error + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Sending Error"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('3', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Sending Error"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + EDocumentPage.ErrorMessagesPart.First(); + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('Error', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('Document started processing', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + + EDocumentPage.ErrorMessagesPart.Next(); + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('Error', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('Wrong data in send xml', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + + EDocumentPage.ErrorMessagesPart.Next(); + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('Error', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('An error has been identified in the submitted document.', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + + EDocumentPage.Close(); + + // Then user manually send + + SetAvalaraConnectionBaseUrl('/avalara/avalara/200'); + EDocument.FindLast(); + + // [THEN] Open E-Document page and resend + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + EDocumentPage.Send_Promoted.Invoke(); + EDocumentPage.Close(); + + EDocument.FindLast(); + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); + + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Pending Response"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('4', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Sending Error"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + + SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + + JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); + LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); + + // [When] EDocument is fetched after running Avalara GetResponse + + EDocument.FindLast(); + + // [Then] E-Document is pending response as Avalara is async + Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be set to processed'); + + // [THEN] Open E-Document page + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has pending response + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::Sent), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('5', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Sending Error"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Pending Response"); + EDocLogList.Add(Enum::"E-Document Service Status"::Sent); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + EDocumentPage.Close(); + end; + + /// + /// Test needs MockService running to work. + /// + [Test] + procedure SubmitDocumentAvalaraServiceDown() + var + EDocument: Record "E-Document"; + EDocumentPage: TestPage "E-Document"; + EDocLogList: List of [Enum "E-Document Service Status"]; + begin + Initialize(); + SetAPIWith500Code(); + + // [Given] Team member + LibraryPermission.SetTeamMember(); + + // [When] Posting invoice and EDocument is created + LibraryEDocument.PostInvoice(Customer); + EDocument.FindLast(); + LibraryEDocument.RunEDocumentJobQueue(EDocument); + + // [When] EDocument is fetched after running Avalara SubmitDocument + EDocument.FindLast(); + + Assert.AreEqual(Enum::"E-Document Status"::Error, EDocument.Status, 'E-Document should be set to error state when service is down.'); + Assert.AreEqual('', EDocument."Document Id", 'Document Id on E-Document should not be set.'); + + EDocumentPage.OpenView(); + EDocumentPage.GoToRecord(EDocument); + + // [THEN] E-Document has correct error status + Assert.AreEqual(Format(EDocument.Status::Error), EDocumentPage."Electronic Document Status".Value(), IncorrectValueErr); + Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); + Assert.AreEqual(EDocument."Document No.", EDocumentPage."Document No.".Value(), IncorrectValueErr); + + // [THEN] E-Document Service Status has correct error status + Assert.AreEqual(EDocumentService.Code, EDocumentPage.EdocoumentServiceStatus."E-Document Service Code".Value(), IncorrectValueErr); + Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Sending Error"), EDocumentPage.EdocoumentServiceStatus.Status.Value(), IncorrectValueErr); + Assert.AreEqual('2', EDocumentPage.EdocoumentServiceStatus.Logs.Value(), IncorrectValueErr); + + Clear(EDocLogList); + EDocLogList.Add(Enum::"E-Document Service Status"::"Exported"); + EDocLogList.Add(Enum::"E-Document Service Status"::"Sending Error"); + LibraryEDocument.AssertEDocumentLogs(EDocument, EDocumentService, EDocLogList); + + // [THEN] E-Document Errors and Warnings has correct status + Assert.AreEqual('Error', EDocumentPage.ErrorMessagesPart."Message Type".Value(), IncorrectValueErr); + Assert.AreEqual('Error Code: 500, Error Message: The HTTP request is not successful. An internal server error occurred.', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); + end; + + /// + /// Test needs MockService running to work. + /// + [Test] + procedure SubmitGetDocuments() + var + EDocument: Record "E-Document"; + PurchaseHeader: Record "Purchase Header"; + EDocServicePage: TestPage "E-Document Service"; + begin + Initialize(); + SetAPIWithReceiveCode(); + SetCompanyIdInConnectionSetup(MockCompanyId(), 'Mock Name'); + + // Open and close E-Doc page creates auto import job due to setting + EDocServicePage.OpenView(); + EDocServicePage.GoToRecord(EDocumentService); + EDocServicePage."Resolve Unit Of Measure".SetValue(false); + EDocServicePage."Lookup Item Reference".SetValue(true); + EDocServicePage."Lookup Item GTIN".SetValue(false); + EDocServicePage."Lookup Account Mapping".SetValue(false); + EDocServicePage."Validate Line Discount".SetValue(false); + EDocServicePage.Close(); + + // Manually fire job queue job to import + LibraryEDocument.RunImportJob(); + + // Assert that we have Purchase Invoice created + EDocument.FindLast(); + PurchaseHeader.Get(EDocument."Document Record ID"); + Assert.AreEqual(Vendor."No.", PurchaseHeader."Buy-from Vendor No.", 'Wrong Vendor'); + end; + + [Test] + [HandlerFunctions('SelectCompany')] + procedure OpenCompanyList() + var + ConnectionSetup: Record "Connection Setup"; + ConnectionSetupCard: TestPage "Connection Setup Card"; + begin + Initialize(); + + // [GIVEN] O365Full member + LibraryPermission.SetO365Full(); + + // [THEN] No company has been selected + ConnectionSetup.Get(); + Assert.AreEqual('', ConnectionSetup."Company Id", 'Has to be empty before selecting company'); + Assert.AreEqual('', ConnectionSetup."Company Name", 'Has to be empty before selecting company'); + + // [WHEN] User click SelectCompanyId action on page + ConnectionSetupCard.OpenView(); + ConnectionSetupCard.SelectCompanyId.Invoke(); + + // Selection of company handled by SelectCompany modal handler... + + // [THEN] Company is populated in connection setup + ConnectionSetup.Get(); + Assert.AreEqual('610f55f3-76b6-42eb-a697-2b0b2e02a5bf', ConnectionSetup."Company Id", 'Has to be empty before selecting company'); + Assert.AreEqual('MS Business Central Ltd - ELR SBX', ConnectionSetup."Company Name", 'Has to be empty before selecting company'); + end; + + local procedure Initialize() + var + ConnectionSetup: Record "Connection Setup"; + CompanyInformation: Record "Company Information"; + AvalaraAuth: Codeunit Authenticator; + KeyGuid: Guid; + begin + LibraryPermission.SetOutsideO365Scope(); + // Clean up token between runs + if ConnectionSetup.Get() then + if IsolatedStorage.Delete(ConnectionSetup."Token - Key", DataScope::Company) then; + + ConnectionSetup.DeleteAll(); + AvalaraAuth.CreateConnectionSetupRecord(); + SetAPIWith200Code(); + + ConnectionSetup.Get(); + AvalaraAuth.SetClientId(KeyGuid, MockServiceGuid()); + ConnectionSetup."Client Id - Key" := KeyGuid; + AvalaraAuth.SetClientSecret(KeyGuid, MockServiceGuid()); + ConnectionSetup."Client Secret - Key" := KeyGuid; + ConnectionSetup.Modify(true); + + if IsInitialized then + exit; + + LibraryEDocument.SetupStandardVAT(); + LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"E-Document Integration"::Avalara); + EDocumentService."Avalara Mandate" := 'GB-Test-Mandate'; + + LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"E-Document Integration"::Avalara); + EDocumentService."Auto Import" := true; + EDocumentService."Import Minutes between runs" := 10; + EDocumentService."Import Start Time" := Time(); + EDocumentService.Modify(); + + Vendor."VAT Registration No." := 'GB777777771'; + Vendor."Receive E-Document To" := Enum::"E-Document Type"::"Purchase Invoice"; + Vendor.Modify(); + + CompanyInformation.Get(); + CompanyInformation."VAT Registration No." := 'GB777777771'; + CompanyInformation.Modify(); + + IsInitialized := true; + end; + + local procedure SetAvalaraConnectionBaseUrl(Base: Text) + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + ConnectionSetup."API URL" := SetMockServiceUrl(Base); + ConnectionSetup."Sandbox API URL" := ConnectionSetup."API URL"; + ConnectionSetup.Modify(true); + end; + + local procedure SetCompanyIdInConnectionSetup(Id: Text[100]; Name: Text[100]) + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + ConnectionSetup."Company Id" := Id; + ConnectionSetup."Company Name" := Name; + ConnectionSetup.Modify(true); + end; + + local procedure SetAPIWithReceiveCode() + begin + SetAPICode('/avalara/200/receive'); + end; + + local procedure SetAPIWith200Code() + begin + SetAPICode('/avalara/200'); + end; + + local procedure SetAPIWith500Code() + begin + SetAPICode('/avalara/500'); + end; + + local procedure SetAPICode(Path: Text) + var + ConnectionSetup: Record "Connection Setup"; + begin + ConnectionSetup.Get(); + ConnectionSetup."API URL" := SetMockServiceUrl(Path); + ConnectionSetup."Authentication URL" := SetMockServiceUrl(Path); + ConnectionSetup."Sandbox API URL" := ConnectionSetup."API URL"; + ConnectionSetup."Sandbox Authentication URL" := ConnectionSetup."Authentication URL"; + ConnectionSetup."Send Mode" := ConnectionSetup."Send Mode"::Test; + ConnectionSetup.Modify(true); + end; + + // Mock values used in mock response files. + // Do not change without fixing MockService files + + local procedure SetMockServiceUrl(Path: Text): Text[200] + begin + exit('https://localhost:8080' + Path); + end; + + local procedure MockServiceGuid(): Text + begin + exit('1590fa93-f12c-446c-8e41-c86d082fe3e0'); + end; + + local procedure MockServiceDocumentId(): Text + begin + exit('52f60401-44d0-4667-ad47-4afe519abb53'); + end; + + local procedure MockCompanyId(): Text[100] + begin + exit('610f55f3-76b6-42eb-a697-2b0b2e02a5bf'); + end; + + [ModalPageHandler] + procedure SelectCompany(var CompanyList: TestPage "Company List") + begin + CompanyList.First(); + CompanyList.OK().Invoke(); + end; + + [ModalPageHandler] + internal procedure EDocServicesPageHandler(var EDocServicesPage: TestPage "E-Document Services") + begin + EDocServicesPage.Filter.SetFilter(Code, EDocumentService.Code); + EDocServicesPage.OK().Invoke(); + end; + + var + Customer: Record Customer; + Vendor: Record Vendor; + EDocumentService: Record "E-Document Service"; + LibraryEDocument: Codeunit "Library - E-Document"; + LibraryPermission: Codeunit "Library - Lower Permissions"; + LibraryJobQueue: Codeunit "Library - Job Queue"; + Assert: Codeunit Assert; + IsInitialized: Boolean; + IncorrectValueErr: Label 'Wrong value'; + +} \ No newline at end of file From e08a53c882c206344f294aae1a3327c3c3a67895 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 29 Oct 2024 13:52:57 +0100 Subject: [PATCH 02/21] Updated for tests --- .../EDocumentConnectors/Tietoevry/app/app.json | 7 +++++++ .../EDocumentConnectors/Tietoevry/test/app.json | 16 ++++++++-------- .../test/src/IntegrationTests.Codeunit.al | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json index 7c5a39ae22..3384db088a 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json @@ -25,6 +25,13 @@ "version": "26.0.0.0" } ], + "internalsVisibleTo": [ + { + "id": "9867c080-e9e7-476e-8245-717d3e124344", + "name": "E-Document Connector - Tietoevry Tests", + "publisher": "Microsoft" + } + ], "screenshots": [ ], diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json index 18c1bd8fbe..98f15bedb9 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json @@ -1,9 +1,9 @@ { - "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8e", - "name": "E-Document Connector - Avalara Tests", + "id": "9867c080-e9e7-476e-8245-717d3e124344", + "name": "E-Document Connector - Tietoevry Tests", "publisher": "Microsoft", - "brief": "E-Document Connector - Avalara Tests", - "description": "E-Document Connector - Avalara Tests", + "brief": "E-Document Connector - Tietoevry Tests", + "description": "E-Document Connector - Tietoevry Tests", "version": "26.0.0.0", "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", @@ -19,8 +19,8 @@ "version": "26.0.0.0" }, { - "id": "f35c56a6-7c5f-4dbe-89c4-fef5145d00f4", - "name": "E-Document Connector - Avalara", + "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "name": "E-Document Connector - Tietoevry", "publisher": "Microsoft", "version": "26.0.0.0" }, @@ -67,8 +67,8 @@ "platform": "26.0.0.0", "idRanges": [ { - "from": 148191, - "to": 148192 + "from": 148193, + "to": 148194 } ], "resourceExposurePolicy": { diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al index da2436a2d3..1c2ae75b95 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al @@ -2,7 +2,7 @@ // 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.Avalara; +namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.eServices.EDocument; using Microsoft.Sales.Customer; @@ -10,7 +10,7 @@ using Microsoft.Purchases.Document; using Microsoft.Foundation.Company; using Microsoft.Purchases.Vendor; using System.Threading; -codeunit 148191 "Integration Tests" +codeunit 148193 "Integration Tests" { Subtype = Test; From 2ff1977626f3c632896a71af1daa0e387a68e3f2 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 29 Oct 2024 13:58:33 +0100 Subject: [PATCH 03/21] Tietoevry tests based on Avalara tests --- .../test/src/IntegrationTests.Codeunit.al | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al index 1c2ae75b95..e93bc6b066 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al @@ -40,11 +40,11 @@ codeunit 148193 "Integration Tests" EDocument.FindLast(); LibraryEDocument.RunEDocumentJobQueue(EDocument); - // [When] EDocument is fetched after running Avalara SubmitDocument + // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); // [Then] Document Id has been correctly set on E-Document, parsed from Integration response. - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); // [THEN] Open E-Document page @@ -69,11 +69,11 @@ codeunit 148193 "Integration Tests" EDocumentPage.Close(); // [WHEN] Executing Get Response succesfully - SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + SetTietoevryConnectionBaseUrl('/Tietoevry/response-complete'); JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); - // [When] EDocument is fetched after running Avalara GetResponse + // [When] EDocument is fetched after running Tietoevry GetResponse EDocument.FindLast(); // [Then] E-Document is considered processed @@ -126,13 +126,13 @@ codeunit 148193 "Integration Tests" EDocument.FindLast(); LibraryEDocument.RunEDocumentJobQueue(EDocument); - // [When] EDocument is fetched after running Avalara SubmitDocument + // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); // [Then] Document Id has been correctly set on E-Document, parsed from Integration response - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); // [THEN] Open E-Document page @@ -156,14 +156,14 @@ codeunit 148193 "Integration Tests" EDocumentPage.Close(); // [WHEN] Executing Get Response succesfully - SetAvalaraConnectionBaseUrl('/avalara/response-pending'); + SetTietoevryConnectionBaseUrl('/Tietoevry/response-pending'); JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); - // [When] EDocument is fetched after running Avalara GetResponse + // [When] EDocument is fetched after running Tietoevry GetResponse EDocument.FindLast(); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); // [THEN] Open E-Document page @@ -189,14 +189,14 @@ codeunit 148193 "Integration Tests" EDocumentPage.Close(); // [WHEN] Executing Get Response succesfully - SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + SetTietoevryConnectionBaseUrl('/Tietoevry/response-complete'); JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); - // [When] EDocument is fetched after running Avalara GetResponse + // [When] EDocument is fetched after running Tietoevry GetResponse EDocument.FindLast(); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be set to processed'); // [THEN] Open E-Document page @@ -248,13 +248,13 @@ codeunit 148193 "Integration Tests" EDocument.FindLast(); LibraryEDocument.RunEDocumentJobQueue(EDocument); - // [When] EDocument is fetched after running Avalara SubmitDocument + // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); // [Then] Document Id has been correctly set on E-Document, parsed from Integration response - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Avalara integration failed to set Document Id on E-Document'); + Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); // [THEN] Open E-Document page @@ -279,11 +279,11 @@ codeunit 148193 "Integration Tests" EDocumentPage.Close(); // [WHEN] Executing Get Response succesfully - SetAvalaraConnectionBaseUrl('/avalara/response-error'); + SetTietoevryConnectionBaseUrl('/Tietoevry/response-error'); JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); - // [When] EDocument is fetched after running Avalara GetResponse + // [When] EDocument is fetched after running Tietoevry GetResponse EDocument.FindLast(); // [Then] E-Document is in error state @@ -325,7 +325,7 @@ codeunit 148193 "Integration Tests" // Then user manually send - SetAvalaraConnectionBaseUrl('/avalara/avalara/200'); + SetTietoevryConnectionBaseUrl('/Tietoevry/Tietoevry/200'); EDocument.FindLast(); // [THEN] Open E-Document page and resend @@ -338,7 +338,7 @@ codeunit 148193 "Integration Tests" EDocumentPage.OpenView(); EDocumentPage.GoToRecord(EDocument); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); Assert.AreEqual(Format(EDocument.Direction::Outgoing), EDocumentPage.Direction.Value(), IncorrectValueErr); @@ -361,16 +361,16 @@ codeunit 148193 "Integration Tests" Assert.AreEqual('', EDocumentPage.ErrorMessagesPart.Description.Value(), IncorrectValueErr); EDocumentPage.Close(); - SetAvalaraConnectionBaseUrl('/avalara/response-complete'); + SetTietoevryConnectionBaseUrl('/Tietoevry/response-complete'); JobQueueEntry.FindJobQueueEntry(JobQueueEntry."Object Type to Run"::Codeunit, Codeunit::"E-Document Get Response"); LibraryJobQueue.RunJobQueueDispatcher(JobQueueEntry); - // [When] EDocument is fetched after running Avalara GetResponse + // [When] EDocument is fetched after running Tietoevry GetResponse EDocument.FindLast(); - // [Then] E-Document is pending response as Avalara is async + // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be set to processed'); // [THEN] Open E-Document page @@ -402,7 +402,7 @@ codeunit 148193 "Integration Tests" /// Test needs MockService running to work. /// [Test] - procedure SubmitDocumentAvalaraServiceDown() + procedure SubmitDocumentTietoevryServiceDown() var EDocument: Record "E-Document"; EDocumentPage: TestPage "E-Document"; @@ -419,7 +419,7 @@ codeunit 148193 "Integration Tests" EDocument.FindLast(); LibraryEDocument.RunEDocumentJobQueue(EDocument); - // [When] EDocument is fetched after running Avalara SubmitDocument + // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); Assert.AreEqual(Enum::"E-Document Status"::Error, EDocument.Status, 'E-Document should be set to error state when service is down.'); @@ -514,7 +514,7 @@ codeunit 148193 "Integration Tests" var ConnectionSetup: Record "Connection Setup"; CompanyInformation: Record "Company Information"; - AvalaraAuth: Codeunit Authenticator; + TietoevryAuth: Codeunit Authenticator; KeyGuid: Guid; begin LibraryPermission.SetOutsideO365Scope(); @@ -523,13 +523,13 @@ codeunit 148193 "Integration Tests" if IsolatedStorage.Delete(ConnectionSetup."Token - Key", DataScope::Company) then; ConnectionSetup.DeleteAll(); - AvalaraAuth.CreateConnectionSetupRecord(); + TietoevryAuth.CreateConnectionSetupRecord(); SetAPIWith200Code(); ConnectionSetup.Get(); - AvalaraAuth.SetClientId(KeyGuid, MockServiceGuid()); + TietoevryAuth.SetClientId(KeyGuid, MockServiceGuid()); ConnectionSetup."Client Id - Key" := KeyGuid; - AvalaraAuth.SetClientSecret(KeyGuid, MockServiceGuid()); + TietoevryAuth.SetClientSecret(KeyGuid, MockServiceGuid()); ConnectionSetup."Client Secret - Key" := KeyGuid; ConnectionSetup.Modify(true); @@ -537,10 +537,10 @@ codeunit 148193 "Integration Tests" exit; LibraryEDocument.SetupStandardVAT(); - LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"E-Document Integration"::Avalara); - EDocumentService."Avalara Mandate" := 'GB-Test-Mandate'; + LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::"TE PEPPOL BIS 3.0", Enum::"E-Document Integration"::Tietoevry); + EDocumentService."Tietoevry Mandate" := 'GB-Test-Mandate'; - LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"E-Document Integration"::Avalara); + LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"TE PEPPOL BIS 3.0", Enum::"E-Document Integration"::Tietoevry); EDocumentService."Auto Import" := true; EDocumentService."Import Minutes between runs" := 10; EDocumentService."Import Start Time" := Time(); @@ -557,7 +557,7 @@ codeunit 148193 "Integration Tests" IsInitialized := true; end; - local procedure SetAvalaraConnectionBaseUrl(Base: Text) + local procedure SetTietoevryConnectionBaseUrl(Base: Text) var ConnectionSetup: Record "Connection Setup"; begin @@ -579,17 +579,17 @@ codeunit 148193 "Integration Tests" local procedure SetAPIWithReceiveCode() begin - SetAPICode('/avalara/200/receive'); + SetAPICode('/Tietoevry/200/receive'); end; local procedure SetAPIWith200Code() begin - SetAPICode('/avalara/200'); + SetAPICode('/Tietoevry/200'); end; local procedure SetAPIWith500Code() begin - SetAPICode('/avalara/500'); + SetAPICode('/Tietoevry/500'); end; local procedure SetAPICode(Path: Text) From 211ab741078737d341a04943f2a1576e329113f8 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Wed, 20 Nov 2024 13:15:30 +0100 Subject: [PATCH 04/21] Updated app id --- Apps/W1/EDocumentConnectors/Tietoevry/app/app.json | 4 ++-- Apps/W1/EDocumentConnectors/Tietoevry/test/app.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json index 3384db088a..b11d4fa147 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json @@ -1,5 +1,5 @@ { - "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "id": "3b3c094c-ae7f-43f2-9246-22111b688d2e", "name": "E-Document Connector - Tietoevry", "publisher": "Microsoft", "brief": "E-Document Connector - Tietoevry", @@ -27,7 +27,7 @@ ], "internalsVisibleTo": [ { - "id": "9867c080-e9e7-476e-8245-717d3e124344", + "id": "985549ff-145a-4b5c-acd1-db6d425f6cbc", "name": "E-Document Connector - Tietoevry Tests", "publisher": "Microsoft" } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json index 98f15bedb9..4a5e78196e 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json @@ -1,5 +1,5 @@ { - "id": "9867c080-e9e7-476e-8245-717d3e124344", + "id": "985549ff-145a-4b5c-acd1-db6d425f6cbc", "name": "E-Document Connector - Tietoevry Tests", "publisher": "Microsoft", "brief": "E-Document Connector - Tietoevry Tests", From 53a81f6fbd9231b1bda53ed72275651a7d8cd9bd Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Wed, 20 Nov 2024 15:12:06 +0100 Subject: [PATCH 05/21] Updated for new namespace --- .../Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al | 2 +- .../Tietoevry/app/src/codeunit/Cod6381.Connection.al | 2 +- .../Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al | 2 +- .../Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al | 2 +- .../Tietoevry/app/src/codeunit/Cod6387.Processing.al | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al index ecb771ed38..3f4e1c2821 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6380.ConnectionEvents.al @@ -10,7 +10,7 @@ using Microsoft.Foundation.Company; using Microsoft.Sales.Customer; using Microsoft.eServices.EDocument; using System.Utilities; -using Microsoft.eServices.EDocument.Service; +using Microsoft.eServices.EDocument.Service.Participant; codeunit 6380 "Connection Events" { diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al index 602f7890cd..7443ac33a2 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6381.Connection.al @@ -6,9 +6,9 @@ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.EServices.EDocument; using System.Utilities; -using System.Text; using Microsoft.Purchases.Posting; using Microsoft.Purchases.Document; + codeunit 6381 "Connection" { Access = Internal; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al index 92530e2cf1..620f6ad759 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6385.EDocTEPEPPOLBIS30.al @@ -8,7 +8,7 @@ using Microsoft.Service.History; using Microsoft.Sales.Document; using Microsoft.Sales.History; using System.IO; -using Microsoft.eServices.EDocument.Service; +using Microsoft.eServices.EDocument.Service.Participant; codeunit 6385 "EDoc TE PEPPOL BIS 3.0" implements "E-Document" { diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al index 2f7fcaef69..38c87f79a5 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6386.EDocImportTietoevry.al @@ -9,7 +9,7 @@ using System.Text; using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Vendor; using Microsoft.Finance.GeneralLedger.Setup; -using Microsoft.eServices.EDocument.Service; +using Microsoft.eServices.EDocument.Service.Participant; codeunit 6386 "EDoc Import Tietoevry" { diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al index 5f0df6b5fe..93e353233b 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/codeunit/Cod6387.Processing.al @@ -10,7 +10,7 @@ using Microsoft.Sales.Peppol; using System.Telemetry; using System.Text; using System.Utilities; -using Microsoft.eServices.EDocument.Service; +using Microsoft.eServices.EDocument.Service.Participant; codeunit 6387 "Processing" { From e214a39efdd6743078d37d6eae1484865aeb5176 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 26 Nov 2024 13:14:48 +0100 Subject: [PATCH 06/21] 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 b11d4fa147..c1f1f74700 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 0000000000..31266f6c8d --- /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 f26e7d4905..bd3f5eaff3 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 bb81462c0f..7560ea2367 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 3f4e1c2821..b9e6811c26 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 e022ca7457..a12921e2b9 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 24e0ae642d..bd5acc0395 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 0000000000..2aa1e8f373 --- /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 620f6ad759..089b0ebcde 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 38c87f79a5..ca954b6542 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 0000000000..638bd7eaaa --- /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 0000000000..e2e3d6fce2 --- /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 0000000000..b9623c4606 --- /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 6d16b11bab..135d0f64f9 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 fc87ec1c29..9e40cbeed7 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 0000000000..b97ebb3491 --- /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 954c5a147d..01cadadafc 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 61be743078..39e1ff0e29 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 ad69d987dc..fa29a1114e 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 0000000000..c3d25f9e83 --- /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 0000000000..edd74fadd2 --- /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 0000000000..026fcfe12c --- /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 7443ac33a2..0000000000 --- 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 9927a8be59..0000000000 --- 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 ea024b687f..0000000000 --- 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 5a4ac1382f..0000000000 --- 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 93e353233b..0000000000 --- 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 bbe24cd5b6..0000000000 --- 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 7c2f5df964..0000000000 --- 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 From 5d265de00b6425fef6a019477bc1ae458976811f Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Tue, 26 Nov 2024 22:37:12 +0100 Subject: [PATCH 07/21] correction after review --- .../app/src/ConnectionSetupCard.Page.al | 5 +- ...umentImpl.al => EDocumentImpl.Codeunit.al} | 0 ...rmatEvents.al => FormatEvents.Codeunit.al} | 0 ...ort.al => TietoevryEDocImport.Codeunit.al} | 20 ++++-- ...evryEDocConnectorEdit.PermissionSetExt.al} | 0 ...nSet.al => TietoevryEdit.PermissionSet.al} | 0 ...t.al => TietoevryObjects.PermissionSet.al} | 0 ...nSet.al => TietoevryRead.PermissionSet.al} | 0 .../Tietoevry/app/src/Upgrade.Codeunit.al | 64 ------------------- 9 files changed, 18 insertions(+), 71 deletions(-) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/{EDocumentImpl.al => EDocumentImpl.Codeunit.al} (100%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/{Cod6398.FormatEvents.al => FormatEvents.Codeunit.al} (100%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/{TietoevryEDocImport.al => TietoevryEDocImport.Codeunit.al} (95%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/{TeitoevryEDocConnectorEdit.PermissionSetExt.al => TietoevryEDocConnectorEdit.PermissionSetExt.al} (100%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/{Edit.PermissionSet.al => TietoevryEdit.PermissionSet.al} (100%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/{Objects.PermissionSet.al => TietoevryObjects.PermissionSet.al} (100%) rename Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/{Read.PermissionSet.al => TietoevryRead.PermissionSet.al} (100%) delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al index 7560ea2367..c406b4abd9 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al @@ -105,7 +105,9 @@ page 6392 "Connection Setup Card" begin Token := TietoevryAuth.GetAccessToken(); if not Token.IsEmpty() then - Message(this.AuthSuccessMsg); + Message(this.AuthSuccessMsg) + else + Error(this.AuthFailedErr); end; } } @@ -130,6 +132,7 @@ page 6392 "Connection Setup Card" TietoevryAuth: Codeunit "Authenticator"; TietoevryProcessing: Codeunit Processing; AuthSuccessMsg: Label 'Authenticated successfully'; + AuthFailedErr: Label 'Authentication failed'; [NonDebuggable] ClientID, ClientSecret : Text; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/Cod6398.FormatEvents.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al similarity index 95% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al index ca954b6542..358bb77be3 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al @@ -14,6 +14,8 @@ using Microsoft.Purchases.Vendor; using Microsoft.Finance.GeneralLedger.Setup; using Microsoft.eServices.EDocument.Service.Participant; +// The following line can be removed when/if Microsoft changes the protection level on the EDocumentAttachmentGen.Insert() procedure +#pragma warning disable AA0137 codeunit 6393 "Tietoevry E-Document Import" { procedure ParseBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") @@ -31,7 +33,7 @@ codeunit 6393 "Tietoevry E-Document Import" EDocument.Direction := EDocument.Direction::Incoming; - case UpperCase(GetDocumentType(TempXMLBuffer)) of + case UpperCase(this.GetDocumentType(TempXMLBuffer)) of 'INVOICE': this.ParseInvoiceBasicInfo(EDocument, TempXMLBuffer); 'CREDITNOTE': @@ -65,7 +67,7 @@ codeunit 6393 "Tietoevry E-Document Import" Currency: Text[10]; 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.")); + EDocument."Incoming E-Document No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); this.ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'Invoice'); this.ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'Invoice'); @@ -103,10 +105,10 @@ codeunit 6393 "Tietoevry E-Document Import" if IssueDate <> '' then Evaluate(EDocument."Document Date", IssueDate, 9); - Evaluate(EDocument."Amount Excl. VAT", GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); - Evaluate(EDocument."Amount Incl. VAT", GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); + Evaluate(EDocument."Amount Excl. VAT", this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); + Evaluate(EDocument."Amount Incl. VAT", this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); - Currency := CopyStr(GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); + Currency := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); if this.LCYCode <> Currency then EDocument."Currency Code" := Currency; end; @@ -173,6 +175,7 @@ codeunit 6393 "Tietoevry E-Document Import" // Insert last document attachment if DocumentAttachment."No." <> '' then begin DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); Clear(DocumentAttachment); end; @@ -204,6 +207,7 @@ codeunit 6393 "Tietoevry E-Document Import" // Insert last document attachment if DocumentAttachment."No." <> '' then begin DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); Clear(DocumentAttachment); end; @@ -322,6 +326,7 @@ codeunit 6393 "Tietoevry E-Document Import" begin if DocumentAttachment."No." <> '' then begin DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); + // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); Clear(DocumentAttachment); end; @@ -433,7 +438,8 @@ codeunit 6393 "Tietoevry E-Document Import" begin if DocumentAttachment."No." <> '' then begin DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - //EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); + // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert + // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); Clear(DocumentAttachment); end; @@ -573,6 +579,8 @@ codeunit 6393 "Tietoevry E-Document Import" end; var + + // The variable "EDocumentAttachmentGen" is when/if Microsoft changes the protection level on the procedure Insert EDocumentAttachmentGen: Codeunit "E-Doc. Attachment Processor"; EDocumentImportHelper: Codeunit "E-Document Import Helper"; LCYCode: Code[10]; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TeitoevryEDocConnectorEdit.PermissionSetExt.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Edit.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEdit.PermissionSet.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Edit.PermissionSet.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEdit.PermissionSet.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Objects.PermissionSet.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Read.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryRead.PermissionSet.al similarity index 100% rename from Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/Read.PermissionSet.al rename to Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryRead.PermissionSet.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al deleted file mode 100644 index 026fcfe12c..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Upgrade.Codeunit.al +++ /dev/null @@ -1,64 +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.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 From 218a37bb9028a880423369655b9241110ab6ab0a Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Wed, 27 Nov 2024 13:16:55 +0100 Subject: [PATCH 08/21] Acknowledge Received --- .../Tietoevry/app/src/IntegrationImpl.Codeunit.al | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al index b9623c4606..66c2bd55e8 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al @@ -10,7 +10,7 @@ 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 +codeunit 6392 "Integration Impl." implements IDocumentSender, IDocumentResponseHandler, IDocumentReceiver, IReceivedDocumentMarker { Access = Internal; @@ -32,7 +32,11 @@ codeunit 6392 "Integration Impl." implements IDocumentSender, IDocumentResponseH 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; + + procedure MarkFetched(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; var DocumentBlob: Codeunit "Temp Blob"; ReceiveContext: Codeunit ReceiveContext) + begin + this.TietoevryProcessing.AcknowledgeDocument(EDocument, EDocumentService, DocumentBlob, ReceiveContext); end; [EventSubscriber(ObjectType::Page, Page::"E-Document Service", OnBeforeOpenServiceIntegrationSetupPage, '', false, false)] From e33767471391a2a456277529122f47fcb76a8a88 Mon Sep 17 00:00:00 2001 From: magnushar Date: Sun, 1 Dec 2024 10:02:16 +0100 Subject: [PATCH 09/21] "Uptake latest master of E-Doc Core. Remove dependency on E-Documents Connector with External Endpoints --- .../Tietoevry/app/.resources/SchemeIds.txt | 1 + .../Tietoevry/app/app.json | 13 +++---- .../app/src/ConnectionSetup.Table.al | 4 +- .../app/src/Extensions/EDocument.TableExt.al | 8 +--- .../app/src/Formats/EDocumentImpl.Codeunit.al | 7 +++- .../app/src/Formats/FormatEvents.Codeunit.al | 39 +++++++++++++++++++ .../app/src/IntegrationImpl.Codeunit.al | 11 ++++-- ...oevryEDocConnectorEdit.PermissionSetExt.al | 4 +- ...oevryEDocConnectorRead.PermissionSetExt.al | 5 ++- .../Tietoevry/app/src/Processing.Codeunit.al | 33 +++++++++------- .../Tietoevry/app/src/Requests.Codeunit.al | 21 ++++++---- .../Tietoevry/app/src/SendMode.Enum.al | 19 +++++++++ 12 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/.resources/SchemeIds.txt create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/.resources/SchemeIds.txt b/Apps/W1/EDocumentConnectors/Tietoevry/app/.resources/SchemeIds.txt new file mode 100644 index 0000000000..1510d0197a --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/.resources/SchemeIds.txt @@ -0,0 +1 @@ +{"schemeids": "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" } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json index c1f1f74700..cc67dba438 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/app.json @@ -16,13 +16,7 @@ "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", "name": "E-Document Core", "publisher": "Microsoft", - "version": "26.0.27172.0" - }, - { - "id": "d852a468-263e-49e5-bfda-f09e33342b89", - "name": "E-Documents Connector with External Endpoints", - "publisher": "Microsoft", - "version": "26.0.27172.0" + "version": "26.0.0.0" } ], "internalsVisibleTo": [ @@ -51,5 +45,8 @@ "target": "OnPrem", "features": [ "TranslationFile" - ] + ], + "resourceFolders": [ + ".resources" + ] } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index bd3f5eaff3..5164bc16b1 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -4,8 +4,6 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; - table 6392 "Connection Setup" { fields @@ -71,7 +69,7 @@ table 6392 "Connection Setup" Rec.FieldError(Rec."Company Id"); end; } - field(13; "Send Mode"; Enum "E-Doc. Ext. Send Mode") + field(13; "Send Mode"; Enum "Send Mode") { Caption = 'Send Mode'; DataClassification = EndUserIdentifiableInformation; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al index bd5acc0395..d307518c56 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al @@ -10,9 +10,9 @@ tableextension 6390 "E-Document" extends "E-Document" { fields { - field(6390; "Bill-to/Pay-to Id"; Text[100]) + field(6390; "Tietoevry Document Id"; Text[50]) { - DataClassification = CustomerContent; + DataClassification = SystemMetadata; } field(6391; "Message Id"; Text[50]) { @@ -26,9 +26,5 @@ tableextension 6390 "E-Document" extends "E-Document" { DataClassification = CustomerContent; } - field(6394; "Receiving Company Id"; Text[100]) - { - DataClassification = CustomerContent; - } } } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al index 089b0ebcde..54c9a32792 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al @@ -7,14 +7,19 @@ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.eServices.EDocument; using Microsoft.Sales.Document; using Microsoft.Sales.History; +using Microsoft.eServices.EDocument.IO.Peppol; using Microsoft.Service.History; using Microsoft.Sales.Peppol; using System.IO; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Purchases.Document; +using System.Utilities; codeunit 6391 "Tietoevry E-Document" implements "E-Document" { + + + procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: enum Microsoft.eServices.EDocument."E-Document Processing Phase") var SalesHeader: Record "Sales Header"; @@ -74,8 +79,6 @@ codeunit 6391 "Tietoevry E-Document" implements "E-Document" end; EDocument.Find(); - ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No."); - EDocument."Bill-to/Pay-to Id" := ServiceParticipant."Participant Identifier"; TempBlob.CreateInStream(DocInStream); TempXMLBuffer.LoadFromStream(DocInStream); diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al index 2aa1e8f373..163387b2fc 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al @@ -8,6 +8,9 @@ using Microsoft.Sales.Peppol; using Microsoft.Sales.Document; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Service.Participant; +using System.IO; +using Microsoft.eServices.EDocument.IO.Peppol; +using System.Utilities; codeunit 6398 "Format Events" { @@ -15,6 +18,42 @@ codeunit 6398 "Format Events" SingleInstance = true; EventSubscriberInstance = StaticAutomatic; + [EventSubscriber(ObjectType::Codeunit, Codeunit::"EDoc PEPPOL BIS 3.0", OnAfterCreatePEPPOLXMLDocument, '', false, false)] + local procedure OnAfterCreatePEPPOLXMLDocument(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob"); + var + TempXMLBuffer: Record "XML Buffer" temporary; + DocInStream: InStream; + MessageDocumentId: Text; + begin + if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then + exit; + + TempBlob.CreateInStream(DocInStream); + TempXMLBuffer.LoadFromStream(DocInStream); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'ProfileID'); + if TempXMLBuffer.FindFirst() then + EDocument."Message Profile Id" := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); + TempXMLBuffer.SetRange(Name, 'xmlns'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '::' + TempXMLBuffer.Name; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'CustomizationID'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '##' + TempXMLBuffer.Value + '::2.1'; + + EDocument."Message Document Id" := MessageDocumentId; + EDocument.Modify(); + 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 diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al index 66c2bd55e8..a4b93b0779 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al @@ -40,10 +40,15 @@ codeunit 6392 "Integration Impl." implements IDocumentSender, IDocumentResponseH end; [EventSubscriber(ObjectType::Page, Page::"E-Document Service", OnBeforeOpenServiceIntegrationSetupPage, '', false, false)] - local procedure OnBeforeOpenServiceIntegrationSetupPage(EDocumentService: Record "E-Document Service"; var SetupPage: Integer) + local procedure OnBeforeOpenServiceIntegrationSetupPage(EDocumentService: Record "E-Document Service"; var IsServiceIntegrationSetupRun: Boolean) + var + ConnectionSetupCard: Page "Connection Setup Card"; begin - if EDocumentService."Service Integration V2" = EDocumentService."Service Integration V2"::Tietoevry then - SetupPage := Page::"Connection Setup Card"; + if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then + exit; + + ConnectionSetupCard.RunModal(); + IsServiceIntegrationSetupRun := true; end; var diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al index 39e1ff0e29..f0484c9d18 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al @@ -4,9 +4,9 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; +using System.Security.AccessControl; -permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "EDocConnector - Edit" +permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "D365 BASIC" { IncludedPermissionSets = "Tietoevry Edit"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al index fa29a1114e..d121e222ad 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al @@ -3,9 +3,10 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; -permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "EDocConnector - Read" +using System.Security.AccessControl; + +permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "D365 READ" { 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 index c3d25f9e83..f5c4fd4c99 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al @@ -35,13 +35,13 @@ codeunit 6399 Processing InStream.Read(RequestContent); Request.Init(); - Request.Authenticate().CreateSubmitDocumentRequest(EDocument, RequestContent); + Request.Authenticate().CreateSubmitDocumentRequest(EDocument, EDocumentService, 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."Tietoevry Document Id" := this.ParseDocumentId(ResponseContent); EDocument.Modify(true); end; @@ -56,10 +56,10 @@ codeunit 6399 Processing HttpExecutor: Codeunit "Http Executor"; ResponseContent: Text; begin - EDocument.TestField("Document Id"); + EDocument.TestField("Tietoevry Document Id"); Request.Init(); - Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Document Id"); + Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Tietoevry Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -119,7 +119,7 @@ codeunit 6399 Processing exit; end; - EDocument."Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id")); + EDocument."Tietoevry Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id")); EDocument.Modify(); Request.Init(); @@ -142,7 +142,7 @@ codeunit 6399 Processing ResponseContent: Text; begin Request.Init(); - Request.Authenticate().CreateAcknowledgeRequest(EDocument."Document Id"); + Request.Authenticate().CreateAcknowledgeRequest(EDocument."Tietoevry Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); ReceiveContext.Http().SetHttpRequestMessage(Request.GetRequest()); ReceiveContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -173,7 +173,7 @@ codeunit 6399 Processing var EDocument: Record "E-Document"; begin - EDocument.SetRange("Document Id", DocumentId); + EDocument.SetRange("Tietoevry Document Id", DocumentId); exit(not EDocument.IsEmpty()); end; @@ -191,10 +191,10 @@ codeunit 6399 Processing ResponseJson.Get('id', ValueJson); DocumentId := ValueJson.AsValue().AsText(); - if StrLen(DocumentId) > MaxStrLen(EDocument."Document Id") then + if StrLen(DocumentId) > MaxStrLen(EDocument."Tietoevry Document Id") then Error(this.TietoevryIdLongerErr); - exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id"))); + exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id"))); end; /// @@ -207,7 +207,7 @@ codeunit 6399 Processing begin ResponseJson.ReadFrom(ResponseMsg); ResponseJson.Get('id', ValueJson); - if EDocument."Document Id" <> ValueJson.AsValue().AsText() then + if EDocument."Tietoevry Document Id" <> ValueJson.AsValue().AsText() then Error(this.IncorrectDocumentIdInResponseErr); ResponseJson.Get('status', ValueJson); @@ -255,9 +255,16 @@ codeunit 6399 Processing ValidSchemeIdList: List of [Text]; SplitSeparator: Text; SchemeId: Text; + ResInStream: InStream; + JsonObject: JsonObject; + JsonToken: JsonToken; begin SplitSeparator := ' '; - ValidSchemeId := ValidSchemeIdTxt; + NavApp.GetResource(ResourceSchemeIdPath, ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(ValidSchemeId); + JsonObject.ReadFrom(ValidSchemeId); + JsonObject.Get('schemeids', JsonToken); + ValidSchemeId := JsonToken.AsValue().AsText(); ValidSchemeIdList := ValidSchemeId.Split(SplitSeparator); foreach SchemeId in ValidSchemeIdList do @@ -273,8 +280,6 @@ codeunit 6399 Processing 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 + ResourceSchemeIdPath: Label 'SchemeIds.txt', Locked = true; } \ 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 index edd74fadd2..263753cd3c 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -8,6 +8,7 @@ using Microsoft.EServices.EDocumentConnector; using Microsoft.eServices.EDocument; using System.Text; using System.Reflection; +using Microsoft.eServices.EDocument.Service.Participant; /// @@ -24,6 +25,7 @@ codeunit 6396 Requests HttpRequestMessage: HttpRequestMessage; BaseUrl, AuthUrl, CompanyId : Text; AccessToken: SecretText; + ServiceParticipantNotFoundErr: Label 'No Service Participant defined for Customer %1 and E-Document Service %2.', Comment = '%1 - The customer no., %2 - The e-document service code'; /// /// Create request for /outbound API @@ -31,8 +33,9 @@ codeunit 6396 Requests /// /// 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 + procedure CreateSubmitDocumentRequest(EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; Data: Text): Codeunit Requests var + ServiceParticipant: Record "Service Participant"; Base64Convert: Codeunit "Base64 Convert"; HttpHeaders, HttpContentHeaders : HttpHeaders; Content: Text; @@ -48,14 +51,18 @@ codeunit 6396 Requests 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"); + + EDocument.TestField("Bill-to/Pay-to No."); + if not ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No.") then + Error(ServiceParticipantNotFoundErr, EDocument."Bill-to/Pay-to No.", EDocumentService.Code); + + ContentJson.Add('receiver', ServiceParticipant."Participant Identifier"); 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); @@ -207,9 +214,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "E-Doc. Ext. Send Mode"::Production: + "Send Mode"::Production: exit(ConnectionSetup."API URL"); - "E-Doc. Ext. Send Mode"::Test: + "Send Mode"::Test: exit(ConnectionSetup."Sandbox API URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); @@ -223,9 +230,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "E-Doc. Ext. Send Mode"::Production: + "Send Mode"::Production: exit(ConnectionSetup."Authentication URL"); - "E-Doc. Ext. Send Mode"::Test: + "Send Mode"::Test: exit(ConnectionSetup."Sandbox Authentication URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al new file mode 100644 index 0000000000..78b99207be --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +enum 6390 "Send Mode" +{ + Extensible = false; + + value(0; Production) + { + Caption = 'Production'; + } + value(1; Test) + { + Caption = 'Test'; + } +} \ No newline at end of file From f89a9f5c3101f6a3d6e2c638ec124b4af6ec0cf4 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 10:51:16 +0100 Subject: [PATCH 10/21] Syncing with version 26.0.27457.0 --- .../app/src/ConnectionSetup.Table.al | 12 +++--- .../app/src/ConnectionSetupCard.Page.al | 2 +- .../app/src/Extensions/EDocument.TableExt.al | 12 ++---- .../app/src/Formats/EDocumentImpl.Codeunit.al | 7 +--- .../app/src/Formats/FormatEvents.Codeunit.al | 39 ------------------- .../app/src/HttpExecutor.Codeunit.al | 33 ++++++++-------- .../Tietoevry/app/src/Integration.EnumExt.al | 18 --------- .../app/src/IntegrationImpl.Codeunit.al | 1 - ...oevryEDocConnectorEdit.PermissionSetExt.al | 4 +- ...oevryEDocConnectorRead.PermissionSetExt.al | 5 +-- .../Tietoevry/app/src/Processing.Codeunit.al | 33 +++++++--------- .../Tietoevry/app/src/Requests.Codeunit.al | 21 ++++------ .../Tietoevry/app/src/SendMode.Enum.al | 19 --------- 13 files changed, 55 insertions(+), 151 deletions(-) delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index 5164bc16b1..62fed8db9f 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -4,6 +4,8 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; +using Microsoft.EServices.EDocumentConnector; + table 6392 "Connection Setup" { fields @@ -49,17 +51,17 @@ table 6392 "Connection Setup" field(8; "Token - Key"; Guid) { Caption = 'Token'; - DataClassification = CustomerContent; + DataClassification = SystemMetadata; } field(9; "Token Expiry"; DateTime) { Caption = 'Token Expiry'; - DataClassification = CustomerContent; + DataClassification = SystemMetadata; } field(10; "Company Id"; Text[250]) { Caption = 'Company ID'; - DataClassification = CustomerContent; + DataClassification = EndUserIdentifiableInformation; trigger OnValidate() var @@ -69,10 +71,10 @@ table 6392 "Connection Setup" Rec.FieldError(Rec."Company Id"); end; } - field(13; "Send Mode"; Enum "Send Mode") + field(13; "Send Mode"; Enum "E-Doc. Ext. Send Mode") { Caption = 'Send Mode'; - DataClassification = EndUserIdentifiableInformation; + DataClassification = CustomerContent; } } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al index c406b4abd9..e9f30e30f2 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al @@ -116,7 +116,7 @@ page 6392 "Connection Setup Card" var FeatureTelemetry: Codeunit "Feature Telemetry"; begin - FeatureTelemetry.LogUptake('0000NHL', this.TietoevryProcessing.GetTietoevryTok(), Enum::"Feature Uptake Status"::Discovered); + FeatureTelemetry.LogUptake('', this.TietoevryProcessing.GetTietoevryTok(), Enum::"Feature Uptake Status"::Discovered); this.TietoevryAuth.CreateConnectionSetupRecord(); this.TietoevryAuth.IsClientCredsSet(this.ClientID, this.ClientSecret); end; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al index d307518c56..f2efbf6968 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al @@ -10,21 +10,17 @@ tableextension 6390 "E-Document" extends "E-Document" { fields { - field(6390; "Tietoevry Document Id"; Text[50]) + field(6390; "Bill-to/Pay-to Id"; Text[100]) { - DataClassification = SystemMetadata; - } - field(6391; "Message Id"; Text[50]) - { - DataClassification = CustomerContent; + DataClassification = EndUserIdentifiableInformation; } field(6392; "Message Profile Id"; Text[50]) { - DataClassification = CustomerContent; + DataClassification = SystemMetadata; } field(6393; "Message Document Id"; Text[200]) { - DataClassification = CustomerContent; + DataClassification = SystemMetadata; } } } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al index 54c9a32792..089b0ebcde 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al @@ -7,19 +7,14 @@ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; using Microsoft.eServices.EDocument; using Microsoft.Sales.Document; using Microsoft.Sales.History; -using Microsoft.eServices.EDocument.IO.Peppol; using Microsoft.Service.History; using Microsoft.Sales.Peppol; using System.IO; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Purchases.Document; -using System.Utilities; codeunit 6391 "Tietoevry E-Document" implements "E-Document" { - - - procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: enum Microsoft.eServices.EDocument."E-Document Processing Phase") var SalesHeader: Record "Sales Header"; @@ -79,6 +74,8 @@ codeunit 6391 "Tietoevry E-Document" implements "E-Document" end; EDocument.Find(); + ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No."); + EDocument."Bill-to/Pay-to Id" := ServiceParticipant."Participant Identifier"; TempBlob.CreateInStream(DocInStream); TempXMLBuffer.LoadFromStream(DocInStream); diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al index 163387b2fc..2aa1e8f373 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al @@ -8,9 +8,6 @@ using Microsoft.Sales.Peppol; using Microsoft.Sales.Document; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Service.Participant; -using System.IO; -using Microsoft.eServices.EDocument.IO.Peppol; -using System.Utilities; codeunit 6398 "Format Events" { @@ -18,42 +15,6 @@ codeunit 6398 "Format Events" SingleInstance = true; EventSubscriberInstance = StaticAutomatic; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"EDoc PEPPOL BIS 3.0", OnAfterCreatePEPPOLXMLDocument, '', false, false)] - local procedure OnAfterCreatePEPPOLXMLDocument(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob"); - var - TempXMLBuffer: Record "XML Buffer" temporary; - DocInStream: InStream; - MessageDocumentId: Text; - begin - if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then - exit; - - TempBlob.CreateInStream(DocInStream); - TempXMLBuffer.LoadFromStream(DocInStream); - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name, 'ProfileID'); - if TempXMLBuffer.FindFirst() then - EDocument."Message Profile Id" := TempXMLBuffer.Value; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); - TempXMLBuffer.SetRange(Name, 'xmlns'); - if TempXMLBuffer.FindFirst() then - MessageDocumentId := TempXMLBuffer.Value; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name); - if TempXMLBuffer.FindFirst() then - MessageDocumentId += '::' + TempXMLBuffer.Name; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name, 'CustomizationID'); - if TempXMLBuffer.FindFirst() then - MessageDocumentId += '##' + TempXMLBuffer.Value + '::2.1'; - - EDocument."Message Document Id" := MessageDocumentId; - EDocument.Modify(); - 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 diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al index 638bd7eaaa..0e0e068742 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/HttpExecutor.Codeunit.al @@ -21,7 +21,7 @@ codeunit 6397 "Http Executor" var HttpResponse: HttpResponseMessage; begin - exit(ExecuteHttpRequest(Request, HttpResponse)); + exit(this.ExecuteHttpRequest(Request, HttpResponse)); end; /// @@ -32,12 +32,12 @@ codeunit 6397 "Http Executor" 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.'); + FeatureTelemetry.LogUptake('', this.TietoevryProcessing.GetTietoevryTok(), Enum::"Feature Uptake Status"::Used); + FeatureTelemetry.LogUsage('', this.TietoevryProcessing.GetTietoevryTok(), 'Tietoevry request.'); HttpClient.Send(Request.GetRequest(), this.HttpResponseMessage); HttpResponse := this.HttpResponseMessage; - HandleHttpResponse(this.HttpResponseMessage, Response); + this.HandleHttpResponse(this.HttpResponseMessage, Response); end; /// @@ -59,31 +59,31 @@ codeunit 6397 "Http Executor" case LocalHttpResponseMessage.HttpStatusCode() of 200: begin - Session.LogMessage('0000NHB', HTTPSuccessMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.AvalaraProcessing.GetTietoevryTok()); + Session.LogMessage('', this.HTTPSuccessMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TietoevryProcessing.GetTietoevryTok()); exit; end; 201: begin - Session.LogMessage('0000NHC', HTTPSuccessAndCreatedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.AvalaraProcessing.GetTietoevryTok()); + Session.LogMessage('', this.HTTPSuccessAndCreatedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TietoevryProcessing.GetTietoevryTok()); exit; end; 400: - FriendlyErrorMsg := HTTPBadRequestMsg; + FriendlyErrorMsg := this.HTTPBadRequestMsg; 401: - FriendlyErrorMsg := HTTPUnauthorizedMsg; + FriendlyErrorMsg := this.HTTPUnauthorizedMsg; 402 .. 499: - if not Parse400Messages(Response, FriendlyErrorMsg) then - FriendlyErrorMsg := HTTPBadRequestMsg; + if not this.Parse400Messages(Response, FriendlyErrorMsg) then + FriendlyErrorMsg := this.HTTPBadRequestMsg; 500: - FriendlyErrorMsg := HTTPInternalServerErrorMsg; + FriendlyErrorMsg := this.HTTPInternalServerErrorMsg; 503: - FriendlyErrorMsg := HTTPServiceUnavailableMsg; + FriendlyErrorMsg := this.HTTPServiceUnavailableMsg; else - FriendlyErrorMsg := HTTPGeneralErrMsg; + FriendlyErrorMsg := this.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()); + FriendlyErrorMsg := StrSubstNo(this.HttpErrorMsg, LocalHttpResponseMessage.HttpStatusCode(), FriendlyErrorMsg); + Session.LogMessage('', StrSubstNo(this.HttpErrorMsg, LocalHttpResponseMessage.HttpStatusCode(), Response), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TietoevryProcessing.GetTietoevryTok()); Error(FriendlyErrorMsg); end; @@ -105,7 +105,7 @@ codeunit 6397 "Http Executor" end; var - AvalaraProcessing: Codeunit Processing; + TietoevryProcessing: 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 @@ -115,5 +115,4 @@ codeunit 6397 "Http Executor" 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 deleted file mode 100644 index e2e3d6fce2..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Integration.EnumExt.al +++ /dev/null @@ -1,18 +0,0 @@ -#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 index a4b93b0779..3f1050ac7b 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/IntegrationImpl.Codeunit.al @@ -46,7 +46,6 @@ codeunit 6392 "Integration Impl." implements IDocumentSender, IDocumentResponseH begin if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then exit; - ConnectionSetupCard.RunModal(); IsServiceIntegrationSetupRun := true; end; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al index f0484c9d18..39e1ff0e29 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al @@ -4,9 +4,9 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using System.Security.AccessControl; +using Microsoft.EServices.EDocumentConnector; -permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "D365 BASIC" +permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "EDocConnector - Edit" { IncludedPermissionSets = "Tietoevry Edit"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al index d121e222ad..fa29a1114e 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.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 6392 "Tietoevry EDoc. Connector - Read" extends "D365 READ" +permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "EDocConnector - Read" { 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 index f5c4fd4c99..c3d25f9e83 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al @@ -35,13 +35,13 @@ codeunit 6399 Processing InStream.Read(RequestContent); Request.Init(); - Request.Authenticate().CreateSubmitDocumentRequest(EDocument, EDocumentService, RequestContent); + 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."Tietoevry Document Id" := this.ParseDocumentId(ResponseContent); + EDocument."Document Id" := this.ParseDocumentId(ResponseContent); EDocument.Modify(true); end; @@ -56,10 +56,10 @@ codeunit 6399 Processing HttpExecutor: Codeunit "Http Executor"; ResponseContent: Text; begin - EDocument.TestField("Tietoevry Document Id"); + EDocument.TestField("Document Id"); Request.Init(); - Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Tietoevry Document Id"); + Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -119,7 +119,7 @@ codeunit 6399 Processing exit; end; - EDocument."Tietoevry Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id")); + EDocument."Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id")); EDocument.Modify(); Request.Init(); @@ -142,7 +142,7 @@ codeunit 6399 Processing ResponseContent: Text; begin Request.Init(); - Request.Authenticate().CreateAcknowledgeRequest(EDocument."Tietoevry Document Id"); + Request.Authenticate().CreateAcknowledgeRequest(EDocument."Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); ReceiveContext.Http().SetHttpRequestMessage(Request.GetRequest()); ReceiveContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -173,7 +173,7 @@ codeunit 6399 Processing var EDocument: Record "E-Document"; begin - EDocument.SetRange("Tietoevry Document Id", DocumentId); + EDocument.SetRange("Document Id", DocumentId); exit(not EDocument.IsEmpty()); end; @@ -191,10 +191,10 @@ codeunit 6399 Processing ResponseJson.Get('id', ValueJson); DocumentId := ValueJson.AsValue().AsText(); - if StrLen(DocumentId) > MaxStrLen(EDocument."Tietoevry Document Id") then + if StrLen(DocumentId) > MaxStrLen(EDocument."Document Id") then Error(this.TietoevryIdLongerErr); - exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id"))); + exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id"))); end; /// @@ -207,7 +207,7 @@ codeunit 6399 Processing begin ResponseJson.ReadFrom(ResponseMsg); ResponseJson.Get('id', ValueJson); - if EDocument."Tietoevry Document Id" <> ValueJson.AsValue().AsText() then + if EDocument."Document Id" <> ValueJson.AsValue().AsText() then Error(this.IncorrectDocumentIdInResponseErr); ResponseJson.Get('status', ValueJson); @@ -255,16 +255,9 @@ codeunit 6399 Processing ValidSchemeIdList: List of [Text]; SplitSeparator: Text; SchemeId: Text; - ResInStream: InStream; - JsonObject: JsonObject; - JsonToken: JsonToken; begin SplitSeparator := ' '; - NavApp.GetResource(ResourceSchemeIdPath, ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(ValidSchemeId); - JsonObject.ReadFrom(ValidSchemeId); - JsonObject.Get('schemeids', JsonToken); - ValidSchemeId := JsonToken.AsValue().AsText(); + ValidSchemeId := ValidSchemeIdTxt; ValidSchemeIdList := ValidSchemeId.Split(SplitSeparator); foreach SchemeId in ValidSchemeIdList do @@ -280,6 +273,8 @@ codeunit 6399 Processing 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; - ResourceSchemeIdPath: Label 'SchemeIds.txt', 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 index 263753cd3c..edd74fadd2 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -8,7 +8,6 @@ using Microsoft.EServices.EDocumentConnector; using Microsoft.eServices.EDocument; using System.Text; using System.Reflection; -using Microsoft.eServices.EDocument.Service.Participant; /// @@ -25,7 +24,6 @@ codeunit 6396 Requests HttpRequestMessage: HttpRequestMessage; BaseUrl, AuthUrl, CompanyId : Text; AccessToken: SecretText; - ServiceParticipantNotFoundErr: Label 'No Service Participant defined for Customer %1 and E-Document Service %2.', Comment = '%1 - The customer no., %2 - The e-document service code'; /// /// Create request for /outbound API @@ -33,9 +31,8 @@ codeunit 6396 Requests /// /// 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"; EDocumentService: Record "E-Document Service"; Data: Text): Codeunit Requests + procedure CreateSubmitDocumentRequest(EDocument: Record "E-Document"; Data: Text): Codeunit Requests var - ServiceParticipant: Record "Service Participant"; Base64Convert: Codeunit "Base64 Convert"; HttpHeaders, HttpContentHeaders : HttpHeaders; Content: Text; @@ -51,18 +48,14 @@ codeunit 6396 Requests EDocument.Get(EDocument."Entry No"); //Refresh ContentJson.Add('payload', Base64Convert.ToBase64(Data)); ContentJson.Add('sender', CompanyId); - - EDocument.TestField("Bill-to/Pay-to No."); - if not ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No.") then - Error(ServiceParticipantNotFoundErr, EDocument."Bill-to/Pay-to No.", EDocumentService.Code); - - ContentJson.Add('receiver', ServiceParticipant."Participant Identifier"); + 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); @@ -214,9 +207,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "Send Mode"::Production: + "E-Doc. Ext. Send Mode"::Production: exit(ConnectionSetup."API URL"); - "Send Mode"::Test: + "E-Doc. Ext. Send Mode"::Test: exit(ConnectionSetup."Sandbox API URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); @@ -230,9 +223,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "Send Mode"::Production: + "E-Doc. Ext. Send Mode"::Production: exit(ConnectionSetup."Authentication URL"); - "Send Mode"::Test: + "E-Doc. Ext. Send Mode"::Test: exit(ConnectionSetup."Sandbox Authentication URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al deleted file mode 100644 index 78b99207be..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.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; - -enum 6390 "Send Mode" -{ - Extensible = false; - - value(0; Production) - { - Caption = 'Production'; - } - value(1; Test) - { - Caption = 'Test'; - } -} \ No newline at end of file From 555c2e930bd4bdb5599fa5ad932dc0d78e0893a3 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 11:09:59 +0100 Subject: [PATCH 11/21] Syncing with version 26.0.27457.0 --- .../Tietoevry/app/src/Events.Codeunit.al | 39 ------------------ .../app/src/Formats/FormatEvents.Codeunit.al | 41 ++++++++++++++++++- .../TietoevryObjects.PermissionSet.al | 1 - 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al index b9e6811c26..913ddb9934 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al @@ -55,41 +55,6 @@ codeunit 6395 Events Error(this.MissingCustInfoErr, ServiceParticipant.FieldCaption("Participant Identifier"), Customer."No."); end; - [EventSubscriber(ObjectType::Table, Database::"E-Document Service", 'OnAfterValidateEvent', 'Document Format', false, false)] - local procedure OnAfterValidateDocumentFormat(var Rec: Record "E-Document Service"; var xRec: Record "E-Document Service"; CurrFieldNo: Integer) - var - EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; - begin - if Rec."Document Format" <> Rec."Document Format"::"Tietoevry PEPPOL BIS 3.0" then - exit; - - EDocServiceSupportedType.SetRange("E-Document Service Code", Rec.Code); - if EDocServiceSupportedType.IsEmpty() then begin - EDocServiceSupportedType.Init(); - EDocServiceSupportedType."E-Document Service Code" := Rec.Code; - EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Invoice"; - EDocServiceSupportedType.Insert(); - - EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Credit Memo"; - EDocServiceSupportedType.Insert(); - - EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Invoice"; - EDocServiceSupportedType.Insert(); - - EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Credit Memo"; - EDocServiceSupportedType.Insert(); - 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 @@ -106,10 +71,6 @@ codeunit 6395 Events var TietoevryProcessing: Codeunit "Processing"; -#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 } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al index 2aa1e8f373..15714e5619 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al @@ -8,6 +8,9 @@ using Microsoft.Sales.Peppol; using Microsoft.Sales.Document; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Service.Participant; +using System.IO; +using Microsoft.eServices.EDocument.IO.Peppol; +using System.Utilities; codeunit 6398 "Format Events" { @@ -15,6 +18,42 @@ codeunit 6398 "Format Events" SingleInstance = true; EventSubscriberInstance = StaticAutomatic; + [EventSubscriber(ObjectType::Codeunit, Codeunit::"EDoc PEPPOL BIS 3.0", OnAfterCreatePEPPOLXMLDocument, '', false, false)] + local procedure OnAfterCreatePEPPOLXMLDocument(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob"); + var + TempXMLBuffer: Record "XML Buffer" temporary; + DocInStream: InStream; + MessageDocumentId: Text; + begin + if EDocumentService."Service Integration V2" <> EDocumentService."Service Integration V2"::Tietoevry then + exit; + + TempBlob.CreateInStream(DocInStream); + TempXMLBuffer.LoadFromStream(DocInStream); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'ProfileID'); + if TempXMLBuffer.FindFirst() then + EDocument."Message Profile Id" := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); + TempXMLBuffer.SetRange(Name, 'xmlns'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId := TempXMLBuffer.Value; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '::' + TempXMLBuffer.Name; + + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Name, 'CustomizationID'); + if TempXMLBuffer.FindFirst() then + MessageDocumentId += '##' + TempXMLBuffer.Value + '::2.1'; + + EDocument."Message Document Id" := MessageDocumentId; + EDocument.Modify(); + 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 @@ -88,4 +127,4 @@ codeunit 6398 "Format Events" SchemeId := Parts.Get(1); EndpointId := Parts.Get(2); end; -} +} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al index b97ebb3491..89676a4bd8 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al @@ -19,6 +19,5 @@ permissionset 6390 "Tietoevry Objects" 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 From e58b5f1c17360644fbf738aa46e376e2009dd585 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 11:17:16 +0100 Subject: [PATCH 12/21] uptake --- .../EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al | 1 - 1 file changed, 1 deletion(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al index edd74fadd2..4ba7458716 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -45,7 +45,6 @@ codeunit 6396 Requests 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"); From 1828d9ce9cd2b8d84466caf595b38b6a10e2b64d Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 11:46:29 +0100 Subject: [PATCH 13/21] Removed dependency to "Document Id"-field --- .../app/src/Extensions/EDocFormat.EnumExt.al | 15 -- .../app/src/Extensions/EDocument.TableExt.al | 4 + .../app/src/Formats/EDocumentImpl.Codeunit.al | 148 ------------------ .../Tietoevry/app/src/Processing.Codeunit.al | 18 +-- 4 files changed, 13 insertions(+), 172 deletions(-) delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al deleted file mode 100644 index a12921e2b9..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocFormat.EnumExt.al +++ /dev/null @@ -1,15 +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; - -enumextension 6390 "E-Doc. Format Ext." extends "E-Document Format" -{ - value(6390; "Tietoevry PEPPOL BIS 3.0") - { - Implementation = "E-Document" = "Tietoevry E-Document"; - } -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al index f2efbf6968..b7ddeba638 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al @@ -14,6 +14,10 @@ tableextension 6390 "E-Document" extends "E-Document" { DataClassification = EndUserIdentifiableInformation; } + field(6391; "Tietoevry Document Id"; Text[50]) + { + DataClassification = SystemMetadata; + } field(6392; "Message Profile Id"; Text[50]) { DataClassification = SystemMetadata; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al deleted file mode 100644 index 089b0ebcde..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/EDocumentImpl.Codeunit.al +++ /dev/null @@ -1,148 +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.History; -using Microsoft.Service.History; -using Microsoft.Sales.Peppol; -using System.IO; -using Microsoft.eServices.EDocument.Service.Participant; -using Microsoft.Purchases.Document; - -codeunit 6391 "Tietoevry E-Document" implements "E-Document" -{ - 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"; - SalesCrMemoHeader: Record "Sales Cr.Memo Header"; - ServiceInvoiceHeader: Record "Service Invoice Header"; - ServiceCrMemoHeader: Record "Service Cr.Memo Header"; - PEPPOLValidation: Codeunit "PEPPOL Validation"; - PEPPOLServiceValidation: Codeunit "PEPPOL Service Validation"; - begin - case SourceDocumentHeader.Number of - Database::"Sales Header": - begin - SourceDocumentHeader.SetTable(SalesHeader); - PEPPOLValidation.Run(SalesHeader); - end; - Database::"Sales Invoice Header": - begin - SourceDocumentHeader.SetTable(SalesInvoiceHeader); - PEPPOLValidation.CheckSalesInvoice(SalesInvoiceHeader); - end; - Database::"Sales Cr.Memo Header": - begin - SourceDocumentHeader.SetTable(SalesCrMemoHeader); - PEPPOLValidation.CheckSalesCreditMemo(SalesCrMemoHeader); - end; - Database::"Service Invoice Header": - begin - SourceDocumentHeader.SetTable(ServiceInvoiceHeader); - PEPPOLServiceValidation.CheckServiceInvoice(ServiceInvoiceHeader); - end; - Database::"Service Cr.Memo Header": - begin - SourceDocumentHeader.SetTable(ServiceCrMemoHeader); - PEPPOLServiceValidation.CheckServiceCreditMemo(ServiceCrMemoHeader); - end; - end; - end; - - 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; - EDocErrorHelper: Codeunit "E-Document Error Helper"; - DocOutStream: OutStream; - DocInStream: InStream; - MessageDocumentId: Text; - begin - TempBlob.CreateOutStream(DocOutStream); - case EDocument."Document Type" of - EDocument."Document Type"::"Sales Invoice", EDocument."Document Type"::"Service Invoice": - this.GenerateInvoiceXMLFile(SourceDocumentHeader, DocOutStream); - EDocument."Document Type"::"Sales Credit Memo", EDocument."Document Type"::"Service Credit Memo": - this.GenerateCrMemoXMLFile(SourceDocumentHeader, DocOutStream); - else - EDocErrorHelper.LogSimpleErrorMessage(EDocument, StrSubstNo(this.DocumentTypeNotSupportedErr, EDocument.FieldCaption("Document Type"), EDocument."Document Type")); - end; - - EDocument.Find(); - ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No."); - EDocument."Bill-to/Pay-to Id" := ServiceParticipant."Participant Identifier"; - - TempBlob.CreateInStream(DocInStream); - TempXMLBuffer.LoadFromStream(DocInStream); - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name, 'ProfileID'); - if TempXMLBuffer.FindFirst() then - EDocument."Message Profile Id" := TempXMLBuffer.Value; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); - TempXMLBuffer.SetRange(Name, 'xmlns'); - if TempXMLBuffer.FindFirst() then - MessageDocumentId := TempXMLBuffer.Value; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name); - if TempXMLBuffer.FindFirst() then - MessageDocumentId += '::' + TempXMLBuffer.Name; - - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Name, 'CustomizationID'); - if TempXMLBuffer.FindFirst() then - MessageDocumentId += '##' + TempXMLBuffer.Value + '::2.1'; - - EDocument."Message Document Id" := MessageDocumentId; - EDocument.Modify(); - end; - - 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 System.Utilities."Temp Blob") - begin - this.ImportTEPeppol.ParseBasicInfo(EDocument, TempBlob); - end; - - 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 - this.ImportTEPeppol.ParseCompleteInfo(EDocument, TempPurchaseHeader, TempPurchaseLine, TempBlob); - - CreatedDocumentHeader.GetTable(TempPurchaseHeader); - CreatedDocumentLines.GetTable(TempPurchaseLine); - end; - - local procedure GenerateInvoiceXMLFile(VariantRec: Variant; var OutStr: OutStream) - var - SalesInvoicePEPPOLBIS30: XMLport "Sales Invoice - PEPPOL BIS 3.0"; - begin - SalesInvoicePEPPOLBIS30.Initialize(VariantRec); - SalesInvoicePEPPOLBIS30.SetDestination(OutStr); - SalesInvoicePEPPOLBIS30.Export(); - end; - - local procedure GenerateCrMemoXMLFile(VariantRec: Variant; var OutStr: OutStream) - var - SalesCrMemoPEPPOLBIS30: XMLport "Sales Cr.Memo - PEPPOL BIS 3.0"; - begin - SalesCrMemoPEPPOLBIS30.Initialize(VariantRec); - SalesCrMemoPEPPOLBIS30.SetDestination(OutStr); - SalesCrMemoPEPPOLBIS30.Export(); - end; - - var - 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'; -} diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al index c3d25f9e83..b7748e1a38 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al @@ -41,7 +41,7 @@ codeunit 6399 Processing SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); EDocument.Get(EDocument."Entry No"); - EDocument."Document Id" := this.ParseDocumentId(ResponseContent); + EDocument."Tietoevry Document Id" := this.ParseDocumentId(ResponseContent); EDocument.Modify(true); end; @@ -56,10 +56,10 @@ codeunit 6399 Processing HttpExecutor: Codeunit "Http Executor"; ResponseContent: Text; begin - EDocument.TestField("Document Id"); + EDocument.TestField("Tietoevry Document Id"); Request.Init(); - Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Document Id"); + Request.Authenticate().CreateGetDocumentStatusRequest(EDocument."Tietoevry Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -119,7 +119,7 @@ codeunit 6399 Processing exit; end; - EDocument."Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id")); + EDocument."Tietoevry Document Id" := CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id")); EDocument.Modify(); Request.Init(); @@ -142,7 +142,7 @@ codeunit 6399 Processing ResponseContent: Text; begin Request.Init(); - Request.Authenticate().CreateAcknowledgeRequest(EDocument."Document Id"); + Request.Authenticate().CreateAcknowledgeRequest(EDocument."Tietoevry Document Id"); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); ReceiveContext.Http().SetHttpRequestMessage(Request.GetRequest()); ReceiveContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -173,7 +173,7 @@ codeunit 6399 Processing var EDocument: Record "E-Document"; begin - EDocument.SetRange("Document Id", DocumentId); + EDocument.SetRange("Tietoevry Document Id", DocumentId); exit(not EDocument.IsEmpty()); end; @@ -191,10 +191,10 @@ codeunit 6399 Processing ResponseJson.Get('id', ValueJson); DocumentId := ValueJson.AsValue().AsText(); - if StrLen(DocumentId) > MaxStrLen(EDocument."Document Id") then + if StrLen(DocumentId) > MaxStrLen(EDocument."Tietoevry Document Id") then Error(this.TietoevryIdLongerErr); - exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Document Id"))); + exit(CopyStr(DocumentId, 1, MaxStrLen(EDocument."Tietoevry Document Id"))); end; /// @@ -207,7 +207,7 @@ codeunit 6399 Processing begin ResponseJson.ReadFrom(ResponseMsg); ResponseJson.Get('id', ValueJson); - if EDocument."Document Id" <> ValueJson.AsValue().AsText() then + if EDocument."Tietoevry Document Id" <> ValueJson.AsValue().AsText() then Error(this.IncorrectDocumentIdInResponseErr); ResponseJson.Get('status', ValueJson); From ab0ea6f7c183199625af4893466c7624381f6a0a Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 12:15:38 +0100 Subject: [PATCH 14/21] update --- .../Tietoevry/app/src/ConnectionSetup.Table.al | 5 ++++- .../Tietoevry/app/src/ConnectionSetupCard.Page.al | 8 ++++---- .../Tietoevry/app/src/Events.Codeunit.al | 8 ++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index 62fed8db9f..6dc96ce120 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -68,7 +68,7 @@ table 6392 "Connection Setup" TietoevryProcessing: Codeunit Processing; begin if not TietoevryProcessing.IsValidSchemeId(Rec."Company Id") then - Rec.FieldError(Rec."Company Id"); + Error(this.NotAValidCompanyIdErr, Rec."Company Id", Rec.FieldCaption("Company Id")); end; } field(13; "Send Mode"; Enum "E-Doc. Ext. Send Mode") @@ -86,4 +86,7 @@ table 6392 "Connection Setup" } } + var + NotAValidCompanyIdErr: Label '%1 is not a valid %2', Comment = '%1 = Company Id, %2 = fieldname'; + } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al index e9f30e30f2..36d7a27f48 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al @@ -105,9 +105,9 @@ page 6392 "Connection Setup Card" begin Token := TietoevryAuth.GetAccessToken(); if not Token.IsEmpty() then - Message(this.AuthSuccessMsg) + Message(this.TietoevryAuthSuccessMsg) else - Error(this.AuthFailedErr); + Error(this.TietoevryAuthFailedErr); end; } } @@ -131,8 +131,8 @@ page 6392 "Connection Setup Card" TietoevryAuth: Codeunit "Authenticator"; TietoevryProcessing: Codeunit Processing; - AuthSuccessMsg: Label 'Authenticated successfully'; - AuthFailedErr: Label 'Authentication failed'; + TietoevryAuthSuccessMsg: Label 'Authenticated successfully'; + TietoevryAuthFailedErr: Label 'Authentication failed'; [NonDebuggable] ClientID, ClientSecret : Text; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al index 913ddb9934..7055b525c3 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al @@ -18,9 +18,9 @@ codeunit 6395 Events [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Validation", OnCheckSalesDocumentOnBeforeCheckCompanyVATRegNo, '', false, false)] local procedure "PEPPOL Validation_OnCheckSalesDocumentOnBeforeCheckCompanyVATRegNo"(SalesHeader: Record "Sales Header"; CompanyInformation: Record "Company Information"; var IsHandled: Boolean) var - ExternalConnectionSetup: Record "Connection Setup"; + ConnectionSetup: Record "Connection Setup"; begin - if not ExternalConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; IsHandled := true; @@ -33,9 +33,9 @@ codeunit 6395 Events var ServiceParticipant: Record "Service Participant"; EDocumentService: Record "E-Document Service"; - EDocExtConnectionSetup: Record "Connection Setup"; + ConnectionSetup: Record "Connection Setup"; begin - if not EDocExtConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); if not EDocumentService.FindFirst() then From 95a42ed8d2e79115e6c2d1b3cebbcba59fd8c1dc Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 12:31:30 +0100 Subject: [PATCH 15/21] resolved issues --- .../app/src/Formats/FormatEvents.Codeunit.al | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al index 15714e5619..6c78ef44d3 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al @@ -57,27 +57,27 @@ codeunit 6398 "Format Events" [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"; + ConnectionSetup: Record "Connection Setup"; begin if not IsBISBilling then exit; - if not EDocExtConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; - this.SplitId(EDocExtConnectionSetup."Company Id", SupplierSchemeID, SupplierEndpointID); + this.SplitId(ConnectionSetup."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"; + ConnectionSetup: Record "Connection Setup"; begin if not IsBISBilling then exit; - if not EDocExtConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; - this.SplitId(EDocExtConnectionSetup."Company Id", PartyLegalEntitySchemeID, PartyLegalEntityCompanyID); + this.SplitId(ConnectionSetup."Company Id", PartyLegalEntitySchemeID, PartyLegalEntityCompanyID); end; [EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management", OnAfterGetAccountingCustomerPartyInfoByFormat, '', false, false)] @@ -85,11 +85,11 @@ codeunit 6398 "Format Events" var ServiceParticipant: Record "Service Participant"; EDocumentService: Record "E-Document Service"; - EDocExtConnectionSetup: Record "Connection Setup"; + ConnectionSetup: Record "Connection Setup"; begin if not IsBISBilling then exit; - if not EDocExtConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); if not EDocumentService.FindFirst() then @@ -104,11 +104,11 @@ codeunit 6398 "Format Events" var ServiceParticipant: Record "Service Participant"; EDocumentService: Record "E-Document Service"; - EDocExtConnectionSetup: Record "Connection Setup"; + ConnectionSetup: Record "Connection Setup"; begin if not IsBISBilling then exit; - if not EDocExtConnectionSetup.Get() then + if not ConnectionSetup.Get() then exit; EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); if not EDocumentService.FindFirst() then From 4620cf6974a42a0d048c019c7bc4c3951cbb97ff Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 13:44:23 +0100 Subject: [PATCH 16/21] workflow --- .../Tietoevry/app/src/Events.Codeunit.al | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al index 7055b525c3..25e9112199 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Events.Codeunit.al @@ -8,6 +8,8 @@ using Microsoft.Sales.Peppol; using Microsoft.Sales.Document; using Microsoft.Foundation.Company; using Microsoft.Sales.Customer; +using System.Automation; +using Microsoft.Foundation.Reporting; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Service.Participant; codeunit 6395 Events @@ -34,25 +36,33 @@ codeunit 6395 Events ServiceParticipant: Record "Service Participant"; EDocumentService: Record "E-Document Service"; ConnectionSetup: Record "Connection Setup"; + DocumentSendingProfile: Record "Document Sending Profile"; begin if not ConnectionSetup.Get() then exit; - EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); - if not EDocumentService.FindFirst() then + + DocumentSendingProfile.GetDefaultForCustomer(Customer."No.", DocumentSendingProfile); + if DocumentSendingProfile."Electronic Document" <> DocumentSendingProfile."Electronic Document"::"Extended E-Document Service Flow" then exit; + if not this.GetServicesInWorkflow(EDocumentService, DocumentSendingProfile."Electronic Service Flow") then + exit; + + EDocumentService.SetRange("Service Integration V2", EDocumentService."Service Integration V2"::Tietoevry); + if not EDocumentService.FindSet() then + exit; IsHandled := true; - if (SalesHeader."Document Type" in [SalesHeader."Document Type"::Invoice, SalesHeader."Document Type"::Order, SalesHeader."Document Type"::"Credit Memo"]) and - Customer.Get(SalesHeader."Bill-to Customer No.") - then - if Customer."VAT Registration No." = '' then - Error(this.MissingCustInfoErr, Customer.FieldCaption("VAT Registration No."), Customer."No."); + repeat + if (SalesHeader."Document Type" in [SalesHeader."Document Type"::Invoice, SalesHeader."Document Type"::Order, SalesHeader."Document Type"::"Credit Memo"]) and Customer.Get(SalesHeader."Bill-to Customer No.") then + if Customer."VAT Registration No." = '' then + 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 not ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, SalesHeader."Bill-to Customer No.") then + ServiceParticipant.Init(); - if ServiceParticipant."Participant Identifier" = '' then - Error(this.MissingCustInfoErr, ServiceParticipant.FieldCaption("Participant Identifier"), Customer."No."); + if ServiceParticipant."Participant Identifier" = '' then + Error(this.MissingCustInfoErr, ServiceParticipant.FieldCaption("Participant Identifier"), Customer."No."); + until EDocumentService.Next() = 0; end; [EventSubscriber(ObjectType::Table, Database::"Service Participant", 'OnAfterValidateEvent', 'Participant Identifier', false, false)] @@ -69,6 +79,40 @@ codeunit 6395 Events Rec.FieldError(Rec."Participant Identifier"); end; + internal procedure GetServicesInWorkflow(var EDocServices: Record "E-Document Service"; WorkFlowCode: Code[20]): Boolean + var + WorkflowStepArgument: Record "Workflow Step Argument"; + WorkflowStep: Record "Workflow Step"; + Workflow: Record Workflow; + Filter: Text; + begin + Workflow.Get(WorkFlowCode); + WorkflowStep.SetRange("Workflow Code", Workflow.Code); + WorkflowStep.SetRange(Type, WorkflowStep.Type::Response); + if WorkflowStep.FindSet() then + repeat + WorkflowStepArgument.Get(WorkflowStep.Argument); + this.AddFilter(Filter, WorkflowStepArgument."E-Document Service"); + until WorkflowStep.Next() = 0; + + if Filter = '' then + exit(false); + + EDocServices.SetFilter(Code, Filter); + exit(true); + end; + + internal procedure AddFilter(var Filter: Text; Value: Text) + begin + if Value = '' then + exit; + + if Filter = '' then + Filter := Value + else + Filter += '|' + Value; + end; + var TietoevryProcessing: Codeunit "Processing"; MissingCompInfVATRegNoErr: Label 'You must specify VAT Registration No. in %1.', Comment = '%1=Company Information'; From 25c524d58acc67f764dc6c9cf900a8cfc590c83c Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Mon, 2 Dec 2024 15:01:37 +0100 Subject: [PATCH 17/21] moved tooltips to table --- .../Tietoevry/app/src/ConnectionSetup.Table.al | 6 ++++++ .../Tietoevry/app/src/ConnectionSetupCard.Page.al | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index 6dc96ce120..8aee457e30 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -28,24 +28,28 @@ table 6392 "Connection Setup" { Caption = 'Authentication URL'; DataClassification = CustomerContent; + ToolTip = 'Specifies the URL to connect to Tietoevry.'; Editable = false; } field(5; "API URL"; Text[250]) { Caption = 'API URL'; DataClassification = CustomerContent; + ToolTip = 'Specifies the URL to connect to Tietoevry''s api.'; Editable = false; } field(6; "Sandbox Authentication URL"; Text[250]) { Caption = 'Sandbox Authentication URL'; DataClassification = CustomerContent; + ToolTip = 'Specifies the URL to connect to Tietoevry''s sandbox.'; Editable = false; } field(7; "Sandbox API URL"; Text[250]) { Caption = 'Sandbox Authentication URL'; DataClassification = CustomerContent; + ToolTip = 'Specifies the URL to connect to Tietoevry sandbox api.'; Editable = false; } field(8; "Token - Key"; Guid) @@ -62,6 +66,7 @@ table 6392 "Connection Setup" { Caption = 'Company ID'; DataClassification = EndUserIdentifiableInformation; + ToolTip = 'Specifies the company ID.'; trigger OnValidate() var @@ -75,6 +80,7 @@ table 6392 "Connection Setup" { Caption = 'Send Mode'; DataClassification = CustomerContent; + ToolTip = 'Specifies the send mode.'; } } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al index 36d7a27f48..1937063f74 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetupCard.Page.al @@ -52,32 +52,26 @@ page 6392 "Connection Setup Card" field("Authentication URL"; Rec."Authentication URL") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the URL to connect to Avalara.'; } field("API URL"; Rec."API URL") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the URL to connect to Avalara''s api.'; } field("Sandbox Authentication URL"; Rec."Sandbox Authentication URL") { ApplicationArea = Basic, Suite; - 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.'; } field("Send Mode"; Rec."Send Mode") { ApplicationArea = Basic, Suite; - ToolTip = 'Specifies the send mode.'; ShowMandatory = true; } } From 95886221632802dcc918c1b2f7362365ebda8a42 Mon Sep 17 00:00:00 2001 From: magnushar Date: Mon, 2 Dec 2024 22:38:05 +0100 Subject: [PATCH 18/21] Fixes --- .../app/src/ConnectionSetup.Table.al | 19 +- .../app/src/Extensions/EDocument.TableExt.al | 10 +- .../app/src/Formats/FormatEvents.Codeunit.al | 6 + .../Formats/TietoevryEDocImport.Codeunit.al | 587 ------------------ ...oevryEDocConnectorEdit.PermissionSetExt.al | 4 +- ...oevryEDocConnectorRead.PermissionSetExt.al | 5 +- .../TietoevryObjects.PermissionSet.al | 3 +- .../Tietoevry/app/src/Processing.Codeunit.al | 16 +- .../Tietoevry/app/src/Requests.Codeunit.al | 19 +- .../Tietoevry/app/src/SendMode.Enum.al | 19 + 10 files changed, 67 insertions(+), 621 deletions(-) delete mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al create mode 100644 Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index 8aee457e30..0dc938c97a 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -8,59 +8,61 @@ using Microsoft.EServices.EDocumentConnector; table 6392 "Connection Setup" { + Access = Internal; + DataClassification = SystemMetadata; + fields { field(1; Id; Code[10]) { - DataClassification = CustomerContent; + Caption = 'Id'; + ToolTip = 'Specifies the connection setup id.'; } field(2; "Client Id - Key"; Guid) { Caption = 'Client Id'; + ToolTip = 'Specifies the client id key.'; DataClassification = EndUserIdentifiableInformation; } field(3; "Client Secret - Key"; Guid) { Caption = 'Client Secret'; + ToolTip = 'Specifies the client secret key.'; DataClassification = EndUserIdentifiableInformation; } field(4; "Authentication URL"; Text[250]) { Caption = 'Authentication URL'; - DataClassification = CustomerContent; ToolTip = 'Specifies the URL to connect to Tietoevry.'; Editable = false; } field(5; "API URL"; Text[250]) { Caption = 'API URL'; - DataClassification = CustomerContent; ToolTip = 'Specifies the URL to connect to Tietoevry''s api.'; Editable = false; } field(6; "Sandbox Authentication URL"; Text[250]) { Caption = 'Sandbox Authentication URL'; - DataClassification = CustomerContent; ToolTip = 'Specifies the URL to connect to Tietoevry''s sandbox.'; Editable = false; } field(7; "Sandbox API URL"; Text[250]) { Caption = 'Sandbox Authentication URL'; - DataClassification = CustomerContent; ToolTip = 'Specifies the URL to connect to Tietoevry sandbox api.'; Editable = false; } field(8; "Token - Key"; Guid) { Caption = 'Token'; - DataClassification = SystemMetadata; + ToolTip = 'Specifies the token key.'; } field(9; "Token Expiry"; DateTime) { Caption = 'Token Expiry'; - DataClassification = SystemMetadata; + ToolTip = 'Specifies the token expiry date.'; } field(10; "Company Id"; Text[250]) { @@ -76,10 +78,9 @@ table 6392 "Connection Setup" Error(this.NotAValidCompanyIdErr, Rec."Company Id", Rec.FieldCaption("Company Id")); end; } - field(13; "Send Mode"; Enum "E-Doc. Ext. Send Mode") + field(13; "Send Mode"; Enum "Send Mode") { Caption = 'Send Mode'; - DataClassification = CustomerContent; ToolTip = 'Specifies the send mode.'; } } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al index b7ddeba638..5c770aac4e 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Extensions/EDocument.TableExt.al @@ -10,19 +10,15 @@ tableextension 6390 "E-Document" extends "E-Document" { fields { - field(6390; "Bill-to/Pay-to Id"; Text[100]) - { - DataClassification = EndUserIdentifiableInformation; - } - field(6391; "Tietoevry Document Id"; Text[50]) + field(6390; "Tietoevry Document Id"; Text[50]) { DataClassification = SystemMetadata; } - field(6392; "Message Profile Id"; Text[50]) + field(6391; "Message Profile Id"; Text[50]) { DataClassification = SystemMetadata; } - field(6393; "Message Document Id"; Text[200]) + field(6392; "Message Document Id"; Text[200]) { DataClassification = SystemMetadata; } diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al index 6c78ef44d3..985d564afc 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/FormatEvents.Codeunit.al @@ -118,6 +118,12 @@ codeunit 6398 "Format Events" this.SplitId(ServiceParticipant."Participant Identifier", CustPartyLegalEntityIDSchemeID, CustPartyLegalEntityCompanyID); end; + [EventSubscriber(ObjectType::Table, Database::"E-Document Log", 'OnBeforeExportDataStorage', '', false, false)] + local procedure SetFileExt(EDocumentLog: Record "E-Document Log"; var FileName: Text) + begin + FileName += '.xml'; + end; + local procedure SplitId(Input: Text; var SchemeId: Text; var EndpointId: Text) var diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al deleted file mode 100644 index 358bb77be3..0000000000 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Formats/TietoevryEDocImport.Codeunit.al +++ /dev/null @@ -1,587 +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.Document; -using System.IO; -using System.Text; -using Microsoft.Foundation.Attachment; -using Microsoft.Purchases.Vendor; -using Microsoft.Finance.GeneralLedger.Setup; -using Microsoft.eServices.EDocument.Service.Participant; - -// The following line can be removed when/if Microsoft changes the protection level on the EDocumentAttachmentGen.Insert() procedure -#pragma warning disable AA0137 -codeunit 6393 "Tietoevry E-Document Import" -{ - procedure ParseBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") - var - TempXMLBuffer: Record "XML Buffer" temporary; - GLSetup: Record "General Ledger Setup"; - DocStream: InStream; - begin - TempXMLBuffer.DeleteAll(); - TempBlob.CreateInStream(DocStream, TextEncoding::UTF8); - TempXMLBuffer.LoadFromStream(DocStream); - - GLSetup.Get(); - this.LCYCode := GLSetup."LCY Code"; - - EDocument.Direction := EDocument.Direction::Incoming; - - case UpperCase(this.GetDocumentType(TempXMLBuffer)) of - 'INVOICE': - this.ParseInvoiceBasicInfo(EDocument, TempXMLBuffer); - 'CREDITNOTE': - this.ParseCreditMemoBasicInfo(EDocument, TempXMLBuffer); - end; - end; - - procedure ParseCompleteInfo(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempBlob: Codeunit "Temp Blob") - var - TempXMLBuffer: Record "XML Buffer" temporary; - DocStream: InStream; - begin - TempXMLBuffer.DeleteAll(); - TempBlob.CreateInStream(DocStream, TextEncoding::UTF8); - TempXMLBuffer.LoadFromStream(DocStream); - - PurchaseHeader."Buy-from Vendor No." := EDocument."Bill-to/Pay-to No."; - PurchaseHeader."Currency Code" := EDocument."Currency Code"; - - case UpperCase(GetDocumentType(TempXMLBuffer)) of - 'INVOICE': - this.CreateInvoice(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); - 'CREDITNOTE': - this.CreateCreditMemo(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer); - end; - end; - - local procedure ParseInvoiceBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary) - var - DueDate, IssueDate : Text; - Currency: Text[10]; - begin - EDocument."Document Type" := EDocument."Document Type"::"Purchase Invoice"; - EDocument."Incoming E-Document No." := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); - this.ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'Invoice'); - this.ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'Invoice'); - - DueDate := this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DueDate'); - if DueDate <> '' then - Evaluate(EDocument."Due Date", DueDate, 9); - IssueDate := this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:IssueDate'); - if IssueDate <> '' then - Evaluate(EDocument."Document Date", IssueDate, 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(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cac:OrderReference/cbc:ID'), 1, MaxStrLen(EDocument."Order No.")); - - Currency := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/Invoice/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); - if this.LCYCode <> Currency then - EDocument."Currency Code" := Currency; - end; - - local procedure ParseCreditMemoBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary) - var - DueDate, IssueDate : Text; - Currency: Text[10]; - 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.")); - this.ParseAccountingSupplierParty(EDocument, TempXMLBuffer, 'CreditNote'); - this.ParseAccountingCustomerParty(EDocument, TempXMLBuffer, 'CreditNote'); - - DueDate := this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate'); - if DueDate <> '' then - Evaluate(EDocument."Due Date", DueDate, 9); - IssueDate := this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:IssueDate'); - if IssueDate <> '' then - Evaluate(EDocument."Document Date", IssueDate, 9); - - Evaluate(EDocument."Amount Excl. VAT", this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); - Evaluate(EDocument."Amount Incl. VAT", this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount'), 9); - - Currency := CopyStr(this.GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); - if this.LCYCode <> Currency then - EDocument."Currency Code" := Currency; - end; - - local procedure ParseAccountingSupplierParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) - var - Vendor: Record Vendor; - ServiceParticipant: Record "Service Participant"; - VendorName, VendorAddress : Text; - VendorId: Text[50]; - VendorNo: Code[20]; - begin - 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(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 := 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; - end; - end; - - local procedure ParseAccountingCustomerParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) - var - ReceivingId: Text[50]; - begin - 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(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) - var - DocumentAttachment: Record "Document Attachment"; - DocumentAttachmentData: Codeunit "Temp Blob"; - InStream: InStream; - begin - PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::Invoice; - 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 - this.ParseInvoice(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); - until TempXMLBuffer.Next() = 0; - - // Insert last document attachment - if DocumentAttachment."No." <> '' then begin - DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert - // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); - Clear(DocumentAttachment); - end; - - // Insert last line - PurchaseLine.Insert(); - PurchaseHeader.Modify(); - - // Allowance charge - 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) - var - DocumentAttachment: Record "Document Attachment"; - DocumentAttachmentData: Codeunit "Temp Blob"; - InStream: InStream; - begin - PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::"Credit Memo"; - PurchaseHeader."No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/CreditNote/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); - PurchaseHeader.Insert(); - - TempXMLBuffer.Reset(); - if TempXMLBuffer.FindSet() then - repeat - this.ParseCreditMemo(EDocument, PurchaseHeader, PurchaseLine, DocumentAttachment, DocumentAttachmentData, TempXMLBuffer); - until TempXMLBuffer.Next() = 0; - - // Insert last document attachment - if DocumentAttachment."No." <> '' then begin - DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert - // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); - Clear(DocumentAttachment); - end; - - // Insert last line - PurchaseLine.Insert(); - PurchaseHeader.Modify(); - - // Allowance charge - 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) - var - LineNo: Integer; - DocumentText: Text; - begin - case EDocument."Document Type" of - EDocument."Document Type"::"Purchase Invoice": - DocumentText := '/Invoice'; - EDocument."Document Type"::"Purchase Credit Memo": - DocumentText := '/CreditNote'; - end; - - TempXMLBuffer.Reset(); - TempXMLBuffer.SetFilter(Path, DocumentText + '/cac:AllowanceCharge*'); - - PurchaseLine.FindLast(); - LineNo := PurchaseLine."Line No." + 10000; - - if TempXMLBuffer.FindSet() then - repeat - case TempXMLBuffer.Path of - DocumentText + '/cac:AllowanceCharge/cbc:ChargeIndicator': - if TempXMLBuffer.Value = 'true' then begin - this.SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); - - PurchaseLine.Init(); - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - PurchaseLine."Line No." := LineNo; - PurchaseLine.Quantity := 1; - PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; - end; - DocumentText + '/cac:AllowanceCharge/cbc:Amount': - if TempXMLBuffer.Value <> '' then begin - Evaluate(PurchaseLine."Direct Unit Cost", TempXMLBuffer.Value, 9); - Evaluate(PurchaseLine.Amount, TempXMLBuffer.Value, 9); - end; - DocumentText + '/cac:AllowanceCharge/cbc:AllowanceChargeReason': - PurchaseLine.Description := CopyStr(TempXMLBuffer.Value, 1, MaxStrLen(PurchaseLine.Description)); - - end; - until TempXMLBuffer.Next() = 0; - - this.SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); - end; - - local procedure SetGLAccountAndInsertLine(var EDocument: Record "E-Document"; var PurchaseLine: record "Purchase Line" temporary; var LineNo: Integer) - var - RecRef: RecordRef; - begin - if PurchaseLine."Line No." = LineNo then begin - RecRef.GetTable(PurchaseLine); - this.EDocumentImportHelper.FindGLAccountForLine(EDocument, RecRef); - PurchaseLine."No." := RecRef.Field(PurchaseLine.FieldNo("No.")).Value; - PurchaseLine.Insert(); - LineNo += 10000; - end; - end; - - /// - /// Parses credit memo information line by line from TempXMLBuffer. - /// We handle the insert of Purchase Order Line and Document Attachment after the call to this function. - /// - local procedure ParseCreditMemo(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var DocumentAttachment: Record "Document Attachment"; DocumentAttachmentData: Codeunit "Temp Blob"; var TempXMLBuffer: Record "XML Buffer" temporary) - var - Base64Convert: Codeunit "Base64 Convert"; - OutStream: OutStream; - InStream: InStream; - Path, Value : Text; - begin - Path := TempXMLBuffer.Path; - Value := TempXMLBuffer.Value; - case Path of - '/CreditNote/cbc:ID': - PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); - '/CreditNote/cbc:IssueDate': - if Value <> '' then - Evaluate(PurchaseHeader."Document Date", Value, 9); - '/CreditNote/cac:OrderReference/cbc:ID': - PurchaseHeader."Vendor Order No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); - '/CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID': - PurchaseHeader."Applies-to Doc. No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Applies-to Doc. No.")); - '/CreditNote/cac:PayeeParty/cac:PartyName/cbc:Name': - PurchaseHeader."Pay-to Name" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Pay-to Name")); - '/CreditNote/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': - if Value <> '' then - Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); - '/CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount': - if Value <> '' then - Evaluate(PurchaseHeader."Amount Including VAT", Value, 9); - '/CreditNote/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ID': - PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); - '/CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName': - PurchaseHeader."Buy-from Address" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Address")); - '/CreditNote/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID', '/CreditNote/cac:PayeeParty/cac:PartyIdentification/cbc:ID': - PurchaseHeader."VAT Registration No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."VAT Registration No.")); - '/CreditNote/cac:PaymentMeans/cbc:PaymentDueDate': - if Value <> '' then - Evaluate(PurchaseHeader."Due Date", Value, 9); - '/CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount': - if Value <> '' then - Evaluate(PurchaseHeader.Amount, Value, 9); - '/CreditNote/cac:AdditionalDocumentReference/cbc:ID': - begin - if DocumentAttachment."No." <> '' then begin - DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert - // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); - Clear(DocumentAttachment); - end; - - DocumentAttachment.Init(); - DocumentAttachment."No." := CopyStr(PurchaseHeader."Vendor Invoice No.", 1, MaxStrLen(DocumentAttachment."No.")); - end; - '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject': - begin - DocumentAttachmentData.CreateOutStream(OutStream, TextEncoding::UTF8); - TempXMLBuffer.CalcFields("Value BLOB"); - TempXMLBuffer."Value BLOB".CreateInStream(InStream); - InStream.Read(Value, InStream.Length); - Base64Convert.FromBase64(Value, OutStream); - end; - '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@mimeCode': - DocumentAttachment.Validate("File Extension", DetermineFileType(Value)); - '/CreditNote/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@filename': - DocumentAttachment."File Name" := CopyStr(Value.Split('.').Get(1), 1, MaxStrLen(DocumentAttachment."File Name")); - '/CreditNote/cac:CreditNoteLine': - begin - if PurchaseLine."Document No." <> '' then - PurchaseLine.Insert(); - - PurchaseLine.Init(); - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - end; - '/CreditNote/cac:CreditNoteLine/cbc:CreditedQuantity': - if Value <> '' then - Evaluate(PurchaseLine.Quantity, Value, 9); - '/CreditNote/cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode': - PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - '/CreditNote/cac:CreditNoteLine/cbc:LineExtensionAmount': - if Value <> '' then - Evaluate(PurchaseLine.Amount, Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:AllowanceCharge/cbc:Amount': - if Value <> '' then - Evaluate(PurchaseLine."Line Discount Amount", Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:TaxTotal/cbc:TaxAmount': - if Value <> '' then - Evaluate(PurchaseLine."Amount Including VAT", Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:Item/cbc:Description': - PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); - '/CreditNote/cac:CreditNoteLine/cac:Item/cbc:Name': - PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); - '/CreditNote/cac:CreditNoteLine/cac:Item/cac:SellersItemIdentification/cbc:ID': - PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); - '/CreditNote/cac:CreditNoteLine/cac:Item/cac:StandardItemIdentification/cbc:ID': - PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); - '/CreditNote/cac:CreditNoteLine/cbc:ID': - Evaluate(PurchaseLine."Line No.", Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': - if Value <> '' then - Evaluate(PurchaseLine."VAT %", Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:Price/cbc:PriceAmount': - if Value <> '' then - Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); - '/CreditNote/cac:CreditNoteLine/cac:Price/cbc:BaseQuantity': - if Value <> '' then - Evaluate(PurchaseLine."Quantity (Base)", Value, 9); - '/CreditNote/cac:CreditNoteLine/cbc:Note': - this.SetlineType(PurchaseLine, Value); - end - end; - - /// - /// Parses invoice information line by line from TempXMLBuffer. - /// We handle the insert of Purchase Order Line and Document Attachment after the call to this function. - /// - local procedure ParseInvoice(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; var DocumentAttachment: Record "Document Attachment"; DocumentAttachmentData: Codeunit "Temp Blob"; var TempXMLBuffer: Record "XML Buffer" temporary) - var - Base64Convert: Codeunit "Base64 Convert"; - OutStream: OutStream; - InStream: InStream; - Path, Value : Text; - begin - Path := TempXMLBuffer.Path; - Value := TempXMLBuffer.Value; - case Path of - '/Invoice/cbc:ID': - PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); - '/Invoice/cac:OrderReference/cbc:ID': - PurchaseHeader."Vendor Order No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Order No.")); - '/Invoice/cac:PayeeParty/cac:PartyName/cbc:Name': - PurchaseHeader."Pay-to Name" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Pay-to Name")); - '/Invoice/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': - if Value <> '' then - Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); - '/Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount': - if Value <> '' then - Evaluate(PurchaseHeader."Amount Including VAT", Value, 9); - '/Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ID': - PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); - '/Invoice/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName': - PurchaseHeader."Buy-from Address" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Address")); - '/Invoice/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID': - PurchaseHeader."VAT Registration No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."VAT Registration No.")); - '/Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount': - if Value <> '' then - Evaluate(PurchaseHeader.Amount, Value, 9); - '/Invoice/cbc:DueDate': - if Value <> '' then - Evaluate(PurchaseHeader."Due Date", Value, 9); - '/Invoice/cbc:IssueDate': - if Value <> '' then - Evaluate(PurchaseHeader."Document Date", Value, 9); - '/Invoice/cac:AdditionalDocumentReference/cbc:ID': - begin - if DocumentAttachment."No." <> '' then begin - DocumentAttachmentData.CreateInStream(InStream, TextEncoding::UTF8); - // The following line can be uncommented when/if Microsoft changes the protection level on the procedure Insert - // EDocumentAttachmentGen.Insert(EDocument, InStream, DocumentAttachment.FindUniqueFileName(DocumentAttachment."File Name", DocumentAttachment."File Extension")); - Clear(DocumentAttachment); - end; - - DocumentAttachment.Init(); - DocumentAttachment."No." := CopyStr(Value, 1, MaxStrLen(DocumentAttachment."No.")); - end; - '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject': - begin - DocumentAttachmentData.CreateOutStream(OutStream, TextEncoding::UTF8); - TempXMLBuffer.CalcFields("Value BLOB"); - TempXMLBuffer."Value BLOB".CreateInStream(InStream); - InStream.Read(Value, InStream.Length); - Base64Convert.FromBase64(Value, OutStream); - end; - '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@mimeCode': - DocumentAttachment.Validate("File Extension", DetermineFileType(Value)); - '/Invoice/cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject/@filename': - DocumentAttachment."File Name" := CopyStr(Value.Split('.').Get(1), 1, MaxStrLen(DocumentAttachment."File Name")); - '/Invoice/cac:InvoiceLine': - begin - if PurchaseLine."Document No." <> '' then - PurchaseLine.Insert(); - - PurchaseLine.Init(); - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - end; - '/Invoice/cac:InvoiceLine/cbc:InvoicedQuantity': - if Value <> '' then - Evaluate(PurchaseLine.Quantity, Value, 9); - '/Invoice/cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode': - PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - '/Invoice/cac:InvoiceLine/cbc:LineExtensionAmount': - if Value <> '' then - Evaluate(PurchaseLine.Amount, Value, 9); - - '/Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount': - if Value <> '' then - Evaluate(PurchaseLine."Line Discount Amount", Value, 9); - '/Invoice/cac:InvoiceLine/cac:TaxTotal/cbc:TaxAmount': - if Value <> '' then - Evaluate(PurchaseLine."Amount Including VAT", Value, 9); - '/Invoice/cac:InvoiceLine/cac:Item/cbc:Description': - PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); - '/Invoice/cac:InvoiceLine/cac:Item/cbc:Name': - PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); - '/Invoice/cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID': - PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); - '/Invoice/cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID': - PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); - '/Invoice/cac:InvoiceLine/cbc:ID': - Evaluate(PurchaseLine."Line No.", Value, 9); - '/Invoice/cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': - if Value <> '' then - Evaluate(PurchaseLine."VAT %", Value, 9); - '/Invoice/cac:InvoiceLine/cac:Price/cbc:PriceAmount': - if Value <> '' then - Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); - '/Invoice/cac:InvoiceLine/cac:Price/cbc:BaseQuantity': - if Value <> '' then - Evaluate(PurchaseLine."Quantity (Base)", Value, 9); - '/Invoice/cac:InvoiceLine/cbc:Note': - this.SetlineType(PurchaseLine, Value); - end; - end; - - procedure DetermineFileType(MimeType: Text) FileExension: Text - begin - case MimeType of - 'image/jpeg': - exit('jpeg'); - 'image/png': - exit('png'); - 'application/pdf': - exit('pdf'); - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'application/vnd.oasis.opendocument.spreadsheet': - exit('xlsx'); - else - exit(''); - end; - end; - - local procedure GetNodeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text - begin - TempXMLBuffer.Reset(); - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange(Path, XPath); - - if TempXMLBuffer.FindFirst() then - exit(TempXMLBuffer.Value); - end; - - local procedure GetNodeAttributeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text - begin - TempXMLBuffer.Reset(); - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); - TempXMLBuffer.SetRange(Path, XPath); - - if TempXMLBuffer.FindFirst() then - exit(TempXMLBuffer.Value); - end; - - local procedure GetDocumentType(var TempXMLBuffer: Record "XML Buffer" temporary): Text - var - begin - TempXMLBuffer.Reset(); - TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); - TempXMLBuffer.SetRange("Parent Entry No.", 0); - - if not TempXMLBuffer.FindFirst() then - Error('Invalid XML file'); - - TempXMLBuffer.Reset(); - exit(TempXMLBuffer.Name); - end; - - local procedure SetLineType(var PurchaseLine: record "Purchase Line" temporary; Value: Text): Text - var - begin - case UpperCase(Value) of - 'ITEM': - PurchaseLine.Type := PurchaseLine.Type::Item; - 'CHARGE (ITEM)': - PurchaseLine.Type := PurchaseLine.Type::"Charge (Item)"; - 'RESOURCE': - PurchaseLine.Type := PurchaseLine.Type::Resource; - 'G/L ACCOUNT': - PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; - end; - end; - - [EventSubscriber(ObjectType::Table, Database::"E-Document Log", 'OnBeforeExportDataStorage', '', false, false)] - local procedure SetFileExt(EDocumentLog: Record "E-Document Log"; var FileName: Text) - begin - FileName += '.xml'; - end; - - var - - // The variable "EDocumentAttachmentGen" is when/if Microsoft changes the protection level on the procedure Insert - EDocumentAttachmentGen: Codeunit "E-Doc. Attachment Processor"; - EDocumentImportHelper: Codeunit "E-Document Import Helper"; - LCYCode: Code[10]; -} \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al index 39e1ff0e29..f0484c9d18 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorEdit.PermissionSetExt.al @@ -4,9 +4,9 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; +using System.Security.AccessControl; -permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "EDocConnector - Edit" +permissionsetextension 6394 "Tietoevry EDoc. Connector - Edit" extends "D365 BASIC" { IncludedPermissionSets = "Tietoevry Edit"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al index fa29a1114e..d121e222ad 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryEDocConnectorRead.PermissionSetExt.al @@ -3,9 +3,10 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; -permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "EDocConnector - Read" +using System.Security.AccessControl; + +permissionsetextension 6392 "Tietoevry EDoc. Connector - Read" extends "D365 READ" { IncludedPermissionSets = "Tietoevry Read"; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al index 89676a4bd8..57d0434892 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Permissions/TietoevryObjects.PermissionSet.al @@ -18,6 +18,5 @@ permissionset 6390 "Tietoevry Objects" codeunit Authenticator = X, codeunit Requests = X, codeunit Events = X, - codeunit "Http Executor" = X, - codeunit "Tietoevry E-Document Import" = X; + codeunit "Http Executor" = X; } \ 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 index b7748e1a38..be72229ed5 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Processing.Codeunit.al @@ -35,7 +35,7 @@ codeunit 6399 Processing InStream.Read(RequestContent); Request.Init(); - Request.Authenticate().CreateSubmitDocumentRequest(EDocument, RequestContent); + Request.Authenticate().CreateSubmitDocumentRequest(EDocument, EDocumentService, RequestContent); ResponseContent := HttpExecutor.ExecuteHttpRequest(Request); SendContext.Http().SetHttpRequestMessage(Request.GetRequest()); SendContext.Http().SetHttpResponseMessage(HttpExecutor.GetResponse()); @@ -255,9 +255,16 @@ codeunit 6399 Processing ValidSchemeIdList: List of [Text]; SplitSeparator: Text; SchemeId: Text; + ResInStream: InStream; + JsonObject: JsonObject; + JsonToken: JsonToken; begin SplitSeparator := ' '; - ValidSchemeId := ValidSchemeIdTxt; + NavApp.GetResource(ResourceSchemeIdPath, ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(ValidSchemeId); + JsonObject.ReadFrom(ValidSchemeId); + JsonObject.Get('schemeids', JsonToken); + ValidSchemeId := JsonToken.AsValue().AsText(); ValidSchemeIdList := ValidSchemeId.Split(SplitSeparator); foreach SchemeId in ValidSchemeIdList do @@ -273,8 +280,5 @@ codeunit 6399 Processing 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 - + ResourceSchemeIdPath: Label 'SchemeIds.txt', Locked = true; } \ 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 index 4ba7458716..270dd19009 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -8,6 +8,7 @@ using Microsoft.EServices.EDocumentConnector; using Microsoft.eServices.EDocument; using System.Text; using System.Reflection; +using Microsoft.eServices.EDocument.Service.Participant; /// @@ -24,6 +25,7 @@ codeunit 6396 Requests HttpRequestMessage: HttpRequestMessage; BaseUrl, AuthUrl, CompanyId : Text; AccessToken: SecretText; + ServiceParticipantNotFoundErr: Label 'No Service Participant defined for Customer %1 and E-Document Service %2.', Comment = '%1 - The customer no., %2 - The e-document service code'; /// /// Create request for /outbound API @@ -31,8 +33,9 @@ codeunit 6396 Requests /// /// 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 + procedure CreateSubmitDocumentRequest(EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; Data: Text): Codeunit Requests var + ServiceParticipant: Record "Service Participant"; Base64Convert: Codeunit "Base64 Convert"; HttpHeaders, HttpContentHeaders : HttpHeaders; Content: Text; @@ -45,9 +48,13 @@ codeunit 6396 Requests this.HttpRequestMessage.GetHeaders(HttpHeaders); HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); + EDocument.TestField("Bill-to/Pay-to No."); + if not ServiceParticipant.Get(EDocumentService.Code, ServiceParticipant."Participant Type"::Customer, EDocument."Bill-to/Pay-to No.") then + Error(ServiceParticipantNotFoundErr, EDocument."Bill-to/Pay-to No.", EDocumentService.Code); + ContentJson.Add('payload', Base64Convert.ToBase64(Data)); ContentJson.Add('sender', CompanyId); - ContentJson.Add('receiver', EDocument."Bill-to/Pay-to Id"); + ContentJson.Add('receiver', ServiceParticipant."Participant Identifier"); ContentJson.Add('profileId', EDocument."Message Profile Id"); ContentJson.Add('documentId', EDocument."Message Document Id"); ContentJson.Add('channel', 'PEPPOL'); @@ -206,9 +213,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "E-Doc. Ext. Send Mode"::Production: + "Send Mode"::Production: exit(ConnectionSetup."API URL"); - "E-Doc. Ext. Send Mode"::Test: + "Send Mode"::Test: exit(ConnectionSetup."Sandbox API URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); @@ -222,9 +229,9 @@ codeunit 6396 Requests ConnectionSetup.Get(); case ConnectionSetup."Send Mode" of - "E-Doc. Ext. Send Mode"::Production: + "Send Mode"::Production: exit(ConnectionSetup."Authentication URL"); - "E-Doc. Ext. Send Mode"::Test: + "Send Mode"::Test: exit(ConnectionSetup."Sandbox Authentication URL"); else Error('Unsupported %1 in %2', ConnectionSetup.FieldCaption("Send Mode"), ConnectionSetup.TableCaption); diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al new file mode 100644 index 0000000000..3b5ba257e5 --- /dev/null +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/SendMode.Enum.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +enum 6390 "Send Mode" +{ + Access = Internal; + Extensible = false; + value(0; Production) + { + Caption = 'Production'; + } + value(1; Test) + { + Caption = 'Test'; + } +} \ No newline at end of file From 71a7b2d88bfd15a060a4b40674468538e0753afc Mon Sep 17 00:00:00 2001 From: magnushar Date: Mon, 2 Dec 2024 22:56:57 +0100 Subject: [PATCH 19/21] Fix tests --- .../Tietoevry/test/app.json | 8 +-- .../test/src/IntegrationTests.Codeunit.al | 57 ++++--------------- 2 files changed, 11 insertions(+), 54 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json index 4a5e78196e..a27f6fa6f0 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/app.json @@ -19,17 +19,11 @@ "version": "26.0.0.0" }, { - "id": "d852a468-263e-49e5-bfda-f09e33342b89", + "id": "3b3c094c-ae7f-43f2-9246-22111b688d2e", "name": "E-Document Connector - Tietoevry", "publisher": "Microsoft", "version": "26.0.0.0" }, - { - "id": "d852a468-263e-49e5-bfda-f09e33342b89", - "name": "E-Documents Connector with External Endpoints", - "publisher": "Microsoft", - "version": "26.0.0.0" - }, { "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8c", "name": "E-Document Core Tests", diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al index e93bc6b066..cfdfec3e89 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/test/src/IntegrationTests.Codeunit.al @@ -10,6 +10,7 @@ using Microsoft.Purchases.Document; using Microsoft.Foundation.Company; using Microsoft.Purchases.Vendor; using System.Threading; +using Microsoft.eServices.EDocument.Integration; codeunit 148193 "Integration Tests" { @@ -43,8 +44,8 @@ codeunit 148193 "Integration Tests" // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); - // [Then] Document Id has been correctly set on E-Document, parsed from Integration response. - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); + // [Then] Tietoevry Document Id has been correctly set on E-Document, parsed from Integration response. + Assert.AreEqual(MockServiceDocumentId(), EDocument."Tietoevry Document Id", 'Tietoevry integration failed to set Tietoevry Document Id on E-Document'); Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); // [THEN] Open E-Document page @@ -129,8 +130,8 @@ codeunit 148193 "Integration Tests" // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); - // [Then] Document Id has been correctly set on E-Document, parsed from Integration response - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); + // [Then] Tietoevry Document Id has been correctly set on E-Document, parsed from Integration response + Assert.AreEqual(MockServiceDocumentId(), EDocument."Tietoevry Document Id", 'Tietoevry integration failed to set Tietoevry Document Id on E-Document'); // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); @@ -251,8 +252,8 @@ codeunit 148193 "Integration Tests" // [When] EDocument is fetched after running Tietoevry SubmitDocument EDocument.FindLast(); - // [Then] Document Id has been correctly set on E-Document, parsed from Integration response - Assert.AreEqual(MockServiceDocumentId(), EDocument."Document Id", 'Tietoevry integration failed to set Document Id on E-Document'); + // [Then] Tietoevry Document Id has been correctly set on E-Document, parsed from Integration response + Assert.AreEqual(MockServiceDocumentId(), EDocument."Tietoevry Document Id", 'Tietoevry integration failed to set Tietoevry Document Id on E-Document'); // [Then] E-Document is pending response as Tietoevry is async Assert.AreEqual(Enum::"E-Document Status"::"In Progress", EDocument.Status, 'E-Document should be set to in progress'); @@ -423,7 +424,7 @@ codeunit 148193 "Integration Tests" EDocument.FindLast(); Assert.AreEqual(Enum::"E-Document Status"::Error, EDocument.Status, 'E-Document should be set to error state when service is down.'); - Assert.AreEqual('', EDocument."Document Id", 'Document Id on E-Document should not be set.'); + Assert.AreEqual('', EDocument."Tietoevry Document Id", 'Tietoevry Document Id on E-Document should not be set.'); EDocumentPage.OpenView(); EDocumentPage.GoToRecord(EDocument); @@ -481,35 +482,6 @@ codeunit 148193 "Integration Tests" Assert.AreEqual(Vendor."No.", PurchaseHeader."Buy-from Vendor No.", 'Wrong Vendor'); end; - [Test] - [HandlerFunctions('SelectCompany')] - procedure OpenCompanyList() - var - ConnectionSetup: Record "Connection Setup"; - ConnectionSetupCard: TestPage "Connection Setup Card"; - begin - Initialize(); - - // [GIVEN] O365Full member - LibraryPermission.SetO365Full(); - - // [THEN] No company has been selected - ConnectionSetup.Get(); - Assert.AreEqual('', ConnectionSetup."Company Id", 'Has to be empty before selecting company'); - Assert.AreEqual('', ConnectionSetup."Company Name", 'Has to be empty before selecting company'); - - // [WHEN] User click SelectCompanyId action on page - ConnectionSetupCard.OpenView(); - ConnectionSetupCard.SelectCompanyId.Invoke(); - - // Selection of company handled by SelectCompany modal handler... - - // [THEN] Company is populated in connection setup - ConnectionSetup.Get(); - Assert.AreEqual('610f55f3-76b6-42eb-a697-2b0b2e02a5bf', ConnectionSetup."Company Id", 'Has to be empty before selecting company'); - Assert.AreEqual('MS Business Central Ltd - ELR SBX', ConnectionSetup."Company Name", 'Has to be empty before selecting company'); - end; - local procedure Initialize() var ConnectionSetup: Record "Connection Setup"; @@ -537,10 +509,9 @@ codeunit 148193 "Integration Tests" exit; LibraryEDocument.SetupStandardVAT(); - LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::"TE PEPPOL BIS 3.0", Enum::"E-Document Integration"::Tietoevry); - EDocumentService."Tietoevry Mandate" := 'GB-Test-Mandate'; + LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"Service Integration"::Tietoevry); - LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"TE PEPPOL BIS 3.0", Enum::"E-Document Integration"::Tietoevry); + LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"PEPPOL BIS 3.0", Enum::"Service Integration"::Tietoevry); EDocumentService."Auto Import" := true; EDocumentService."Import Minutes between runs" := 10; EDocumentService."Import Start Time" := Time(); @@ -573,7 +544,6 @@ codeunit 148193 "Integration Tests" begin ConnectionSetup.Get(); ConnectionSetup."Company Id" := Id; - ConnectionSetup."Company Name" := Name; ConnectionSetup.Modify(true); end; @@ -628,13 +598,6 @@ codeunit 148193 "Integration Tests" exit('610f55f3-76b6-42eb-a697-2b0b2e02a5bf'); end; - [ModalPageHandler] - procedure SelectCompany(var CompanyList: TestPage "Company List") - begin - CompanyList.First(); - CompanyList.OK().Invoke(); - end; - [ModalPageHandler] internal procedure EDocServicesPageHandler(var EDocServicesPage: TestPage "E-Document Services") begin From 456c1e5453e02e9989ee56249cc2b9aba0c46e55 Mon Sep 17 00:00:00 2001 From: Roger Larsson Date: Wed, 11 Dec 2024 08:21:02 +0100 Subject: [PATCH 20/21] Removed unused directives --- .../Tietoevry/app/src/ConnectionSetup.Table.al | 2 -- .../EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al | 1 - 2 files changed, 3 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al index 0dc938c97a..1940e71f1e 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/ConnectionSetup.Table.al @@ -4,8 +4,6 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; - table 6392 "Connection Setup" { Access = Internal; diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al index 270dd19009..ca203df054 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -4,7 +4,6 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.EServices.EDocumentConnector.Tietoevry; -using Microsoft.EServices.EDocumentConnector; using Microsoft.eServices.EDocument; using System.Text; using System.Reflection; From ac9597fa249db2f5ca756ef88f3550dfdd17a8b5 Mon Sep 17 00:00:00 2001 From: "mats.johansson8" Date: Mon, 23 Dec 2024 13:51:38 +0100 Subject: [PATCH 21/21] Fixed DocumentDownload --- .../Tietoevry/app/src/Requests.Codeunit.al | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al index ca203df054..971379640e 100644 --- a/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Tietoevry/app/src/Requests.Codeunit.al @@ -115,7 +115,7 @@ codeunit 6396 Requests /// /// Create request for /inbound/$id - /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Inbound%20Resource/get_inbound__id_ + /// https://accesspoint.qa.dataplatfor.ms/swagger-ui/#/Inbound%20Resource/get_inbound__id___payload_type__document /// /// Document Id /// A request object that can be used for the endpoint. @@ -124,12 +124,12 @@ codeunit 6396 Requests HttpHeaders: HttpHeaders; begin Clear(this.HttpRequestMessage); - this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/inbound/' + Id); + this.HttpRequestMessage.SetRequestUri(this.BaseUrl + '/inbound/' + Id + '/PAYLOAD/document'); this.HttpRequestMessage.Method := 'GET'; this.HttpRequestMessage.GetHeaders(HttpHeaders); HttpHeaders.Add('Authorization', this.AddBearer(this.AccessToken)); - HttpHeaders.Add('Accept', 'application/json'); + HttpHeaders.Add('Accept', 'application/octet-stream'); exit(this); end;