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:
1 parent
003e3eed33
commit
c46798faac
10 files changed
+112
-17
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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']
|
||||
@@ -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])
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in new issue
Block a user