As we have seen in previous chapters, form views can follow a simple layout or a business document layout, similar to a paper document.
We will now see how to design business views and to use the elements and widgets available. Usually this would be done by inheriting the base view. But to make the code simpler, we will instead create a completely new view for to-do tasks that will override the previously defined one.
In fact, the same model can have several views of the same type. When an action asks to open a view type for a model, the one with the lowest priority is picked. Or as an alternative, the action can specify the exact identifier of the view to use. The action we defined at the beginning of this chapter does just that; the view_id
tells the action to specifically use the form with ID view_form_todo_task_ui
. This is the view we will create next.
In a business application we can differentiate auxiliary data from main business data. For example, in our app the main data is the to-do tasks, and the tags and stages are auxiliary tables used by it.
These business models can use improved business view layouts for a better user experience. If you recall the to-do task form view added in Chapter 2, Building Your First Odoo Application, it was already following the business view structure.
The corresponding form view should be added after the actions and menu items we added before, and its generic structure is this:
<record id="view_form_todo_task_ui" model="ir.ui.view"> <field name="name">view_form_todo_task_ui</field> <field name="model">todo.task</field> <field name="arch" type="xml"> <form> <header> <!-- Buttons and status widget --> </header> <sheet> <!-- Form content --> </sheet> <!-- History and communication: --> <div class="oe_chatter"> <field name="message_follower_ids" widget="mail_followers" /> <field name="message_ids" widget="mail_thread" /> </div> </form> </field> </record>
Business views are composed of three visual areas:
The history and communication section, with the social network widgets at the lower end is added by inheriting our model from mail.thread
(from the mail
module), and adding at the end of the form view the elements in the XML sample as previously mentioned. We've also seen this in Chapter 3, Inheritance - Extending Existing Applications.
The status bar on top usually features the business flow pipeline and action buttons.
The action buttons are regular form buttons, and the most common next steps should be highlighted, using class="oe_highlight"
. In todo_ui/todo_view.xml
we can now expand the empty header to add a status bar to it:
<header> <field name="stage_state" invisible="True" /> <button name="do_toggle_done" type="object" attrs="{'invisible': [('stage_state','in',['done','cancel'])]}" string="Toggle Done" class="oe_highlight" /> <!-- Add stage statusbar: ... --> </header>
Depending on where in the process the current document is, the action buttons available could differ. For example, a Set as Done button doesn't make sense if we are already in the "Done" state.
This can be done using the states
attribute, listing the states where the button should be visible, like this: states="draft,open"
.
For more flexibility we can use the attrs
attribute, forming conditions where the button should be made invisible: attrs="{'invisible': [('stage_state','in', ['done','cancel'])]
.
These visibility features are also available for other view elements, and not only for buttons. We will be exploring that in more detail later in this chapter.
The business flow pipeline is a status-bar widget on a field that represents the point in the flow where the record is. This is usually a State selection field, or a Stage many to one field. Both cases can be found across several Odoo modules.
The Stage is a many to one field using a model where the process steps are defined. Because of this they can be easily configured by end users to fit their specific business process, and are perfect to support kanban boards.
The State is a selection list featuring rather stable major steps in a process, such as New, In Progress, or Done. They are not configurable by end users but on the other hand are easier to use in business logic. States also have special support for views: the state
attribute allows for an element to be selectively available to the user depending on the state of the record.
To add a stage pipeline to our form header:
<!-- Add stage statusbar: ... --> <field name="stage_id" widget="statusbar" clickable="True" options="{'fold_field': 'fold'}" />
The clickable
attribute enables clicking on the widget, to change the document's stage or state. We may not want this if the progress through process steps should be done only through action buttons.
In the options
attribute we can use some specific settings:
fold_field
, when using stages, is the name of the field that the stage model uses to indicate which stages should be shown folded.statusbar_visible
, when using states, lists the states that should be always made visible, to keep hidden the other exception states used for less common cases. Example: statusbar_visible="draft,open,done"
.The sheet canvas is the area of the form containing the main form elements. It is designed to look like an actual paper document, and its data records are sometimes referred to as documents.
The document structure in general has these components:
When using the sheet layout, fields outside a <group>
block won't have automatic labels displayed. It's up to the developer to control if and where to display the labels.
HTML tags can also be used to make the title shine. For best results, the document title should be in a div
with the oe_title
class:
<div class="oe_title"> <label for="name" class="oe_edit_only"/> <h1><field name="name"/></h1> <h3> <span class="oe_read_only">By</span> <label for="user_id" class="oe_edit_only"/> <field name="user_id" class="oe_inline" /> </h3> </div>
Here we can see the use of regular HTML elements such as div
, span
, h1
and h3
.
Outside <group>
sections the field labels are not automatically displayed, but we can display them using the <label>
element:
for
attribute identify the field to get the label text fromstring
attribute to override the field's original label textWith the class
attribute to we can also use CSS classes to control their presentation. Some useful classes are:
oe_edit_only
to display only when the form is in edit modeoe_read_only
to display only when the form is in read modeAn interesting example is to replace the text with an icon:
<label for="name" string=" " class="fa fa-wrench" />
Odoo bundles the Font Awesome icons, being used here. The available icons can be browsed at http://fontawesome.org.
The top right area can have an invisible box to place smart buttons. These work like regular buttons but can include statistical information. As an example we will add a button displaying the total number of to-dos for the owner of the current to-do.
First we need to add the corresponding computed field to todo_ui/todo_model.py
. Add the following to the TodoTask
class:
@api.one def compute_user_todo_count(self): self.user_todo_count = self.search_count( [('user_id', '=', self.user_id.id)]) user_todo_count = fields.Integer( 'User To-Do Count', compute='compute_user_todo_count')
Now we will add the button box with one button inside it. Right after the end of the oe_title
div
block, add the following:
<div name="buttons" class="oe_right oe_button_box"> <button class="oe_stat_button" type="action" icon="fa-tasks" name="%(todo_app.action_todo_task)d" string="" context="{'search_default_user_id': user_id, 'default_user_id': user_id}" help="Other to-dos for this user" > <field string="To-dos" name="user_todo_count" widget="statinfo"/> </button> </div>
The container for the buttons is a div
with the oe_button_box
class and also oe_right
, to have it aligned to the right hand side of the form.
In the example the button displays the total number of to-do tasks the document responsible has. Clicking on it will browse them, and if creating new tasks the original responsible will be used as default.
The button attributes used are:
class="oe_stat_button"
is to use a rectangle style instead of a button.icon
is the icon to use, chosen from the Font Awesome icon set.type
will be usually action
, for a window action, and name
will be the ID of the action to execute. It can be inserted using the formula %(action-external-id)d
, to translate the external ID into the actual ID number. This action is expected to open a view with related records.string
can be used to add text to the button. It is not used here because the contained field already provides the text for it.context
will set defaults on the target view, when clicking through the button, to filter data and set default values for new records created.help
is the tooltip to display.The button itself is a container and can have inside it's fields to display statistics. These are regular fields using the widget statinfo
. The field should be a computed field, defined in the underlying module. We can also use static text instead or alongside the statinfo
fields, such as: <div>User's To-dos</div>
The main content of the form should be organized using <group>
tags. A group is a grid with two columns. A field and its label take two columns, so adding fields inside a group will have them stacked vertically.
If we nest two <group>
elements inside a top group, we will be able to get two columns of fields with labels, side by side.
<group name="group_top"> <group name="group_left"> <field name="date_deadline" /> <separator string="Reference" /> <field name="refers_to" /> </group> <group name="group_right"> <field name="tag_ids" widget="many2many_tags"/> </group> </group>
Groups can have a string
attribute, used as a title for the section. Inside a group section, titles can also be added using a separator
element.
Another way to organize content is the notebook, containing multiple tabbed sections called pages. These can be used to keep less used data out of sight until needed or to organize a large number of fields by topic.
We won't need this on our to-do task form, but here is an example that could be added in the task stages form:
<notebook> <page string="Whiteboard" name="whiteboard"> <field name="docs" /> </page> <page name="second_page"> <!-- Second page content --> </page> </notebook>
It is good practice to have names on pages, to make it more reliable for other modules to extend them.