Add code to template as variant prefix code

The product code is stored as the concatenation of the template code and
variant suffix code each time the template or the product is modified.

issue9080
review293101002
This commit is contained in:
Cédric Krier committed 2020-03-19 00:28:43 +01:00
1 parent 003e3eed33
commit c46798faac
10 files changed
+112 -17

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* 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
* Search also on variant name when searching on template record name
+1
View File
@@ -42,6 +42,7 @@ Template and Product.
The Product Template model contains the following fields:
- Name.
- Code, a common prefix for all products.
- Type, whose value can be *Goods*, *Assets*, *Service*.
- Category.
- List Price, the default sale price expressed in the List Price UOM.
+77 -7
View File
@@ -47,6 +47,7 @@ class Template(
__name__ = "product.template"
name = fields.Char(
"Name", size=None, required=True, translate=True, select=True)
code = fields.Char("Code", select=True)
type = fields.Selection(TYPES, "Type", required=True)
consumable = fields.Boolean('Consumable',
states={
@@ -97,6 +98,12 @@ class Template(
return pool.get('product.cost_price_method')
return super(Template, cls).multivalue_model(field)
def get_rec_name(self, name):
if self.code:
return '[' + self.code + ']' + self.name
else:
return self.name
@classmethod
def search_rec_name(cls, name, clause):
return [('products.rec_name',) + tuple(clause[1:])]
@@ -146,10 +153,24 @@ class Template(
@classmethod
def create(cls, vlist):
pool = Pool()
Product = pool.get('product.product')
vlist = [v.copy() for v in vlist]
for values in vlist:
values.setdefault('products', None)
return super(Template, cls).create(vlist)
templates = super(Template, cls).create(vlist)
products = sum((t.products for t in templates), ())
Product.sync_code(products)
return templates
@classmethod
def write(cls, *args):
pool = Pool()
Product = pool.get('product.product')
super().write(*args)
templates = sum(args[0:None:2], [])
products = sum((t.products for t in templates), ())
Product.sync_code(products)
@classmethod
def search_global(cls, text):
@@ -203,12 +224,19 @@ class Product(
search_context={'default_products': False})
code_readonly = fields.Function(fields.Boolean('Code Readonly'),
'get_code_readonly')
code = fields.Char(
"Code", size=None, select=True,
prefix_code = fields.Function(fields.Char(
"Prefix Code",
states={
'invisible': ~Eval('prefix_code'),
}),
'on_change_with_prefix_code')
suffix_code = fields.Char(
"Suffix Code",
states={
'readonly': Eval('code_readonly', False),
},
depends=['code_readonly'])
code = fields.Char("Code", readonly=True, select=True)
identifiers = fields.One2Many(
'product.identifier', 'product', "Identifiers",
help="Add other identifiers to the variant.")
@@ -263,6 +291,22 @@ class Product(
if isinstance(tfield, fields.One2Many):
getattr(cls, attr).setter = '_set_template_function'
@classmethod
def __register__(cls, module):
table = cls.__table__()
table_h = cls.__table_handler__(module)
fill_suffix_code = (
table_h.column_exist('code')
and not table_h.column_exist('suffix_code'))
super().__register__(module)
cursor = Transaction().connection.cursor()
# Migration from 5.4: split code into prefix/suffix
if fill_suffix_code:
cursor.execute(*table.update(
[table.suffix_code],
[table.code]))
@classmethod
def _set_template_function(cls, products, name, value):
# Prevent NotImplementedError for One2Many
@@ -288,6 +332,11 @@ class Product(
else:
return value
@fields.depends('template', '_parent_template.code')
def on_change_with_prefix_code(self, name=None):
if self.template:
return self.template.code
@classmethod
def multivalue_model(cls, field):
pool = Pool()
@@ -337,6 +386,7 @@ class Product(
('code', clause[1], code_value) + tuple(clause[3:]),
('identifiers.code', clause[1], code_value) + tuple(clause[3:]),
('template.name',) + tuple(clause[1:]),
('template.code',) + tuple(clause[1:]),
]
@staticmethod
@@ -374,7 +424,7 @@ class Product(
return self.default_code_readonly()
@classmethod
def _new_code(cls):
def _new_suffix_code(cls):
pool = Pool()
Sequence = pool.get('ir.sequence')
Configuration = pool.get('product.configuration')
@@ -387,9 +437,17 @@ class Product(
def create(cls, vlist):
vlist = [x.copy() for x in vlist]
for values in vlist:
if not values.get('code'):
values['code'] = cls._new_code()
return super().create(vlist)
if not values.get('suffix_code'):
values['suffix_code'] = cls._new_suffix_code()
products = super().create(vlist)
cls.sync_code(products)
return products
@classmethod
def write(cls, *args):
super().write(*args)
products = sum(args[0:None:2], [])
cls.sync_code(products)
@classmethod
def copy(cls, products, default=None):
@@ -397,6 +455,7 @@ class Product(
default = {}
else:
default = default.copy()
default.setdefault('suffix_code', None)
default.setdefault('code', None)
return super().copy(products, default=default)
@@ -404,6 +463,17 @@ class Product(
def list_price_used(self):
return self.template.get_multivalue('list_price')
@classmethod
def sync_code(cls, products):
for product in products:
code = ''.join(filter(None, [
product.prefix_code, product.suffix_code]))
if not code:
code = None
if code != product.code:
product.code = code
cls.save(products)
class ProductListPrice(ModelSQL, CompanyValueMixin):
"Product List Price"
+18
View File
@@ -21,9 +21,17 @@ Create a template::
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.list_price = Decimal('42.0000')
>>> template.code = "PROD"
>>> template.save()
>>> len(template.products)
1
>>> product, = template.products
>>> product.code
'PROD'
>>> product.suffix_code = "001"
>>> product.save()
>>> product.code
'PROD001'
Create a variant::
@@ -34,6 +42,16 @@ Create a variant::
'Product'
>>> product.list_price
Decimal('42.0000')
>>> product.suffix_code = "002"
>>> product.save()
>>> product.list_price
Decimal('42.0000')
>>> product.code
'PROD002'
Change template code::
>>> template.code = "PRD"
>>> template.save()
>>> sorted([p.code for p in template.products])
['PRD001', 'PRD002']
+2 -2
View File
@@ -395,14 +395,14 @@ class ProductTestCase(ModuleTestCase):
'type': 'assets',
'list_price': Decimal('10'),
'default_uom': uom.id,
'products': [('create', [{'code': 'AA'}])],
'products': [('create', [{'suffix_code': 'AA'}])],
}
values2 = {
'name': 'Product B',
'type': 'goods',
'list_price': Decimal('10'),
'default_uom': uom.id,
'products': [('create', [{'code': 'BB'}])],
'products': [('create', [{'suffix_code': 'BB'}])],
}
template1, template2 = Template.create([values1, values2])
+5 -3
View File
@@ -2,9 +2,11 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/field[@name='name']" position="after">
<label name="code"/>
<field name="code"/>
<xpath expr="/form/field[@name='code']" position="replace">
<group col="-1" name="code" string="">
<field name="prefix_code"/>
<field name="suffix_code"/>
</group>
</xpath>
<xpath expr="/form/notebook/page[@id='general']/label[@name='type']"
position="before">
+4 -1
View File
@@ -6,7 +6,10 @@ this repository contains the full copyright notices and license terms. -->
<field name="template"/>
<newline/>
<label name="code"/>
<field name="code"/>
<group col="-1" name="code" string="">
<field name="prefix_code"/>
<field name="suffix_code"/>
</group>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<label name="cost_price"/>
-3
View File
@@ -4,7 +4,4 @@ this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//field[@name='products']" position="replace">
</xpath>
<xpath expr="/tree/field[@name='name']" position="before">
<field name="code" expand="1"/>
</xpath>
</data>
+3 -1
View File
@@ -4,9 +4,11 @@ this repository contains the full copyright notices and license terms. -->
<form col="6">
<label name="name"/>
<field name="name" xexpand="1"/>
<label name="code"/>
<field name="code"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<newline/>
<notebook colspan="6">
<page string="General" id="general">
<label name="type"/>
+1
View File
@@ -3,6 +3,7 @@
this repository contains the full copyright notices and license terms. -->
<tree>
<field name="name" expand="2"/>
<field name="code" expand="1"/>
<field name="list_price"/>
<field name="cost_price"/>
<field name="type"/>