Zum Inhalt

Erste Schritte

Hier lernst du das Handwerkszeug für alle anderen Seiten: Formularfelder lesen und setzen, rechnen, mit Texten und Datumswerten arbeiten und Fehler melden. Die Beispiele benutzen Felder wie name, vorname, betrag, menge oder email — nimm stattdessen die Namen deiner Felder.

Ausführliches dazu: ctx und kc-Helfer.

Einsteiger

1. Etwas ins Protokoll schreiben

log.info gibt eine Zeile aus. So prüfst du, ob dein Skript überhaupt läuft und welche Werte gerade da sind.

log.info("Mein erstes Skript läuft!");

2. Ein Feld lesen

Alle Felder des Formulars stehen in ctx.form. Der Name hinter dem Punkt ist der Feldname aus dem Formular-Editor.

var name = ctx.form.name;
log.info("Im Feld steht: " + name);

3. Ein Feld setzen

Was du in ctx.form schreibst, erscheint danach im Formular.

ctx.form.ergebnis = "Vom Skript ausgefüllt";
log.info("Feld gesetzt");

4. Zwei Felder zusammenfügen

Texte verbindest du mit +. Das Leerzeichen in der Mitte ist ein eigener kleiner Text.

ctx.form.ergebnis = ctx.form.vorname + " " + ctx.form.name;
log.info(ctx.form.ergebnis);   // Anna Meyer

5. Mit Zahlen rechnen

Eingaben kommen als Text an — auch aus Zahlenfeldern. kc.num macht daraus eine Zahl und versteht dabei auch 2,50 oder 1.234,56.

var summe = $menge.num * $preis.num;
log.info("Summe: " + summe);   // Summe: 10

Ohne kc.num würde "4" + "2,50" einfach zu "42,50" zusammengeklebt.

6. Auf zwei Nachkommastellen runden

toFixed(2) rundet und liefert einen Text mit genau zwei Stellen.

var betrag = $betrag.num;
log.info(betrag.toFixed(2));   // 20.00

Das „20.00" ist allerdings englisch geschrieben. Soll der Betrag so aussehen, wie ihn jemand auf einem Beleg erwartet, nimm .numFormat(…) — das rundet, setzt das Komma, gruppiert die Tausender und hängt die Währung an, alles in einem Aufruf:

log.info($betrag.numFormat(null, "€", 12, 2, ",", ".", 3));   // 12.345,76 €
log.info($betrag.numFormat("ca.", "EUR", 0, 0));              // ca. 12346 EUR

Die Reihenfolge ist prefix, suffix, stellenInsgesamt, anzNachkommastellen, dezimaltrenner, separatorZeichen, separatorBlockabstand. Alles ist optional, null überspringt eine Position — alle Einzelheiten.

7. Prüfen, ob ein Feld leer ist

.isEmpty ist wahr bei „nichts eingegeben", bei reinen Leerzeichen und bei einem Feld, das noch nie ausgefüllt wurde (NULL in der Datenbank). Ohne Klammern — es ist eine Eigenschaft, keine Methode.

if ($email.isEmpty) {
  log.info("Das Feld E-Mail ist leer.");
}

0 und false sind nicht leer: eine eingetragene Menge von 0 ist ein gültiger Wert. Genau deshalb reicht if ($menge) nicht.

if ($menge.isEmpty) {
  log.info("Keine Menge angegeben.");
} else {
  log.info("Menge ist eingetragen: " + $menge.num);   // auch bei 0
}

Außerhalb von Formularfeldern — für Zwischenwerte, Datenbankzeilen oder in Bedingungen — macht kc.isEmpty(...) dasselbe.

8. Einen Fehler am Feld anzeigen

ctx.addError(feld, text) zeigt eine rote Meldung unter dem Feld. Im onValidate-Hook verhindert das außerdem das Speichern.

if (kc.isEmpty(ctx.form.email)) {
  ctx.addError("email", "Bitte eine E-Mail-Adresse eingeben.");
}

9. Die Kurzschreibweise mit $

