When building wizards in Odoo, getting the user interface and triggering mechanisms right is just as important as the underlying logic. A well-designed wizard feels intuitive, reduces errors, and guides users through complex operations smoothly. In this guide, we'll explore practical approaches to creating clean, user-friendly wizard interfaces and the various ways to launch them in Odoo 19.
Why Wizard Views Matter
Think of your wizard as a conversation with the user. If the interface is cluttered or confusing, users will make mistakes or abandon the process altogether. A good wizard view should:
- Present only the necessary fields at each moment
- Use clear labels and helpful placeholders
- Group related information logically
- Provide obvious next steps
Designing Clean and Focused Form Views
Your wizard form should feel lightweight. Unlike standard form views that might display dozens of fields, wizard forms work best when they're minimal and purpose-driven.
Basic Structure Example:
<record id="view_sale_order_confirm_wizard_form" model="ir.ui.view">
<field name="name">sale.order.confirm.wizard.form</field>
<field name="model">sale.order.confirm.wizard</field>
<field name="arch" type="xml">
<form string="Confirm Sale Orders">
<group>
<field name="order_ids" widget="many2many_tags"/>
</group>
<footer>
<button string="Confirm Orders"
name="action_confirm_orders"
type="object"
class="btn-primary"/>
<button string="Cancel"
class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
Key Points:
- Use <group> tags to organize fields into columns naturally
- Add placeholder text to guide input
- The footer section keeps action buttons prominent and consistent
- special="cancel" automatically closes the wizard without processing
Make Fields Self-Explanatory
Instead of cramming instructions into help text that users might miss, make your field labels and placeholders do the work:
<field name="effective_date"
string="When should this take effect?"
default_focus="1"/>
The default_focus attribute places the cursor in that field when the wizard opens, immediately guiding the user where to start.
XML Buttons and Actions: Bringing Wizards to Life
Understanding ir.actions.act_window
This is the engine that powers wizard launching. Think of ir.actions.act_window as a set of instructions telling Odoo what to display, where to get the data, and how to present it.
Complete Action Definition:
<record id="action_sale_order_confirm_wizard" model="ir.actions.act_window">
<field name="name">Confirm Orders</field>
<field name="res_model">sale.order.confirm.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_view_types">list,form</field>
</record>
What Each Field Does:
- name: What users see in menus or as the window title
- res_model: Your wizard's technical model name
- view_mode: Usually just "form" for wizards
- target: Controls how the window appears (more on this next)
- binding_model_id: When set, adds an "Action" menu item to that model's form view
- binding_view_types: Specifies where the action appears (form, list, or both)
The Magic of target="new"
Setting target="new" creates a modal popup that overlays the current screen. This is essential for wizards because:
- Context Preservation: Users stay connected to what they were working on
- Focus: The pop-up demands attention without allowing distractions
- Clear Exit Points: Closing the pop-up is an obvious "never mind" action
Without target="new", your wizard would replace the entire screen, which feels jarring and can confuse users about navigation.
Four Ways to Trigger Your Wizard
1. Form View Buttons (The Most Common Approach)
Add buttons directly to existing form views for context-aware actions.
<record id="view_sale_order_form_inherit_wizard" model="ir.ui.view">
<field name="name">sale.order.form.inherit.confirm.wizard</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="%(action_sale_order_confirm_wizard)d"
string="Confirm via Wizard"
type="action"
class="btn-primary"
groups="sales_team.group_sale_manager"
invisible="state != 'draft'"/>
</xpath>
</field>
</record>
Tips:
- Use invisible to show buttons only when relevant (e.g., in draft state)
- The groups attribute restricts access to specific user roles
- The %(action_name)d syntax references your action by XML ID
- Place buttons in <header> for prominent placement, or in <div name="button_box"> for less critical actions
2. Menu Items (For General Access)
Create a menu entry when your wizard isn't tied to a specific record.
<menuitem id="menu_confirm_orders_wizard"
name="Confirm Orders Wizard"
action="action_sale_order_confirm_wizard"
parent="sale.sale_menu_root"
sequence="99"/>
This approach works well for:
- Batch operations affecting multiple records
- Administrative tools
- Reporting wizards that generate documents
3. Server Actions (For Automation and Bulk Operations)
Server actions let you trigger wizards from list views, scheduled jobs, or other automated contexts.
Create the server action:
<record id="server_action_confirm_orders" model="ir.actions.server">
<field name="name">Bulk Confirm Selected Orders</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_view_types">list</field>
<field name="state">code</field>
<field name="code">
action = {
'type': 'ir.actions.act_window',
'res_model': 'sale.order.confirm.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_order_ids': records.ids}
}
</field>
</record>
Why This Is Powerful:
When you select multiple sale orders in the list view and choose "Confirm Selected Orders" from the Action menu, this passes all selected record IDs to your wizard through the context. Your wizard can then process them in bulk.
Wizard Model Receiving the Context:
From odoo import models,fields,api
From odoo.fields import Command
class SaleOrderConfirmWizard(models.TransientModel):
_name = 'sale.order.confirm.wizard'
_description = 'Bulk Confirm Orders'
order_ids = fields.Many2many('sale.order', string='Orders to Confirm')
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
if self.env.context.get('default_order_ids'):
res['order_ids'] = [Command.set(self.env.context['default_order_ids'])]
return res
def action_confirm_orders(self):
self.order_ids.action_confirm()
return {'type': 'ir.actions.act_window_close'}
4. Programmatic Triggering (From Code)
Sometimes you need to launch a wizard from within Python code (like another button's method).
def action_request_approval(self):
return {
'name': 'Request Manager Approval',
'type': 'ir.actions.act_window',
'res_model': 'approval.request.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_document_id': self.id,
'default_document_model': self._name,
'default_reason': 'Discount exceeds limit'
}
}
This pattern is incredibly useful for conditional workflows where certain actions require additional input before proceeding.
Design Principles That Matter
1. Consistent Button Placement
Users develop muscle memory. Keep your action buttons in predictable places:
- Primary actions: Form header, left side
- Secondary actions: Form header, right side
- Dangerous actions: Separate visually, maybe with a different color class
2. Progressive Disclosure
Don't show everything at once. Use invisible to reveal fields only when needed:
<field name="adjustment_type"
widget="radio"/>
<field name="adjustment_reason"
invisible="adjustment_type != 'manual'"
required="adjustment_type == 'manual'"/>
3. Visual Hierarchy Through Classes
Odoo's CSS classes help communicate importance:
<button string="Process Now"
class="btn-primary"/> <!-- Most important -->
<button string="Save Draft"
class="btn-secondary"/> <!-- Alternative action -->
<button string="Delete"
class="btn-danger"/> <!-- Destructive action -->
Creating effective wizard interfaces in Odoo is about understanding your users' workflow and removing friction from their path. Whether you're triggering wizards from buttons, menus, or server actions, the key is keeping things predictable and intuitive.
To read more about How to Create and Manage Wizard in Odoo 18, refer to our blog How to Create and Manage Wizard in Odoo 18.