Product Configurator

product_configurator
REPOSITORY
REPOSITORYOCA/product-configurator
GIT
GIThttps://github.com/OCA/product-configurator.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/product-configurator/tree/18.0/product_configurator
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYGeneric Modules/Base
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONYes
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Pledra
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Pledra
COMMITTERS
COMMITTERSRuchir Shukla, Weblate, OCA-git-bot, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/product-configurator
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:30:08
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - account
    - base_setup
    - base
    - web
    - onboarding
    - product
    - mail
    - bus
    - web_tour
    - html_editor
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - resource
    - stock
    - barcodes_gs1_nomenclature
    - barcodes
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES mako
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module has all the mechanics to support product configuration. It
serves as a base dependency for configuration interfaces.

Code Analysis

Views touched (23)
XML IDNameModelTypeStatus
config_step_form_view product.configurator.config.step.form product.config.step form New
config_step_tree_view product.configurator.config.step.tree product.config.step form New
configurator_settings_view_form res.config.settings.view.form.inherit.sale res.config.settings xpath Inherits base.res_config_settings_view_form
product_attribute_form_view product.attribute.form.view product.attribute xpath Inherits product.product_attribute_view_form
product_attribute_tree_view product.config.product.attribute.tree product.attribute xpath Inherits product.attribute_tree_view
product_attribute_value_form_view product.config.product.attribute.value.form.view product.attribute.value form New
product_attribute_value_list product.attribute.value.list.inherit product.attribute.value xpath Inherits product.product_attribute_value_list
product_config_domain_form_view product.configurator.domain.form product.config.domain form New
product_config_domain_form_view_template product.configurator.domain.form.template product.config.domain form New
product_config_session_form_view product.config.session.form product.config.session form New
product_config_session_tree_view product.config.session.tree product.config.session list New
product_configurator_form product.configurator product.configurator form New
product_form_view_custom_vals_inherit product.configurator.form.view.custom.vals product.product xpath Inherits product.product_normal_form_view
product_kanban_view_inherited Product Kanban product.product xpath Inherits product.product_kanban_view
product_template_attribute_line_form_config product.template.attribute.line.form product.template.attribute.line xpath Inherits product.product_template_attribute_line_form
product_template_attribute_value_view_form_weight_extra product.template.attribute.value.view.form.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_form
product_template_attribute_value_view_tree_weight_extra product.template.attribute.value.view.tree.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_tree
product_template_form_view_config_inherited product.template.common.form product.template xpath Inherits product.product_template_form_view
product_template_kanban_view_inherited Product.template.product.kanban product.template xpath Inherits product.product_template_kanban_view
product_template_only_form_view_inherited product.configurator.product.template.form product.template xpath Inherits product.product_template_only_form_view
product_template_search_view product.configurator.product.template.search.view product.template xpath Inherits product.product_template_search_view
product_variant_easy_edit_view_inherit product.product.view.form.easy product.product xpath Inherits product.product_variant_easy_edit_view
template_view_tree_configurable product.template.product.tree product.template xpath Inherits product.product_template_tree_view
Models touched (17)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (11)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute without deleting it'
  • custom_type Selection
    help='The type of the custom field generated in the frontend' selection=CUSTOM_TYPES string='Field Type'
  • description Text
    translate=True
  • image Binary
  • max_val Integer
    help='Maximum value allowed' string='Max Value'
  • min_val Integer
    help='Minimum value allowed' string='Min Value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • required Boolean
    default=True help='Determines the required value of this attribute though it can be change on the template level'
  • search_ok Boolean
    help='When checking for variants with the same configuration, do we include this field in the search?' string='Searchable'
  • uom_id Many2one → uom.uom
    comodel_name='uom.uom' string='Unit of Measure'
  • val_custom Boolean
    help='Allow custom value for this attribute?' string='Custom Value'
Public methods (5)
  • check_searchable_field(self)
    @api.constrains('custom_type', 'search_ok')
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • onchange_custom_type(self)
    @api.onchange('custom_type')
  • onchange_val_custom_field(self)
    @api.onchange('val_custom')
  • validate_custom_val(self, val)
    Pass in a desired custom value and ensure it is valid. Probably should check type, etc., but let's assume fine for the moment.

New fields (3)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute value without deleting it'
  • image Binary
    attachment=True help='Attribute value image (Display on website for radio buttons)'
  • product_id Many2one → product.product
    comodel_name='product.product'
Public methods (3)
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • get_attribute_value_extra_prices(self, product_tmpl_id, pt_attr_value_ids, pricelist=None)
    @api.model
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Use name_search as a domain restriction for the frontend to show only values set on the product template taking all the configuration restrictions into account. TODO: This only works when activating the selection not when typing

New fields (6)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' related='value_id.attribute_id'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • product_value_ids Many2many → product.attribute.value
    column1='product_val_id' column2='attr_val_id' comodel_name='product.attribute.value' compute='_compute_get_value_id' relation='product_attr_values_attr_values_rel' store=True
  • sequence Integer
    default=10
  • value_id Many2one → product.attribute.value
    comodel_name='product.attribute.value' required=True string='Attribute Value'
  • value_ids Many2many → product.attribute.value
    column1='product_attribute_value_line_id' column2='product_attribute_value_id' comodel_name='product.attribute.value' relation='product_attribute_value_product_attribute_value_line_rel' string='Values Configuration'
Public methods (0)

No public methods.

New fields (4)
  • domain_line_ids One2many → product.config.domain.line
    comodel_name='product.config.domain.line' copy=True inverse_name='domain_id' required=True string='Restrictions'
  • implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' relation='product_config_domain_implied_rel' string='Inherited'
  • name Char
    required=True
  • trans_implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' compute=_get_trans_implied string='Transitively inherits'
Public methods (1)
  • compute_domain(self)
    Returns a list of domains defined on a product.config.domain_line_ids and all implied_ids

New fields (7)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' domain=<expr> required=True string='Attribute'
  • condition Selection
    required=True selection=_get_domain_conditions
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Rule'
  • operator Selection
    default='and' required=True selection=_get_domain_operators string='Operators'
  • sequence Integer
    default=1 help='Set the order of operations for evaluation domain lines'
  • template_attribute_value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attribute_value_ids' string='Template Attribute Values'
  • value_ids Many2many → product.attribute.value
    column1='line_id' column2='attribute_id' comodel_name='product.attribute.value' relation='product_config_domain_line_attr_rel' required=True string='Values'
Public methods (0)

No public methods.

New fields (4)
  • name Char
    required=True translate=True
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product'
  • sequence Integer
    default=10
  • value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' string='Configuration'
Public methods (0)

No public methods.

New fields (7)
  • attr_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_attr_line_val_ids' help="For normal attributes the values configured for the product can be selected.\nFor custom attributes the 'Custom' value can also be selected." string='Allowed Attribute Values'
  • attribute_line_id Many2one → product.template.attribute.line
    comodel_name='product.template.attribute.line' ondelete='cascade' required=True string='Attribute Line'
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Restrictions'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
  • template_attribute_ids Many2many → product.attribute
    comodel_name='product.attribute' compute='_compute_template_attribute_ids' string='Template Attributes'
  • value_ids Many2many → product.attribute.value
    column1='cfg_line_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='cfg_line_attr_val_id_rel' string='Values'
Public methods (2)
  • check_value_attributes(self)
    @api.constrains('value_ids')
    Values selected in config lines must be allowed.
  • onchange_attribute(self)
    @api.onchange('attribute_line_id')

New fields (13)
  • config_step Char
    string='Configuration Step ID'
  • config_step_name Char
    compute='_compute_config_step_name' string='Configuration Step'
  • currency_id Many2one → res.currency
    comodel_name='res.currency' compute='_compute_currency_id' string='Currency'
  • custom_value_ids One2many → product.config.session.custom.value
    comodel_name='product.config.session.custom.value' inverse_name='cfg_session_id' string='Custom Values'
  • name Char
    readonly=True string='Configuration Session Number'
  • price Float
    compute='_compute_cfg_price' digits='Product Price' store=True
  • product_id Many2one → product.product
    comodel_name='product.product' name='Configured Variant' ondelete='cascade'
  • product_preset_id Many2one → product.product
    comodel_name='product.product' string='Preset'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' domain=[('config_ok', '=', True)] required=True string='Configurable Template'
  • state Selection
    default='draft' required=True selection=[('draft', 'Draft'), ('done', 'Done')]
  • user_id Many2one → res.users
    comodel_name='res.users' required=True string='User'
  • value_ids Many2many → product.attribute.value
    column1='cfg_session_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='product_config_session_attr_values_rel'
  • weight Float
    compute='_compute_cfg_weight' digits='Stock Weight'
Public methods (35)
  • action_confirm(self, product_id=None)
  • check_and_open_incomplete_step(self, value_ids=None, custom_value_ids=None)
    Check and open incomplete step if any :param value_ids: recordset of product.attribute.value
  • check_attributes_configuration(self, attribute_line_ids, custom_vals, value_ids, final=True)
  • create(self, vals_list)
    @api.model_create_multi
  • create_get_session(self, product_tmpl_id, parent_id=None, force_create=False, user_id=None)
    @api.model
  • create_get_variant(self, value_ids=None, custom_vals=None)
    Creates a new product variant with the attributes passed via value_ids and custom_values or retrieves an existing one based on search result :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: new/existing product.product recordset
  • flatten_attribute_value_ids(self, value_ids)
  • flatten_val_ids(self, value_ids)
    Return a list of value_ids from a list with a mix of ids and list of ids (multiselection) :param value_ids: list of value ids or mix of ids and list of ids (e.g: [1, 2, 3, [4, 5, 6]]) :returns: flattened list of ids ([1, 2, 3, 4, 5, 6])
  • formatPrices(self, prices=None, dp='Product Price')
  • get_active_step(self)
    @api.model
    Attempt to return product.config.step.line object that has the id of the config session step stored as string
  • get_adjacent_steps(self, value_ids=None, active_step_line_id=None)
    @api.model
    Returns the previous and next steps given the configuration passed via value_ids and the active step line passed via cfg_step_line_id.
  • get_all_step_lines(self, product_tmpl_id=None)
    @api.model
    Returns a recordset of configuration step lines of product_tmpl_id :param product_tmpl_id: record-set of product.template :returns: recordset of all configuration steps
  • get_cfg_price(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the price of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration price
  • get_cfg_weight(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the weight of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration weight
  • get_child_specification(self, model, parent)
    @api.model
    return dictiory of onchange specification by appending parent before each key
  • get_components_prices(self, prices, pricelist, value_ids=None)
    Return prices of the components which make up the final configured variant
  • get_config_image(self, value_ids=None, custom_vals=None, size=None)
    Retreive the image object that most closely resembles the configuration code sent via value_ids list For more information check _get_config_image
  • get_custom_value_id(self)
    Return record set of attribute value 'custom'
  • get_extra_attribute_line_ids(self, product_template_id)
    @api.model
    Retrieve attribute lines defined on the product_template_id which are not assigned to configuration steps
  • get_next_step(self, state, product_tmpl_id=False, value_ids=False, custom_value_ids=False)
    Find and return next step if it exists. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • get_onchange_specifications(self, model)
    @api.model
    return onchange specification - same functionality by _onchange_spec - needed this method because odoo don't add specification for fields one2many or many2many there is view-reference(using : tree_view_ref) intead of view in that field
  • get_open_step_lines(self, value_ids=None)
    @api.model
    Returns a recordset of configuration step lines open for access given the configuration passed through value_ids e.g: Field A and B from configuration step 2 depend on Field C from configuration step 1. Since fields A and B require action from the previous step, configuration step 2 is deemed closed and redirect is made for configuration step 1. :param value_ids: list of value.ids representing the current configuration :returns: recordset of accesible configuration steps
  • get_session_search_domain(self, product_tmpl_id, state='draft', parent_id=None)
    Return domain to search session linked to given product template and current login user
  • get_session_vals(self, product_tmpl_id, parent_id=None, user_id=None)
    Return the values for creating session
  • get_vals_to_write(self, values, model)
    @api.model
    Return values in formate excepted by write/create methods - same functionality by _convert_to_write - needed this method because odoo don't call convert to write for the many2many/one2many fields
  • get_variant_search_domain(self, product_tmpl_id, value_ids=None)
    @api.model
    Method called by search_variant used to search duplicates in the database
  • get_variant_vals(self, value_ids=None, custom_vals=None, **kwargs)
    @api.model
    Hook to alter the values of the product variant before creation :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: dictionary of values to pass to product.create() method
  • search_session(self, product_tmpl_id, parent_id=None)
  • search_variant(self, value_ids=None, product_tmpl_id=None)
    @api.model
    Searches product.variants with given value_ids and custom values given in the custom_vals dict :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: product.product recordset of products matching domain
  • update_config(self, attr_val_dict=None, custom_val_dict=None)
    Update the session object with the given value_ids and custom values. Use this method instead of write in order to prevent incompatible configurations as this removed duplicate values for the same attribute. :param attr_val_dict: Dictionary of the form { int (attribute_id): attribute_value_id OR [attribute_value_ids] } :custom_val_dict: Dictionary of the form { int (attribute_id): { 'value': 'custom val', OR 'attachment_ids': { [{ 'name': 'attachment name', 'datas': base64_encoded_string }] } } }
  • update_session_configuration_value(self, vals, product_tmpl_id=None)
    Update value of configuration in current session :param: vals: Dictionary of fields(of configution wizard) and values :param: product_tmpl_id: record set of preoduct template :return: True/False
  • validate_configuration(self, value_ids=None, custom_vals=None, product_tmpl_id=False, final=True)
    @api.model
    Verifies if the configuration values passed via value_ids and custom_vals are valid :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :param final: boolean marker to check required attributes. pass false to check non-final configurations :returns: Error dict with reason of validation failure or True
  • validate_domains_against_sels(self, domains, value_ids=None, custom_vals=None)
  • values_available(self, check_val_ids=None, value_ids=None, custom_vals=None, product_tmpl_id=None, product_template_attribute_line_id=None)
    @api.model
    Determines whether the attr_values from the product_template are available for selection given the configuration ids and the dependencies set on the product template :param check_val_ids: list of attribute value ids to check for availability :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :returns: list of available attribute values
  • write(self, vals)
    Validate configuration when writing new values to session

New fields (5)
  • attachment_ids Many2many → ir.attachment
    column1='cfg_sesion_custom_val_id' column2='attachment_id' comodel_name='ir.attachment' relation='product_config_session_custom_value_attachment_rel' string='Attachments'
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' required=True string='Attribute'
  • cfg_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Session'
  • name Char
    compute='_compute_val_name' readonly=True store=True
  • value Char
    help='Custom value held as string'
Public methods (3)
  • check_custom_type(self)
    @api.constrains('attachment_ids', 'value')
  • eval(self)
    Return custom value evaluated using the related custom field type
  • unique_attribute(self)
    @api.constrains('cfg_session_id', 'attribute_id')

New fields (1)
  • name Char
    required=True translate=True
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids Many2many → product.template.attribute.line
    column1='cfg_line_id' column2='attr_id' comodel_name='product.template.attribute.line' relation='config_step_line_attr_id_rel' string='Attribute Lines'
  • config_step_id Many2one → product.config.step
    comodel_name='product.config.step' required=True string='Configuration Step'
  • name Char
    related='config_step_id.name'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids One2many → product.template.attribute.line
    comodel_name='product.template.attribute.line' compute='_compute_attr_lines' readonly=True store=False string='Attributes'
  • config_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Configuration Session'
  • config_step_ids Many2many → product.config.step
    column1='config_wiz_id' column2='config_step_id' comodel_name='product.config.step' readonly=True relation='product_config_config_steps_rel' store=False string='Configuration Steps'
  • product_id Many2one → product.product
    comodel_name='product.product' help='Set only when re-configuring a existing variant' readonly=True string='Product Variant'
  • product_img Binary
    compute='_compute_cfg_image' readonly=True
Public methods (20)
  • action_config_done(self)
    This method is for the final step which will be taken care by a separate module
  • action_next_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template. More importantly it sets metadata on the context variable so the fields_get and fields_view_get methods can generate the appropriate dynamic content
  • action_previous_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • action_reset(self)
    Delete wizard and configuration session then create a new wizard+session and return an action for the new wizard object
  • add_dynamic_fields(self, res, dynamic_fields, wiz)
    @api.model
    Create the configuration view using the dynamically generated fields in fields_get()
  • apply_onchange_values(self, values, field_names, field_onchange)
    Called from web-controller - original onchange returns M2o values in format (attr-value.id, attr-value.name) but on website we need only attr-value.id
  • create(self, vals_list)
    @api.model_create_multi
    Sets the configuration values of the product_id if given (if any). This is used in reconfiguration of a existing variant
  • fields_get(self, allfields=None, write_access=True, attributes=None)
    @api.model
    Artificially inject fields which are dynamically created using the attribute_ids on the product.template as reference
  • get_field_default_attrs(self)
    @api.model
  • get_form_vals(self, dynamic_fields, domains, cfg_val_ids=None, product_tmpl_id=None, config_session_id=None, values=None)
    Generate a dictionary to return new values via onchange method. Domains hold the values available, this method enforces these values if a selection exists in the view that is not available anymore. :param dynamic_fields: Dictionary with the current {dynamic_field: val} :param domains: Odoo domains restricting attribute values :returns vals: Dictionary passed to {'value': vals} by onchange method
  • get_onchange_domains(self, cfg_val_ids, product_tmpl_id=False, config_session_id=False)
    Generate domains to be returned by onchange method in order to restrict the availble values of dynamically inserted fields :param values: values argument passed to onchance wrapper :cfg_val_ids: current configuration passed as a list of value_ids (usually in the form of db value_ids + interface value_ids) :returns: a dictionary of domains returned by onchance method
  • get_onchange_vals(self, cfg_val_ids, config_session_id=None)
    Onchange hook to add / modify returned values by onchange method
  • get_state_selection(self)
    Get the states of the wizard using standard values and optional configuration steps set on the product.template via config_step_line_ids
  • get_view(self, view_id=None, view_type='form', **options)
    @api.model
    Generate view dynamically using attributes stored on the product.template
  • get_wizard_action(self, view_cache=False, wizard=None)
    Return action of wizard :param view_cache: Boolean (True/False) :param wizard: recordset of product.configurator :returns : dictionary
  • onchange(self, values: dict, field_names: <expr>, fields_spec: dict)
    Override the onchange wrapper to return domains to dynamic fields as onchange isn't triggered for non-db fields
  • onchange_product_tmpl(self)
    @api.onchange('product_tmpl_id')
    set the preset_id if exist in session
  • open_step(self, step)
    Open wizard step 'step' :param step: recordset of product.config.step.line
  • read(self, fields=None, load='_classic_read')
    Remove dynamic fields from the fields list and update the returned values with the dynamic data stored in value_ids
  • write(self, vals)
    Prevent database storage of dynamic fields and instead write values to database persistent value_ids field

New fields (5)
  • config_name Char
    compute='_compute_config_name' string='Configuration Name'
  • config_preset_ok Boolean
    string='Is Preset'
  • weight Float
    compute='_compute_product_weight' inverse='_inverse_product_weight' search='_search_product_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' string='Manual Weight'
  • weight_extra Float
    compute='_compute_product_weight_extra'
Public methods (5)
  • check_config_user_access(self, mode)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • reconfigure_product(self)
    launches a product configurator wizard with a linked template and variant in order to re-configure an existing product. It is essentially a shortcut to pre-fill configuration data of a variant
  • unlink(self)
    - Signal unlink from product variant through context so removal can be stopped for configurable templates - check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (9)
  • attribute_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attr_vals' store=False
  • attribute_value_line_ids One2many → product.attribute.value.line
    comodel_name='product.attribute.value.line' copy=True inverse_name='product_tmpl_id' string='Attribute Value Lines'
  • config_image_ids One2many → product.config.image
    comodel_name='product.config.image' copy=True inverse_name='product_tmpl_id' string='Configuration Images'
  • config_line_ids One2many → product.config.line
    comodel_name='product.config.line' copy=False inverse_name='product_tmpl_id' string='Attribute Dependencies'
  • config_ok Boolean
    string='Can be Configured'
  • config_step_line_ids One2many → product.config.step.line
    comodel_name='product.config.step.line' copy=False inverse_name='product_tmpl_id' string='Configuration Lines'
  • mako_tmpl_name Text
    copy=True help='Generate Name based on Mako Template' string='Variant name'
  • weight Float
    compute='_compute_weight' inverse='_set_weight' search='_search_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' help='Manual setting of product template weight' string='Manual Weight'
Public methods (9)
  • check_attr_value_ids(self)
    @api.constrains('attribute_line_ids', 'attribute_value_line_ids')
    Check attribute lines don't have some attribute value that is not present in attribute lines of that product template
  • check_config_user_access(self)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • configure_product(self)
    launches a product configurator wizard with a linked template in order to configure new product.
  • copy(self, default=None)
    Copy restrictions, config Steps and attribute lines ith product template
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • create_config_wizard(self, model_name='product.configurator', extra_vals=None, click_next=True)
    create product configuration wizard - return action to launch wizard - click on next step based on value of click_next
  • toggle_config(self)
  • unlink(self)
    - Prevent the removal of configurable product templates from variants - Patch for check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (8)
  • custom Boolean
    help='Allow custom values for this attribute?'
  • default_val Many2one → product.attribute.value
    comodel_name='product.attribute.value'
  • invisible_condition Char
    compute='_compute_attribute_condition' store=True
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • readonly_condition Char
    compute='_compute_attribute_condition' store=True
  • required Boolean
    help='Is this attribute required?'
  • required_condition Char
    compute='_compute_attribute_condition' store=True
  • sequence Integer
    default=10
Public methods (6)
  • get_dependencies(self)
  • get_invisible_condition(self, config_steps)
  • get_readonly_condition(self, config_steps, dependencies)
  • get_required_condition(self, config_steps, dependencies)
  • onchange_attribute(self)
    @api.onchange('attribute_id')
    Set default value of required/multi/cutom from attribute
  • onchange_values(self)
    @api.onchange('value_ids')

New fields (1)
  • weight_extra Float
    digits='Stock Weight' string='Attribute Weight Extra'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/product-configurator
GIT
GIThttps://github.com/OCA/product-configurator.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/product-configurator/tree/17.0/product_configurator
VERSION
VERSION 1.0.2
CATEGORY
CATEGORYGeneric Modules/Base
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONYes
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Pledra
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Pledra
COMMITTERS
COMMITTERSRuchir Shukla, Weblate, OCA-git-bot, Simone Orsi, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/product-configurator
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:20:02
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - account
    - base_setup
    - base
    - web
    - onboarding
    - product
    - mail
    - bus
    - web_tour
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - resource
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES mako
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module has all the mechanics to support product configuration. It
serves as a base dependency for configuration interfaces.

Code Analysis

Views touched (23)
XML IDNameModelTypeStatus
config_step_form_view product.configurator.config.step.form product.config.step form New
config_step_tree_view product.configurator.config.step.tree product.config.step form New
configurator_settings_view_form res.config.settings.view.form.inherit.sale res.config.settings xpath Inherits base.res_config_settings_view_form
product_attribute_form_view product.attribute.form.view product.attribute xpath Inherits product.product_attribute_view_form
product_attribute_tree_view product.config.product.attribute.tree product.attribute xpath Inherits product.attribute_tree_view
product_attribute_value_form_view product.config.product.attribute.value.form.view product.attribute.value form New
product_attribute_value_list product.attribute.value.list.inherit product.attribute.value xpath Inherits product.product_attribute_value_list
product_config_domain_form_view product.configurator.domain.form product.config.domain form New
product_config_domain_form_view_template product.configurator.domain.form.template product.config.domain form New
product_config_session_form_view product.config.session.form product.config.session form New
product_config_session_tree_view product.config.session.tree product.config.session tree New
product_configurator_form product.configurator product.configurator form New
product_form_view_custom_vals_inherit product.configurator.form.view.custom.vals product.product xpath Inherits product.product_normal_form_view
product_kanban_view_inherited Product Kanban product.product xpath Inherits product.product_kanban_view
product_template_attribute_line_form_config product.template.attribute.line.form product.template.attribute.line xpath Inherits product.product_template_attribute_line_form
product_template_attribute_value_view_form_weight_extra product.template.attribute.value.view.form.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_form
product_template_attribute_value_view_tree_weight_extra product.template.attribute.value.view.tree.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_tree
product_template_form_view_config_inherited product.template.common.form product.template xpath Inherits product.product_template_form_view
product_template_kanban_view_inherited Product.template.product.kanban product.template xpath Inherits product.product_template_kanban_view
product_template_only_form_view_inherited product.configurator.product.template.form product.template xpath Inherits product.product_template_only_form_view
product_template_search_view product.configurator.product.template.search.view product.template xpath Inherits product.product_template_search_view
product_variant_easy_edit_view_inherit product.product.view.form.easy product.product xpath Inherits product.product_variant_easy_edit_view
template_view_tree_configurable product.template.product.tree product.template xpath Inherits product.product_template_tree_view
Models touched (17)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (11)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute without deleting it'
  • custom_type Selection
    help='The type of the custom field generated in the frontend' selection=CUSTOM_TYPES string='Field Type'
  • description Text
    translate=True
  • image Binary
  • max_val Integer
    help='Maximum value allowed' string='Max Value'
  • min_val Integer
    help='Minimum value allowed' string='Min Value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • required Boolean
    default=True help='Determines the required value of this attribute though it can be change on the template level'
  • search_ok Boolean
    help='When checking for variants with the same configuration, do we include this field in the search?' string='Searchable'
  • uom_id Many2one → uom.uom
    comodel_name='uom.uom' string='Unit of Measure'
  • val_custom Boolean
    help='Allow custom value for this attribute?' string='Custom Value'
Public methods (5)
  • check_searchable_field(self)
    @api.constrains('custom_type', 'search_ok')
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • onchange_custom_type(self)
    @api.onchange('custom_type')
  • onchange_val_custom_field(self)
    @api.onchange('val_custom')
  • validate_custom_val(self, val)
    Pass in a desired custom value and ensure it is valid. Probably should check type, etc., but let's assume fine for the moment.

New fields (3)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute value without deleting it'
  • image Binary
    attachment=True help='Attribute value image (Display on website for radio buttons)'
  • product_id Many2one → product.product
    comodel_name='product.product'
Public methods (3)
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • get_attribute_value_extra_prices(self, product_tmpl_id, pt_attr_value_ids, pricelist=None)
    @api.model
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Use name_search as a domain restriction for the frontend to show only values set on the product template taking all the configuration restrictions into account. TODO: This only works when activating the selection not when typing

New fields (6)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' related='value_id.attribute_id'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • product_value_ids Many2many → product.attribute.value
    column1='product_val_id' column2='attr_val_id' comodel_name='product.attribute.value' compute='_compute_get_value_id' relation='product_attr_values_attr_values_rel' store=True
  • sequence Integer
    default=10
  • value_id Many2one → product.attribute.value
    comodel_name='product.attribute.value' required=True string='Attribute Value'
  • value_ids Many2many → product.attribute.value
    column1='product_attribute_value_line_id' column2='product_attribute_value_id' comodel_name='product.attribute.value' relation='product_attribute_value_product_attribute_value_line_rel' string='Values Configuration'
Public methods (0)

No public methods.

New fields (4)
  • domain_line_ids One2many → product.config.domain.line
    comodel_name='product.config.domain.line' copy=True inverse_name='domain_id' required=True string='Restrictions'
  • implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' relation='product_config_domain_implied_rel' string='Inherited'
  • name Char
    required=True
  • trans_implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' compute=_get_trans_implied string='Transitively inherits'
Public methods (1)
  • compute_domain(self)
    Returns a list of domains defined on a product.config.domain_line_ids and all implied_ids

New fields (7)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' domain=<expr> required=True string='Attribute'
  • condition Selection
    required=True selection=_get_domain_conditions
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Rule'
  • operator Selection
    default='and' required=True selection=_get_domain_operators string='Operators'
  • sequence Integer
    default=1 help='Set the order of operations for evaluation domain lines'
  • template_attribute_value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attribute_value_ids' string='Template Attribute Values'
  • value_ids Many2many → product.attribute.value
    column1='line_id' column2='attribute_id' comodel_name='product.attribute.value' relation='product_config_domain_line_attr_rel' required=True string='Values'
Public methods (0)

No public methods.

New fields (4)
  • name Char
    required=True translate=True
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product'
  • sequence Integer
    default=10
  • value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' string='Configuration'
Public methods (0)

No public methods.

New fields (7)
  • attr_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_attr_line_val_ids' help="For normal attributes the values configured for the product can be selected.\nFor custom attributes the 'Custom' value can also be selected." string='Allowed Attribute Values'
  • attribute_line_id Many2one → product.template.attribute.line
    comodel_name='product.template.attribute.line' ondelete='cascade' required=True string='Attribute Line'
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Restrictions'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
  • template_attribute_ids Many2many → product.attribute
    comodel_name='product.attribute' compute='_compute_template_attribute_ids' string='Template Attributes'
  • value_ids Many2many → product.attribute.value
    column1='cfg_line_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='cfg_line_attr_val_id_rel' string='Values'
Public methods (2)
  • check_value_attributes(self)
    @api.constrains('value_ids')
    Values selected in config lines must be allowed.
  • onchange_attribute(self)
    @api.onchange('attribute_line_id')

New fields (13)
  • config_step Char
    string='Configuration Step ID'
  • config_step_name Char
    compute='_compute_config_step_name' string='Configuration Step'
  • currency_id Many2one → res.currency
    comodel_name='res.currency' compute='_compute_currency_id' string='Currency'
  • custom_value_ids One2many → product.config.session.custom.value
    comodel_name='product.config.session.custom.value' inverse_name='cfg_session_id' string='Custom Values'
  • name Char
    readonly=True string='Configuration Session Number'
  • price Float
    compute='_compute_cfg_price' digits='Product Price' store=True
  • product_id Many2one → product.product
    comodel_name='product.product' name='Configured Variant' ondelete='cascade'
  • product_preset_id Many2one → product.product
    comodel_name='product.product' domain="[('product_tmpl_id', '=', product_tmpl_id), ('config_preset_ok', '=', True)]" string='Preset'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' domain=[('config_ok', '=', True)] required=True string='Configurable Template'
  • state Selection
    default='draft' required=True selection=[('draft', 'Draft'), ('done', 'Done')]
  • user_id Many2one → res.users
    comodel_name='res.users' required=True string='User'
  • value_ids Many2many → product.attribute.value
    column1='cfg_session_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='product_config_session_attr_values_rel'
  • weight Float
    compute='_compute_cfg_weight' digits='Stock Weight'
Public methods (35)
  • action_confirm(self, product_id=None)
  • check_and_open_incomplete_step(self, value_ids=None, custom_value_ids=None)
    Check and open incomplete step if any :param value_ids: recordset of product.attribute.value
  • check_attributes_configuration(self, attribute_line_ids, custom_vals, value_ids, final=True)
  • create(self, vals_list)
    @api.model_create_multi
  • create_get_session(self, product_tmpl_id, parent_id=None, force_create=False, user_id=None)
    @api.model
  • create_get_variant(self, value_ids=None, custom_vals=None)
    Creates a new product variant with the attributes passed via value_ids and custom_values or retrieves an existing one based on search result :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: new/existing product.product recordset
  • encode_custom_values(self, custom_vals)
    Hook to alter the values of the custom values before creating or writing :param custom_vals: dict {product.attribute.id: custom_value} :returns: list of custom values compatible with write and create
  • flatten_val_ids(self, value_ids)
    Return a list of value_ids from a list with a mix of ids and list of ids (multiselection) :param value_ids: list of value ids or mix of ids and list of ids (e.g: [1, 2, 3, [4, 5, 6]]) :returns: flattened list of ids ([1, 2, 3, 4, 5, 6])
  • formatPrices(self, prices=None, dp='Product Price')
  • get_active_step(self)
    @api.model
    Attempt to return product.config.step.line object that has the id of the config session step stored as string
  • get_adjacent_steps(self, value_ids=None, active_step_line_id=None)
    @api.model
    Returns the previous and next steps given the configuration passed via value_ids and the active step line passed via cfg_step_line_id.
  • get_all_step_lines(self, product_tmpl_id=None)
    @api.model
    Returns a recordset of configuration step lines of product_tmpl_id :param product_tmpl_id: record-set of product.template :returns: recordset of all configuration steps
  • get_cfg_price(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the price of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration price
  • get_cfg_weight(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the weight of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration weight
  • get_child_specification(self, model, parent)
    @api.model
    return dictiory of onchange specification by appending parent before each key
  • get_components_prices(self, prices, pricelist, value_ids=None)
    Return prices of the components which make up the final configured variant
  • get_config_image(self, value_ids=None, custom_vals=None, size=None)
    Retreive the image object that most closely resembles the configuration code sent via value_ids list For more information check _get_config_image
  • get_custom_value_id(self)
    Return record set of attribute value 'custom'
  • get_extra_attribute_line_ids(self, product_template_id)
    @api.model
    Retrieve attribute lines defined on the product_template_id which are not assigned to configuration steps
  • get_next_step(self, state, product_tmpl_id=False, value_ids=False, custom_value_ids=False)
    Find and return next step if it exists. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • get_onchange_specifications(self, model)
    @api.model
    return onchange specification - same functionality by _onchange_spec - needed this method because odoo don't add specification for fields one2many or many2many there is view-reference(using : tree_view_ref) intead of view in that field
  • get_open_step_lines(self, value_ids=None)
    @api.model
    Returns a recordset of configuration step lines open for access given the configuration passed through value_ids e.g: Field A and B from configuration step 2 depend on Field C from configuration step 1. Since fields A and B require action from the previous step, configuration step 2 is deemed closed and redirect is made for configuration step 1. :param value_ids: list of value.ids representing the current configuration :returns: recordset of accesible configuration steps
  • get_session_search_domain(self, product_tmpl_id, state='draft', parent_id=None)
    Return domain to search session linked to given product template and current login user
  • get_session_vals(self, product_tmpl_id, parent_id=None, user_id=None)
    Return the values for creating session
  • get_vals_to_write(self, values, model)
    @api.model
    Return values in formate excepted by write/create methods - same functionality by _convert_to_write - needed this method because odoo don't call convert to write for the many2many/one2many fields
  • get_variant_search_domain(self, product_tmpl_id, value_ids=None)
    @api.model
    Method called by search_variant used to search duplicates in the database
  • get_variant_vals(self, value_ids=None, custom_vals=None, **kwargs)
    @api.model
    Hook to alter the values of the product variant before creation :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: dictionary of values to pass to product.create() method
  • search_session(self, product_tmpl_id, parent_id=None)
  • search_variant(self, value_ids=None, product_tmpl_id=None)
    @api.model
    Searches product.variants with given value_ids and custom values given in the custom_vals dict :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: product.product recordset of products matching domain
  • update_config(self, attr_val_dict=None, custom_val_dict=None)
    Update the session object with the given value_ids and custom values. Use this method instead of write in order to prevent incompatible configurations as this removed duplicate values for the same attribute. :param attr_val_dict: Dictionary of the form { int (attribute_id): attribute_value_id OR [attribute_value_ids] } :custom_val_dict: Dictionary of the form { int (attribute_id): { 'value': 'custom val', OR 'attachment_ids': { [{ 'name': 'attachment name', 'datas': base64_encoded_string }] } } }
  • update_session_configuration_value(self, vals, product_tmpl_id=None)
    Update value of configuration in current session :param: vals: Dictionary of fields(of configution wizard) and values :param: product_tmpl_id: record set of preoduct template :return: True/False
  • validate_configuration(self, value_ids=None, custom_vals=None, product_tmpl_id=False, final=True)
    @api.model
    Verifies if the configuration values passed via value_ids and custom_vals are valid :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :param final: boolean marker to check required attributes. pass false to check non-final configurations :returns: Error dict with reason of validation failure or True
  • validate_domains_against_sels(self, domains, value_ids=None, custom_vals=None)
  • values_available(self, check_val_ids=None, value_ids=None, custom_vals=None, product_tmpl_id=None, product_template_attribute_line_id=None)
    @api.model
    Determines whether the attr_values from the product_template are available for selection given the configuration ids and the dependencies set on the product template :param check_val_ids: list of attribute value ids to check for availability :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :returns: list of available attribute values
  • write(self, vals)
    Validate configuration when writing new values to session

New fields (5)
  • attachment_ids Many2many → ir.attachment
    column1='cfg_sesion_custom_val_id' column2='attachment_id' comodel_name='ir.attachment' relation='product_config_session_custom_value_attachment_rel' string='Attachments'
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' required=True string='Attribute'
  • cfg_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Session'
  • name Char
    compute='_compute_val_name' readonly=True store=True
  • value Char
    help='Custom value held as string'
Public methods (3)
  • check_custom_type(self)
    @api.constrains('attachment_ids', 'value')
  • eval(self)
    Return custom value evaluated using the related custom field type
  • unique_attribute(self)
    @api.constrains('cfg_session_id', 'attribute_id')

New fields (1)
  • name Char
    required=True translate=True
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids Many2many → product.template.attribute.line
    column1='cfg_line_id' column2='attr_id' comodel_name='product.template.attribute.line' relation='config_step_line_attr_id_rel' string='Attribute Lines'
  • config_step_id Many2one → product.config.step
    comodel_name='product.config.step' required=True string='Configuration Step'
  • name Char
    related='config_step_id.name'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids One2many → product.template.attribute.line
    comodel_name='product.template.attribute.line' compute='_compute_attr_lines' readonly=True store=False string='Attributes'
  • config_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Configuration Session'
  • config_step_ids Many2many → product.config.step
    column1='config_wiz_id' column2='config_step_id' comodel_name='product.config.step' readonly=True relation='product_config_config_steps_rel' store=False string='Configuration Steps'
  • product_id Many2one → product.product
    comodel_name='product.product' help='Set only when re-configuring a existing variant' readonly=True string='Product Variant'
  • product_img Binary
    compute='_compute_cfg_image' readonly=True
Public methods (21)
  • action_config_done(self)
    This method is for the final step which will be taken care by a separate module
  • action_next_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template. More importantly it sets metadata on the context variable so the fields_get and fields_view_get methods can generate the appropriate dynamic content
  • action_previous_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • action_reset(self)
    Delete wizard and configuration session then create a new wizard+session and return an action for the new wizard object
  • add_dynamic_fields(self, res, dynamic_fields, wiz)
    @api.model
    Create the configuration view using the dynamically generated fields in fields_get()
  • apply_onchange_values(self, values, field_names, field_onchange)
    Called from web-controller - original onchage return M2o values in formate (attr-value.id, attr-value.name) but on website we need only attr-value.id
  • create(self, vals_list)
    @api.model_create_multi
    Sets the configuration values of the product_id if given (if any). This is used in reconfiguration of a existing variant
  • fields_get(self, allfields=None, write_access=True, attributes=None)
    @api.model
    Artificially inject fields which are dynamically created using the attribute_ids on the product.template as reference
  • get_field_default_attrs(self)
    @api.model
  • get_form_vals(self, dynamic_fields, domains, cfg_val_ids=None, product_tmpl_id=None, config_session_id=None, values=None)
    Generate a dictionary to return new values via onchange method. Domains hold the values available, this method enforces these values if a selection exists in the view that is not available anymore. :param dynamic_fields: Dictionary with the current {dynamic_field: val} :param domains: Odoo domains restricting attribute values :returns vals: Dictionary passed to {'value': vals} by onchange method
  • get_onchange_domains(self, cfg_val_ids, product_tmpl_id=False, config_session_id=False)
    Generate domains to be returned by onchange method in order to restrict the availble values of dynamically inserted fields :param values: values argument passed to onchance wrapper :cfg_val_ids: current configuration passed as a list of value_ids (usually in the form of db value_ids + interface value_ids) :returns: a dictionary of domains returned by onchance method
  • get_onchange_vals(self, cfg_val_ids, config_session_id=None)
    Onchange hook to add / modify returned values by onchange method
  • get_state_selection(self)
    Get the states of the wizard using standard values and optional configuration steps set on the product.template via config_step_line_ids
  • get_view(self, view_id=None, view_type='form', **options)
    @api.model
    Generate view dynamically using attributes stored on the product.template
  • get_wizard_action(self, view_cache=False, wizard=None)
    Return action of wizard :param view_cache: Boolean (True/False) :param wizard: recordset of product.configurator :returns : dictionary
  • onchange(self, values: dict, field_names: <expr>, fields_spec: dict)
    Override the onchange wrapper to return domains to dynamic fields as onchange isn't triggered for non-db fields
  • onchange_product_tmpl(self)
    @api.onchange('product_tmpl_id')
    set the preset_id if exist in session
  • open_step(self, step)
    Open wizard step 'step' :param step: recordset of product.config.step.line
  • prepare_attrs_initial(self, attr_lines, field_prefix, custom_field_prefix, dynamic_fields, wiz)
  • read(self, fields=None, load='_classic_read')
    Remove dynamic fields from the fields list and update the returned values with the dynamic data stored in value_ids
  • write(self, vals)
    Prevent database storage of dynamic fields and instead write values to database persistent value_ids field

New fields (5)
  • config_name Char
    compute='_compute_config_name' string='Configuration Name'
  • config_preset_ok Boolean
    string='Is Preset'
  • weight Float
    compute='_compute_product_weight' inverse='_inverse_product_weight' search='_search_product_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' string='Manual Weight'
  • weight_extra Float
    compute='_compute_product_weight_extra'
Public methods (5)
  • check_config_user_access(self, mode)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • reconfigure_product(self)
    launches a product configurator wizard with a linked template and variant in order to re-configure an existing product. It is essentially a shortcut to pre-fill configuration data of a variant
  • unlink(self)
    - Signal unlink from product variant through context so removal can be stopped for configurable templates - check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (9)
  • attribute_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attr_vals' store=False
  • attribute_value_line_ids One2many → product.attribute.value.line
    comodel_name='product.attribute.value.line' copy=True inverse_name='product_tmpl_id' string='Attribute Value Lines'
  • config_image_ids One2many → product.config.image
    comodel_name='product.config.image' copy=True inverse_name='product_tmpl_id' string='Configuration Images'
  • config_line_ids One2many → product.config.line
    comodel_name='product.config.line' copy=False inverse_name='product_tmpl_id' string='Attribute Dependencies'
  • config_ok Boolean
    string='Can be Configured'
  • config_step_line_ids One2many → product.config.step.line
    comodel_name='product.config.step.line' copy=False inverse_name='product_tmpl_id' string='Configuration Lines'
  • mako_tmpl_name Text
    copy=True help='Generate Name based on Mako Template' string='Variant name'
  • weight Float
    compute='_compute_weight' inverse='_set_weight' search='_search_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' help='Manual setting of product template weight' string='Manual Weight'
Public methods (9)
  • check_attr_value_ids(self)
    @api.constrains('attribute_line_ids', 'attribute_value_line_ids')
    Check attribute lines don't have some attribute value that is not present in attribute lines of that product template
  • check_config_user_access(self)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • configure_product(self)
    launches a product configurator wizard with a linked template in order to configure new product.
  • copy(self, default=None)
    Copy restrictions, config Steps and attribute lines ith product template
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • create_config_wizard(self, model_name='product.configurator', extra_vals=None, click_next=True)
    create product configuration wizard - return action to launch wizard - click on next step based on value of click_next
  • toggle_config(self)
  • unlink(self)
    - Prevent the removal of configurable product templates from variants - Patch for check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (5)
  • custom Boolean
    help='Allow custom values for this attribute?'
  • default_val Many2one → product.attribute.value
    comodel_name='product.attribute.value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • required Boolean
    help='Is this attribute required?'
  • sequence Integer
    default=10
Public methods (2)
  • onchange_attribute(self)
    @api.onchange('attribute_id')
    Set default value of required/multi/cutom from attribute
  • onchange_values(self)
    @api.onchange('value_ids')

New fields (1)
  • weight_extra Float
    digits='Stock Weight' string='Attribute Weight Extra'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/product-configurator
GIT
GIThttps://github.com/OCA/product-configurator.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/product-configurator/tree/16.0/product_configurator
VERSION
VERSION 1.1.2
CATEGORY
CATEGORYGeneric Modules/Base
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONYes
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Pledra
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Pledra
COMMITTERS
COMMITTERSDaniel Reis, Weblate, OCA-git-bot, Simone Rubino, Simone Orsi, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/product-configurator
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:53:41
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - account
    - base_setup
    - base
    - web
    - product
    - mail
    - bus
    - web_tour
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - resource
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES mako
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (23)
XML IDNameModelTypeStatus
config_step_form_view product.configurator.config.step.form product.config.step form New
config_step_tree_view product.configurator.config.step.tree product.config.step form New
configurator_settings_view_form res.config.settings.view.form.inherit.sale res.config.settings xpath Inherits base.res_config_settings_view_form
product_attribute_form_view product.attribute.form.view product.attribute xpath Inherits product.product_attribute_view_form
product_attribute_tree_view product.config.product.attribute.tree product.attribute xpath Inherits product.attribute_tree_view
product_attribute_value_form_view product.config.product.attribute.value.form.view product.attribute.value form New
product_config_domain_form_view product.configurator.domain.form product.config.domain form New
product_config_domain_form_view_template product.configurator.domain.form.template product.config.domain form New
product_config_session_form_view product.config.session.form product.config.session form New
product_config_session_tree_view product.config.session.tree product.config.session tree New
product_configurator_form product.configurator product.configurator form New
product_form_view_custom_vals_inherit product.configurator.form.view.custom.vals product.product xpath Inherits product.product_normal_form_view
product_kanban_view_inherited Product Kanban product.product xpath Inherits product.product_kanban_view
product_template_attribute_line_form_config product.template.attribute.line.form product.template.attribute.line xpath Inherits product.product_template_attribute_line_form
product_template_attribute_value_view_form_weight_extra product.template.attribute.value.view.form.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_form
product_template_attribute_value_view_tree_weight_extra product.template.attribute.value.view.tree.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_tree
product_template_form_view_config_inherited product.template.common.form product.template xpath Inherits product.product_template_form_view
product_template_kanban_view_inherited Product.template.product.kanban product.template xpath Inherits product.product_template_kanban_view
product_template_only_form_view_inherited product.configurator.product.template.form product.template xpath Inherits product.product_template_only_form_view
product_template_search_view product.configurator.product.template.search.view product.template xpath Inherits product.product_template_search_view
product_variant_easy_edit_view_inherit product.product.view.form.easy product.product xpath Inherits product.product_variant_easy_edit_view
template_view_tree_configurable product.template.product.tree product.template xpath Inherits product.product_template_tree_view
variants_tree_view product.attribute.value.tree product.attribute.value tree New
Models touched (17)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (11)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute without deleting it'
  • custom_type Selection
    help='The type of the custom field generated in the frontend' selection=CUSTOM_TYPES string='Field Type'
  • description Text
    translate=True
  • image Binary
  • max_val Integer
    help='Maximum value allowed' string='Max Value'
  • min_val Integer
    help='Minimum value allowed' string='Min Value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • required Boolean
    default=True help='Determines the required value of this attribute though it can be change on the template level'
  • search_ok Boolean
    help='When checking for variants with the same configuration, do we include this field in the search?' string='Searchable'
  • uom_id Many2one → uom.uom
    comodel_name='uom.uom' string='Unit of Measure'
  • val_custom Boolean
    help='Allow custom value for this attribute?' string='Custom Value'
Public methods (5)
  • check_searchable_field(self)
    @api.constrains('custom_type', 'search_ok')
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • onchange_custom_type(self)
    @api.onchange('custom_type')
  • onchange_val_custom_field(self)
    @api.onchange('val_custom')
  • validate_custom_val(self, val)
    Pass in a desired custom value and ensure it is valid. Probably should check type, etc., but let's assume fine for the moment.

New fields (3)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute value without deleting it'
  • image Binary
    attachment=True help='Attribute value image (Display on website for radio buttons)'
  • product_id Many2one → product.product
    comodel_name='product.product'
Public methods (4)
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • get_attribute_value_extra_prices(self, product_tmpl_id, pt_attr_value_ids, pricelist=None)
    @api.model
  • name_get(self)
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Use name_search as a domain restriction for the frontend to show only values set on the product template taking all the configuration restrictions into account. TODO: This only works when activating the selection not when typing

New fields (6)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' related='value_id.attribute_id'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • product_value_ids Many2many → product.attribute.value
    column1='product_val_id' column2='attr_val_id' comodel_name='product.attribute.value' compute='_compute_get_value_id' relation='product_attr_values_attr_values_rel' store=True
  • sequence Integer
    default=10
  • value_id Many2one → product.attribute.value
    comodel_name='product.attribute.value' required=True string='Attribute Value'
  • value_ids Many2many → product.attribute.value
    column1='product_attribute_value_line_id' column2='product_attribute_value_id' comodel_name='product.attribute.value' relation='product_attribute_value_product_attribute_value_line_rel' string='Values Configuration'
Public methods (0)

No public methods.

New fields (4)
  • domain_line_ids One2many → product.config.domain.line
    comodel_name='product.config.domain.line' copy=True inverse_name='domain_id' required=True string='Restrictions'
  • implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' relation='product_config_domain_implied_rel' string='Inherited'
  • name Char
    required=True
  • trans_implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' compute=_get_trans_implied string='Transitively inherits'
Public methods (1)
  • compute_domain(self)
    Returns a list of domains defined on a product.config.domain_line_ids and all implied_ids

New fields (7)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' domain=<expr> required=True string='Attribute'
  • condition Selection
    required=True selection=_get_domain_conditions
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Rule'
  • operator Selection
    default='and' required=True selection=_get_domain_operators string='Operators'
  • sequence Integer
    default=1 help='Set the order of operations for evaluation domain lines'
  • template_attribute_value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attribute_value_ids' string='Template Attribute Values'
  • value_ids Many2many → product.attribute.value
    column1='line_id' column2='attribute_id' comodel_name='product.attribute.value' relation='product_config_domain_line_attr_rel' required=True string='Values'
Public methods (0)

No public methods.

New fields (4)
  • name Char
    required=True translate=True
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product'
  • sequence Integer
    default=10
  • value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' string='Configuration'
Public methods (0)

No public methods.

New fields (7)
  • attr_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_attr_line_val_ids' help="For normal attributes the values configured for the product can be selected.\nFor custom attributes the 'Custom' value can also be selected." string='Allowed Attribute Values'
  • attribute_line_id Many2one → product.template.attribute.line
    comodel_name='product.template.attribute.line' ondelete='cascade' required=True string='Attribute Line'
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Restrictions'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
  • template_attribute_ids Many2many → product.attribute
    comodel_name='product.attribute' compute='_compute_template_attribute_ids' string='Template Attributes'
  • value_ids Many2many → product.attribute.value
    column1='cfg_line_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='cfg_line_attr_val_id_rel' string='Values'
Public methods (2)
  • check_value_attributes(self)
    @api.constrains('value_ids')
    Values selected in config lines must be allowed.
  • onchange_attribute(self)
    @api.onchange('attribute_line_id')

New fields (13)
  • config_step Char
    string='Configuration Step ID'
  • config_step_name Char
    compute='_compute_config_step_name' string='Configuration Step'
  • currency_id Many2one → res.currency
    comodel_name='res.currency' compute='_compute_currency_id' string='Currency'
  • custom_value_ids One2many → product.config.session.custom.value
    comodel_name='product.config.session.custom.value' inverse_name='cfg_session_id' string='Custom Values'
  • name Char
    readonly=True string='Configuration Session Number'
  • price Float
    compute='_compute_cfg_price' digits='Product Price' store=True
  • product_id Many2one → product.product
    comodel_name='product.product' name='Configured Variant' ondelete='cascade'
  • product_preset_id Many2one → product.product
    comodel_name='product.product' domain="[('product_tmpl_id', '=', product_tmpl_id), ('config_preset_ok', '=', True)]" string='Preset'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' domain=[('config_ok', '=', True)] required=True string='Configurable Template'
  • state Selection
    default='draft' required=True selection=[('draft', 'Draft'), ('done', 'Done')]
  • user_id Many2one → res.users
    comodel_name='res.users' required=True string='User'
  • value_ids Many2many → product.attribute.value
    column1='cfg_session_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='product_config_session_attr_values_rel'
  • weight Float
    compute='_compute_cfg_weight' digits='Stock Weight'
Public methods (35)
  • action_confirm(self, product_id=None)
  • check_and_open_incomplete_step(self, value_ids=None, custom_value_ids=None)
    Check and open incomplete step if any :param value_ids: recordset of product.attribute.value
  • check_attributes_configuration(self, attribute_line_ids, custom_vals, value_ids, final=True)
  • create(self, vals_list)
    @api.model_create_multi
  • create_get_session(self, product_tmpl_id, parent_id=None, force_create=False, user_id=None)
    @api.model
  • create_get_variant(self, value_ids=None, custom_vals=None)
    Creates a new product variant with the attributes passed via value_ids and custom_values or retrieves an existing one based on search result :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: new/existing product.product recordset
  • encode_custom_values(self, custom_vals)
    Hook to alter the values of the custom values before creating or writing :param custom_vals: dict {product.attribute.id: custom_value} :returns: list of custom values compatible with write and create
  • flatten_val_ids(self, value_ids)
    Return a list of value_ids from a list with a mix of ids and list of ids (multiselection) :param value_ids: list of value ids or mix of ids and list of ids (e.g: [1, 2, 3, [4, 5, 6]]) :returns: flattened list of ids ([1, 2, 3, 4, 5, 6])
  • formatPrices(self, prices=None, dp='Product Price')
  • get_active_step(self)
    @api.model
    Attempt to return product.config.step.line object that has the id of the config session step stored as string
  • get_adjacent_steps(self, value_ids=None, active_step_line_id=None)
    @api.model
    Returns the previous and next steps given the configuration passed via value_ids and the active step line passed via cfg_step_line_id.
  • get_all_step_lines(self, product_tmpl_id=None)
    @api.model
    Returns a recordset of configuration step lines of product_tmpl_id :param product_tmpl_id: record-set of product.template :returns: recordset of all configuration steps
  • get_cfg_price(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the price of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration price
  • get_cfg_weight(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the weight of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration weight
  • get_child_specification(self, model, parent)
    @api.model
    return dictiory of onchange specification by appending parent before each key
  • get_components_prices(self, prices, pricelist, value_ids=None)
    Return prices of the components which make up the final configured variant
  • get_config_image(self, value_ids=None, custom_vals=None, size=None)
    Retreive the image object that most closely resembles the configuration code sent via value_ids list For more information check _get_config_image
  • get_custom_value_id(self)
    Return record set of attribute value 'custom'
  • get_extra_attribute_line_ids(self, product_template_id)
    @api.model
    Retrieve attribute lines defined on the product_template_id which are not assigned to configuration steps
  • get_next_step(self, state, product_tmpl_id=False, value_ids=False, custom_value_ids=False)
    Find and return next step if it exists. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • get_onchange_specifications(self, model)
    @api.model
    return onchange specification - same functionality by _onchange_spec - needed this method because odoo don't add specification for fields one2many or many2many there is view-reference(using : tree_view_ref) intead of view in that field
  • get_open_step_lines(self, value_ids=None)
    @api.model
    Returns a recordset of configuration step lines open for access given the configuration passed through value_ids e.g: Field A and B from configuration step 2 depend on Field C from configuration step 1. Since fields A and B require action from the previous step, configuration step 2 is deemed closed and redirect is made for configuration step 1. :param value_ids: list of value.ids representing the current configuration :returns: recordset of accesible configuration steps
  • get_session_search_domain(self, product_tmpl_id, state='draft', parent_id=None)
    Return domain to search session linked to given product template and current login user
  • get_session_vals(self, product_tmpl_id, parent_id=None, user_id=None)
    Return the values for creating session
  • get_vals_to_write(self, values, model)
    @api.model
    Return values in formate excepted by write/create methods - same functionality by _convert_to_write - needed this method because odoo don't call convert to write for the many2many/one2many fields
  • get_variant_search_domain(self, product_tmpl_id, value_ids=None)
    @api.model
    Method called by search_variant used to search duplicates in the database
  • get_variant_vals(self, value_ids=None, custom_vals=None, **kwargs)
    @api.model
    Hook to alter the values of the product variant before creation :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: dictionary of values to pass to product.create() method
  • search_session(self, product_tmpl_id, parent_id=None)
  • search_variant(self, value_ids=None, product_tmpl_id=None)
    @api.model
    Searches product.variants with given value_ids and custom values given in the custom_vals dict :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: product.product recordset of products matching domain
  • update_config(self, attr_val_dict=None, custom_val_dict=None)
    Update the session object with the given value_ids and custom values. Use this method instead of write in order to prevent incompatible configurations as this removed duplicate values for the same attribute. :param attr_val_dict: Dictionary of the form { int (attribute_id): attribute_value_id OR [attribute_value_ids] } :custom_val_dict: Dictionary of the form { int (attribute_id): { 'value': 'custom val', OR 'attachment_ids': { [{ 'name': 'attachment name', 'datas': base64_encoded_string }] } } }
  • update_session_configuration_value(self, vals, product_tmpl_id=None)
    Update value of configuration in current session :param: vals: Dictionary of fields(of configution wizard) and values :param: product_tmpl_id: record set of preoduct template :return: True/False
  • validate_configuration(self, value_ids=None, custom_vals=None, product_tmpl_id=False, final=True)
    @api.model
    Verifies if the configuration values passed via value_ids and custom_vals are valid :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :param final: boolean marker to check required attributes. pass false to check non-final configurations :returns: Error dict with reason of validation failure or True
  • validate_domains_against_sels(self, domains, value_ids=None, custom_vals=None)
  • values_available(self, check_val_ids=None, value_ids=None, custom_vals=None, product_tmpl_id=None, product_template_attribute_line_id=None)
    @api.model
    Determines whether the attr_values from the product_template are available for selection given the configuration ids and the dependencies set on the product template :param check_val_ids: list of attribute value ids to check for availability :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :returns: list of available attribute values
  • write(self, vals)
    Validate configuration when writing new values to session

New fields (5)
  • attachment_ids Many2many → ir.attachment
    column1='cfg_sesion_custom_val_id' column2='attachment_id' comodel_name='ir.attachment' relation='product_config_session_custom_value_attachment_rel' string='Attachments'
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' required=True string='Attribute'
  • cfg_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Session'
  • name Char
    compute='_compute_val_name' readonly=True store=True
  • value Char
    help='Custom value held as string'
Public methods (3)
  • check_custom_type(self)
    @api.constrains('attachment_ids', 'value')
  • eval(self)
    Return custom value evaluated using the related custom field type
  • unique_attribute(self)
    @api.constrains('cfg_session_id', 'attribute_id')

New fields (1)
  • name Char
    required=True translate=True
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids Many2many → product.template.attribute.line
    column1='cfg_line_id' column2='attr_id' comodel_name='product.template.attribute.line' relation='config_step_line_attr_id_rel' string='Attribute Lines'
  • config_step_id Many2one → product.config.step
    comodel_name='product.config.step' required=True string='Configuration Step'
  • name Char
    related='config_step_id.name'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids One2many → product.template.attribute.line
    comodel_name='product.template.attribute.line' compute='_compute_attr_lines' readonly=True store=False string='Attributes'
  • config_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Configuration Session'
  • config_step_ids Many2many → product.config.step
    column1='config_wiz_id' column2='config_step_id' comodel_name='product.config.step' readonly=True relation='product_config_config_steps_rel' store=False string='Configuration Steps'
  • product_id Many2one → product.product
    comodel_name='product.product' help='Set only when re-configuring a existing variant' readonly=True string='Product Variant'
  • product_img Binary
    compute='_compute_cfg_image' readonly=True
Public methods (22)
  • action_config_done(self)
    This method is for the final step which will be taken care by a separate module
  • action_next_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template. More importantly it sets metadata on the context variable so the fields_get and fields_view_get methods can generate the appropriate dynamic content
  • action_previous_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • action_reset(self)
    Delete wizard and configuration session then create a new wizard+session and return an action for the new wizard object
  • add_dynamic_fields(self, res, dynamic_fields, wiz)
    @api.model
    Create the configuration view using the dynamically generated fields in fields_get()
  • apply_onchange_values(self, values, field_name, field_onchange)
    Called from web-controller - original onchage return M2o values in formate (attr-value.id, attr-value.name) but on website we need only attr-value.id
  • create(self, vals_list)
    @api.model_create_multi
    Sets the configuration values of the product_id if given (if any). This is used in reconfiguration of a existing variant
  • fields_get(self, allfields=None, write_access=True, attributes=None)
    @api.model
    Artificially inject fields which are dynamically created using the attribute_ids on the product.template as reference
  • get_field_default_attrs(self)
    @api.model
  • get_form_vals(self, dynamic_fields, domains, cfg_val_ids=None, product_tmpl_id=None, config_session_id=None)
    Generate a dictionary to return new values via onchange method. Domains hold the values available, this method enforces these values if a selection exists in the view that is not available anymore. :param dynamic_fields: Dictionary with the current {dynamic_field: val} :param domains: Odoo domains restricting attribute values :returns vals: Dictionary passed to {'value': vals} by onchange method
  • get_onchange_domains(self, values, cfg_val_ids, product_tmpl_id=False, config_session_id=False)
    Generate domains to be returned by onchange method in order to restrict the availble values of dynamically inserted fields :param values: values argument passed to onchance wrapper :cfg_val_ids: current configuration passed as a list of value_ids (usually in the form of db value_ids + interface value_ids) :returns: a dictionary of domains returned by onchance method
  • get_onchange_vals(self, cfg_val_ids, config_session_id=None)
    Onchange hook to add / modify returned values by onchange method
  • get_state_selection(self)
    Get the states of the wizard using standard values and optional configuration steps set on the product.template via config_step_line_ids
  • get_view(self, view_id=None, view_type='form', **options)
    @api.model
    Generate view dynamically using attributes stored on the product.template
  • get_wizard_action(self, view_cache=False, wizard=None)
    Return action of wizard :param view_cache: Boolean (True/False) :param wizard: recordset of product.configurator :returns : dictionary
  • onchange(self, values, field_name, field_onchange)
    Override the onchange wrapper to return domains to dynamic fields as onchange isn't triggered for non-db fields
  • onchange_product_tmpl(self)
    @api.onchange('product_tmpl_id')
    set the preset_id if exist in session
  • open_step(self, step)
    Open wizard step 'step' :param step: recordset of product.config.step.line
  • prepare_attrs_initial(self, attr_lines, field_prefix, custom_field_prefix, dynamic_fields, wiz)
  • read(self, fields=None, load='_classic_read')
    Remove dynamic fields from the fields list and update the returned values with the dynamic data stored in value_ids
  • setup_modifiers(self, node, field=None)
    @api.model
    Processes node attributes and field descriptors to generate the ``modifiers`` node attribute and set it on the provided node. Alters its first argument in-place. :param node: ``field`` node from an OpenERP view :type node: lxml.etree._Element :param dict field: field descriptor corresponding to the provided node :param dict context: execution context used to evaluate node attributes :param bool current_node_path: triggers the ``column_invisible`` code path (separate from ``invisible``): in tree view there are two levels of invisibility, cell content (a column is present but the cell itself is not displayed) with ``invisible`` and column invisibility (the whole column is hidden) with ``column_invisible``. :returns: nothing
  • write(self, vals)
    Prevent database storage of dynamic fields and instead write values to database persistent value_ids field

New fields (5)
  • config_name Char
    compute='_compute_config_name' string='Configuration Name'
  • config_preset_ok Boolean
    string='Is Preset'
  • weight Float
    compute='_compute_product_weight' inverse='_inverse_product_weight' search='_search_product_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' string='Manual Weight'
  • weight_extra Float
    compute='_compute_product_weight_extra'
Public methods (5)
  • check_config_user_access(self, mode)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • reconfigure_product(self)
    launches a product configurator wizard with a linked template and variant in order to re-configure an existing product. It is essentially a shortcut to pre-fill configuration data of a variant
  • unlink(self)
    - Signal unlink from product variant through context so removal can be stopped for configurable templates - check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (9)
  • attribute_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attr_vals' store=False
  • attribute_value_line_ids One2many → product.attribute.value.line
    comodel_name='product.attribute.value.line' copy=True inverse_name='product_tmpl_id' string='Attribute Value Lines'
  • config_image_ids One2many → product.config.image
    comodel_name='product.config.image' copy=True inverse_name='product_tmpl_id' string='Configuration Images'
  • config_line_ids One2many → product.config.line
    comodel_name='product.config.line' copy=False inverse_name='product_tmpl_id' string='Attribute Dependencies'
  • config_ok Boolean
    string='Can be Configured'
  • config_step_line_ids One2many → product.config.step.line
    comodel_name='product.config.step.line' copy=False inverse_name='product_tmpl_id' string='Configuration Lines'
  • mako_tmpl_name Text
    copy=True help='Generate Name based on Mako Template' string='Variant name'
  • weight Float
    compute='_compute_weight' inverse='_set_weight' search='_search_weight' store=False
  • weight_dummy Float
    digits='Stock Weight' help='Manual setting of product template weight' string='Manual Weight'
Public methods (9)
  • check_attr_value_ids(self)
    @api.constrains('attribute_line_ids', 'attribute_value_line_ids')
    Check attribute lines don't have some attribute value that is not present in attribute lines of that product template
  • check_config_user_access(self)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • configure_product(self)
    launches a product configurator wizard with a linked template in order to configure new product.
  • copy(self, default=None)
    Copy restrictions, config Steps and attribute lines ith product template
  • create(self, vals_list)
    @api.model_create_multi
    Patch for check access rights of user(configurable products)
  • create_config_wizard(self, model_name='product.configurator', extra_vals=None, click_next=True)
    create product configuration wizard - return action to launch wizard - click on next step based on value of click_next
  • toggle_config(self)
  • unlink(self)
    - Prevent the removal of configurable product templates from variants - Patch for check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (5)
  • custom Boolean
    help='Allow custom values for this attribute?'
  • default_val Many2one → product.attribute.value
    comodel_name='product.attribute.value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?'
  • required Boolean
    help='Is this attribute required?'
  • sequence Integer
    default=10
Public methods (2)
  • onchange_attribute(self)
    @api.onchange('attribute_id')
    Set default value of required/multi/cutom from attribute
  • onchange_values(self)
    @api.onchange('value_ids')

New fields (1)
  • weight_extra Float
    digits='Stock Weight' string='Attribute Weight Extra'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/product-configurator
GIT
GIThttps://github.com/OCA/product-configurator.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/product-configurator/tree/14.0/product_configurator
VERSION
VERSION 1.4.0
CATEGORY
CATEGORYGeneric Modules/Base
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONYes
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Pledra
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Pledra
COMMITTERS
COMMITTERSPaul Catinean, Daniel Reis, Donatas, oca-travis, Weblate, OCA-git-bot, Benjamin Henquet, Chandresh Thakkar, Patrick Wilson, Chandresh Thakkar OSI, SHS, RUS, csil300, HET, Julio c Bravo, RabbitJon-S73
WEBSITE
WEBSITEhttps://github.com/OCA/product-configurator
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:40:50
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - account
    - base_setup
    - base
    - web
    - product
    - mail
    - bus
    - web_tour
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - resource
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (24)
XML IDNameModelTypeStatus
assets_backend Product Configurator Assets ir.ui.view qweb Inherits web.assets_backend
config_step_form_view product.configurator.config.step.form product.config.step form New
config_step_tree_view product.configurator.config.step.tree product.config.step form New
configurator_settings_view_form res.config.settings.view.form.inherit.sale res.config.settings xpath Inherits base.res_config_settings_view_form
product_attribute_form_view product.attribute.form.view product.attribute xpath Inherits product.product_attribute_view_form
product_attribute_tree_view product.config.product.attribute.tree product.attribute xpath Inherits product.attribute_tree_view
product_attribute_value_form_view product.config.product.attribute.value.form.view product.attribute.value form New
product_config_domain_form_view product.configurator.domain.form product.config.domain form New
product_config_domain_form_view_template product.configurator.domain.form.template product.config.domain form New
product_config_session_form_view product.config.session.form product.config.session form New
product_config_session_tree_view product.config.session.tree product.config.session tree New
product_configurator_form product.configurator product.configurator form New
product_form_view_custom_vals_inherit product.configurator.form.view.custom.vals product.product xpath Inherits product.product_normal_form_view
product_kanban_view_inherited Product Kanban product.product xpath Inherits product.product_kanban_view
product_template_attribute_line_form_config product.template.attribute.line.form product.template.attribute.line xpath Inherits product.product_template_attribute_line_form
product_template_attribute_value_view_form_weight_extra product.template.attribute.value.view.form.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_form
product_template_attribute_value_view_tree_weight_extra product.template.attribute.value.view.tree.weight.extra product.template.attribute.value xpath Inherits product.product_template_attribute_value_view_tree
product_template_form_view_config_inherited product.template.common.form product.template xpath Inherits product.product_template_form_view
product_template_kanban_view_inherited Product.template.product.kanban product.template xpath Inherits product.product_template_kanban_view
product_template_only_form_view_inherited product.configurator.product.template.form product.template xpath Inherits product.product_template_only_form_view
product_template_search_view product.configurator.product.template.search.view product.template xpath Inherits product.product_template_search_view
product_variant_easy_edit_view_inherit product.product.view.form.easy product.product xpath Inherits product.product_variant_easy_edit_view
template_view_tree_configurable product.template.product.tree product.template xpath Inherits product.product_template_tree_view
variants_tree_view product.attribute.value.tree product.attribute.value tree New
Models touched (17)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (11)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute without deleting it' string='Active'
  • custom_type Selection
    help='The type of the custom field generated in the frontend' selection=CUSTOM_TYPES string='Field Type'
  • description Text
    string='Description' translate=True
  • image Binary
    string='Image'
  • max_val Integer
    help='Maximum value allowed' string='Max Value'
  • min_val Integer
    help='Minimum value allowed' string='Min Value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?' string='Multi'
  • required Boolean
    default=True help='Determines the required value of this attribute though it can be change on the template level' string='Required'
  • search_ok Boolean
    help='When checking for variants with the same configuration, do we include this field in the search?' string='Searchable'
  • uom_id Many2one → uom.uom
    comodel_name='uom.uom' string='Unit of Measure'
  • val_custom Boolean
    help='Allow custom value for this attribute?' string='Custom Value'
Public methods (5)
  • check_searchable_field(self)
    @api.constrains('custom_type', 'search_ok')
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • onchange_custom_type(self)
    @api.onchange('custom_type')
  • onchange_val_custom_field(self)
    @api.onchange('val_custom')
  • validate_custom_val(self, val)
    Pass in a desired custom value and ensure it is valid. Probaly should check type, etc, but let's assume fine for the moment.

New fields (3)
  • active Boolean
    default=True help='By unchecking the active field you can disable a attribute value without deleting it' string='Active'
  • image Binary
    attachment=True help='Attribute value image (Display on website for radio buttons)' string='Image'
  • product_id Many2one → product.product
    comodel_name='product.product' string='Related Product'
Public methods (4)
  • copy(self, default=None)
    Add ' (Copy)' in name to prevent attribute having same name while copying
  • get_attribute_value_extra_prices(self, product_tmpl_id, pt_attr_value_ids, pricelist=None)
    @api.model
  • name_get(self)
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Use name_search as a domain restriction for the frontend to show only values set on the product template taking all the configuration restrictions into account. TODO: This only works when activating the selection not when typing

New fields (6)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' related='value_id.attribute_id'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • product_value_ids Many2many → product.attribute.value
    column1='product_val_id' column2='attr_val_id' comodel_name='product.attribute.value' compute='_compute_get_value_id' relation='product_attr_values_attr_values_rel' store=True
  • sequence Integer
    default=10 string='Sequence'
  • value_id Many2one → product.attribute.value
    comodel_name='product.attribute.value' required='True' string='Attribute Value'
  • value_ids Many2many → product.attribute.value
    column1='product_attribute_value_line_id' column2='product_attribute_value_id' comodel_name='product.attribute.value' relation='product_attribute_value_product_attribute_value_line_rel' string='Values Configuration'
Public methods (0)

No public methods.

New fields (4)
  • domain_line_ids One2many → product.config.domain.line
    comodel_name='product.config.domain.line' copy=True inverse_name='domain_id' required=True string='Restrictions'
  • implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' relation='product_config_domain_implied_rel' string='Inherited'
  • name Char
    required=True string='Name'
  • trans_implied_ids Many2many → product.config.domain
    column1='domain_id' column2='parent_id' comodel_name='product.config.domain' compute=_get_trans_implied string='Transitively inherits'
Public methods (1)
  • compute_domain(self)
    Returns a list of domains defined on a product.config.domain_line_ids and all implied_ids

New fields (7)
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' required=True string='Attribute'
  • condition Selection
    required=True selection=_get_domain_conditions string='Condition'
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Rule'
  • operator Selection
    default='and' required=True selection=_get_domain_operators string='Operators'
  • sequence Integer
    default=1 help='Set the order of operations for evaluation domain lines' string='Sequence'
  • template_attribute_value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attribute_value_ids' string='Template Attribute Values'
  • value_ids Many2many → product.attribute.value
    column1='line_id' column2='attribute_id' comodel_name='product.attribute.value' relation='product_config_domain_line_attr_rel' required=True string='Values'
Public methods (0)

No public methods.

New fields (4)
  • name Char
    required=True translate=True args: 'Name'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product'
  • sequence Integer
    default=10 string='Sequence'
  • value_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' string='Configuration'
Public methods (0)

No public methods.

New fields (7)
  • attr_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' related='attribute_line_id.value_ids' string='Attribute Line Values'
  • attribute_line_id Many2one → product.template.attribute.line
    comodel_name='product.template.attribute.line' ondelete='cascade' required=True string='Attribute Line'
  • domain_id Many2one → product.config.domain
    comodel_name='product.config.domain' required=True string='Restrictions'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10 string='Sequence'
  • template_attribute_ids Many2many → product.attribute
    comodel_name='product.attribute' compute='_compute_template_attribute_ids' string='Template Attributes'
  • value_ids Many2many → product.attribute.value
    column1='cfg_line_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='cfg_line_attr_val_id_rel' string='Values'
Public methods (2)
  • check_value_attributes(self)
    @api.constrains('value_ids')
    Values selected in config lines must belong to the attribute exist on linked attribute line
  • onchange_attribute(self)
    @api.onchange('attribute_line_id')

New fields (13)
  • config_step Char
    string='Configuration Step ID'
  • config_step_name Char
    compute='_compute_config_step_name' string='Configuration Step'
  • currency_id Many2one → res.currency
    comodel_name='res.currency' compute='_compute_currency_id' string='Currency'
  • custom_value_ids One2many → product.config.session.custom.value
    comodel_name='product.config.session.custom.value' inverse_name='cfg_session_id' string='Custom Values'
  • name Char
    readonly=True string='Configuration Session Number'
  • price Float
    compute='_compute_cfg_price' digits='Product Price' store=True string='Price'
  • product_id Many2one → product.product
    comodel_name='product.product' name='Configured Variant' ondelete='cascade'
  • product_preset_id Many2one → product.product
    comodel_name='product.product' domain="[('product_tmpl_id', '=', product_tmpl_id), ('config_preset_ok', '=', True)]" string='Preset'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' domain=[('config_ok', '=', True)] required=True string='Configurable Template'
  • state Selection
    default='draft' required=True selection=[('draft', 'Draft'), ('done', 'Done')] string='State'
  • user_id Many2one → res.users
    comodel_name='res.users' required=True string='User'
  • value_ids Many2many → product.attribute.value
    column1='cfg_session_id' column2='attr_val_id' comodel_name='product.attribute.value' relation='product_config_session_attr_values_rel'
  • weight Float
    compute='_compute_cfg_weight' digits='Stock Weight' string='Weight'
Public methods (35)
  • action_confirm(self, product_id=None)
  • check_and_open_incomplete_step(self, value_ids=None, custom_value_ids=None)
    Check and open incomplete step if any :param value_ids: recordset of product.attribute.value
  • check_attributes_configuration(self, attribute_line_ids, custom_vals, value_ids, final=True)
  • create(self, vals)
    @api.model
  • create_get_session(self, product_tmpl_id, parent_id=None, force_create=False, user_id=None)
    @api.model
  • create_get_variant(self, value_ids=None, custom_vals=None)
    Creates a new product variant with the attributes passed via value_ids and custom_values or retrieves an existing one based on search result :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: new/existing product.product recordset
  • encode_custom_values(self, custom_vals)
    Hook to alter the values of the custom values before creating or writing :param custom_vals: dict {product.attribute.id: custom_value} :returns: list of custom values compatible with write and create
  • flatten_val_ids(self, value_ids)
    Return a list of value_ids from a list with a mix of ids and list of ids (multiselection) :param value_ids: list of value ids or mix of ids and list of ids (e.g: [1, 2, 3, [4, 5, 6]]) :returns: flattened list of ids ([1, 2, 3, 4, 5, 6])
  • formatPrices(self, prices=None, dp='Product Price')
  • get_active_step(self)
    @api.model
    Attempt to return product.config.step.line object that has the id of the config session step stored as string
  • get_adjacent_steps(self, value_ids=None, active_step_line_id=None)
    @api.model
    Returns the previous and next steps given the configuration passed via value_ids and the active step line passed via cfg_step_line_id.
  • get_all_step_lines(self, product_tmpl_id=None)
    @api.model
    Returns a recordset of configuration step lines of product_tmpl_id :param product_tmpl_id: record-set of product.template :returns: recordset of all configuration steps
  • get_cfg_price(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the price of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration price
  • get_cfg_weight(self, value_ids=None, custom_vals=None)
    @api.model
    Computes the weight of the configured product based on the configuration passed in via value_ids and custom_values :param value_ids: list of attribute value_ids :param custom_vals: dictionary of custom attribute values :returns: final configuration weight
  • get_child_specification(self, model, parent)
    @api.model
    return dictiory of onchange specification by appending parent before each key
  • get_components_prices(self, prices, pricelist, value_ids=None)
    Return prices of the components which make up the final configured variant
  • get_config_image(self, value_ids=None, custom_vals=None, size=None)
    Retreive the image object that most closely resembles the configuration code sent via value_ids list For more information check _get_config_image
  • get_custom_value_id(self)
    Return record set of attribute value 'custom'
  • get_extra_attribute_line_ids(self, product_template_id)
    @api.model
    Retrieve attribute lines defined on the product_template_id which are not assigned to configuration steps
  • get_next_step(self, state, product_tmpl_id=False, value_ids=False, custom_value_ids=False)
    Find and return next step if exit. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • get_onchange_specifications(self, model)
    @api.model
    return onchange specification - same functionality by _onchange_spec - needed this method because odoo don't add specification for fields one2many or many2many there is view-reference(using : tree_view_ref) intead of view in that field
  • get_open_step_lines(self, value_ids=None)
    @api.model
    Returns a recordset of configuration step lines open for access given the configuration passed through value_ids e.g: Field A and B from configuration step 2 depend on Field C from configuration step 1. Since fields A and B require action from the previous step, configuration step 2 is deemed closed and redirect is made for configuration step 1. :param value_ids: list of value.ids representing the current configuration :returns: recordset of accesible configuration steps
  • get_session_search_domain(self, product_tmpl_id, state='draft', parent_id=None)
    Return domain to search session linked to given product template and current login user
  • get_session_vals(self, product_tmpl_id, parent_id=None, user_id=None)
    Return the values for creating session
  • get_vals_to_write(self, values, model)
    @api.model
    Return values in formate excepted by write/create methods - same functionality by _convert_to_write - needed this method because odoo don't call convert to write for the many2many/one2many fields
  • get_variant_search_domain(self, product_tmpl_id, value_ids=None)
    @api.model
    Method called by search_variant used to search duplicates in the database
  • get_variant_vals(self, value_ids=None, custom_vals=None, **kwargs)
    @api.model
    Hook to alter the values of the product variant before creation :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: dictionary of values to pass to product.create() method
  • search_session(self, product_tmpl_id, parent_id=None)
  • search_variant(self, value_ids=None, product_tmpl_id=None)
    @api.model
    Searches product.variants with given value_ids and custom values given in the custom_vals dict :param value_ids: list of product.attribute.values ids :param custom_vals: dict {product.attribute.id: custom_value} :returns: product.product recordset of products matching domain
  • update_config(self, attr_val_dict=None, custom_val_dict=None)
    Update the session object with the given value_ids and custom values. Use this method instead of write in order to prevent incompatible configurations as this removed duplicate values for the same attribute. :param attr_val_dict: Dictionary of the form { int (attribute_id): attribute_value_id OR [attribute_value_ids] } :custom_val_dict: Dictionary of the form { int (attribute_id): { 'value': 'custom val', OR 'attachment_ids': { [{ 'name': 'attachment name', 'datas': base64_encoded_string }] } } }
  • update_session_configuration_value(self, vals, product_tmpl_id=None)
    Update value of configuration in current session :param: vals: Dictionary of fields(of configution wizard) and values :param: product_tmpl_id: record set of preoduct template :return: True/False
  • validate_configuration(self, value_ids=None, custom_vals=None, product_tmpl_id=False, final=True)
    @api.model
    Verifies if the configuration values passed via value_ids and custom_vals are valid :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :param final: boolean marker to check required attributes. pass false to check non-final configurations :returns: Error dict with reason of validation failure or True
  • validate_domains_against_sels(self, domains, value_ids=None, custom_vals=None)
  • values_available(self, check_val_ids=None, value_ids=None, custom_vals=None, product_tmpl_id=None)
    @api.model
    Determines whether the attr_values from the product_template are available for selection given the configuration ids and the dependencies set on the product template :param check_val_ids: list of attribute value ids to check for availability :param value_ids: list of attribute value ids :param custom_vals: custom values dict {attr_id: custom_val} :returns: list of available attribute values
  • write(self, vals)
    Validate configuration when writing new values to session

New fields (5)
  • attachment_ids Many2many → ir.attachment
    column1='cfg_sesion_custom_val_id' column2='attachment_id' comodel_name='ir.attachment' relation='product_config_session_custom_value_attachment_rel' string='Attachments'
  • attribute_id Many2one → product.attribute
    comodel_name='product.attribute' required=True string='Attribute'
  • cfg_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Session'
  • name Char
    compute='_compute_val_name' readonly=True store=True string='Name'
  • value Char
    help='Custom value held as string' string='Value'
Public methods (3)
  • check_custom_type(self)
    @api.constrains('attachment_ids', 'value')
  • eval(self)
    Return custom value evaluated using the related custom field type
  • unique_attribute(self)
    @api.constrains('cfg_session_id', 'attribute_id')

New fields (1)
  • name Char
    required=True string='Name' translate=True
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids Many2many → product.template.attribute.line
    column1='cfg_line_id' column2='attr_id' comodel_name='product.template.attribute.line' relation='config_step_line_attr_id_rel' string='Attribute Lines'
  • config_step_id Many2one → product.config.step
    comodel_name='product.config.step' required=True string='Configuration Step'
  • name Char
    related='config_step_id.name'
  • product_tmpl_id Many2one → product.template
    comodel_name='product.template' ondelete='cascade' required=True string='Product Template'
  • sequence Integer
    default=10 string='Sequence'
Public methods (0)

No public methods.

New fields (5)
  • attribute_line_ids One2many → product.template.attribute.line
    comodel_name='product.template.attribute.line' compute='_compute_attr_lines' readonly=True store=False string='Attributes'
  • config_session_id Many2one → product.config.session
    comodel_name='product.config.session' ondelete='cascade' required=True string='Configuration Session'
  • config_step_ids Many2many → product.config.step
    column1='config_wiz_id' column2='config_step_id' comodel_name='product.config.step' readonly=True relation='product_config_config_steps_rel' store=False string='Configuration Steps'
  • product_id Many2one → product.product
    comodel_name='product.product' help='Set only when re-configuring a existing variant' readonly=True string='Product Variant'
  • product_img Binary
    compute='_compute_cfg_image' readonly=True
Public methods (22)
  • action_config_done(self)
    This method is for the final step which will be taken care by a separate module
  • action_next_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template. More importantly it sets metadata on the context variable so the fields_get and fields_view_get methods can generate the appropriate dynamic content
  • action_previous_step(self)
    Proceeds to the next step of the configuration process. This usually implies the next configuration step (if any) defined via the config_step_line_ids on the product.template.
  • action_reset(self)
    Delete wizard and configuration session then create a new wizard+session and return an action for the new wizard object
  • add_dynamic_fields(self, res, dynamic_fields, wiz)
    @api.model
    Create the configuration view using the dynamically generated fields in fields_get()
  • apply_onchange_values(self, values, field_name, field_onchange)
    Called from web-controller - original onchage return M2o values in formate (attr-value.id, attr-value.name) but on website we need only attr-value.id
  • create(self, vals)
    @api.model
    Sets the configuration values of the product_id if given (if any). This is used in reconfiguration of a existing variant
  • fields_get(self, allfields=None, write_access=True, attributes=None)
    @api.model
    Artificially inject fields which are dynamically created using the attribute_ids on the product.template as reference
  • fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False)
    @api.model
    Generate view dynamically using attributes stored on the product.template
  • get_field_default_attrs(self)
    @api.model
  • get_form_vals(self, dynamic_fields, domains, cfg_val_ids=None, product_tmpl_id=None, config_session_id=None)
    Generate a dictionary to return new values via onchange method. Domains hold the values available, this method enforces these values if a selection exists in the view that is not available anymore. :param dynamic_fields: Dictionary with the current {dynamic_field: val} :param domains: Odoo domains restricting attribute values :returns vals: Dictionary passed to {'value': vals} by onchange method
  • get_onchange_domains(self, values, cfg_val_ids, product_tmpl_id=False, config_session_id=False)
    Generate domains to be returned by onchange method in order to restrict the availble values of dynamically inserted fields :param values: values argument passed to onchance wrapper :cfg_val_ids: current configuration passed as a list of value_ids (usually in the form of db value_ids + interface value_ids) :returns: a dictionary of domains returned by onchance method
  • get_onchange_vals(self, cfg_val_ids, config_session_id=None)
    Onchange hook to add / modify returned values by onchange method
  • get_state_selection(self)
    Get the states of the wizard using standard values and optional configuration steps set on the product.template via config_step_line_ids
  • get_wizard_action(self, view_cache=False, wizard=None)
    Return action of wizard :param view_cache: Boolean (True/False) :param wizard: recordset of product.configurator :returns : dictionary
  • onchange(self, values, field_name, field_onchange)
    Override the onchange wrapper to return domains to dynamic fields as onchange isn't triggered for non-db fields
  • onchange_product_tmpl(self)
    @api.onchange('product_tmpl_id')
    set the preset_id if exist in session
  • open_step(self, step)
    Open wizard step 'step' :param step: recordset of product.config.step.line
  • prepare_attrs_initial(self, attr_lines, field_prefix, custom_field_prefix, dynamic_fields, wiz)
  • read(self, fields=None, load='_classic_read')
    Remove dynamic fields from the fields list and update the returned values with the dynamic data stored in value_ids
  • setup_modifiers(self, node, field=None, context=None, current_node_path=None)
    @api.model
    Processes node attributes and field descriptors to generate the ``modifiers`` node attribute and set it on the provided node. Alters its first argument in-place. :param node: ``field`` node from an OpenERP view :type node: lxml.etree._Element :param dict field: field descriptor corresponding to the provided node :param dict context: execution context used to evaluate node attributes :param bool current_node_path: triggers the ``column_invisible`` code path (separate from ``invisible``): in tree view there are two levels of invisibility, cell content (a column is present but the cell itself is not displayed) with ``invisible`` and column invisibility (the whole column is hidden) with ``column_invisible``. :returns: nothing
  • write(self, vals)
    Prevent database storage of dynamic fields and instead write values to database persistent value_ids field

New fields (5)
  • config_name Char
    compute='_compute_config_name' string='Configuration Name'
  • config_preset_ok Boolean
    string='Is Preset'
  • weight Float
    compute='_compute_product_weight' inverse='_inverse_product_weight' store=True
  • weight_dummy Float
    digits='Stock Weight' string='Manual Weight'
  • weight_extra Float
    compute='_compute_product_weight_extra' store=True string='Weight Extra'
Public methods (6)
  • check_config_user_access(self, mode)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • create(self, vals)
    @api.model
    Patch for check access rights of user(configurable products)
  • get_product_attribute_values_action(self)
  • reconfigure_product(self)
    launches a product configurator wizard with a linked template and variant in order to re-configure an existing product. It is essentially a shortcut to pre-fill configuration data of a variant
  • unlink(self)
    - Signal unlink from product variant through context so removal can be stopped for configurable templates - check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (8)
  • attribute_line_val_ids Many2many → product.attribute.value
    comodel_name='product.attribute.value' compute='_compute_template_attr_vals' store=False
  • attribute_value_line_ids One2many → product.attribute.value.line
    comodel_name='product.attribute.value.line' copy=True inverse_name='product_tmpl_id' string='Attribute Value Lines'
  • config_image_ids One2many → product.config.image
    comodel_name='product.config.image' copy=True inverse_name='product_tmpl_id' string='Configuration Images'
  • config_line_ids One2many → product.config.line
    comodel_name='product.config.line' copy=False inverse_name='product_tmpl_id' string='Attribute Dependencies'
  • config_ok Boolean
    string='Can be Configured'
  • config_step_line_ids One2many → product.config.step.line
    comodel_name='product.config.step.line' copy=False inverse_name='product_tmpl_id' string='Configuration Lines'
  • mako_tmpl_name Text
    copy=True help='Generate Name based on Mako Template' string='Variant name'
  • weight_dummy Float
    digits='Stock Weight' help='Manual setting of product template weight' string='Manual Weight'
Public methods (11)
  • check_attr_value_ids(self)
    @api.constrains('attribute_line_ids', 'attribute_value_line_ids')
    Check attribute lines don't have some attribute value that is not present in attribute lines of that product template
  • check_config_user_access(self)
    @api.model
    Check user have access to perform action(create/write/delete) on configurable products
  • configure_product(self)
    launches a product configurator wizard with a linked template in order to configure new product.
  • copy(self, default=None)
    Copy restrictions, config Steps and attribute lines ith product template
  • create(self, vals)
    @api.model
    Patch for check access rights of user(configurable products)
  • create_config_wizard(self, model_name='product.configurator', extra_vals=None, click_next=True)
    create product configuration wizard - return action to launch wizard - click on next step based on value of click_next
  • get_product_attribute_values_action(self)
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
  • toggle_config(self)
  • unlink(self)
    - Prevent the removal of configurable product templates from variants - Patch for check access rights of user(configurable products)
  • write(self, vals)
    Patch for check access rights of user(configurable products)

New fields (5)
  • custom Boolean
    help='Allow custom values for this attribute?' string='Custom'
  • default_val Many2one → product.attribute.value
    comodel_name='product.attribute.value' string='Default Value'
  • multi Boolean
    help='Allow selection of multiple values for this attribute?' string='Multi'
  • required Boolean
    help='Is this attribute required?' string='Required'
  • sequence Integer
    default=10 string='Sequence'
Public methods (2)
  • onchange_attribute(self)
    @api.onchange('attribute_id')
    Set default value of required/multi/cutom from attribute
  • onchange_values(self)
    @api.onchange('value_ids')

New fields (1)
  • weight_extra Float
    digits='Stock Weight' string='Attribute Weight Extra'
Public methods (0)

No public methods.

STATUS
STATUSOpen migration PR - not merged yet for this version
REPOSITORY
REPOSITORYOCA/product-configurator
PULL REQUEST
PULL REQUEST[19.0][MIG] product_configurator: Migration to 19.0. (#168)