Statt ctx.form.menge darfst du $menge schreiben — das ist dasselbe.

$ergebnis = $menge.num * $preis.num;
log.info($ergebnis);   // 15

Steht der Feldname erst zur Laufzeit fest — typisch bei durchnummerierten Feldern beitrag1, beitrag2, … — nimm eckige Klammern:

var summe = 0;
for (var i = 1; i <= 2; i++) {
  summe += $["wert" + i].num;
}
log.info("Summe: " + summe);   // Summe: 300.5

Einen Namen, den es nicht gibt, meldet $[...] sofort — genau wie $tippfehler einen ReferenceError gibt. Wer erst nachsehen will, fragt mit if ("wert3" in $).

10. Wer ist gerade angemeldet?

ctx.user kennt den angemeldeten Benutzer.

log.info("Angemeldet: " + ctx.user.firstName + " " + ctx.user.lastName
       + " (" + ctx.user.name + ")");

Fortgeschritten

11. Das heutige Datum schön ausgeben

format wandelt ein Datum in Text um. dd = Tag, MM = Monat, yyyy = Jahr, HH:mm = Uhrzeit.

var heute = new Date();
log.info("Heute ist der " + heute.format("dd.MM.yyyy"));
log.info("Mit Wochentag: " + heute.format("EEEE, d. MMMM yyyy", "de_DE"));

12. Ein Datum aus einem Text lesen und damit rechnen

Date.fromString(text, muster) liest ein Datum. Danach kannst du rechnen, z. B. wie viele Tage es noch bis zu einem Termin sind.

var termin = Date.fromString("24.12.2030", "dd.MM.yyyy");
var heute  = new Date();
var tage   = Math.ceil((termin - heute) / (1000 * 60 * 60 * 24));
log.info("Noch " + tage + " Tage bis zum Termin.");

Zwei Daten voneinander abgezogen ergeben Millisekunden; durch 1000 * 60 * 60 * 24 geteilt sind es Tage.

13. Texte aufräumen

Leerzeichen entfernen, Groß- und Kleinschreibung ändern, Zeichen ersetzen.

var roh = ctx.form.name;              // "  müller-lüdenscheidt  "
var sauber = roh.trim();              // Leerzeichen vorne/hinten weg
log.info(sauber.toUpperCase());       // MÜLLER-LÜDENSCHEIDT
log.info(sauber.replace("-", " ").toUpperCase());   // MÜLLER LÜDENSCHEIDT
log.info("Länge: " + sauber.length);

14. Eine E-Mail-Adresse prüfen

Ein regulärer Ausdruck beschreibt, wie ein Text aussehen muss. test(...) sagt true oder false.

var muster = /^[^@\s]+@[^@\s]+\.[a-z]{2,}$/i;
if (!muster.test(ctx.form.email)) {
  ctx.addError("email", "Das sieht nicht wie eine E-Mail-Adresse aus.");
}

15. Mehrere Bedingungen verbinden

&& heißt „und", || heißt „oder", ! heißt „nicht".

var betrag = $betrag.num;
var ausland = ctx.form.land !== "DE";

if (betrag > 1000 && ausland) {
  log.info("Freigabe nötig: großer Betrag im Ausland");
} else if (betrag > 1000 || ausland) {
  log.info("Bitte kurz prüfen");
} else {
  log.info("Alles in Ordnung");
}

16. Eine Auswahl mit switch auswerten

Praktisch, wenn ein Feld mehrere feste Werte haben kann.

var mwst;
switch (ctx.form.land) {
  case "DE": mwst = 19; break;
  case "AT": mwst = 20; break;
  case "CH": mwst = 8.1; break;
  default:   mwst = 0;
}
log.info("Mehrwertsteuer: " + mwst + " %");

17. Positionen einer Tabelle zusammenzählen

Eine Detail-Tabelle ist eine Liste von Zeilen. Mit einer Schleife gehst du jede Zeile durch.

