From b15ef56222af20f99443114301c535d02550852f Mon Sep 17 00:00:00 2001 From: Kilian Seizinger <56249171+pri-kise@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:31:54 +0100 Subject: [PATCH 1/2] Use correct IBAN for export --- .../ExportXRechnungDocument.Codeunit.al | 52 ++++++++-- .../src/XRechnungXMLDocumentTests.Codeunit.al | 94 ++++++++++++++++++- 2 files changed, 134 insertions(+), 12 deletions(-) diff --git a/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al b/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al index e5b7895848..9a7b08c291 100644 --- a/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al +++ b/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.eServices.EDocument.Formats; +using Microsoft.Bank.BankAccount; using Microsoft.CRM.Team; using Microsoft.eServices.EDocument; using Microsoft.Finance.Currency; @@ -119,7 +120,7 @@ codeunit 13916 "Export XRechnung Document" InsertAccountingSupplierParty(SalesInvoiceHeader."Responsibility Center", SalesInvoiceHeader."Salesperson Code", RootXMLNode); InsertAccountingCustomerParty(RootXMLNode, SalesInvoiceHeader); InsertDelivery(RootXMLNode, SalesInvoiceHeader); - InsertPaymentMeans(RootXMLNode, '68', 'PayeeFinancialAccount', SalesInvoiceHeader."Company Bank Account Code"); + InsertPaymentMeans(RootXMLNode, '58', 'PayeeFinancialAccount', SalesInvoiceHeader."Company Bank Account Code"); InsertPaymentTerms(RootXMLNode, SalesInvoiceHeader."Payment Terms Code"); InsertVATAmounts(SalesInvLine, LineVATAmount, LineAmount, LineDiscAmount, SalesInvoiceHeader."Prices Including VAT", Currency); InsertInvDiscountAllowanceCharge(LineAmounts, SalesInvLine, CurrencyCode, RootXMLNode, LineDiscAmount, LineAmount, Currency."Amount Rounding Precision"); @@ -164,7 +165,7 @@ codeunit 13916 "Export XRechnung Document" InsertAccountingSupplierParty(SalesCrMemoHeader."Responsibility Center", SalesCrMemoHeader."Salesperson Code", RootXMLNode); InsertAccountingCustomerParty(RootXMLNode, SalesCrMemoHeader); InsertDelivery(RootXMLNode, SalesCrMemoHeader); - InsertPaymentMeans(RootXMLNode, '68', '', SalesCrMemoHeader."Company Bank Account Code"); + InsertPaymentMeans(RootXMLNode, '58', '', SalesCrMemoHeader."Company Bank Account Code"); InsertPaymentTerms(RootXMLNode, SalesCrMemoHeader."Payment Terms Code"); InsertVATAmounts(SalesCrMemoLine, LineVATAmount, LineAmount, LineDiscAmount, SalesCrMemoHeader."Prices Including VAT", Currency); InsertInvDiscountAllowanceCharge(LineAmounts, SalesCrMemoLine, CurrencyCode, RootXMLNode, LineDiscAmount, LineAmount, Currency."Amount Rounding Precision"); @@ -388,22 +389,23 @@ codeunit 13916 "Export XRechnung Document" local procedure InsertPayeeFinancialAccount(var PaymentMeansElement: XmlElement; PayeeFinancialAccount: Text[30]; CompanyBankAccountCode: Code[20]); var PayeeFinancialAccountElement: XmlElement; + IBAN: Text[50]; + SWIFTCode: Code[20]; begin PayeeFinancialAccountElement := XmlElement.Create(PayeeFinancialAccount, XmlNamespaceCAC); - if CompanyBankAccountCode <> '' then - PayeeFinancialAccountElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, CompanyBankAccountCode)) - else - PayeeFinancialAccountElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, CompanyInformation."Bank Account No.")); - InsertFinancialInstitutionBranch(PayeeFinancialAccountElement); + GetBankAccountPaymentDetails(CompanyBankAccountCode, IBAN, SWIFTCode); + PayeeFinancialAccountElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, GetIBAN(IBAN))); + if SWIFTCode <> '' then + InsertFinancialInstitutionBranch(PayeeFinancialAccountElement, SWIFTCode); PaymentMeansElement.Add(PayeeFinancialAccountElement); end; - local procedure InsertFinancialInstitutionBranch(var RootElement: XmlElement); + local procedure InsertFinancialInstitutionBranch(var RootElement: XmlElement; SWIFTCode: Code[20]); var FinancialInstitutionBranchElement: XmlElement; begin FinancialInstitutionBranchElement := XmlElement.Create('FinancialInstitutionBranch', XmlNamespaceCAC); - FinancialInstitutionBranchElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, CompanyInformation."Bank Branch No.")); + FinancialInstitutionBranchElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, GetIBAN(SWIFTCode))); RootElement.Add(FinancialInstitutionBranchElement); end; @@ -1291,6 +1293,38 @@ codeunit 13916 "Export XRechnung Document" GeneralLedgerSetup.Get(); OnAfterGetSetups(CompanyInformation, GeneralLedgerSetup); end; + + local procedure GetIBAN(IBAN: Text[50]) IBANFormatted: Text[50] + begin + // Format IBAN to remove spaces and ensure it is in uppercase + if IBAN = '' then + exit(''); + IBANFormatted := UpperCase(DelChr(IBAN, '=', ' ')); + exit(CopyStr(IBANFormatted, 1, 50)); + end; + + local procedure GetBankAccountPaymentDetails(BankAccountCode: Code[20]; var IBAN: Text[50]; var SWIFTCode: Code[20]) + var + BankAccount: Record "Bank Account"; + begin + Clear(IBAN); + Clear(SWIFTCode); + + if BankAccountCode = '' then begin + IBAN := CompanyInformation.IBAN; + SWIFTCode := CompanyInformation."SWIFT Code"; + exit; + end; + + if BankAccount.Get(BankAccountCode) then begin + IBAN := BankAccount.IBAN; + SWIFTCode := BankAccount."SWIFT Code"; + exit; + end; + + IBAN := CompanyInformation.IBAN; + SWIFTCode := CompanyInformation."SWIFT Code"; + end; #endregion [IntegrationEvent(false, false)] diff --git a/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al b/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al index 03b0924e35..a13df13a35 100644 --- a/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al +++ b/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.eServices.EDocument.Formats; +using Microsoft.Bank.BankAccount; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Integration; using Microsoft.Finance.Currency; @@ -190,7 +191,37 @@ codeunit 13918 "XRechnung XML Document Tests" ExportInvoice(SalesInvoiceHeader, TempXMLBuffer); // [THEN] XRechnung Electronic Document is created with bank informarion as payment means - VerifyPaymentMeans(TempXMLBuffer, '/ubl:Invoice/cac:PaymentMeans'); + VerifyPaymentMeans(TempXMLBuffer, '/ubl:Invoice/cac:PaymentMeans', CompanyInformation.IBAN, CompanyInformation."SWIFT Code"); + end; + + [Test] + procedure ExportPostedSalesInvoiceInXRechnungFormatVerifyBankAccountPaymentMeans(); + var + BankAccount: Record "Bank Account"; + SalesInvoiceHeader: Record "Sales Invoice Header"; + TempXMLBuffer: Record "XML Buffer" temporary; + BankAccountIBAN: Text; + BankAccountSWIFT: Text; + begin + // [SCENARIO 496414] Export posted sales invoice uses Bank Account IBAN and SWIFT Code when Company Bank Account Code is specified + Initialize(); + + // [GIVEN] Create Bank Account with specific IBAN and SWIFT Code + BankAccountIBAN := LibraryUtility.GenerateMOD97CompliantCode(); + BankAccountSWIFT := LibraryUtility.GenerateGUID(); + LibraryERM.CreateBankAccount(BankAccount); + BankAccount.IBAN := BankAccountIBAN; + BankAccount."SWIFT Code" := BankAccountSWIFT; + BankAccount.Modify(true); + + // [GIVEN] Create and Post Sales Invoice with Bank Account Code + SalesInvoiceHeader.Get(CreateAndPostSalesDocumentWithBankAccount("Sales Document Type"::Invoice, Enum::"Sales Line Type"::Item, BankAccount."No.")); + + // [WHEN] Export XRechnung Electronic Document. + ExportInvoice(SalesInvoiceHeader, TempXMLBuffer); + + // [THEN] XRechnung Electronic Document uses Bank Account IBAN and SWIFT Code + VerifyPaymentMeans(TempXMLBuffer, '/ubl:Invoice/cac:PaymentMeans', BankAccountIBAN, BankAccountSWIFT); end; [Test] @@ -514,7 +545,37 @@ codeunit 13918 "XRechnung XML Document Tests" // [WHEN] Export XRechnung Electronic Document. ExportCreditMemo(SalesCrMemoHeader, TempXMLBuffer); - // [THEN] XRechnung Electronic Document is created with bank informarion as payment means + // [THEN] XRechnung Electronic Document is created with payment means code + VerifyPaymentMeans(TempXMLBuffer, '/ns0:CreditNote/cac:PaymentMeans'); + end; + + [Test] + procedure ExportPostedSalesCrMemoInXRechnungFormatVerifyBankAccountPaymentMeans(); + var + BankAccount: Record "Bank Account"; + SalesCrMemoHeader: Record "Sales Cr.Memo Header"; + TempXMLBuffer: Record "XML Buffer" temporary; + BankAccountIBAN: Text; + BankAccountSWIFT: Text; + begin + // [SCENARIO 496414] Export posted sales cr. memo uses Bank Account IBAN and SWIFT Code when Company Bank Account Code is specified + Initialize(); + + // [GIVEN] Create Bank Account with specific IBAN and SWIFT Code + BankAccountIBAN := LibraryUtility.GenerateMOD97CompliantCode(); + BankAccountSWIFT := LibraryUtility.GenerateGUID(); + LibraryERM.CreateBankAccount(BankAccount); + BankAccount.IBAN := BankAccountIBAN; + BankAccount."SWIFT Code" := BankAccountSWIFT; + BankAccount.Modify(true); + + // [GIVEN] Create and Post sales cr. memo with Bank Account Code + SalesCrMemoHeader.Get(CreateAndPostSalesDocumentWithBankAccount("Sales Document Type"::"Credit Memo", Enum::"Sales Line Type"::Item, BankAccount."No.")); + + // [WHEN] Export XRechnung Electronic Document. + ExportCreditMemo(SalesCrMemoHeader, TempXMLBuffer); + + // [THEN] XRechnung Electronic Document has payment means code VerifyPaymentMeans(TempXMLBuffer, '/ns0:CreditNote/cac:PaymentMeans'); end; @@ -751,6 +812,17 @@ codeunit 13918 "XRechnung XML Document Tests" exit(LibrarySales.PostSalesDocument(SalesHeader, true, true)); end; + local procedure CreateAndPostSalesDocumentWithBankAccount(DocumentType: Enum "Sales Document Type"; LineType: Enum "Sales Line Type"; BankAccountCode: Code[20]): Code[20]; + var + SalesHeader: Record "Sales Header"; + begin + CreateSalesHeader(SalesHeader, DocumentType); + SalesHeader.Validate("Company Bank Account Code", BankAccountCode); + SalesHeader.Modify(true); + CreateSalesLine(SalesHeader, LineType, false); + exit(LibrarySales.PostSalesDocument(SalesHeader, true, true)); + end; + local procedure CreatePurchDocument(var PurchaseHeader: Record "Purchase Header"; DocumentType: Enum "Purchase Document Type") var PurchaseLine: Record "Purchase Line"; @@ -1027,7 +1099,20 @@ codeunit 13918 "XRechnung XML Document Tests" Path: Text; begin Path := DocumentTok + '/cbc:PaymentMeansCode'; - Assert.AreEqual('68', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path)); + Assert.AreEqual('58', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path)); + end; + + local procedure VerifyPaymentMeans(var TempXMLBuffer: Record "XML Buffer" temporary; DocumentTok: Text; ExpectedIBAN: Text; ExpectedSWIFT: Text); + var + Path: Text; + begin + VerifyPaymentMeans(TempXMLBuffer, DocumentTok); + Path := DocumentTok + '/cac:PayeeFinancialAccount/cbc:ID'; + Assert.AreEqual(ExpectedIBAN, GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path)); + if ExpectedSWIFT <> '' then begin + Path := DocumentTok + '/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID'; + Assert.AreEqual(ExpectedSWIFT, GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path)); + end; end; local procedure VerifyPaymentTerms(PaymentTermsCode: Code[10]; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentTok: Text); @@ -1465,6 +1550,9 @@ codeunit 13918 "XRechnung XML Document Tests" LibraryTestInitialize.OnBeforeTestSuiteInitialize(Codeunit::"XRechnung XML Document Tests"); IsInitialized := true; CompanyInformation.Get(); + CompanyInformation.IBAN := LibraryUtility.GenerateMOD97CompliantCode(); + CompanyInformation."SWIFT Code" := LibraryUtility.GenerateGUID(); + CompanyInformation.Modify(); GeneralLedgerSetup.Get(); EDocumentService.DeleteAll(); EDocumentService.Get(LibraryEdocument.CreateService("E-Document Format"::XRechnung, "Service Integration"::"No Integration")); From d45a1ae8e3357bf0d60364cb86dd959cc313f3da Mon Sep 17 00:00:00 2001 From: Kilian Seizinger <56249171+pri-kise@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:33:49 +0100 Subject: [PATCH 2/2] Add Additional Check for the IBAN check --- .../src/XRechnung/XRechnungFormat.Codeunit.al | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungFormat.Codeunit.al b/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungFormat.Codeunit.al index 1a21ce99bc..a86c1b19ae 100644 --- a/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungFormat.Codeunit.al +++ b/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungFormat.Codeunit.al @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.eServices.EDocument.Formats; +using Microsoft.Bank.BankAccount; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.IO.Peppol; using Microsoft.Foundation.Company; @@ -30,6 +31,7 @@ codeunit 13914 "XRechnung Format" implements "E-Document" begin OnBeforeCheck(SourceDocumentHeader, EDocumentService, EDocumentProcessingPhase); CheckCompanyInfoMandatory(); + CheckBankAccountIBANMandatory(SourceDocumentHeader); CheckBuyerReferenceMandatory(EDocumentService, SourceDocumentHeader); BindSubscription(EDocPEPPOLValidationDE); EDocPEPPOLBIS30.Check(SourceDocumentHeader, EDocumentService, EDocumentProcessingPhase); @@ -85,10 +87,40 @@ codeunit 13914 "XRechnung Format" implements "E-Document" CompanyInformation.TestField("E-Mail"); end; - local procedure CheckBuyerReferenceMandatory(EDocumentService: Record "E-Document Service"; SourceDocumentHeader: RecordRef) + local procedure CheckBankAccountIBANMandatory(SourceDocumentHeader: RecordRef) var + BankAccount: Record "Bank Account"; + CompanyInformation: Record "Company Information"; SalesInvoiceHeader: Record "Sales Invoice Header"; + BankAccountCodeFieldRef: FieldRef; + CheckBankAccount: Boolean; + BankAccountCode: Code[20]; + begin + if not (SourceDocumentHeader.Number() in + [Database::"Sales Header", + Database::"Sales Invoice Header", + Database::"Sales Cr.Memo Header"]) + then + exit; + + BankAccountCodeFieldRef := SourceDocumentHeader.Field(SalesInvoiceHeader.FieldNo("Company Bank Account Code")); + BankAccountCode := BankAccountCodeFieldRef.Value(); + + if BankAccountCode <> '' then + CheckBankAccount := BankAccount.Get(BankAccountCode); + + if CheckBankAccount then + BankAccount.TestField(IBAN) + else begin + CompanyInformation.Get(); + CompanyInformation.TestField(IBAN); + end; + end; + + local procedure CheckBuyerReferenceMandatory(EDocumentService: Record "E-Document Service"; SourceDocumentHeader: RecordRef) + var Customer: Record Customer; + SalesInvoiceHeader: Record "Sales Invoice Header"; CustomerNoFieldRef: FieldRef; YourReferenceFieldRef: FieldRef; begin