Add Function fields on Product from Template

issue5087
review23611002
This commit is contained in:
Cédric Krier committed 2015-12-31 16:03:26 +01:00
1 parent 6fe65f9fe2
commit 0f385fc72e
7 files changed
+138 -69

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
* Add Function fields on Product from Template
Version 3.8.0 - 2015-11-02
* Bug fixes (see mercurial logs for details)
* Be more strict on unit of measure conversion
+85 -54
View File
@@ -1,14 +1,17 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, fields
import copy
from sql import Column
from trytond.model import ModelView, ModelSQL, Model, fields
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond import backend
from trytond.const import OPERATORS
from trytond.config import config
__all__ = ['Template', 'Product', 'price_digits']
__all__ = ['Template', 'Product', 'price_digits', 'TemplateFunction']
STATES = {
'readonly': ~Eval('active', True),
@@ -129,6 +132,36 @@ class Template(ModelSQL, ModelView):
yield record, rec_name, icon
class TemplateFunction(fields.Function):
def __init__(self, field):
super(TemplateFunction, self).__init__(
field, 'get_template', searcher='search_template')
def __copy__(self):
return TemplateFunction(copy.copy(self._field))
def __deepcopy__(self, memo):
return TemplateFunction(copy.deepcopy(self._field, memo))
@staticmethod
def order(name):
@classmethod
def order(cls, tables):
pool = Pool()
Template = pool.get('product.template')
product, _ = tables[None]
if 'template' not in tables:
template = Template.__table__()
tables['template'] = {
None: (template, product.template == template.id),
}
else:
template = tables['template']
return [Column(template, name)]
return order
class Product(ModelSQL, ModelView):
"Product Variant"
__name__ = "product.product"
@@ -141,15 +174,60 @@ class Product(ModelSQL, ModelView):
description = fields.Text("Description", translate=True, states=STATES,
depends=DEPENDS)
active = fields.Boolean('Active', select=True)
default_uom = fields.Function(fields.Many2One('product.uom',
'Default UOM'), 'get_default_uom', searcher='search_default_uom')
type = fields.Function(fields.Selection(TYPES, 'Type'),
'on_change_with_type', searcher='search_type')
list_price_uom = fields.Function(fields.Numeric('List Price',
digits=price_digits), 'get_price_uom')
cost_price_uom = fields.Function(fields.Numeric('Cost Price',
digits=price_digits), 'get_price_uom')
@classmethod
def __setup__(cls):
pool = Pool()
Template = pool.get('product.template')
if not hasattr(cls, '_no_template_field'):
cls._no_template_field = set()
cls._no_template_field.update(['products'])
super(Product, cls).__setup__()
for attr in dir(Template):
tfield = getattr(Template, attr)
if not isinstance(tfield, fields.Field):
continue
if attr in cls._no_template_field:
continue
field = getattr(cls, attr, None)
if not field or isinstance(field, TemplateFunction):
setattr(cls, attr, TemplateFunction(copy.deepcopy(tfield)))
order_method = getattr(cls, 'order_%s' % attr, None)
if not order_method:
order_method = TemplateFunction.order(attr)
setattr(cls, 'order_%s' % attr, order_method)
@fields.depends('template')
def on_change_template(self):
for name, field in self._fields.iteritems():
if isinstance(field, TemplateFunction):
if self.template:
value = getattr(self.template, name)
else:
value = None
setattr(self, name, value)
def get_template(self, name):
value = getattr(self.template, name)
if isinstance(value, Model):
return value.id
elif (isinstance(value, (list, tuple))
and value and isinstance(value[0], Model)):
return [r.id for r in value]
else:
return value
@classmethod
def search_template(cls, name, clause):
return [('template.%s' % name,) + tuple(clause[1:])]
@classmethod
def order_rec_name(cls, tables):
pool = Pool()
@@ -169,13 +247,6 @@ class Product(ModelSQL, ModelView):
def default_active():
return True
def __getattr__(self, name):
try:
return super(Product, self).__getattr__(name)
except AttributeError:
pass
return getattr(self.template, name)
def get_rec_name(self, name):
if self.code:
return '[' + self.code + '] ' + self.name
@@ -193,22 +264,6 @@ class Product(ModelSQL, ModelView):
('template.name',) + tuple(clause[1:]),
]
def get_default_uom(self, name):
return self.template.default_uom.id
@classmethod
def search_default_uom(cls, name, clause):
return [('template.default_uom',) + tuple(clause[1:])]
@fields.depends('template')
def on_change_with_type(self, name=None):
if self.template:
return self.template.type
@classmethod
def search_type(cls, name, clause):
return [('template.type',) + tuple(clause[1:])]
@staticmethod
def get_price_uom(products, name):
Uom = Pool().get('product.uom')
@@ -229,27 +284,3 @@ class Product(ModelSQL, ModelView):
for id_, rec_name, icon in super(Product, cls).search_global(text):
icon = icon or 'tryton-product'
yield id_, rec_name, icon
@classmethod
def search_domain(cls, domain, active_test=True, tables=None):
def is_leaf(expression):
return (isinstance(expression, (list, tuple))
and len(expression) > 2
and isinstance(expression[1], basestring)
and expression[1] in OPERATORS) # TODO remove OPERATORS test
def convert_domain(domain):
'Replace missing product field by the template one'
if is_leaf(domain):
field = domain[0].split('.', 1)[0]
if not getattr(cls, field, None):
field = 'template.' + domain[0]
return (field,) + tuple(domain[1:])
else:
return domain
elif domain in ['OR', 'AND']:
return domain
else:
return [convert_domain(d) for d in domain]
return super(Product, cls).search_domain(convert_domain(domain),
active_test=active_test, tables=tables)
+20 -4
View File
@@ -79,19 +79,35 @@ this repository contains the full copyright notices and license terms. -->
</record>
<record model="ir.ui.view" id="product_view_tree">
<field name="model">product.product</field>
<field name="type" eval="None"/>
<field name="inherit" ref="template_view_tree"/>
<field name="priority" eval="10"/>
<field name="name"></field>
</record>
<record model="ir.ui.view" id="product_view_tree_simple">
<field name="model">product.product</field>
<field name="type">tree</field>
<field name="inherit" eval="None"/>
<field name="name">product_tree</field>
<field name="priority" eval="20"/>
<field name="name">product_tree_simple</field>
</record>
<record model="ir.ui.view" id="product_view_form">
<field name="model">product.product</field>
<field name="type">form</field>
<field name="inherit" eval="None"/>
<field name="type" eval="None"/>
<field name="inherit" ref="template_view_form"/>
<field name="priority" eval="10"/>
<field name="name">product_form</field>
</record>
<record model="ir.ui.view" id="product_view_form_simple">
<field name="model">product.product</field>
<field name="type">form</field>
<field name="priority" eval="20"/>
<field name="name">product_form_simple</field>
</record>
<record model="ir.action.act_window" id="act_product_form">
<field name="name">Variants</field>
<field name="res_model">product.product</field>
+15 -10
View File
@@ -1,13 +1,18 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form string="Product" col="6">
<label name="template"/>
<field name="template"/>
<label name="code"/>
<field name="code"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<separator name="description" colspan="6"/>
<field name="description" colspan="6"/>
</form>
<data>
<xpath expr="/form/field[@name='name']" position="after">
<label name="code"/>
<field name="code"/>
</xpath>
<xpath expr="/form/notebook/page[@id='general']/label[@name='type']"
position="before">
<label name="template"/>
<field name="template" colspan="3"/>
</xpath>
<xpath expr="/form/notebook/page/field[@name='products']" position="replace">
<separator name="description" colspan="6"/>
<field name="description" colspan="6"/>
</xpath>
</data>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form string="Product" col="6">
<label name="template"/>
<field name="template"/>
<label name="code"/>
<field name="code"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<separator name="description" colspan="6"/>
<field name="description" colspan="6"/>
</form>
File renamed without changes.
+3 -1
View File
@@ -6,6 +6,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="name" xexpand="1"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<newline/>
<notebook colspan="6">
<page string="General" id="general">
<label name="type"/>
@@ -23,7 +24,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="cost_price"/>
<label name="cost_price_method"/>
<field name="cost_price_method"/>
<field name="products" mode="form,tree" colspan="4"/>
<field name="products" mode="form,tree" colspan="4"
view_ids="product.product_view_form_simple"/>
</page>
</notebook>
</form>