
Computed fields in Odoo are powerful tools for automating dynamic values based on other fields. However, their default read-only behavior can limit flexibility when users need to edit these fields. This guide explores how to use inverse functions to make computed fields editable while maintaining data consistency. Whether you’re an Odoo developer or a business user, this blog will help you unlock bidirectional field synchronization in Odoo 18.
What Are Computed Fields?
Computed fields dynamically calculate values using Python logic instead of storing them directly in the database. For example, a total_price field might depend on quantity and unit_price 13. Here’s a basic example:
total = fields.Float(compute='_compute_total')  
@api.depends('quantity', 'unit_price')  
def _compute_total(self):  
    for record in self:  
        record.total = record.quantity * record.unit_price  By default, computed fields are read-only, as their values are derived from other fields
Why Use Inverse Functions?
Inverse functions bridge the gap between computed fields and user input. They allow users to edit a computed field and propagate changes back to its dependencies. For instance:
- If a user edits a totalfield, an inverse function can adjustquantityorunit_priceto maintain consistency 1, 3.
- A capital_letterfield computed fromsmall_lettercan inversely updatesmall_letterwhen edited 2, 10.
- Without inverse functions, computed fields remain static and uneditable, limiting real-world usability 4, 8
Step-by-Step Implementation:
- Define the Compute Method:
 Start by declaring a computed field withcomputeand dependencies
from odoo import models, fields, api  
class SaleOrder(models.Model):  
    _inherit = 'sale.order'  
    total_amount = fields.Float(  
        string='Total Amount',  
        compute='_compute_total_amount',  
        inverse='_inverse_total_amount',  
        store=True  # Required for inverse to work :cite[5]:cite[10]  
    )  
    @api.depends('order_line.price', 'order_line.quantity')  
    def _compute_total_amount(self):  
        for order in self:  
            order.total_amount = sum(  
                line.price * line.quantity  
                for line in order.order_line  
            )  2. Add the Inverse Function:
The inverse method reverses the computation. For example, updating line prices when total_amount changes:
def _inverse_total_amount(self):  
    for order in self:  
        if order.total_amount and order.order_line:  
            avg_price = order.total_amount / len(order.order_line)  
            for line in order.order_line:  
                line.price = avg_price / line.quantity  Here, editing total_amount redistributes the value across order lines 3, 9.
3. Enable Editing with inverse and store=True:
- inverse: Links the field to the inverse method.
- store=True: Stores the computed value in the database, enabling edits 51, 1.
Real-World Use Cases
- Bidirectional Synchronization
Example: Synchronize small_letter and capital_letter fields
capital_letter = fields.Char(  
    compute='_compute_capital',  
    inverse='_inverse_capital'  
)  
@api.depends('small_letter')  
def _compute_capital(self):  
    for rec in self:  
        rec.capital_letter = rec.small_letter.upper()  
def _inverse_capital(self):  
    for rec in self:  
        rec.small_letter = rec.capital_letter.lower()  Editing either field updates the other 2, 10.
2. Adjusting Related Models:
For related fields (e.g., partner_id.street), an inverse function can update the linked model:
street = fields.Char(  
    related='partner_id.street',  
    inverse='_set_street'  
)  
def _set_street(self):  
    for rec in self:  
        rec.partner_id.street = rec.street  This ensures changes cascade to the partner model 5
Best Practices for Inverse Functions:
- Use store=True: Ensures the computed value is stored and editable
- Define Clear Dependencies: Use @api.dependsto trigger recomputations accurately
- Avoid Overcomplication: Keep inverse logic simple to prevent performance issues
- Test Edge Cases: Handle zero divisions (e.g., if order.total_amount) to avoid errors
Common Pitfalls:
- Missing store=True: Inverse functions won’t trigger without it
- Circular Dependencies: Ensure compute and inverse methods don’t create infinite loops
- Performance Overheads: Stored computed fields can slow down large datasets; optimize dependencies
 
				 
 