GuideMarch 13, 2026

Product Configurator in Odoo 19:
Dynamic Attributes, Variants & Custom Options

INTRODUCTION

Your Sales Team Is Quoting Impossible Combinations. The Warehouse Ships Them Anyway.

A customer orders a "Large / Red / Matte" t-shirt. The warehouse picks a "Large / Red / Glossy" because "Matte" was never actually produced in red. The sales rep created the quote manually, the system accepted it, and nobody caught the mismatch until the customer complained. We see this pattern at every company selling configurable products without proper attribute rules.

Odoo 19's product configurator solves this at the source. Instead of letting users type free-form descriptions, the configurator presents only valid combinations of attributes, enforces exclusion rules, suggests optional products, and generates the exact variant the warehouse needs to pick. It works on sales orders, e-commerce storefronts, and POS terminals.

This guide walks through the full setup: creating attributes and values, choosing the right variant generation strategy, configuring the sales order configurator, defining exclusion rules for impossible combinations, adding optional products for upselling, and publishing everything to your Odoo e-commerce storefront.

01

Setting Up Product Attributes and Values in Odoo 19

Attributes are the building blocks of the configurator. An attribute is a property like "Color" or "Size," and each attribute has values like "Red," "Blue," or "XL." Odoo 19 supports three display types that control how the customer interacts with the selector on both the sales order form and the e-commerce product page.

Display TypeUI RenderingBest ForExample
RadioRadio buttons, one per valueSmall sets (2-5 values)Size: S / M / L / XL
PillsClickable pill buttonsMedium sets, visual selectionStorage: 128GB / 256GB / 512GB
ColorColor swatchesColor attributes specificallyColor: Red / Blue / Black
SelectDropdown menuLarge sets (10+ values)Fabric: 40+ textile options
Multi-checkboxCheckboxes (multiple selection)Non-exclusive add-onsExtras: Engraving, Gift Wrap, Express

Navigate to Sales → Configuration → Attributes (or Inventory → Configuration → Attributes). Create your first attribute:

Python — Creating attributes via XML data or code
# Method 1: Via the UI
# Sales → Configuration → Attributes → New
# Name: "Color"
# Display Type: "Color"
# Variants Creation Mode: "Instantly"

# Method 2: Via XML data file (for module deployment)
<record id="attr_color" model="product.attribute">
    <field name="name">Color</field>
    <field name="display_type">color</field>
    <field name="create_variant">always</field>
</record>

<record id="attr_color_red" model="product.attribute.value">
    <field name="name">Red</field>
    <field name="attribute_id" ref="attr_color"/>
    <field name="html_color">#E74C3C</field>
    <field name="sequence">1</field>
</record>

<record id="attr_color_blue" model="product.attribute.value">
    <field name="name">Blue</field>
    <field name="attribute_id" ref="attr_color"/>
    <field name="html_color">#3498DB</field>
    <field name="sequence">2</field>
</record>

<record id="attr_color_black" model="product.attribute.value">
    <field name="name">Black</field>
    <field name="attribute_id" ref="attr_color"/>
    <field name="html_color">#2C3E50</field>
    <field name="sequence">3</field>
</record>

Assigning Attributes to a Product Template

Attributes don't mean anything until they're assigned to a product template. On the product form, go to the Attributes & Variants tab and add attribute lines. Each line ties an attribute to the product and specifies which values are available for that specific product.

XML — Linking attributes to a product template
<record id="product_tshirt" model="product.template">
    <field name="name">Premium T-Shirt</field>
    <field name="type">consu</field>
    <field name="list_price">29.99</field>
    <field name="attribute_line_ids" eval="[
        Command.create({
            'attribute_id': ref('attr_color'),
            'value_ids': [Command.set([
                ref('attr_color_red'),
                ref('attr_color_blue'),
                ref('attr_color_black'),
            ])],
        }),
        Command.create({
            'attribute_id': ref('attr_size'),
            'value_ids': [Command.set([
                ref('attr_size_s'),
                ref('attr_size_m'),
                ref('attr_size_l'),
                ref('attr_size_xl'),
            ])],
        }),
    ]"/>
</record>
Price Extras on Attribute Values

Each attribute value can carry a price extra that adds to the product's base price. For example, "XL" might add $3.00 and "Organic Cotton" might add $8.00. This is set on the product.template.attribute.value record (the junction between product template and attribute value), not on the global attribute value itself. This means "XL" can cost +$3 on t-shirts but +$10 on hoodies.

02

Variant Generation Strategies: Instantly, Dynamically, or Never

