The module adds configurable banners for backend form views. Define rules per model (and optionally per view) to show context-aware alerts with a chosen severity (info/warning/danger).
Messages can be plain text with \${placeholders} or fully custom HTML; visibility, severity, and values are computed server-side via a safe Python expression.
Banners are injected just before or after a target node (default: //sheet) and refresh on form load/save/reload.
auto_awesome What this module does
AI-generated from the code · Odoo 19.0Lets administrators define rules that display configurable alert banners (info, warning or danger) on backend form views, with messages computed from the record's data and refreshed live as the user edits.
This module adds a new configuration screen, "Form Banner Rules", where an administrator creates rules that place coloured alert banners inside standard Odoo form views.
For each rule you choose the target model, optionally specific form views, where the banner is inserted (by default just before the main form area, or after it), and the default severity: info, warning or danger.
- The message can be plain text with ${placeholders} or raw HTML.
- A small Python expression, evaluated on the server, decides whether the banner is visible, its severity, the placeholder values and/or the final HTML. It can use the current record, the current user, the environment, date helpers, rounding helpers and a helper that builds a link to a record's form.
- Optional "Trigger Fields" make the banner recompute live while the user edits those fields, including on unsaved new records.
- Banners refresh when a form is opened, saved, discarded, duplicated or when the user moves to another record.
Rules are ordered and can be archived. The rule list can be searched and grouped by model or severity. Only system administrators can create or change rules; other internal users, portal users and public users can only read them. The module touches the generic form view loading for all models and the web.form.banner.rule model.
Depends on
Menus it adds
Models and fields (2)
- base · extends an existing model
- web.form.banner.rule · new modelname, active, message, model_id, position, sequence, severity (Default Severity), view_ids (Views), model_name (Model Name), target_xpath (Target XPath), message_is_html (HTML), trigger_field_ids (Trigger Fields), message_value_code
Text generated by AI (DeepSeek) from reading the Odoo 19.0 code on GitHub; it may contain mistakes, so check the code before deciding. View the code on GitHub
Availability by version
Reviewed: 2026-11-03Confirmation status: 3 confirmed, 6 with no information of 9 versions.
Sources · Odoo 16.0
Confirmed on 2026-11-03 in branch 16.0 of OCA/web.
terminal Installation and usage
OCA documentation · Odoo 19.0Usage
Source · Odoo 19.0- Go to Settings \> Technical \> User Interface \> Form Banner Rules and create a rule.
- Choose Model, select Trigger Fields (optional), set Default Severity, select Views (optional), update Target XPath (insertion point) and Position, and configure the message.
- Save. Open any matching form record—the banner will appear and auto-refresh after load/save/reload.
Usage of message fields:
- Message (message): Text shown in the banner. Supports \${placeholders} filled from values returned by message_value_code. Ignored if message_value_code returns an html value.
- HTML (message_is_html): If enabled, the message string is rendered as HTML; otherwise it's treated as plain text.
- Message Value Code (message_value_code): Safe Python expression evaluated per record. Return a dict such as {"visible": True, "severity": "warning", "values": {"name": record.name}}. Use either message or html (from this code), not both. Several evaluation context variables are available.
Evaluation context variables available in Message Value Code:
env: Odoo environment for ORM access.user: Current user (env.user).ctx: Copy of the current context (dict(env.context)).record: Current record (the form's record).draft: The persisted field values of the ORM record (before applying the current form's unsaved changes) + the current unsaved changes on trigger fields. Should be used instead of record when your rule is triggered dynamically by an update to a trigger field. It doesn't include any values from complex fields (one2many/reference, etc).record_id: Integer id of the record being edited, or False if the form is creating a new record.model: Shortcut to the current model (env[record._name]).url_for(obj): Helper that returns a backend form URL forobj.context_today(ts=None): User-timezone “today” (date) for reliable date comparisons.- time,
datetime: Standard Python time/datetime modules. dateutil: { "parser": dateutil.parser, "relativedelta": dateutil.relativedelta }timezone: pytz.timezone for TZ handling.- float_compare, float_is_zero,
float_round: Odoo float utils for precision-safe comparisons/rounding.
All of the above are injected by the module to the safe_eval locals.
Trigger Fields
Trigger Fields is an optional list of model fields that, when changed in the open form, cause the banner to recompute live. If left empty, the banner does not auto-refresh as the user edits the form.
When a trigger fires, the module sends the current draft values to the server, sanitizes them, builds an evaluation record, and re-runs your message_value_code.
You should use draft instead of record to access the current form values if your rule is triggered based on an update to a trigger field.
Message setting examples:
A) Missing email on contact (warning)
- Model: res.partner
- Message: This contact has no email.
- Message Value Code:
{"visible": not bool(record.email)}B) Show partner comment if available
- Model: purchase.order
- Message: Vendor Comments: \${comment}
- Message Value Code (single expression):
{
"visible": bool(record.partner_id.comment),
"values": {"comment": record.partner_id.comment},
}It is also possible to use "convenience placeholders" without an explicit values key:
{
"visible": bool(record.partner_id.comment),
"comment": record.partner_id.comment,
}C) High-value sale order (dynamic severity)
- Model: sale.order
- Message: High-value order: \${amount_total}
- Message Value Code:
{
"visible": record.amount_total >= 30000,
"severity": "danger" if record.amount_total >= 100000 else "warning",
"values": {"amount_total": record.amount_total},
}D) Quotation past validity date
- Model: sale.order
- Message: This quotation is past its validity date (\${validity_date}).
- Message Value Code:
{
"visible": bool(record.validity_date and context_today() > record.validity_date and record.state in ["draft", "sent"]),
"values": {"validity_date": record.validity_date},
}E) Pending activities on a task (uses env)
- Model: project.task
- Message: There are \${cnt} pending activities.
- Message Value Code (multi-line with result):
cnt = env["mail.activity"].search_count([("res_model","=",record._name),("res_id","=",record.id)])
result = {"visible": cnt > 0, "values": {"cnt": cnt}}F) Product is missing internal reference (uses trigger fields)
- Model: product.template
- Trigger Fields: default_code
- Message: Make sure to set an internal reference!
- Message Value Code:
{"visible": not bool(draft.default_code)}G) HTML banner linking to the customer's last sales order (uses trigger fields)
- Model: sale.order
- Trigger Fields: partner_id
- Message: (leave blank; html provided by Message Value Code)
- Message Value Code (multi-line with result):
domain = [("partner_id", "=", draft.partner_id.id)]
if record_id:
domain += [("id", "<", record_id)]
last = model.search(domain, order="date_order desc, id desc", limit=1)
if last:
html = "<strong>Previous order:</strong> <a href='%s'>%s</a>" % (url_for(last), last.name)
result = {"visible": True, "html": html}
else:
result = {"visible": False}If we set up the rules for a partner record as shown below:
The banners will be displayed in the partner form view:
Once the values are filled in, the banners will disappear:
No verified information in OCA: Installation, Configuration.
Installation instructions, the distribution package, dependencies and configuration steps are not shown because they have not yet been verified in the module documentation for any version.
Data imported from GitHub: this sheet reflects the latest catalog review, not a live lookup. Always check the module documentation and staging before installing in production.