# Odoo 19 to 20 Website & eCommerce migration: a guide for custom modules

> A large release for Website and eCommerce where several public-facing controller routes have been removed or reshaped, so most of the risk for custom modules sits in the cart, delivery, donation and checkout flows rather than in the data model.

Start by mapping where your module hooks into the storefront: any JavaScript that calls Odoo's shop endpoints, any controller you subclass, and any template you inherit. This release moves a good deal of the cart and checkout plumbing around, so the fastest way to find your exposure is to grep your addon for the old route paths and for the controller methods you override, then work outward from there. Because much of the churn is in HTTP/JSON routes rather than in fields, a broken frontend is more likely to show up as a silent 404 than as a load error, so test the buying journey end to end.

This guide tracks the Odoo 20 development branch, which is still pre-release, and it will be refreshed at general availability once the APIs settle. The areas most likely to bite are the cart quantity and clearing flow, the delivery pickup-location lookups, and the donation payment path — all of which had their public routes removed and reorganised.

## What needs the closest look

### Reading the cart quantity from its own endpoint is gone

*Removed method — `Cart.cart_quantity`*

The dedicated route that returned the number of items in the cart (with a session fallback) has been removed. If your frontend polls this endpoint or you override it to tweak the badge count, move to the current cart update flow that returns quantities as part of its response instead of calling this separately.

Present in Odoo 19, **removed in 20**:

```python
@route(
    route='/shop/cart/quantity',
    type='jsonrpc',
    auth='public',
    methods=['POST'],
    website=True
)
def cart_quantity(self):
    if 'website_sale_cart_quantity' not in request.session:
        return request.cart.cart_quantity
    return request.session['website_sale_cart_quantity']
```

### The empty-the-cart route no longer exists

*Removed method — `Cart.clear_cart`*

The route that unlinked every order line to clear the cart is gone. Any custom 'clear cart' button or automation that hit this path needs to be repointed at the current cart handling, and if you overrode it to add cleanup logic, that logic needs a new home.

Present in Odoo 19, **removed in 20**:

```python
@route(
    route='/shop/cart/clear',
    type='jsonrpc',
    auth='public',
    website=True
)
def clear_cart(self):
    request.cart.order_line.unlink()
```

### Looking up pickup locations has moved

*Removed method — `Delivery.website_sale_get_pickup_locations`*

The public route that fetched nearby pickup points from a zip code (using GeoIP or the delivery address country) has been removed. If your delivery integration calls this endpoint or extends it to add carriers, you'll need to hook into the reworked pickup-location handling rather than this route.

Removed in Odoo 20 — it was defined as:

```python
@route('/website_sale/get_pickup_locations', type='jsonrpc', auth='public', website=True)
def website_sale_get_pickup_locations(self, zip_code=None, **kwargs):
```

### Setting the chosen pickup location has moved

*Removed method — `Delivery.website_sale_set_pickup_location`*

Its companion route that stored the customer's selected pickup point on the order is also gone. Custom delivery modules that persist extra data when a location is chosen should move that behaviour to the new mechanism that pairs with the lookup change above.

Removed in Odoo 20 — it was defined as:

```python
@route('/website_sale/set_pickup_location', type='jsonrpc', auth='public', website=True)
def website_sale_set_pickup_location(self, pickup_location_data):
```

### The donation payment page route is removed

*Removed method — `PaymentPortal.donation_pay`*

The controller that rendered the donation form (a donation-flavoured variant of the standard payment page, carrying donation options and prefilled amount descriptions) no longer exists. If your module links to /donation/pay or customises the donation form, rework it against the current payment flow, as donation handling is no longer a bespoke route.

Removed in Odoo 20 — it was defined as:

```python
@http.route('/donation/pay', type='http', methods=['GET', 'POST'], auth='public', website=True, sitemap=False, list_as_website_content=_lt("Donation Payment"))
def donation_pay(self, **kwargs):
```

### The donation transaction endpoint is gone

*Removed method — `PaymentPortal.donation_transaction`*

The route that validated a minimum donation amount and created the transaction (including public-partner handling) has been removed alongside the donation page. Any custom validation or partner logic you layered on this needs to be reimplemented within the standard transaction path.

Removed in Odoo 20 — it was defined as:

```python
@http.route('/donation/transaction/<minimum_amount>', type='jsonrpc', auth='public', website=True, sitemap=False)
def donation_transaction(self, amount, currency_id, partner_id, access_token, minimum_amount=0, **kwargs):
```

### The 'website info' sitemap generator was dropped

*Removed method — `Website.sitemap_website_info`*

The helper that conditionally added the /website/info page to the sitemap (only when those views were active) has been removed. If you relied on that page appearing in the sitemap or extended its generator, you'll need to register your own sitemap entry.

Removed in Odoo 20 — it was defined as:

```python
def sitemap_website_info(env, rule, qs):
```

## Also worth checking

**Checkout and product pages.** Several eCommerce helpers on the sale controllers have been dropped and are worth a look if you extended checkout. The express-checkout path lost its separate tax-computation helper for the shipping address, so if you adjusted taxes during express checkout, follow that logic into the current flow. The old product-page route and the image-clearing helper on the sale controller are gone, as is the check that decided whether to show the product configurator — if you gated the configurator or cleaned up product images through these, revisit those overrides. Kit-based availability also changed: the helper that reported unavailable quantities coming from MRP kits has been removed, which matters if your module surfaces stock messages for kit products.

**Backend dashboard and installs.** On the website backend, the access-rights check and the module-install tracking helpers have been removed, so any customisation of the dashboard's create-permission logic or its 'installing modules' progress needs rechecking.

**Other apps built on Website.** A scattering of app-specific controllers lost helper methods: partner-assignment lost its partner detail method, the forum lost its URL-title helper, and recruitment lost the check that spotted a recent duplicate application — relevant only if you extended those specific pages. On the model side, `account.payment` no longer carries the `is_donation` flag (in line with the donation rework above), and on `blog.post` both the `post_date` field and the custom `copy_data` have been removed, so any code that read a blog post's publish date through that field or relied on its duplication behaviour must be updated. The `social` helper on the website model has also gone.

Beyond these, roughly 2314 smaller internal changes exist — compute helpers and body-only edits — only relevant if you override Odoo's private helpers.

## Your migration checklist

1. Grep your module for the removed route paths (/shop/cart/quantity, /shop/cart/clear, /website_sale/get_pickup_locations, /website_sale/set_pickup_location, /donation/pay, /donation/transaction) in both Python and JavaScript, and repoint or reimplement each caller.
2. Review any controller you subclass in Website/eCommerce for the removed methods, especially around cart, delivery pickup, donation and express checkout, and move your added logic into the current flows.
3. Update model references: remove or replace uses of account.payment.is_donation and blog.post.post_date, and rework anything relying on blog.post.copy_data.
4. Re-test the full buying journey (add to cart, update quantity, clear cart, choose pickup location, checkout) and the donation flow in a staging build, watching for silent 404s.
5. If you customised the website backend dashboard or app-specific pages (forum, recruitment, partner assign), verify those still load after the helper removals.
6. Re-run this pass at Odoo 20 GA, since these are pre-release APIs and some routes may settle differently.
