Skip to content
Home » Dynamic Domain in Odoo 17: Python Custom Domain Guide

Dynamic Domain in Odoo 17: Python Custom Domain Guide

  • Odoo
dynamic domain in Odoo 17

Dynamic Domain in Odoo 17 immediately empowers you to create custom domains using Python and XML. In this tutorial, we explain dynamic domains and how you can implement them in Odoo 17. We use keyphrases such as “dynamic domain in Odoo 17,” “custom domain in Odoo 17,” and “Python domain compute” throughout. In every step, we actively guide you with transition words, short sentences, and familiar language. We also provide code samples and explanations while linking to external resources like the Odoo Documentation.


Introduction

You begin by discovering dynamic domains in Odoo 17, which allow you to filter records dynamically based on computed criteria. To set up a dynamic domain, you compute the domain value directly using Python methods inside your models. This approach improves your business processes by limiting user selections when creating records. In this tutorial, we focus on a common use case: configuring custom domains for a sales team based on the partner associated with a sale order. Furthermore, we incorporate dynamic domains into both Python model definitions and XML view inheritance so that your forms display only the allowed sales teams.

Overview of Dynamic Domains in Odoo 17

Dynamic domains in Odoo 17 help you filter and limit record selections on forms based on conditions evaluated on the fly. You explicitly set up these domains through compute methods in your models. Moreover, they let you create adaptive workflows in which the available options change dynamically according to other field values. For instance, when a sale order has an associated partner, a custom domain can filter the sales teams to show only those that are allowed to serve that partner.

You must note that dynamic domains are crucial if you have many-to-many relationships such as the one between sales teams and partners. By using a dynamic domain, you avoid manual updates and ensure that every change in a partner’s assignment triggers an update in the available selection of teams.

Why Use Custom Dynamic Domains?

Dynamic domains let you automate rule-based filtering in your forms. You actively enforce business rules that affect which teams, products, or records appear when a user selects a given option. In real-world scenarios, companies work with many partners or customers, and they must assign them to the relevant sales teams. Instead of manually updating every record, you calculate the appropriate domain using a Python compute method. This method increases efficiency, reduces errors, and keeps your user interface clean and relevant.

Additionally, dynamic domains improve data consistency. When users see only what is permitted, they avoid selecting ineligible records. Furthermore, you benefit from a maintainable solution, as you centralize the logic in a compute method rather than scattering it across several views or client-side scripts.

Prerequisites and Environment Setup

Before you begin, you must meet several prerequisites. First, ensure you run Odoo 17 with a functioning development environment. Second, make sure you have basic knowledge of Python and XML. You also need to understand how to inherit and override existing models in Odoo. Moreover, verify that you know how to update views by using XML inheritance.

You should also install any necessary development tools such as a Python IDE, Git, and an Odoo community edition for testing. Transitioning step by step will help you clearly understand the process.

The Source Code Overview

In this tutorial, we demonstrate dynamic domains using a step-by-step explanation with two main code components:

  1. A Python file (e.g., models/sale_custom_domain.py) that defines our model changes.
  2. An XML file (e.g., views/sale_custom_domain_views.xml) that updates the corresponding views.

We designed this custom domain for a sales team to filter allowed customers based on the partner selected in a sale order. You will later see that the dynamic domain is computed using a method in the SalesOrder model and then applied to the form view with a domain attribute.

Python Model Implementation

Below is the Python code that defines the models. In this code, we inherit the existing models in Odoo and add new fields to create dynamic behavior.

from odoo import fields, models, api

class SalesTeam(models.Model):
    _inherit = 'crm.team'

    sale_partner_ids = fields.Many2many(
        'res.partner',
        'crm_team_sale_partners_rel',
        'team_id',
        'partner_id',
        string='Allowed Customers'
    )


class SalesOrder(models.Model):
    _inherit = 'sale.order'

    additional_team_id = fields.Many2one('crm.team', string='Sales Team')
    sales_team_domain = fields.Char(
        string='Sales Team Domain',
        compute='_compute_additional_sales_team_domain'
    )

    @api.depends('partner_id')
    def _compute_additional_sales_team_domain(self):
        for order in self:
            if order.partner_id:
                order.sales_team_domain = "[('sale_partner_ids', 'in', [%d])]" % order.partner_id.id
            else:
                order.sales_team_domain = "[]"

