Skip to content

Commit

Permalink
misc UBL bug fixes
Browse files Browse the repository at this point in the history
closes #588
  • Loading branch information
stephanstapel committed Jan 19, 2025
1 parent d82c2d2 commit 1270df9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 18 deletions.
42 changes: 39 additions & 3 deletions ZUGFeRD.Test/XRechnungUBLTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public void TestSpecialUnitCodes()
Assert.AreEqual(loadedInvoice.TradeLineItems[1].UnitCode, QuantityCodes.H87);
} // !TestSpecialUnitCodes()


[TestMethod]
public void TestTradelineitemAdditionalDocuments()
{
Expand All @@ -223,6 +224,7 @@ public void TestTradelineitemAdditionalDocuments()
Assert.AreEqual(loadedInvoice.TradeLineItems[0].GetAdditionalReferencedDocuments()[1].ID, "testid2");
} // !TestTradelineitemAdditionalDocuments()


/// <summary>
/// https://github.com/stephanstapel/ZUGFeRD-csharp/issues/319
/// </summary>
Expand Down Expand Up @@ -250,6 +252,7 @@ public void TestSkippingOfAllowanceChargeBasisAmount()
Assert.AreEqual(null, tax.AllowanceChargeBasisAmount);
} // !TestInvoiceCreation()


[TestMethod]
public void TestAllowanceChargeOnDocumentLevel()
{
Expand Down Expand Up @@ -294,6 +297,7 @@ public void TestAllowanceChargeOnDocumentLevel()

} // !TestAllowanceChargeOnDocumentLevel


[TestMethod]
public void TestInvoiceWithAttachment()
{
Expand Down Expand Up @@ -330,7 +334,6 @@ public void TestInvoiceWithAttachment()
} // !TestInvoiceWithAttachment()



[TestMethod]
public void TestActualDeliveryDateWithoutDeliveryAddress()
{
Expand All @@ -351,7 +354,6 @@ public void TestActualDeliveryDateWithoutDeliveryAddress()
} // !TestActualDeliveryDateWithoutDeliveryAddress()



[TestMethod]
public void TestActualDeliveryDateWithDeliveryAddress()
{
Expand Down Expand Up @@ -390,7 +392,6 @@ public void TestActualDeliveryDateWithDeliveryAddress()
} // !TestActualDeliveryDateWithDeliveryAddress()



[TestMethod]
public void TestActualDeliveryAddressWithoutDeliveryDate()
{
Expand Down Expand Up @@ -1207,5 +1208,40 @@ public void TestDecimals()
Assert.IsFalse(invoiceAsString.Contains($">{Math.Round(duePayableAmount, 4, MidpointRounding.AwayFromZero).ToString("F4", CultureInfo.InvariantCulture)}<"));
Assert.AreEqual(desc.DuePayableAmount, Math.Round(duePayableAmount, 2, MidpointRounding.AwayFromZero));
} // !TestDecimals()


[TestMethod]
public void TestDesignatedProductClassificationWithFullClassification()
{
InvoiceDescriptor desc = this._InvoiceProvider.CreateInvoice();
desc.TradeLineItems.First().AddDesignatedProductClassification(
DesignatedProductClassificationClassCodes.HS,
"List Version ID Value",
"Class Code",
"Class Name");

MemoryStream ms = new MemoryStream();

desc.Save(ms, ZUGFeRDVersion.Version23, Profile.XRechnung, ZUGFeRDFormats.UBL);
desc.Save("e:\\output.xml", ZUGFeRDVersion.Version23, Profile.XRechnung, ZUGFeRDFormats.UBL);


// string comparison
ms.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(ms);
string content = reader.ReadToEnd();
Assert.IsTrue(content.Contains("<cac:CommodityClassification>"));
Assert.IsTrue(content.Contains("<cbc:ItemClassificationCode listID=\"HS\" listVersionID=\"List Version ID Value\">Class Code</cbc:ItemClassificationCode>"));
Assert.IsTrue(content.Contains("</cac:CommodityClassification>"));

// structure comparison
ms.Seek(0, SeekOrigin.Begin);
InvoiceDescriptor loadedInvoice = InvoiceDescriptor.Load(ms);

Assert.AreEqual(DesignatedProductClassificationClassCodes.HS, loadedInvoice.TradeLineItems.First().GetDesignatedProductClassifications().First().ListID);
Assert.AreEqual("List Version ID Value", loadedInvoice.TradeLineItems.First().GetDesignatedProductClassifications().First().ListVersionID);
Assert.AreEqual("Class Code", loadedInvoice.TradeLineItems.First().GetDesignatedProductClassifications().First().ClassCode);
Assert.AreEqual(String.Empty, loadedInvoice.TradeLineItems.First().GetDesignatedProductClassifications().First().ClassName);
} // !TestDesignatedProductClassificationWithFullClassification()
}
}
14 changes: 8 additions & 6 deletions ZUGFeRD/InvoiceDescriptor22UBLWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo
{
throw new IllegalStreamException("Cannot write to stream");
}


