Working with Savepoint


Here I'll write about Odoo transactions.

It's a general recommendation to let Odoo manage transactions and Odoo does it quite well.

By default a new transaction is opened on every HTTP request thus covering the whole bunch of operations the method executed.

If an exception happends during any method execution the whole transation is rolled back and error is returned.

But sometimes we need to keep some amount of work done in the middle of request and if the rest of the request fails we are saved.

This is done by using self.env.cr.commit() call.

Savepoint

Savepoint defines a new saving point in current transation. So when an error occurs the transation is rolled back to that point.

If no error happens RELEASE SAVEPOINT is called to keep the effects of commands executed after the savepoint was established.

Here is the code snippet from odoo/sql_db.py:

@contextmanager
@check
def savepoint(self, flush=True):
    """context manager entering in a new savepoint"""
    name = uuid.uuid1().hex
    if flush:
        flush_env(self)
    self.execute('SAVEPOINT "%s"' % name)
    try:
        yield
        if flush:
            flush_env(self)
    except Exception:
        if flush:
            clear_env(self)
        self.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
        raise
    else:
        self.execute('RELEASE SAVEPOINT "%s"' % name)

Form's initial mode


When you click on a tree view row a form view is opened and this form is opened in read mode.

When you click the Create button from a tree view, a new record form view is opened in edit mode (how otherwise? :-)

But sometimes it's more usable to immediately open an existing record in edit mode.

This can be done using form option initial_mode.

In different Odoo versions this can be done in different ways:

10.0

In this version it can work in form declaration (by chance as developers say) like in the following example:

<form options="{'initial_mode': 'edit'}">

If transition from tree to form is done within python code:

return {
    'name': action.name,
    'help': action.help,
    'type': action.type,
    'views': [(form_id, 'form')],
    'view_mode': 'action.view_mode',
    'target': action.target,
    'context': action.context,
    'res_model': action.res_model,
    'flags': {'initial_mode': 'edit'},
    'res_id': record.id
}

11.0, 12.0, ...

Next Odoo versions "fix" the above and now you should use context:

context = dict(self.env.context)
context['form_view_initial_mode'] = 'edit'
return {
    'type': 'ir.actions.act_window',
    'view_type': 'form',
    'view_mode': 'form',
    'res_model': 'your.model',
    'res_id': your_object_id,
    'context': context,
}

🙈

New xpath element named "move"


The position='move' has been introduced to move an element in an inherited view.

It's used as

<xpath expr="//@target" position="after">
    <xpath expr="//@node" position="move"/>
</xpath>

Or also:

<field name="target_field" position="after">
    <field name="my_field" position="move"/>
</field>

In the above example my_field is placed after target_field in the parent form.

So now we can inherit and have the ability to manipulate fields order in views, not only add new fields.

Hiding entire tree column with column_invisible


We all know a way to hide a field in tree & form views using attrs={'invisible': [(...)]}.

We also know that we can hide entire column using invisible="1" attribute like this:

<tree decoration-warning="event_input_mode == True">
    <field name="event_input_mode" invisible="1"/>
    <field name="description"/>
    <field name="facility" invisible="context.get('hide_facility')"/>
    <field name="uid"/>
</tree>

In the above example we need event_input_mode column just to highlight a row if it have event_input_mode set so we hide it with invisible="1".

We can also notice that we can use context values in condition expression. In the above example if context has hide_facility key then entire column is hidden.

The treasure

But what if you need to hide entire column by a condition coming from complex business logic implemented in Python?

Here column_invisible attribute comes!

See this examples:

<tree>
    ...
    <field name="foo" attrs="{'column_invisible': [('parent.product_id', '!=', False)]}"/>
    ...
</tree>

By digging the source code I can see column_invisible was introduced in Odoo 11.0.

Nice, but works only for many2one and one2many fields :-)