Skip to content
19 changes: 17 additions & 2 deletions ZUGFeRD.Test/XRechnungUBLTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,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);
Expand All @@ -1124,7 +1124,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);
Expand Down Expand Up @@ -1325,6 +1325,21 @@ public void TestNonStandardDateTimeFormat()
} // !TestNonStandardDateTimeFormat()


/// <summary>
/// Test that InvoicePeriod is not read TradeLineItem -> InvoicePeriod
/// </summary>
[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()
{
Expand Down
8 changes: 4 additions & 4 deletions ZUGFeRD/InvoiceDescriptor1Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -478,7 +478,7 @@ private static Party _nodeAsParty(XmlNode baseNode, string xpath, XmlNamespaceMa
if (!String.IsNullOrWhiteSpace(lineTwo))
{
retval.ContactName = lineOne;
retval.Street = lineOne;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behaviour is consistent across all versions. I'd leave this like it is, i.e.

retval.Street = LineOne
retval.Street2 = LineTwo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implementied it as per PR #863 where you said:

"'d like to leave the mapping as it was for consistency purpose.

Street = lineTwo
Street2 = lineOne"

retval.Street = lineTwo;
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion ZUGFeRD/InvoiceDescriptor20Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -958,7 +958,7 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes pa

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());
Expand Down
18 changes: 7 additions & 11 deletions ZUGFeRD/InvoiceDescriptor22UblReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public override InvoiceDescriptor Load(Stream stream)
};
}

retval.SellerTaxRepresentative = _nodeAsParty(doc.DocumentElement, "//cac:TaxRepresentativeParty/cac:Party", nsmgr);
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 @@ -308,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 <cac:InvoiceLine>
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)
Expand Down Expand Up @@ -533,7 +533,7 @@ private static List<TradeLineItem> _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<TaxCategoryCodes>(XmlUtils.NodeAsString(tradeLineItem, ".//cac:Item/cac:ClassifiedTaxCategory/cbc:ID", nsmgr)),
Expand Down Expand Up @@ -695,8 +695,9 @@ private static List<TradeLineItem> _parseTradeLineItem(XmlNode tradeLineItem, Xm

if (!item.UnitCode.HasValue)
{
// UnitCode alternativ aus BilledQuantity extrahieren
item.UnitCode = EnumExtensions.StringToNullableEnum<QuantityCodes>(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:InvoicedQuantity/@unitCode", nsmgr));
// UnitCode alternativ aus InvoicedQuantity oder CreditedQuantity extrahieren
item.UnitCode = EnumExtensions.StringToNullableEnum<QuantityCodes>(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:InvoicedQuantity/@unitCode", nsmgr))
?? EnumExtensions.StringToNullableEnum<QuantityCodes>(XmlUtils.NodeAsString(tradeLineItem, ".//cbc:CreditedQuantity/@unitCode", nsmgr));
}

// TODO: Find value //if (tradeLineItem.SelectSingleNode(".//ram:SpecifiedLineTradeDelivery/ram:DeliveryNoteReferencedDocument/ram:IssuerAssignedID", nsmgr) != null)
Expand Down Expand Up @@ -795,11 +796,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);
Expand Down
2 changes: 1 addition & 1 deletion ZUGFeRD/InvoiceDescriptor23CIIReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion ZUGFeRD/InvoiceValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading