Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions product_attribute_custom_value_variant/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
========================================
Create product variant from custom value
========================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cd9b2a07b3d2aa2243eed929758b4c9b4c69c058aa5772d09cb411de2d0ca13d
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github
:target: https://github.com/OCA/product-attribute/tree/16.0/product_attribute_custom_value_variant
:alt: OCA/product-attribute
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/product-attribute-16-0/product-attribute-16-0-product_attribute_custom_value_variant
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/product-attribute&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Create variants when configured custom attributes are used.

The created variants are useful to retrieve the stock quantity or for purchase.

**Table of contents**

.. contents::
:local:

Configuration
=============

In any custom attribute value, enable `Create custom variant`.

Usage
=====

When a product template is added to a sale order line, the configured custom values become real attribute values.
Here is an example:

1. Create an attribute "Length (cm)"
#. Add some values to the attribute:
- 5
- 10
- Custom
#. For the "Custom" value, enable `custom` and `Create custom variant` (added by this module)
#. Create a product template "Glass" having the created attribute and all its values
#. Create a sale order that includes the product template
#. Set the value 42 for the attribute "Length (cm)"

With the above steps, this module creates:

- | A new attribute value "42" for the attribute "Length (cm)"
| This attribute value is not linked to the product template, so it does not appear as a choice when the template is sold.
- A variant for the template, linked to the created attribute value

The created variant can then be used to retrieve its stock quantity or to purchase that exact product variant that had been sold.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/product-attribute/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/product-attribute/issues/new?body=module:%20product_attribute_custom_value_variant%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Aion Tech

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/product-attribute <https://github.com/OCA/product-attribute/tree/16.0/product_attribute_custom_value_variant>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
3 changes: 3 additions & 0 deletions product_attribute_custom_value_variant/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import models
19 changes: 19 additions & 0 deletions product_attribute_custom_value_variant/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Simone Rubino - Aion Tech
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Create product variant from custom value",
"summary": "When a custom value is assigned, create a variant instead.",
"version": "16.0.1.0.0",
"website": "https://github.com/OCA/product-attribute",
"author": "Aion Tech, Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Sales/Sales",
"depends": [
"product",
"sale",
],
"data": [
"views/product_attribute_value_views.xml",
],
}
6 changes: 6 additions & 0 deletions product_attribute_custom_value_variant/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import product_attribute_value
from . import product_attribute
from . import product_product
from . import sale_order_line
35 changes: 35 additions & 0 deletions product_attribute_custom_value_variant/models/product_attribute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2025 Simone Rubino - Aion Tech
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class ProductAttribute(models.Model):
_inherit = "product.attribute"

def _prepare_variant_custom_attribute_value(self, custom_value):
self.ensure_one()
return {
"attribute_id": self.id,
"name": custom_value,
}

def _get_variant_custom_attribute_value(self, custom_value):
self.ensure_one()
new_attribute_value = self.env["product.attribute.value"].search(
[
(
"attribute_id",
"=",
self.id,
),
("name", "=", custom_value),
],
limit=1,
)
if not new_attribute_value:
new_attribute_value = self.env["product.attribute.value"].create(
self._prepare_variant_custom_attribute_value(custom_value)
)

return new_attribute_value
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2025 Simone Rubino - Aion Tech
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError


class ProductAttributeValue(models.Model):
_inherit = "product.attribute.value"

create_custom_variant = fields.Boolean(
string="Create custom variant",
help="When this custom attribute is used, \
a new value and variant will be generated.",
)

@api.constrains(
"create_custom_variant",
"is_custom",
)
def _constrain_create_custom_variant(self):
for value in self:
if value.create_custom_variant and not value.is_custom:
raise ValidationError(

Check warning on line 24 in product_attribute_custom_value_variant/models/product_attribute_value.py

View check run for this annotation

Codecov / codecov/patch

product_attribute_custom_value_variant/models/product_attribute_value.py#L24

Added line #L24 was not covered by tests
_("'Create custom variant' can only be set on custom values")
)
14 changes: 14 additions & 0 deletions product_attribute_custom_value_variant/models/product_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2025 Simone Rubino - Aion Tech
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class ProductProduct(models.Model):
_inherit = "product.product"

