# Design Patterns in Odoo: Template Method

> What the Template Method pattern is and how Odoo's delivery module uses it to add new shipping carriers without changing core code.

**Source:** <https://trobz.com/insights/design-patterns-odoo-template-method/>

---


Imagine managing shipping in Odoo with every delivery carrier's logic crammed into one place. For each action (getting a rate, sending a package or tracking it), you would face a long `if/elif/else` block, and adding a new carrier would mean changing that code and risking the integrations that already work. It would be hard to scale, fragile and difficult to maintain.

This is where the Template Method pattern comes in. In this post, we look at the pattern and at how Odoo's delivery module implements it.

## What Is the Template Method Pattern?

The Template Method is a behavioral design pattern that defines the skeleton of an algorithm in a base class and lets subclasses override specific steps without changing the overall structure (see [Refactoring.Guru](https://refactoring.guru/design-patterns/template-method)). In simpler terms, it gives you a template for how something is done and lets you fill in the details later. The base class calls a series of methods, some of them abstract or with a default implementation, and subclasses provide the concrete implementation of those methods.

## How Odoo Implements the Pattern in the Delivery Module

Odoo uses the `delivery.carrier` model as the template. It holds the main logic for handling shipments. To add a new shipping provider, you don't modify the core delivery module. Instead, you create a new module and follow three steps:

1. **Inherit the base class.** Create a model that inherits from `delivery.carrier`, so your provider becomes part of the delivery system.
2. **Declare your provider.** Extend the selection options of the `delivery_type` field. This field acts as the dispatcher, telling Odoo which set of methods to use for a given carrier.
3. **Implement the specific steps.** Add the methods your provider needs, following the naming convention `<provider_name>_<method_name>`. The core `delivery.carrier` model calls these methods dynamically based on the selected `delivery_type`, using Python's `hasattr` and `getattr`.

As a result, the main `send_shipping` method in `delivery.carrier` contains no provider-specific logic. It looks at the carrier's `delivery_type` and calls the matching method, such as `dhl_send_shipping` or `fedex_send_shipping`. In Odoo 17.0, this method is defined in the `stock_delivery` module:

```python
def send_shipping(self, pickings):
    ''' Send the package to the service provider

    :param pickings: A recordset of pickings
    :return list: A list of dictionaries (one per picking) containing of the form::
                     { 'exact_price': price,
                       'tracking_number': number }
                       # TODO missing labels per package
                       # TODO missing currency
                       # TODO missing success, error, warnings
    '''
    self.ensure_one()
    if hasattr(self, '%s_send_shipping' % self.delivery_type):
        return getattr(self, '%s_send_shipping' % self.delivery_type)(pickings)
```

## A Real-World Example in OCA

The OCA [`delivery_cttexpress`](https://github.com/OCA/delivery-carrier/tree/17.0/delivery_cttexpress) module shows the pattern in practice. Like most delivery connector modules, it is organized around two model files:

- `cttexpress_request.py`: the interface between Odoo and the carrier's API, here the CTT Express SOAP API.
- `delivery_carrier.py`: inherits `delivery.carrier`, adds `cttexpress` to `delivery_type` and implements the `cttexpress_*` methods, such as `cttexpress_send_shipping`, `cttexpress_cancel_shipment` and `cttexpress_get_label`.

This approach keeps the code clean and modular, and new carriers can be added without touching Odoo's core code.