Code Explanation

You first import necessary modules from Odoo by using the command from odoo import fields, models, api. Then, in the SalesTeam class, you inherit from the crm.team model and add the sale_partner_ids field as a Many2many relationship between crm.team and res.partner. This field, labeled “Allowed Customers,” specifies which customers are allowed to be assigned to a sales team.

In the SalesOrder model, you inherit from the sale.order model to introduce additional fields. The additional_team_id is a Many2one field that holds a reference to one of the sales teams. Next, you create the sales_team_domain field as a computed Char field. This field will store the dynamic domain expression as a string.

The compute method _compute_additional_sales_team_domain actively evaluates the partner linked to the sale order. If a partner exists, the method constructs a domain expression that filters the sales teams to include only those teams whose sale_partner_ids contain the partner’s ID. Otherwise, the method returns an empty domain ("[]"). This dynamic filtering ensures that, if you change the partner on the sale order, you update the allowed sales teams automatically.

XML View Inheritance

After implementing the models in Python, you must update the corresponding views. The XML code below shows you how to inherit the existing forms and integrate dynamic domains.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
  <data>

    <!-- Inherit CRM Team Form to add sale_partner_ids field -->
    <record id="crm_team_view_form_inherit" model="ir.ui.view">
      <field name="name">crm.team.form.inherit</field>
      <field name="model">crm.team</field>
      <!-- Adjust the inherit_id reference if necessary -->
      <field name="inherit_id" ref="sales_team.crm_team_view_form"/>
      <field name="arch" type="xml">
        <field name="user_id" position="after">
          <field name="sale_partner_ids" widget="many2many_tags"/>
        </field>
      </field>
    </record>

    <!-- Inherit Sale Order Form to add additional_team_id field with custom domain -->
    <record id="sale_order_form_inherit" model="ir.ui.view">
      <field name="name">sale.order.form.inherit</field>
      <field name="model">sale.order</field>
      <!-- Adjust the inherit_id reference to your sale order form view -->
      <field name="inherit_id" ref="sale.view_order_form"/>
      <field name="arch" type="xml">
        <field name="sale_order_template_id" position="after">
          <field name="sales_team_domain" invisible="1"/>
          <field name="additional_team_id" domain="sales_team_domain" options="{'no_create': 1}"/>
        </field>
      </field>
    </record>

  </data>
</odoo>

XML Code Explanation

In the XML file, you start with the XML declaration and <odoo> root element. Within the <data> tag, you include two records:

  1. The first record inherits the CRM Team form view. You add the new field sale_partner_ids next to the existing user_id field. The widget many2many_tags is specified so that the user sees a tag view for selecting allowed customers. You actively instruct the XML engine to locate the proper view through the inherit_id reference.
  2. The second record inherits the Sale Order form view. In this case, you position the new fields after the sale_order_template_id field. First, the computed field sales_team_domain is added with invisible="1" so that it does not display on the form. Next, you add the additional_team_id field with its domain attribute set as sales_team_domain. The attribute options="{'no_create': 1}" prevents the user from creating new records from the dropdown list, which ensures data integrity.

Each XML record actively updates the forms to include dynamic domain behavior. You must adjust the inheritance references (inherit_id) if your base views have different XML IDs in your database.

Detailed Walkthrough: How Dynamic Domain Works

Let us now review step by step how these code snippets work together to produce a dynamic domain:

  1. Model Inheritance and Field Definitions
    You inherit the base models crm.team and sale.order. Then, in the SalesTeam model, you add a Many2many relationship field. This field connects sales teams to allowed partners. In the SalesOrder model, you add a Many2one field to link a sales team to a sale order. Next, you define a computed Char field to store the dynamic domain expression.
  2. Computing the Domain
    As soon as the user selects a partner on the sale order, the compute method _compute_additional_sales_team_domain is triggered. Transitioning quickly, the compute method checks whether a partner exists. If it does, the method creates a valid domain expression using Python string formatting. The domain expression looks like this: [(′sale_partner_ids′,′in′,[partner_id])] This expression tells Odoo to filter the sales teams that include the partner’s ID in the many2many field sale_partner_ids.
  3. Integration with the View
    Once the dynamic domain has been computed, the XML view reads the sales_team_domain field and applies it as a filter to the additional_team_id field. As a result, when the user opens the sale order form, the sales team dropdown displays only the teams that have the relevant partner in their allowed list.
  4. User Experience and Business Logic
    Transitioning to the user experience, the dynamic domain automatically updates in real time. When a user changes the partner on a sale order, the computed domain recalculates, and the dropdown for sales teams adjusts its available options accordingly. In doing so, you ensure that only the appropriate sales teams are available for selection. This dynamic behavior reduces errors and enforces company rules effortlessly.

