Electronic invoicing (Factur-X, ZUGFeRD, EN16931) with MBS

Thank you very much, Christian, for your plugins that allow us to manage electronic invoicing (Factur-X, ZUGFeRD, EN16931) with Xojo, which is gradually becoming mandatory in Europe for all companies!!

I saw that there was a complete example for Filemaker. Would it be possible to have an equivalent for Xojo? I can contribute to the funding if it helps.

Does anyone else have any experiences to share on this?

Never heard of Factur-X, ZUGFeRD, EN16931

Aren’t you referring to Peppol? Because as far as I know this is the only way to make invoices for B2B in EU. In some EU countries it is already mandatory.

There are several steps involved:

- First, you need to create a strictly structured invoice. There are several accepted formats, but the most popular is Factur-X (the name in France) / ZUGFeRD (the original name in Germany), because this format includes both a human-readable PDF and an XML for IT platforms (PDF/A-3 file with integrated XML). The technical basis is the European standard EN 16931 (here in French, but easily translatable by AI for those who are curious), which is the European semantic data model that must be strictly adhered to.

- Next, this electronic invoice must be sent to an approved platform that is compatible/interconnected with the Peppol network. Peppol is therefore the standard that allows European (and other) electronic invoicing platforms to communicate with each other, even if they are not part of the same country.

Example: a French company invoices a Belgian company. The French company sends the invoice to its approved electronic sending platform, usually automatically via its invoicing software. The platform receives it and, as it is part of the Peppol network, identifies the customer company in Belgium and delivers the invoice directly to the customer company’s accounting software (while also providing sales, payment, and VAT data to the authorities, etc.).

Please note that there may be inaccuracies in what I have stated above.

So, if billing software is designed with Xojo, it must be able to generate an electronic invoice that is compatible with the EN 16931 standard, generally an invoice in Factur-X/ZUGFeRD format. This is where Christian’s plugins are very helpful.

Then, once the software knows how to generate this type of document, it can automatically send it to a Peppol network platform, but that’s another topic (and much easier, I think).

In Germany we have to make xml invoices and several formats are acceptable.

With DynaPDF we can do the embedding of the XML into the PDF. We do have examples for this.

The part about creating or parsing the XML, well I only made an example for FileMaker as they use our xml functions. For Xojo you use Xojo XML classes and you have whatever database for the fields, so it is more a thing to write yourself.

If you would like someone to read the MBS docs and write something for you, I very much welcome funding.

Reach me directly here or by email support@strawberrysw.com

1 Like

You can always use Xojo find a developer to reach dev’s

Xojo find Developer

Just trying to help @Tim_Parnell

did you find my last post not helpful to the OP?

The find a developer program has flaws, such as the paywall that prevents knowledgeable developers from answering requests. I don’t support finding a developer through that form these days. Those seeking skilled advice are much better off just posting in the General category.

I deleted my post because it was me who wasn’t being helpful. I was the one who was wrong. I apologize for the noise.

@Tim_Parnell

Fair analysis

No noise, it helps us all to hear from experienced Xojo developers so thank you for you’re input

Happy New Year!

Here is an excerpt from my code with comments in German. Basically, I create a text file and merge it with the PDF I created. To do this, I use PerfectPDF from Softxpansion.de

Var sPathXML AS String ""
Var fiPathXML As FolderItem = SpecialFolder.Desktop
Var fiFileXML As New FolderItem //= fiPathXML.Child ("R2025001.xml")
Var tosXML As TextOutputStream

fiFileXML = fiPathXML.Child("R"+efVorgangsNr.Text+".xml")

If fiFileXML.Exists = True Then
  Try
    fiFileXML.Remove
    //MessageBox("File removed!")
  Catch exceptionmyerp As IOException
    MessageBox(exceptionmyerp.Message)
  End Try
End If

