# HG changeset patch # User Matthieu Gautier <matthieu.gautier@mgautier.fr> # Date 1429114256 -7200 # Wed Apr 15 18:10:56 2015 +0200 # Branch odoo8 # Node ID 459109cdadb33779e8e16af495e4a6d2302e952b # Parent e680f59ab07db9a142e6729f71a773cb2d224a5e Port to odoo v8 Main changes are : - Change fields declaration - Update metaclass to add field to nmspc instead of _columns - Add v8's version of compute/reverse/search function of disable_per_company field diff --git a/MetaAnalytic.py b/MetaAnalytic.py --- a/MetaAnalytic.py +++ b/MetaAnalytic.py @@ -1,5 +1,5 @@ from openerp import SUPERUSER_ID -from openerp.osv import fields +from openerp import fields, api from openerp.tools import config from openerp.addons.oemetasl import OEMetaSL @@ -30,7 +30,7 @@ Notes: * This metaclass may directly modify attributes that are used by OpenERP, - specifically _columns and _inherits. + specifically _inherits and the fields of the class. * New superclasses are used to define or override methods, in order to avoid interacting with OEMetaSL or the model's own method (re)definitions. """ @@ -41,11 +41,6 @@ para = nmspc.get('_para_analytic', {}) dimension = nmspc.get('_dimension', {}) - columns = nmspc.get('_columns', None) - if columns is None: - columns = {} - nmspc['_columns'] = columns - defaults = nmspc.get('_defaults', None) if defaults is None: defaults = {} @@ -58,13 +53,13 @@ # Analytic fields should be defined in the _analytic attribute. if analytic or para: bases = cls._setup_analytic_fields( - analytic, para, columns, defaults, orm_name, name, bases, nmspc + analytic, para, defaults, orm_name, name, bases, nmspc ) # The bound dimension should be defined in the _dimension attribute. if dimension: bases = cls._setup_bound_dimension( - dimension, columns, defaults, orm_name, name, bases, nmspc + dimension, defaults, orm_name, name, bases, nmspc ) return super(MetaAnalytic, cls).__new__(cls, name, bases, nmspc) @@ -74,7 +69,7 @@ @classmethod def _setup_analytic_fields( - cls, analytic, para, columns, defaults, orm_name, name, bases, nmspc + cls, analytic, para, defaults, orm_name, name, bases, nmspc ): """Generate analytic and para-analytic fields on the model.""" @@ -86,17 +81,16 @@ # Create a field that will be used for replacement in the view if analytic: - columns['analytic_dimensions'] = fields.function( - lambda self, cr, uid, ids, *a: {i: '' for i in ids}, - string=u"Analytic Dimensions", - readonly=True, - store=False, + nmspc['analytic_dimensions'] = fields.Char( + string = u"Analytic Dimensions", + compute = lambda self, cr, uid, ids, *a: {i: '' for i in ids}, + readonly=True ) col_pattern = '{pre}{n}_{suf}' size = int(config.get_misc('analytic', 'analytic_size', 5)) - # Generate the fields directly into the _columns attribute. + # Generate the fields directly into the nmspc. all_analytic = [] for prefix, model_name in analytic.iteritems(): @@ -106,7 +100,7 @@ for n in xrange(1, size + 1): col_name = col_pattern.format(pre=prefix, n=n, suf='id') domain_field = 'nd_id.ns{n}_id.model_name'.format(n=n) - columns[col_name] = fields.many2one( + nmspc[col_name] = fields.Many2one( 'analytic.code', "Generated Analytic Field", domain=[ @@ -130,7 +124,7 @@ kwargs = value['kwargs'] for n in xrange(1, size + 1): col_name = col_pattern.format(pre=prefix, n=n, suf=suffix) - columns[col_name] = field_type(*args, **kwargs) + nmspc[col_name] = field_type(*args, **kwargs) if 'default' in value: defaults[col_name] = value['default'] @@ -186,7 +180,7 @@ @classmethod def _setup_bound_dimension( - cls, dimension, columns, defaults, orm_name, name, bases, nmspc + cls, dimension, defaults, orm_name, name, bases, nmspc ): """Bind a dimension to the model, creating a code for each record.""" @@ -252,7 +246,7 @@ use_inherits = dimension.get('use_inherits', None) if use_inherits is None: use_inherits = not ( - any(col in columns for col in ('name', 'nd_id')) + any(field in nmspc for field in ('name', 'nd_id')) or nmspc.get('_inherits', False) or nmspc.get('_inherit', False) ) @@ -271,8 +265,8 @@ nmspc['_inherits'] = inherits # Default column for the underlying analytic code. - if column not in columns: - columns[column] = fields.many2one( + if column not in nmspc: + nmspc[column] = fields.Many2one( 'analytic.code', u"Bound Analytic Code", required=True, @@ -297,20 +291,12 @@ return osv.search(cr, uid, domain, context=context) for string, model_col, code_col, dtype, req, default in rel_cols: - columns[model_col] = fields.related( - column, - code_col, - string=string, - type=dtype, + nmspc[model_col] = getattr(fields, dtype)( + string = string, + related = ".".join([column, code_cod]), relation="analytic.code", required=req, - store={ - 'analytic.code': ( - _record_from_code_id, - [code_col], - 10 - ) - } + store=True ) if model_col not in defaults: defaults[model_col] = default diff --git a/analytic_code.py b/analytic_code.py --- a/analytic_code.py +++ b/analytic_code.py @@ -18,11 +18,11 @@ # ############################################################################## -from openerp.osv import fields, osv +from openerp import models, fields, api from openerp.tools import config -class analytic_code(osv.Model): +class analytic_code(models.Model): _name = 'analytic.code' _description = u"Analytic Code" @@ -31,69 +31,44 @@ _parent_order = 'name' _order = 'parent_left' - def _read_disabled_per_company( - self, cr, uid, ids, field_name, arg, context - ): + @api.depends('blacklist_ids') + def _read_disabled_per_company(self): """Mark the code as disabled when it is in the blacklist (depending on the current user's company). """ - anc_obj = self.pool['analytic.code'] - user_obj = self.pool['res.users'] - - company_id = user_obj.read( - cr, uid, [uid], ['company_id'], context=context - )[0]['company_id'][0] - - ret = {} + company_id = self.env.user.company_id.id - for anc in anc_obj.browse(cr, uid, ids, context=context): + for anc in self: blacklist = (company.id for company in anc.blacklist_ids) - ret[anc.id] = company_id in blacklist + anc.disabled_per_company = company_id in blacklist - return ret - - def _write_disabled_per_company( - self, cr, uid, anc_id, field_name, field_value, arg, context - ): + def _write_disabled_per_company(self): """Update the blacklist depending on the current user's company. """ - anc_obj = self.pool['analytic.code'] - user_obj = self.pool['res.users'] - - company_id = user_obj.read( - cr, uid, [uid], ['company_id'], context=context - )[0]['company_id'][0] - - anc = anc_obj.browse(cr, uid, anc_id, context=context) - blacklist = (company.id for company in anc.blacklist_ids) + company_id = self.env.user.company_id.id - to_write = None - if field_value and company_id not in blacklist: - to_write = [(4, company_id)] # Link. - elif not field_value and company_id in blacklist: - to_write = [(3, company_id)] # Unlink. - - if to_write: - anc_obj.write( - cr, uid, [anc_id], {'blacklist_ids': to_write}, context=context - ) + for anc in self: + blacklist = (company.id for company in anc.blacklist_ids) + + to_write = None + if anc.disabled_per_company and company_id not in blacklist: + to_write = [(4, company_id)] # Link. + elif not anc.disabled_per_company and company_id in blacklist: + to_write = [(3, company_id)] # Unlink. + + if to_write: + anc.write({'blacklist_ids': to_write}) return True - def _search_disabled_per_company( - self, cr, uid, model_again, field_name, criterion, context - ): + def _search_disabled_per_company(self, operator, value): """Update the domain to take the blacklist into account (depending on the current user's company). """ - user_obj = self.pool['res.users'] - - company_id = user_obj.read( - cr, uid, [uid], ['company_id'], context=context - )[0]['company_id'][0] + company_id = self.env.user.company_id.id # We assume the criterion was "disabled_per_company = False". dom = [ @@ -101,96 +76,86 @@ ('blacklist_ids', '=', False), ('blacklist_ids', '!=', company_id), # Not blacklists_ids.id! ] - if criterion[0][2] is True: + if value is True: dom = ['!'] + dom return dom - _columns = { - 'name': fields.char( - u"Name", - size=128, - translate=config.get_misc('analytic', 'translate', False), - required=True, - ), - 'nd_id': fields.many2one( - 'analytic.dimension', - string=u"Dimension", - ondelete='cascade', - required=True, - ), + name = fields.Char( + u"Name", + size=128, + translate=config.get_misc('analytic', 'translate', False), + required=True, + ) + nd_id = fields.Many2one( + 'analytic.dimension', + string=u"Dimension", + ondelete='cascade', + required=True, + ) - 'active': fields.boolean( - u"Active", - help=( - u"Determines whether an analytic code is in the referential." - ), + active = fields.Boolean( + u"Active", + help=( + u"Determines whether an analytic code is in the referential." ), - 'view_type': fields.boolean( - u"View type", - help=( - u"Determines whether an analytic code is not selectable (but " - u"still in the referential)." - ), - ), - 'blacklist_ids': fields.many2many( - 'res.company', - 'analytic_code_company_rel', - 'code_id', - 'company_id', - u"Blacklist", - help=u"Companies the code is hidden in.", + default = lambda *a: True + ) + view_type = fields.Boolean( + u"View type", + help=( + u"Determines whether an analytic code is not selectable (but " + u"still in the referential)." ), - 'disabled_per_company': fields.function( - _read_disabled_per_company, - fnct_inv=_write_disabled_per_company, - fnct_search=_search_disabled_per_company, - method=True, - type='boolean', - store=False, # Not persistent as it depends on the company. - string=u"Disabled in my company", - help=( - u"Determines whether an analytic code is disabled for the " - u"current company." - ), - ), - - 'nd_name': fields.related( - 'nd_id', - 'name', - type='char', - string=u"Dimension Name", - store=False + default = lambda *a: False + ) + blacklist_ids = fields.Many2many( + 'res.company', + 'analytic_code_company_rel', + 'code_id', + 'company_id', + u"Blacklist", + help=u"Companies the code is hidden in.", + ) + disabled_per_company = fields.Boolean( + string = u"Disable in my company", + compute = _read_disabled_per_company, + inverse = _write_disabled_per_company, + search = _search_disabled_per_company, + help=( + u"Determines whether an analytic code is disabled for the " + u"current company." ), - 'description': fields.char( - u"Description", - size=512, - translate=config.get_misc('analytic', 'translate', False), - ), - 'code_parent_id': fields.many2one( - 'analytic.code', - u"Parent Code", - select=True, - ondelete='restrict', - ), - 'child_ids': fields.one2many( - 'analytic.code', - 'code_parent_id', - u"Child Codes", - ), - 'parent_left': fields.integer(u"Left parent", select=True), - 'parent_right': fields.integer(u"Right parent", select=True), - } + default = lambda *a: False + ) - _defaults = { - 'active': lambda *a: True, - 'view_type': lambda *a: False, - 'disabled_per_company': lambda *a: False, - } + nd_name = fields.Char( + related = 'nd_id.name', + string=u"Dimension Name", + store=False + ) + description = fields.Char( + u"Description", + size=512, + translate=config.get_misc('analytic', 'translate', False), + ) + code_parent_id = fields.Many2one( + 'analytic.code', + u"Parent Code", + select=True, + ondelete='restrict', + ) + child_ids = fields.One2many( + 'analytic.code', + 'code_parent_id', + u"Child Codes", + ) + parent_left = fields.Integer(u"Left parent", select=True) + parent_right = fields.Integer(u"Right parent", select=True) _constraints = [ # very useful base class constraint ( - osv.Model._check_recursion, + models.Model._check_recursion, u"Error ! You can not create recursive analytic codes.", ['parent_id'] ), diff --git a/analytic_dimension.py b/analytic_dimension.py --- a/analytic_dimension.py +++ b/analytic_dimension.py @@ -18,7 +18,7 @@ # ############################################################################## -from openerp.osv import fields, osv +from openerp import models, fields, api from openerp.addons.oemetasl import OEMetaSL from openerp.tools import config @@ -27,10 +27,9 @@ def __new__(cls, name, bases, nmspc): - columns = nmspc['_columns'] size = int(config.get_misc('analytic', 'analytic_size', 5)) for n in xrange(1, size + 1): - columns['ns{}_id'.format(n)] = fields.one2many( + nmspc['ns{}_id'.format(n)] = fields.One2many( 'analytic.structure', 'nd_id', "Generated Subset of Structures", @@ -40,22 +39,28 @@ return super(_dimension_meta, cls).__new__(cls, name, bases, nmspc) -class analytic_dimension(osv.Model): +class analytic_dimension(models.Model): __metaclass__ = _dimension_meta _name = 'analytic.dimension' _description = u"Analytic Dimension" - _columns = { - 'name': fields.char( - u"Name", - size=128, - translate=config.get_misc('analytic', 'translate', False), - required=True, - ), - 'nc_ids': fields.one2many('analytic.code', 'nd_id', u"Codes"), - 'ns_id': fields.one2many('analytic.structure', 'nd_id', u"Structures"), - } + name = fields.Char( + string=u"Name", + size=128, + translate=config.get_misc('analytic', 'translate', False), + required=True, + ) + + nc_ids = fields.One2many( + comodel_name='analytic.code', + inverse_name='nd_id', + string=u"Codes") + + ns_id = fields.One2many( + comodel_name='analytic.structure', + inverse_name='nd_id', + string=u"Structures") _sql_constraints = [ ('unique_name', 'unique(name)', u"Name must be unique"), diff --git a/analytic_structure.py b/analytic_structure.py --- a/analytic_structure.py +++ b/analytic_structure.py @@ -18,7 +18,7 @@ # ############################################################################## -from openerp.osv import fields, osv +from openerp import fields, models, api from openerp.tools import config from openerp.tools.translate import _ import re @@ -26,12 +26,12 @@ import json -class analytic_structure(osv.Model): +class analytic_structure(models.Model): _name = 'analytic.structure' _description = u"Analytic Structure" - def order_selection(self, cr, uid, context=None): + def order_selection(self): order_selection = getattr(self, '_order_selection', None) if order_selection is None: size = int(config.get_misc('analytic', 'analytic_size', 5)) @@ -41,9 +41,10 @@ setattr(self, '_order_selection', order_selection) return order_selection - def _check_unique_ordering_no_company(self, cr, uid, ids, context=None): + @api.constrains('company_id', 'model_name', 'ordering') + def _check_unique_ordering_no_company(self): columns = ['company_id', 'model_name', 'ordering'] - structures = self.read(cr, uid, ids, columns, context=context) + structures = self.read(columns) for structure in structures: if structure['company_id']: continue # Already checked by the SQL constraint. @@ -51,48 +52,33 @@ ('model_name', '=', structure['model_name']), ('ordering', '=', structure['ordering']), ] - count = self.search(cr, uid, domain, count=True, context=context) + count = self.search_count(domain) if count > 1: - return False - return True + raise exceptions.ValidationError(u"One dimension per Analysis slot per object when the structure is common to all companies.") - _columns = { - 'model_name': fields.char( - u"Object", - size=128, - required=True, - select='1', - ), - 'nd_id': fields.many2one( - 'analytic.dimension', - u"Related Dimension", - ondelete='restrict', - required=True, - select='1', - ), - 'ordering': fields.selection( - order_selection, - u"Analysis slot", - required=True, - ), - 'company_id': fields.many2one( - 'res.company', - u"Company", - ), - } - - _defaults = { - 'company_id': lambda *a: False, - } - - _constraints = [ - ( - _check_unique_ordering_no_company, - u"One dimension per Analysis slot per object when the structure " - u"is common to all companies.", - ['company_id', 'model_name', 'ordering'] - ) - ] + model_name = fields.Char( + u"Object", + size=128, + required=True, + select='1', + ) + nd_id = fields.Many2one( + 'analytic.dimension', + u"Related Dimension", + ondelete='restrict', + required=True, + select='1', + ) + ordering = fields.Selection( + order_selection, + u"Analysis slot", + required=True, + ) + company_id = fields.Many2one( + 'res.company', + u"Company", + default = lambda *a: False + ) _sql_constraints = [ (