Enable Dark Mode!
how-to-migrate-custom-modules-from-odoo-19-to-odoo-20.jpg
By: Muhammed Fahis V P

How to Migrate Custom Modules from Odoo 19 to Odoo 20

Technical Odoo 20 Odoo Enterprises Odoo Community

Updating the version in the manifest file __manifest__.py is just a first step while migrating a custom module from Odoo 19 to Odoo 20. Odoo 20 brings some changes in ORM, security, binary fields, QWeb, and the JavaScript framework. As a result, a module that works fine in Odoo 19 requires several code modifications before being able to run in Odoo 20.

Among such changes, one may mention the security definition changes in Odoo 20, the replacement of _sql_constraints with a new constraint API, configuration parameter getters becoming typed, binary fields using BinaryValue instead of Base64, and OWL components requiring migration to OWL 3. Changes in official Odoo source code at 20.0 also confirm these changes.

The blog post provides a review of major changes that should be taken into account during the Odoo 19 to Odoo 20 migration process.

What Needs to Be Checked?

The custom module might include Python models, security files, XML views, QWeb templates, JavaScript objects, reports, controllers, and tests. It is necessary to check all these parts during migration.

The main areas covered in this migration are:

  • Module version updating
  • Running odoo-bin upgrade_code
  • Updating the security access file
  • Replace _sql_constraints
  • Updating configuration parameter access
  • Updating binary field processing
  • Migration of OWL 2 code to OWL 3
  • QWeb t-call updating
  • XML views and assets review
  • Python and Hoot tests running

It all depends on how large the module is. A small module, consisting of only several models, can be upgraded without much effort, whereas a larger module, which has a custom dashboard, reports and JS components, will take more time to update.

1. Update the Module Version

Step one is to change the module version in the __manifest__.py.

For example:

'version': '19.0.1.0.0', 

For an Odoo 19 module.

This should be changed to:

'version': '20.0.1.0.0', 

For an Odoo 20 module.

An example manifest could be:

{
    'name': 'Custom Sales Extension',
    'version': '20.0.1.0.0',
    'depends': [
        'sale',
    ],
    'data': [
        'security/ir.access.csv',
        'views/sale_order_views.xml',
    ],
    'installable': True,
}

It is important to note that ir.access.csv is the security file used in this example. This is just one of the security changes that have to be managed during migration. Changing the version alone does not do the migration of the module. It simply tells Odoo which version of the module it is loading.

2. Run odoo-bin upgrade_code

Odoo offers the upgrade_code command, which is used to perform source code modifications needed during migration between Odoo versions. The command is accessible via the odoo-bin file of the Odoo source and uses the migration scripts located in odoo/upgrade_code/.

For the migration from Odoo 19 to Odoo 20, you will have to run it using the Odoo 20 source:

./odoo-bin upgrade_code --addons-path /path/to/custom/addons --from 19.0

The --from 19.0 flag indicates which version of Odoo the source code has been written in. The --addons-path flag specifies the path where the custom modules to migrate reside.

Moreover, you can use the --glob option if you wish to limit the processing to certain files only. The upgrade_code command accepts parameters like --from, --to, --script, --glob, and --dry-run.

Once the command has been executed, check what changes have been applied to your custom module. You should not rely on the upgrade_code execution, as some of the modifications will require manual adjustments.

Namely, after performing the command, check the following folders:

models/
views/
security/
static/src/
tests/

and verify migration-specific components such as:

ir.model.access.csv > ir.access.csv
_sql_constraints
get_param()
BinaryValue
OWL 2 > OWL 3
QWeb t-call

upgrade_code can thus be seen as the beginning of the migration process, not an alternative to the review and testing of the module.

3. Update the Security Access File

Definitions for security access have seen considerable evolution in Odoo 20. In earlier versions of Odoo, custom modules usually include:

security/ir.model.access.csv

in a format like:

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_custom_model_user,Custom Model User,model_custom_model,base.group_user,1,1,1,0

In Odoo 20, the access definition file has been changed to:

security/ir.access.csv

and has the following format:

id,name,model_id,group_id/id,operation,domain

This file is used by Odoo 20.0 itself in its modules' manifests, as in the case of the account module of Odoo 20.

An example of a simple access file for Odoo 20 would be:

id,name,model_id,group_id/id,operation,domain
access_custom_model_user,Custom Model User,custom.model,base.group_user,cru,

The operation column defines CRUD:

c = Create
r = Read
u = Write
d = Delete

In case of

r

read access is granted, while in the case of

cru

create, read, and write access are granted.

All four can be included in:

crud

And the final column is the domain, which can be used if access has to be given only to certain records.

For example:

access_custom_model_user,Custom Model User,custom.model,base.group_user,cru,"[('user_id', '=', user.id)]"

Migration of an existing access file

Assuming that an existing Odoo 19 module includes:

access_custom_model_user,Custom Model User,model_custom_model,base.group_user,1,1,1,0

Then, the corresponding entry in Odoo 20 will be:

access_custom_model_user,Custom Model User,custom.model,base.group_user,cru,

Thus, there are some modifications to be made:

  • Name the ir.model.access.csv file as ir.access.csv.
  • Replace model_id:id with model_id.
  • Make use of the model name, like custom.model instead of model_custom_model.
  • Remove perm_read, perm_write, perm_create and perm_unlink.
  • Include the operation column.
  • Use c, r, u, and d for required operations.
  • Include the domain column when record restrictions are required.
  • Modify the manifest to include the security/ir.access.csv file.

E.g.

'data': [
    'security/ir.access.csv',
]

This is not only about renaming the file. Actually, there are modifications that have been made in the security structure too. Odoo 20 access rights refactoring modifies the concept of access rights and record-level access into the new ir.access security format.

4. Replace _sql_constraints with models.Constraint

Custom models might have an older declaration of _sql_constraints.

For instance:

_sql_constraints = [
    (
        'unique_code',
        'unique(code)',
        'The code must be unique.'
    ),
]

Such a code needs to be updated using the new constraint declaration method.

Using Odoo 20, one should declare a constraint as follows:

from odoo import fields, models

class CustomModel(models.Model):
    _name = 'custom.model'
    _description = 'Custom Model'
    code = fields.Char()
    _code_unique = models.Constraint(
        'UNIQUE(code)',
        'The code must be unique.'
    )

Odoo code uses models.Constraint class to define database constraints. It looks much simpler and cleaner as it is declared inside the model.

Locate _sql_constraints in code.

Searching the custom addons:

grep -R "_sql_constraints" /path/to/custom_addons

All instances found need to be checked carefully. This step is especially crucial since the old constraint is easy to overlook. As a result, the module can be installed and work without any error, although the corresponding database constraint is not applied.

5. The Custom modules frequently utilize ir.config_parameter for saving settings.

Old functions might look like this:

value = self.env['ir.config_parameter'].sudo().get_param( 'custom_module.enabled')

Starting from Odoo 20, the get_param() and set_param() have been substituted with typed configuration functions like get_str(), get_bool(), get_int(), and get_float().

So, for example, for a Boolean parameter:

enabled = self.env['ir.config_parameter'].sudo().get_bool( 'custom_module.enabled')

For an integer:

limit = self.env['ir.config_parameter'].sudo().get_int( 'custom_module.limit') 

For a string:

name = self.env['ir.config_parameter'].sudo().get_str( 'custom_module.name')

And for a float:

amount = self.env['ir.config_parameter'].sudo().get_float( 'custom_module.amount') 

One should apply the same for setting parameters.

For example:

self.env['ir.config_parameter'].sudo().set_bool( 'custom_module.enabled', True) 

To migrate the data, run:

grep -R "get_param" /path/to/custom_module 
grep -R "set_param" /path/to/custom_module 

Then figure out what kind of configuration data to expect.

6. Update Binary Field Handling

Binary fields have also changed in Odoo 20.

The field declaration itself remains familiar:

document = fields.Binary(
    string='Document'
)

One of the key changes lies in the method of dealing with values when working in Python.

With the introduction of Odoo 20, it was decided to make use of the BinaryValue for binary fields. According to the documentation of ORM, binary fields will return a BinaryValue that can be loaded only when actually needed, while the BinaryBytes will be used to create the necessary binary fields.

For instance:

from odoo import fields, models
from odoo.tools import BinaryBytes 
class CustomDocument(models.Model):
  _name = 'custom.document' 
      document = fields.Binary()
 
      def set_document(self, content): 
          self.document = BinaryBytes(content) 

When accessing the content, code that expects a base64 value needs to be updated.

For instance:

data = record.document.content 

How it needs to be done depends on how the custom code is using the value. It is particularly true for modules that:

  • Generate PDF documents
  • Generate Excel documents
  • Process Images
  • Upload Documents
  • Attach documents
  • Transfer files via API
  • Generate downloadable files