Try
  tosXML = TextOutputStream.Open(fiFileXML)
  
  tosXML.WriteLine "<?xml version="+Chr(34)+"1.0"+chr(34)+" encoding="+Chr(34)+"UTF-8"+Chr(34)+"?>"                  
  tosXML.WriteLine "<rsm:CrossIndustryInvoice xmlns:xsi="+chr(34)+"http://www.w3.org/2001/XMLSchema-instance"+chr(34)+" "
  tosXML.WriteLine "xmlns:rsm="+chr(34)+"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"+chr(34)+" "
  tosXML.WriteLine "xmlns:ram="+chr(34)+"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"+chr(34)+" "
  tosXML.WriteLine "xmlns:udt="+chr(34)+"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"+chr(34)+" "
  tosXML.WriteLine "xmlns:qdt="+chr(34)+"urn:un:unece:uncefact:data:standard:QualifiedDataType:100"+chr(34)+">"
  tosXML.WriteLine "<rsm:ExchangedDocumentContext>"
  tosXML.WriteLine "<ram:GuidelineSpecifiedDocumentContextParameter>"
  tosXML.WriteLine "<ram:ID>urn:cen.eu:en16931:2017</ram:ID>"
  tosXML.WriteLine "</ram:GuidelineSpecifiedDocumentContextParameter>"
  tosXML.WriteLine "</rsm:ExchangedDocumentContext>"
  tosXML.WriteLine "<rsm:ExchangedDocument>"
  
  // Belgnummer
  tosXML.WriteLine "<ram:ID>"+efVorgangsNr.Text+"</ram:ID>"
  tosXML.WriteLine "<ram:TypeCode>380</ram:TypeCode>"
  
  // Belegdatum
  tosXML.WriteLine "<ram:IssueDateTime>"
  tosXML.WriteLine "<udt:DateTimeString format="+chr(34)+"102"+chr(34)+">"+sBelegdatumJJJJMMTT+"</udt:DateTimeString>"
  tosXML.WriteLine "</ram:IssueDateTime>"
  
  tosXML.WriteLine "</rsm:ExchangedDocument>"
  
  // Begin der Belegpositionen
  tosXML.WriteLine "<rsm:SupplyChainTradeTransaction>"
  
Catch exceptionmyerp As IOException
  MessageBox("Error writing to file.")
Finally
  sPathXML =  fiPathXML.NativePath
  tosXML.Close
End Try
End If

        
      tosXML.WriteLine "<ram:IncludedSupplyChainTradeLineItem>"
      tosXML.WriteLine "<ram:AssociatedDocumentLineDocument>"
      tosXML.WriteLine "<ram:LineID>"+App.rsmyerpDummy.field("position").StringValue+"</ram:LineID>"
      tosXML.WriteLine "</ram:AssociatedDocumentLineDocument>"
      
      // 
      tosXML.WriteLine "<ram:SpecifiedTradeProduct>"
      
      // Artikelnummer
      tosXML.WriteLine "<ram:SellerAssignedID>"+App.rsmyerpDummy.field("tbelegdaten_artikelnummer").StringValue+"</ram:SellerAssignedID>"
      
      // Artikelbeschreibung kurz
      sArtikelKurz = App.rsmyerpDummy.field("artikelkurz").StringValue
      //sArtikelKurz = sArtikelKurz.DefineEncoding(Encodings.UTF8)
      tosXML.WriteLine "<ram:Name>"+KonvertiereStringZuXmlAusgabe(sArtikelKurz)+"</ram:Name>"
      
      // Artikelbeschreibung lang
      tosXML.WriteLine "<ram:Description>"+KonvertiereStringZuXmlAusgabe(App.rsmyerpDummy.field("artikellang").StringValue)+"</ram:Description>"
      
      // Hersteller
      //tosXML.WriteLine "<ram:BrandName>"+KonvertiereStringZuXmlAusgabe(sHersteller)+"</ram:BrandName>"
      
      tosXML.WriteLine "</ram:SpecifiedTradeProduct>"
      tosXML.WriteLine "<ram:SpecifiedLineTradeAgreement>"
      tosXML.WriteLine "<ram:GrossPriceProductTradePrice>"
      tosXML.WriteLine "<ram:ChargeAmount>"+App.rsmyerpDummy.field("vk").StringValue+"</ram:ChargeAmount>"
      tosXML.WriteLine "</ram:GrossPriceProductTradePrice>"
      tosXML.WriteLine "<ram:NetPriceProductTradePrice>"
      tosXML.WriteLine "<ram:ChargeAmount>"+App.rsmyerpDummy.field("vk").StringValue+"</ram:ChargeAmount>"
      tosXML.WriteLine "</ram:NetPriceProductTradePrice>"
      tosXML.WriteLine "</ram:SpecifiedLineTradeAgreement>"
      tosXML.WriteLine "<ram:SpecifiedLineTradeDelivery>"
      tosXML.WriteLine "<ram:BilledQuantity unitCode="+chr(34)+"H87"+chr(34)+">"+App.rsmyerpDummy.field("stueckzahl").StringValue+"</ram:BilledQuantity>"
      tosXML.WriteLine "</ram:SpecifiedLineTradeDelivery>"
      tosXML.WriteLine "<ram:SpecifiedLineTradeSettlement>"
      tosXML.WriteLine "<ram:ApplicableTradeTax>"
      tosXML.WriteLine "<ram:TypeCode>VAT</ram:TypeCode>"
      tosXML.WriteLine "<ram:CategoryCode>S</ram:CategoryCode>"
      tosXML.WriteLine "<ram:RateApplicablePercent>19</ram:RateApplicablePercent>"
      tosXML.WriteLine "</ram:ApplicableTradeTax>"
      tosXML.WriteLine "<ram:SpecifiedTradeSettlementLineMonetarySummation>"
      tosXML.WriteLine "<ram:LineTotalAmount>"+App.rsmyerpDummy.field("gvk").StringValue+"</ram:LineTotalAmount>"
      tosXML.WriteLine "</ram:SpecifiedTradeSettlementLineMonetarySummation>"
      tosXML.WriteLine "</ram:SpecifiedLineTradeSettlement>"
      tosXML.WriteLine "</ram:IncludedSupplyChainTradeLineItem>"
      
    Catch exceptionmyerp As IOException
      MessageBox("Error writing to file.")
    Finally
      tosXML.Close
    End Try
  End If
  

