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 these business document views and how to use the elements and widgets available. We would normally do this by inheriting and extending the todo_app views. But for the sake of clarity, we will instead create completely new views to override the original ones.

Business applications are often systems of record – for products in a warehouse, invoices in an accounting department, and many more. Most of the recorded data can be represented as a paper document. For a better user experience, form views can mimic these paper documents. For example, in our app, we could think of a To-Do Task as something that has a simple paper form to fill out. We will provide a form view that follows this design.

To add a view XML with the basic skeleton of a business document view, we should edit the views/todo_views.xml file and add it to the top:

<record id="view_form_todo_task_ui" 
  model="ir.ui.view"> 
  <field name="model">todo.task</field> 
  <field name="priority">15</field> 
  <field name="arch" type="xml"> 
    <form> 
      <header>  
        <!-- To add buttons and status widget --> 
      </header>
      <sheet> 
        <!-- To add form content -->  
      </sheet> 
      <!-- Discuss widgets for 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> 

The view name is optional and automatically generated if missing. For simplicity, we took advantage of that and omitted the <field name="name"> element from the view record.

We can see that business document views usually use three main areas: the header status bar, the sheet for the main content, and a bottom history and communication section, also known as chatter.

The history and communication section, at the bottom, uses the social network widgets provided by the mail addon module. To be able to use them, our model should inherit the mail.thread mixin model, as we saw in Chapter 3Inheritance - Extending Existing Applications.

The header at the top usually features the life cycle or steps that the document will move through and the action buttons.

These action buttons are regular form buttons, and the most important next steps can be highlighted, using class="oe_highlight".

The document life cycle uses the statusbar widget on a field that represents the point in the life cycle where the document is currently at. This is usually a State selection field or a Stage many-to-one  field. These two fields can be found across several Odoo core modules.

The stage is a many-to-one field which uses a supporting model to set up the steps of the process. Because of this it can be dynamically configured by end users to fit their specific business process, and is perfect to support kanban boards.

The state is a selection list featuring a few, rather stable, steps in a process, such as New, In Progress, and Done. It is not configurable by end users but, since it is static, it is much easier to be used in business logic. The view fields even have special support for it: the state attribute allows a field to be available to the user or not, depending on the document state.

Historically, stages were introduced later than states. Both have coexisted, but the trend in the Odoo core is for stages to replace states. But as seen in the preceding explanation, states still provide some features that stages don't.

It is still possible to benefit from the best of both worlds, by mapping the stages into states. This was what we did in the previous chapter, by adding a state field in the task Stages model, and making it also available in the To-do Task documents through a computed field,  enabling the use of the state field attribute.

In the views/todo_view.xml file we can now expand the basic header to add a status bar:

<header>  
  <field name="state" invisible="True" />
  <button name="do_toggle_done" type="object" 
    attrs="{'invisible':[('state','in',['draft'])]}"
    string="Toggle Done"
    class="oe_highlight" />
  <field name="stage_id"
    widget="statusbar"  
    clickable="True"
    options="{'fold_field': 'fold'}" />
</header>

Here we add state as a hidden field. We need this to force the client to also include that field in the data requests sent to the server. Otherwise it won't be available to be used in expressions.

Next a button is added to the status bar, to let the user toggle the Task's Done flag.

The buttons displayed in the status bar should change based on the where in the life cycle the current document is.

 We used the attrs attribute to hide the button when the document is in the draft state. The condition to do this uses the state field, not shown on the form, which is why we had to add it as a hidden field.

If we have a state selection field, we can instead use the states attribute. In this case we do, and the same effect could be achieved using states="open,done". While it's not as flexible as attrs attribute, it is more concise.

These visibility features can be also used on other view elements, such as fields. We will explore them in more detail later in this chapter.

The clickable attribute allows the user to change the document stage by clicking on the status bar. We usually want to enable this, but there are also cases where we don't, such as when we need more control over the workflow, and require the users to progress through the stages using only the available action buttons, so that these can perform validations before moving between stages.

When using a status bar widget with stages, we can have the seldom used stages hidden in a More stage group. For this, the stages model must have a flag to configure the ones to hide, usually named fold. And the statusbar widget should use an options attribute, as shown in the preceding code, to provide that field name to the fold_field option.

When using the status bar widget with a state field, a similar effect can be achieved with the statusbar_visible attribute, used to list states that should be always visible and hide exception states necessary for less common cases. For example:

<field name="stage_id" widget="statusbar"
  clickable="True"
  statusbar_visible="draft,open" />

Fields outside a <group> element don't automatically have labels rendered for them. This will be the case for the title elements, so the <label for"..."/> element should be used to render it. At the expense of some extra work, this has the advantage of giving more control over the label display.

Regular HTML, including CSS-style elements, can also be used to make the title shine. For best results, the title should be inside a <div> with the oe_title class.

Here is the <sheet> element expanded to include the title plus some additional fields as subtitles:

<sheet> 
  <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> 
  <!-- More elements will be added from here... --> 
</sheet> 

Here we can see that we use regular HTML elements, such as div, span, h1, and h2. The <label> element allows us to control when and where it will be shown. The for attribute identifies the field we should get the label text from. Another possibility is to use the string attribute to provide a specific text to use for the label. Our example also uses the class="oe_edit_only" attribute so that it is visible only in edit mode.

In some cases, such as Partners or Products, a representative image is shown at the top-left corner. Supposing we had a my_image binary field, we could add before the <div class="oe_title"> line, using:

<field name="my_image" widget="image" class="oe_avatar"/> 

The main content of the form should be organized using <group> tags. The group tag inserts two columns in the canvas, and inside it, by default, fields will be displayed with labels.

A field value and field label takes 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.

Continuing with our form view, we can now add the main content after the smart buttons box:

<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>

It is a good practice to assign a name to group tags so that it's easier to reference them later to extend the view (either by you or another developer). The string attribute is also allowed, and if set, is used to display section title.

Inside a group, a <newline> element will force a new line and the next element will be rendered in the group's first column. Additional section titles can be added inside a group using the <separator> element.

We can have greater control over the layout of a group element using the col and colspan attributes.

The col attribute can be used on <group> elements to customize the number of columns it will contain. The default is 2, but it can be changed to any other number. Even numbers work better since by default each field added takes up two columns, for the label plus the field value.

The elements placed inside the group, including <field> elements, can use a colspan attribute to set a specific number of columns they should take. By default one column is taken up.