Zum Inhalt

Word-Dokumente & Serienbriefe (docx)

Das Skript-Binding docx füllt Word-Vorlagen (.docx) mit Formulardaten und erzeugt daraus fertige Dokumente und Serienbriefe — bewusst als nachbearbeitbare .docx (nicht als PDF), damit Fachabteilungen das Ergebnis in Word weiterbearbeiten können.

Die Vorlagen sind ganz normale Word-Dateien, in denen mit einer geschweiften Doppelklammer Platzhalter gesetzt werden — mustache-artig, ohne Feldfunktionen-Fummelei:

Sehr geehrte(r) {{anrede}} {{nachname}},

Betreff: {{betreff}}

{{text}}

Technisch steckt darunter poi-tl (auf Basis von Apache POI).


Kürzer: docx.document(…)

docx.document(vorlage, daten) macht dasselbe wie docx.render, liefert aber ein Dokument, das sich selbst ausliefert:

docx.document(vorlage, daten).deliver("Brief.docx");

Ist daten ein Array, entsteht wie bei docx.mailMerge ein Serienbrief. docx.render und docx.mailMerge bleiben unverändert und geben weiterhin rohe Bytes zurück.

Überblick

Aufruf Ergebnis
docx.render(vorlage, daten) ein Dokument aus Feldern (+ Tabellen-Listen) → byte[]
docx.mailMerge(vorlage, zeilen) Serienbrief: pro Zeile ein Brief, EIN Dokument mit Seitenumbruch → byte[]
docx.deliver(bytes, dateiname) Download-Link (URL) für das erzeugte Dokument
docx.MIME offizieller docx-MIME-Typ (für mp.addBytes(...))

Das Ergebnis von render/mailMerge ist immer ein rohes byte[] — es komponiert mit mail, fs, zip und ctx.attachments genau wie das zip-Modul:

var bytes = docx.render(vorlage, daten);

var url = docx.deliver(bytes, "Brief.docx");         // Download-Link
mp.addBytes("Brief.docx", bytes, docx.MIME, null);   // an eine Mail
fs["/out/Brief.docx"] = bytes;                        // in die fs-Sandbox
zip.create([{ name: "Brief.docx", data: bytes }]);    // in ein ZIP

Woher kommt die Vorlage?

docx.render/mailMerge erwarten die Vorlage als byte[] (auch eine fs-Datei oder ein Base64-String werden akzeptiert). Übliche Quellen:

// aus der fs-Sandbox (die Fachabteilung pflegt dort die Word-Datei)
var vorlage = fs["/vorlagen/einfacher-brief.docx"].readBytes();

// aus einem hochgeladenen Anhang / einer Property-Datei → ebenfalls byte[]

Vorlage editierbar halten

Lege die Word-Vorlagen in der fs-Sandbox unter z. B. /vorlagen/… ab. So kann die Fachabteilung die Datei jederzeit in Word öffnen, das Layout anpassen (Logo, Schriftart, Fußzeile) und ersetzen — ohne dass am Skript etwas geändert werden muss.


1) Einfacher Brief aus Formularfeldern

var vorlage = fs["/vorlagen/einfacher-brief.docx"].readBytes();

var bytes = docx.render(vorlage, {
  anrede:   ctx.form.anrede,
  vorname:  ctx.form.vorname,
  nachname: ctx.form.nachname,
  strasse:  ctx.form.strasse,
  plz:      ctx.form.plz,
  ort:      ctx.form.ort,
  datum:    macros.resolve("${date(dd.MM.yyyy)|now}"),
  betreff:  ctx.form.betreff,
  text:     ctx.form.text
});

// Modaler Download-Dialog (prominent im Vordergrund):
view.add(new view.Download(docx.deliver(bytes, "Brief.docx"), "Brief herunterladen (.docx)", "Brief erzeugt"));

Fehlende Werte werden zu leerem Text ersetzt — es bleiben keine {{platzhalter}} im Dokument stehen.


2) Serienbrief aus einer 1:n-Namensliste

docx.mailMerge(vorlage, zeilen) rendert dieselbe Brief-Vorlage einmal je Zeile und fügt die Einzelbriefe zu einem Dokument zusammen — getrennt durch einen Seitenumbruch (klassischer Serienbrief zum Nachbearbeiten).

var vorlage = fs["/vorlagen/einladung.docx"].readBytes();

