zurück zur Dokumentation
[reference] formulas calculated-fields expressions logic

Formeln

Referenz der Formel-Engine: berechnete Felder, Validierungsregeln, bedingte Logik, Navigationspfade und eingebaute Funktionen.

veröffentlicht · aktualisiert

Überblick

Formeln verwenden eine kleine, deterministische DSL (kein JavaScript, Python oder SQL). Die Grammatik ist bewusst einfach, sicher und seiteneffekt-frei. Formeln werden einmal auf dem Server in einen AST geparst, gecached und zur Laufzeit erneut ausgewertet — manchmal auf dem Server, manchmal auf dem Client, manchmal direkt in die SQL-Abfrage eingebettet.

Sie erscheinen in:

  • Berechneten Spalten — Formel-Spalten (columnType: "F"), die Werte aus anderen Feldern berechnen
  • Generated Columns — physischen Spalten (columnType: "G"), die von der Datenbank automatisch gepflegt werden, für Aggregate über Kind-Sets (z. B. SUM([lines].[amount]))
  • Standardwerten — Anfangswerte für neue Datensätze setzen
  • ValidierungsregelnCHECK-Constraints, die Geschäftslogik durchsetzen
  • Aktions-BedingungencanExecute-Formeln, die steuern, wann Aktionen verfügbar sind
  • Sicherheit auf Zeilenebene — Filterausdrücke, die einschränken, welche Datensätze ein Anwender sehen kann
  • Anzeige-StringstoString-Vorlagen mit {field}-Platzhaltern

Syntax

Feldreferenzen

Greifen Sie auf die Felder des aktuellen Datensatzes mit Klammern zu:

[quantity]
[unit_price]
[account_name]

Arithmetik

[quantity] * [unit_price]
[subtotal] + [tax_amount]
[total] - [discount]
[amount] / [count]

String-Operationen

[first_name] + ' ' + [last_name]

Vergleich

dForge verwendet SQL-artige einzelne Gleichheitszeichen für Gleichheit, nicht ==.

[amount] > 1000
[status] = 'Active'
[end_date] >= [start_date]
[category] != 'Internal'

Andere Vergleichsoperatoren: <, <=, >, >=, IN, NOT IN, BETWEEN.

Logische Operatoren

[amount] > 1000 AND [status] = 'Approved'
[type] = 'A' OR [type] = 'B'
NOT [is_archived]

Bedingte Logik

IF([amount] > 10000, 'Enterprise', IF([amount] > 1000, 'Business', 'Standard'))
IF([discount_pct] > 0, [price] * (1 - [discount_pct] / 100), [price])

IN-Operator

[status] IN ('Open', 'In Progress', 'Pending')
[type] NOT IN ('Internal', 'Test')
[amount] BETWEEN 100 AND 1000

String-Prädikate

[name] CONTAINS 'corp'
[email] STARTS_WITH 'admin@'
[file] ENDS_WITH '.pdf'

Folgen Sie Referenzen, um auf Felder verbundener Entitäten zuzugreifen. Navigieren Sie über die virtuelle Referenz-Spalte (nicht über die FK-Spalte selbst):

[customer].[name]
[customer].[industry]
[order].[customer].[email]

Navigations-Formeln sind asynchron — sie erfordern einen separaten Datenabruf. Die Plattform erkennt Navigationsmuster automatisch und löst sie nach den synchronen Formeln auf. Das Frontend cached jede referenzierte Entität, um N+1-Abrufe zu vermeiden.

So funktioniert es

  1. [customer] — die Referenz-Spalte am aktuellen Datensatz
  2. . — folgt der Referenz zur Ziel-Entität
  3. [name] — liest das Feld vom referenzierten Datensatz

Mehrstufige Navigation wird unterstützt: [order].[customer].[name] folgt zwei Referenzen. Aktuell unterstützt der Client bis zu 2 Ebenen.

Synchron vs. asynchron