This is the single most important decision in your configurator setup. Odoo 19 offers three variant creation modes, and choosing wrong has real consequences for database size, inventory tracking, and system performance.

ModeWhen Variants Are CreatedInventory TrackingBest ForRisk
InstantlyAll combinations generated when attribute line is savedFull (each variant has its own stock quant)Products with few combinations (<50 variants)Combinatorial explosion with many attributes
DynamicallyVariant created on first sale/purchase orderFull (once the variant exists)Products with many potential combinations (50-10,000)First order for a new combination is slightly slower
Never (no_variant)No product.product record is ever createdNone (stored as text on order line only)Custom text input, non-inventory attributesCannot track stock per combination

The Combinatorial Explosion Problem

Consider a laptop configurator with 5 attributes: Processor (4 options), RAM (3 options), Storage (4 options), Color (3 options), and Keyboard Layout (6 options). With "Instantly" mode on all attributes, Odoo generates 4 x 3 x 4 x 3 x 6 = 864 variants the moment you save the product. Each variant is a product.product record with its own barcode, stock quants, price history, and reordering rules. For a company with 200 configurable products, that's 170,000+ product records — most of which will never be sold.

Python — Checking variant count before it's too late
# In the Odoo shell, check how many variants a template would generate
template = env['product.template'].browse(42)
lines = template.attribute_line_ids

# Calculate the Cartesian product
from functools import reduce
from operator import mul
total = reduce(mul, [len(line.value_ids) for line in lines], 1)
print(f"Template '{template.name}' would generate {total} variants")

# If total > 1000, you almost certainly want 'dynamic' mode
# Check current actual variant count
print(f"Current variants: {template.product_variant_count}")

# List attributes contributing most to the explosion
for line in lines.sorted(key=lambda l: len(l.value_ids), reverse=True):
    print(f"  {line.attribute_id.name}: {len(line.value_ids)} values")
Mix Modes on the Same Product

