Files
tryton/trytond/ir/rule.py
T

231 lines
8.9 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.
"Rule"
from trytond.osv import fields, OSV
from trytond.tools import Cache
import time
class RuleGroup(OSV):
"Rule group"
_name = 'ir.rule.group'
_description = __doc__
name = fields.Char('Name', select=1)
model = fields.Many2One('ir.model', 'Model', select=1,
required=True)
global_p = fields.Boolean('Global', select=1,
help="Make the rule global \n" \
"so every users must follow this rule")
default_p = fields.Boolean('Default', select=1,
help="Add this rule to all users by default")
rules = fields.One2Many('ir.rule', 'rule_group', 'Tests',
help="The rule is satisfied if at least one test is True")
groups = fields.Many2Many('res.group', 'group_rule_group_rel',
'rule_group_id', 'group_id', 'Groups')
users = fields.Many2Many('res.user', 'user_rule_group_rel',
'rule_group_id', 'user_id', 'Users')
def __init__(self):
super(RuleGroup, self).__init__()
self._order.insert(0, ('model', 'ASC'))
self._order.insert(1, ('global_p', 'ASC'))
self._order.insert(2, ('default_p', 'ASC'))
self._sql_constraints += [
('global_default_exclusive', 'CHECK(NOT(global_p AND default_p))',
'Global and Default are mutually exclusive!'),
]
def default_global_p(self, cursor, user, context=None):
return True
def default_default_p(self, cursor, user, context=None):
return False
def delete(self, cursor, user, ids, context=None):
res = super(RuleGroup, self).delete(cursor, user, ids,
context=context)
# Restart the cache on the domain_get method of ir.rule
self.pool.get('ir.rule').domain_get(cursor.dbname)
return res
def create(self, cursor, user, vals, context=None):
res = super(RuleGroup, self).create(cursor, user, vals,
context=context)
# Restart the cache on the domain_get method of ir.rule
self.pool.get('ir.rule').domain_get(cursor.dbname)
return res
def write(self, cursor, user, ids, vals, context=None):
res = super(RuleGroup, self).write(cursor, user, ids, vals,
context=context)
# Restart the cache on the domain_get method of ir.rule
self.pool.get('ir.rule').domain_get(cursor.dbname)
return res
RuleGroup()
class Rule(OSV):
"Rule"
_name = 'ir.rule'
_rec_name = 'field'
_description = __doc__
field = fields.Many2One('ir.model.field', 'Field',
domain="[('model', '=', _parent_rule_group.model)]", select=1,
required=True)
operator = fields.Selection([
('=', '='),
('<>', '<>'),
('<=', '<='),
('>=', '>='),
('in', 'in'),
('child_of', 'child_of'),
], 'Operator', required=True, translate=False)
operand = fields.Selection('get_operand','Operand', required=True)
rule_group = fields.Many2One('ir.rule.group', 'Group', select=2,
required=True, ondelete="cascade")
def _operand_get(self, cursor, user, obj_name='', level=3, recur=None, root_tech='', root=''):
res = {}
if not obj_name:
obj_name = 'res.user'
res.update({"False": "False", "True": "True", "User": "user.id"})
if not recur:
recur = []
obj_fields = self.pool.get(obj_name).fields_get(cursor, user)
key = obj_fields.keys()
key.sort()
for k in key:
if obj_fields[k]['type'] in ('many2one'):
res[root + '/' + obj_fields[k]['string']] = \
root_tech + '.' + k + '.id'
elif obj_fields[k]['type'] in ('many2many', 'one2many'):
res[root + '/' + obj_fields[k]['string']] = \
'[x.id for x in ' + root_tech + '.' + k + ']'
else:
res[root + '/' + obj_fields[k]['string']] = \
root_tech + '.' + k
if (obj_fields[k]['type'] in recur) and (level>0):
res.update(self._operand_get(cursor, user,
obj_fields[k]['relation'], level-1,
recur, root_tech + '.' + k, root + '/' + \
obj_fields[k]['string']))
return res
def get_operand(self, cursor, user, context=None):
res = []
operands = self._operand_get(cursor, user, 'res.user', level=1,
recur=['many2one'], root_tech='user', root='User')
for i in operands.keys():
res.append((i, i))
return res
def domain_get(self, cursor, user, model_name, context=None):
if context is None:
context = {}
# root user above constraint
if user == 0:
if not context.get('user'):
return '', []
ctx = context.copy()
del ctx['user']
return self.domain_get(cursor, context['user'], model_name,
context=ctx)
cursor.execute("SELECT r.id FROM ir_rule r " \
"JOIN (ir_rule_group g " \
"JOIN ir_model m ON (g.model = m.id)) " \
"ON (g.id = r.rule_group) " \
"WHERE m.model = %s "
"AND (g.id IN (" \
"SELECT rule_group_id FROM user_rule_group_rel " \
"WHERE user_id = %s " \
"UNION SELECT rule_group_id " \
"FROM group_rule_group_rel g_rel " \
"JOIN res_group_user_rel u_rel " \
"ON (g_rel.group_id = u_rel.gid) " \
"WHERE u_rel.uid = %s) " \
"OR default_p " \
"OR g.global_p)", (model_name, user, user))
ids = [x[0] for x in cursor.fetchall()]
if not ids:
return '', []
obj = self.pool.get(model_name)
clause = {}
clause_global = {}
operand2query = self._operand_get(cursor, user, 'res.user', level=1,
recur=['many2one'], root_tech='user', root='User')
# Use root user without context to prevent recursion
for rule in self.browse(cursor, 0, ids):
dom = eval("[('%s', '%s', %s)]" % \
(rule.field.name, rule.operator,
operand2query[rule.operand]),
{'user': self.pool.get('res.user').browse(cursor, 0,
user), 'time': time})
if rule.rule_group['global_p']:
clause_global.setdefault(rule.rule_group.id, ['OR'])
clause_global[rule.rule_group.id].append(dom)
else:
clause.setdefault(rule.rule_group.id, ['OR'])
clause[rule.rule_group.id].append(dom)
query = ''
val = []
# Test if there is no rule_group that have no rule
cursor.execute("""SELECT g.id FROM
ir_rule_group g
JOIN ir_model m ON (g.model = m.id)
WHERE m.model = %s
AND (g.id NOT IN (SELECT rule_group FROM ir_rule))
AND (g.id IN (SELECT rule_group_id FROM user_rule_group_rel
WHERE user_id = %s
UNION SELECT rule_group_id FROM group_rule_group_rel g_rel
JOIN res_group_user_rel u_rel
ON g_rel.group_id = u_rel.gid
WHERE u_rel.uid = %s))""", (model_name, user, user))
if cursor.rowcount:
group_id = cursor.fetchone()[0]
clause[group_id] = []
clause = clause.values()
clause.insert(0, 'OR')
clause_global = clause_global.values()
if clause_global:
clause_global.insert(0, 'AND')
clause = ['AND', clause_global, clause]
query, val, _, _ = obj.search_domain(cursor, user,
clause, active_test=False, context=context)
return query, val
domain_get = Cache('ir_rule.domain_get')(domain_get)
def delete(self, cursor, user, ids, context=None):
res = super(Rule, self).delete(cursor, user, ids, context=context)
# Restart the cache on the domain_get method of ir.rule
self.domain_get(cursor.dbname)
return res
def create(self, cursor, user, vals, context=None):
res = super(Rule, self).create(cursor, user, vals, context=context)
# Restart the cache on the domain_get method of ir.rule
self.domain_get(cursor.dbname)
return res
def write(self, cursor, user, ids, vals, context=None):
res = super(Rule, self).write(cursor, user, ids, vals,
context=context)
# Restart the cache on the domain_get method
self.domain_get(cursor.dbname)
return res
Rule()