2025, Nov 25 19:00

Fixing Missing Products on Odoo Portal Pages: Record Rules for sale.order.line and Related Models

Why products don't show for Odoo portal users when sale orders are visible, and how to fix it with record rules and access rights on sale.order.line in Odoo

When exposing business objects in Odoo’s website/portal, it’s easy to get tripped up by record rules and access rights. A common case is showing sale orders and their products to portal users: orders appear, but products stubbornly refuse to load. The setup looks correct at first glance, yet the website doesn’t render the product data.

Problem setup

The goal is to display both sales orders and products on the website for users in base.group_portal. Record rules and access rights were introduced for sale.order, product.template, and product.product. The result: sales orders became visible, but products did not.

The rules and rights below illustrate the situation.

<record id="rule_portal_so_by_final_recipient_alt" model="ir.rule">
    <field name="name">Portal: SO by Final Recipient (alt)</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="groups" eval="[(4, ref('base.group_portal'))]"/>
    <field name="domain_force">[('final_recipient_id', '=', user.partner_id.id)]</field>
</record>
<record id="rule_portal_prod_template_open_alt" model="ir.rule">
    <field name="name">Portal: Product Template (alt)</field>
    <field name="model_id" ref="product.model_product_template"/>
    <field name="global" eval="False"/>
    <field name="groups" eval="[(4, ref('base.group_portal'))]"/>
    <field name="domain_force">[]</field>
</record>
<record id="rule_portal_prod_product_open_alt" model="ir.rule">
    <field name="name">Portal: Product Variant (alt)</field>
    <field name="model_id" ref="product.model_product_product"/>
    <field name="global" eval="False"/>
    <field name="groups" eval="[(4, ref('base.group_portal'))]"/>
    <field name="domain_force">[]</field>
</record>
acc_prod_product_user,acc_prod_product_user,product.model_product_product,base.group_user,1,1,1,1
acc_prod_product_portal,acc_prod_product_portal,product.model_product_product,base.group_portal,1,0,0,0
acc_prod_template_user,acc_prod_template_user,model_product_template,base.group_user,1,1,1,1
acc_prod_template_portal,acc_prod_template_portal,model_product_template,base.group_portal,1,0,0,0
acc_sale_order_portal,acc_sale_order_portal,model_sale_order,base.group_user,1,1,1,1
acc_sale_order_user,acc_sale_order_user,model_sale_order,base.group_portal,1,0,0,0

Even forcing a trivial domain such as (1, "=", 1) didn’t change the outcome, and no other overlapping rules were present in the portal group configuration.

Why it happens

In Odoo, access to data is enforced on every model involved in a request. Making sale.order visible is not enough when the website needs to traverse related models. If a view or controller fetches or renders fields that depend on other objects, each of those models must also be readable under the current user’s security context. If one link in the chain lacks a permissive record rule, the data resolution stops there, and the UI ends up empty on that part.

In this case, the path that feeds product information also touches sale.order.line. With rules only for sale.order, product.template, and product.product, the portal user still didn’t have a clear path to the necessary related records, so the product portion didn’t render.

Fix

The issue was resolved by adding a rule that grants portal users read access to sale.order.line. In similar scenarios, you may need to ensure every related model used by the website route or view has a compatible rule.

<record id="rule_portal_so_line_open_alt" model="ir.rule">
  <field name="name">Portal: Sale Order Line (alt)</field>
  <field name="model_id" ref="sale.model_sale_order_line"/>
  <field name="groups" eval="[(4, ref('base.group_portal'))]"/>
  <field name="domain_force">[]</field>
</record>

Why this matters

Portal-facing features frequently cross multiple models: orders, lines, products, templates. If even one of these lacks a rule consistent with the intended visibility, the final UI looks incomplete. Understanding that Odoo evaluates access at each hop prevents hours of trial and error and avoids risky workarounds.

Takeaways

When publishing data to portal users, think in terms of the full data graph. Start from the object you display, list the related models your views and routes touch, and ensure each one has the appropriate record rule and access rights. If something shows up partially—orders but not products—check the intermediate models involved in that resolution path and add the missing rule there. This focused approach keeps security intact while ensuring the website reliably renders the data your users expect.