Do not update every occurrence of fields.Binary. The migration only impacts the code that accesses or processes the binary value directly.

Check Attachment Handling Too

While examining the module code, ensure that if the custom module has had to create/update any ir.attachment records, those need to be checked too. The binary storage mechanism has been changed in Odoo 20, and hence, any previous design/code relying upon binary strings must be discarded in favour of the new procedure of handling blobs.

Say, if your module is creating attachments, then check if the code is compliant with the present-day binary field handling.

7. Migrate from OWL 2 to OWL 3

During migration, JavaScript is one of the important considerations.

OWL 3 is used by Odoo 20 and, compared to OWL 2, OWL 3 has several incompatible modifications. According to the official OWL migration guide, these modifications involve, among other things, the discontinuation of the    usid,name,model_id,group_id/id,operation,domaineState function, changes in props, modifications in the rendering context, removal of several lifecycle APIs, and changes in t-call.

Replace useState

For example, in Odoo 19, this component has:

import { Component, useState } from "@odoo/owl";
export class MyComponent extends Component {
    setup() {
        this.state = useState({
            count: 0,
        });
    }
}

In OWL 3, useState is removed. The migration guide recommends using the newer reactive APIs, including proxy, for this type of state migration.

For example:

import { Component, proxy } from "@odoo/owl";
export class MyComponent extends Component {
    state = proxy({
        count: 0,
    });
}

The exact replacement depends on how the original component uses its state, so the JavaScript code should be reviewed rather than blindly replacing every occurrence.

Review Component Props

Props have also changed in OWL 3. Code that relies on the old this.props behaviour needs to be reviewed. The OWL 3 migration guide explains that components can explicitly obtain their props using useProps().

import { Component, useProps } from "@odoo/owl";
export class MyComponent extends Component {
    props = useProps();
    setup() {
        console.log(this.props);
    }
}

The component should then be checked to make sure the props it receives are handled using the Odoo 20 API.

Review Plugins and Services

Odoo 20 also changes how some functionality is provided to OWL components.

Older custom code may use environment-based patterns that need to be migrated to the newer plugin approach. The OWL 3 migration documentation describes replacing some uses of the old environment approach with plugins and usePlugin().

For example, custom code using services, environment extensions, or application-level plugins should be reviewed individually.

Check custom code involving:

components
hooks
services
plugins
registries
patches
environment extensions

Do not only check whether the JavaScript file loads. A component may load correctly and still fail when a particular button, action, or service is used.

8. Update QWeb t-call Usage

QWeb templates also need attention during an Odoo 19 to Odoo 20 migration.

One of the OWL 3 changes is related to t-call. The OWL 3 migration guide states that a t-call is restricted to t elements and that the body of a t-call is evaluated lazily, with variables passed as parameters.

Older code may look like this:

<t t-call="custom_module.some_template">
    <t t-set="title" t-value="'Sales Dashboard'"/>
</t>

When migrating, review how values are passed to the called template.

For example, use explicit arguments where the Odoo 20 QWeb syntax expects them:

<t t-call="custom_module.some_template"
   title="'Sales Dashboard'"/>

Then, the template can access the passed value according to the Odoo 20 QWeb context.

Search the module for:

grep -R "t-call=" /path/to/custom_module

Pay particular attention to:

  • reports
  • website templates
  • mail templates
  • backend QWeb templates
  • OWL templates

Every custom t-call should be reviewed rather than assuming that the old template structure will behave in the same way.

9. Review Other QWeb Changes

The QWeb migration should not stop at t-call.

The OWL 3 migration guide also lists changes such as the removal of t-esc and changes to template rendering context.

For example, older code may contain:

<t t-esc="name"/>

which should be reviewed and migrated to the supported output syntax:

<t t-out="name"/>

Also check expressions that previously accessed component variables directly.

For example:

<t t-out="name"/>

may need to become:

<t t-out="this.name"/>

depending on the template and component context. This is especially important for custom OWL components.

10. XML View Review

Normal XML Odoo views need reviewing as well during migration.

Views to be reviewed include:

  • form views
  • list views
  • kanban views
  • search views
  • actions
  • menus
  • QWeb templates
  • reports

Example:

<record id="view_custom_model_form" model="ir.ui.view">
    <field name="name">custom.model.form</field>
    <field name="model">custom.model</field>
    <field name="arch" type="xml">
        <form>
            <sheet>
                <group>
                    <field name="name"/>
                </group>
            </sheet>
        </form>
    </field>