Hands-On Example and Testing

Let us now simulate a real-world scenario:

Suppose you work at a company where each sales team is assigned a set of customers. When a sale order is created, the responsible sales agent must select a sales team. However, the sales team selection must be filtered so that the agent sees only the teams permitted to manage the customer in question.

You create two sales teams with different allowed customers. Next, you create several partners (customers) in your system. When you assign a customer to a sale order, the dynamic domain computed by the system filters the available sales teams based on whether the customer is present in each team’s allowed list.

Furthermore, you decide to test the functionality. Transitioning into test mode, you assign a partner to a sale order and immediately see the adjusted list in the dropdown. If you change the partner, the list updates instantly. This quick validation confirms the correctness of the compute method and XML view inheritance.

Best Practices When Implementing Dynamic Domains

When you implement dynamic domains in Odoo 17, you must keep the following best practices in mind:

  1. Centralize Logic in Compute Methods:
    You organize your code by centralizing the domain logic in computed fields. This approach makes it easier to maintain and update the business rules later. In our example, the compute method _compute_additional_sales_team_domain handles everything.
  2. Validate the Domain Expression:
    Make sure that the domain expression you build is valid. Transitioning from development to production, you must test all cases where the partner is present and where it is absent. A misconstructed domain may lead to empty dropdown lists or runtime errors.
  3. Keep XML Inheritance Clean:
    In your XML files, you must clearly define your changes. Avoid cluttering your record nodes with unnecessary attributes and ensure that you reference the correct base view IDs.
  4. User Interface Considerations:
    You ensure that computed fields are marked as invisible when they do not need to be shown to maintain a simple user interface. Furthermore, you use the widget attribute (like many2many_tags) so that end users easily interact with the field.
  5. Test Extensively:
    Always test your dynamic domains in various scenarios to ensure that the correct records appear. During testing, use logging and debugging tools to monitor the values of computed fields.

Troubleshooting Common Issues

Despite following best practices, you may encounter issues. Here are some common problems and their solutions:

  • Incorrect Domain Expression:
    If the dynamic domain does not work, ensure that the compute method returns a string that represents a valid domain. You can print the computed value with logging commands to verify its content.
  • XML Inheritance Failure:
    If the view does not update, confirm that the inherit_id references in your XML file match the target view’s XML ID. Transitioning to Odoo 17, these IDs may change or vary by installation.
  • Compute Method Not Triggering:
    Verify that your field dependencies are correctly listed in the @api.depends decorator. In our example, if the partner field is changed, Odoo should automatically recompute the dynamic domain.
  • User Permissions:
    Check that the user has the necessary access rights to view and modify the computed field and its related models. Sometimes, security settings may prevent the view from updating correctly.

Enhancing and Customizing the Domain Further

You can customize the dynamic domain to meet more complex requirements. For example, suppose you want to combine several criteria to filter the sales teams. You may modify the compute method as follows:

@api.depends('partner_id', 'date_order')
def _compute_additional_sales_team_domain(self):
    for order in self:
        if order.partner_id:
            # Additional conditions can be added to the domain
            order.sales_team_domain = "[('sale_partner_ids', 'in', [%d]), ('active', '=', True)]" % order.partner_id.id
        else:
            order.sales_team_domain = "[]"

In this modified version, you add an extra condition that checks if the team is active. Transitioning further, you can enhance the dynamic domain to include time-based conditions, product-specific filters, or even user group checks. Every time you adjust the logic, you must validate that the domain remains correct.

