diff --git a/ZUGFeRD.Test/XRechnungUBLTests.cs b/ZUGFeRD.Test/XRechnungUBLTests.cs index 93c30c3d..fbfb2cd8 100644 --- a/ZUGFeRD.Test/XRechnungUBLTests.cs +++ b/ZUGFeRD.Test/XRechnungUBLTests.cs @@ -127,6 +127,36 @@ public void TestParentLineId() } + [TestMethod] + public void TestTaxRepresentativePartyNoNestedPartyElement() + { + InvoiceDescriptor desc = this._InvoiceProvider.CreateInvoice(); + desc.SellerTaxRepresentative = new Party() + { + Name = "Tax Rep GmbH", + Postcode = "12345", + City = "Berlin", + Country = CountryCodes.DE + }; + desc.AddSellerTaxRepresentativeTaxRegistration("DE999999999", TaxRegistrationSchemeID.VA); + + MemoryStream ms = new MemoryStream(); + desc.Save(ms, ZUGFeRDVersion.Version23, Profile.XRechnung, ZUGFeRDFormats.UBL); + + // Verify XML structure: TaxRepresentativeParty must NOT contain nested cac:Party + string content = Encoding.UTF8.GetString(ms.ToArray()); + Assert.IsTrue(content.Contains(""), "TaxRepresentativeParty element should exist"); + Assert.IsFalse(Regex.IsMatch(content, @"\s*"), + "TaxRepresentativeParty must not contain nested cac:Party element (UBL schema)"); + + // Verify roundtrip + ms.Seek(0, SeekOrigin.Begin); + InvoiceDescriptor loadedInvoice = InvoiceDescriptor.Load(ms); + Assert.IsNotNull(loadedInvoice.SellerTaxRepresentative); + Assert.AreEqual("Tax Rep GmbH", loadedInvoice.SellerTaxRepresentative.Name); + } + + [TestMethod] public void TestInvoiceCreation() { diff --git a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs index 9f89983e..2c6d3866 100644 --- a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs +++ b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs @@ -956,7 +956,9 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa // break; } - writer.WriteStartElement("cac", "Party", this._Descriptor.Profile); + // TaxRepresentativeParty is already of type PartyType in UBL, no nested cac:Party needed + if (partyType != PartyTypes.SellerTaxRepresentativeTradeParty) + writer.WriteStartElement("cac", "Party", this._Descriptor.Profile); if (ElectronicAddress != null) { @@ -1096,8 +1098,9 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa writer.WriteEndElement(); // !Contact } - writer.WriteEndElement(); //!Party - _Writer.WriteEndElement(); //Invoice + if (partyType != PartyTypes.SellerTaxRepresentativeTradeParty) + writer.WriteEndElement(); //!Party + _Writer.WriteEndElement(); //!*TradeParty } } // !_writeOptionalParty() diff --git a/ZUGFeRD/InvoiceDescriptor22UblReader.cs b/ZUGFeRD/InvoiceDescriptor22UblReader.cs index 3e00132d..f086056d 100644 --- a/ZUGFeRD/InvoiceDescriptor22UblReader.cs +++ b/ZUGFeRD/InvoiceDescriptor22UblReader.cs @@ -197,7 +197,8 @@ public override InvoiceDescriptor Load(Stream stream) }; } - retval.SellerTaxRepresentative = _nodeAsParty(doc.DocumentElement, "//cac:TaxRepresentativeParty/cac:Party", nsmgr); + // TaxRepresentativeParty is directly of type PartyType in UBL, no nested cac:Party + retval.SellerTaxRepresentative = _nodeAsParty(doc.DocumentElement, "//cac:TaxRepresentativeParty", nsmgr); //Get all referenced and embedded documents (BG-24) // TODO //XmlNodeList referencedDocNodes = doc.SelectNodes(".//ram:ApplicableHeaderTradeAgreement/ram:AdditionalReferencedDocument", nsmgr); @@ -732,9 +733,10 @@ private static List _parseTradeLineItem(XmlNode tradeLineItem, Xm //Add main item to result list resultList.Add(item); - //Find sub invoice lines recursively - //Note that selectnodes also select the sub invoice line from other nodes - XmlNodeList subInvoiceLineNodes = tradeLineItem.SelectNodes(".//cac:SubInvoiceLine", nsmgr); + //Find sub invoice lines recursively - use direct children only (not .//) + //to avoid capturing grandchildren which would cause duplicate entries with wrong ParentLineIDs + string subSelector = isInvoice ? "cac:SubInvoiceLine" : "cac:SubCreditNoteLine"; + XmlNodeList subInvoiceLineNodes = tradeLineItem.SelectNodes(subSelector, nsmgr); foreach (XmlNode subInvoiceLineNode in subInvoiceLineNodes) { List parseResultList = _parseTradeLineItem(subInvoiceLineNode, nsmgr, item.AssociatedDocument.LineID);