You are on page 1of 64

Creating form buttons and actions

http://www.odoo.yenthevg.com/creating-form-buttons-and-actions/

In this tutorial I will learn you how to create buttons in views. The form buttons are always shown at the
top of the form and are very handy to perform actions on your current record. After this tutorial you can
create your own buttons and actions behind the buttons.

1. Creating your model and fields


The first step that you need to do is to create a model and add the fields you need. In my example I will
just create a simple form with two fields named name and password. So my model looks like this:

1 # -*- coding: utf-8 -*2 from openerp import models, fields, api
3
4 class button_action_demo(models.Model):
5 _name = 'button.demo'
6 name = fields.Char(required=True,default='Click on generate name!')
7 password = fields.Char()

This creates a new model in the database named button.demo and will contain two custom added fields:
name and password. The field name will be a required field and by default it will contain the text Click
on generate name!)

2. Creating the view with buttons


After the model fields have been made it is time to create your view. For this example I will create a simple
form view that shows these two fields and I will add three buttons:

1 <record model="ir.ui.view" id="view_buttons_form">


2
<field name="name">Buttons</field>
3
<field name="model">button.demo</field>
4
<field name="type">form</field>
5
<field name="arch" type="xml">
6
<form string="Button record">
7
<!--The header tag is built to add buttons within. This puts them at the top -->
8
<header>
9
<!--The oe_highlight class gives the button a red color when it is saved.
1
It is usually used to indicate the expected behaviour. -->
0
<button string="Generate name" type="object" name="generate_record_name" class="oe_highlight"/>
1
<button string="Generate password" type="object" name="generate_record_password"/>
1
<button string="Clear data" type="object" name="clear_record_data"/>
1
</header>
2
<group>
1
<field name="name"/>
3
<field name="password"/>
1
</group>
4
</form>
1
</field>
5 </record>
1
6
1
7
1
8

1
9
2
0
2
1

Which will look like this:

So, how exactly does Odoo know that the buttons need to be at the top of the form? Because of the header
tag! Everything within this tag will be shown at the top of your form. So, this creates three buttons, but
how do we trigger an action from this button? Have a closer look at the code for one of those buttons:

1<button string="Generate name" type="object" name="generate_record_name" class="oe_highlight"/>

Noticing anything? The type=object is telling us that we have an object and the
name=generate_record_name is a link to a Python function. When the user clicks on the button
Generate name Odoo will trigger the Python function generate_record_name and from there on we can
do whatever we want. So let us create the actions for the buttons!

3. Creating actions behind the buttons


Now that the view is built and we gave all buttons a name that makes the Python go off we should create
the Python functions! Lets first create one for the generate_record_name button:

1 @api.one
2 def generate_record_name(self):
3 #Generates a random name between 9 and 15 characters long and writes it to the record.
4 self.write({'name': ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(randint(9,15)))})

self is defining the current record, so this means we can access anything from that record and modify it.
The self.write will update the values for this record in the database. By saying name: we are telling to
update the field name in the view. In this example I created a simple function that generates a random
name between 9 and 15 characters long.
Tip: @api.one is used for one record and cannot be used for multiple records. If youd want this you would
need @api.multi.
If you would now click on the button a name would be generated from this function and would be shown
directly on the view.
Now create the second function generate_record_password for the button Generate password:

1 @api.one
2 def generate_record_password(self):
3 #Generates a random password between 12 and 15 characters long and writes it to the record.
4 self.write({'password': ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(randint(12,15)))})

This function does almost the same as the previous but it will generate a password between 12 and 15
characters and write it on the field password in place of name.
Next, lets create our third function clear_record_data for the button Clear data. This function will clear all
data from the fields:

1 @api.one
2 def clear_record_data(self):
3
self.write({
4
'name': '',
5
password': ''
6
})

That is all! You can now create buttons with actions and perform changes on the fields.

4. Conclusion

Buttons are very handy in Odoo and can do just about anything. It is easy to perform actions for the user,
change fields, show new views,
Tip: You can also use buttons in combination with attrs and flows to make them even more useful! You can
find this tutorial here.
Do you want to try a demo module and see the source code of this tutorial? You can view on my Github
account. Has this tutorial helped you, do you have any feedback or questions? Post away!

Creating and managing statusbars (selections) in Odoo


In this tutorial I will learn you how to create and manage statusbars. Statusbars are selections (on database level) which
are visually respresented by Odoo. They give you the ability to follow a specific flow and can be very handy to show you
the progress that has been made. After this tutorial you can create and manage your own statusbars.
In this example I will create a new form view which contains a statusbar and Ill add buttons on the form which change the
state of the record.

1. Creating the model and fields


The first step is to create a model (if you dont have one yet) and to add a selection field:

1
2
3
4
5
6
7
8
9
1
0
1
1
1
2
1
3
1
4

class statusbar(models.Model):
_name = 'statusbar.demo'
name = fields.Char('Name', required=True)
"""
This selection field contains all the possible values for the statusbar.
The first part is the database value, the second is the string that is showed. Example:
('finished','Done'). 'finished' is the database key and 'Done' the value shown to the user
"""
state = fields.Selection([
('concept', 'Concept'),
('started', 'Started'),
('progress', 'In progress'),
('finished', 'Done'),
],default='concept')