TypWann ausgewertetBeispiel
SynchronSofort beim Datenladen und bei Feldänderungen[quantity] * [unit_price]
AsynchronNach synchronen Formeln, erfordert Datenabruf[account_id].[industry]

Die Plattform klassifiziert Formeln automatisch danach, ob sie Navigationsmuster ([field].[field]) enthalten.

Abhängigkeitsverfolgung

Formeln deklarieren ihre Abhängigkeiten über Feldreferenzen. Wenn sich ein Feld ändert:

  1. Alle synchronen Formeln, die davon abhängen, werden sofort neu ausgewertet
  2. Alle asynchronen Formeln, die davon abhängen, lösen einen Datenabruf aus und werten neu aus, wenn die Daten ankommen

Formeln werden topologisch nach Abhängigkeiten sortiert, um die korrekte Auswertungsreihenfolge sicherzustellen.

Eingebaute Funktionen

Datumsfunktionen

FunktionBeschreibungBeispiel
TODAY()Aktuelles Datum[due_date] >= TODAY()
NOW()Aktuelles Datum/Zeit[created_at] > NOW()
WEEKDAY(date)Wochentag (1–7)WEEKDAY(TODAY())
STARTMONTH(date)Erster Tag des Monats des DatumsSTARTMONTH(NOW())
ENDMONTH(date)Letzter Tag des Monats des DatumsENDMONTH(NOW())
STARTQUARTER(date)Erster Tag des Quartals des DatumsSTARTQUARTER(NOW())
ENDQUARTER(date)Letzter Tag des Quartals des DatumsENDQUARTER(NOW())
STARTYEAR(date)Erster Tag des Jahres des DatumsSTARTYEAR(NOW())

Kontext-Funktionen

AusdruckBeschreibung
CURRENT_USER_ID()Die ID des aktuellen Anwenders
$[SettingName]Wert einer Modul-Einstellung (aufgelöst aus dem Ordner-Kontext — siehe unten)

Bedingte Funktionen

FunktionBeschreibungBeispiel
IF(cond, then, else)Bedingter WertIF([qty] > 100, 'Bulk', 'Standard')

Aggregat-Funktionen (Generated Columns)

Aggregat-Funktionen arbeiten auf einem Set-Feld (1:N-Kind-Sammlung) und benötigen columnType: "G" — eine physische Spalte, die von der Datenbank automatisch gepflegt wird.

FunktionBeschreibung
SUM([set].[field])Summe der Feldwerte über Kind-Datensätze
COUNT([set].[field])Anzahl der Kind-Datensätze
AVG([set].[field])Durchschnittswert
MIN([set].[field])Minimalwert
MAX([set].[field])Maximalwert

Mehrspaltige Ausdrücke innerhalb des Aggregats werden unterstützt:

SUM([lines].[quantity] * [lines].[unit_price])

Die Plattform wählt die richtige Strategie automatisch: Selbe-Zeile-Formeln werden zu PostgreSQL-GENERATED ALWAYS AS ... STORED-Spalten, während Set-Aggregate Datenbank-Trigger auf der Kind-Tabelle verwenden.

Einstellungs-Referenzen

Modul-Einstellungen sind konfigurierbare Werte, die pro Ordner überschrieben werden können. Referenzieren Sie sie in Formeln mit $[SettingName]:

[department_id] = $[DepartmentId]               -- Sicherheit auf Zeilenebene
[amount] <= $[ApprovalThreshold]                -- canExecute-Bedingung
$[DefaultWarehouseId]                            -- Standardwert

Wenn eine Formel eine Einstellung referenziert, sucht die Plattform sie im aktuellen Ordner, läuft den Ordnerbaum hinauf und fällt schließlich auf den Modul-Default zurück. Gibt NULL zurück, wenn nirgendwo ein Wert gesetzt ist.

Anwendungsbeispiele

Berechnete Summe

[quantity] * [unit_price] * (1 - [discount_pct] / 100)

Eine Formel-Spalte auf einer Rechnungsposition, die die Zeilensumme nach Rabatt berechnet.

