How to Create & Manage Custom Fields from Functions in Odoo 18
By Braincuber Team
Published on January 16, 2026
In modern ERP systems, static data is rarely enough. Businesses often need fields that respond intelligently to other data points—calculating credit scores based on payment history, determining delivery priorities based on location, or grading customer loyalty based on sales volume. Odoo 18 handles this elegantly through Computed Fields.
This tutorial explores how to create and manage custom fields using Python functions in Odoo 18. We will move beyond simple data entry and build a dynamic "Customer Loyalty Score" system that automatically updates based on a customer's purchase history. This approach ensures your data remains live, accurate, and actionable without manual intervention.
What You Will Learn:
- Inheriting existing Odoo models (res.partner)
- Defining computed fields with Python dependencies
- Implementing business logic in triggered functions
- Optimizing performance with
@api.depends - Displaying computed fields in the user interface
The Scenario: Customer Loyalty Scoring
We want to add a field called Loyalty Tier to the Contact form. Instead of manually selecting "Gold" or "Silver," the system should calculate this automatically based on the total confirmed sales orders linked to that customer.
- Platinum: Sales > $10,000
- Gold: Sales > $5,000
- Silver: Sales > $1,000
- Bronze: Sales <= $1,000
Step 1: Inherit the Model
First, we create a new Python file in our custom module's models directory. We need to inherit the res.partner model to add our new field.
from odoo import models, fields, api
class ResPartner(models.Model):
_inherit = 'res.partner'
loyalty_tier = fields.Selection([
('platinum', 'Platinum'),
('gold', 'Gold'),
('silver', 'Silver'),
('bronze', 'Bronze')
], string='Loyalty Tier', compute='_compute_loyalty_tier', store=True)
@api.depends('sale_order_ids.amount_total', 'sale_order_ids.state')
def _compute_loyalty_tier(self):
for partner in self:
# Calculate total confirmed sales
total_sales = sum(
order.amount_total
for order in partner.sale_order_ids
if order.state in ['sale', 'done']
)
# Assign Tier
if total_sales >= 10000:
partner.loyalty_tier = 'platinum'
elif total_sales >= 5000:
partner.loyalty_tier = 'gold'
elif total_sales >= 1000:
partner.loyalty_tier = 'silver'
else:
partner.loyalty_tier = 'bronze'
Note on store=True: By default, computed fields are not stored in the database; they are calculated on-the-fly. We added store=True so we can search, filter, and group by this field in reports.
Step 2: Understanding @api.depends
The @api.depends decorator is critical for performance and correctness. It tells Odoo when to re-calculate the field.
In our code: @api.depends('sale_order_ids.amount_total', 'sale_order_ids.state') ensures the function runs whenever:
- A new Sales Order is linked to the partner.
- A Sales Order's total amount changes.
- A Sales Order's state changes (e.g., from 'Draft' to 'Sale').
Step 3: Add to the User Interface
Now that the backend logic is ready, we need to display the field on the Contact form. We'll add it right below the tags.
<odoo>
<record id="view_partner_form_inherit_loyalty" model="ir.ui.view">
<field name="name">res.partner.form.inherit.loyalty</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='category_id']" position="after">
<field name="loyalty_tier"
widget="badge"
decoration-warning="loyalty_tier == 'gold'"
decoration-info="loyalty_tier == 'platinum'"
decoration-muted="loyalty_tier == 'silver'"/>
</xpath>
</field>
</record>
</odoo>
We use the widget="badge" and decoration-* attributes to give the field a visual pop, making the tiers instantly recognizable with colors.
Step 4: Update the Manifest
Don't forget to include the new view file in your module's manifest.
{
'name': 'Customer Loyalty',
'version': '18.0.1.0.0',
'depends': ['base', 'sale_management'],
'data': [
'views/res_partner_views.xml',
],
'installable': True,
}
Why Use Computed Fields?
Automation
Eliminate manual data entry errors. The system updates itself instanly as business activities occur.
Analytics
When stored, these fields become powerful dimensions for Pivot tables and Graph views.
Advanced Tips for Developers
- Inverse Functions: You can make computed fields editable by defining an
inversefunction that writes changes back to the source fields. - Performance: Avoid complex calculations (like searching millions of records) inside strict loops. Use standard Python optimization and Odoo's
read_groupwhen aggregating large datasets. - Search Functions: To search on a non-stored computed field, you must define a custom
searchmethod.
Frequently Asked Questions
@api.depends decorator (e.g., @api.depends('order_line_ids.product_id.standard_price')) to listen for changes in related records.
store=True is the best practice.