var positionen = ctx.form.positionen || [];
var gesamt = 0;
for (var i = 0; i < positionen.length; i++) {
  var p = positionen[i];
  gesamt += kc.num(p.menge) * kc.num(p.preis);
}
log.info("Gesamt: " + gesamt.toFixed(2));   // Gesamt: 8.60

|| [] sorgt dafür, dass das Skript auch bei leerer Tabelle läuft.

18. Eine eigene Funktion schreiben

Eine Funktion ist ein wiederverwendbarer Baustein mit einem Namen.

function brutto(netto, steuersatz) {
  return netto * (1 + steuersatz / 100);
}

log.info(brutto(100, 19).toFixed(2));   // 119.00
log.info(brutto(50, 7).toFixed(2));     // 53.50

19. Prüfen, ob jemand in einer Gruppe ist

ctx.user.roles enthält die Gruppen des Benutzers, ctx.user.permissions seine Berechtigungen.

var gruppen = ctx.user.roles;
log.info("Gruppen: " + gruppen.join(", "));

if (gruppen.indexOf("ADMIN") >= 0) {
  log.info("Du bist Administrator.");
}

20. Fehler kurz an ein Feld hängen

field.<name>.error(...) ist die Kurzform von ctx.addError. hardError blockiert das Speichern in jedem Fall.

if ($menge.num > 100) {
  field.menge.error("Mehr als 100 Stück bitte telefonisch bestellen.");
}
if ($betrag.num > 10000) {
  field.betrag.hardError("Beträge über 10.000 € sind nicht erlaubt.");
}

Profi

21. Eine komplette Rechnung berechnen

Positionen summieren, Mengenrabatt abziehen, Mehrwertsteuer aufschlagen und alles kaufmännisch runden. Die Ergebnisse landen in Formularfeldern.

function runden(x) { return Math.round(x * 100) / 100; }

var netto = 0;
(ctx.form.positionen || []).forEach(function (p) {
  netto += kc.num(p.menge) * kc.num(p.preis);
});

// Rabattstaffel: ab 200 € 3 %, ab 1000 € 5 %
var rabattProzent = netto >= 1000 ? 5 : (netto >= 200 ? 3 : 0);
var rabatt = runden(netto * rabattProzent / 100);
var nachRabatt = netto - rabatt;
var mwst = runden(nachRabatt * 0.19);

ctx.form.netto  = runden(nachRabatt);
ctx.form.mwst   = mwst;
ctx.form.brutto = runden(nachRabatt + mwst);
log.info("netto=" + ctx.form.netto.toFixed(2) + " mwst=" + mwst.toFixed(2)
       + " brutto=" + ctx.form.brutto.toFixed(2));   // netto=237.65 mwst=45.15 brutto=282.80

Math.round(x * 100) / 100 rundet auf Cent. Rechne mit Zahlen und runde erst am Ende jedes Schritts — so gibt es keine Cent-Differenzen.

22. Eine IBAN prüfen

Die IBAN hat eine eingebaute Prüfsumme (Modulo 97). Dieses Beispiel findet Tippfehler, bevor gespeichert wird.

function ibanGueltig(iban) {
  var s = String(iban || "").replace(/\s/g, "").toUpperCase();
  if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{10,30}$/.test(s)) return false;
  // Länderkennung und Prüfziffer ans Ende, Buchstaben in Zahlen (A=10 … Z=35)
  var umgestellt = s.slice(4) + s.slice(0, 4);
  var rest = 0;
  for (var i = 0; i < umgestellt.length; i++) {
    var c = umgestellt.charAt(i);
    var ziffern = /[A-Z]/.test(c) ? String(c.charCodeAt(0) - 55) : c;
    for (var j = 0; j < ziffern.length; j++) {
      rest = (rest * 10 + Number(ziffern.charAt(j))) % 97;
    }
  }
  return rest === 1;
}

if (ibanGueltig(ctx.form.ergebnis)) {
  log.info("IBAN gültig");
} else {
  ctx.addError("ergebnis", "Die IBAN ist nicht gültig. Bitte prüfen.");
}

