Skip to content

Commit 3217d31

Browse files
committed
[18.0][ADD] web_backend_action: New Module
1 parent c729a3b commit 3217d31

File tree

13 files changed

+1184
-0
lines changed

13 files changed

+1184
-0
lines changed

web_backend_action/README.rst

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
==================
2+
Web Backend Action
3+
==================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:2c74fe8141e08a0ee82f5bac841673ab92bb84ebeeba5172c98a9a7ac5731710
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
20+
:target: https://github.com/OCA/web/tree/18.0/web_backend_action
21+
:alt: OCA/web
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_backend_action
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
**Trigger UI actions from backend code in real time.**
32+
33+
This technical module allows you to send standard Odoo actions
34+
(``ir.actions.*``) from the server to the user’s web client and execute
35+
them live via the bus. Instead of only showing a toast notification, the
36+
client actually runs the action when the conditions match.
37+
38+
**Typical use cases include**:
39+
40+
- Open a wizard automatically when a background job finishes (e.g. an
41+
import summary or a follow-up confirmation dialog).
42+
- Redirect the user to a specific list/form view after a workflow event
43+
(approval, status change, external webhook, etc.).
44+
- React in real time to server-side events by refreshing or changing the
45+
current screen without manual user navigation.
46+
47+
Backend helpers are provided on ``res.users`` to send actions,
48+
optionally tagged with a target model (``res_model``), a specific record
49+
(``res_id``) and allowed view types (``view_types``). A small service in
50+
the web client listens to the bus channel and only executes the action
51+
when the current UI context (model / record / view type) matches the
52+
hints sent from the server.
53+
54+
**Table of contents**
55+
56+
.. contents::
57+
:local:
58+
59+
Installation
60+
============
61+
62+
This module is based on the Instant Messaging Bus. To work properly, the
63+
server must be launched in gevent mode.
64+
65+
Usage
66+
=====
67+
68+
To trigger an action for the current user, call ``_send_action`` on
69+
``res.users``:
70+
71+
.. code:: python
72+
73+
action = {
74+
"type": "ir.actions.act_window",
75+
"name": "My Wizard",
76+
"res_model": "my.wizard",
77+
"view_mode": "form",
78+
"target": "new",
79+
"context": {
80+
"default_some_field": 42,
81+
},
82+
}
83+
84+
self.env.user._send_action(action)
85+
86+
This will send a cleaned version of the action to the web client of the
87+
current user.
88+
89+
**Filter by model (``res_model``)**
90+
91+
If you want the action to run when the user is currently working with a
92+
specific model. You can pass a ``res_model`` hint:
93+
94+
.. code:: python
95+
96+
action = {
97+
"type": "ir.actions.act_window",
98+
"name": "Order Helper",
99+
"res_model": "sale.order.helper.wizard",
100+
"view_mode": "form",
101+
"target": "new",
102+
}
103+
104+
self.env.user._send_action(
105+
action,
106+
res_model="sale.order",
107+
)
108+
109+
**Filter by record (``res_id``)**
110+
111+
For form views, it is often useful to restrict execution to a specific
112+
record (for example, only when the user is looking at a particular
113+
partner or document). You can pass a res_id value:
114+
115+
.. code:: python
116+
117+
action = {
118+
"type": "ir.actions.client",
119+
"tag": "soft_reload",
120+
}
121+
122+
# Only execute when the user is viewing this specific partner record
123+
self.env.user._send_action(
124+
action,
125+
res_model="res.partner",
126+
res_id=self.id,
127+
view_types=["form"],
128+
)
129+
130+
**Filter by view type (view_types)**
131+
132+
You can also restrict execution to specific view types, for example only
133+
in form view or only in list view. Use the ``view_types`` parameter:
134+
135+
.. code:: python
136+
137+
action = {
138+
"type": "ir.actions.act_window",
139+
"name": "Mass Update",
140+
"res_model": "stock.quant",
141+
"view_mode": "form",
142+
"target": "new",
143+
}
144+
145+
self.env.user._send_action(
146+
action,
147+
res_model="stock.quant",
148+
view_types=["list"], # only when the user is on a list view
149+
)
150+
151+
If ``view_types`` is omitted or an empty list, no restriction by view
152+
type is applied.
153+
154+
**Example patterns**
155+
156+
- Update UI after a background job
157+
158+
.. code:: python
159+
160+
def _cron_enrich_contact(self):
161+
# ... heavy enrich logic ...
162+
163+
action = {
164+
"type": "ir.actions.client",
165+
"tag": "soft_reload",
166+
}
167+
168+
# Ask the client to soft-reload when viewing this contact
169+
self.env.user._send_action(
170+
action,
171+
res_model="res.partner",
172+
res_id=self.id,
173+
view_types=["form"],
174+
)
175+
176+
- Notify after creating a record
177+
178+
.. code:: python
179+
180+
def create(self, vals_list):
181+
res = super().create(vals_list)
182+
action = {
183+
"type": "ir.actions.client",
184+
"tag": "display_notification",
185+
"params": {
186+
"type": "info",
187+
"title": _("Info"),
188+
"message": _("Record was created successfully."),
189+
"next": {"type": "ir.actions.act_window_close"},
190+
},
191+
}
192+
# Notify only when the user is on the partner form
193+
self.env.user._send_action(
194+
action,
195+
res_model="res.partner",
196+
res_id=self.id,
197+
view_types=["form"],
198+
)
199+
200+
- Ask the user to fill in missing information
201+
202+
.. code:: python
203+
204+
def _cron_check_employees_data(self):
205+
# ... find employees with incomplete data ...
206+
employees = self.search([("some_field", "=", False)])
207+
208+
for employee in employees:
209+
action = {
210+
"type": "ir.actions.act_window",
211+
"name": "Complete Employee Data",
212+
"res_model": "hr.employee.wizard",
213+
"view_mode": "form",
214+
"target": "new",
215+
"context": {
216+
"default_employee_id": employee.id,
217+
},
218+
}
219+
220+
# Ask the responsible user to complete data
221+
employee.user_id._send_action(
222+
action,
223+
)
224+
225+
Bug Tracker
226+
===========
227+
228+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
229+
In case of trouble, please check there if your issue has already been reported.
230+
If you spotted it first, help us to smash it by providing a detailed and welcomed
231+
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_backend_action%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
232+
233+
Do not contact contributors directly about support or help with technical issues.
234+
235+
Credits
236+
=======
237+
238+
Authors
239+
-------
240+
241+
* Dinar Gabbasov
242+
243+
Contributors
244+
------------
245+
246+
- Dinar Gabbasov <[email protected]>
247+
248+
Maintainers
249+
-----------
250+
251+
This module is maintained by the OCA.
252+
253+
.. image:: https://odoo-community.org/logo.png
254+
:alt: Odoo Community Association
255+
:target: https://odoo-community.org
256+
257+
OCA, or the Odoo Community Association, is a nonprofit organization whose
258+
mission is to support the collaborative development of Odoo features and
259+
promote its widespread use.
260+
261+
This module is part of the `OCA/web <https://github.com/OCA/web/tree/18.0/web_backend_action>`_ project on GitHub.
262+
263+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

