diff --git a/trytond/module.py b/trytond/module.py index fcdf5e09ce..9cf1439493 100644 --- a/trytond/module.py +++ b/trytond/module.py @@ -53,7 +53,8 @@ class Node(Singleton): super(Node, self).__init__() self.name = name self.graph = graph - self.datas = None + if not hasattr(self, 'datas'): + self.datas = None if not hasattr(self, 'childs'): self.childs = [] if not hasattr(self, 'depth'): @@ -232,6 +233,7 @@ def register_classes(): module_list = os.listdir(ADDONS_PATH) module_list.append('ir') module_list.append('workflow') + module_list.append('res') import ir import workflow import res diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index 3498c05737..6689240a6c 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -48,8 +48,6 @@ browse_null = BrowseNull class BrowseRecordList(list): def __init__(self, lst, context=None): - if not context: - context = {} super(BrowseRecordList, self).__init__(lst) self.context = context @@ -64,8 +62,6 @@ class BrowseRecord(object): table : the object (inherited from orm) context : a dictionnary with an optionnal context ''' - if context is None: - context = {} assert object_id, 'Wrong ID for the browse record, got ' + \ str(object_id) + ', expected an integer.' self._list_class = list_class or BrowseRecordList @@ -698,8 +694,6 @@ class ORM(object): self._inherits_reload_src() def browse(self, cursor, user, select, context=None, list_class=None): - if context is None: - context = {} list_class = list_class or BrowseRecordList cache = {} # need to accepts ints and longs because ids coming from a method @@ -749,8 +743,6 @@ class ORM(object): return [data] + lines def export_data(self, cursor, user, ids, fields_names, context=None): - if context is None: - context = {} fields_names = [x.split('/') for x in fields_names] datas = [] for row in self.browse(cursor, user, ids, context): @@ -954,8 +946,6 @@ class ORM(object): def read(self, cursor, user, ids, fields_names=None, context=None, load='_classic_read'): - if context is None: - context = {} self.pool.get('ir.model.access').check(cursor, user, self._name, 'read') if not fields_names: fields_names = self._columns.keys() + self._inherit_fields.keys() @@ -1081,8 +1071,6 @@ class ORM(object): ('\n'.join(field_err_str), ','.join(field_error))) def default_get(self, cursor, user, fields_names, context=None): - if context is None: - context = {} value = {} # get the default values for the inherited fields for i in self._inherits.keys(): @@ -1336,8 +1324,6 @@ class ORM(object): user = user id vals = dictionary of the form {'field_name': field_value, ...} """ - if context is None: - context = {} self.pool.get('ir.model.access').check(cursor, user, self._name, 'create') @@ -1431,8 +1417,6 @@ class ORM(object): return id_new def _update_function_stored(self, cursor, user, ids, context=None): - if context is None: - context = {} ffields = [x for x in self._columns if isinstance(self._columns[x], fields.function) and self._columns[x].store] if ffields: @@ -1634,8 +1618,6 @@ class ORM(object): return fields_attrs def __view_look_dom_arch(self, cursor, user, node, context=None): - if context is None: - context = {} fields_def = self.__view_look_dom(cursor, user, node, context=context) arch = node.toxml(encoding="utf-8").replace('\t', '') fields2 = self.fields_get(cursor, user, fields_def.keys(), context) @@ -1645,8 +1627,6 @@ class ORM(object): def fields_view_get(self, cursor, user, view_id=None, view_type='form', context=None, toolbar=False): - if context is None: - context = {} def _inherit_apply(src, inherit): @@ -2091,8 +2071,6 @@ class ORM(object): return (qu1, qu2, tables) def search_count(self, cursor, user, args, context=None): - if context is None: - context = {} res = self.search(cursor, user, args, context=context, count=True) if isinstance(res, list): return len(res) @@ -2100,8 +2078,6 @@ class ORM(object): def search(self, cursor, user, args, offset=0, limit=None, order=None, context=None, count=False): - if context is None: - context = {} # compute the where, order by, limit and offset clauses (qu1, qu2, tables) = self._where_calc(cursor, user, args, context=context) @@ -2136,8 +2112,6 @@ class ORM(object): return [x[0] for x in res] def name_get(self, cursor, user, ids, context=None): - if context is None: - context = {} if not ids: return [] if isinstance(ids, (int, long)): @@ -2149,8 +2123,6 @@ class ORM(object): context=None, limit=80): if args is None: args = [] - if context is None: - context = {} args = args[:] if name: args += [(self._rec_name, operator, name)] @@ -2159,8 +2131,6 @@ class ORM(object): return res def copy(self, cursor, user, object_id, default=None, context=None): - if context is None: - context = {} if default is None: default = {} if 'state' not in default: @@ -2209,8 +2179,6 @@ class ORM(object): def read_string(self, cursor, user, object_id, langs, fields_names=None, context=None): - if context is None: - context = {} res = {} res2 = {} self.pool.get('ir.model.access').check(cursor, user, 'ir.translation', @@ -2240,8 +2208,6 @@ class ORM(object): return res def write_string(self, cursor, user, object_id, langs, vals, context=None): - if context is None: - context = {} self.pool.get('ir.model.access').check(cursor, user, 'ir.translation', 'write') for lang in langs: diff --git a/trytond/res/__init__.py b/trytond/res/__init__.py index e69de29bb2..4deda13a9b 100644 --- a/trytond/res/__init__.py +++ b/trytond/res/__init__.py @@ -0,0 +1,3 @@ +from user import * +from lang import * +from request import * diff --git a/trytond/res/__terp__.py b/trytond/res/__terp__.py new file mode 100644 index 0000000000..59ba355cb7 --- /dev/null +++ b/trytond/res/__terp__.py @@ -0,0 +1,5 @@ +{ + 'name': 'Res', + 'active': True, + 'depends': ['ir'], +} diff --git a/trytond/res/lang.py b/trytond/res/lang.py new file mode 100644 index 0000000000..03edd37d04 --- /dev/null +++ b/trytond/res/lang.py @@ -0,0 +1,25 @@ +"Lang" +from trytond.osv import fields, OSV + + +class Lang(OSV): + "Language" + _name = "res.lang" + _description = __doc__ + _columns = { + 'name': fields.Char('Name', size=64, required=True), + 'code': fields.Char('Code', size=5, required=True), + 'translatable': fields.Boolean('Translatable'), + 'active': fields.Boolean('Active'), + 'direction': fields.Selection([ + ('ltr', 'Left-to-right'), + ('rtl', 'Right-to-left'), + ], 'Direction',required=True), + } + _defaults = { + 'active': lambda *a: 1, + 'translatable': lambda *a: 0, + 'direction': lambda *a: 'ltr', + } + +Lang() diff --git a/trytond/res/request.py b/trytond/res/request.py new file mode 100644 index 0000000000..7de3bce35b --- /dev/null +++ b/trytond/res/request.py @@ -0,0 +1,177 @@ +"Request" +from trytond.osv import OSV, fields +import time + +class Request(OSV): + "Request" + _name = 'res.request' + _table = 'res_request' + _description = __doc__ + + def _links_get(self, cursor, user, context=None): + request_link_obj = self.pool.get('res.request.link') + ids = request_link_obj.search(cursor, user, []) + request_links = request_link_obj.browse(cursor, user, ids, + context=context) + return [(x.object, x.name) for x in request_links] + + _columns = { + 'create_date': fields.DateTime('Created date', readonly=True), + 'name': fields.Char('Subject', states={ + 'waiting': [('readonly', True)], + 'active': [('readonly', True)], + 'closed': [('readonly', True)], + }, required=True, size=128), + 'active': fields.Boolean('Active'), + 'priority': fields.Selection([ + ('0', 'Low'), + ('1', 'Normal'), + ('2', 'High'), + ], 'Priority', states={ + 'waiting': [('readonly', True)], + 'closed': [('readonly', True)], + }, required=True), + 'act_from': fields.Many2One('res.users', 'From', required=True, + readonly=True, states={ + 'closed': [('readonly', True)], + }), + 'act_to': fields.Many2One('res.users', 'To', required=True, + states={ + 'waiting': [('readonly', True)], + 'closed': [('readonly', True)], + }), + 'body': fields.Text('Request', states={ + 'waiting': [('readonly', True)], + 'closed': [('readonly', True)], + }), + 'date_sent': fields.DateTime('Date', readonly=True), + 'trigger_date': fields.DateTime('Trigger Date', states={ + 'waiting': [('readonly', True)], + 'closed': [('readonly', True)], + }), +# 'ref_partner_id': fields.Many2One('res.partner', 'Partner Ref.', +# states={ +# 'closed': [('readonly', True)], +# }), + #TODO: use one2many instead limit number of reference + 'ref_doc1': fields.Reference('Document Ref 1', selection=_links_get, + size=128, states={ + 'closed': [('readonly', True)], + }), + 'ref_doc2': fields.Reference('Document Ref 2', selection=_links_get, + size=128, states={ + 'closed': [('readonly', True)], + }), + 'state': fields.Selection([ + ('draft', 'draft'), + ('waiting', 'waiting'), + ('active', 'active'), + ('closed', 'closed'), + ], 'State', required=True, readonly=True), + 'history': fields.One2Many('res.request.history', 'req_id', 'History'), + } + _defaults = { + 'act_from': lambda obj, cursor, user, context: user, + 'state': lambda *a: 'draft', + 'active': lambda *a: True, + 'priority': lambda *a: '1', + } + _order = 'priority DESC, trigger_date, create_date DESC' + + def request_send(self, cursor, user, ids, context=None): + #TODO use write and browse instead of SQL query + request_history_obj = self.pool.get('res.request.history') + for request_id in ids: + cursor.execute('UPDATE res_request ' \ + 'SET state = %s, date_sent = %s ' \ + 'WHERE id = %d', + ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), request_id)) + cursor.execute('SELECT act_from, act_to, body, date_sent ' \ + 'FROM res_request ' \ + 'WHERE id = %d', (request_id,)) + values = cursor.dictfetchone() + if len(values['body']) > 128: + values['name'] = values['body'][:125] + '...' + else: + values['name'] = values['body'] or '/' + values['req_id'] = id + request_history_obj.create(cursor, user, values) + return True + + def request_reply(self, cursor, user, ids, context=None): + for request_id in ids: + cursor.execute('UPDATE res_request ' \ + 'set state = \'active\', act_from = %d, ' \ + 'act_to = act_from, trigger_date = NULL, body = \'\' ' \ + 'WHERE id = %d', (user, request_id)) + return True + + def request_close(self, cursor, user, ids, context=None): + self.write(cursor, user, ids, {'state': 'closed', 'active': False}) + return True + + def request_get(self, cursor, user): + cursor.execute('SELECT id FROM res_request ' \ + 'WHERE act_to = %d ' \ + 'AND (trigger_date <= %s OR trigger_date IS NULL) ' \ + 'AND active = True', (user, time.strftime('%Y-%m-%d'))) + ids = [x[0] for x in cursor.fetchall()] + cursor.execute('SELECT id FROM res_request ' \ + 'WHERE act_from = %d AND (act_to <> %d) ' \ + 'AND (trigger_date <= %s OR trigger_date IS NULL) ' \ + 'AND active = True', + (user, user, time.strftime('%Y-%m-%d'))) + ids2 = [x[0] for x in cursor.fetchall()] + return (ids, ids2) + +Request() + + +class RequestLink(OSV): + "Request link" + _name = 'res.request.link' + _description = __doc__ + _columns = { + 'name': fields.Char('Name', size=64, required=True, translate=True), + 'object': fields.Char('Object', size=64, required=True), + 'priority': fields.Integer('Priority'), + } + _defaults = { + 'priority': lambda *a: 5, + } + _order = 'priority' + +RequestLink() + + +class RequestHistory(OSV): + "Request history" + _name = 'res.request.history' + _description = __doc__ + _columns = { + 'name': fields.Char('Summary', size=128, states={ + 'active': [('readonly', True)], + 'waiting': [('readonly', True)], + }, required=True), + 'req_id': fields.Many2One('res.request', 'Request', required=True, + ondelete='cascade', select=True), + 'act_from': fields.Many2One('res.users', 'From', required=True, + readonly=True), + 'act_to': fields.Many2One('res.users', 'To', required=True, states={ + 'waiting': [('readonly', True)], + }), + 'body': fields.Text('Body', states={ + 'waiting': [('readonly', True)], + }), + 'date_sent': fields.DateTime('Date sent', states={ + 'waiting': [('readonly', True)], + }, required=True) + } + _defaults = { + 'name': lambda *a: 'NoName', + 'act_from': lambda obj, cursor, user, context: user, + 'act_to': lambda obj, cursor, user, context: user, + 'date_sent': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + +RequestHistory() diff --git a/trytond/res/user.py b/trytond/res/user.py new file mode 100644 index 0000000000..7d48ce7f98 --- /dev/null +++ b/trytond/res/user.py @@ -0,0 +1,146 @@ +"User" +from trytond.osv import fields, OSV, ExceptOSV +#from trytond.tools import Cache + + +class Groups(OSV): + "Groups" + _name = "res.groups" + _description = __doc__ + _columns = { + 'name': fields.Char('Group Name', size=64, required=True), + 'model_access': fields.One2Many('ir.model.access', 'group_id', + 'Access Controls'), + 'rule_groups': fields.Many2Many('ir.rule.group', 'group_rule_group_rel', + 'group_id', 'rule_group_id', 'Rules', + domain="[('global', '<>', True)]"), + 'menu_access': fields.Many2Many('ir.ui.menu', 'ir_ui_menu_group_rel', + 'gid', 'menu_id', 'Access Menu'), + } + _sql_constraints = [ + ('name_uniq', 'unique (name)', 'The name of the group must be unique!') + ] + + def write(self, cursor, user, ids, vals, context=None): + if 'name' in vals: + if vals['name'].startswith('-'): + raise ExceptOSV('Error', + 'The name of the group can not start with "-"') + res = super(Groups, self).write(cursor, user, ids, vals, + context=context) + # Restart the cache on the company_get method + self.pool.get('ir.rule').domain_get() + return res + +Groups() + + +class Roles(OSV): + "Roles" + _name = "res.roles" + _description = __doc__ + _columns = { + 'name': fields.Char('Role Name', size=64, required=True), + 'parent_id': fields.Many2One('res.roles', 'Parent', select=True), + 'child_id': fields.One2Many('res.roles', 'parent_id', 'Childs') + } + + def check_recursion(self, cursor, user, ids, parent=None): + "Check for recursion" + return super(Roles, self).check_recursion(cursor, user, ids, + parent=parent) + + _constraints = [ + (check_recursion, 'Error! You can not create recursive roles.', + ['parent_id']) + ] + +Roles() + + +class Users(OSV): + "Users" + _name = "res.users" + _log_access = False + _description = __doc__ + _columns = { + 'name': fields.Char('Name', size=64, required=True, select=True), + 'login': fields.Char('Login', size=64, required=True), + 'password': fields.Char('Password', size=64, invisible=True), + 'signature': fields.Text('Signature', size=64), + #'address_id': fields.Many2One('res.partner.address', 'Address'), + 'active': fields.Boolean('Active'), + 'action_id': fields.Many2One('ir.actions.actions', 'Home Action'), + 'menu_id': fields.Many2One('ir.actions.actions', 'Menu Action'), + 'groups_id': fields.Many2Many('res.groups', 'res_groups_users_rel', + 'user', 'gid', 'Groups'), + 'roles_id': fields.Many2Many('res.roles', 'res_roles_users_rel', + 'user', 'rid', 'Roles'), + #'company_id': fields.Many2One('res.company', 'Company'), + 'rule_groups': fields.Many2Many('ir.rule.group', 'user_rule_group_rel', + 'user_id', 'rule_group_id', 'Rules', + domain="[('global', '<>', True)]"), + } + _sql_constraints = [ + ('login_key', 'UNIQUE (login)', + 'You can not have two users with the same login!') + ] + _defaults = { + 'password' : lambda *a: '', + 'active' : lambda *a: 1, + } +# def company_get(self, cursor, user, uid2): +# company_id = self.pool.get('res.users').browse(cursor, user, +# user).company_id.id +# return company_id +# company_get = Cache()(company_get) + + def write(self, cursor, user, ids, vals, context=None): + res = super(Users, self).write(cursor, user, ids, vals, context=context) +# self.company_get() + # Restart the cache on the company_get method + self.pool.get('ir.rule').domain_get() + return res + + def unlink(self, cursor, user, ids, context=None): + if 1 in ids: + raise ExceptOSV('UserError', + 'You can not remove the root user\n' \ + 'as it is used internally for resources\n' \ + 'created by Tiny ERP ' \ + '(updates, module installation, ...)') + return super(Users, self).unlink(cursor, user, ids, context=context) + + def name_search(self, cursor, user, name='', args=None, operator='ilike', + context=None, limit=80): + if args is None: + args = [] + ids = [] + if name: + ids = self.search(cursor, user, [('login', '=', name)] + args, + limit=limit, context=context) + if not ids: + ids = self.search(cursor, user, [('name', operator, name)] + args, + limit=limit, context=context) + return self.name_get(cursor, user, ids, context=context) + + def copy(self, cursor, user, obj_id, default=None, context=None): + if default is None: + default = {} + login = self.read(cursor, user, obj_id, ['login'])['login'] + default.update({'login': login+' (copy)'}) + return super(Users, self).copy(cursor, user, obj_id, default, + context=context) +Users() + + +class Groups2(OSV): + "Groups" + _inherit = 'res.groups' + _description = __doc__ + _columns = { + 'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', + 'user', 'Users'), + } + +Groups2()