Create ZUGFeRD and XRechnung invoices with VB6, VB5 and VBA – late and early binding

SDK members
ActualDeliveryDate_102 AddPaymentMeans() AddProduct_Comfort() AddTradePaymentTerms() AddTradeTax() BuyerAddressLine1 BuyerCity BuyerCountry BuyerName BuyerPostCode
Business terms
BT-1 Invoice number BT-2 Invoice issue date BT-3 Invoice type code BT-5 Invoice currency code BT-31 Seller VAT identifier BT-72 Actual delivery date BG-25 Invoice line BG-23 VAT breakdown
Code samples
Command line, VB6 / VBScript (COM)
Published

In short: Applications in Visual Basic 6, Visual Basic 5 and VBA (Access, Excel, Word) create e-invoices according to EN 16931 (ZUGFeRD 2.x, Factur-X, XRechnung) through the COM interface of the Aloaha ZUGFeRD SDK. A migration to .NET is not needed. There are two ways: late binding with CreateObject("Aloaha_ZUGFeRD.XML") without a reference, or early binding with a reference to the type library "Aloaha ZUGFeRD Toolkit". Your program creates the PDF invoice as before; the SDK writes the XML and embeds it in the PDF (hybrid ZUGFeRD invoice, PDF/A-3).

Late or early binding?

Late bindingEarly binding
Create the objectSet alo = CreateObject("Aloaha_ZUGFeRD.XML")Set alo = New Aloaha_ZUGFeRD.XML
Reference in the projectnoyes, "Aloaha ZUGFeRD Toolkit"
IntelliSense in the IDEnoyes
Checking of names and parametersat run timeat compile time
Optional parametersomit them at the end
Registration of the DLLrequired in both cases – done by the SDK setup

Late binding is robust when you distribute to many computers and works the same way in VBScript. Early binding is more convenient while programming. Both use the same methods.

Requirements

  • Windows with .NET Framework 4.7.2 or later.
  • Aloaha ZUGFeRD SDK from the download page. The SDK setup (MSI) registers the COM interface for 32 and 64 bit.
  • Mind the bitness: VB5 and VB6 programs are always 32 bit. VBA runs with the bitness of Office – a 64-bit Office needs the 64-bit registration.
  • Manual distribution without the setup: register the DLL as administrator with the Regasm of the matching bitness – for 32 bit from C:\Windows\Microsoft.NET\Framework\v4.0.30319\, for 64 bit from Framework64:
    regasm /codebase Aloaha_ZUGFeRD.dll
    Which files belong to it is described in Register libraries.
  • COM self-test: the package zugferdbinaries.zip contains the script zugferd_com_test.vbs. It checks the registration and bitness and creates the object as a test. For the 32-bit test on 64-bit Windows:
    C:\Windows\SysWOW64\cscript.exe //nologo zugferd_com_test.vbs
  • Licence key: you get a free, fully functional trial licence by e-mail to info@aloaha.com.

Step by step

  1. Install the SDK (or register the DLL, see above).
  2. Create the object, set the licence key and the profile.
  3. Set the invoice header, seller, buyer and payment details.
  4. Add the lines in a loop (AddProduct_Comfort).
  5. Set the VAT breakdown per rate (AddTradeTax) and the totals (WriteTotals).
  6. Save the XML (SaveXML) and embed it into the existing PDF invoice (MergeXMLIntoPDF).
  7. Check the first invoice with a validator – see How do I check my XRechnung before sending it?

Always pass amounts with a decimal point

The SDK expects amounts as text with a dot as the decimal separator ("59.50"). With German Windows settings, Format$ writes a comma. This helper function (Betrag = amount) works in VB5, VB6 and VBA (VB5 does not have Replace yet):

Public Function Betrag(ByVal Wert As Double) As String
    Dim s As String, p As Integer
    s = Format$(Wert, "0.00")
    p = InStr(s, ",")
    If p > 0 Then Mid$(s, p, 1) = "."
    Betrag = s
End Function

Option 1: late binding with CreateObject