Die Zahl wird Stück für Stück durch 97 geteilt, weil eine IBAN zu lang für eine normale Zahl ist.

23. Volljährigkeit aus dem Geburtsdatum prüfen

Das Alter wird exakt berechnet — ein Geburtstag „später im Jahr" zählt noch nicht.

function alter(geburt, stichtag) {
  var jahre = stichtag.getFullYear() - geburt.getFullYear();
  var hatteGeburtstag = stichtag.getMonth() > geburt.getMonth()
    || (stichtag.getMonth() === geburt.getMonth() && stichtag.getDate() >= geburt.getDate());
  return hatteGeburtstag ? jahre : jahre - 1;
}

if (!kc.isEmpty(ctx.form.datum)) {
  var geburt = Date.fromString(ctx.form.datum, "yyyy-MM-dd");   // Datumsfelder kommen so an
  var a = alter(geburt, new Date());
  log.info("Alter: " + a);
  if (a < 18) ctx.addError("datum", "Antragstellende müssen volljährig sein.");
}

Datumsfelder liefern ihren Wert als Text im Format JJJJ-MM-TT.

24. Arbeitstage zwischen zwei Daten zählen

Samstage und Sonntage werden übersprungen. Feiertage kannst du als Liste ergänzen.

function arbeitstage(von, bis, feiertage) {
  var tage = 0;
  var d = new Date(von.getTime());
  while (d <= bis) {
    var wochentag = d.getDay();              // 0 = Sonntag, 6 = Samstag
    var text = d.format("yyyy-MM-dd");
    if (wochentag !== 0 && wochentag !== 6 && feiertage.indexOf(text) < 0) tage++;
    d.setDate(d.getDate() + 1);
  }
  return tage;
}

var von = Date.fromString("01.12.2026", "dd.MM.yyyy");
var bis = Date.fromString("31.12.2026", "dd.MM.yyyy");
log.info("Arbeitstage: " + arbeitstage(von, bis, ["2026-12-25"]));   // Arbeitstage: 22

25. Mehrere Prüfungen sammeln und nur dann weitermachen

Alle Fehler auf einmal melden ist freundlicher als einen nach dem anderen. ctx.isValid() sagt am Ende, ob alles gut war.

function pflicht(feld, text) {
  if (kc.isEmpty(ctx.form[feld])) ctx.addError(feld, text + " fehlt.");
}

pflicht("vorname", "Der Vorname");
pflicht("email", "Die E-Mail-Adresse");
if (!kc.isEmpty(ctx.form.email) && ctx.form.email.indexOf("@") < 0) {
  ctx.addError("email", "Die E-Mail-Adresse braucht ein @.");
}

// Telefonnummer vereinheitlichen: nur Ziffern und ein führendes +
ctx.form.telefon = String(ctx.form.telefon || "").replace(/[^0-9+]/g, "");

if (ctx.isValid()) {
  log.info("Alles gut – weiter geht's");
} else {
  log.info(ctx.errors.length + " Problem(e) gefunden");
}

26. Einstellbare Werte aus den Properties nutzen

Grenzwerte gehören nicht ins Skript, sondern in die Properties. Fehlt der Wert, nimmt das Skript einen Standard.

var grenze = ctx.params && ctx.params.freigabegrenze
  ? kc.num(ctx.params.freigabegrenze)
  : 500;                                   // Standard, wenn nichts gepflegt ist
log.info("Grenze: " + grenze);

if ($betrag.num > grenze) {
  log.info("Dieser Antrag braucht eine Freigabe.");
}

27. Texte in der Sprache des Benutzers

ctx.lang ist "de", "en" oder "sv". Ein kleines Wörterbuch-Objekt hält die Texte zusammen.

var texte = {
  de: "Vielen Dank für Ihre Bestellung!",
  en: "Thank you for your order!",
  sv: "Tack för din beställning!"
};
var gruss = texte[ctx.lang] || texte.de;   // unbekannte Sprache → Deutsch
log.info(gruss);