8000 xml actions · odoo/documentation@a5139d0 · GitHub
[go: up one dir, main page]

Skip to content

Commit a5139d0

Browse files
committed
xml actions
1 parent 5713223 commit a5139d0

File tree

3 files changed

+157
-31
lines changed

3 files changed

+157
-31
lines changed

content/developer/tutorials/server_framework_101/03_build_user_interface.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ the `ir.actions.act_window` model whose key fields include:
219219
attribute of the :ref:`field <reference/data/field>` data operation.
220220

221221
.. seealso::
222-
:doc:`Reference documentation for actions <../../reference/backend/actions>`
222+
:ref:`Reference documentation for window actions <reference/actions/window>`
223223

224224
As promised, we'll finally get to interact with our real estate properties in the UI. All we need
225225
now is an action to assign to the menu item.

content/developer/tutorials/server_framework_101/05_connect_the_dots.rst

Lines changed: 155 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,10 @@ perform custom validation and raise blocking validation errors if the constraint
907907
@api.constrains('amount')
908908
def _check_amount_higher_than_previous_offers(self):
909909
for offer in self:
910-
if offer.amount < max(offer.property_id.offer_ids.mapped('amount')):
910+
same_buyer_offers = offer.property_id.offer_ids.filtered(
911+
lambda o: o.buyer_id == offer.buyer_id
912+
)
913+
if offer.amount < max(same_buyer_offers.mapped('amount')):
911914
raise ValidationError(_(
912915
"The amount of the new offer must be higher than the amount of the previous "
913916
"offers."
@@ -1014,14 +1017,6 @@ fields with default values.
10141017
Trigger business workflows
10151018
==========================
10161019

1017-
**Action buttons** allow users to trigger specific workflows directly from the user interface. These
1018-
buttons can be of type **action**, defined in XML, or **object**, implemented in the model.
1019-
Together, these types of buttons facilitate the integration of user interactions with business
1020-
logic.
1021-
1022-
.. todo: note: mention that the method is public so it can be called directly by the client.
1023-
always return something in public methods as they are part of the :ref:external API and can be called through XML-RPC
1024-
10251020
.. _tutorials/server_framework_101/crud_methods:
10261021

10271022
CRUD methods
@@ -1126,9 +1121,10 @@ accessed directly using :code:`record.field`.
11261121
11271122
def unlink(self):
11281123
for offer in self:
1124+
property_offers = offer.property_id.offer_ids
11291125
if (
11301126
offer.property_id.state in ('offer_received', 'under_option')
1131-
and len(offer.property_id.offer_ids) == 1 # The current offer is the last one.
1127+
and not (property_offers - self) # All the property's offers are being deleted.
11321128
):
11331129
offer.property_id.state = 'new'
11341130
return super().unlink()
@@ -1165,46 +1161,175 @@ accessed directly using :code:`record.field`.
11651161
property.address_id = address.id
11661162
return res
11671163
1168-
.. _tutorials/server_framework_101/action_type_actions:
1164+
.. _tutorials/server_framework_101/xml_actions:
1165+
1166+
XML actions
1167+
-----------
1168+
1169+
**Action buttons** allow users to trigger workflows directly from the user interface. The simplest
1170+
type of action button is **action**. These buttons are linked to actions defined in XML and are
1171+
typically used to open specific views or trigger server actions. These buttons allow developers to
1172+
link workflows to the UI without needing to write Python code, making them ideal for simple,
1173+
preconfigured tasks.
1174+
1175+
We have already seen how to :ref:`link menu items to XML-defined window actions
1176+
<tutorials/server_framework_101/define_window_actions>`. To link a **button** to an XML-defined
1177+
action, a `button` element must be added to the view, with its `type` attribute set to `action`. The
1178+
`name` attribute should reference the XML ID of the action to be executed, following the format
1179+
`%(XML_ID)d`.
1180+
1181+
.. example::
1182+
In the following example, a button is added to the product form view to display all products in
1183+
the same category.
11691184

1170-
XML-defined actions
1171-
-------------------
1185+
.. code-block:: xml
11721186
1173-
Action-type buttons link to actions defined in XML and are typically used to display specific views
1174-
or trigger server actions. These buttons allow developers to link workflows to the UI without
1175-
writing Python code, making them ideal for simple, preconfigured tasks.
1187+
<form>
1188+
<sheet>
1189+
<div name="button_box">
1190+
<button
1191+
string="Similar Products"
1192+
type="action"
1193+
name="product.view_products_action"
1194+
context="{'search_default_category_id': category_id.id, 'create': False, 'edit': False}"
1195+
/>
1196+
</div>
1197+
</sheet>
1198+
</form>
11761199
1177-
We already saw :ref:`how to link XML-defined window actions to menu items
1178-
<tutorials/server_framework_101/define_window_actions>`. To link a button to an XML-defined action,
1179-
a `button` element must be added to the view, with its `type` attribute set to `action`. The `name`
1180-
attribute should reference the XML ID of the action to execute.
1200+
.. note::
1201+
- The button is placed at the top of the form view by using a button container (`button_box`).
1202+
- The `context` attribute is used to:
1203+
1204+
- Filter the products to display only those in the same category as the current product.
1205+
- Prevent users from creating or editing products when browsing them through the button.
1206+
1207+
.. seealso::
1208+
Reference documentation for :ref:`button containers
1209+
<reference/view_architectures/form/button_container>`.
11811210

11821211
.. exercise::
1212+
Replace the property form view's :guilabel:`Offers` notebook page with a **stat button**. This
1213+
button should:
1214+
1215+
- Be placed at the top of the property form view.
1216+
- Display the total number of offers for the property.
1217+
- Use a relevant icon.
1218+
- Allow users to browse offers in list and form views.
1219+
11831220
.. tip::
1184-
Rely on the reference documentation for :ref:`action buttons
1185-
<reference/view_architectures/form/button>` and :ref:`headers
1186-
<reference/view_architectures/form/header>` in form views.
1221+
- Rely on the reference documentation for :ref:`action buttons
1222+
<reference/view_architectures/form/button>` in form views.
1223+
- Find icon codes (`fa-<something>`) in the `Font Awesome v4 catalog
1224+
<https://fontawesome.com/v4/icons/>`_.
1225+
- Ensure that your count computations :ref:`scale with the number of records to process
1226+
<performance/good_practices/batch>`.
1227+
- Assign the `default_<field>` context key to a button to define default values when creating
1228+
new records opened through that button.
11871229

1188-
.. todo: "view offers" statbutton with count + remove notebook page of offers
1230+
.. spoiler:: Solution
11891231

1190-
.. _tutorials/server_framework_101/object_type_actions:
1232+
.. code-block:: python
1233+
:caption: `real_estate_property.py`
1234+
:emphasize-lines: 4,8-15
11911235
1192-
Model-defined actions
1193-
---------------------
1236+
offer_ids = fields.One2many(
1237+
string="Offers", comodel_name='real.estate.offer', inverse_name='property_id'
1238+
)
1239+
offer_count = fields.Integer(string="Offer Count", compute='_compute_offer_count')
1240+
1241+
[...]
1242+
1243+
@api.depends('offer_ids')
1244+
def _compute_offer_count(self):
1245+
offer_data = self.env['real.estate.offer']._read_group(
1246+
[('property_id', 'in', self.ids)], groupby=['property_id'], aggregates=['__count'],
1247+
)
1248+
property_data = {property.id: count for property, count in offer_data}
1249+
for property in self:
1250+
property.offer_count = property_data.get(property.id, 0)
1251+
1252+
.. code-block:: xml
1253+
:caption: `real_estate_offer_views.xml`
1254+
:emphasize-lines: 1-10
1255+
1256+
<record id="real_estate.view_offers_action" model="ir.actions.act_window">
1257+
<field name="name">Offers</field>
1258+
<field name="res_model">real.estate.offer</field>
1259+
<field name="view_mode">list,form</field>
1260+
<field name="help" type="html">
1261+
<p class="o_view_nocontent_smiling_face">
1262+
Create a new offer.
1263+
</p>
1264+
</field>
1265+
</record>
1266+
1267+
.. code-block:: python
1268+
:caption: `__manifest__.py`
1269+
:emphasize-lines: 1
1270+
1271+
'views/real_estate_property_views.xml', # Depends on `real_estate_offer_views.xml`.
1272+
1273+
.. code-block:: xml
1274+
:caption: `real_estate_property_views.xml`
1275+
:emphasize-lines: 2-12
1276+
1277+
<sheet>
1278+
<div name="button_box" class="oe_button_box">
1279+
<button
1280+
string="Offers"
1281+
icon="fa-handshake-o"
1282+
type="action"
1283+
name="real_estate.view_offers_action"
1284+
context="{'default_property_id': id}"
1285+
>
1286+
<field string="Offers" name="offer_count" widget="statinfo"/>
1287+
</button>
1288+
</div>
1289+
[...]
1290+
1291+
.. _tutorials/server_framework_101/model_actions:
1292+
1293+
Model actions
1294+
-------------
11941295

1195-
Object-type buttons link to model methods that execute custom business logic. These methods enable
1196-
more complex workflows, such as processing the current records, configuring actions depending on
1197-
these records, or integrating with external systems.
1296+
Another, more versatile type of action button is **object**. These buttons are linked to model
1297+
methods that execute custom business logic. These methods enable more complex workflows, such as
1298+
processing the current records, configuring client actions depending on these records, or
1299+
integrating with external systems.
11981300

11991301
To link a button to a model-defined action, its `type` attribute must be set to `object`, and its
12001302
`name` attribute must be set to the name of the model method to call when the button is clicked. The
12011303
method receives the current recordset through `self` and should return a dictionary acting as an
12021304
action descriptor.
12031305

1306+
.. example::
1307+
In the following example,
1308+
1309+
.. note::
1310+
- Action methods should be public :dfn:`not prefixed with an underscore` to make them callable
1311+
by the client. Such methods should always return something as they are automatically part of
1312+
the :doc:`external API <../../reference/external_api>`.
1313+
1314+
.. exercise::
1315+
#. tmp ... in the header.
1316+
1317+
.. tip::
1318+
- Rely on the reference documentation for :ref:`headers
1319+
<reference/view_architectures/form/header>` in form views.
1320+
12041321
.. todo: accept/refuse offer buttons -> auto refuse others when accepting (write)
12051322
.. todo: multi-checkbox refuse offers in bulk
12061323
.. todo: "assign myself as salesperson" action
12071324
1325+
.. spoiler:: Solution
1326+
1327+
.. code-block:: python
1328+
:caption: `real_estate_property.py`
1329+
:emphasize-lines: 1
1330+
1331+
[...]
1332+
12081333
----
12091334

12101335
.. todo: add incentive for chapter 6

content/developer/tutorials/server_framework_101/07_advanced_views.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ tmp
1313
.. todo: wizards -> create a "receive offer wizard" to default the amount to the property's selling price
1414
.. todo: context active_test False on the category_id field of products to see archived categories
1515
.. todo: sequence widget on tags
16+
.. todo: compute display_name for offers in form view
1617
1718
1819
----

0 commit comments

Comments
 (0)
0