Furthermore, you may integrate these custom domains with other modules. For instance, if you require dynamic filtering for product categories or lead assignments, you can adopt a similar approach: compute a domain dynamically based on the context and then assign it in the view.

Integrating with the Odoo Ecosystem

As you extend your custom dynamic domains, you must align your changes with the evolving Odoo ecosystem. Transitioning often, you update your modules to remain compatible with new Odoo versions. For advanced users, you can integrate the dynamic domain logic into custom modules and share them with your team via external version control systems such as GitHub.

It is also advisable to review the Odoo Developer Documentation regularly, because updates may introduce new APIs or alternative techniques to compute dynamic domains. By staying informed, you ensure that your implementation adheres to the latest best practices.

In-Depth Explanation of the Compute Method

Let’s take a more detailed look at the compute method _compute_additional_sales_team_domain:

  1. Dependency Declaration:
    By using @api.depends('partner_id'), you ensure that any change to the partner_id field triggers a recomputation of the dynamic domain. This mechanism guarantees that the domain remains in sync with the related partner.
  2. Looping Over Records:
    For each sale order in the recordset (for order in self:), you actively check whether the partner exists. This looping mechanism is efficient and leverages Odoo’s ORM to handle multiple records at once.
  3. Dynamic Domain Construction:
    When a partner is available, you construct a domain string using Python’s string formatting. The expression "[('sale_partner_ids', 'in', [%d])]" % order.partner_id.id dynamically inserts the partner’s ID into the domain. Transitioning smoothly, if no partner is specified, the method returns an empty domain represented by "[]".
  4. Extending the Logic:
    You can easily extend this method to include more sophisticated logic. For example, if you want to filter by multiple fields or incorporate conditions based on other records, add extra conditions to the list inside the domain string.

This compute method is the linchpin of the dynamic domain implementation. Every time you change the partner in a sale order, Odoo automatically re-evaluates the computed field, and the updated domain flows seamlessly to the view.

Step-by-Step Testing and Debugging

After writing your code, it is essential to test your changes:

  1. Restart the Odoo Server:
    Transitioning from development to testing, restart your Odoo server so that the new Python models are loaded.
  2. Upgrade the Module:
    Upgrade your custom module using Odoo’s Apps interface. This step ensures that your changes in both Python and XML are applied.
  3. Create Test Records:
    Enter the sale order form and create a new record. First, select a partner and observe whether the additional_team_id dropdown updates automatically according to the dynamic domain.
  4. Examine the Output:
    If you face any issues, use Odoo’s log files to check for errors. You may add debugging print statements in your compute method temporarily to display the computed domain.
  5. Verify the XML Changes:
    Confirm that the view correctly inherits the changes. If the new field is not visible or the domain does not seem to filter correctly, inspect the XML inheritance and adjust your inherit_id references.

These steps help you identify and correct any misconfigurations. Transitioning step-by-step will save you time during development and ensure a smooth user experience once the module is live.

Advanced Topics and Customizations

As you become more comfortable with dynamic domains in Odoo 17, consider the following advanced topics:

Integrating Multiple Dependencies

You can declare multiple field dependencies to compute complex domains. For example:

@api.depends('partner_id', 'date_order', 'user_id')
def _compute_additional_sales_team_domain(self):
    for order in self:
        domain_list = []
        if order.partner_id:
            domain_list.append("('sale_partner_ids', 'in', [%d])" % order.partner_id.id)
        if order.date_order:
            domain_list.append("('active', '=', True)")
        order.sales_team_domain = "[" + ", ".join(domain_list) + "]" if domain_list else "[]"

In this factorized approach, you build a list of domain conditions and join them into a valid domain string. Transitioning, this method is flexible and can accommodate numerous filtering criteria.

Modularizing Code for Reusability

You may want to extract common domain logic into a separate helper function. This modularity makes your code easier to maintain and test. For instance, you can write a utility function that generates a domain based on given parameters and then reuse it across multiple models.

Testing with Automated Scripts

Odoo supports automated tests written in Python. You can write tests to ensure that your dynamic domains function as expected when different fields change. Incorporating automated tests guarantees that your module remains robust as you apply future updates or refactor the code.

User Interface Customization