def _unlink_or_archive(self, check_access=True):
custom_variants_to_keep_ids = self.env.context.get("no_remove_custom_variants")
return super(
ProductProduct, self - self.browse(custom_variants_to_keep_ids)
)._unlink_or_archive(check_access=check_access)
71 changes: 71 additions & 0 deletions product_attribute_custom_value_variant/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2025 Simone Rubino - Aion Tech
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

def _assign_new_custom_variant(self):
"""Create new variants and assign them to the `self`."""
custom_values_to_unlink = self.env["product.attribute.custom.value"].browse()
for line in self:

# Create new attribute values for each "Create custom variant" custom value
new_attribute_values = self.env["product.attribute.value"].browse()
for custom_attribute_value in line.product_custom_attribute_value_ids:
template_attribute_value = (
custom_attribute_value.custom_product_template_attribute_value_id
)
attribute_value = template_attribute_value.product_attribute_value_id
if attribute_value.create_custom_variant:
attribute = template_attribute_value.attribute_id
new_attribute_value = attribute._get_variant_custom_attribute_value(
custom_attribute_value.custom_value,
)
new_attribute_values |= new_attribute_value
template_attribute_value.attribute_line_id.value_ids |= (
new_attribute_value
)

custom_values_to_unlink |= custom_attribute_value

# If new attribute values have been created,
# use them to create a new variant and set it in the line
attribute_lines = line.product_template_id.attribute_line_ids
custom_combination = attribute_lines.product_template_value_ids.filtered(
lambda ptav: ptav.product_attribute_value_id in new_attribute_values
)
if custom_combination:
variant_combination = (
line.product_id.product_template_attribute_value_ids
)
new_variant_combination = (
variant_combination.filtered(
lambda ptav: not ptav.product_attribute_value_id.create_custom_variant
)
| custom_combination
)
new_variant = line.product_template_id._create_product_variant(
new_variant_combination
)
line.product_id = new_variant
# The new attribute values must not be available for new models
for attribute_line in attribute_lines:
attribute_line.with_context(
no_remove_custom_variants=new_variant.ids,
).value_ids -= (
attribute_line.value_ids & new_attribute_values
)

if custom_values_to_unlink:
custom_values_to_unlink.unlink()

@api.model_create_multi
def create(self, vals_list):
lines = super().create(vals_list)
custom_value_lines = lines.filtered("product_custom_attribute_value_ids")
if custom_value_lines:
custom_value_lines._assign_new_custom_variant()
return lines
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In any custom attribute value, enable `Create custom variant`.
3 changes: 3 additions & 0 deletions product_attribute_custom_value_variant/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Create variants when configured custom attributes are used.

The created variants are useful to retrieve the stock quantity or for purchase.
20 changes: 20 additions & 0 deletions product_attribute_custom_value_variant/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
When a product template is added to a sale order line, the configured custom values become real attribute values.
Here is an example:

1. Create an attribute "Length (cm)"
#. Add some values to the attribute:
- 5
- 10
- Custom
#. For the "Custom" value, enable `custom` and `Create custom variant` (added by this module)
#. Create a product template "Glass" having the created attribute and all its values
#. Create a sale order that includes the product template
#. Set the value 42 for the attribute "Length (cm)"

With the above steps, this module creates:

- | A new attribute value "42" for the attribute "Length (cm)"
| This attribute value is not linked to the product template, so it does not appear as a choice when the template is sold.
- A variant for the template, linked to the created attribute value

The created variant can then be used to retrieve its stock quantity or to purchase that exact product variant that had been sold.
Loading
Loading