You can set different creation modes per attribute, not per product. A laptop product can use "Instantly" for Color (3 variants you always stock), "Dynamically" for Processor/RAM/Storage (only create variants as customers order them), and "Never" for "Engraving Text" (a free-text custom field that doesn't need inventory tracking). This hybrid approach gives you the best of all three modes.

Setting the Mode via Code

XML — Attribute with dynamic variant creation
<!-- 'always' = Instantly | 'dynamic' = Dynamically | 'no_variant' = Never -->
<record id="attr_processor" model="product.attribute">
    <field name="name">Processor</field>
    <field name="display_type">radio</field>
    <field name="create_variant">dynamic</field>
</record>

<record id="attr_engraving" model="product.attribute">
    <field name="name">Engraving Text</field>
    <field name="display_type">radio</field>
    <field name="create_variant">no_variant</field>
</record>
03

Using the Product Configurator on Odoo 19 Sales Orders

When a sales rep adds a configurable product to a sales order, Odoo 19 automatically opens the product configurator dialog. This is a modal window that presents each attribute with its available values, enforces exclusion rules in real time, updates the price as selections change, and offers optional products before confirming.

The configurator triggers automatically when a product template has more than one variant. There's no setting to enable — it's built into the sales order line widget. The flow:

  • Step 1: Sales rep selects the product template (e.g., "Premium T-Shirt") on the order line.
  • Step 2: The configurator modal appears showing all attributes with their values.
  • Step 3: As the rep selects values, the price updates live. Excluded combinations are grayed out.
  • Step 4: Optional products (upsells) appear at the bottom. The rep can add them with one click.
  • Step 5: Clicking "Add" creates the order line with the exact variant (or a dynamic variant if one doesn't exist yet).
Python — Programmatically adding a configured product to a sale order
# Useful for integrations that need to create configured order lines via RPC
sale_order = env['sale.order'].browse(101)

# Find or create the exact variant
template = env['product.template'].browse(42)
ptav_ids = env['product.template.attribute.value'].search([
    ('product_tmpl_id', '=', template.id),
    ('product_attribute_value_id.name', 'in', ['Red', 'L']),
])

# _get_variant_for_combination handles dynamic creation
variant = template._get_variant_for_combination(ptav_ids)

if not variant:
    # For 'dynamic' attributes, create the variant on the fly
    variant = template._create_product_variant(ptav_ids)

# Add the order line
env['sale.order.line'].create({
    'order_id': sale_order.id,
    'product_id': variant.id,
    'product_uom_qty': 5,
    # price_unit is computed from base price + attribute extras
})

# Verify the configuration description
line = sale_order.order_line[-1]
print(f"Product: {line.product_id.display_name}")
print(f"Variant values: {line.product_id.product_template_variant_value_ids.mapped('name')}")
print(f"Unit price: {line.price_unit}")

Custom Attribute Values (no_variant mode)

For attributes set to "Never Create Variant" — like engraving text or custom dimensions — the configurator renders an input field instead of selection buttons. The value is stored directly on the sale order line as a product_no_variant_attribute_value_ids or as custom text in product_custom_attribute_value_ids.

Python — Reading custom attribute values from an order line
# After the configurator saves the line
line = env['sale.order.line'].browse(505)

# Standard variant attributes (created a product.product)
for ptav in line.product_id.product_template_variant_value_ids:
    print(f"  {ptav.attribute_id.name}: {ptav.name} (+${ptav.price_extra})")

# no_variant attributes (stored on the line, not the product)
for ptav in line.product_no_variant_attribute_value_ids:
    print(f"  [no_variant] {ptav.attribute_id.name}: {ptav.name}")

# Custom values (free text input)
for custom in line.product_custom_attribute_value_ids:
    attr_name = custom.custom_product_template_attribute_value_id.attribute_id.name
    print(f"  [custom] {attr_name}: '{custom.custom_value}'")
04

Exclusion Rules and Optional Products: Preventing Invalid Combos and Driving Upsells

Exclusion rules are how you tell Odoo "these two attribute values cannot be selected together." Without them, the configurator happily lets customers order a "Matte finish in Transparent" or a "Diesel engine with Electric-only transmission." The invalid combination either gets caught at production (costing rework time) or ships to the customer (costing a return).

Defining Exclusion Rules

On the product template form, go to the Attributes & Variants tab. Under "Variant exclusion rules," define pairs of values that cannot coexist:

XML — Exclusion rules via data file
<!-- Prevent "Matte" finish with "Red" color on the Premium T-Shirt -->
<record id="exclusion_matte_red" model="product.template.attribute.exclusion">
    <field name="product_tmpl_id" ref="product_tshirt"/>
    <field name="product_template_attribute_value_id"
           ref="ptav_tshirt_matte"/>
    <field name="value_ids" eval="[
        Command.set([ref('ptav_tshirt_red')])
    ]"/>
</record>

<!-- Multiple exclusions: "Organic" fabric excludes both "Neon Green" and "Neon Pink" -->
<record id="exclusion_organic_neon" model="product.template.attribute.exclusion">
    <field name="product_tmpl_id" ref="product_tshirt"/>
    <field name="product_template_attribute_value_id"
           ref="ptav_tshirt_organic"/>
    <field name="value_ids" eval="[
        Command.set([
            ref('ptav_tshirt_neon_green'),
            ref('ptav_tshirt_neon_pink'),
        ])
    ]"/>
</record>

When exclusion rules are active, the configurator grays out excluded values in real time. If the user has already selected "Matte," the "Red" color swatch becomes unselectable with a tooltip explaining why. This works identically on sales orders and e-commerce.

Optional Products for Upselling

Optional products appear in the configurator dialog after the customer selects their variant. They are separate products — not attribute values — that complement the main product. Think screen protectors for phones, matching belts for shoes, or extended warranties for electronics.

XML — Defining optional products
<!-- On the Premium T-Shirt, suggest these optional add-ons -->
<record id="product_tshirt" model="product.template">
    <field name="optional_product_ids" eval="[
        Command.set([
            ref('product_matching_cap'),
            ref('product_gift_wrapping'),
            ref('product_tshirt_care_kit'),
        ])
    ]"/>
</record>

<!-- Optional products can themselves be configurable -->
<!-- The "Matching Cap" has its own Color attribute -->
<!-- The configurator pre-selects the same color as the parent product -->
Optional Products Are Recursive

If an optional product is itself configurable, the configurator opens a nested dialog for that product. And if that optional product has its own optional products, those appear too. This is powerful for complex bundles (laptop + case + mouse + warranty) but can create a confusing UX if nested more than two levels deep. Keep it to one level of optional products for the best customer experience.

Programmatic Access to Exclusions

Python — Querying exclusion rules for a product
# Check which combinations are excluded for a product template
template = env['product.template'].browse(42)

exclusions = env['product.template.attribute.exclusion'].search([
    ('product_tmpl_id', '=', template.id),
])

