Actions
Examples target Odoo 18 and 19 unless marked. Platform covers Odoo 11 to 20.
Window Actions
Basic Window Action
Odoo 18 renamed the tree view type to list. Use list in view_mode on Odoo 18 and later. The other examples on this page use list,form; replace it with tree,form on Odoo 17 and earlier.
xml
<record id="action_model_list" model="ir.actions.act_window">
<field name="name">Model Records</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="target">current</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first record!
</p>
</field>
</record>Window Action with Domain and Context
xml
<record id="action_filtered_records" model="ir.actions.act_window">
<field name="name">Active Records</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="domain">[('active', '=', True)]</field>
<field name="context">{
'default_active': True,
'search_default_current_month': 1
}</field>
<field name="limit">80</field>
</record>Form Action for Specific Record
xml
<record id="action_open_record" model="ir.actions.act_window">
<field name="name">Edit Record</field>
<field name="res_model">model.name</field>
<field name="view_mode">form</field>
<field name="res_id" ref="model_record_id"/>
<field name="target">new</field>
</record>Multi-View Window Action
xml
<record id="action_multi_view" model="ir.actions.act_window">
<field name="name">Multi View Action</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form,kanban,calendar,graph,pivot</field>
<field name="view_id" ref="view_tree_custom"/>
</record>
<!-- Specify view sequences -->
<record id="action_multi_view_view_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">list</field>
<field name="view_id" ref="view_tree_custom"/>
<field name="act_window_id" ref="action_multi_view"/>
</record>
<record id="action_multi_view_view_form" model="ir.actions.act_window.view">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_form_custom"/>
<field name="act_window_id" ref="action_multi_view"/>
</record>Server Actions
Python Code Server Action
xml
<record id="server_action_python" model="ir.actions.server">
<field name="name">Process Records</field>
<field name="model_id" ref="model_model_name"/>
<field name="binding_model_id" ref="model_model_name"/>
<field name="state">code</field>
<field name="code">
for record in records:
if record.state == 'draft':
record.write({
'state': 'confirmed',
'confirmed_date': fields.Datetime.now(),
'confirmed_by': env.user.id
})
# Send notification
env['mail.thread'].message_post(
body="Records have been processed",
message_type='notification'
)
# Log activity
records.message_post(body="Status updated to confirmed")
</field>
</record>Multi-Action Server Action
xml
<record id="server_action_multi" model="ir.actions.server">
<field name="name">Multi Step Process</field>
<field name="model_id" ref="model_model_name"/>
<field name="state">multi</field>
</record>
<record id="server_action_step1" model="ir.actions.server">
<field name="name">Step 1: Validate</field>
<field name="model_id" ref="model_model_name"/>
<field name="state">code</field>
<field name="sequence">1</field>
<field name="code">
if not records.filtered(lambda r: r.state == 'draft'):
raise UserError("No draft records to process")
</field>
</record>
<record id="server_action_step2" model="ir.actions.server">
<field name="name">Step 2: Update Records</field>
<field name="model_id" ref="model_model_name"/>
<field name="state">code</field>
<field name="sequence">2</field>
<field name="code">
records.write({'state': 'processing'})
</field>
</record>Object Method Server Action
xml
<record id="server_action_method" model="ir.actions.server">
<field name="name">Call Method</field>
<field name="model_id" ref="model_model_name"/>
<field name="binding_model_id" ref="model_model_name"/>
<field name="state">object_create</field>
<field name="crud_model_id" ref="model_other_model"/>
<field name="link_field_id" ref="field_model_name_other_id"/>
<field name="fields_lines">
<record model="ir.server.object.lines">
<field name="col1" ref="field_other_model_name"/>
<field name="value">record.name + ' - Copy'</field>
<field name="type">equation</field>
</record>
<record model="ir.server.object.lines">
<field name="col1" ref="field_other_model_date"/>
<field name="value">datetime.now()</field>
<field name="type">equation</field>
</record>
</field>
</record>URL Actions
External URL Action
xml
<record id="action_external_url" model="ir.actions.act_url">
<field name="name">Open Website</field>
<field name="url">https://www.example.com</field>
<field name="target">new</field>
</record>Dynamic URL Action
xml
<record id="action_dynamic_url" model="ir.actions.act_url">
<field name="name">Open Record URL</field>
<field name="url">/web#id={{object.id}}&model={{object._name}}&view_type=form</field>
<field name="target">self</field>
</record>Report URL Action
xml
<record id="action_report_url" model="ir.actions.act_url">
<field name="name">Download Report</field>
<field name="url">/report/pdf/module.report_template/{{object.id}}</field>
<field name="target">self</field>
</record>Report Actions
PDF Report Action
xml
<record id="action_report_pdf" model="ir.actions.report">
<field name="name">Model Report</field>
<field name="model">model.name</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">module.report_template</field>
<field name="report_file">module.report_template</field>
<field name="binding_model_id" ref="model_model_name"/>
<field name="binding_type">report</field>
</record>HTML Report Action
xml
<record id="action_report_html" model="ir.actions.report">
<field name="name">HTML Report</field>
<field name="model">model.name</field>
<field name="report_type">qweb-html</field>
<field name="report_name">module.report_template_html</field>
<field name="report_file">module.report_template_html</field>
<field name="paperformat_id" ref="base.paperformat_euro"/>
</record>Excel Report Action
xml
<record id="action_report_xlsx" model="ir.actions.report">
<field name="name">Excel Export</field>
<field name="model">model.name</field>
<field name="report_type">xlsx</field>
<field name="report_name">module.report_excel</field>
<field name="report_file">module.report_excel</field>
<field name="binding_model_id" ref="model_model_name"/>
</record>Wizard Actions
Simple Wizard Action
xml
<record id="action_wizard_simple" model="ir.actions.act_window">
<field name="name">Simple Wizard</field>
<field name="res_model">wizard.model</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_model_name"/>
<field name="binding_type">action</field>
</record>Multi-Step Wizard Action
xml
<record id="action_wizard_multi_step" model="ir.actions.act_window">
<field name="name">Multi Step Wizard</field>
<field name="res_model">wizard.multi.step</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{
'default_step': 'step1',
'active_ids': active_ids,
'active_model': active_model
}</field>
</record>Wizard with Custom View
xml
<record id="wizard_view_form" model="ir.ui.view">
<field name="name">wizard.form</field>
<field name="model">wizard.model</field>
<field name="arch" type="xml">
<form string="Wizard Title">
<group>
<field name="field1"/>
<field name="field2"/>
</group>
<footer>
<button string="Process" name="action_process"
type="object" class="btn-primary"/>
<button string="Cancel" class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_wizard_custom_view" model="ir.actions.act_window">
<field name="name">Custom Wizard</field>
<field name="res_model">wizard.model</field>
<field name="view_mode">form</field>
<field name="view_id" ref="wizard_view_form"/>
<field name="target">new</field>
</record>Client Actions
JavaScript Client Action
xml
<record id="action_client_js" model="ir.actions.client">
<field name="name">Custom Dashboard</field>
<field name="tag">custom_dashboard</field>
<field name="context">{
'dashboard_type': 'sales',
'period': 'monthly'
}</field>
</record>Client Action with Parameters
xml
<record id="action_client_params" model="ir.actions.client">
<field name="name">Interactive Chart</field>
<field name="tag">interactive_chart</field>
<field name="params" eval="{'chart_type': 'line', 'data_source': 'sales.report'}"/>
<field name="context">{'default_period': 'current_year'}</field>
</record>Cron Actions
Scheduled Action
xml
<record id="cron_daily_cleanup" model="ir.cron">
<field name="name">Daily Cleanup</field>
<field name="model_id" ref="model_model_name"/>
<field name="state">code</field>
<field name="code">model.cleanup_old_records()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 02:00:00')"/>
<field name="active" eval="True"/>
</record>Cron with Method Call
xml
<record id="cron_send_reminders" model="ir.cron">
<field name="name">Send Daily Reminders</field>
<field name="model_id" ref="model_model_name"/>
<field name="state">object_write</field>
<field name="code">send_daily_reminders()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00')"/>
<field name="user_id" ref="base.user_admin"/>
<field name="doall" eval="False"/>
</record>Action Bindings
Model Binding
xml
<record id="action_with_binding" model="ir.actions.server">
<field name="name">Quick Action</field>
<field name="model_id" ref="model_model_name"/>
<field name="binding_model_id" ref="model_model_name"/>
<field name="binding_type">action</field>
<field name="state">code</field>
<field name="code">
records.write({'processed': True})
</field>
</record>Report Binding
xml
<record id="report_with_binding" model="ir.actions.report">
<field name="name">Custom Report</field>
<field name="model">model.name</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">module.report_template</field>
<field name="binding_model_id" ref="model_model_name"/>
<field name="binding_type">report</field>
</record>View Binding (Button)
xml
<!-- In view definition -->
<button name="%(action_custom_wizard)d"
string="Open Wizard"
type="action"
class="btn-primary"/>
<button name="action_server_method"
string="Server Action"
type="object"
confirm="Are you sure?"/>Advanced Action Patterns
Conditional Actions
xml
<record id="action_conditional" model="ir.actions.act_window">
<field name="name">Conditional View</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="domain">[('state', 'in', ['draft', 'confirmed'])]</field>
<field name="context">{
'create': False if context.get('readonly_mode') else True,
'edit': False if context.get('readonly_mode') else True,
'delete': False
}</field>
</record>Action with Groups
xml
<record id="action_restricted" model="ir.actions.act_window">
<field name="name">Admin Only Action</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
</record>Parent-Child Actions
xml
<record id="action_parent_records" model="ir.actions.act_window">
<field name="name">Parent Records</field>
<field name="res_model">parent.model</field>
<field name="view_mode">list,form</field>
</record>
<record id="action_child_records" model="ir.actions.act_window">
<field name="name">Child Records</field>
<field name="res_model">child.model</field>
<field name="view_mode">list,form</field>
<field name="domain">[('parent_id', '=', active_id)]</field>
<field name="context">{
'default_parent_id': active_id,
'search_default_parent_id': active_id
}</field>
</record>Menu Integration
Top Menu
xml
<menuitem id="menu_main"
name="Main Module"
sequence="10"/>Sub Menu with Action
xml
<menuitem id="menu_records"
name="Records"
parent="menu_main"
action="action_model_list"
sequence="10"/>Menu with Groups
xml
<menuitem id="menu_admin"
name="Administration"
parent="menu_main"
action="action_admin_panel"
groups="base.group_system"
sequence="100"/>Dynamic Menu
xml
<menuitem id="menu_dynamic"
name="Dynamic Menu"
parent="menu_main"
action="action_dynamic_content"
active="True"
sequence="20"/>Security and Permissions
Action with Record Rules
xml
<record id="action_with_rules" model="ir.actions.act_window">
<field name="name">My Records</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="domain">[('user_id', '=', uid)]</field>
</record>
<!-- Corresponding record rule -->
<record id="rule_own_records" model="ir.rule">
<field name="name">Own Records Only</field>
<field name="model_id" ref="model_model_name"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>Action Security
xml
<record id="action_secure" model="ir.actions.act_window">
<field name="name">Secure Action</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('custom.group_manager')])]"/>
</record>Action Context Variables
Common Context Variables
xml
<record id="action_with_context" model="ir.actions.act_window">
<field name="name">Context Example</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="context">{
'default_field_name': 'Default Value',
'search_default_filter_name': 1,
'group_by': 'category_id',
'create': True,
'edit': True,
'delete': False,
'duplicate': False,
'active_test': False,
'lang': 'en_US',
'tz': 'UTC',
'uid': uid,
'company_id': company_id,
'allowed_company_ids': [company_id]
}</field>
</record>Dynamic Context
xml
<record id="action_dynamic_context" model="ir.actions.act_window">
<field name="name">Dynamic Context</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="context">{
'default_date': context_today().strftime('%Y-%m-%d'),
'default_user_id': uid,
'search_default_my_records': 1 if context.get('filter_my_records') else 0,
'tree_view_ref': 'module.view_tree_compact' if context.get('compact_view') else False
}</field>
</record>Performance Optimization
Optimized Actions
xml
<record id="action_optimized" model="ir.actions.act_window">
<field name="name">Optimized List</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="limit">80</field>
<field name="context">{
'active_test': True,
'prefetch_fields': ['name', 'state', 'date']
}</field>
<field name="domain">[('active', '=', True)]</field>
</record>Lazy Loading Actions
xml
<record id="action_lazy_load" model="ir.actions.act_window">
<field name="name">Lazy Loaded Records</field>
<field name="res_model">model.name</field>
<field name="view_mode">list,form</field>
<field name="auto_search" eval="False"/>
<field name="context">{'search_disable_custom_filters': True}</field>
</record>