diff --git a/CHANGELOG b/CHANGELOG index 1fe906523c..185bb03e98 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Make product code unique * Add code to template as variant prefix code * Hide product fields inherited from template when empty * Skip default products when template is created from product diff --git a/doc/index.rst b/doc/index.rst index 8ce7d3dffe..c06abe7157 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -61,10 +61,10 @@ The Product Template model contains the following fields: - Active, allow to disable a product. -The Product model extend the Product Template with two fields: Code -and Description. If a sequence is set on product configuration the code will -be readonly and will be filled in using the sequence. It's also possible to -define a list of identifiers on product. An identifier is composed by a type +The Product model extends the Product Template with two fields: Code (aka SKU_) +and Description. If a sequence is set on product configuration the code will be +read-only and will be filled in using the sequence. It's also possible to +define a list of identifiers on product. An identifier is composed by a type and a code. The following types are available: * International Article Number (EAN) @@ -74,6 +74,8 @@ and a code. The following types are available: * International Securities Identification Number (ISIN) * International Standard Music Number (ISMN) +.. _SKU: https://en.wikipedia.org/wiki/Stock_keeping_unit + Configuration ************* diff --git a/message.xml b/message.xml index d77bc0e9f2..d8b1f2f10e 100644 --- a/message.xml +++ b/message.xml @@ -24,5 +24,8 @@ this repository contains the full copyright notices and license terms. --> The %(type)s "%(code)s" for product "%(product)s" is not valid. + + Code of active product must be unique. + diff --git a/product.py b/product.py index 1927da7d90..e9f20e1f5f 100644 --- a/product.py +++ b/product.py @@ -7,12 +7,13 @@ from importlib import import_module import stdnum import stdnum.exceptions -from sql import Null, Column +from sql import Null, Column, Literal +from sql.operators import Equal from trytond.i18n import gettext from trytond.model import ( ModelView, ModelSQL, Model, UnionMixin, DeactivableMixin, sequence_ordered, - fields) + Exclude, fields) from trytond.pyson import Eval from trytond.transaction import Transaction from trytond.pool import Pool @@ -235,7 +236,8 @@ class Product( states={ 'readonly': Eval('code_readonly', False), }, - depends=['code_readonly']) + depends=['code_readonly'], + help="The unique identifier for the product (aka SKU).") code = fields.Char("Code", readonly=True, select=True) identifiers = fields.One2Many( 'product.identifier', 'product', "Identifiers", @@ -261,6 +263,14 @@ class Product( super(Product, cls).__setup__() + t = cls.__table__() + cls._sql_constraints = [ + ('code_exclude', Exclude(t, (t.code, Equal), + where=(t.active == Literal(True)) + & (t.code != '')), + 'product.msg_product_code_unique'), + ] + for attr in dir(Template): tfield = getattr(Template, attr) if not isinstance(tfield, fields.Field):