Try
  tosXML = TextOutputStream.Open(fiFileXML)
  
  
  // Eigenedaten / Rechnungsersteller
  tosXML.WriteLine "<ram:ApplicableHeaderTradeAgreement>"
  
  // Leitweg-ID
  //tosXML.WriteLine "<ram:BuyerReference>0</ram:BuyerReference>"
  
  tosXML.WriteLine "<ram:SellerTradeParty>"
  tosXML.WriteLine "<ram:ID></ram:ID>"
  
  tosXML.WriteLine "<ram:Name>Franks Company</ram:Name>"
  
  tosXML.WriteLine "<ram:DefinedTradeContact>"
  tosXML.WriteLine "<ram:PersonName>Franks Company</ram:PersonName>"
  tosXML.WriteLine "<ram:TelephoneUniversalCommunication>"
  tosXML.WriteLine "<ram:CompleteNumber>+49 1234 45789</ram:CompleteNumber>"
  tosXML.WriteLine "</ram:TelephoneUniversalCommunication>"
  tosXML.WriteLine "<ram:EmailURIUniversalCommunication>"
  tosXML.WriteLine "<ram:URIID>buchhaltung@example.com</ram:URIID>"
  tosXML.WriteLine "</ram:EmailURIUniversalCommunication>"
  tosXML.WriteLine "</ram:DefinedTradeContact>"
  tosXML.WriteLine "<ram:PostalTradeAddress>"
  
  If efVorgangsart.Text="R" Then
    tosXML.WriteLine "<ram:PostcodeCode>123456</ram:PostcodeCode>"
    tosXML.WriteLine "<ram:LineOne>Where the streets have no name</ram:LineOne>"
    tosXML.WriteLine "<ram:CityName>Nirgendwo</ram:CityName>"
  End If
  
  tosXML.WriteLine "<ram:CountryID>D</ram:CountryID>"
  tosXML.WriteLine "<ram:CountrySubDivisionName>NRW</ram:CountrySubDivisionName>"
  tosXML.WriteLine "</ram:PostalTradeAddress>"
  tosXML.WriteLine "<ram:URIUniversalCommunication>"
  tosXML.WriteLine "<ram:URIID schemeID="+chr(34)+"EM"+chr(34)+">email@example.com</ram:URIID>"
  tosXML.WriteLine "</ram:URIUniversalCommunication>"
  tosXML.WriteLine "<ram:SpecifiedTaxRegistration>"
  tosXML.WriteLine "<ram:ID schemeID="+chr(34)+"VA"+chr(34)+">DE123456789</ram:ID>"
  tosXML.WriteLine "</ram:SpecifiedTaxRegistration>"
  tosXML.WriteLine "<ram:SpecifiedTaxRegistration>"
  tosXML.WriteLine "<ram:ID schemeID="+chr(34)+"FC"+chr(34)+">111 2222 4444</ram:ID>"
  tosXML.WriteLine "</ram:SpecifiedTaxRegistration>"
  tosXML.WriteLine "</ram:SellerTradeParty>"
  
  // Kundendaten / Rechnungsempfänger
  tosXML.WriteLine "<ram:BuyerTradeParty>"
  tosXML.WriteLine "<ram:ID>"+efKundennr.Text+"</ram:ID>"
  
  // Empfänger Firma1
  sFirma1 = KonvertiereStringZuXmlAusgabe(efName1.Text)
  
  tosXML.WriteLine "<ram:Name>"+sFirma1+"</ram:Name>"
  
  tosXML.WriteLine "<ram:PostalTradeAddress>"
  tosXML.WriteLine "<ram:PostcodeCode>"+efPLZ.Text+"</ram:PostcodeCode>"
  tosXML.WriteLine "<ram:LineOne>"+KonvertiereStringZuXmlAusgabe(efStrasse.Text)+"</ram:LineOne>"
  tosXML.WriteLine "<ram:CityName>"+efOrt.Text+"</ram:CityName>"
  tosXML.WriteLine "<ram:CountryID>"+efLKZ.Text+"</ram:CountryID>"
  tosXML.WriteLine "<ram:CountrySubDivisionName></ram:CountrySubDivisionName>"
  tosXML.WriteLine "</ram:PostalTradeAddress>"
  
  tosXML.WriteLine "<ram:URIUniversalCommunication>"
  tosXML.WriteLine "<ram:URIID schemeID="+chr(34)+"EM"+chr(34)+">"+efKundenRechnungsEMAIL.Text+"</ram:URIID>"
  tosXML.WriteLine "</ram:URIUniversalCommunication>"
  tosXML.WriteLine "</ram:BuyerTradeParty>"
  
  //tosXML.WriteLine "<ram:SellerOrderReferencedDocument>"
  //tosXML.WriteLine "<ram:IssuerAssignedID>"+efKundenAuftrNr.Text+"</ram:IssuerAssignedID>"
  //tosXML.WriteLine "</ram:SellerOrderReferencedDocument>"
  
  tosXML.WriteLine "<ram:BuyerOrderReferencedDocument>"
  tosXML.WriteLine "<ram:IssuerAssignedID>"+efKundenAuftrNr.Text+"</ram:IssuerAssignedID>"
  tosXML.WriteLine "</ram:BuyerOrderReferencedDocument>"
  
  tosXML.WriteLine "</ram:ApplicableHeaderTradeAgreement>"
  tosXML.WriteLine "<ram:ApplicableHeaderTradeDelivery/>"
  tosXML.WriteLine "<ram:ApplicableHeaderTradeSettlement>"
  tosXML.WriteLine "<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>"
  tosXML.WriteLine "<ram:SpecifiedTradeSettlementPaymentMeans>"
  tosXML.WriteLine "<ram:TypeCode>58</ram:TypeCode>"
  
  // Bankverbindungen
  tosXML.WriteLine "<ram:PayeePartyCreditorFinancialAccount>"
  tosXML.WriteLine "<ram:IBANID>DE12345678901234567890</ram:IBANID>"
  tosXML.WriteLine "<ram:AccountName>Frank Company</ram:AccountName>"
  tosXML.WriteLine "</ram:PayeePartyCreditorFinancialAccount>"
  tosXML.WriteLine "<ram:PayeeSpecifiedCreditorFinancialInstitution>"
  tosXML.WriteLine "<ram:BICID>BICNUMBER</ram:BICID>"
  tosXML.WriteLine "</ram:PayeeSpecifiedCreditorFinancialInstitution>"
  tosXML.WriteLine "</ram:SpecifiedTradeSettlementPaymentMeans>"
  tosXML.WriteLine "<ram:SpecifiedTradeSettlementPaymentMeans>"
  tosXML.WriteLine "<ram:TypeCode>58</ram:TypeCode>"
  tosXML.WriteLine "<ram:PayeePartyCreditorFinancialAccount>"
  tosXML.WriteLine "<ram:IBANID>DE012345678901234567890</ram:IBANID>"
  tosXML.WriteLine "<ram:AccountName>Frank Company</ram:AccountName>"
  tosXML.WriteLine "</ram:PayeePartyCreditorFinancialAccount>"
  tosXML.WriteLine "<ram:PayeeSpecifiedCreditorFinancialInstitution>"
  tosXML.WriteLine "<ram:BICID>SECONDBIC</ram:BICID>"
  tosXML.WriteLine "</ram:PayeeSpecifiedCreditorFinancialInstitution>"
  tosXML.WriteLine "</ram:SpecifiedTradeSettlementPaymentMeans>"
  tosXML.WriteLine "<ram:ApplicableTradeTax>"
  
  // Rechnungssumme + Zahlungsbedingungen
  
  // MWST
  tosXML.WriteLine "<ram:CalculatedAmount>"+Str(dMwSt2)+"</ram:CalculatedAmount>"
  tosXML.WriteLine "<ram:TypeCode>VAT</ram:TypeCode>"
  
  // Nettobetrag
  tosXML.WriteLine "<ram:BasisAmount>"+Str(dWarenwertNetto)+"</ram:BasisAmount>"
  tosXML.WriteLine "<ram:CategoryCode>S</ram:CategoryCode>"
  
  // MWST-Satz
  tosXML.WriteLine "<ram:RateApplicablePercent>"+efMwSt.Text+"</ram:RateApplicablePercent>"
  
  tosXML.WriteLine "</ram:ApplicableTradeTax>"
  tosXML.WriteLine "<ram:SpecifiedTradePaymentTerms>"
  
  // Zahlungsbedingung 
  tosXML.WriteLine "<ram:Description>"+efZahlungsbedingung.Text+"</ram:Description>"
  
  tosXML.WriteLine "<ram:DueDateDateTime>"
  
  tosXML.WriteLine "<udt:DateTimeString format="+chr(34)+"102"+chr(34)+">"+sDatumZahlbarBis+"</udt:DateTimeString>"
  tosXML.WriteLine "</ram:DueDateDateTime>"
  tosXML.WriteLine "</ram:SpecifiedTradePaymentTerms>"
  tosXML.WriteLine "<ram:SpecifiedTradeSettlementHeaderMonetarySummation>"
  
  // Nettobetrag
  tosXML.WriteLine "<ram:LineTotalAmount>"+Str(dWarenwertNetto)+"</ram:LineTotalAmount>"
  tosXML.WriteLine "<ram:ChargeTotalAmount>0</ram:ChargeTotalAmount>"
  tosXML.WriteLine "<ram:AllowanceTotalAmount>0</ram:AllowanceTotalAmount>"
  
  // Nettobetrag
  tosXML.WriteLine "<ram:TaxBasisTotalAmount>"+Str(dWarenwertNetto)+"</ram:TaxBasisTotalAmount>"
  
  // MWST
  tosXML.WriteLine "<ram:TaxTotalAmount currencyID="+chr(34)+"EUR"+chr(34)+">"+Str(dMwSt2)+"</ram:TaxTotalAmount>"
  
  // Endbetrag
  tosXML.WriteLine "<ram:GrandTotalAmount>"+Str(dEndbetrag)+"</ram:GrandTotalAmount>"
  tosXML.WriteLine "<ram:DuePayableAmount>"+Str(dEndbetrag)+"</ram:DuePayableAmount>"
  
  tosXML.WriteLine "</ram:SpecifiedTradeSettlementHeaderMonetarySummation>"
  tosXML.WriteLine "</ram:ApplicableHeaderTradeSettlement>"
  tosXML.WriteLine "</rsm:SupplyChainTradeTransaction>"
  tosXML.WriteLine "</rsm:CrossIndustryInvoice>"
  
Catch exceptionmyerp As IOException
  MessageBox("Error writing to file.")
Finally
  tosXML.Close
End Try

Greetings Frank

4 Likes

Oh, thank you so much, Frank! That’s a wonderful gift you’re giving us! Thanks again, and I wish you a very happy new year!