Odoo 18 → 20 · Enterprisepreview

Odoo 18 to 20 Inventory migration: a guide for custom modules

Odoo 20 trims the product, pricelist and barcode plumbing that Inventory customisations usually hook into: controller hooks, duplication overrides, packaging and unit-of-measure fields, and several pricelist rule fields are gone or renamed, so code that extends products and pricing needs the most at

2,739 verified changesInventoryverified on Odoo 20
Preview. Odoo 20 is pre-release; this guide is built from Odoo's development branch and will be refreshed at the official release.
2,739
verified changes
162
models affected
28
modules affected

Change breakdown

Changed method616
Added method383
Changed view / record317
Added view / record294
Changed field222
Removed view / record172
Added field161
Removed method138

Removed and renamed items are what a custom module feels first — they break references at load time. Added and internally-changed items rarely touch existing code.

Where the changes land

stock1,331
product746
stock_barcode147
delivery117
stock_picking_batch102
stock_delivery69
stock_landed_costs42
stock_fleet38

Changes reach 28 modules in all — but they cluster. stock carries the most (1,331 changes), so it's the first place to look if you extend it.

Odoo 20 trims the product, pricelist and barcode plumbing that Inventory customisations usually hook into: controller hooks, duplication overrides, packaging and unit-of-measure fields, and several pricelist rule fields are gone or renamed, so code that extends products and pricing needs the most attention.

Start by grepping your addon for every Odoo field, method and route named in this guide. Most of what breaks here is removal rather than reshaping, so a failed lookup or a dead super() call is the usual symptom. Fix the Python model and controller references first, then your XML views and any JS that calls server routes. That order surfaces the hard errors before the silent ones. This guide tracks the Odoo 20 development branch, which is still pre-release, and will be refreshed when it reaches general availability.

The areas most likely to bite are pricelist rules, product packaging and units of measure, the barcode app's server endpoints, and record duplication. Also expect breakage in the small action methods behind product form buttons, such as updating on-hand quantity. If your module touches any of those, budget real review time. Everything else is mostly a quick rename pass.

What needs the closest look

Product document upload hook is gone

Removed method — ProductDocumentController.get_additional_create_params

The product document controller used to call get_additional_create_params so subclasses could inject extra values when a document was uploaded and created. That hook no longer exists, so an override in your module will simply never be called. Move that logic onto the document model's create flow or another supported extension point.

Removed in Odoo 20 — it was defined as:

def get_additional_create_params(self, **kwargs):

Batched barcode data route removed

Removed method — StockBarcodeController.get_specific_barcode_data_batch

/stock_barcode/get_specific_barcode_data_batch let the barcode client fetch records from several models in one request, doing one search per model plus related records such as units of measure. The route and its controller method are gone. Any custom JS, scanner integration or controller override that calls or extends it will fail and must be repointed at the barcode endpoints that remain.

Removed in Odoo 20 — it was defined as:

@http.route('/stock_barcode/get_specific_barcode_data_batch', type='json', auth='user')
def get_specific_barcode_data_batch(self, kwargs):

Carrier duplication no longer has its own naming override

Removed method — delivery.carrier.copy_data

delivery.carrier used to override copy_data to append "(copy)" to the name when a carrier was duplicated. That model-level override is removed. If your module extends copy_data on carriers, or tests or depends on the duplicated name, re-check the behaviour on Odoo 20 and adjust your override.

Removed in Odoo 20 — it was defined as:

def copy_data(self, default=None):

Pricelist duplication override removed

Removed method — product.pricelist.copy_data

product.pricelist likewise dropped its copy_data override, which suffixed "(copy)" to the name unless a name was passed in. Custom duplication logic that chained onto it, or that relied on that naming, should be retested and simplified accordingly.

Removed in Odoo 20 — it was defined as:

def copy_data(self, default=None):

Pricelist rule 'applied on' toggle removed

Removed field — product.pricelist.item.display_applied_on

display_applied_on was the simplified Product/Category selector on pricelist items that drove which target a rule applied to. It no longer exists. Remove it from your inherited views, domains, defaults and import data, and set rule scope through the fields that remain on product.pricelist.item.

Present in Odoo 18, removed in 20:

display_applied_on = fields.Selection(
        selection=[
            ('1_product', "Product"),
            ('2_product_category', "Category"),
        ],
        default='1_product',
        required=True,
        help="Pricelist Item applicable on selected option")

Percentage price field on pricelist rules removed

Removed field — product.pricelist.item.percent_price

percent_price stored the percentage discount (or mark-up, when negative) for percentage-based pricelist rules. Any code, view, data file or report that reads or writes it will break. Rework those spots against the pricing fields still available on Odoo 20 pricelist items, and check that migrated rules still compute the prices you expect.

Present in Odoo 18, removed in 20:

percent_price = fields.Float(
        string="Percentage Price",
        help="You can apply a mark-up by setting a negative discount.")

'Update quantity on hand' action removed from products

Removed method — product.product.action_update_quantity_on_hand

action_update_quantity_on_hand on product.product was the button action that opened the on-hand quantity editor, delegating to the template with the variant preselected. It is gone, and so is the template version. Remove buttons, server actions or overrides in your module that point at it, and route users to the stock quantity flow Odoo 20 provides instead.

Removed in Odoo 20 — it was defined as:

def action_update_quantity_on_hand(self):

Also worth checking

Pricelists and pricing rules. Beyond the removed fields above, the unit field on product.pricelist.item is renamed from product_uom to product_uom_name, so update related fields, view references and reports that display a rule's unit. The open_pricelist_rules action is removed from both product.product and product.template. Any smart button or menu in your module that called it needs a new target.

Packaging and units of measure. packaging_ids is removed from both product.product and product.template. Custom code that walks a product's packagings, or views that embed that one2many, must be reworked. On the replenishment wizard (product.replenish), product_uom_id becomes uom_id and product_uom_category_id is dropped. Fix any wizard inheritance or default context that sets them. Vendor pricelines (product.supplierinfo) also lose product_uom, so purchase-side customisations that read the supplier's unit need revisiting.

Duplication and product setup. product.tag no longer overrides copy_data, matching the carrier and pricelist changes. If you overrode duplication on tags, retest it. On variants, exclude_for is removed from product.template.attribute.value, which affects any code that inspected attribute exclusions. product.product also drops its init method, which only matters if your module extended it.

Company and settings. The stock_move_sms_validation flag on res.company is removed, so drop it from settings views and any logic that sent SMS on move validation. In res.config.settings, property_stock_journal is renamed to account_interco_clearing_journal_id. Update settings view inheritance and any code reading it.

Beyond these, 1204 smaller internal changes exist. They are only relevant if you override Odoo's private helpers.

Your migration checklist

  1. Grep your addon (Python, XML, JS, CSV/XML data) for every field, method and route named above, and list each hit.
  2. Fix pricelist rule references first: display_applied_on, percent_price, product_uom → product_uom_name, and open_pricelist_rules. Then re-verify computed prices on a copy of real data.
  3. Rework packaging and unit-of-measure code: packaging_ids, product.replenish (uom_id), and product.supplierinfo units.
  4. Replace removed controller hooks and routes (get_additional_create_params, the batched barcode endpoint) and update any JS or external scanner clients that call them.
  5. Remove or repoint buttons and actions using action_update_quantity_on_hand, and retest duplication of carriers, pricelists and tags.
  6. Update settings and company views for the removed SMS flag and the renamed journal setting, then run your module's tests on the Odoo 20 branch.

Related guides

Is your module ready for Odoo 20?
Scan it free — every breaking change above, found in your actual code, with AI fixes verified on a real Odoo server.
Scan your module →