Skip to content

Commit

Permalink
Rewritten party profile filter and trading business name handling
Browse files Browse the repository at this point in the history
  • Loading branch information
stephanstapel committed Nov 19, 2023
1 parent a3a64a8 commit 695c92d
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,4 @@ ASALocalRun/
# MFractors (Xamarin productivity tool) working folder
.mfractor/
documentation/xRechnung/XRechnung 2.3.1/Schematron/generated/ubl-inv-br-de-20-paymentmeans-code59-test-106-code-urn_cen.eu_en16931_2017_compliant_urn_xoev-de_kosit_standard_xrechnung_2.3_conformant_urn_xoev-de_kosit_extension_xrechnung_2.3.xml
/documentation/Factur-X 1.0.06 2022 03 01 - FINAL EN
14 changes: 13 additions & 1 deletion ZUGFeRD-Test/ZUGFeRD21Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,22 @@ public void TestElectronicAddress()
Assert.AreEqual(loadedInvoice.BuyerElectronicAddress.ElectronicAddressSchemeID, ElectronicAddressSchemeIdentifiers.LuxemburgVatNumber);
} // !TestElectronicAddress()


[TestMethod]
public void TestMinimumInvoice()
{
InvoiceDescriptor desc = this.InvoiceProvider.CreateInvoice();
desc.Invoicee = new Party() // this information will not be stored in the output file since it is available in Extended profile only
{
Name = "Test"
Name = "Invoicee"
};
desc.Seller = new Party()
{
Name = "Seller",
SpecifiedLegalOrganization = new LegalOrganization()
{
TradingBusinessName = "Trading business name for seller party"
}
};
desc.TaxBasisAmount = 73; // this information will not be stored in the output file since it is available in Extended profile only
MemoryStream ms = new MemoryStream();
Expand All @@ -221,6 +230,9 @@ public void TestMinimumInvoice()

InvoiceDescriptor loadedInvoice = InvoiceDescriptor.Load(ms);
Assert.AreEqual(loadedInvoice.Invoicee, null);
Assert.AreNotEqual(loadedInvoice.Seller, null);
Assert.AreNotEqual(loadedInvoice.Seller.SpecifiedLegalOrganization, null);
Assert.AreEqual(loadedInvoice.Seller.SpecifiedLegalOrganization.TradingBusinessName, "");
} // !TestMinimumInvoice()


Expand Down
94 changes: 77 additions & 17 deletions ZUGFeRD/InvoiceDescriptor21Writer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,12 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream)

#region SellerTradeParty
// BT-31: this.Descriptor.SellerTaxRegistration
_writeOptionalParty(Writer, "ram:SellerTradeParty", this.Descriptor.Seller, this.Descriptor.SellerContact, this.Descriptor.SellerElectronicAddress, this.Descriptor.SellerTaxRegistration, descriptor.Profile, ALL_PROFILES, Profile.Extended);
_writeOptionalParty(Writer, PartyTypes.SellerTradeParty, this.Descriptor.Seller, this.Descriptor.SellerContact, this.Descriptor.SellerElectronicAddress, this.Descriptor.SellerTaxRegistration);
#endregion

#region BuyerTradeParty
// BT-48: this.Descriptor.BuyerTaxRegistration
_writeOptionalParty(Writer, "ram:BuyerTradeParty", this.Descriptor.Buyer, this.Descriptor.BuyerContact, this.Descriptor.BuyerElectronicAddress, this.Descriptor.BuyerTaxRegistration, descriptor.Profile, ALL_PROFILES, Profile.Comfort | Profile.Extended);
_writeOptionalParty(Writer, PartyTypes.BuyerTradeParty, this.Descriptor.Buyer, this.Descriptor.BuyerContact, this.Descriptor.BuyerElectronicAddress, this.Descriptor.BuyerTaxRegistration);
#endregion

// TODO: implement SellerTaxRepresentativeTradeParty
Expand Down Expand Up @@ -630,9 +630,9 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream)

#region ApplicableHeaderTradeDelivery
Writer.WriteStartElement("ram:ApplicableHeaderTradeDelivery"); // Pflichteintrag
_writeOptionalParty(Writer, "ram:ShipToTradeParty", this.Descriptor.ShipTo, profile: Profile.Extended | Profile.XRechnung1 | Profile.XRechnung, legalOrganizationProfile: Profile.Extended, tradingBusinessNameProfile: Profile.Extended);
_writeOptionalParty(Writer, PartyTypes.ShipToTradeParty, this.Descriptor.ShipTo);
//ToDo: UltimateShipToTradeParty
_writeOptionalParty(Writer, "ram:ShipFromTradeParty", this.Descriptor.ShipFrom, profile: Profile.Extended, legalOrganizationProfile: Profile.Extended, tradingBusinessNameProfile: Profile.Extended); // ShipFrom shall not be written in XRechnung profiles
_writeOptionalParty(Writer, PartyTypes.ShipFromTradeParty, this.Descriptor.ShipFrom); // ShipFrom shall not be written in XRechnung profiles

#region ActualDeliverySupplyChainEvent
if (this.Descriptor.ActualDeliveryDate.HasValue)
Expand Down Expand Up @@ -710,12 +710,10 @@ public override void Save(InvoiceDescriptor descriptor, Stream stream)
Writer.WriteElementString("ram:InvoiceCurrencyCode", this.Descriptor.Currency.EnumToString());

// 7. InvoiceeTradeParty (optional)
_writeOptionalParty(Writer, "ram:InvoiceeTradeParty", this.Descriptor.Invoicee, profile: Profile.Extended,
legalOrganizationProfile: Profile.Extended, tradingBusinessNameProfile: Profile.Extended);
_writeOptionalParty(Writer, PartyTypes.InvoiceeTradeParty, this.Descriptor.Invoicee);

// 8. PayeeTradeParty (optional)
_writeOptionalParty(Writer, "ram:PayeeTradeParty", this.Descriptor.Payee, profile: ALL_PROFILES ^ Profile.Minimum,
legalOrganizationProfile: ALL_PROFILES, tradingBusinessNameProfile: Profile.BasicWL | Profile.Basic | Profile.Comfort | Profile.Extended);
_writeOptionalParty(Writer, PartyTypes.PayeeTradeParty, this.Descriptor.Payee);

#region SpecifiedTradeSettlementPaymentMeans
// 10. SpecifiedTradeSettlementPaymentMeans (optional)
Expand Down Expand Up @@ -1163,12 +1161,11 @@ private void _writeNotes(ProfileAwareXmlTextWriter writer, List<Note> notes)
}
} // !_writeNotes()