long streamPosition = stream.Position;

this.Descriptor = descriptor;
this.Writer = new ProfileAwareXmlTextWriter(stream, descriptor.Profile);
bool isInvoice = true;
if (this.Descriptor.Type == InvoiceType.Invoice || this.Descriptor.Type == InvoiceType.Correction)
{
Expand All @@ -54,10 +59,6 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream, ZUGFeRDFo
throw new NotImplementedException("Not implemented yet.");
}

long streamPosition = stream.Position;

this.Descriptor = descriptor;
this.Writer = new ProfileAwareXmlTextWriter(stream, descriptor.Profile);
Dictionary<string, string> namespaces = new Dictionary<string, string>()
{
{ "cac", "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" },
Expand Down Expand Up @@ -704,6 +705,7 @@ private void _WriteTradeLineItem(TradeLineItem tradeLineItem, bool isInvoice = t
Writer.WriteEndElement(); //!InvoiceLine
}


private void _WriteCommodityClassification(ProfileAwareXmlTextWriter writer, List<DesignatedProductClassification> designatedProductClassifications)
{
if ((designatedProductClassifications == null) || (designatedProductClassifications.Count == 0))
Expand All @@ -720,8 +722,7 @@ private void _WriteCommodityClassification(ProfileAwareXmlTextWriter writer, Lis
continue;
}

writer.WriteStartElement("cbc", "ItemClassificationCode"); // BT-158
writer.WriteValue(classification.ClassCode, profile: ALL_PROFILES);
writer.WriteStartElement("cbc", "ItemClassificationCode"); // BT-158
Writer.WriteAttributeString("listID", classification.ListID.EnumToString()); // BT-158-1

if (!String.IsNullOrWhiteSpace(classification.ListVersionID))
Expand All @@ -730,6 +731,7 @@ private void _WriteCommodityClassification(ProfileAwareXmlTextWriter writer, Lis
}

// no name attribute in Peppol Billing!
writer.WriteValue(classification.ClassCode, profile: ALL_PROFILES);

writer.WriteEndElement();
}
Expand Down
2 changes: 1 addition & 1 deletion ZUGFeRD/InvoiceDescriptor22UblReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ private static List<TradeLineItem> _parseTradeLineItem(XmlNode tradeLineItem, Xm
DesignatedProductClassificationClassCodes listID = default(DesignatedProductClassificationClassCodes).FromString(XmlUtils.NodeAsString(commodityClassification, "./@listID", nsmgr));
item.AddDesignatedProductClassification(
listID,
XmlUtils.NodeAsString(commodityClassification, "./@istVersionID", nsmgr),
XmlUtils.NodeAsString(commodityClassification, "./@listVersionID", nsmgr),
commodityClassification.InnerText,
String.Empty // no name in Peppol Billing!
);
Expand Down
16 changes: 8 additions & 8 deletions ZUGFeRD/ProfileAwareXmlTextWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,20 @@ public void Flush()

public void WriteStartElement(string prefix, string localName, Profile profile = Profile.Unknown)
{
Profile _profile = profile;
Profile safeProfile = profile;
if (profile == Profile.Unknown)
{
_profile = this.CurrentProfile;
safeProfile = this.CurrentProfile;
}

if (!_IsNodeVisible() || !_DoesProfileFitToCurrentProfile(_profile))
if (!_IsNodeVisible() || !_DoesProfileFitToCurrentProfile(safeProfile))
{
this.XmlStack.Push(new StackInfo() { Profile = _profile, IsVisible = false });
this.XmlStack.Push(new StackInfo() { Profile = safeProfile, IsVisible = false });
return;
}
else
{
this.XmlStack.Push(new StackInfo() { Profile = _profile, IsVisible = true });
this.XmlStack.Push(new StackInfo() { Profile = safeProfile, IsVisible = true });
}

if (this.TextWriter == null)
Expand Down Expand Up @@ -150,13 +150,13 @@ public void WriteOptionalElementString(string prefix, string tagName, string val

public void WriteElementString(string prefix, string localName, string ns, string value, Profile profile = Profile.Unknown)
{
Profile _profile = profile;
Profile safeProfile = profile;
if (profile == Profile.Unknown)
{
_profile = this.CurrentProfile;
safeProfile = this.CurrentProfile;
}

if (this.TextWriter == null || !_IsNodeVisible() || !_DoesProfileFitToCurrentProfile(_profile))
if (this.TextWriter == null || !_IsNodeVisible() || !_DoesProfileFitToCurrentProfile(safeProfile))
{
return;
}
Expand Down

0 comments on commit 1270df9

Please sign in to comment.