Beyond the code, you can further enhance the user interface. For instance, if a dynamic domain results in an empty dropdown, you might add a tooltip explaining why no options are available. Transitioning, these small tweaks improve the user experience by providing context and guidance.

External Resources and Further Reading

It is a good idea to supplement your learning with other resources. You can refer to the following links for more information:
Odoo Developer Documentation
Odoo Community Association (OCA)

These external resources offer detailed guides, tutorials, and best practices that will help you develop advanced customizations in Odoo.

Conclusion

In conclusion, you have learned how to implement a dynamic domain in Odoo 17 using the new Python method. You focused on creating a custom domain for the sales team based on the partner associated with a sale order. We actively walked through the Python code that computes the domain expression, and we explained how the XML view inheritance applies that domain. By using active voice, clear transition words, and familiar language, you can now implement similar dynamic functionalities in your own projects.

Moreover, you now understand how to enforce business rules dynamically, which increases consistency and efficiency. As you move forward, remember to test extensively and to integrate user feedback into your customization process. Finally, always keep up to date with the latest Odoo developments by checking the Odoo Documentation.

Thank you for following this comprehensive tutorial on dynamic domains in Odoo 17. We invite you to share your feedback or any additional tips you have discovered on your journey toward mastering Odoo customizations. Happy coding and may your dynamic domains streamline your business workflow!


Below is an outline of the complete source code used in this blog post with explanations for quick reference.

Python Code: models/sale_custom_domain.py

from odoo import fields, models, api

class SalesTeam(models.Model):
    _inherit = 'crm.team'

    sale_partner_ids = fields.Many2many(
        'res.partner',
        'crm_team_sale_partners_rel',
        'team_id',
        'partner_id',
        string='Allowed Customers'
    )


class SalesOrder(models.Model):
    _inherit = 'sale.order'

    additional_team_id = fields.Many2one('crm.team', string='Sales Team')
    sales_team_domain = fields.Char(
        string='Sales Team Domain',
        compute='_compute_additional_sales_team_domain'
    )

    @api.depends('partner_id')
    def _compute_additional_sales_team_domain(self):
        for order in self:
            if order.partner_id:
                order.sales_team_domain = "[('sale_partner_ids', 'in', [%d])]" % order.partner_id.id
            else:
                order.sales_team_domain = "[]"

Explanation:
This code inherits models from crm.team and sale.order. It creates a Many2many field on the sales team and a computed Many2one field on the sale order. The compute method actively creates a domain based on the partner’s ID.

XML Code: views/sale_custom_domain_views.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
  <data>

    <!-- Inherit CRM Team Form to add sale_partner_ids field -->
    <record id="crm_team_view_form_inherit" model="ir.ui.view">
      <field name="name">crm.team.form.inherit</field>
      <field name="model">crm.team</field>
      <field name="inherit_id" ref="sales_team.crm_team_view_form"/>
      <field name="arch" type="xml">
        <field name="user_id" position="after">
          <field name="sale_partner_ids" widget="many2many_tags"/>
        </field>
      </field>
    </record>

    <!-- Inherit Sale Order Form to add additional_team_id field with custom domain -->
    <record id="sale_order_form_inherit" model="ir.ui.view">
      <field name="name">sale.order.form.inherit</field>
      <field name="model">sale.order</field>
      <field name="inherit_id" ref="sale.view_order_form"/>
      <field name="arch" type="xml">
        <field name="sale_order_template_id" position="after">
          <field name="sales_team_domain" invisible="1"/>
          <field name="additional_team_id" domain="sales_team_domain" options="{'no_create': 1}"/>
        </field>
      </field>
    </record>

  </data>
</odoo>

Explanation:
This XML file updates the existing views by adding the sale_partner_ids field in the CRM Team form and the dynamic domain field in the Sale Order form. The additional_team_id field uses the computed domain stored in sales_team_domain to filter the displayed teams.


This extensive blog post provided a step-by-step tutorial on implementing dynamic domains in Odoo 17 using custom Python methods and XML view inheritance. Enjoy tailoring your own custom domains and further enhancing your Odoo ERP system!


Discover more from teguhteja.id

Subscribe to get the latest posts sent to your email.

Leave a Reply

WP Twitter Auto Publish Powered By : XYZScripts.com