</record>

Inherited views need special consideration.

For instance:

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

In case the Odoo 20 parent view has been changed, then the XPath might no longer refer to the correct element, so it is better to open the Odoo 20 parent view and check the necessary XPath expressions rather than just assuming that the Odoo 19 structure hasn't changed.

11. Check Module Manifest

The whole manifest file should be checked.

Example:

{
    'name': 'Custom Module',
    'version': '20.0.1.0.0',
    'depends': [
        'base',
        'sale',
    ],
    'data': [
        'security/ir.access.csv',
        'views/custom_views.xml',
    ],
    'assets': {
        'web.assets_backend': [
            'custom_module/static/src/js/custom_component.js',
            'custom_module/static/src/xml/custom_templates.xml',
        ],
    },
    'installable': True,
}

The following should be checked when installing the module:

  • Release of module
  • Dependencies
  • Security files
  • XML files
  • Reports
  • Assets
  • Demo sample data
  • Hooks

It can be that the dependency for Odoo 19 is not there in Odoo 20; hence, all dependencies have to be checked first before installing the module.

12. Verify JavaScript Assets

If the module uses JavaScript, verify the assets declaration too.

Example:

'assets': {
    'web.assets_backend': [
        'custom_module/static/src/js/custom_component.js',
        'custom_module/static/src/xml/custom_templates.xml',
    ],
}

Once the module upgrade is done, open the developer console of the browser and verify for errors.

The errors may be as follows:

  • Module not found
  • Template not found
  • Component invalid
  • Props missing
  • Registry error
  • Service errors

If the module uses the OWL components, ensure that the component renders properly and that its events and services function as expected.

13. Review Python ORM Code

In addition to the specific changes identified in the migration, there should be a review of all the Python code.

Do not limit yourself just to the above changes. Custom modules may also rely on ORM methods that were modified in some way.

Review:

  • models
  • fields
  • compute
  • onchange
  • constraints
  • create()
  • write()
  • unlink()
  • controllers
  • wizards
  • cron jobs
  • server actions
  • reports
  • API integrations

For example:

def create(self, vals):
    ...
def write(self, vals):
    ...
def unlink(self):
    ...

Compare this with the Odoo 20 implementation. Another thing is to look for specific APIs that are sensitive to migration in your custom modules:

grep -R "_sql_constraints" /path/to/custom_module
grep -R "get_param" /path/to/custom_module
grep -R "set_param" /path/to/custom_module
grep -R "t-call=" /path/to/custom_module
grep -R "useState" /path/to/custom_module

This provides a good starting point for looking for the code that should be checked.

14. Update Module in Odoo 20

After completing all the changes to the source code, update the module in an Odoo 20 development database.

In case of fresh installation:

python odoo-bin -c odoo.conf -d database_name -i custom_module

In case of an existing module:

python odoo-bin -c odoo.conf -d database_name -u custom_module

Monitor the Odoo logs during the installation process of the module. It should not be assumed that all is well just because the module is installed. A warning relating to the old API and changes in fields means that migration is still required in some parts of the module.

15. Python Test of Module

After loading a module, run the available Python tests for the module.

In case of a custom module, there can be:

tests/
+-- __init__.py
+-- test_custom_model.py

For example:

from odoo.tests import TransactionCase

class TestCustomModel(TransactionCase):
    def test_create_record(self):
        record = self.env['custom.model'].create({
            'name': 'Test Record',
        })
        self.assertEqual(record.name, 'Test Record')

It is essential that all aspects of module functionality are checked.

Check the following areas:

  • record creation
  • record update
  • access control
  • constraints
  • computed fields
  • wizards
  • reports
  • scheduled actions
  • controllers
  • API integration

The objective is to ensure the proper installation and functionality of the module in Odoo 20.

16. Run Hoot Tests

Modules containing JavaScript and OWL components should also have their frontend tests reviewed.

Odoo's Hoot testing framework is used for testing frontend behaviour and OWL components.

For example, a custom dashboard component may need tests covering:

  • Component rendering
  • Props
  • User interactions
  • Reactive state
  • Services
  • Plugins
  • Events
  • Template output

After migrating from OWL 2 to OWL 3, run the relevant Hoot suites and fix any failures.

A JavaScript file loading without a console error does not necessarily mean that the component has been migrated correctly.

