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/InvoiceDescriptor1Reader.cs b/ZUGFeRD/InvoiceDescriptor1Reader.cs
index 277b99dd..86a91941 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;
@@ -479,7 +479,7 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
if (!String.IsNullOrWhiteSpace(lineTwo))
{
retval.ContactName = lineOne;
- retval.Street = lineOne;
+ retval.Street = lineTwo;
}
else
{
diff --git a/ZUGFeRD/InvoiceDescriptor20Reader.cs b/ZUGFeRD/InvoiceDescriptor20Reader.cs
index e9ebc7bc..1a88bdb2 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;
diff --git a/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs b/ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
index 2c6d3866..aa25791c 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());
diff --git a/ZUGFeRD/InvoiceDescriptor22UblReader.cs b/ZUGFeRD/InvoiceDescriptor22UblReader.cs
index f086056d..f1e64f08 100644
--- a/ZUGFeRD/InvoiceDescriptor22UblReader.cs
+++ b/ZUGFeRD/InvoiceDescriptor22UblReader.cs
@@ -196,8 +196,7 @@ public override InvoiceDescriptor Load(Stream stream)
EmailAddress = XmlUtils.NodeAsString(doc.DocumentElement, "//cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail", 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)
@@ -309,8 +308,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 +533,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 +695,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 +797,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);
diff --git a/ZUGFeRD/InvoiceDescriptor23CIIReader.cs b/ZUGFeRD/InvoiceDescriptor23CIIReader.cs
index 82f9e6f8..14d7cafc 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;
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;