Skip to content

Commit 021b7b2

Browse files
committed
Document Field Event Rules plugin in docusaurus
1 parent 43c6112 commit 021b7b2

3 files changed

Lines changed: 306 additions & 0 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"label": "Available Plugins",
3+
"position": 55,
4+
"link": {
5+
"type": "generated-index",
6+
"description": "Community and third-party plugins available for iDempiere."
7+
}
8+
}
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
5+
# Field Event Rules
6+
7+
- **Maintainer:** Diego Ruiz - BX Service GmbH
8+
- **Sponsor:** [Energy Kinetics, Inc.](https://energykinetics.com/)
9+
- **Status:** Beta, not tested in production
10+
- **License:** GPLv2
11+
- **Sources:** [GitHub](https://github.com/bxservice/de.bxservice.fieldEventRules)
12+
13+
Field Event Rules let you define "when this field changes, do that" logic directly inside the iDempiere Application Dictionary without writing Java code, installing plugins, or touching an IDE.
14+
15+
Think of it as a lightweight form event system: you attach rules to fields or columns, describe what should happen in plain SQL or a simple expression, and the system takes care of executing them both in the UI and when records are saved through any channel.
16+
17+
## What you can do with it
18+
19+
- Auto-fill fields: when a user picks a Business Partner, automatically populate the Payment Term, Price List, or any other field from a lookup.
20+
- Copy values between fields: when an Org is selected, default the Warehouse from that org's configuration.
21+
- Conditional defaults: set a field only when it is currently blank, leaving intentional values untouched.
22+
- Validate data: show an error or warning when a value breaks a business rule, either immediately when the field is changed or when the record is saved.
23+
24+
For rules with a Save trigger, this applies regardless of how the record was created: a window, a background process, a data import, or the API. UI triggers only fire in the browser when a user interacts with the field. For a detailed comparison, see [Event handlers vs callouts](/docs/basic-development/plugin-development/plugin-eventhandler#event-handlers-vs-callouts).
25+
26+
## How it works conceptually
27+
28+
A Field Event Rule sits between a field (or column) and a piece of logic you define. It has three parts:
29+
30+
**1. Trigger** - when does it fire?
31+
32+
- *On field change* (UI / callout): fires immediately in the UI when a user leaves the field, the same moment a callout would. Does not fire on background saves.
33+
- *On save* (model): fires when the record is being saved, regardless of how it was created. Runs after the user clicks Save, or when a process/import writes the record.
34+
- *Both*: fires at both moments, so the UI stays responsive and data consistency is guaranteed for non-UI operations too.
35+
36+
**2. Condition** *(optional)* - should it fire this time?
37+
38+
An optional guard expression. If it evaluates to false, the rule is skipped entirely. Useful for rules that only apply in certain situations (e.g. only on Sales Orders, only when the amount exceeds a threshold).
39+
40+
**3. Actions** - what happens?
41+
42+
One or more ordered steps. Each action sets a field to a computed value, clears it, or validates it.
43+
44+
## Where to configure rules
45+
46+
Open the **Field Event Rules** window from the Application Dictionary menu (System tenant or your tenant, depending on your access).
47+
48+
:::info
49+
50+
Rules configured in the System tenant apply to all tenants in the system. You can also configure tenant-specific rules that only apply to that tenant's records.
51+
52+
:::
53+
54+
:::warning
55+
56+
This window supports SQL statements and is marked as advanced. Only roles with the **Advanced** flag enabled can access it.
57+
58+
:::
59+
60+
![Field Event Rule Window](/img/available-plugins/field-event-rules/FieldEventRuleEngineWindow.png)
61+
62+
Each rule record has:
63+
64+
| Field | What it does |
65+
|---|---|
66+
| Name | A label for the rule, shown in lists |
67+
| Window / Tab / Field | Scope the rule to a specific place in the UI. Leave blank to apply model-wide. |
68+
| Table / Column | Attach the rule at the column level so it fires for any window using that column. |
69+
| Trigger | On field change / On save / Both |
70+
| Execution scope | UI only / Model only / Both |
71+
| Condition | Optional guard (see Conditions below) |
72+
| Rule type | SET (data consequence) or VALIDATE |
73+
| Active | Enable or disable without deleting |
74+
| Sequence | Controls execution order when multiple rules exist on the same field |
75+
76+
77+
Below each rule you define its **Actions** (what to do) and optionally **Parameters** (named values to simplify your expressions).
78+
79+
## Scoping a rule
80+
81+
Rules can be scoped at two levels and you can combine them.
82+
83+
**Field-level scope** (UI-specific): attach a rule to a particular field in a specific Window + Tab. The rule only fires when a user interacts with that field in that window. Use this when the logic is UI-specific or when the same column behaves differently in different windows.
84+
85+
**Column-level scope** (model-wide): attach a rule to a column on a table. The rule fires whenever any record on that table is saved, regardless of which window was used, or even if no window was involved. Use this for data integrity rules that must hold universally.
86+
87+
If you want a rule to do both - show responsive feedback in the UI and enforce the consequence on save - set the Trigger to "Both" and the Execution Scope to "Both". The engine avoids double-applying the same value when the UI path already set it.
88+
89+
:::warning
90+
91+
If you leave the Window (and Tab / Field) blank, the rule applies to every window that uses the configured table/column. Use this when the logic should be universal rather than window-specific.
92+
93+
:::
94+
95+
## Conditions
96+
97+
The Condition field is an optional guard that must be true for the rule to execute. Two formats are accepted.
98+
99+
### Context expression
100+
101+
Uses iDempiere's standard variable syntax. Variables in `@Brackets@` are resolved from the current record and session context.
102+
103+
```
104+
@IsSoTrx@=Y
105+
@GrandTotal@ > 100
106+
@IsSoTrx@=Y & @GrandTotal@ > 100
107+
@C_BPartner_ID@ > 0 | @IsAnonymous@=Y
108+
```
109+
110+
Operators: `&` means AND, `|` means OR. Each clause compares a `@Variable@` to a value.
111+
112+
### SQL WHERE clause
113+
114+
Starts with `@SQL=` followed by a SQL WHERE clause. If it returns any row (or evaluates to true), the rule proceeds.
115+
116+
```sql
117+
@SQL=EXISTS (
118+
SELECT 1 FROM C_BPartner bp
119+
WHERE bp.C_BPartner_ID = @C_BPartner_ID@
120+
AND bp.IsCustomer = 'Y'
121+
)
122+
```
123+
124+
```sql
125+
@SQL=@GrandTotal@ > (
126+
SELECT SO_CreditLimit
127+
FROM C_BPartner
128+
WHERE C_BPartner_ID = @C_BPartner_ID@
129+
)
130+
```
131+
132+
If the Condition is left blank, the rule always fires (subject to Trigger and Active).
133+
134+
## Actions
135+
136+
Each rule has one or more actions, executed in sequence order. An action either sets a value on a field or validates a condition.
137+
138+
### Action types
139+
140+
**SET** - always writes the computed value to the target column, overwriting whatever is there.
141+
142+
**SET IF BLANK** - writes the computed value only if the target column is currently empty. Useful for defaults that should not override intentional entries.
143+
144+
**CLEAR** - sets the target column to null. No expression needed.
145+
146+
### Value expressions
147+
148+
The expression that produces the new value can be written in two ways.
149+
150+
**SQL scalar subquery** - any expression starting with `SELECT`. Must return a single value (one row, one column).
151+
152+
```sql
153+
@SQL=SELECT p.PriceStd
154+
FROM M_ProductPrice p
155+
WHERE p.M_Product_ID = @M_Product_ID@
156+
AND p.M_PriceList_Version_ID = @M_PriceList_Version_ID@
157+
AND p.AD_Client_ID IN (0, @#AD_Client_ID@)
158+
FETCH FIRST 1 ROW ONLY
159+
```
160+
161+
**Arithmetic / inline expression** - for simple calculations without a full query.
162+
163+
```
164+
@QtyOrdered@ * @PriceActual@
165+
```
166+
167+
```
168+
@SQL=CASE WHEN @IsSOTrx@ = 'Y' THEN @PriceList@ ELSE @PriceStd@ END from dual
169+
```
170+
171+
### Variable substitution in expressions
172+
173+
Use `@ColumnName@` to reference any value from the current record. The engine resolves these before executing the SQL.
174+
175+
| Syntax | Resolves to |
176+
|---|---|
177+
| `@ColumnName@` | Current value of that column in the record |
178+
| `@Table.ColumnName@` | Value of a column on a related table, resolved via the current record's foreign key (e.g. `@C_BPartner.Description@` reads the Description from the linked Business Partner) |
179+
| `@#Variable@` | Global system context (e.g. `@#AD_Client_ID@`) |
180+
| `@$Variable@` | Window-level context |
181+
182+
If a variable cannot be resolved, it is substituted with `NULL` and a warning is logged. The rule continues rather than failing hard.
183+
184+
:::warning
185+
186+
When you save a rule configuration, the system performs a dry run to detect malformed SQL before the rule can affect real data. Fix any reported syntax errors before the rule will activate.
187+
188+
:::
189+
190+
## Examples
191+
192+
### Example 1 - Fill description and check credit status from Business Partner
193+
194+
When the Business Partner is changed on a Sales Order, copy the partner's description into the order's Description field and write a credit status indicator into PO Reference.
195+
196+
| Field | Value |
197+
|---|---|
198+
| Window | Sales Order |
199+
| Tab | Order |
200+
| Field | Business Partner |
201+
| Trigger | On field change (UI) |
202+
| Rule Type | SET |
203+
204+
Action 1 - copy the partner description:
205+
206+
| Field | Value |
207+
|---|---|
208+
| Type | SET |
209+
| Target | `Description` |
210+
| Expression | `@C_BPartner.Description@` |
211+
212+
Action 2 - write credit status into PO Reference:
213+
214+
| Field | Value |
215+
|---|---|
216+
| Type | SET |
217+
| Target | `POReference` |
218+
| Expression | `SELECT CASE WHEN @GrandTotal@ <= SO_CreditLimit THEN 'OK' ELSE 'Over the limit' END FROM C_BPartner WHERE C_BPartner_ID = @C_BPartner_ID@` |
219+
220+
### Example 2 - Validate credit limit on Business Partner change
221+
222+
Warn the user immediately when the order's Grand Total already exceeds the selected Business Partner's credit limit. The Condition pre-checks the limit so the VALIDATE action only fires when there is actually a problem.
223+
224+
| Field | Value |
225+
|---|---|
226+
| Window | Sales Order |
227+
| Tab | Order |
228+
| Field | Business Partner |
229+
| Trigger | On field change (UI) |
230+
| Condition | `@SQL=(SELECT bp.SO_CreditLimit FROM C_BPartner bp WHERE bp.C_BPartner_ID = @C_BPartner_ID@) < @GrandTotal@` |
231+
| Rule Type | VALIDATE |
232+
233+
Action:
234+
235+
| Field | Value |
236+
|---|---|
237+
| Type | VALIDATE |
238+
| Expression | (evaluates to `N` - the Condition already ensures this fires only on violation) |
239+
| Message | Grand Total exceeds the credit limit for this Business Partner. |
240+
241+
### Example 3 - Auto-set Drop Ship flag based on delivery region
242+
243+
When the Partner Location is selected on a Sales Order, automatically enable Drop Ship if the delivery address is in New Jersey.
244+
245+
| Field | Value |
246+
|---|---|
247+
| Window | Sales Order |
248+
| Tab | Order |
249+
| Field | Partner Location |
250+
| Trigger | On field change (UI) |
251+
| Condition | `@SQL=(SELECT l.RegionName FROM C_Location l JOIN C_BPartner_Location cbl ON l.C_Location_ID = cbl.C_Location_ID WHERE cbl.C_BPartner_Location_ID = @C_BPartner_Location_ID@) = 'NJ'` |
252+
| Rule Type | SET |
253+
254+
Action:
255+
256+
| Field | Value |
257+
|---|---|
258+
| Type | SET |
259+
| Target | `IsDropShip` |
260+
| Expression | `'Y'` |
261+
262+
## Multiple rules on the same field
263+
264+
You can define several rules on the same field or column. They execute in Sequence Number order. Each rule sees the values already written by the previous ones, so a later rule can depend on what an earlier rule set.
265+
266+
If a rule has **Stop on Error** enabled and produces a blocking validation error, execution stops and the remaining rules are skipped.
267+
268+
System-level rules (configured in the System tenant) always execute before tenant-level rules. Tenant rules can build on top of or further refine the results of system rules.
269+
270+
## Validation rules in detail
271+
272+
When Rule Type is VALIDATE, the action expression must evaluate to `'Y'` (case-insensitive) for the validation to pass. Any other result (including `NULL`) is treated as a failure.
273+
274+
**Error level** controls what happens on failure:
275+
276+
- **Error**: blocks the operation. On field change (UI), a popup is shown and the field is flagged. On save (model), an exception is thrown and the record cannot be saved until the condition is satisfied.
277+
- **Warning**: allows the operation to proceed, but shows a message the user must acknowledge before continuing.
278+
279+
Validation rules work at both the UI (immediate feedback on field change) and model level (enforced on save regardless of channel).
280+
281+
## What rules cannot do
282+
283+
Rules are intentionally limited to keep them safe for implementer-level configuration.
284+
285+
- Expressions are read-only. `INSERT`, `UPDATE`, `DELETE`, `DROP`, and similar statements are rejected.
286+
- Rules always run in the context of the current tenant. Cross-tenant data access is not possible.
287+
- SQL expressions are checked for structure only during configuration. They are not executed against real data until a record is actually being edited or saved.
288+
- Rules cannot invoke processes, send notifications, or trigger document actions. Those use cases require conventional plugin development.
289+
290+
## Deploying rules across environments
291+
292+
Because rules are stored as Application Dictionary records, they are fully portable using iDempiere's standard **2Pack** (Package In / Package Out) mechanism. The recommended workflow for moving rules from development to production is:
293+
294+
1. Configure and test rules in the development environment.
295+
2. Export as a 2Pack XML file.
296+
3. Import in test, then production.
297+
298+
No server restart is required when a new rule is saved. The system registers it immediately.
92.9 KB
Loading

0 commit comments

Comments
 (0)