private void _writeOptionalLegalOrganization(ProfileAwareXmlTextWriter writer, string legalOrganizationTag, LegalOrganization legalOrganization,
Profile profile = Profile.Unknown, Profile tradingBusinessNameProfile = Profile.Unknown)
private void _writeOptionalLegalOrganization(ProfileAwareXmlTextWriter writer, string legalOrganizationTag, LegalOrganization legalOrganization, PartyTypes partyType = PartyTypes.Unknown)
{
if (legalOrganization != null)
{
writer.WriteStartElement(legalOrganizationTag, profile);
writer.WriteStartElement(legalOrganizationTag, this.Descriptor.Profile);
if (legalOrganization.ID != null)
{
if (!String.IsNullOrEmpty(legalOrganization.ID.ID) && !String.IsNullOrEmpty(legalOrganization.ID.SchemeID.EnumToString()))
Expand All @@ -1184,19 +1181,65 @@ private void _writeOptionalLegalOrganization(ProfileAwareXmlTextWriter writer, s
}
if (!String.IsNullOrEmpty(legalOrganization.TradingBusinessName))
{
writer.WriteElementString("ram:TradingBusinessName", legalOrganization.TradingBusinessName, tradingBusinessNameProfile);
// filter according to https://github.com/stephanstapel/ZUGFeRD-csharp/pull/221
if (((partyType == PartyTypes.SellerTradeParty) && (this.Descriptor.Profile != Profile.Minimum)) ||
((partyType == PartyTypes.PayeeTradeParty) && (this.Descriptor.Profile != Profile.Minimum)) ||
((partyType == PartyTypes.BuyerTradeParty) && (this.Descriptor.Profile == Profile.Comfort)) ||
((partyType == PartyTypes.BuyerTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
(this.Descriptor.Profile == Profile.Extended) /* remaining party types */
)
{
writer.WriteElementString("ram:TradingBusinessName", legalOrganization.TradingBusinessName, this.Descriptor.Profile);
}
}
}
writer.WriteEndElement();
}
}

private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, string partyTag, Party party, Contact contact = null, ElectronicAddress ElectronicAddress = null, List<TaxRegistration> taxRegistrations = null,
Profile profile = Profile.Unknown, Profile legalOrganizationProfile = Profile.Unknown, Profile tradingBusinessNameProfile = Profile.Unknown)
private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, PartyTypes partyType, Party party, Contact contact = null, ElectronicAddress ElectronicAddress = null, List<TaxRegistration> taxRegistrations = null)
{
// filter according to https://github.com/stephanstapel/ZUGFeRD-csharp/pull/221
if ( ((partyType == PartyTypes.UltimateShipToTradeParty) && (this.Descriptor.Profile != Profile.Extended)) ||
((partyType == PartyTypes.ShipToTradeParty) && (this.Descriptor.Profile != Profile.Extended)) ||
((partyType == PartyTypes.ShipFromTradeParty) && (this.Descriptor.Profile != Profile.Extended)) ||
((partyType == PartyTypes.InvoiceeTradeParty) && (this.Descriptor.Profile != Profile.Extended)) ||
((partyType == PartyTypes.PayeeTradeParty) && (this.Descriptor.Profile == Profile.Minimum)) ||
((partyType == PartyTypes.PayerTradeParty) && (this.Descriptor.Profile != Profile.Extended))
)
{
return;
}

if (party != null)
{
writer.WriteStartElement(partyTag, profile);
switch (partyType)
{
case PartyTypes.SellerTradeParty:
writer.WriteStartElement("ram:SellerTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.BuyerTradeParty:
writer.WriteStartElement("ram:BuyerTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.ShipToTradeParty:
writer.WriteStartElement("ram:ShipToTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.UltimateShipToTradeParty:
writer.WriteStartElement("ram:UltimateShipToTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.ShipFromTradeParty:
writer.WriteStartElement("ram:ShipFromTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.InvoiceeTradeParty:
writer.WriteStartElement("ram:InvoiceeTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.PayeeTradeParty:
writer.WriteStartElement("ram:PayeeTradeParty", this.Descriptor.Profile);
break;
case PartyTypes.PayerTradeParty:
writer.WriteStartElement("ram:PayerTradeParty", this.Descriptor.Profile);
break;
}

if (party.ID != null)
{
Expand Down Expand Up @@ -1226,9 +1269,26 @@ private void _writeOptionalParty(ProfileAwareXmlTextWriter writer, string partyT
writer.WriteElementString("ram:Name", party.Name);
}

if ((party.SpecifiedLegalOrganization != null) && ((profile & legalOrganizationProfile) == profile))
if (party.SpecifiedLegalOrganization != null)
{
_writeOptionalLegalOrganization(writer, "ram:SpecifiedLegalOrganization", party.SpecifiedLegalOrganization, tradingBusinessNameProfile);
// filter according to https://github.com/stephanstapel/ZUGFeRD-csharp/pull/221
if (((partyType == PartyTypes.ShipToTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.ShipFromTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.SalesAgentTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.UltimateShipToTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.BuyerTaxRepresentativeTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.ProductEndUserTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.BuyerAgentTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.InvoicerTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.InvoiceeTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.PayerTradeParty) && (this.Descriptor.Profile == Profile.Extended)) ||
((partyType == PartyTypes.SellerTradeParty) /* all profiles */ ) ||
((partyType == PartyTypes.BuyerTradeParty) /* all profiles */ ) ||
((partyType == PartyTypes.PayeeTradeParty) /* all profiles */ )
)
{
_writeOptionalLegalOrganization(writer, "ram:SpecifiedLegalOrganization", party.SpecifiedLegalOrganization, partyType);
}
}

if (contact != null)
Expand Down
15 changes: 14 additions & 1 deletion ZUGFeRD/PartyTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ namespace s2industries.ZUGFeRD
{
internal enum PartyTypes
{
SellerTradeParty
Unknown,
SellerTradeParty,
BuyerTradeParty,
ShipToTradeParty,
UltimateShipToTradeParty,
ShipFromTradeParty,
InvoiceeTradeParty,
PayeeTradeParty,
SalesAgentTradeParty,
BuyerTaxRepresentativeTradeParty,
ProductEndUserTradeParty,
BuyerAgentTradeParty,
InvoicerTradeParty,
PayerTradeParty
}
}

0 comments on commit 695c92d

Please sign in to comment.