for exc in exclusions:
    source = exc.product_template_attribute_value_id
    targets = exc.value_ids
    print(f"  If '{source.name}' is selected, exclude: "
          f"{', '.join(targets.mapped('name'))}")

# Validate a combination before creating a variant
ptav_ids = env['product.template.attribute.value'].browse([101, 205, 308])
is_valid = template._is_combination_possible(ptav_ids)
print(f"Combination valid: {is_valid}")
05

Publishing Configurable Products on Odoo 19 E-Commerce

The same configurator that works on sales orders renders on your Odoo website's product page. When a customer visits a configurable product, they see attribute selectors (radio buttons, color swatches, dropdowns) that update the price, image, and stock availability in real time via JavaScript — no page reload.

To publish a configurable product on e-commerce:

  • Step 1: On the product template form, toggle "Published" to make it visible on the website.
  • Step 2: Upload variant-specific images. Go to each variant and add photos. Odoo swaps the product image when the customer changes color/style.
  • Step 3: Set per-variant stock. If using "Instantly" or "Dynamically" created variants, each variant tracks its own inventory. The product page shows "In Stock" or "Out of Stock" per combination.
  • Step 4: Configure the "Add to Cart" behavior. Under Website → Configuration → Settings → Shop, choose whether the configurator opens as a modal or inline on the product page.
XML — QWeb template: Custom configurator snippet for e-commerce
<!-- Extend the product page to show a size guide below the Size attribute -->
<template id="product_size_guide"
          inherit_id="website_sale.product"
          name="Size Guide Link">
    <xpath expr="//div[hasclass('js_product')]//table[hasclass('table')]"
           position="after">
        <t t-if="any(
            line.attribute_id.name == 'Size'
            for line in product.attribute_line_ids
        )">
            <a href="#size-guide-modal"
               class="btn btn-link btn-sm mt-1"
               data-bs-toggle="modal">
                View Size Guide
            </a>
        </t>
    </xpath>
</template>

<!-- Show stock per variant on the product page -->
<template id="product_variant_stock"
          inherit_id="website_sale.product"
          name="Variant Stock Display">
    <xpath expr="//div[@id='product_details']//a[@id='add_to_cart']"
           position="before">
        <div class="variant-stock-info mb-2"
             t-if="product_variant and product_variant.sudo().free_qty > 0">
            <span class="badge bg-success">
                <t t-out="int(product_variant.sudo().free_qty)"/> in stock
            </span>
        </div>
        <div class="variant-stock-info mb-2"
             t-elif="product_variant">
            <span class="badge bg-warning">Made to order</span>
        </div>
    </xpath>
</template>

Variant-Specific Images on the Storefront

Odoo 19 swaps the main product image when a customer selects a different variant — but only if variant-specific images are uploaded. Without them, every combination shows the same generic product photo, which hurts conversion rates on visual products like clothing and furniture.

Python — Bulk-uploading variant images via script
import base64
from pathlib import Path

template = env['product.template'].browse(42)

# Map variant attribute values to image files
image_map = {
    'Red': '/opt/images/tshirt_red.jpg',
    'Blue': '/opt/images/tshirt_blue.jpg',
    'Black': '/opt/images/tshirt_black.jpg',
}

for variant in template.product_variant_ids:
    # Get the color value for this variant
    color_val = variant.product_template_variant_value_ids.filtered(
        lambda v: v.attribute_id.name == 'Color'
    )
    if color_val and color_val.name in image_map:
        img_path = Path(image_map[color_val.name])
        if img_path.exists():
            variant.image_1920 = base64.b64encode(img_path.read_bytes())
            print(f"  Set image for {variant.display_name}")

env.cr.commit()
E-Commerce Performance with Many Variants

The e-commerce product page loads all variant combinations into a JavaScript object for instant client-side filtering. For products with 500+ variants, this JSON payload can exceed 200KB, causing a noticeable delay on mobile connections. If your product has more than 200 variants, switch high-cardinality attributes to "Dynamic" mode and consider using the website_sale_product_configurator module's lazy-loading feature, which fetches variant data on demand instead of preloading everything.

06

5 Product Configurator Mistakes That Cost Real Money in Odoo 19

1

Using "Instantly" on High-Cardinality Attributes

A furniture company added 12 fabric options, 8 leg styles, 5 sizes, and 3 armrest types to a sofa product — all set to "Instantly." Odoo generated 1,440 variants on save. The product form took 15 seconds to load, inventory views became unusable, and their nightly stock valuation report went from 10 minutes to 3 hours. They had 40 similar products.

Our Fix

