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.
2. Ein Feld lesen¶
Alle Felder des Formulars stehen in ctx.form. Der Name hinter dem Punkt
ist der Feldname aus dem Formular-Editor.
3. Ein Feld setzen¶
Was du in ctx.form schreibst, erscheint danach im Formular.
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.
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.
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.
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.
9. Die Kurzschreibweise mit $¶
Statt ctx.form.menge darfst du $menge schreiben — das ist dasselbe.
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.