web_backend_action/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2025 Dinar Gabbasov <[email protected]>
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
from . import models

web_backend_action/__manifest__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2025 Dinar Gabbasov <[email protected]>
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
{
5+
"name": "Web Backend Action",
6+
"summary": "Send actions from the backend to the UI via the bus "
7+
"and execute them immediately",
8+
"version": "18.0.1.0.0",
9+
"category": "Extra Tools",
10+
"license": "LGPL-3",
11+
"author": "Odoo Community Association (OCA), Dinar Gabbasov",
12+
"website": "https://github.com/OCA/web",
13+
"depends": ["web", "bus"],
14+
"data": [],
15+
"assets": {
16+
"web.assets_backend": [
17+
"web_backend_action/static/src/js/services/*.js",
18+
],
19+
},
20+
"installable": True,
21+
"application": False,
22+
"auto_install": False,
23+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2025 Dinar Gabbasov <[email protected]>
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
from . import res_users
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2025 Dinar Gabbasov <[email protected]>
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
from odoo import models
5+
6+
from odoo.addons.web.controllers.utils import clean_action
7+
8+
9+
class ResUsers(models.Model):
10+
_inherit = "res.users"
11+
12+
def _send_action(self, action, res_model=None, res_id=None, view_types=None):
13+
"""
14+
Send an action to each user to webclient.
15+
16+
:param dict action: Standard Odoo action definition to be executed in
17+
the web client.
18+
:param str res_model: Optional model name tag. The webclient can compare
19+
this value with the current controller's ``resModel`` and only run
20+
the action when they match.
21+
:param int res_id: Optional record ID. The web client can compare this
22+
value with the current controller's ``resId`` and only run the
23+
action when they match.
24+
:param list[str] view_types: Optional list of allowed view types
25+
(e.g. ``["form", "list", "kanban"]``). The webclient can use this
26+
list to restrict execution to specific view types. If ``None`` or
27+
empty, no view-type restriction is applied.
28+
:return bool: ``True`` if a message was sent, or ``None`` when no
29+
action was provided.
30+
"""
31+
if not action:
32+
return
33+
34+
clean = clean_action(action, self.env)
35+
message = {
36+
"action": clean,
37+
"res_model": res_model,
38+
"res_id": res_id,
39+
"view_types": view_types or [],
40+
}
41+
for user in self:
42+
user._bus_send("web.backend_action", message)
43+
44+
return True

web_backend_action/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Dinar Gabbasov \<<[email protected]>\>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Trigger UI actions from backend code in real time.**
2+
3+
This technical module allows you to send standard Odoo actions
4+
(`ir.actions.*`) from the server to the user’s web client and execute
5+
them live via the bus. Instead of only showing a toast
6+
notification, the client actually runs the action when the
7+
conditions match.
8+
9+
**Typical use cases include**:
10+
11+
- Open a wizard automatically when a background job finishes
12+
(e.g. an import summary or a follow-up confirmation dialog).
13+
- Redirect the user to a specific list/form view after a workflow event
14+
(approval, status change, external webhook, etc.).
15+
- React in real time to server-side events by refreshing or changing
16+
the current screen without manual user navigation.
17+
18+
Backend helpers are provided on `res.users` to send actions, optionally
19+
tagged with a target model (`res_model`), a specific record (`res_id`)
20+
and allowed view types (`view_types`). A small service in the web client
21+
listens to the bus channel and only executes the action when the current
22+
UI context (model / record / view type) matches the hints sent from the
23+
server.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This module is based on the Instant Messaging Bus. To work properly, the
2+
server must be launched in gevent mode.

0 commit comments

Comments
 (0)