// Empfänger z.B. aus einer Kindtabelle (1:n) einsammeln …
var rows = [];
var gaeste = ctx.form.gaeste || [];
for (var i = 0; i < gaeste.length; i++) {
  var g = gaeste[i];
  rows.push({
    anrede: g.anrede, vorname: g.vorname, nachname: g.nachname,
    // gemeinsame Felder an JEDEN Brief anhängen:
    anlass: ctx.form.anlass, termin: ctx.form.termin, uhrzeit: ctx.form.uhrzeit
  });
}

var bytes = docx.mailMerge(vorlage, rows);
view.add(new view.Download(docx.deliver(bytes, "Serienbrief.docx"),
    rows.length + " Einladungen herunterladen", "Serienbrief erzeugt"));

Jede zeile ist ein eigenes Datenmodell — gemeinsame Felder (Anlass, Termin …) werden einfach an jede Zeile mitgegeben.


3) Tabellen (1:n) in einem Dokument — z. B. Rechnungsposten

Für Detail-Tabellen (Rechnungsposten, Einzelverbindungen …) wird eine Vorlagen-Zeile pro Listenelement wiederholt. In der Word-Vorlage:

  • Der Listen-Platzhalter {{posten}} steht in der Zeile über der Wiederhol-Zeile (bequem: in der Kopfzeile).
  • Die Zellen der Wiederhol-Zeile referenzieren die Element-Felder mit eckigen Klammern: [datum], [ziel], [betrag].
{{posten}}Datum Rufnummer / Ziel Dauer Betrag
[datum] [ziel] [dauer] [betrag]

Das Skript liefert die Liste unter dem Schlüssel posten — jeder Wert, der eine Liste ist, wird automatisch als Zeilen-Schleife gebunden; du musst nichts weiter konfigurieren:

var vorlage = fs["/vorlagen/telefonrechnung.docx"].readBytes();

var summe = 0, posten = [];
(ctx.form.verbindungen || []).forEach(function (v) {
  summe += Number(v.betrag) || 0;
  posten.push({ datum: v.datum, ziel: v.ziel, dauer: v.dauer,
                betrag: (Number(v.betrag)||0).toFixed(2).replace(".", ",") });
});

var bytes = docx.render(vorlage, {
  kunde: ctx.form.kunde, rechnungsnr: ctx.form.rechnungsnr,
  posten: posten,                                   // → Tabellen-Schleife
  summe:  summe.toFixed(2).replace(".", ",")
});
view.add(new view.Download(docx.deliver(bytes, "Rechnung.docx"), "Rechnung herunterladen", "Rechnung erzeugt"));

4) Bilder / Logos einsetzen

Für ein Bild (z. B. das Veranstalter-Logo auf einem Fortbildungsantrag) trägt die Vorlage einen Bild-Platzhalter mit @ ein: {{@logo}}. Im Skript wird der Wert mit docx.image(...) erzeugt — Quelle wie bei der Vorlage (byte[], fs-Datei, Base64), Größe optional:

var vorlage = fs["/vorlagen/einfacher-brief.docx"].readBytes();
var logo    = fs["/vorlagen/uni-logo.png"].readBytes();

var bytes = docx.render(vorlage, {
  logo: docx.image(logo, { width: 180, height: 48 }),   // Vorlage: {{@logo}}
  name: ctx.form.name,
  titel: ctx.form.titel
});

docx.image(quelle, { width, height, type }) — type ist optional ("PNG" Default, auch JPEG/GIF). Das Ergebnis setzt du einfach als Datenwert unter demselben Schlüssel wie der {{@…}}-Platzhalter.

Vorlagen-Syntax auf einen Blick

Zweck In der Word-Vorlage
Textfeld {{feld}}
Tabellen-Schleife (1:n) {{liste}} in der Zeile über der Wiederhol-Zeile; Zellen [feld]
Serienbrief (je Empfänger eine Seite) normale Brief-Vorlage + docx.mailMerge(vorlage, zeilen)

Beispiele installieren

Die Beispiel-Gruppe „Word-Dokumente & Serienbriefe (docx)" (System-Administration → Formular-Bausteine → Beispiele) legt drei Formulare an — Brief aus Formular, Einladungs- Serienbrief und Telefonrechnung — samt editierbaren Word-Vorlagen in der fs-Sandbox unter /vorlagen/.