Vollständiger Name

[first_name] + ' ' + [last_name]

Eine Formel-Spalte, die Namensteile zur Anzeige zusammenfügt.

Überfällig-Prüfung

[due_date] < TODAY() AND [status] != 'Paid'

Verwendet als Check-Constraint oder bedingte Formatierungsregel.

Phasenbasierte Betragsprognose

IF([stage] = 'Closed Won', [amount],
IF([stage] = 'Negotiation', [amount] * 0.7,
IF([stage] = 'Proposal', [amount] * 0.4,
[amount] * 0.1)))

Eine gewichtete Pipeline-Prognose basierend auf der Verkaufschancen-Phase.

Entitätsübergreifende Anzeige

[customer].[name] + ' (' + [customer].[industry] + ')'

Eine asynchrone Formel, die Kundenname und Branche aus dem verlinkten Kunden-Datensatz anzeigt.

toString-Vorlagen

Die toString-Eigenschaft auf einer Entität verwendet eine andere Syntax — {field}-Platzhalter innerhalb einer Text-Vorlage, keine Klammer-Ausdrücke:

"toString": "{invoice_number} — {customer}"
"toString": "{first_name} {last_name}"
"toString": "{product} x {quantity}"

Wenn ein Platzhalter ein Referenzfeld benennt (z. B. {customer}), löst die Plattform es rekursiv über die toString-Vorlage der referenzierten Entität auf. Das wird zur SQL-Zeit ausgewertet (über JOINs in einer einzigen Abfrage) und vermeidet N+1-Abrufe.

Formel-Spalten in Entitätsdefinitionen

Wenn Sie eine Formel-Spalte (Spaltentyp F) definieren, müssen Sie angeben:

PropertyPflichtBeschreibung
formulaJaDer Ausdruckstext
baseDatatypeCdJaErgebnis-Datentyp: string, number, date, bool
fieldTypeCdJaWie das Ergebnis dargestellt wird: text, number, currency, date usw.
columnTypeJaMuss "F" sein
flagsJaÜblicherweise "V" (visible) — Formel-Spalten sind nie editierbar

Check-Constraints

Entitäten können Check-Constraints mit Formel-Syntax definieren:

"constraints": {
  "positive_quantity": {
    "type": "check",
    "expression": "quantity > 0",
    "message": "Menge muss positiv sein"
  },
  "valid_date_range": {
    "type": "check",
    "expression": "end_date >= start_date",
    "message": "Enddatum muss am oder nach dem Startdatum liegen"
  }
}

Check-Constraints werden zu echten PostgreSQL-CHECK-Constraints (serverseitige Durchsetzung) und werden auch clientseitig über den Formel-Auswerter für sofortiges UI-Feedback ausgewertet. Wenn der Ausdruck false zurückgibt, wird das Speichern mit der angegebenen Fehlermeldung abgelehnt.

Wo Formeln laufen

KontextAusgewertet aufWarum
toString-AnzeigeServer / SQL-ZeitJOINs zu referenzierten Tabellen in einer Abfrage — kein N+1
canExecuteClient + ServerClient für sofortigen Button-Zustand; Server prüft erneut für Sicherheit
Einfache berechnete FelderClientSofortiges Feedback, wenn der Anwender ein Feld bearbeitet
StandardwerteServerBeim Insert angewendet
Sicherheit auf ZeilenebeneServerZur Abfragezeit durchgesetzt
Validierung / CHECKServer (und Client für UI-Feedback)Beim Speichern durchgesetzt
Generated-Column-AggregateDatenbankPostgreSQL-Trigger oder GENERATED ALWAYS AS-Spalte
/ war das hilfreich?

Stecken Sie irgendwo fest?
Sagen Sie es uns.

Wir lesen jede Nachricht und aktualisieren die Dokumentation auf Basis dessen, was Leser fragen. Der schnellste Weg, die Dokumentation zu verbessern, ist, uns zu schreiben.