From 1bfcd15e79981fc847768b2b6cd5578b510991cc Mon Sep 17 00:00:00 2001 From: Jeffrey Bulanadi Date: Wed, 22 Apr 2026 05:00:00 +0800 Subject: [PATCH] fix: ZUGFeRD invoice export now includes SellerAssignedID (BT-155) in SpecifiedTradeProduct Per EN 16931 / ZUGFeRD 2.x spec, BT-155 (Seller Item Number) must be emitted as ram:SellerAssignedID inside ram:SpecifiedTradeProduct for each supply-chain trade line item. InsertInvoiceLine and InsertCrMemoLine in ExportZUGFeRDDocument (codeunit 13917) only emitted ram:Name. SellerAssignedID is now added before Name when the line No. is non-empty, matching the element order mandated by the CII schema and consistent with the XRechnung UBL implementation in codeunit 13916. Two automated tests are added to ZUGFeRDXMLDocumentTests (codeunit 13922) to verify the fix for both Sales Invoice and Sales Cr. Memo document types. Fixes #29871 --- .../ZUGFeRD/ExportZUGFeRDDocument.Codeunit.al | 4 ++ .../src/ZUGFeRDXMLDocumentTests.Codeunit.al | 47 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/Apps/DE/EDocumentDE/app/src/ZUGFeRD/ExportZUGFeRDDocument.Codeunit.al b/Apps/DE/EDocumentDE/app/src/ZUGFeRD/ExportZUGFeRDDocument.Codeunit.al index cee9d3b97e..aeff00595b 100644 --- a/Apps/DE/EDocumentDE/app/src/ZUGFeRD/ExportZUGFeRDDocument.Codeunit.al +++ b/Apps/DE/EDocumentDE/app/src/ZUGFeRD/ExportZUGFeRDDocument.Codeunit.al @@ -924,6 +924,8 @@ codeunit 13917 "Export ZUGFeRD Document" InvoiceLineElement.Add(AssociatedDocumentLineElement); SpecifiedTradeProductElement := XmlElement.Create('SpecifiedTradeProduct', XmlNamespaceRAM); + if SalesInvoiceLine."No." <> '' then + SpecifiedTradeProductElement.Add(XmlElement.Create('SellerAssignedID', XmlNamespaceRAM, SalesInvoiceLine."No.")); SpecifiedTradeProductElement.Add(XmlElement.Create('Name', XmlNamespaceRAM, SalesInvoiceLine.Description)); InvoiceLineElement.Add(SpecifiedTradeProductElement); @@ -1008,6 +1010,8 @@ codeunit 13917 "Export ZUGFeRD Document" CrMemoLineElement.Add(AssociatedDocumentLineElement); SpecifiedTradeProductElement := XmlElement.Create('SpecifiedTradeProduct', XmlNamespaceRAM); + if SalesCrMemoLine."No." <> '' then + SpecifiedTradeProductElement.Add(XmlElement.Create('SellerAssignedID', XmlNamespaceRAM, SalesCrMemoLine."No.")); SpecifiedTradeProductElement.Add(XmlElement.Create('Name', XmlNamespaceRAM, SalesCrMemoLine.Description)); CrMemoLineElement.Add(SpecifiedTradeProductElement); diff --git a/Apps/DE/EDocumentDE/test/src/ZUGFeRDXMLDocumentTests.Codeunit.al b/Apps/DE/EDocumentDE/test/src/ZUGFeRDXMLDocumentTests.Codeunit.al index 270790ea8b..5fcdddccbe 100644 --- a/Apps/DE/EDocumentDE/test/src/ZUGFeRDXMLDocumentTests.Codeunit.al +++ b/Apps/DE/EDocumentDE/test/src/ZUGFeRDXMLDocumentTests.Codeunit.al @@ -465,6 +465,29 @@ codeunit 13922 "ZUGFeRD XML Document Tests" VerifyInvoiceLine(SalesInvoiceHeader, TempXMLBuffer); end; + [Test] + procedure ExportPostedSalesInvoiceInZUGFeRDFormatVerifySellerAssignedID(); + var + SalesInvoiceHeader: Record "Sales Invoice Header"; + SalesInvoiceLine: Record "Sales Invoice Line"; + TempXMLBuffer: Record "XML Buffer" temporary; + SellerAssignedIDPathTok: Label '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:SellerAssignedID', Locked = true; + begin + // [SCENARIO] Export posted sales invoice creates electronic document in ZUGFeRD format with seller item number (BT-155) in invoice line + Initialize(); + + // [GIVEN] Create and Post Sales Invoice with an item line. + SalesInvoiceHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::Invoice, Enum::"Sales Line Type"::Item, false)); + + // [WHEN] Export ZUGFeRD Electronic Document. + ExportInvoice(SalesInvoiceHeader, TempXMLBuffer); + + // [THEN] ZUGFeRD Electronic Document contains the seller item number (BT-155) in ram:SpecifiedTradeProduct/ram:SellerAssignedID. + SalesInvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No."); + SalesInvoiceLine.FindFirst(); + Assert.AreEqual(SalesInvoiceLine."No.", GetNodeByPathWithError(TempXMLBuffer, SellerAssignedIDPathTok), StrSubstNo(IncorrectValueErr, SellerAssignedIDPathTok)); + end; + [Test] procedure ExportPostedSalesInvoiceInZUGFeRDFormatVerifyInvoiceLineWithLineDiscount(); var @@ -868,6 +891,30 @@ codeunit 13922 "ZUGFeRD XML Document Tests" // [THEN] ZUGFeRD Electronic Document is created with 2 cr.memo lines VerifyCrMemoLine(SalesCrMemoHeader, TempXMLBuffer); end; + + [Test] + procedure ExportPostedSalesCrMemoInZUGFeRDFormatVerifySellerAssignedID(); + var + SalesCrMemoHeader: Record "Sales Cr.Memo Header"; + SalesCrMemoLine: Record "Sales Cr.Memo Line"; + TempXMLBuffer: Record "XML Buffer" temporary; + SellerAssignedIDPathTok: Label '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:SellerAssignedID', Locked = true; + begin + // [SCENARIO] Export posted sales cr. memo creates electronic document in ZUGFeRD format with seller item number (BT-155) in cr. memo line + Initialize(); + + // [GIVEN] Create and Post sales cr. memo with an item line. + SalesCrMemoHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::"Credit Memo", Enum::"Sales Line Type"::Item, false)); + + // [WHEN] Export ZUGFeRD Electronic Document. + ExportCreditMemo(SalesCrMemoHeader, TempXMLBuffer); + + // [THEN] ZUGFeRD Electronic Document contains the seller item number (BT-155) in ram:SpecifiedTradeProduct/ram:SellerAssignedID. + SalesCrMemoLine.SetRange("Document No.", SalesCrMemoHeader."No."); + SalesCrMemoLine.FindFirst(); + Assert.AreEqual(SalesCrMemoLine."No.", GetNodeByPathWithError(TempXMLBuffer, SellerAssignedIDPathTok), StrSubstNo(IncorrectValueErr, SellerAssignedIDPathTok)); + end; + #endregion #region ServiceInvoice