17. Test the Module Manually

Automated tests are useful, but the final migration should also be checked from the Odoo interface.

Go through the main workflows of the module.

For example:

  1. Open the module menu.
  2. Create a record.
  3. Edit an existing record.
  4. Test search and filters.
  5. Check user permissions.
  6. Open reports.
  7. Test custom buttons.
  8. Run server actions.
  9. Check scheduled actions.
  10. Upload and download files.
  11. Test custom OWL components.
  12. Check browser console errors.

This is particularly important for modules containing custom views and JavaScript.

A Python test can pass while a custom dashboard or QWeb report still has a problem.

Complete Odoo 19 to Odoo 20 Migration Checklist

Before considering a custom module ready for Odoo 20, use the following checklist.

Module

  • Update the version to 20.0.x.y.z
  • Review module dependencies
  • Run odoo-bin upgrade_code
  • Review the changes made by the migration tool

Security

  • Rename ir.model.access.csv to ir.access.csv
  • Update the manifest to load security/ir.access.csv
  • Replace the old CSV permission columns with operation
  • Use c, r, u, and d for CRUD operations
  • Update model references to the Odoo 20 format
  • Review domains in access definitions
  • Review existing record-level security rules and custom security logic

Python and ORM

  • Review model definitions
  • Replace _sql_constraints with models.Constraint
  • Review get_param() usage
  • Replace configuration parameter access with typed getters
  • Review set_param() usage
  • Update binary field handling
  • Use BinaryValue / BinaryBytes where required
  • Review attachment handling
  • Check overridden ORM methods
  • Review controllers and wizards
  • Review cron jobs and server actions

XML and QWeb

  • Review form, list, kanban, and search views
  • Check inherited views
  • Review XPath expressions
  • Review t-call usage
  • Update t-call arguments where required
  • Review t-esc usage
  • Review template expressions and component context
  • Test QWeb reports and templates

JavaScript and OWL

  • Review OWL 2 code
  • Replace useState with the appropriate OWL 3 reactive API
  • Review component props
  • Review this.props usage
  • Review plugins and services
  • Review registries and patches
  • Check OWL templates
  • Review JavaScript assets
  • Check browser console errors
  • Run Hoot tests

Testing

  • Install the module on Odoo 20
  • Upgrade the module on a migrated database
  • Run Python tests
  • Run Hoot suites
  • Test the main workflows manually
  • Test different user permissions
  • Test reports
  • Test file uploads and downloads
  • Check Odoo logs for warnings and errors

Migrating a custom module from Odoo 19 to Odoo 20 involves more than simply changing the version in the manifest. The module must undergo a review of its Python, security, XML, QWeb, and JavaScript code. It is important to pay attention to specific changes. Security access files now use ir.access.csv with the operation and the domain columns. _sql_constraints should be replaced by models.Constraint. Configuration parameters must utilize the typed getter/setter APIs. As for binary fields, they now utilize BinaryValue. We also need to review the custom OWL code since, in Odoo 20, OWL 3 is used, and changes are introduced in states, props, plugins, and templates.

Using the Odoo-bin upgrade_code can reduce the amount of manual work, and it should only be viewed as a first step of the migration process, not a final one. Once the code is written, you may proceed to install and upgrade your module on an Odoo 20 development database. It is also advised to run Python and Hoot tests. Don't forget to check the important workflows manually as well.

Using a checklist will help you identify migration issues and check whether your custom module is prepared for Odoo 20.

To read more about How to Migrate Custom Modules from Odoo 18 to Odoo 19, refer to our blog, How to Migrate Custom Modules from Odoo 18 to Odoo 19.


Frequently Asked Questions

Can an Odoo 19 custom module be used directly in Odoo 20?

It depends on the module. Simple modules may only need a few minor adjustments, while those that rely on custom security, ORM APIs, binary processing, or OWL components are likely to require more significant updates. The recommended approach is to review the module thoroughly and run proper tests in an Odoo 20 environment before deploying it to production.

Do I only need to change the module version to 20.0.x.y.z?

No. Changing the version in __manifest__.py simply marks the module as compatible with Odoo 20. You still need to go through the Python, security, XML, QWeb, and JavaScript code to check for anything that has changed in Odoo 20 and update it accordingly.

What happened to ir.model.access.csv in Odoo 20?

In Odoo 20, access definitions are placed in: security/ir.access.csv rather than the previous path: security/ir.model.access.csv

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp