# 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): "Product Category" __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(Category, cls).__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], 'pyson_domain': PYSONEncoder().encode( [('id', '=', categories[0].id)]), } @classmethod def _new_code(cls): pool = Pool() Configuration = pool.get('product.configuration') config = Configuration(1) sequence = config.category_sequence if sequence: return sequence.get() @classmethod def create(cls, vlist): vlist = [v.copy() for v in vlist] for values in vlist: if not values.get('code'): values['code'] = cls._new_code() return super().create(vlist) @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)