This creates a new model with the name statusbar.demo which contains two fields, the field name and the field state.
The state field is of the type selection, which means it will contain multiple values which can be chosen. A selection is
always built up in the same way, the first part if the database name and the second part is the text that will be shown to the
user. For example:

1 ('progress', 'In progress'),

If you wish to change the state to In progress you will need to tell the database about progress, since this is the name
the database listens to, while the name shown to the user will be In progress.
This is all you need on the database level. You have your selection values in the database so you only need to show them
to the user now!

2. Creating the view


Now that you have the database part ready it is time to create your view. In this example I will create a simple form view
which shows the field name and the statusbar with some buttons:

1 <record model="ir.ui.view" id="view_statusbar_form">


2

<field name="name">Statusbar</field>

<field name="model">statusbar.demo</field>

<field name="type">form</field>

<field name="arch" type="xml">

<form string="Workflow record">

<!--The header tag is built to add buttons within. This puts them at the top -->

<header>

<button string="Set to concept" type="object" name="concept_progressbar" attrs="{'invisible': [('state', '=', 'concept')]}"/>

1
0

<!--The oe_highlight class gives the button a red color when it is saved.

1
1
1
2
1
3
1
4
1
5
1
6
1
7

It is usually used to indicate the expected behaviour. -->


<button string="Set to started" type="object" name="started_progressbar" class="oe_highlight" attrs="{'invisible': [('state','!
=','concept')]}"/>
<button string="In progress" type="object" name="progress_progressbar" attrs="{'invisible': [('state','=','progress')]}"/>
<button string="Done" type="object" name="done_progressbar" attrs="{'invisible': [('state','=','finished')]}"/>
<!--This will create the statusbar, thanks to the widget. -->
<field name="state" widget="statusbar"/>
</header>
<group>
<field name="name"/>
</group>

1
8
1
9
2
0

</form>

</field>
2
1 </record>
2
2
2
3

So what does this tell us? Let us break this down part by part. The first thing you will notice is the field header.

1 <header>
2 </header>

Everything within these tags will be shown at the top of your Odoo form.
In this code example it will create four buttons and it will also show the field state. The field state has a widget set to it
that is made by Odoo and which will make the typical statusbar, just by adding a widget! This will generate the following

bar at the top of your Odoo screen:

Now there is one more thing I should explain, the buttons. By using those buttons we will modify in which state we are on
the statusbar (selection) and we will control the whole flow. Let us take the button In progress as an example to explain
the code:

1<button string="In progress" type="object" name="progress_progressbar" attrs="{'invisible': [('state','=','progress')]}"/>

So what does this do exactly? It gives the button the text In progress and well give the button the type object, this will
make it accessible in a function from the Python code later on. The name of the button will be the name of the function in
your Python code and last but not least is the attrs. The attrs is a handy feature to make buttons visible/invisible when
youre in a state for example. In this example the button In progress will not be shown when the statusbar is in the state
progress.
Tip: If you have multiple states where buttons should be hidden you can use domains! For example:[(state,=,
[progress,finished])]
Makes sense right? Let us continue and create the button functions!

3. Adding functions for the buttons

Now that the view is ready and you have buttons they should also trigger something. Open up your Python file and create
a function for all of those button names you have. Every button will have a separate Python function and will modify the
current record with a write. In this tutorial I will create four functions that simply change the state of what the current record
is in:

1 #This function is triggered when the user clicks on the button 'Set to concept'
2 @api.one
3 def concept_progressbar(self):
4

self.write({

5
6

'state': 'concept',
})

7
8 #This function is triggered when the user clicks on the button 'Set to started'
9 @api.one
10 def started_progressbar(self):
11

self.write({

12
13

'state': 'started'
})

14
15 #This function is triggered when the user clicks on the button 'In progress'
16 @api.one
17 def progress_progressbar(self):
18

self.write({

19
20

'state': 'progress'
})

21
22 #This function is triggered when the user clicks on the button 'Done'
23 @api.one
24 def done_progressbar(self):
25

self.write({

26
27

'state': 'finished',
})

When you now click on the button In progress it will trigger the function progress_progressbar. The self.write will be
triggered, which is telling Odoo that it should write on the current record, and it will modify the state to finished.
Depending on in which state you are youll see buttons showing up and dissapearing. This is controller by the attrs value

on the buttons. For example when I save my record and set it to in progress:

4. Conclusion
Statusbars (selections) are very handy in Odoo and give you the ability to show the progress to the user in a visual way.
Statusbars are ideal to use in combination with buttons which will modify the state where a record is in. Another great
thing about statusbars is that theyre always at the exact same location and you can find out what the progress is within
seconds.
Do you want to try a demo module and see the source code of this tutorial? You can view on my Github account.
Has this tutorial helped you, do you have any feedback or questions? Post away!

Creating many2many drag and drop (handle) widget in Odoo


