diff --git a/ZUGFeRD.Test/XRechnungUBLTests.cs b/ZUGFeRD.Test/XRechnungUBLTests.cs
index fbfb2cd8..f9b16b33 100644
--- a/ZUGFeRD.Test/XRechnungUBLTests.cs
+++ b/ZUGFeRD.Test/XRechnungUBLTests.cs
@@ -1143,7 +1143,7 @@ public void TestReferenceXRechnung21UBL()
Assert.AreEqual(desc.OrderNo, "0815-99-1");
Assert.AreEqual(desc.Currency, CurrencyCodes.EUR);
- Assert.AreEqual(desc.Buyer.Name, "Rechnungs Roulette GmbH & Co KG");
+ Assert.AreEqual(desc.Buyer.Name, "");
Assert.AreEqual(desc.Buyer.City, "Klein Schlappstadt a.d. Lusche");
Assert.AreEqual(desc.Buyer.Postcode, "12345");
Assert.AreEqual(desc.Buyer.Country, CountryCodes.DE);
@@ -1154,7 +1154,7 @@ public void TestReferenceXRechnung21UBL()
Assert.AreEqual(desc.BuyerContact.EmailAddress, "manfred.mustermann@rr.de");
Assert.AreEqual(desc.BuyerContact.PhoneNo, "012345 98 765 - 44");
- Assert.AreEqual(desc.Seller.Name, "Harry Hirsch Holz- und Trockenbau");
+ Assert.AreEqual(desc.Seller.Name, "");
Assert.AreEqual(desc.Seller.City, "Klein Schlappstadt a.d. Lusche");
Assert.AreEqual(desc.Seller.Postcode, "12345");
Assert.AreEqual(desc.Seller.Country, CountryCodes.DE);
@@ -1355,6 +1355,21 @@ public void TestNonStandardDateTimeFormat()
} // !TestNonStandardDateTimeFormat()
+ ///
+ /// Test that InvoicePeriod is not read TradeLineItem -> InvoicePeriod
+ ///
+ [TestMethod]
+ public void TestDontMixInvoicePeriodWithTradeLineItem()
+ {
+ string path = @"..\..\..\..\demodata\xRechnung\01.01a-INVOICE_ubl.xml";
+ path = _makeSurePathIsCrossPlatformCompatible(path);
+
+ InvoiceDescriptor desc = InvoiceDescriptor.Load(path);
+
+ Assert.IsNull(desc.BillingPeriodStart);
+ Assert.IsNull(desc.BillingPeriodEnd);
+ } // !TestDontMixInvoicePeriodWithTradeLineItem()
+
[TestMethod]
public void TestSellerPartyDescription()
{
diff --git a/ZUGFeRD.Test/ZUGFeRD22Tests.cs b/ZUGFeRD.Test/ZUGFeRD22Tests.cs
index e3842964..dac8819c 100644
--- a/ZUGFeRD.Test/ZUGFeRD22Tests.cs
+++ b/ZUGFeRD.Test/ZUGFeRD22Tests.cs
@@ -162,6 +162,142 @@ public void TestExtendedInvoiceWithIncludedItems()
} // !TestExtendedInvoiceWithIncludedItems()
+ ///
+ /// Roundtrip test for TradeLineItem product identification fields added in PR #863:
+ /// IndustryAssignedID, ModelID, BatchID, BrandName, ModelName
+ /// These fields are Extended profile only (CII 2.3).
+ ///
+ [TestMethod]
+ public void TestTradeLineItemProductFieldsRoundtrip()
+ {
+ string path = @"..\..\..\..\demodata\zugferd21\zugferd_2p1_EXTENDED_Warenrechnung-factur-x.xml";
+ path = _makeSurePathIsCrossPlatformCompatible(path);
+
+ Stream s = File.Open(path, FileMode.Open);
+ InvoiceDescriptor desc = InvoiceDescriptor.Load(s);
+ s.Close();
+
+ desc.TradeLineItems.Clear();
+
+ var lineItem = desc.AddTradeLineItem(
+ lineID: "1",
+ name: "Test Product",
+ billedQuantity: 10m,
+ unitCode: QuantityCodes.H87,
+ netUnitPrice: 5.0m,
+ grossUnitPrice: 5.0m,
+ categoryCode: TaxCategoryCodes.S,
+ taxPercent: 19.0m,
+ taxType: TaxTypes.VAT);
+
+ lineItem.SellerAssignedID = "SELLER-001";
+ lineItem.BuyerAssignedID = "BUYER-001";
+ lineItem.IndustryAssignedID = "IND-12345";
+ lineItem.ModelID = "MDL-67890";
+ lineItem.BatchID = "BATCH-2025-001";
+ lineItem.BrandName = "TestBrand";
+ lineItem.ModelName = "TestModel Pro";
+
+ MemoryStream ms = new MemoryStream();
+ desc.Save(ms, ZUGFeRDVersion.Version23, Profile.Extended);
+ ms.Seek(0, SeekOrigin.Begin);
+
+ InvoiceDescriptor loadedInvoice = InvoiceDescriptor.Load(ms);
+
+ Assert.AreEqual(1, loadedInvoice.TradeLineItems.Count);
+ var loadedLineItem = loadedInvoice.TradeLineItems[0];
+ Assert.AreEqual("Test Product", loadedLineItem.Name);
+ Assert.AreEqual("SELLER-001", loadedLineItem.SellerAssignedID);
+ Assert.AreEqual("BUYER-001", loadedLineItem.BuyerAssignedID);
+ Assert.AreEqual("IND-12345", loadedLineItem.IndustryAssignedID);
+ Assert.AreEqual("MDL-67890", loadedLineItem.ModelID);
+ Assert.AreEqual("BATCH-2025-001", loadedLineItem.BatchID);
+ Assert.AreEqual("TestBrand", loadedLineItem.BrandName);
+ Assert.AreEqual("TestModel Pro", loadedLineItem.ModelName);
+ } // !TestTradeLineItemProductFieldsRoundtrip()
+
+
+ ///
+ /// Roundtrip test for IncludedReferencedProduct fields added in PR #863:
+ /// GlobalID, SellerAssignedID, BuyerAssignedID, IndustryAssignedID, Description
+ ///
+ [TestMethod]
+ public void TestIncludedReferencedProductFieldsRoundtrip()
+ {
+ string path = @"..\..\..\..\demodata\zugferd21\zugferd_2p1_EXTENDED_Warenrechnung-factur-x.xml";
+ path = _makeSurePathIsCrossPlatformCompatible(path);
+
+ Stream s = File.Open(path, FileMode.Open);
+ InvoiceDescriptor desc = InvoiceDescriptor.Load(s);
+ s.Close();
+
+ desc.TradeLineItems.Clear();
+
+ var lineItem = desc.AddTradeLineItem(
+ lineID: "1",
+ name: "Main Product",
+ billedQuantity: 5m,
+ unitCode: QuantityCodes.H87,
+ netUnitPrice: 20.0m,
+ grossUnitPrice: 20.0m,
+ categoryCode: TaxCategoryCodes.S,
+ taxPercent: 19.0m,
+ taxType: TaxTypes.VAT);
+
+ // Add included product with all new fields set
+ lineItem.IncludedReferencedProducts.Add(new IncludedReferencedProduct()
+ {
+ GlobalID = new GlobalID(GlobalIDSchemeIdentifiers.EAN, "4012345999999"),
+ SellerAssignedID = "SEL-REF-001",
+ BuyerAssignedID = "BUY-REF-001",
+ IndustryAssignedID = "IND-REF-001",
+ Name = "Included Part A",
+ Description = "Description of included part A",
+ UnitQuantity = 2,
+ UnitCode = QuantityCodes.C62
+ });
+
+ // Add a second included product with minimal fields
+ lineItem.IncludedReferencedProducts.Add(new IncludedReferencedProduct()
+ {
+ Name = "Included Part B",
+ Description = "Description of included part B"
+ });
+
+ MemoryStream ms = new MemoryStream();
+ desc.Save(ms, ZUGFeRDVersion.Version23, Profile.Extended);
+ ms.Seek(0, SeekOrigin.Begin);
+
+ InvoiceDescriptor loadedInvoice = InvoiceDescriptor.Load(ms);
+
+ Assert.AreEqual(1, loadedInvoice.TradeLineItems.Count);
+ var loadedLineItem = loadedInvoice.TradeLineItems[0];
+ Assert.AreEqual(2, loadedLineItem.IncludedReferencedProducts.Count);
+
+ // Verify first included product (all fields)
+ var product1 = loadedLineItem.IncludedReferencedProducts[0];
+ Assert.AreEqual(GlobalIDSchemeIdentifiers.EAN, product1.GlobalID.SchemeID);
+ Assert.AreEqual("4012345999999", product1.GlobalID.ID);
+ Assert.AreEqual("SEL-REF-001", product1.SellerAssignedID);
+ Assert.AreEqual("BUY-REF-001", product1.BuyerAssignedID);
+ Assert.AreEqual("IND-REF-001", product1.IndustryAssignedID);
+ Assert.AreEqual("Included Part A", product1.Name);
+ Assert.AreEqual("Description of included part A", product1.Description);
+ Assert.AreEqual(2m, product1.UnitQuantity);
+ Assert.AreEqual(QuantityCodes.C62, product1.UnitCode);
+
+ // Verify second included product (minimal fields)
+ var product2 = loadedLineItem.IncludedReferencedProducts[1];
+ Assert.AreEqual("Included Part B", product2.Name);
+ Assert.AreEqual("Description of included part B", product2.Description);
+ Assert.AreEqual("", product2.SellerAssignedID);
+ Assert.AreEqual("", product2.BuyerAssignedID);
+ Assert.AreEqual("", product2.IndustryAssignedID);
+ Assert.AreEqual(false, product2.UnitQuantity.HasValue);
+ Assert.IsNull(product2.UnitCode);
+ } // !TestIncludedReferencedProductFieldsRoundtrip()
+
+
[TestMethod]
public void TestReferenceEReportingFacturXInvoice()
{
diff --git a/ZUGFeRD/InvoiceDescriptor1Reader.cs b/ZUGFeRD/InvoiceDescriptor1Reader.cs
index 277b99dd..77b5a8c6 100644
--- a/ZUGFeRD/InvoiceDescriptor1Reader.cs
+++ b/ZUGFeRD/InvoiceDescriptor1Reader.cs
@@ -169,8 +169,8 @@ public override InvoiceDescriptor Load(Stream stream)
};
retval.PaymentMeans = paymentMeans;
- retval.BillingPeriodStart = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod/ram:StartDateTime", nsmgr);
- retval.BillingPeriodEnd = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod/ram:EndDateTime", nsmgr);
+ retval.BillingPeriodStart = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//ram:ApplicableSupplyChainTradeSettlement/ram:BillingSpecifiedPeriod/ram:StartDateTime", nsmgr);
+ retval.BillingPeriodEnd = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//ram:ApplicableSupplyChainTradeSettlement/ram:BillingSpecifiedPeriod/ram:EndDateTime", nsmgr);
XmlNodeList creditorFinancialAccountNodes = doc.SelectNodes("//ram:ApplicableSupplyChainTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount", nsmgr);
XmlNodeList creditorFinancialInstitutions = doc.SelectNodes("//ram:ApplicableSupplyChainTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeeSpecifiedCreditorFinancialInstitution", nsmgr);
@@ -261,7 +261,7 @@ public override InvoiceDescriptor Load(Stream stream)
decimal? penaltyPercent = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:CalculationPercent", nsmgr, null);
int? penaltyDueDays = null; // XmlUtils.NodeAsInt(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisPeriodMeasure", nsmgr);
decimal? penaltyBaseAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisAmount", nsmgr, null);
- decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentDiscountTerms/ram:ActualPenaltyAmount", nsmgr, null);
+ decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:ActualPenaltyAmount", nsmgr, null);
PaymentTermsType? paymentTermsType = discountPercent.HasValue ? PaymentTermsType.Skonto :
penaltyPercent.HasValue ? PaymentTermsType.Verzug :
(PaymentTermsType?)null;
@@ -478,12 +478,14 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
if (!String.IsNullOrWhiteSpace(lineTwo))
{
+ retval.Street2 = lineOne;
retval.ContactName = lineOne;
- retval.Street = lineOne;
+ retval.Street = lineTwo;
}
else
{
retval.Street = lineOne;
+ retval.Street2 = null;
retval.ContactName = null;
}
diff --git a/ZUGFeRD/InvoiceDescriptor1Writer.cs b/ZUGFeRD/InvoiceDescriptor1Writer.cs
index d17c2300..b8560e95 100644
--- a/ZUGFeRD/InvoiceDescriptor1Writer.cs
+++ b/ZUGFeRD/InvoiceDescriptor1Writer.cs
@@ -952,8 +952,10 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, string prefix
_writeOptionalContact(writer, "ram", "DefinedTradeContact", Contact);
writer.WriteStartElement("ram", "PostalTradeAddress");
writer.WriteOptionalElementString("ram", "PostcodeCode", Party.Postcode);
- writer.WriteOptionalElementString("ram", "LineOne", string.IsNullOrWhiteSpace(Party.ContactName) ? Party.Street : Party.ContactName);
- if (!string.IsNullOrWhiteSpace(Party.ContactName)) { writer.WriteOptionalElementString("ram", "LineTwo", Party.Street); }
+ string lineOneValue = !string.IsNullOrWhiteSpace(Party.Street2) ? Party.Street2 : (!string.IsNullOrWhiteSpace(Party.ContactName) ? Party.ContactName : Party.Street);
+ string lineTwoValue = (!string.IsNullOrWhiteSpace(Party.Street2) || !string.IsNullOrWhiteSpace(Party.ContactName)) ? Party.Street : null;
+ writer.WriteOptionalElementString("ram", "LineOne", lineOneValue);
+ writer.WriteOptionalElementString("ram", "LineTwo", lineTwoValue);
writer.WriteOptionalElementString("ram", "CityName", Party.City);
if (Party.Country.HasValue)
diff --git a/ZUGFeRD/InvoiceDescriptor20Reader.cs b/ZUGFeRD/InvoiceDescriptor20Reader.cs
index e9ebc7bc..01061efe 100644
--- a/ZUGFeRD/InvoiceDescriptor20Reader.cs
+++ b/ZUGFeRD/InvoiceDescriptor20Reader.cs
@@ -351,7 +351,7 @@ public override InvoiceDescriptor Load(Stream stream)
decimal? penaltyPercent = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:CalculationPercent", nsmgr, null);
int? penaltyDueDays = null; // XmlUtils.NodeAsInt(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisPeriodMeasure", nsmgr);
decimal? penaltyBaseAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisAmount", nsmgr, null);
- decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentDiscountTerms/ram:ActualPenaltyAmount", nsmgr, null);
+ decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:ActualPenaltyAmount", nsmgr, null);
PaymentTermsType? paymentTermsType = discountPercent.HasValue ? PaymentTermsType.Skonto :
penaltyPercent.HasValue ? PaymentTermsType.Verzug :
(PaymentTermsType?)null;
@@ -644,13 +644,14 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
if (!String.IsNullOrWhiteSpace(lineTwo))
{
+ retval.Street2 = lineOne;
retval.ContactName = lineOne;
retval.Street = lineTwo;
}
else
{
retval.Street = lineOne;
- retval.ContactName = null;
+ retval.Street2 = null;
}
retval.AddressLine3 = XmlUtils.NodeAsString(node, "ram:PostalTradeAddress/ram:LineThree", nsmgr);
diff --git a/ZUGFeRD/InvoiceDescriptor20Writer.cs b/ZUGFeRD/InvoiceDescriptor20Writer.cs
index 16d823f7..948cfa2c 100644
--- a/ZUGFeRD/InvoiceDescriptor20Writer.cs
+++ b/ZUGFeRD/InvoiceDescriptor20Writer.cs
@@ -1247,8 +1247,10 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, string prefix
writer.WriteStartElement("ram", "PostalTradeAddress");
writer.WriteOptionalElementString("ram", "PostcodeCode", party.Postcode);
- writer.WriteOptionalElementString("ram", "LineOne", string.IsNullOrWhiteSpace(party.ContactName) ? party.Street : party.ContactName);
- if (!string.IsNullOrWhiteSpace(party.ContactName)) { writer.WriteOptionalElementString("ram", "LineTwo", party.Street); }
+ string lineOneValue = !string.IsNullOrWhiteSpace(party.Street2) ? party.Street2 : (!string.IsNullOrWhiteSpace(party.ContactName) ? party.ContactName : party.Street);
+ string lineTwoValue = (!string.IsNullOrWhiteSpace(party.Street2) || !string.IsNullOrWhiteSpace(party.ContactName)) ? party.Street : null;
+ writer.WriteOptionalElementString("ram", "LineOne", lineOneValue);
+ writer.WriteOptionalElementString("ram", "LineTwo", lineTwoValue);
writer.WriteOptionalElementString("ram", "LineThree", party.AddressLine3); // BT-163
diff --git a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
index 2c6d3866..1f4c6993 100644
--- a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
+++ b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
@@ -146,7 +146,7 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo
_Writer.WriteOptionalElementString("cbc", "BuyerReference", this._Descriptor.ReferenceOrderNo);
- if (this._Descriptor.BillingPeriodEnd.HasValue || this._Descriptor.BillingPeriodEnd.HasValue)
+ if (this._Descriptor.BillingPeriodStart.HasValue || this._Descriptor.BillingPeriodEnd.HasValue)
{
_Writer.WriteStartElement("cac", "InvoicePeriod");
@@ -960,7 +960,7 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa
if (partyType != PartyTypes.SellerTaxRepresentativeTradeParty)
writer.WriteStartElement("cac", "Party", this._Descriptor.Profile);
- if (ElectronicAddress != null)
+ if (!String.IsNullOrWhiteSpace(ElectronicAddress?.Address))
{
writer.WriteStartElement("cbc", "EndpointID");
writer.WriteAttributeString("schemeID", ElectronicAddress.ElectronicAddressSchemeID.EnumToString());
@@ -1037,7 +1037,13 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa
writer.WriteStartElement("cac", "PostalAddress");
_Writer.WriteOptionalElementString("cbc", "StreetName", party.Street);
- _Writer.WriteOptionalElementString("cbc", "AdditionalStreetName", party.AddressLine3);
+ _Writer.WriteOptionalElementString("cbc", "AdditionalStreetName", party.Street2);
+ if (!string.IsNullOrWhiteSpace(party.AddressLine3))
+ {
+ writer.WriteStartElement("cac", "AddressLine");
+ _Writer.WriteOptionalElementString("cbc", "Line", party.AddressLine3);
+ writer.WriteEndElement(); //!AddressLine
+ }
_Writer.WriteElementString("cbc", "CityName", party.City);
_Writer.WriteElementString("cbc", "PostalZone", party.Postcode);
_Writer.WriteOptionalElementString("cbc", "CountrySubentity", party.CountrySubdivisionName);
diff --git a/ZUGFeRD/InvoiceDescriptor22UblReader.cs b/ZUGFeRD/InvoiceDescriptor22UblReader.cs
index f086056d..a0032f97 100644
--- a/ZUGFeRD/InvoiceDescriptor22UblReader.cs
+++ b/ZUGFeRD/InvoiceDescriptor22UblReader.cs
@@ -309,8 +309,8 @@ public override InvoiceDescriptor Load(Stream stream)
retval.PaymentMeans = tempPaymentMeans;
- retval.BillingPeriodStart = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//cac:InvoicePeriod/cbc:StartDate", nsmgr);
- retval.BillingPeriodEnd = XmlUtils.NodeAsDateTime(doc.DocumentElement, "//cac:InvoicePeriod/cbc:EndDate", nsmgr);
+ retval.BillingPeriodStart = XmlUtils.NodeAsDateTime(doc.DocumentElement, "/*[1]/cac:InvoicePeriod/cbc:StartDate", nsmgr); // do not find InvoicePeriod in
+ retval.BillingPeriodEnd = XmlUtils.NodeAsDateTime(doc.DocumentElement, "/*[1]/cac:InvoicePeriod/cbc:EndDate", nsmgr);
XmlNodeList creditorFinancialAccountNodes = doc.SelectNodes("//cac:PaymentMeans/cac:PayeeFinancialAccount", nsmgr);
foreach (XmlNode node in creditorFinancialAccountNodes)
@@ -534,7 +534,7 @@ private static List _parseTradeLineItem(XmlNode tradeLineItem, Xm
BuyerAssignedID = XmlUtils.NodeAsString(tradeLineItem, "./cac:Item/cac:BuyersItemIdentification/cbc:ID", nsmgr),
Name = XmlUtils.NodeAsString(tradeLineItem, "./cac:Item/cbc:Name", nsmgr),
Description = XmlUtils.NodeAsString(tradeLineItem, ".//cac:Item/cbc:Description", nsmgr),
- NetQuantity = XmlUtils.NodeAsDecimal(tradeLineItem, ".//cac:Price/cbc:BaseQuantity", nsmgr, 1),
+ NetQuantity = XmlUtils.NodeAsDecimal(tradeLineItem, ".//cac:Price/cbc:BaseQuantity", nsmgr),
BilledQuantity = billedQuantity ?? 0,
LineTotalAmount = XmlUtils.NodeAsDecimal(tradeLineItem, ".//cbc:LineExtensionAmount", nsmgr, 0),
TaxCategoryCode = EnumExtensions.StringToEnum(XmlUtils.NodeAsString(tradeLineItem, ".//cac:Item/cac:ClassifiedTaxCategory/cbc:ID", nsmgr)),
@@ -696,8 +696,9 @@ private static List _parseTradeLineItem(XmlNode tradeLineItem, Xm
if (!item.UnitCode.HasValue)
{
- // UnitCode alternativ aus BilledQuantity extrahieren
- item.UnitCode = EnumExtensions.StringToNullableEnum(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:InvoicedQuantity/@unitCode", nsmgr));
+ // UnitCode alternativ aus InvoicedQuantity oder CreditedQuantity extrahieren
+ item.UnitCode = EnumExtensions.StringToNullableEnum(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:InvoicedQuantity/@unitCode", nsmgr))
+ ?? EnumExtensions.StringToNullableEnum(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:CreditedQuantity/@unitCode", nsmgr));
}
// TODO: Find value //if (tradeLineItem.SelectSingleNode(".//ram:SpecifiedLineTradeDelivery/ram:DeliveryNoteReferencedDocument/ram:IssuerAssignedID", nsmgr) != null)
@@ -797,11 +798,6 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
retval.Name = XmlUtils.NodeAsString(node, "cac:PartyName/cbc:Name", nsmgr);
retval.SpecifiedLegalOrganization = _nodeAsLegalOrganization(node, "cac:PartyLegalEntity", nsmgr);
- if (string.IsNullOrWhiteSpace(retval.Name))
- {
- retval.Name = XmlUtils.NodeAsString(node, "cac:PartyLegalEntity/cbc:RegistrationName", nsmgr);
- }
-
if (string.IsNullOrWhiteSpace(retval.Description))
{
retval.Description = XmlUtils.NodeAsString(node, "cac:PartyLegalEntity/cbc:CompanyLegalForm", nsmgr);
@@ -855,24 +851,13 @@ private static Party _nodeAsAddressParty(XmlNode baseNode, string xpath, XmlName
Party retval = new Party()
{
Street = XmlUtils.NodeAsString(node, "cbc:StreetName", nsmgr),
- AddressLine3 = XmlUtils.NodeAsString(node, "cbc:AdditionalStreetName", nsmgr),
+ Street2 = XmlUtils.NodeAsString(node, "cbc:AdditionalStreetName", nsmgr),
+ AddressLine3 = XmlUtils.NodeAsString(node, "cac:AddressLine/cbc:Line", nsmgr),
City = XmlUtils.NodeAsString(node, "cbc:CityName", nsmgr),
Postcode = XmlUtils.NodeAsString(node, "cbc:PostalZone", nsmgr),
CountrySubdivisionName = XmlUtils.NodeAsString(node, "cbc:CountrySubentity", nsmgr),
Country = EnumExtensions.StringToNullableEnum(XmlUtils.NodeAsString(node, "cac:Country/cbc:IdentificationCode", nsmgr)),
};
- string addressLine2 = XmlUtils.NodeAsString(node, "cac:AddressLine/cbc:Line", nsmgr);
- if (!string.IsNullOrWhiteSpace(addressLine2))
- {
- if (string.IsNullOrWhiteSpace(retval.AddressLine3))
- {
- retval.AddressLine3 = addressLine2;
- }
- else if (!string.IsNullOrWhiteSpace(addressLine2) && string.IsNullOrWhiteSpace(retval.ContactName))
- {
- retval.ContactName = addressLine2;
- }
- }
return retval;
} // !_nodeAsAddressParty()
diff --git a/ZUGFeRD/InvoiceDescriptor23CIIReader.cs b/ZUGFeRD/InvoiceDescriptor23CIIReader.cs
index 82f9e6f8..ba4314af 100644
--- a/ZUGFeRD/InvoiceDescriptor23CIIReader.cs
+++ b/ZUGFeRD/InvoiceDescriptor23CIIReader.cs
@@ -432,7 +432,7 @@ public override InvoiceDescriptor Load(Stream stream)
penaltyDueDays = XmlUtils.NodeAsInt(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisPeriodMeasure", nsmgr);
DateTime? penaltyMaturityDate = XmlUtils.NodeAsDateTime(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisDateTime/udt:DateTimeString", nsmgr); // BT-X-276-0
decimal? penaltyBaseAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:BasisAmount", nsmgr, null);
- decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentDiscountTerms/ram:ActualPenaltyAmount", nsmgr, null);
+ decimal? penaltyActualAmount = XmlUtils.NodeAsDecimal(node, ".//ram:ApplicableTradePaymentPenaltyTerms/ram:ActualPenaltyAmount", nsmgr, null);
PaymentTermsType? paymentTermsType = discountPercent.HasValue ? PaymentTermsType.Skonto :
penaltyPercent.HasValue ? PaymentTermsType.Verzug :
(PaymentTermsType?)null;
@@ -865,12 +865,14 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
if (!String.IsNullOrWhiteSpace(lineTwo))
{
+ retval.Street2 = lineOne;
retval.ContactName = lineOne;
retval.Street = lineTwo;
}
else
{
retval.Street = lineOne;
+ retval.Street2 = null;
retval.ContactName = null;
}
diff --git a/ZUGFeRD/InvoiceDescriptor23CIIWriter.cs b/ZUGFeRD/InvoiceDescriptor23CIIWriter.cs
index b75a9de2..30f6fd33 100644
--- a/ZUGFeRD/InvoiceDescriptor23CIIWriter.cs
+++ b/ZUGFeRD/InvoiceDescriptor23CIIWriter.cs
@@ -1817,11 +1817,10 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa
{
writer.WriteStartElement("ram", "PostalTradeAddress");
writer.WriteOptionalElementString("ram", "PostcodeCode", party.Postcode); // buyer: BT-53
- writer.WriteOptionalElementString("ram", "LineOne", string.IsNullOrWhiteSpace(party.ContactName) ? party.Street : party.ContactName); // buyer: BT-50
- if (!string.IsNullOrWhiteSpace(party.ContactName))
- {
- writer.WriteOptionalElementString("ram", "LineTwo", party.Street); // buyer: BT-51
- }
+ string lineOneValue = !string.IsNullOrWhiteSpace(party.Street2) ? party.Street2 : (!string.IsNullOrWhiteSpace(party.ContactName) ? party.ContactName : party.Street);
+ string lineTwoValue = (!string.IsNullOrWhiteSpace(party.Street2) || !string.IsNullOrWhiteSpace(party.ContactName)) ? party.Street : null;
+ writer.WriteOptionalElementString("ram", "LineOne", lineOneValue); // buyer: BT-50
+ writer.WriteOptionalElementString("ram", "LineTwo", lineTwoValue); // buyer: BT-51
writer.WriteOptionalElementString("ram", "LineThree", party.AddressLine3); // buyer: BT-163
writer.WriteOptionalElementString("ram", "CityName", party.City); // buyer: BT-52
diff --git a/ZUGFeRD/InvoiceValidator.cs b/ZUGFeRD/InvoiceValidator.cs
index e7231c10..e596c7a5 100644
--- a/ZUGFeRD/InvoiceValidator.cs
+++ b/ZUGFeRD/InvoiceValidator.cs
@@ -127,7 +127,7 @@ public static ValidationResult Validate(InvoiceDescriptor descriptor, ZUGFeRDVer
// TODO
// TODO ausgeben: Recalculating tax basis for tax percentages: [Key{percentage=7.00, code=[VAT] Value added tax, category=[S] Standard rate}, Key{percentage=19.00, code=[VAT] Value added tax, category=[S] Standard rate}]
- retval.Messages.Add(String.Format("Recalculated tax basis = {0:0.0000}", lineTotal - allowanceTotal));
+ retval.Messages.Add(String.Format("Recalculated tax basis = {0:0.0000}", lineTotal - allowanceTotal + chargeTotal));
retval.Messages.Add("Calculating tax total...");
decimal taxTotal = 0.0m;
diff --git a/ZUGFeRD/Party.cs b/ZUGFeRD/Party.cs
index dcc40bdf..64757f7c 100644
--- a/ZUGFeRD/Party.cs
+++ b/ZUGFeRD/Party.cs
@@ -60,9 +60,15 @@ public class Party
///
/// Street name and number
- /// e.g. used for BT-35
+ /// e.g. used for BT-35, BT-50
///
public string Street { get; set; }
+
+ ///
+ /// Additional address line (LineTwo in CII, AddressLine2 in UBL)
+ /// e.g. used for BT-36, BT-51
+ ///
+ public string Street2 { get; set; }
///
/// Global identifier
///