Finally now we are going to see some UI part of odoo application.
DataFile XML.
The CSV format is convenient when the data to load has a simple format. When the format is more complex (e.g. load the structure of a view or an email template), we use the XML format. For example, this help field contains HTML tags. While it would be possible to load such data through a CSV file, it is more convenient to use an XML file.
The XML files must be added to the same folders as the CSV files and defined similarly in the __manifest__.py. The content of the data files is also sequentially loaded when a module is installed or updated, therefore all remarks made for CSV files hold true for XML files. When the data is linked to views, we add them to the views folder.
In Odoo, the user interface (actions, menus and views) is largely defined by creating and composing records defined in an XML file. A common pattern is Menu > Action > View. To access records the user navigates through several menu levels; the deepest level is an action which triggers the opening of a list of the records.
Action:
the documentation related to this topic can be found in Actions.
Actions can be triggered in three ways:
by clicking on menu items (linked to specific actions)
by clicking on buttons in views (if these are connected to actions)
as contextual actions on object
# test_model
from odoo import fields, models
from random import randint
class TestModel(models.Model):
_name = "test_model"
_description = "Test Model"
def _get_default_color(self):
return randint(1, 11)
def _default_currency_id(self):
return self.env.user.company_id.currency_id
def name_get(self):
result = []
for rec in self:
name = self.category_id.name
result.append((rec.id, name))
return result
name = fields.Char(required=True)
description=fields.Text()
post_code = fields.Char()
date_availability=fields.Date()
expected_price=fields.Float(required=True)
selling_price=fields.Float()
bedrooms=fields.Integer()
living_area=fields.Integer()
facades=fields.Integer()
garden_area=fields.Integer()
garage=fields.Boolean()
garden=fields.Boolean()
garden_orientation=fields.Selection(selection=[('North','North'),("South","South"),
("East","East"),("West","West")])
color = fields.Integer(
string='Color',
default=_get_default_color
)
type = fields.Selection(
string='In/Out',
selection=[('in', 'In'), ('out', 'Out')],
default='out',
required=True
)
tags_ids = fields.Many2many(
comodel_name='money.flow.tags',
string='Tags',
domain="[('create_uid','=',uid)]",
)
currency_id = fields.Many2one(
comodel_name='res.currency',
string='Currency',
default=lambda self: self._default_currency_id()
)
credit_card_id = fields.Many2one(
comodel_name='credit.card',
domain="[('create_uid','=',uid)]"
)
redit_card_info = fields.Char(
related='credit_card_id.info_card',
)
issue_date = fields.Datetime(
string='Issue date',
default=fields.Datetime.now,
required=True
)
A basic action for test_model is:
<odoo>
<!-- window action -->
<record id="patient_action" model="ir.actions.act_window">
<field name="name">Patient</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hospital.patient</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('age', '<=', 10)]</field>
<!--
<field name="domain">[('create_uid', '=', uid)]</field>
-->
<field name="context">{'search_default_state': 1, 'search_default_male': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first Patient Record
</p>
</field>
</record>
<!-- server action -->
<record id="action_appointment_confirm" model="ir.actions.server">
<field name="name">Confirm appointment</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_kmhospital_appointment"/>
<field name="binding_model_id" ref="model_kmhospital_appointment"/>
<field name="binding_view_types">list,form</field>
<field name="state">code</field>
<field name="code">records.action_status_confirm()</field>
</record>
</odoo>
id is an external identifier. It can be used to refer to the record (without knowing its in-database identifier).
model has a fixed value of ir.actions.act_window (Window Actions (ir.actions.act_window)).
name is the name of the action.
res_model is the model which the action applies to.
view_mode are the views that will be available; in this case they are the list (tree) and form views. We’ll see later that there can be other view modes.
Menus
the documentation related to this topic can be found in Shortcuts.
To reduce the complexity in declaring a menu (ir.ui.menu) and connecting it to the corresponding action, we can use the <menuitem> shortcut .
A basic menu for our test_model_action is:
<menuitem id="hospital_menu_root" name="Hospital Management"/>
<menuitem id="all_department" name="Departments" parent='hospital_menu_root'
action="department_action"/>
<menuitem id="doctor_menu" name="Doctor" parent='hospital_menu_root' action='doctor_action'/>
<menuitem id="patient_menu" name="Patient" parent='hospital_menu_root' action='patient_action'/>
The menu test_model_menu_action is linked to the action test_model_action, and the action is linked to the model test_model. As previously mentioned, the action can be seen as the link between the menu and the model.
However, menus always follow an architecture, and in practice there are three levels of menus:
The root menu, which is displayed in the App switcher (the Odoo Community App switcher is a dropdown menu)
The first level menu, displayed in the top bar
The action menus


Basic View
Odoo is able to generate default views for a given model. In practice, the default view is never acceptable for a business application. Instead, we should at least organize the various fields in a logical manner.
Views are defined in XML files with actions and menus. They are instances of the ir.ui.view model.
In our real estate module, we need to organize the fields in a logical way:
in the list (tree) view, we want to display more than just the name.
in the form view, the fields should be grouped.
in the search view, we must be able to search on more than just the name. Specifically, we want a filter for the “Available” properties and a shortcut to group by postcode.
Reference: the documentation related to this topic can be found in List.
List views, also called tree views, display records in a tabular form.
Their root element is <tree>. The most basic version of this view simply lists all the fields to display in the table (where each field is a column):
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="doctor_view_tree" model="ir.ui.view">
<field name="name">doctor.view.tree</field>
<field name="model">hospital.doctor</field>
<field name="arch" type="xml">
<tree name="Doctor" string="Doctor" editable="bottom" sample="1" create="false"
delete="false" edit="false" decoration-primary="status=='draft'"
decoration-warning="status=='confirm'"
decoration-success="status=='done'" decoration-danger="status=='cancel'">
<field name="id"/>
<field name="name"/>
<field name="gender"/>
<field name="email"/>
<field name="create_date"/>
<field name="create_uid"/>
<field name="__last_update"/>
<field name="color" widget="color_picker"/>
</tree>
</field>
</record>
</odoo>
Reference: the documentation related to this topic can be found in Form.
Forms are used to create and edit single records.
Their root element is <form>. They are composed of high-level structure elements (groups and notebooks) and interactive elements (buttons and fields):
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="doctor_view_form" model="ir.ui.view">
<field name="name">doctor.view.form</field>
<field name="model">hospital.doctor</field>
<field name="arch" type="xml">
<form name='Doctor'>
<header>
<button id="button_confirm" name="action_status_confirm" string="Confirm" class="btn-warning"
type="object"
confirm="Are you sure you want to confirm?"/>
<button id="button_done" name="action_status_done" string="Done" class="btn-success"
type="object"
confirm="Are you sure you want to set done?"/>
<button id="button_cancel" name="action_status_cancel" string="Cancel" class="btn-danger"
type="object" confirm="Are you sure you want to cancel?"/>
<field name="status" widget="statusbar" statusbar_visible="draft,confirm,done,cancel"
options="{'clickable':'1'}"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group>
<group>
<field name="name"/>
<field name="college"/>
<field name="address"/>
<field name="gender"/>
<field name="email"/>
<field name="phone"/>
<field name="color" widget="color_picker"/>
<field name="card_type" attrs="{'invisible': [('card_number', '=', False)]}"/>
</group>
<group>
<field name="note" readonly="1" force_save="1"/>
<field name="image" widget='image' class='oe_avatar'/>
<field name="category_id" options="{'no_open': True}"/>
<field name="tags_ids" widget="many2many_tags" options="{'color_field': 'color'}" />
<field name="amount" sum="Total" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<field name="credit_card_id" attrs="{'invisible': [('money_type', '!=', 'credit_card')], 'required':[('money_type', '=', 'credit_card')]}" options="{'no_open': True}"/>
<field name="credit_card_info" string = '' attrs="{'invisible': [('credit_card_id', '=', False)]}"/>
</group>
<notebook>
<page string="Description" name='description'>
<field name="description"/>
</page>
<page string="Medicine" name="medicine">
<field name="prescription_medicine_ids">
<!-- here name is One2many relation model filed name in parent model -->
<tree editable="bottom">
<field name="name"/>
<field name="quantity"/>
<field name="create_date"/>
<field name="create_uid"/>
<field name="__last_update"/>
</tree>
<form>
<group>
<group>
<field name="name"/>
<field name="quantity"/>
</group>
</group>
</form>
</field>
</page>
</notebook>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
</odoo>
It is possible to use regular HTML tags such as div and h1 as well as the the class attribute (Odoo provides some built-in classes) to fine-tune the look.
similary there is also an kanban view as well
kanban view
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="doctor_kanban" model="ir.ui.view">
<field name="name">hospital.doctor.kanban</field>
<field name="model">hospital.doctor</field>
<field name="arch" type="xml">
<kanban default_group_by="department_id" decoration-warning="status=='draft'"
decoration-danger="status=='done'"
decoration-success="status=='confirmed'">
<field name="name"/>
<field name="gender"/>
<field name="phone"/>
<field name="email"/>
<field name="status"/>
<field name="college"/>
<field name="department_id"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="oe_kanban_details">
<field name="image" widget='image'
style='width:100px;height:100px;'/>
<h1 class="o_kanban_record_title">
<field name="name"/>
</h1>
<ul>
<li>Gender :
<field name="gender"/>
</li>
<li>Phone :
<field name="phone"/>
</li>
<li>Email :
<field name="email"/>
</li>
<li>College :
<field name="college"/>
</li>
<li>Status :
<field name="status"/>
</li>
<li>Department :
<field name="department_id"/>
</li>
</ul>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
</odoo>
calender view:
<!-- calender view -->
<record id="appointment_view_calendar" model="ir.ui.view">
<field name="name">hospital.appointment.calendar</field>
<field name="model">hospital.appointment</field>
<field name="arch" type="xml">
<calendar string="Appointments" date_start="appointment_date" date_stop="checkup_date"
event_open_popup="true" mode="month" color="name" quick_add="False">
<field name="patient_id"/>
</calendar>
</field>
</record>
pivot view
<!-- pivot view -->
<record id="appointment_pivot" model="ir.ui.view">
<field name="name">kmhospital.appointment.pivot</field>
<field name="model">kmhospital.appointment</field>
<field name="arch" type="xml">
<pivot string="Appointments Pivot View">
<field name="patient_id" type="row"/>
</pivot>
</field>
</record>
Reference: the documentation related to this topic can be found in Search.
Search views are slightly different from the list and form views since they don’t display content. Although they apply to a specific model, they are used to filter other views” content (generally aggregated views such as List). Beyond the difference in use case, they are defined the same way.
Their root element is <search>. The most basic version of this view simply lists all the fields for which a shortcut is desired:
<record id="appointment_search" model="ir.ui.view">
<field name="name">kmhospital.appointment.search</field>
<field name="model">kmhospital.appointment</field>
<field name="arch" type="xml">
<search string="Appointment">
<field name="name"/>
<field name="patient_id"/>
<field name="gender"/>
<field name="phone" groups="km_hospital.group_hospital_admin"/>
<field name="age"/>
<field name="status"/>
<field name="email" filter_domain='['|',('mobile','ilike','self'),
('email','ilike','self')]'/>
<field name="appointed_doctor_id"/>
<separator/>
<filter string="Male" name="male" domain="[('gender', '=', 'male')]"/>
<filter string="Female" name="female" domain="[('gender', '=', 'female')]"/>
<filter string="Type" name="type" domain="[]" context="{'group_by': 'type'}"/>
<group expand="1" string="Group By">
<filter string="Status" name="state" context="{'group_by':'status'}"/>
<filter string="Gender" name="gender" context="{'group_by':'gender'}"/>
</group>
</search>
</field>
</record>
The default search view generated by Odoo provides a shortcut to filter by name. It is very common to add the fields which the user is likely to filter on in a customized search view.
Search views can also contain <filter> elements, which act as toggles for predefined searches. Filters must have one of the following attributes:
domain: adds the given domain to the current search
context: adds some context to the current search; uses the key group_by to group results on the given field name
Reference: the documentation related to this topic can be found in Search domains.
In Odoo, a domain encodes conditions on records: a domain is a list of criteria used to select a subset of a model’s records. Each criterion is a triplet with a field name, an operator and a value. A record satisfies a criterion if the specified field meets the condition of the operator applied to the value.
For instance, when used on the Product model the following domain selects all services with a unit price greater than 1000:
[('product_type', '=', 'service'), ('unit_price', '>', 1000)]
By default criteria are combined with an implicit AND, meaning every criterion needs to be satisfied for a record to match a domain. The logical operators & (AND), | (OR) and ! (NOT) can be used to explicitly combine criteria. They are used in prefix position (the operator is inserted before its arguments rather than between). For instance, to select products “which are services OR have a unit price which is NOT between 1000 and 2000”:
['|',
('product_type', '=', 'service'),
'!', '&',
('unit_price', '>=', 1000),
('unit_price', '<', 2000)]
Amanda Martines 5 days ago
Exercitation photo booth stumptown tote bag Banksy, elit small batch freegan sed. Craft beer elit seitan exercitation, photo booth et 8-bit kale chips proident chillwave deep v laborum. Aliquip veniam delectus, Marfa eiusmod Pinterest in do umami readymade swag. Selfies iPhone Kickstarter, drinking vinegar jean.
ReplyBaltej Singh 5 days ago
Drinking vinegar stumptown yr pop-up artisan sunt. Deep v cliche lomo biodiesel Neutra selfies. Shorts fixie consequat flexitarian four loko tempor duis single-origin coffee. Banksy, elit small.
ReplyMarie Johnson 5 days ago
Kickstarter seitan retro. Drinking vinegar stumptown yr pop-up artisan sunt. Deep v cliche lomo biodiesel Neutra selfies. Shorts fixie consequat flexitarian four loko tempor duis single-origin coffee. Banksy, elit small.
Reply