From f3352d3a54e27bb8dadf6686540bbf49e488ae61 Mon Sep 17 00:00:00 2001 From: Kilian Seizinger <56249171+pri-kise@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:37:13 +0100 Subject: [PATCH] E-Document for Germany - XRechnung Export - Skip not supported document attachment --- .../ExportXRechnungDocument.Codeunit.al | 40 +++++-- .../DE/EDocumentDE/test/.resources/CRONUS.jpg | Bin 0 -> 15225 bytes .../test/.resources/d365businesscentral.bmp | Bin 0 -> 9354 bytes Apps/DE/EDocumentDE/test/app.json | 3 + .../src/XRechnungXMLDocumentTests.Codeunit.al | 102 ++++++++++++++++-- 5 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 Apps/DE/EDocumentDE/test/.resources/CRONUS.jpg create mode 100644 Apps/DE/EDocumentDE/test/.resources/d365businesscentral.bmp diff --git a/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al b/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al index 2b10c9c477..8ccad4abd9 100644 --- a/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al +++ b/Apps/DE/EDocumentDE/app/src/XRechnung/ExportXRechnungDocument.Codeunit.al @@ -1162,28 +1162,35 @@ codeunit 13916 "Export XRechnung Document" AttachmentElement: XmlElement; OutStream: OutStream; InStream: InStream; + MimeCode: Text; + FileName: Text; begin + MimeCode := GetMimeCode(DocumentAttachment); + if not IsValidMimeCode(MimeCode) then + exit; + TempBlob.CreateOutStream(OutStream); DocumentAttachment.ExportToStream(OutStream); TempBlob.CreateInStream(InStream); + FileName := DocumentAttachment."File Name" + '.' + DocumentAttachment."File Extension"; AttachmentElement := XmlElement.Create('AdditionalDocumentReference', XmlNamespaceCAC); - AttachmentElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, DocumentAttachment."File Name" + '.' + DocumentAttachment."File Extension")); + AttachmentElement.Add(XmlElement.Create('ID', XmlNamespaceCBC, FileName)); AttachmentElement.Add(XmlElement.Create('DocumentDescription', XmlNamespaceCBC, DocumentAttachment."File Name")); - AddAttachmentObject(AttachmentElement, InStream, DocumentAttachment); + AddAttachmentObject(AttachmentElement, InStream, MimeCode, FileName); RootElement.Add(AttachmentElement); end; - local procedure AddAttachmentObject(var AttachmentElement: XmlElement; var InStream: InStream; var DocumentAttachment: Record "Document Attachment"); + local procedure AddAttachmentObject(var AttachmentElement: XmlElement; var InStream: InStream; MimeCode: Text; FileName: Text); var Base64Convert: Codeunit "Base64 Convert"; AttachmentObjectElement: XmlElement; begin AttachmentObjectElement := XmlElement.Create('Attachment', XmlNamespaceCAC); AttachmentObjectElement.Add(XmlElement.Create('EmbeddedDocumentBinaryObject', XmlNamespaceCBC, - XmlAttribute.Create('mimeCode', GetMimeCode(DocumentAttachment)), - XmlAttribute.Create('filename', DocumentAttachment."File Name" + '.' + DocumentAttachment."File Extension"), + XmlAttribute.Create('mimeCode', MimeCode), + XmlAttribute.Create('filename', FileName), Base64Convert.ToBase64(InStream))); AttachmentElement.Add(AttachmentObjectElement); end; @@ -1192,9 +1199,14 @@ codeunit 13916 "Export XRechnung Document" begin case DocumentAttachment."File Type" of "Document Attachment File Type"::Image: - exit('image/' + LowerCase(DocumentAttachment."File Extension")); + case LowerCase(DocumentAttachment."File Extension") of + 'png': + exit('image/png'); + 'jpeg', 'jpg': + exit('image/jpeg'); + end; "Document Attachment File Type"::PDF: - exit('application/' + LowerCase(DocumentAttachment."File Extension")); + exit('application/pdf'); "Document Attachment File Type"::Excel: exit('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); "Document Attachment File Type"::Other: @@ -1212,6 +1224,20 @@ codeunit 13916 "Export XRechnung Document" end; end; + local procedure IsValidMimeCode(MimeCode: Text): Boolean + begin + case MimeCode of + 'application/pdf', + 'image/png', + 'image/jpeg', + 'text/csv', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.oasis.opendocument.spreadsheet': + exit(true); + end; + exit(false); + end; + local procedure CalculateLineAmounts(SalesInvoiceHeader: Record "Sales Invoice Header"; var SalesInvLine: Record "Sales Invoice Line"; Currency: Record Currency; var LineAmounts: Dictionary of [Text, Decimal]) var TotalInvDiscountAmount: Decimal; diff --git a/Apps/DE/EDocumentDE/test/.resources/CRONUS.jpg b/Apps/DE/EDocumentDE/test/.resources/CRONUS.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e562b8d54e54f410935b89d32ccdfb7110b27b18 GIT binary patch literal 15225 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 literal 0 HcmV?d00001 diff --git a/Apps/DE/EDocumentDE/test/.resources/d365businesscentral.bmp b/Apps/DE/EDocumentDE/test/.resources/d365businesscentral.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2f6e69fd2923fd0f9789eb657feeb5cd7777c22d GIT binary patch literal 9354 zcmeH}e@qi+9L6yUw18OX6$(QjgDL3T1a#BsHgtb%)9BEVg&7u65uMY_xoo^kh zbtPC9ocY(Jn3x!8N=nKM+T2P?O2lt_3`~wl%#R*<*>;-pXh*mf91t8391t8391t83 z91t9MCJq#(OpPnpC}t0?7h7}IiiWcjMeLpgQS37r+{uF|wtA%>`P>7CQaq5iUQDQd zWF?BQ_fruv;@#4n{Q0LEzI3}+?6Ga0q5`+4Wy$Nn)PrMt7l09Bn zhVt#6EY*+VEgpo|pT9vw4kU>QuRm)IWmxS>^6B|vHuFL`TAJt_INhu1J=$65(S^pzebFGvta0c1>H44HvOH)<ZLs#-T}E_&E+K|H@51!^B4WO| zfS7N@5VK`2OorLecZ8#fW}rDOC)zUsXf5+YizN!}=14n(BL}rVF;fOUhY<#=6C{rs zQe#aY)(&li_U>}<40@V{iDqH`Z6Pc-=7VRr))N8tXMgma4QLFhag0L`x-qIEEu zGDQ5xV9P-r0LutN*9n>P_1{0nYOTA8b|{(j+*^*`KbC-JFy2~385Y1Y_y%-ck!Y(7 zguX5W#`+L&s_D@6$Ai1Qf-=OQ-4a1KLpbTvh7!ko;B%n04$Z0=Xgc>YI-4V*)6w?_8fQyK8Mpg{ZK*o`={vs`P`1FsQ%s?mv&Dhjk!K( zE0&_Cl0kp1^K1NZTF1KQs3pyCI?9tgaXeX!)7z&)`Q0>J$nZsdjxW@QrKGEZfnMpy zuQg6zW5&`t)_n&-0Fp@Z@i&Om5}P)Qo4U*&{W8fwo(S%DTYkM8abZM@t#$eElaEX(hFz4^(M+x z34Y1&K~1IGXDcg|m{JrIq>J{D-91t8391t8391t839QZ#u F@Go?jtt0>d literal 0 HcmV?d00001 diff --git a/Apps/DE/EDocumentDE/test/app.json b/Apps/DE/EDocumentDE/test/app.json index 8c60e454b8..14374e709e 100644 --- a/Apps/DE/EDocumentDE/test/app.json +++ b/Apps/DE/EDocumentDE/test/app.json @@ -70,5 +70,8 @@ "allowDownloadingSource": true, "includeSourceInSymbolFile": true }, + "resourceFolders": [ + ".resources" + ], "application": "29.0.0.0" } diff --git a/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al b/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al index 134b4b2517..3b41cbbe89 100644 --- a/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al +++ b/Apps/DE/EDocumentDE/test/src/XRechnungXMLDocumentTests.Codeunit.al @@ -1394,6 +1394,67 @@ codeunit 13918 "XRechnung XML Document Tests" end; #endregion + #region DocumentAttachmentFiltering + [Test] + procedure ExportPostedSalesInvoiceInXRechnungFormatVerifyUnsupportedAttachmentIsSkipped(); + var + SalesInvoiceHeader: Record "Sales Invoice Header"; + TempXMLBuffer: Record "XML Buffer" temporary; + RecRef: RecordRef; + CSVText: Text; + begin + // [SCENARIO] Attachments with unsupported MIME types are not exported in XRechnung format + Initialize(); + + // [GIVEN] Create and Post Sales Invoice + SalesInvoiceHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::Invoice, "Sales Line Type"::Item, false)); + RecRef.GetTable(SalesInvoiceHeader); + + // [GIVEN] Create one supported CSV attachment and one unsupported TXT attachment + CSVText := CreateCSVDocumentAttachment(RecRef, 'data.csv'); + CreateDocumentAttachment(RecRef, 'report.txt', 'Some text content'); + + // [WHEN] Export XRechnung Electronic Document + ExportInvoice(SalesInvoiceHeader, TempXMLBuffer); + + // [THEN] Only the CSV attachment (supported) is present; TXT is skipped + VerifyAdditionalDocumentReferenceCount(TempXMLBuffer, 1); + VerifyCSVAttachmentInXML(TempXMLBuffer, 'data.csv', 'text/csv', CSVText); + end; + + [Test] + procedure ExportPostedSalesInvoiceInXRechnungFormatVerifyUnsupportedImageExtensionIsSkipped(); + var + DocumentAttachment: Record "Document Attachment"; + SalesInvoiceHeader: Record "Sales Invoice Header"; + TempXMLBuffer: Record "XML Buffer" temporary; + Base64Convert: Codeunit "Base64 Convert"; + TempBlob: Codeunit "Temp Blob"; + RecRef: RecordRef; + begin + // [SCENARIO] Image attachments with unsupported extensions (e.g. bmp) are skipped; supported ones (jpg) are exported + Initialize(); + + // [GIVEN] Create and Post Sales Invoice + SalesInvoiceHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::Invoice, "Sales Line Type"::Item, false)); + RecRef.GetTable(SalesInvoiceHeader); + + // [GIVEN] Create one unsupported BMP image and one supported JGP image attachment + LoadFileFromResourceFolders('d365businesscentral.bmp', TempBlob); + DocumentAttachment.SaveAttachment(RecRef, 'd365businesscentral.bmp', TempBlob); + LoadFileFromResourceFolders('CRONUS.jpg', TempBlob); + Clear(DocumentAttachment); + DocumentAttachment.SaveAttachment(RecRef, 'CRONUS.jpg', TempBlob); + + // [WHEN] Export XRechnung Electronic Document + ExportInvoice(SalesInvoiceHeader, TempXMLBuffer); + + // [THEN] Only the JPG attachment (supported) is present; BMP is skipped + VerifyAdditionalDocumentReferenceCount(TempXMLBuffer, 1); + VerifyAttachmentInXML(TempXMLBuffer, 'CRONUS.jpg', 'image/jpeg', ''); + end; + #endregion + local procedure CreateAndPostSalesDocument(DocumentType: Enum "Sales Document Type"; LineType: Enum "Sales Line Type"; InvoiceDiscount: Boolean): Code[20]; var SalesHeader: Record "Sales Header"; @@ -2421,10 +2482,10 @@ codeunit 13918 "XRechnung XML Document Tests" VerifyAdditionalDocumentReferenceCount(TempXMLBuffer, 2); // [THEN] First attachment is verified in XML with correct ID, MIME type, and content - VerifyAttachmentInXML(TempXMLBuffer, FileName1, 'text/csv', CSVText1); + VerifyCSVAttachmentInXML(TempXMLBuffer, FileName1, 'text/csv', CSVText1); // [THEN] Second attachment is verified in XML with correct ID, MIME type, and content - VerifyAttachmentInXML(TempXMLBuffer, FileName2, 'text/csv', CSVText2); + VerifyCSVAttachmentInXML(TempXMLBuffer, FileName2, 'text/csv', CSVText2); end; @@ -2436,9 +2497,17 @@ codeunit 13918 "XRechnung XML Document Tests" Assert.AreEqual(ExpectedCount, TempXMLBuffer.Count, 'Incorrect number of AdditionalDocumentReference nodes'); end; - local procedure VerifyAttachmentInXML(var TempXMLBuffer: Record "XML Buffer" temporary; AttachmentID: Text; ExpectedMIMEType: Text; ExpectedCSVText: Text) + + local procedure VerifyCSVAttachmentInXML(var TempXMLBuffer: Record "XML Buffer" temporary; AttachmentID: Text; ExpectedMIMEType: Text; ExpectedCSVText: Text) var Base64Convert: Codeunit "Base64 Convert"; + Base64EncodedContent: Text; + begin + Base64EncodedContent := Base64Convert.ToBase64(ExpectedCSVText); + end; + + local procedure VerifyAttachmentInXML(var TempXMLBuffer: Record "XML Buffer" temporary; AttachmentID: Text; ExpectedMIMEType: Text; ExpectedBase64Content: Text) + var TempXMLBufferAttachment: Record "XML Buffer" temporary; TempXMLBufferChild: Record "XML Buffer" temporary; DecodedText: Text; @@ -2488,9 +2557,8 @@ codeunit 13918 "XRechnung XML Document Tests" if TempXMLBufferChild.FindFirst() then Assert.AreEqual(ExpectedMIMEType, TempXMLBufferChild.Value, 'Incorrect MIME type'); - // Verify decoded content - DecodedText := Base64Convert.FromBase64(EncodedContent); - Assert.AreEqual(ExpectedCSVText, DecodedText, 'Decoded attachment content does not match original CSV text'); + if ExpectedBase64Content <> '' then + Assert.AreEqual(ExpectedBase64Content, EncodedContent, 'Attachment content does not match original value'); end else Error('EmbeddedDocumentBinaryObject not found for attachment %1', AttachmentID); end else @@ -2767,6 +2835,28 @@ codeunit 13918 "XRechnung XML Document Tests" exit(CSVText); end; + local procedure CreateDocumentAttachment(RecRef: RecordRef; FileName: Text; ContentText: Text) + var + DocumentAttachment: Record "Document Attachment"; + TempBlob: Codeunit "Temp Blob"; + OutStream: OutStream; + begin + TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8); + OutStream.WriteText(ContentText); + DocumentAttachment.SaveAttachment(RecRef, FileName, TempBlob); + end; + + local procedure LoadFileFromResourceFolders(FilePath: Text; var TempBlob: Codeunit "Temp Blob") + var + ImageOutStream: OutStream; + FileInStream: InStream; + begin + Clear(TempBlob); + NavApp.GetResource(FilePath, FileInStream); + ImageOutStream := TempBlob.CreateOutStream(); + CopyStream(ImageOutStream, FileInStream); + end; + local procedure GetCurrencyCode(DocumentCurrencyCode: Code[10]; var Currency: Record Currency): Code[10] begin if DocumentCurrencyCode = '' then begin