Switch to "Dynamic" for any attribute with more than 5 values. Variants are created on first order and exist only for combinations customers actually buy — typically <10% of the theoretical maximum.

2

Forgetting Exclusion Rules on Safety-Critical Products

An industrial equipment supplier let customers configure electrical panels without exclusion rules. A customer ordered a 120V panel with a 240V-only breaker. The order shipped, the electrician installed it, and the breaker tripped on first power-on. The return, rework, and emergency on-site visit cost $4,200 — 8x the product margin.

Our Fix

Audit every configurable product for invalid combinations. Create exclusion rules for all technically impossible or safety-critical pairings. Test by trying to order every excluded combination — the configurator should prevent selection, not just warn.

3

Not Setting Variant-Specific Images for E-Commerce

A clothing brand published 200 configurable products on their Odoo e-commerce store. Every variant showed the same default photo. Customers couldn't see what "Navy Blue" actually looked like versus "Midnight Blue." Their e-commerce conversion rate was 40% below industry average for configurable products. After adding color-specific images, conversions increased by 28%.

Our Fix

At minimum, upload images for every value of your most visual attribute (usually Color). Use the bulk upload script above or the product image import wizard in the Odoo UI.

4

Archiving Attribute Values Instead of Removing Them from the Product

When a color is discontinued, the instinct is to archive the attribute value. But archiving a product.attribute.value archives every variant across every product that uses it. A company archived "Forest Green" and accidentally made 45 products across 12 templates unavailable for sale — including their best-selling item in that color which still had 2,000 units in stock.

Our Fix

Instead of archiving the global attribute value, remove it from the specific product template's attribute line. This deactivates the variant only on that product. If you need to discontinue a color globally, first run a query to check which products still have stock in that variant.

5

Price Extras Not Reflecting in Pricelists

A B2B company set up attribute price extras ($5 for "XL") and separate pricelists with percentage discounts for wholesale customers. The pricelist discount applied to the base price only, not to the base + attribute extra. An XL item priced at $30 base + $5 extra should have been $31.50 at 10% discount, but the system charged $27 + $5 = $32. Wholesale customers were paying more for XL items than retail customers.

Our Fix

Check your pricelist computation method. If using "Discount" based rules, verify that the price_extra is included in the discount base. In Odoo 19, pricelist rules with "Based on: Sales Price" include the extra by default, but rules "Based on: Other Pricelist" may not cascade correctly. Always test with a real sale order.

BUSINESS ROI

What a Properly Configured Product Configurator Saves Your Business

The product configurator is a standard Odoo feature — no extra license cost. The ROI comes from error reduction, faster quoting, and higher e-commerce revenue:

92%Fewer Invalid Orders

Exclusion rules prevent impossible combinations at the point of sale. No more returns, rework, or angry customers receiving wrong configurations.

3xFaster Quoting

Sales reps select attributes from dropdowns instead of typing descriptions. A configured quote that took 15 minutes now takes 5.

+18%Average Order Value

Optional products (upsells) shown in the configurator add accessories, warranties, and add-ons that sales reps forget to suggest manually.

The hidden ROI is inventory accuracy. When every sale generates an exact variant with a barcode, your warehouse picks the right item every time. No more "the order says Large/Red but we shipped Large/Maroon because the description was ambiguous." Accurate variants mean accurate stock levels, which means accurate reorder points, which means fewer stockouts and less overstock.

SEO NOTES

Optimization Metadata

Meta Desc

Complete guide to Odoo 19 product configurator. Set up attributes, variant generation, exclusion rules, optional products, and e-commerce integration with code examples.

H2 Keywords

1. "Setting Up Product Attributes and Values in Odoo 19"
2. "Variant Generation Strategies: Instantly, Dynamically, or Never"
3. "Using the Product Configurator on Odoo 19 Sales Orders"
4. "5 Product Configurator Mistakes That Cost Real Money in Odoo 19"

Stop Letting Customers Order Impossible Products

Every configurable product without proper attributes, exclusion rules, and variant strategy is a ticking time bomb. It works fine during the demo and the first month of production. Then a customer finds a combination you didn't think of, the warehouse ships something wrong, and the return costs more than the product margin. The configurator is free — the mistakes aren't.

If you're selling configurable products on Odoo 19 and aren't sure your attributes, variants, and exclusion rules are set up correctly, we can audit your catalog. We check variant explosion risk, missing exclusion rules, pricelist interaction bugs, and e-commerce rendering issues. The audit typically takes a day and produces actionable fixes with exact XML/Python code.

Book a Free Configurator Audit