In this tutorial I will learn you how to create a handle widget. A handle widget gives you the option to reorder lines in a many2many, making records drag and droppable. This is a very hand feature to order
items and eventually change the order on reports for example.
An example from before re-arranging a many2many and after:

1. Creating the model and fields


For a drag and drop (handle) widget you will need a many2many and for having a many2many you need to
create a link to another table. The first step is to create a new table, in case you dont already have one, or
otherwise you can skip to part 2. The table where you want to link your many2many to needs atleast one
field (such as name) and what you absolutely need is a field named sequence. The sequence field is an
integer and will keep track of the order on your many2many. For example:

1 class many2many_default_data_demo(models.Model):
2

_name = 'sale.order.handle'

_description = 'Model for many2many (handle)'

_order = 'sequence'

name = fields.Char('Naam')

sequence = fields.Integer('sequence', help="Sequence for the handle.",default=10)

As you can see I created a field named sequence which is an integer. The second thing that you need for
this field is to give it a default value, otherwise it will not work correctly. After youve created your model
and fields its time to create your many2many.

2. Creating the many2many (widget)


The second step is to create a many2many which links from the table where you want to use the
many2many to the table where you want to link it with. For example:

1
2
3

class print_order_sample(models.Model):
def _get_default_print_order_ids(self):
return self.pool.get('sale.order.handle').search(self.env.cr, self.env.uid, [])

4
5
6

