Ü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
- Validierungsregeln —
CHECK-Constraints, die Geschäftslogik durchsetzen - Aktions-Bedingungen —
canExecute-Formeln, die steuern, wann Aktionen verfügbar sind - Sicherheit auf Zeilenebene — Filterausdrücke, die einschränken, welche Datensätze ein Anwender sehen kann
- Anzeige-Strings —
toString-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'
Navigationspfade (asynchrone Formeln)
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
[customer]— die Referenz-Spalte am aktuellen Datensatz.— folgt der Referenz zur Ziel-Entität[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
| Typ | Wann ausgewertet | Beispiel |
|---|---|---|
| Synchron | Sofort beim Datenladen und bei Feldänderungen | [quantity] * [unit_price] |
| Asynchron | Nach 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:
- Alle synchronen Formeln, die davon abhängen, werden sofort neu ausgewertet
- 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
| Funktion | Beschreibung | Beispiel |
|---|---|---|
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 Datums | STARTMONTH(NOW()) |
ENDMONTH(date) | Letzter Tag des Monats des Datums | ENDMONTH(NOW()) |
STARTQUARTER(date) | Erster Tag des Quartals des Datums | STARTQUARTER(NOW()) |
ENDQUARTER(date) | Letzter Tag des Quartals des Datums | ENDQUARTER(NOW()) |
STARTYEAR(date) | Erster Tag des Jahres des Datums | STARTYEAR(NOW()) |
Kontext-Funktionen
| Ausdruck | Beschreibung |
|---|---|
CURRENT_USER_ID() | Die ID des aktuellen Anwenders |
$[SettingName] | Wert einer Modul-Einstellung (aufgelöst aus dem Ordner-Kontext — siehe unten) |
Bedingte Funktionen
| Funktion | Beschreibung | Beispiel |
|---|---|---|
IF(cond, then, else) | Bedingter Wert | IF([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.
| Funktion | Beschreibung |
|---|---|
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:
| Property | Pflicht | Beschreibung |
|---|---|---|
formula | Ja | Der Ausdruckstext |
baseDatatypeCd | Ja | Ergebnis-Datentyp: string, number, date, bool |
fieldTypeCd | Ja | Wie das Ergebnis dargestellt wird: text, number, currency, date usw. |
columnType | Ja | Muss "F" sein |
flags | Ja | Ü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
| Kontext | Ausgewertet auf | Warum |
|---|---|---|
toString-Anzeige | Server / SQL-Zeit | JOINs zu referenzierten Tabellen in einer Abfrage — kein N+1 |
canExecute | Client + Server | Client für sofortigen Button-Zustand; Server prüft erneut für Sicherheit |
| Einfache berechnete Felder | Client | Sofortiges Feedback, wenn der Anwender ein Feld bearbeitet |
| Standardwerte | Server | Beim Insert angewendet |
| Sicherheit auf Zeilenebene | Server | Zur Abfragezeit durchgesetzt |
Validierung / CHECK | Server (und Client für UI-Feedback) | Beim Speichern durchgesetzt |
| Generated-Column-Aggregate | Datenbank | PostgreSQL-Trigger oder GENERATED ALWAYS AS-Spalte |