113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
# 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 sql.conditionals import NullIf
|
|
from sql.functions import CharLength
|
|
from sql.operators import Equal
|
|
|
|
from trytond.model import Exclude, ModelSQL, ModelView, fields, tree
|
|
from trytond.pool import Pool
|
|
from trytond.pyson import Eval, PYSONEncoder
|
|
from trytond.tools import is_full_text, lstrip_wildcard
|
|
|
|
|
|
class Category(tree(separator=' / '), ModelSQL, ModelView):
|
|
__name__ = "product.category"
|
|
name = fields.Char('Name', required=True, translate=True)
|
|
code = fields.Char(
|
|
"Code",
|
|
states={
|
|
'readonly': Eval('code_readonly', False),
|
|
})
|
|
code_readonly = fields.Function(
|
|
fields.Boolean("Code Readonly"), 'get_code_readonly')
|
|
parent = fields.Many2One(
|
|
'product.category', "Parent",
|
|
help="Used to add structure above the category.")
|
|
childs = fields.One2Many(
|
|
'product.category', 'parent', string="Children",
|
|
help="Used to add structure below the category.")
|
|
templates = fields.Many2Many(
|
|
'product.template-product.category', 'category', 'template',
|
|
"Products")
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
cls.code.search_unaccented = False
|
|
super().__setup__()
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('code_unique', Exclude(t, (NullIf(t.code, ''), Equal)),
|
|
'product.msg_category_code_unique'),
|
|
]
|
|
cls._order.insert(0, ('name', 'ASC'))
|
|
cls._buttons.update({
|
|
'add_products': {
|
|
'icon': 'tryton-add',
|
|
},
|
|
})
|
|
|
|
@classmethod
|
|
def default_code_readonly(cls):
|
|
pool = Pool()
|
|
Configuration = pool.get('product.configuration')
|
|
config = Configuration(1)
|
|
return bool(config.category_sequence)
|
|
|
|
def get_code_readonly(self, name):
|
|
return self.default_code_readonly()
|
|
|
|
@classmethod
|
|
def order_code(cls, tables):
|
|
table, _ = tables[None]
|
|
if cls.default_code_readonly():
|
|
return [CharLength(table.code), table.code]
|
|
else:
|
|
return [table.code]
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
_, operator, operand, *extra = clause
|
|
if operator.startswith('!') or operator.startswith('not '):
|
|
bool_op = 'AND'
|
|
else:
|
|
bool_op = 'OR'
|
|
code_value = operand
|
|
if operator.endswith('like') and is_full_text(operand):
|
|
code_value = lstrip_wildcard(operand)
|
|
return [bool_op,
|
|
('name', operator, operand, *extra),
|
|
('code', operator, code_value, *extra),
|
|
]
|
|
|
|
@classmethod
|
|
@ModelView.button_action('product.act_category_product')
|
|
def add_products(cls, categories):
|
|
return {
|
|
'res_id': [categories[0].id if categories else None],
|
|
'pyson_domain': PYSONEncoder().encode(
|
|
[('id', '=', categories[0].id if categories else None)]),
|
|
}
|
|
|
|
@classmethod
|
|
def _code_sequence(cls):
|
|
pool = Pool()
|
|
Configuration = pool.get('product.configuration')
|
|
config = Configuration(1)
|
|
return config.category_sequence
|
|
|
|
@classmethod
|
|
def preprocess_values(cls, mode, values):
|
|
values = super().preprocess_values(mode, values)
|
|
if mode == 'create' and not values.get('code'):
|
|
if sequence := cls._code_sequence():
|
|
values['code'] = sequence.get()
|
|
return values
|
|
|
|
@classmethod
|
|
def copy(cls, categories, default=None):
|
|
default = default.copy() if default is not None else {}
|
|
default.setdefault('templates')
|
|
default.setdefault('code', None)
|
|
return super().copy(categories, default=default)
|