_inherit = 'sale.order'
print_handle_ids = fields.Many2many('sale.order.handle','sale_handle','order_id','order_handle_id','Many2many order',help='This could be
used to create a print order for your report, for example.This is a drag an drop (handle) widget.',default=_get_default_print_order_ids)

As you can see Ive inherited the model sale.order since I want to create my many2many in this model. I
then created a new field and created a link to the table sale.order.handle. Ive also created a default which
links to a function. The idea behind this is to automatically fill the many2many handle with all the data
from the model sale.order.handle. Ofcourse you dont have to, this is just for the demo.
Tip: Out of safety uses you should never create a table longer than 16 characters in a many2many! The
postgreSQL database cannot handle a many2many link longer than 64 characters in total. For more
information see this bug report.

3. Creating the view


Now that youve created both your model and the many2many you only need to add the many2many to
your view. The code will look like this:

1 <record id="view_orderinherit_orderlines" model="ir.ui.view">


2

<field name="name">sale.order.form.inherit.many2many.handle</field>

<field name="model">sale.order</field>

<field name="inherit_id" ref="sale.view_order_form"/>

<field name="arch" type="xml">

<!--Adds a new page (tab) to the view after the tab Order Lines-->

<xpath expr="//page[@string='Order Lines']" position="after">

<page name="many2manyhandledemo" string="Many2many handle demo">

<group>

10

<field name="print_handle_ids">

11

<tree string="Printing order" editable="bottom">

12

<field name="sequence" widget="handle"/>

13

<field name="name"/>

14

</tree>

15

</field>

16

</group>

17
18
19

</page>
</xpath>
</field>

20 </record>

There isnt to much difference with a normal many2many, there are just three things you should always
do.
1. Create your own tree to render the many2many correctly.
2. always add the field sequence in this tree.
3. call the widget with widget=handle. This will make the many2many drag and droppable.
And thats it, youve created your own many2many handle which makes items drag and droppable. The
result will look something like this:

Do you want to see the source code or try this module for yourself? You can download / view it on Github.
Has this tutorial helped you, do you have any feedback or questions? Post away!

Xpath expressions in Odoo 8


In this tutorial I will learn you how to use xpath expressions. Xpath expressions give you the ability to add
new pages, groups, fields, to an already existing view. When you inherit a currently existing view in a
module and want to extend it you will absolutely need xpath expressions. In this sample I will add new
pages, groups and fields to the view under sales > products.
Tip:view my sample xpath module.

1. Create new fields


The first step is to create new fields inside your model. For example:

1 # -*- coding: utf-8 -*2


3 from openerp import models, fields, api
4
5 class on_change_function(models.Model):
6

#Inhertis the model product.template

7
8

_inherit = 'product.template'

#Creates two new fields (CostPrice and ShippingCost) in the model product.template

1
0

CostPrice = fields.Float('Buy price')

1
1
1
2

ShippingCost = fields.Float('Shipping Cost')


FieldAfterGroup = fields.Char(string='Field After Group')
FieldNewPage = fields.Char(string='Field New Page')

2. Inherit existing view


When youve created your fields you need to show them. In this example I will inherit the view from the
product module which is showed under sales > products. You can reference another view with the ref=
tag. The first step is to find the name of the view you want and then adding the module name in front of it.
For example:

1 <record id="view_product_form_inherit" model="ir.ui.view">


2 <field name="name">product.template.common.form.inherit</field>
3 <field name="model">product.template</field>

4 <field name="inherit_id" ref="product.product_template_form_view"/>

This now links to the product module to the view product_template_form_view (which you can find in
product_view.xml under the product module). After weve inherited the view we can add the xpath
expressions.

3. Xpath expressions
xpath expressions are basically paths to the page / group / field inside the view that youve inherited and
are always built in the same way. Something along these lines:

1 <xpath expr="//element[@string='ElementName']" position="yourPosition"/>

Since the syntax is always a bit different I will simply give an example of each. The idea is to give the
name from the page / group / field / .. so that Odoo can find the correct location to insert your new fields.

3.1 XPATH PAGE

1 <xpath expr="//page[@string='Information']" position="after">


2 <page name="Sample" string="Custom page">
3
4
5

<group>
<field name="FieldNewPage"/>
</group>

6 </page>
7 </xpath>

This code will create a new page after the currently existing page named Information. The new page will
be named Custom page and will show the field FieldNewPage.

Which will result in the following page:

3.2 XPATH GROUP

1 <xpath expr="//page[@string='Information']/group" position="after">


2 <group>
3

<field name="FieldAfterGroup"/>

4 </group>
5 </xpath>

This code will create a new group after the group, inside the page with as title Information and will show
the field FieldAfterGroup in it.
Which will result in the following page:

3.3 XPATH FIELDS

1 <xpath expr="//field[@name='standard_price']" position="before">


2 <field name="CostPrice" on_change="on_change_price(CostPrice,ShippingCost)"/>
3 <field name="ShippingCost" on_change="on_change_price(CostPrice,ShippingCost)"/>
4 </xpath>

This code will show the fields CostPrice and ShippingCost after the field standard_price. This is by far
the easiest xpath expression and in usual cases this will work just fine.
This code will result in the following page:

Tip: there are three options for the position tag:

1 <xpath expr="//field[@name='standard_price']" position="before">

2 <xpath expr="//field[@name='standard_price']" position="inside">


3 <xpath expr="//field[@name='standard_price']" position="after">

These are the most used xpath expressions but there are many more, as you can see in the source code of
Odoo.
xpath is very powerfull and has a lot of options but sadly there is barely any documentation about it. Ive
created a sample module which you can view on my Github account.

Remove Odoo Support from Odoo chat


Today Ill learn you how to remove the Odoo Support user from the Odoo chat. Most of us do not want this
to show up in our list of people to chat with and especially not when youre selling Odoo to customers.
Originally in Odoo you will get this:

So how do you remove this user?


1. Go to your addons folder and search for the addon im_odoo_support. Open this folder up, youll now
see this structure:

2. Open up the folder views and then open the XML fileim_odoo_support.xml. In this file you can directly
see that there is a function and it looks to Odoo Support. So go ahead and comment this block out, like
this:

1 <?xml version="1.0" encoding="utf-8"?>


2 <openerp>
3

<data>

4
5

<!-- <template id="assets_backend" name="im_odoo_support assets" inherit_id="web.assets_backend">

6
7
8
9
1
0

<xpath expr="." position="inside">


<script type="text/javascript" src="/im_odoo_support/static/src/js/im_odoo_support.js"></script>
<script type="text/javascript">

1
1

(function() {
openerp.im_odoo_support.support = new openerp.im_odoo_support.OdooSupport(

1
2

"<t t-esc="request.session.login"/>",

1
3

"<t t-esc="request.registry['ir.config_parameter'].get_param(request.cr, request.uid, 'database.uuid')"/>"


);

1
4
1
5
1
6

})();
</script>
</xpath>
</template>-->

1
7
</data>
1
8 </openerp>
1
9
2
0

2. Go to im_odoo_support/static/src/xml/ and open the fileim_odoo_support.xml. Youll see an li class


here for Odoo Support too. Comment it out like this:

1 <?xml version="1.0" encoding="UTF-8"?>


2
3 <templates xml:space="preserve">
4
5

<t t-extend="UserMenu">
<t t-jquery=".dropdown-menu li:eq(3)" t-operation="after">

<!--

<li class="odoo_support_contact">

<a data-menu="odoo_support" href="#">Odoo Support</a>

9
10
11

</li>-->
</t>
</t>

12 </templates>

3. Now restart your Odoo server and update your module. You can do it with this command:
./odoo.py -u im_odoo_support

4. Open Odoo in your browser and youll see there is no more Odoo Support user in the list!

Inheriting and modifying QWeb reports


In this tutorial you will learn how to inherit already existing QWeb reports in a custom module. After this
tutorial you will be able to inherit and modify any already existing QWeb report in Odoo!
In this example I will modify the default Quotation / order report through inheritance and I will remove a
few elements from the report.

1. Creating a new module


The first step is to create a new module. The correct behaviour in Odoo is to always inherit, dont ever
modify existing files. Start by creating yourself a new module.
Tip: use the scaffold command from Odoo!

This will create a new module from scratch and the default structure of your module is already there.

2. Creating your XML file


The next step is to open up your XML file (in my example templates.xml) and to create a new record to
inherit the report. To inherit a report you have to know in which module it is and what the original XML id of
the report is. So, how do you find this?
The easiest way is to go to Settings > Reports > Reports and to search the report you want to modify.
In this tutorial this will be the quotation/order report so Ill search for Order:

Now that youve found your report click on it. This will open a new view where you will find all the technical
information you need! For example with the quotation report:

At the right you will see a clickable link named Search associated QWeb views. Click on it. This will show
you a list of all records that are associated to this specific report. In the example of the quotation report:

So this usually shows you two XML records. How do you know which one you need? The one that ends with
_document is the correct XML record that you need to inherit. Under the column External ID you will see
there is an unique name, in this example sale.report_saleorder_document. The first part of this text (sale)

is the module where the report is from, the second part (report_saleorder_document) is the name of the
report that you want to inherit and modify.
Remember this value and now open up your XML file. To inherit a QWeb report you need two things: an
unique template id and the inherit_id. The template id can be chosen by yourself, just make sure its
unique. The inherit_id should contain the value youve just found on your report
(sale.report_saleorder_document).

1 <!-- Inherit quotation report (from module sale) -->


2 <template id="report_quotation_inherit_demo" inherit_id="sale.report_saleorder_document">
3 </template>

That is it! Youre now already on the correct report and are inheriting it. So, how do you now add or remove
elements? To do this you will need Xpath expressions to find, modify, replace or add elements. Tip: Dont
know how Xpath expressions work? Follow my tutorial here!
For this example I will remove the columns that show the amount, the tax and the price per item. The first
step is to modify the table header:

1 <!-- Finds the first table with as class table table-condensed and gives the ability to modify it
2 This will replace everything withing tr (including tr)-->
3 <xpath expr="//table[@class='table table-condensed']//thead//tr" position="replace">
4

<tr style="background-color:lightgray;">

<th>Description</th>

<th class="text-right">Price</th>

</tr>

8 </xpath>

After modifying the table header the table content should also be modified.

1 <xpath expr="//tbody[@class='sale_tbody']//tr//td[4]" position="replace">


2 </xpath>

3 <xpath expr="//tbody[@class='sale_tbody']//tr//td[3]" position="replace">


4 </xpath>
5 <xpath expr="//tbody[@class='sale_tbody']//tr//td[2]" position="replace">
6 </xpath>

This code will remove the fourth, third and second td element and all its content but only for the tbody
with class sale_tbody and inside the tr.
So this will replace the header and the table content from this report. Have a look at the full code to inherit
and modify your QWeb report:

1 <openerp>
2

<data>

<!-- Inherit quotation report (from module sale) -->

<template id="report_quotation_inherit_demo" inherit_id="sale.report_saleorder_document">

5
6
7
8

<!-- Finds the first table with as class table table-condensed and gives the ability to modify it
This will replace everything withing tr (including tr)-->
<xpath expr="//table[@class='table table-condensed']//thead//tr" position="replace">
<tr style="background-color:lightgray;">

9
1
0
1
1

<th>Description</th>

1
2
1
3
1
4
1
5
1
6
1
7
1
8

<th class="text-right">Price</th>
</tr>
</xpath>
<xpath expr="//tbody[@class='sale_tbody']//tr//td[4]" position="replace">
</xpath>
<xpath expr="//tbody[@class='sale_tbody']//tr//td[3]" position="replace">
</xpath>
<xpath expr="//tbody[@class='sale_tbody']//tr//td[2]" position="replace">
</xpath>
</template>
</data>

1
9 </openerp>
2
0
2
1

If you would now print out the report you would get this as a result:

3. Adding the dependency for the external QWeb report


The next, and final step, is to add a dependency. Because this QWeb report is inside another module Odoo
has to know about this module and its content so you should add a dependency. Without this your module
will not work and you will get errors.
Open up your __openerp__.py file in your custom module and find the line with depends.
Now take back that External ID from the QWeb report and take the first part of the external id (before the
dot). This tells you which module you should add as a dependency:

In this example my QWeb comes from the sale module, so I will add it as a dependency.

1 # any module necessary for this one to work correctly


2 'depends': ['sale'],

4. Conclusion
Thats it! Youre done with inheriting and modifying the QWeb report. When you now install this module in
your Odoo you will see the modified report.
Do you want to try a demo module and see the source code of this tutorial? You can view on my Github
account.

on_change event in Odoo 8


In this tutorial I will learn you how to create an on_change event that automatically updates a field when
another field changes. In this sample I will create two new fields under sales > products in the tab
Procurements. When a value changes on one of the two fields a third field will be updated with the total
of the other two fields.

1. Creating new fields


The first step is to create new fields in the Python file. For example:

1 # -*- coding: utf-8 -*2


3 from openerp import models, fields, api
4
5 class on_change_function(models.Model):
6

#Inhertis the model product.template

_inherit = 'product.template'

8
9
1
0

#Creates two new fields (CostPrice and ShippingCost) in the model product.template
CostPrice = fields.Float('Buy price')
ShippingCost = fields.Float('Shipping Cost')

This adds two fields (CostPrice and ShippingCost) to the model product.template, which I first inherited.
Both these fields are for numbers. After adding your new fields you need to add them to the view.

2. Add fields to view


In case youre coding in the current module you can simply add the fields like this:

1
2

<field name="CostPrice" on_change="on_change_price(CostPrice,ShippingCost)"/>


<field name="ShippingCost" on_change="on_change_price(CostPrice,ShippingCost)"/>

In case you have a new view you can simply add them but if you want to add them to an existing view you
will need to inherit the record from the other module, just like in my sample. For example:

1 <openerp>
2
3

<data>
<record id="view_product_form_inherit" model="ir.ui.view">

<field name="name">product.template.common.form.inherit</field>

<field name="model">product.template</field>

<field name="inherit_id" ref="product.product_template_form_view"/>

<field name="arch" type="xml">

<xpath expr="//field[@name='standard_price']" position="before">

<field name="CostPrice" on_change="on_change_price(CostPrice,ShippingCost)"/>

1
0
1
1
1
2

<field name="ShippingCost" on_change="on_change_price(CostPrice,ShippingCost)"/>


</xpath>
</field>
</record>
</data>

1
3 </openerp>
1
4

1
5

3. On_change function
Noticing anything in this code? Ive already added the on_change event in the view like this:

1 on_change="on_change_price(CostPrice,ShippingCost)"

This will search for the function on_change_price in your module and sends two values to the function,
CostPrice and ShippingCost. This are the two fields that will contain the values and in these variables are
now the values from the two fields. Were almost there now, the only thing you need to do now is create
the function on_change_price in your Python file. Return to your Python file and add the following code:

#This method will be called when either the field CostPrice or the field ShippingCost changes.

def on_change_price(self,cr,user,ids,CostPrice,ShippingCost,context=None):

3
4

#Calculate the total

total = CostPrice + ShippingCost

6
7

res = {
'value': {
#This sets the total price on the field standard_price.

'standard_price': total

9
1
0
1
1
1
2

}
}
#Return the values to update it in the view.
return res

In the variables CostPrice and ShippingCost are the values that the user has entered. The variable total
simply contains the sum of CostPrice and ShippingCost and this should now be filled in to our third field,
which is named standard_price. After the value is calculated and added to the new field it should be
returned so that the user can see it.

The result:

1
2
3
4

#Import logger
import logging
#Get the logger
_logger = logging.getLogger(__name__)

2. Using logging to print/log statements


After importing logging you can directly start writing log statements. There are five different states for
logging pre-built:

logger.debug: for debugging purposes.


logger.info: for showing information.
logger.warning: for showing minor issues.
logger.error: for showing that an operation has failed
logger.critical: for showing that something has gone horribly wrong. In Odoo these will be used to
notify a module doesnt work anymore.

You can use logging anywhere in Python and at any time as you wish. In this tutorial I will create a simple
field and button. When the button is printed I will print all kind of pre-built logging states to the terminal
for demo purposes. Ive created a button that calls the function print_log_data:

1
2
3
4
5
6
7
8
9

def print_log_data(self, cr, uid, ids,record,context=None):


#These are all the logging types.
_logger.debug("Use _logger.debug for debugging purposes, nothing else.")
_logger.info("Use _logger.info for information messages. This is used for notifying about something important.")
_logger.warning("Use _logger.warning for minor issues, which will not crash your module.")
_logger.error("Use _logger.error to report a failed operation.")
_logger.critical("Use _logger.critical for critical message: when this goes off the module will not work anymore.")
#Want to include data from your field? Pass it along in the context, get it from the pool or from the dict.
_logger.critical("The name '" + str(record.get('name')) + "' is not okay for us!")

As you can see logging is very simple in Odoo. Choose what kind of logging it is and then simply add a text
to it. if you want to use variables from your view you can simply call them in Python and add them to the
message. For example:

1 logger.critical("The name '" + str(record.get('name')) + "' is not okay for us!")

When you click on the button the logging statements will be printed in your terminal. For example with my
function:

Thats it! It is as simple as that to log data in Odoo. If you want to go further though, there are quite some
extra options.

3. Advanced logging configuration


Odoo goes a bit further than this though! You can configure multiple logging options in Odoo:

logfile: the log filename. If this is not filled in all the log details will be written to stdout (terminal).

log_level: any value in the list of [debug_rpc_answer, debug_rpc, debug, debug_sql, info,

warn, error, critical]. Odoo changed the log_level meaning here because these level values are
mapped to a set of predefined module:log_level pairs.
log_handler: can be a list of module:log_level pairs. The default value is :INFO it means the

default logging level is INFO for all modules.


logrotate: True/False. If True, create a daily log file and keep 30 files.
log_db: True/False. If this is set to True the logging will also be written to the ir_logging table in the

database.
For example if I define a logfile in the terminal:

When you now go to this location you will see there is a new logfile created and that it is automatically
filled with the data that you defined:

Do you want to see the source code or try this module for yourself? You can download / view it on Github.
Has this tutorial helped you, do you have any feedback or questions? Post away!

Creating many2many drag and drop (handle) widget in Odoo


JULY 12, 2015 YENTHE666 LEAVE A COMMENT

Hi guys,
In this tutorial I will learn you how to create a handle widget. A handle widget gives you the option to reorder lines in a many2many, making records drag and droppable. This is a very hand feature to order
items and eventually change the order on reports for example.
An example from before re-arranging a many2many and after:

1. Creating the model and fields


For a drag and drop (handle) widget you will need a many2many and for having a many2many you need to
create a link to another table. The first step is to create a new table, in case you dont already have one, or

otherwise you can skip to part 2. The table where you want to link your many2many to needs atleast one
field (such as name) and what you absolutely need is a field named sequence. The sequence field is an
integer and will keep track of the order on your many2many. For example:

1 class many2many_default_data_demo(models.Model):
2 _name = 'sale.order.handle'
3 _description = 'Model for many2many (handle)'
4 _order = 'sequence'
5 name = fields.Char('Naam')
6 sequence = fields.Integer('sequence', help="Sequence for the handle.",default=10)

As you can see I created a field named sequence which is an integer. The second thing that you need for
this field is to give it a default value, otherwise it will not work correctly. After youve created your model
and fields its time to create your many2many.

2. Creating the many2many (widget)


The second step is to create a many2many which links from the table where you want to use the
many2many to the table where you want to link it with. For example:

1
2
3
4
5
6

class print_order_sample(models.Model):
def _get_default_print_order_ids(self):
return self.pool.get('sale.order.handle').search(self.env.cr, self.env.uid, [])
_inherit = 'sale.order'
print_handle_ids = fields.Many2many('sale.order.handle','sale_handle','order_id','order_handle_id','Many2many order',help='This could be
used to create a print order for your report, for example.This is a drag an drop (handle) widget.',default=_get_default_print_order_ids)

As you can see Ive inherited the model sale.order since I want to create my many2many in this model. I
then created a new field and created a link to the table sale.order.handle. Ive also created a default which
links to a function. The idea behind this is to automatically fill the many2many handle with all the data
from the model sale.order.handle. Ofcourse you dont have to, this is just for the demo.
Tip: Out of safety uses you should never create a table longer than 16 characters in a many2many! The
postgreSQL database cannot handle a many2many link longer than 64 characters in total. For more
information see this bug report.

3. Creating the view


Now that youve created both your model and the many2many you only need to add the many2many to
your view. The code will look like this:

1 <record id="view_orderinherit_orderlines" model="ir.ui.view">


2
<field name="name">sale.order.form.inherit.many2many.handle</field>
3
<field name="model">sale.order</field>
4
<field name="inherit_id" ref="sale.view_order_form"/>
5
<field name="arch" type="xml">
6
<!--Adds a new page (tab) to the view after the tab Order Lines-->

7
<xpath expr="//page[@string='Order Lines']" position="after">
8
<page name="many2manyhandledemo" string="Many2many handle demo">
9
<group>
10
<field name="print_handle_ids">
11
<tree string="Printing order" editable="bottom">
12
<field name="sequence" widget="handle"/>
13
<field name="name"/>
14
</tree>
15
</field>
16
</group>
17
</page>
18
</xpath>
19 </field>
20 </record>

There isnt to much difference with a normal many2many, there are just three things you should always
do.
1. Create your own tree to render the many2many correctly.
2. always add the field sequence in this tree.
3. call the widget with widget=handle. This will make the many2many drag and droppable.
And thats it, youve created your own many2many handle which makes items drag and droppable. The

result will look something like this:

Do you want to see the source code or try this module for yourself? You can download / view it on Github.
Has this tutorial helped you, do you have any feedback or questions? Post away!

Automatically fill Many2many in Odoo

In this tutorial I will learn you how to automatically fill a Many2many with data. In some cases a
many2many always has to have data so in that case having it filled in automatically is really handy.

1. Creating the model


The first step is to create a new model with fields from which we want to get data. (in case you already
have a model where you want to load from skip to chapter 2)
In my example I want to create a many2many which gets all records from the model sale.order.printorder
and automatically adds them, so lets first create the model sale.order.printorder:

1 class many2many_default_data_demo(models.Model):
2
_name = 'sale.order.printorder'
3
_description = 'Model for many2many'
4
name = fields.Char('Name')

2. Create and fill many2many


After the model is made we now have to fill up the many2many. Create a many2many to your other model
and add the default parameter to it:

1
2
3
4
5
6
7
8

class print_order_sample(models.Model):
def _get_default_print_order_ids(self):
cr = self.pool.cursor()
self.env
return self.pool.get('sale.order.printorder').search(cr, self.env.uid, [])
_inherit = 'sale.order'
print_order_ids = fields.Many2many('sale.order.printorder','sale_order_print','print_id','order_print_id','Print order',help='This could be used
to create a print order for your report, for example.',default=_get_default_print_order_ids)

As you can see there is a default=_get_default_print_order_ids which links to a function in the class. This
function will automatically get all data from the model sale.order.printorder thanks to self.pool. After the
pool got all records we will return all the data and it will be added to the many2many.
Now lets create a view that shows the many2many:

1 <record id="view_orderinherit_orderlines" model="ir.ui.view">


2
<field name="name">sale.order.form.inherit.many2many</field>
3
<field name="model">sale.order</field>
4
<field name="inherit_id" ref="sale.view_order_form"/>
5
<field name="arch" type="xml">
6
<!--Adds a new page (tab) to the view after the tab Information -->
7
<xpath expr="//page[@string='Order Lines']" position="after">
8
<page name="many2manydemo" string="Many2many demo">
9
<label for="print_order_ids" string="printorder records:"/>

<field name="print_order_ids"/>
10
</page>
11
</xpath>
12
</field>
13
</record>
14

When a user would now create a new quotation/order there will be a new tab named Many2many demo
which is automatically filled with all records from the model sale.order.printorder:

3. Creating default records for sale.order.printorder


Want to go further and want to have the records generated by default when the module is installed? Then
create a new XML file where you create new records. Ive created a file defaultdata.xml in my module and
added the following records:

1 <?xml version="1.0" encoding="utf-8"?>


2 <openerp>
3
<data noupdate="1">
4
<!--This will create some default records if they do not exist yet. We will always need these so these are auto-rendered. -->
5
<record id="goals_print_order" model="sale.order.printorder">
6
<field name="name">Goals</field>
7
</record>
8
<record id="workmethod_print_order" model="sale.order.printorder">
9
<field name="name">Work method</field>
1
</record>
0
<record id="approach_print_order" model="sale.order.printorder">
1
<field name="name">Approach</field>
1
</record>
1
<record id="orderlines_print_order" model="sale.order.printorder">
2
<field name="name">Orderlines</field>
1
</record>
3
<record id="about_print_order" model="sale.order.printorder">
1
<field name="name">About</field>
4
</record>
1
<record id="customer_print_order" model="sale.order.printorder">
5
<field name="name">Customer</field>
1
</record>
6
<record id="references_print_order" model="sale.order.printorder">
1
<field name="name">References</field>
7
</record>
1
</data>
8 </openerp>
1
9
2
0
2
1
2
2

2
3
2
4
2
5
2
6
2
7

TIP: Add the filename defaultdata.xml to __openerp__py under data.


When the module is installed these records will be automatically created in the database and will never be
updated thanks to the noupdate=1. When you need a many2many filled with default data that always
has to be the same this is perfect. If this is not the case simply create a new view where users can create
new records in to the model sale.order.printorder!
Do you want to see the source code or try this module out? You can download / view it on Github!
Has this tutorial helped you, do you have any feedback or questions? Post away!

Installing default data in Odoo module


In this tutorial I will learn you how to install data in a module by default. This will mean that data is
automatically inserted in the database whenever you would install this module. In some cases this is very
handy because you need some default values inserted when the module is installed.

1. Adding a new XML file to the module


The first thing you need to do is create a new XML file in your custom module, for example demodata.xml,
and to add the filename to __openerp__.py. An example of my __openerp__.py file:

1 # -*- coding: utf-8 -*2{


3
'name': "default_data_demo",
4
5
'summary': """
6
A demo that shows how default data is created in code and how it is inserted in the Odoo db.""",
7
8
'description': """
9
A demo that shows how default data is created in code and how it is inserted in the Odoo db.
1
""",
0
1
'author': "Yenthe Van Ginneken",
1
'website': "http://www.odoo.yenthevg.com",
1
2
# Categories can be used to filter modules in modules listing
1
# Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
3
# for the full list
1
'category': 'Uncategorized',
4
'version': '0.1',
1
5
# any module necessary for this one to work correctly
1
'depends': ['base'],
6
1
# always loaded
7
'data': [
1
# 'security/ir.model.access.csv',
8
'templates.xml',
1
#This will link to a file in the folder data. This file, defaultdata.xml will contain the data that is automatically installed when the module
9 is installed.
2
'data/defaultdata.xml',
0
],
2
# only loaded in demonstration mode
1
'demo': [
2
'demo.xml',
2
],

2
3
2
4
2
5
2
6
2
7
2
8
2
}
9
3
0
3
1
3
2
3
3
3
4
3
5

Create default data


After youve added the location to the file (to __openerp__.py) you should create the data for the file. Open
up your custom module, go in to the folder data and edit your XML file. The structure is always the same.
You give the record a name (this can be anything, make sure its unique) and add the name of the model
where you want data installed in. Then add a field for every field you have in the model which you want to
fill with data. The following is an example that creates a record in the model demo.default.data when you
install the module:

1 <record id="first_course_item" model="demo.default.data">


2
<field name="name">Record #1</field>
3
<field name="description">A description for record #1.</field>
4 </record>

As you can see by this record with demo data it will create a new record with a name named Record #1
and a description with the text A description for record #1. These are the fields that Ive created in my
custom model (models.py) like this:

1 # -*- coding: utf-8 -*2


3 from openerp import models, fields, api
4
5 class default_data_demo(models.Model):
6
_name = 'demo.default.data'
7
_description = 'Demo default data'
8
name = fields.Char('Name', required=True)
9
description = fields.Html('Description')

When you would now install this module it will automatically install the records that are defined.

Want to see an example module where you can see how everything works? You can download/view it on
Github!
Has this tutorial helped you, do you have any feedback or questions? Post away!

You might also like