Public Sub ErzeugeERechnung_LateBinding()
    Dim alo As Object
    Set alo = CreateObject("Aloaha_ZUGFeRD.XML")
    alo.Licensekey = "YOUR-LICENCE-KEY"
    alo.ZUGFeRD_Profile = 1                        ' 0 = BASIC, 1 = EN 16931 (COMFORT), 2 = EXTENDED

    ' invoice header: number (BT-1), text, date (BT-2), date format, name, language, currency (BT-5)
    Call alo.CreateInvoiceObject("RE-2027-0001", "Invoice as per order", _
                                 "20270104", "102", "", "DE", "EUR")
    alo.Document_TypeINT = 380                     ' BT-3: 380 = invoice, 381 = credit note
    alo.ActualDeliveryDate_102 = "20270104"        ' BT-72: delivery date

    ' seller with VAT ID (BT-31)
    alo.WriteSeller "Muster GmbH", "20095", "Hamburg", "Hauptstr. 1", "", "DE", "DE123456789"

    ' buyer
    alo.BuyerName = "Kunde AG"
    alo.BuyerAddressLine1 = "Marktplatz 5"
    alo.BuyerPostCode = "80331"
    alo.BuyerCity = "München"
    alo.BuyerCountry = "DE"

    ' payment: 58 = SEPA credit transfer (BT-81)
    alo.AddPaymentMeans 58, "SEPA credit transfer", "DE02120300000000202051", _
                        "Muster GmbH", "", "BYLADEM1001"
    alo.AddTradePaymentTerms "Payable within 30 days net"

    ' line (BG-25): 10 pieces at EUR 5.00, 19 %
    Call alo.AddProduct_Comfort(1, "", "ART-100", "10.00", "C62", "Paper A4", "500 sheets", _
                                "", "EUR", "", "C62", False, "", "", "", Betrag(5), "EUR", "1.00", _
                                "C62", "VAT", "S", "19.00", "", Betrag(50), "EUR", "", "")

    ' VAT breakdown (BG-23): tax, currency, basis, currency, type, category, rate
    alo.AddTradeTax Betrag(9.5), "EUR", Betrag(50), "EUR", "VAT", "S", "19.00"

    ' totals (BG-22): lines, grand total, amount due, net, tax
    alo.WriteTotals Betrag(50), Betrag(59.5), Betrag(59.5), Betrag(50), Betrag(9.5)

    ' save the XML and embed it into the existing PDF invoice
    If alo.SaveXML("C:\Invoices\RE-2027-0001.xml") Then
        Call alo.MergeXMLIntoPDF("C:\Invoices\RE-2027-0001.xml", _
                                 "C:\Invoices\RE-2027-0001.pdf", _
                                 "C:\Invoices\RE-2027-0001_zugferd.pdf", _
                                 True, "", "YOUR-LICENCE-KEY")
    Else
        MsgBox alo.Last_Error
    End If
    Set alo = Nothing
End Sub

In practice, replace the fixed values with the fields of your recordset and call AddProduct_Comfort in a loop over the lines. For an XRechnung to German public authorities, also set alo.isKOSIT = True and alo.LeitwegID.

Option 2: early binding with a reference

  1. In VB6: Project > References; in VBA: Tools > References.
  2. Tick "Aloaha ZUGFeRD Toolkit". If the entry is not in the list, choose the file Aloaha_ZUGFeRD.tlb via Browse; it ships with the SDK.
  3. Declare the variable with the class type. The calls stay the same as with late binding:
Dim alo As Aloaha_ZUGFeRD.XML
Set alo = New Aloaha_ZUGFeRD.XML
alo.Licensekey = "YOUR-LICENCE-KEY"
alo.ZUGFeRD_Profile = 1
' ... continue as in option 1

With the reference, the IDE shows all methods and parameters (IntelliSense), and the Object Browser (F2) lists the complete interface. The order of the methods in the COM interface has not changed since version 10; new functions are only added at the end. Compiled programs with a reference therefore keep working with newer SDK versions.

Overloaded methods have different names in COM

COM has no overloads. If a method exists more than once in .NET, the second variant gets the suffix _2 in VB6 and VBA:

