Vorlagensprache verstehen und anpassen

Wie die Platzhalter, Bedingungen, Schleifen und Helfer in Druckvorlagen funktionieren, welche Daten dir zur Verfügung stehen und wie du eine bestehende Vorlage gefahrlos änderst.

Eine Druckvorlage ist eine ganz normale HTML-Seite mit CSS — angereichert um Platzhalter in doppelten geschweiften Klammern. Beim Erzeugen eines Belegs setzt FFN Connect die Platzhalter durch die echten Werte der Bestellung ein und wandelt das fertige HTML anschließend in ein PDF um.

Die Platzhaltersprache heißt Handlebars. Sie ist bewusst klein gehalten: Es gibt Platzhalter, Bedingungen, Schleifen und eine Handvoll Helfer — mehr nicht. Rechnen, Datenbankabfragen oder Programmlogik sind nicht vorgesehen. Alles, was auf dem Beleg stehen soll, muss also entweder fester Text sein oder als Wert in den Daten vorliegen.

Hinweis

Wie du eine Druckvorlage anlegst, eine fertige Vorlage auswählst und sie einem Verkaufskanal zuweist, beschreibt der Artikel Lieferschein-Vorlagen erstellen. Dieser Artikel hier geht ausschließlich um den Quellcode.

Wo du die Vorlagensprache bearbeitest

  1. Öffne Einstellungen → Druckvorlagen und klicke die Vorlage an.
  2. Öffne oben rechts das Menü und wähle Quellcode bearbeiten.

Der Editor besteht aus drei Bereichen:

  • links der Code,
  • in der Mitte die Liste Variablen — der komplette Datenbaum, den deine Vorlage nutzen kann,
  • rechts die Live-Vorschau als PDF mit Beispieldaten.

