Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions ZUGFeRD.Test/XRechnungUBLTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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("<cac:TaxRepresentativeParty>"), "TaxRepresentativeParty element should exist");
Assert.IsFalse(Regex.IsMatch(content, @"<cac:TaxRepresentativeParty>\s*<cac:Party>"),
"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()
{
Expand Down
9 changes: 6 additions & 3 deletions ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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()

Expand Down
10 changes: 6 additions & 4 deletions ZUGFeRD/InvoiceDescriptor22UblReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -732,9 +733,10 @@ private static List<TradeLineItem> _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<TradeLineItem> parseResultList = _parseTradeLineItem(subInvoiceLineNode, nsmgr, item.AssociatedDocument.LineID);
Expand Down
Loading