Name in VB6/VBAParameters
WriteTotalsamounts as text ("59.50")
WriteTotals_2amounts as Double
AddProduct_Comfortline number as a number
AddProduct_Comfort_2line number as text (e.g. "1.1")
AddPaymentMeanspayment means as a number (UNTDID 4461, e.g. 58)
AddPaymentMeans_2payment means as text ("58")

Pass enumerations such as the profile or the payment means as numbers. For the invoice type (BT-3) there is a dedicated Document_TypeINT, see Creating an invoice and the invoice type (BT-3) (German).

VB5, VBA and VBScript

  • Visual Basic 5: the code above runs unchanged. The helper function Betrag deliberately uses only functions that already exist in VB5.
  • VBA in Access, Excel and Word: the same code in a standard module. With 64-bit Office, the 64-bit registration must be present (the SDK setup does both). Reading invoices and evaluating line values is shown in Reading line values in VBA and Access (German).
  • VBScript: declare variables without a type (Dim alo) and use late binding only; otherwise the same.

Typical errors

MessageCauseSolution
Run-time error 429: "ActiveX component can't create object"The COM class is not registered, is registered only for the other bitness (e.g. 32 bit with 64-bit Office), or the .NET Framework is missing.Run the SDK setup or register with the matching Regasm; zugferd_com_test.vbs shows the cause.
Run-time error 438: "Object doesn't support this property or method"Typo, or the overloaded variant has a different name (_2).Check the name; with a reference, the Object Browser (F2) shows all names.
Run-time error 13: "Type mismatch"Text where the SDK expects a number (or vice versa).Check the parameter type of the variant, see the table above.
Run-time error 450: "Wrong number of arguments"Too many parameters, or a mandatory parameter is missing.Compare the parameter list with the API reference.
Automation error "File not found"Registered without /codebase, and the DLL is not in the GAC.Register again with regasm /codebase.
Validator: BR-CO-10, BR-S-08, BR-CO-17Totals do not match the lines or the tax basis.Tax basis = net after allowances, see BR-S-08, BR-S-09 and BR-CO-17.

If SaveXML returns False, the reason is in Last_Error.

Alternative without COM: zugcmd

If the VB code should stay as unchanged as possible, your program writes the invoice data to CSV files and calls the command-line tool zugcmd, for example via Shell. zugcmd creates ZUGFeRD, Factur-X or XRechnung from them and embeds the XML in your PDF.

Step by step with tested sample files, the call from your own application and file names: Creating e-invoices via CSV and zugcmd from your own application.

All calls in detail: Quick start with VB.NET – the methods and parameters are the same in VB6. There is a separate guide for Delphi: Create ZUGFeRD and XRechnung with Delphi.

Frequently asked questions

Can I create ZUGFeRD invoices and XRechnung with Visual Basic 6?

Yes. The Aloaha ZUGFeRD SDK has a COM interface that VB6 uses via CreateObject("Aloaha_ZUGFeRD.XML") or via a reference to "Aloaha ZUGFeRD Toolkit". A migration to .NET is not needed.

Does this also work with Visual Basic 5?

Yes. VB5 uses the same COM interface. Only functions such as Replace do not exist there yet; the decimal-point helper function in this article does without them.

Does it also work in Access or Excel with VBA?

Yes, with the same code in a standard module. With 64-bit Office, the DLL must also be registered for 64 bit; the SDK setup does that.

Do I need a reference to the type library?

No, not for late binding with CreateObject. The reference (early binding) gives you IntelliSense and checks at compile time. The DLL has to be registered in both cases.

What does run-time error 429 on CreateObject mean?

The COM class is not registered on this computer, or only for the other bitness. Run the SDK setup or register the DLL with the matching Regasm; the script zugferd_com_test.vbs shows the cause.

Why is a method called WriteTotals_2 in VB6?

COM has no overloads. The second .NET variant of a method gets the suffix _2. WriteTotals expects amounts as text, WriteTotals_2 as Double.

Business terms in this article: BT-1 BT-2 BT-3 BT-5 BT-31 BT-72 BG-25 BG-23 BG-22 BT-81 BT-106 BT-109 BT-110 BT-112 BT-115 BT-116