Ein Klick auf ein Feld im Variablen-Baum kopiert den Pfad (z. B. outbound.merchantOutboundNumber) in die Zwischenablage. Bei Listen findest du am rechten Rand ein -Menü mit Copy Loop — das kopiert einen fertigen {{#each …}}-Block. Beim Tippen schlägt der Editor außerdem passende Pfade vor, sobald du einen Punkt setzt.

Wichtig

Solange deine Druckvorlage noch mit einer fertigen Vorlage (Preset) verknüpft ist, kannst du den Code nicht bearbeiten — er wird von der Vorlage verwaltet. Über Quellcode bearbeiten bietet dir FFN Connect dann Trennen & Anpassen an. Damit wird der Code der Vorlage einmalig in deine Druckvorlage kopiert und die Verknüpfung entfernt. Das lässt sich nicht rückgängig machen, und künftige Verbesserungen an der Vorlage erreichen deine Druckvorlage nicht mehr.

Grundbausteine

Werte ausgeben

Ein Wert wird mit doppelten geschweiften Klammern eingesetzt:

<h1>{{company.name}}</h1>
<p>Bestell-Nr.: #{{order.number}}</p>

Verschachtelte Daten erreichst du mit Punktnotation — jeder Punkt geht eine Ebene tiefer:

{{company.address.postal_code}} {{company.address.city}}<br>
{{company.address.country.name}}

Leerzeichen innerhalb der Klammern sind erlaubt: {{ order.number }} und {{order.number}} sind gleichwertig.

Hinweis

Ein Platzhalter auf ein Feld, das es nicht gibt oder das leer ist, erzeugt keinen Fehler — es wird schlicht nichts ausgegeben. Ein Tippfehler im Pfad fällt dir deshalb nur dadurch auf, dass die Stelle im PDF leer bleibt.

HTML in Werten

{{ … }} gibt Werte immer maskiert aus. Steht in einem Feld Müller & Söhne, erscheint im PDF korrekt Müller & Söhne; enthält ein Feld HTML wie <b>fett</b>, erscheint dieser Text sichtbar als <b>fett</b> und wird nicht als Formatierung interpretiert. Das ist Absicht und schützt dein Layout vor kaputten Daten.

Soll HTML aus einem Feld tatsächlich als HTML wirken, nimmst du drei Klammern oder ein kaufmännisches Und:

{{{product.description}}}
{{& product.description}}

Achtung

Nutze die ungeschützte Ausgabe nur bei Feldern, deren Inhalt du selbst pflegst. Enthält ein solches Feld unvollständiges HTML (z. B. ein nicht geschlossenes <div>), verrutscht das gesamte Layout des Belegs.

Kommentare

Kommentare erscheinen nicht im PDF:

{{! Dieser Hinweis ist nur im Editor sichtbar }}

Welche Daten dir zur Verfügung stehen

Jede Vorlage bekommt beim Erzeugen des Belegs denselben Datenbaum. Auf oberster Ebene gibt es diese Bereiche:

Bereich Was drinsteckt
order Die Bestellung aus dem Verkaufskanal: Nummern, Lieferadresse, Positionen wie bestellt, Attribute, Tags
outbound Der Warenausgang (Fulfillment-Auftrag): Lieferschein-Nummer, Lager, Datum, die tatsächlich zu versendenden Positionen, Summen und Steueraufteilung
company Deine Firmendaten: Name, Adressen, Logo
settings Die Einstellungen der Vorlage (siehe unten)
lang Das Sprachkürzel der Ausgabe, z. B. de

Hinweis

Der Datenbaum ist für alle Vorlagentypen — Lieferschein, Rechnung und eigener Beleg — identisch. Was einen Lieferschein von einer Rechnung unterscheidet, ist also allein dein Code, nicht die verfügbaren Daten. Maßgeblich ist immer die Liste Variablen im Editor: Was dort steht, kannst du verwenden.

outbound — der Warenausgang

Für Lieferscheine ist das der wichtigste Bereich, denn hier stehen die Positionen so, wie sie tatsächlich rausgehen.

Feld Inhalt
outbound.merchantOutboundNumber Nummer des Warenausgangs in FFN Connect
outbound.externalNumber Nummer im angebundenen System
outbound.internalNote Hinweis an den Fulfiller
outbound.date Datum des Warenausgangs
outbound.shippingMethod Bezeichnung der Versandart
outbound.shippingAddress Lieferadresse (Adressfelder siehe unten)
outbound.warehouse name, fulfiller_name und address des Lagers
outbound.items Liste der Positionen
outbound.netTotal Summe netto
outbound.taxAmount Summe Steuer
outbound.grossTotal Summe brutto
outbound.shippingCosts Versandkosten
outbound.grandTotal Gesamtsumme inkl. Versandkosten
outbound.vatBreakdown Steueraufteilung, je Steuersatz mit rate, basis und total

Innerhalb einer Schleife über outbound.items stehen dir je Position zur Verfügung:

Feld Inhalt
position Laufende Positionsnummer (1, 2, 3 …)
name Artikelbezeichnung
description Artikelbeschreibung
merchantSku Deine SKU
ean EAN/Barcode-Nummer
quantity Menge in diesem Warenausgang
orderedQuantity Ursprünglich bestellte Menge
note Positions-Hinweis
image Bild-Adresse des Artikels
weight / grossWeight Netto- bzw. Bruttogewicht
netUnitPrice Nettobetrag je Stück
netTotalPrice Nettobetrag der Position
unitTax Steuerbetrag je Stück
taxTotal Steuerbetrag der Position
vatPercentage Steuersatz in Prozent
total Gesamtbetrag der Position
hsCode Zolltarifnummer
countryOfOrigin / countryCodeOfOrigin Ursprungsland bzw. dessen Länderkürzel

order — die Bestellung

Feld Inhalt
order.number Bestellnummer
order.original_number Ursprüngliche Nummer im Verkaufskanal
order.sales_channel Verkaufskanal
order.shipping_method_name Versandart laut Bestellung
order.shipping_address Lieferadresse
order.line_items Bestellpositionen mit sku, name, quantity, note, image_preview_url, attributes und dem kompletten Artikel unter product
order.meta_data Zusatzdaten als Liste aus key und value
order.attributes Bestell-Attribute als Liste aus key, type und value
order.tags Liste der Tags
order.desired_delivery_date Wunschliefertermin
order.priority Priorität als Zahl: 1 hoch, 0 mittel, -1 niedrig

company — deine Firma

company.name, company.logo_url sowie company.address und company.billing_address.

Adressfelder

Alle Adressen (order.shipping_address, outbound.shippingAddress, company.address, company.billing_address, outbound.warehouse.address) haben denselben Aufbau:

fullname, firstname, lastname, company, street, number, address_line1, address_line2, postal_code, city, state, email, phone sowie country mit country.name und country.iso_alpha_2.

Tipp

address_line1 fasst Straße und Hausnummer bereits zusammen — du brauchst street und number also nur, wenn du beides getrennt setzen willst.

Bedingungen

Mit {{#if …}} blendest du einen Abschnitt nur dann ein, wenn ein Wert gefüllt ist. Jeder {{#if}}-Block muss mit {{/if}} geschlossen werden:

{{#if address_line2}}
{{address_line2}}<br>
{{/if}}

Leer, 0, false und „nicht vorhanden" gelten dabei als nicht erfüllt. Optional gibt es {{else}} und {{else if …}}:

{{#if outbound.internalNote}}
    <p>Hinweis: {{outbound.internalNote}}</p>
{{else}}
    <p>Kein Hinweis hinterlegt.</p>
{{/if}}

{{#unless …}} ist die Umkehrung — der Abschnitt erscheint, wenn der Wert nicht gefüllt ist:

{{#unless order.tags}}Keine Tags vorhanden{{/unless}}

Schleifen

{{#each …}} wiederholt einen Abschnitt für jeden Eintrag einer Liste. Innerhalb der Schleife beziehen sich die Platzhalter direkt auf den aktuellen Eintrag — du schreibst also {{name}}, nicht {{outbound.items.name}}:

{{#each outbound.items}}
<tr>
    <td>{{position}}</td>
    <td>{{name}}</td>
    <td>{{merchantSku}}</td>
    <td style="text-align: right;">{{quantity}}</td>
</tr>
{{/each}}

Willst du in der Schleife auf etwas außerhalb zugreifen, stellst du dem Pfad ../ voran. So machen es auch die mitgelieferten Vorlagen, um Spalten je nach Einstellung ein- und auszublenden:

{{#each outbound.items}}
    {{#if ../settings.show_column_sku}}
    <td>{{merchantSku}}</td>
    {{/if}}
{{/each}}

Zusätzlich stehen dir in jeder Schleife diese Hilfswerte zur Verfügung:

Wert Bedeutung
{{@index}} Laufende Nummer, beginnend bei 0
{{@first}} / {{@last}} true beim ersten bzw. letzten Eintrag
{{@key}} Der Schlüssel — bei benannten Listen wie outbound.vatBreakdown der Steuersatz

Ist die Liste leer, kannst du mit {{else}} einen Ersatztext ausgeben:

{{#each order.tags}}{{.}} {{else}}Keine Tags{{/each}}

Bei einfachen Listen aus Text (z. B. order.tags) gibt {{.}} den aktuellen Eintrag aus.

outbound.vatBreakdown ist keine einfache Liste, sondern nach Steuersatz benannt. Deshalb arbeitest du dort mit {{@key}} oder mit dem Feld rate:

{{#each outbound.vatBreakdown}}
<tr>
    <td>{{rate}} %</td>
    <td>{{#money basis}}</td>
    <td>{{#money total}}</td>
</tr>
{{/each}}

Immer denselben Bereich ansprechen

Wenn du viele Felder aus demselben Ast brauchst, spart {{#with …}} das ständige Wiederholen des Pfades:

{{#with order.shipping_address}}
    {{#if company}}{{company}}<br>{{/if}}
    {{#if fullname}}{{fullname}}<br>{{/if}}
    {{address_line1}}<br>
    {{postal_code}} {{city}}<br>
    {{country.name}}
{{/with}}

Helfer

Helfer sind kleine Funktionen, die einen Wert aufbereiten. Sie werden mit einem # eingeleitet und stehen dort, wo das Ergebnis erscheinen soll.

Helfer Wofür Beispiel
money Zahl mit zwei Nachkommastellen {{#money outbound.grossTotal}}
date Datum formatieren {{#date format="d.m.Y" value=outbound.date}} {{/date}}
image Bild aus dem Internet einbetten <img src="{{#image company.logo_url}}">
barcode Barcode oder QR-Code erzeugen {{#barcode value=ean}}
font Schriftart einbetten {{#font name="roboto"}}{{/font}}
withLineBreaks Zeilenumbrüche aus einem Textfeld als Umbruch ausgeben {{#withLineBreaks settings.footer_additional_info}}
replace Text im umschlossenen Abschnitt ersetzen {{#replace find="Straße" replace="Str."}}{{company.address.street}}{{/replace}}
start Sicherstellen, dass ein Text mit einem bestimmten Zeichen beginnt {{#start value=order.number start="#"}}
limit Langen Text kürzen {{#limit value=item.name chars=40 end="…"}}

Wichtig zu wissen:

  • money rundet auf zwei Nachkommastellen und setzt Tausendertrennzeichen — allerdings im englischen Format (1,234.50). Ein Währungszeichen fügt der Helfer nicht hinzu; die mitgelieferten Vorlagen schreiben es bei Bedarf selbst dahinter: {{#money outbound.grossTotal}}€.
  • date nutzt die üblichen Kürzel: d Tag, m Monat, Y vierstelliges Jahr, H:i Stunde:Minute. format="d.m.Y" ergibt also 05.01.2026. Ohne Angabe wird d-m-Y verwendet.
  • image lädt das Bild herunter und bettet es direkt in das PDF ein. Das ist zwingend nötig: Ein <img src="https://…"> ohne diesen Helfer bleibt im fertigen PDF leer. Unterstützt werden JPEG und PNG; sehr große Bilder werden automatisch verkleinert. Lässt sich ein Bild nicht laden, erscheint ein neutrales Platzhalter-Symbol statt einer Fehlermeldung.
  • barcode erkennt den Typ automatisch anhand des Werts. Du kannst ihn auch vorgeben, z. B. {{#barcode value=ean type="EAN13"}} oder {{#barcode value=order.number type="QRCODE"}}. Weitere Angaben sind w und h für Breite und Höhe, showCode="true" für die Klartextzeile und color="0,0,0" für die Farbe als RGB-Werte.
  • limit kürzt zu lange Texte, damit sie eine Tabellenspalte nicht sprengen. chars gibt die maximale Länge an (ohne Angabe 50 Zeichen), end das Anhängsel für gekürzte Texte — ohne Angabe wird nichts angehängt. Kürzere Texte bleiben unverändert.
  • font gehört in den <head> der Vorlage und bettet die Schriftart ein, damit sie im PDF sicher zur Verfügung steht. Verfügbar sind unter anderem roboto, lato, opensans, montserrat, nunito, oswald, raleway, merriweather, notosans, ptsans, sourcesans3, ibmplexsans, ibmplexserif und newsreader. Ein unbekannter Name fällt automatisch auf roboto zurück.

Zusätzlich gibt es einige allgemeine Textwerkzeuge: {{#upper …}} und {{#lower …}} für Groß- bzw. Kleinschreibung, {{#capitalize …}} für einen großen Anfangsbuchstaben, {{#truncate feld 40 "…"}} zum Kürzen und {{#default feld "Ersatztext"}} für einen Ersatzwert, wenn ein Feld leer ist.

Achtung

upper und lower funktionieren bei Umlauten nicht zuverlässig (aus Müller wird MüLLER). Für Überschriften erreichst du dasselbe Ergebnis sauberer über CSS: text-transform: uppercase;.

Einstellungen (settings)

Fertige Vorlagen bringen Einstellungen mit — die Ankreuzfelder und Textfelder, die du links neben der Vorschau siehst, etwa Heading, Show Column: SKU oder Footer Additional Info. Jede dieser Einstellungen ist im Code über settings. plus ihren Namen erreichbar:

<h2>{{settings.heading}}</h2>

{{#if settings.show_delivery_address}}
    … Lieferanschrift …
{{/if}}

Ankreuzfelder liefern true oder false und werden deshalb typischerweise mit {{#if}} abgefragt; Text- und Textfeld-Einstellungen gibst du direkt aus. Änderst du eine Einstellung, aktualisiert sich die Vorschau sofort.

Hinweis

Die Einstellungen stammen aus der fertigen Vorlage. Löst du dich mit Trennen & Anpassen von ihr, bleiben die zuletzt gesetzten Werte erhalten — {{settings.…}} liefert also weiterhin das, was du eingestellt hattest, und Vorschau und erzeugter Beleg zeigen dasselbe. Neue Einstellungen kommen aber nicht mehr dazu: Ab jetzt änderst du solche Stellen direkt im Code.

Eigene Anpassungen sicher vornehmen

Arbeite immer mit Entwurf und Vorschau. Alles, was du im Code-Editor tippst, landet zunächst nur im Entwurf — der Beleg, der bei echten Bestellungen erzeugt wird, bleibt unverändert. Rechts siehst du sofort, wie sich die Änderung mit Beispieldaten auswirkt. Erst Änderungen veröffentlichen macht den Entwurf produktiv; Änderungen verwerfen stellt den zuletzt veröffentlichten Stand wieder her. Beide Knöpfe erscheinen nur, solange es einen abweichenden Entwurf gibt.

Gehe in kleinen Schritten vor. Ändere eine Sache, sieh in der Vorschau nach, mach weiter. Wenn nach zwanzig Änderungen etwas kaputt ist, hilft nur noch das Verwerfen aller Änderungen.

Achte auf Paare. Jedes {{#if}} braucht ein {{/if}}, jedes {{#each}} ein {{/each}}, jedes {{#with}} ein {{/with}}. Rücke verschachtelte Blöcke sauber ein — fehlende Schlusstags fallen dann sofort auf.

Beachte die Grenzen der PDF-Erzeugung. Der Beleg wird nicht von einem Browser gerendert, sondern von einem PDF-Werkzeug mit eingeschränktem CSS-Umfang. Halte dich deshalb an das, was die mitgelieferten Vorlagen vormachen:

  • Layouts über <table> aufbauen, kein display: flex und kein grid.
  • Fußzeilen mit position: fixed platzieren, damit sie auf jeder Seite erscheinen.
  • Seitenumbrüche über CSS steuern: page-break-after: always an einem Element, page-break-inside: avoid an Tabellenzeilen, thead { display: table-header-group; } für wiederholte Tabellenköpfe.
  • Seitenränder über @page { margin: 2cm; } setzen.
  • Bilder ausschließlich über den image-Helfer einbinden.

Was bei einem Fehler passiert. Kommt die Vorlage nicht durch — etwa wegen eines unbekannten Helfers oder eines überzähligen Schlusstags — entsteht trotzdem ein PDF: Es enthält dann nur den Satz „Es ist ein Fehler bei der Generierung des PDFs aufgetreten. Es kann sein, dass du in deiner Vorlage Syntaxfehler hast." Siehst du diesen Satz in der Vorschau, ist im Code etwas nicht in Ordnung. Der zuletzt veröffentlichte Stand ist davon nicht betroffen, solange du den Entwurf nicht veröffentlichst.

Tipp

Bevor du eine veröffentlichte Vorlage stark umbaust, kopiere dir den kompletten Code aus dem Editor in eine Textdatei. Das ist dein Sicherungsnetz — der Editor kennt nur „Entwurf" und „veröffentlicht", keine Versionshistorie.

Häufige Fehler

Beobachtung Ursache Lösung
Das PDF enthält nur den Satz zum Generierungsfehler Syntaxfehler: unbekannter Helfer, falsch geschriebener Blockname oder ein Schlusstag ohne passendes Starttag Zuletzt geänderte Stelle prüfen; im Zweifel Änderungen verwerfen
Ein Platzhalter bleibt leer Pfad falsch geschrieben oder das Feld ist bei dieser Bestellung nicht gefüllt Pfad aus dem Variablen-Baum kopieren statt abtippen
Nach einem {{#if}} fehlt der restliche Beleg oder Inhalte erscheinen doppelt Fehlendes {{/if}} bzw. {{/each}} — das meldet die Vorlage nicht als Fehler Blöcke einrücken und Paare durchzählen
HTML aus einem Feld erscheint als sichtbarer Text {{ … }} schützt die Ausgabe Bewusst {{{ … }}} verwenden
Ein Bild fehlt im PDF <img src="https://…"> ohne den image-Helfer, oder kein JPEG/PNG {{#image …}} verwenden; Platzhalter-Symbol heißt: Bild nicht ladbar
Beträge erscheinen als 1,234.50 money nutzt das englische Zahlenformat So belassen oder die Zahl ohne Helfer ausgeben
Eine Spalte lässt sich nicht mehr über die Einstellungen ausblenden Die Vorlage wurde von der fertigen Vorlage getrennt — es gibt keine Einstellungen mehr Abfrage {{#if settings.…}} im Code entfernen oder durch feste Entscheidung ersetzen
Positionen auf dem Lieferschein passen nicht zur Bestellung order.line_items zeigt die Bestellung, outbound.items den tatsächlichen Warenausgang Für Lieferscheine outbound.items verwenden
Änderungen wirken sich nicht auf neue Belege aus Der Entwurf wurde nicht veröffentlicht Änderungen veröffentlichen klicken

Nicht das Richtige gefunden?

Unser Support-Team hilft dir gerne weiter.

Support kontaktieren