From d82c2d2e883b934bbc3ec13df14f58bc715bded8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 19 Jan 2025 11:40:02 +0100 Subject: [PATCH] added missing bits of UBL creditnote support --- ZUGFeRD.Test/XRechnungUBLTests.cs | 1 + ZUGFeRD/InvoiceDescriptor22UBLWriter.cs | 67 ++++++++++++++++++------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/ZUGFeRD.Test/XRechnungUBLTests.cs b/ZUGFeRD.Test/XRechnungUBLTests.cs index bae69b5d..5a45b4b1 100644 --- a/ZUGFeRD.Test/XRechnungUBLTests.cs +++ b/ZUGFeRD.Test/XRechnungUBLTests.cs @@ -1043,6 +1043,7 @@ public void TestSampleCreditNote326() Assert.AreEqual(desc.TradeLineItems.Last().Name, "Beratung"); Assert.AreEqual(desc.TradeLineItems.Last().Description, String.Empty); } // !TestSampleCreditNote326() + [TestMethod] public void TestSampleCreditNote384() diff --git a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs index 7fd8c0c0..e9fe1f13 100644 --- a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs +++ b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs @@ -39,6 +39,21 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo throw new IllegalStreamException("Cannot write to stream"); } + bool isInvoice = true; + if (this.Descriptor.Type == InvoiceType.Invoice || this.Descriptor.Type == InvoiceType.Correction) + { + // this is a duplicate, just to make sure: also a Correction is regarded as an Invoice + isInvoice = true; + } + else if (this.Descriptor.Type == InvoiceType.CreditNote) + { + isInvoice = false; + } + else + { + throw new NotImplementedException("Not implemented yet."); + } + long streamPosition = stream.Position; this.Descriptor = descriptor; @@ -50,12 +65,13 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo { "ext", "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" }, { "xs", "http://www.w3.org/2001/XMLSchema" } }; - if (this.Descriptor.Type == InvoiceType.Invoice || this.Descriptor.Type == InvoiceType.Correction) - { + + if (isInvoice) + { namespaces.Add("ubl", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"); } - else if (this.Descriptor.Type == InvoiceType.CreditNote) - { + else + { namespaces.Add("ubl", "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"); } this.Writer.SetNamespaces(namespaces); @@ -63,20 +79,14 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo Writer.WriteStartDocument(); - - if (this.Descriptor.Type != InvoiceType.Invoice && this.Descriptor.Type != InvoiceType.CreditNote && this.Descriptor.Type != InvoiceType.Correction) - { - throw new NotImplementedException("Not implemented yet."); - } - #region Kopfbereich // UBL has different namespace for different types - if (this.Descriptor.Type == InvoiceType.Invoice || this.Descriptor.Type == InvoiceType.Correction) + if (isInvoice) { Writer.WriteStartElement("ubl", "Invoice"); Writer.WriteAttributeString("xmlns", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"); } - else if (this.Descriptor.Type == InvoiceType.CreditNote) + else { Writer.WriteStartElement("ubl", "CreditNote"); Writer.WriteAttributeString("xmlns", "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"); @@ -102,7 +112,14 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo Writer.WriteElementString("cbc", "DueDate", _formatDate(dueDate.Value, false, true)); } - Writer.WriteElementString("cbc", "InvoiceTypeCode", String.Format("{0}", _encodeInvoiceType(this.Descriptor.Type))); //Code für den Rechnungstyp + if (isInvoice) + { + Writer.WriteElementString("cbc", "InvoiceTypeCode", String.Format("{0}", _encodeInvoiceType(this.Descriptor.Type))); //Code für den Rechnungstyp + } + else + { + Writer.WriteElementString("cbc", "CreditNoteTypeCode", String.Format("{0}", _encodeInvoiceType(this.Descriptor.Type))); //Code für den Rechnungstyp + } _writeNotes(Writer, this.Descriptor.Notes); @@ -522,7 +539,7 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo //Skip items with parent line id because these are written recursively in the _WriteTradeLineItem method if (String.IsNullOrEmpty(tradeLineItem.AssociatedDocument.ParentLineID)) { - _WriteTradeLineItem(tradeLineItem); + _WriteTradeLineItem(tradeLineItem, isInvoice); } } @@ -534,15 +551,29 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo } - private void _WriteTradeLineItem(TradeLineItem tradeLineItem) + private void _WriteTradeLineItem(TradeLineItem tradeLineItem, bool isInvoice = true) { if (String.IsNullOrWhiteSpace(tradeLineItem.AssociatedDocument.ParentLineID)) { - Writer.WriteStartElement("cac", "InvoiceLine"); + if (isInvoice) + { + Writer.WriteStartElement("cac", "InvoiceLine"); + } + else + { + Writer.WriteStartElement("cac", "CreditNoteLine"); + } } else { - Writer.WriteStartElement("cac", "SubInvoiceLine"); + if (isInvoice) + { + Writer.WriteStartElement("cac", "SubInvoiceLine"); + } + else + { + Writer.WriteStartElement("cac", "SubCreditNoteLine"); + } } Writer.WriteElementString("cbc", "ID", tradeLineItem.AssociatedDocument.LineID); @@ -667,7 +698,7 @@ private void _WriteTradeLineItem(TradeLineItem tradeLineItem) //Write sub invoice lines recursively foreach (TradeLineItem subTradeLineItem in this.Descriptor.TradeLineItems.Where(t => t.AssociatedDocument.ParentLineID == tradeLineItem.AssociatedDocument.LineID)) { - _WriteTradeLineItem(subTradeLineItem); + _WriteTradeLineItem(subTradeLineItem, isInvoice); } Writer.WriteEndElement(); //!InvoiceLine