[IMP] Rename menu Configuration
[odoo/odoo.git] / openerp / osv / osv.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 #.apidoc title: Objects Services (OSV)
23
24 from functools import wraps
25 import logging
26 from psycopg2 import IntegrityError, errorcodes
27
28 import orm
29 import openerp
30 import openerp.netsvc as netsvc
31 import openerp.pooler as pooler
32 import openerp.sql_db as sql_db
33 from openerp.tools.translate import translate
34 from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
35 import openerp.exceptions
36
37 _logger = logging.getLogger(__name__)
38
39 # Deprecated.
40 class except_osv(Exception):
41     def __init__(self, name, value):
42         self.name = name
43         self.value = value
44         self.args = (name, value)
45
46 service = None
47
48 class object_proxy(object):
49     def __init__(self):
50         global service
51         service = self
52
53     def check(f):
54         @wraps(f)
55         def wrapper(self, dbname, *args, **kwargs):
56             """ Wraps around OSV functions and normalises a few exceptions
57             """
58
59             def tr(src, ttype):
60                 # We try to do the same as the _(), but without the frame
61                 # inspection, since we aready are wrapping an osv function
62                 # trans_obj = self.get('ir.translation') cannot work yet :(
63                 ctx = {}
64                 if not kwargs:
65                     if args and isinstance(args[-1], dict):
66                         ctx = args[-1]
67                 elif isinstance(kwargs, dict):
68                     ctx = kwargs.get('context', {})
69
70                 uid = 1
71                 if args and isinstance(args[0], (long, int)):
72                     uid = args[0]
73
74                 lang = ctx and ctx.get('lang')
75                 if not (lang or hasattr(src, '__call__')):
76                     return src
77
78                 # We open a *new* cursor here, one reason is that failed SQL
79                 # queries (as in IntegrityError) will invalidate the current one.
80                 cr = False
81
82                 if hasattr(src, '__call__'):
83                     # callable. We need to find the right parameters to call
84                     # the  orm._sql_message(self, cr, uid, ids, context) function,
85                     # or we skip..
86                     # our signature is f(osv_pool, dbname [,uid, obj, method, args])
87                     try:
88                         if args and len(args) > 1:
89                             obj = self.get(args[1])
90                             if len(args) > 3 and isinstance(args[3], (long, int, list)):
91                                 ids = args[3]
92                             else:
93                                 ids = []
94                         cr = sql_db.db_connect(dbname).cursor()
95                         return src(obj, cr, uid, ids, context=(ctx or {}))
96                     except Exception:
97                         pass
98                     finally:
99                         if cr: cr.close()
100
101                     return False # so that the original SQL error will
102                                  # be returned, it is the best we have.
103
104                 try:
105                     cr = sql_db.db_connect(dbname).cursor()
106                     res = translate(cr, name=False, source_type=ttype,
107                                     lang=lang, source=src)
108                     if res:
109                         return res
110                     else:
111                         return src
112                 finally:
113                     if cr: cr.close()
114
115             def _(src):
116                 return tr(src, 'code')
117
118             try:
119                 if pooler.get_pool(dbname)._init:
120                     raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
121                 return f(self, dbname, *args, **kwargs)
122             except orm.except_orm, inst:
123                 raise except_osv(inst.name, inst.value)
124             except except_osv:
125                 raise
126             except IntegrityError, inst:
127                 osv_pool = pooler.get_pool(dbname)
128                 for key in osv_pool._sql_error.keys():
129                     if key in inst[0]:
130                         netsvc.abort_response(1, _('Constraint Error'), 'warning',
131                                         tr(osv_pool._sql_error[key], 'sql_constraint') or inst[0])
132                 if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
133                     msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
134                     _logger.debug("IntegrityError", exc_info=True)
135                     try:
136                         errortxt = inst.pgerror.replace('«','"').replace('»','"')
137                         if '"public".' in errortxt:
138                             context = errortxt.split('"public".')[1]
139                             model_name = table = context.split('"')[1]
140                         else:
141                             last_quote_end = errortxt.rfind('"')
142                             last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
143                             model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
144                         model = table.replace("_",".")
145                         model_obj = osv_pool.get(model)
146                         if model_obj:
147                             model_name = model_obj._description or model_obj._name
148                         msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
149                     except Exception:
150                         pass
151                     netsvc.abort_response(1, _('Integrity Error'), 'warning', msg)
152                 else:
153                     netsvc.abort_response(1, _('Integrity Error'), 'warning', inst[0])
154             except Exception:
155                 _logger.exception("Uncaught exception")
156                 raise
157
158         return wrapper
159
160     def execute_cr(self, cr, uid, obj, method, *args, **kw):
161         object = pooler.get_pool(cr.dbname).get(obj)
162         if not object:
163             raise except_osv('Object Error', 'Object %s doesn\'t exist' % str(obj))
164         return getattr(object, method)(cr, uid, *args, **kw)
165
166     def execute_kw(self, db, uid, obj, method, args, kw=None):
167         return self.execute(db, uid, obj, method, *args, **kw or {})
168
169     @check
170     def execute(self, db, uid, obj, method, *args, **kw):
171         cr = pooler.get_db(db).cursor()
172         try:
173             try:
174                 if method.startswith('_'):
175                     raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
176                 res = self.execute_cr(cr, uid, obj, method, *args, **kw)
177                 if res is None:
178                     _logger.warning('The method %s of the object %s can not return `None` !', method, obj)
179                 cr.commit()
180             except Exception:
181                 cr.rollback()
182                 raise
183         finally:
184             cr.close()
185         return res
186
187     def exec_workflow_cr(self, cr, uid, obj, method, *args):
188         wf_service = netsvc.LocalService("workflow")
189         return wf_service.trg_validate(uid, obj, args[0], method, cr)
190
191     @check
192     def exec_workflow(self, db, uid, obj, method, *args):
193         cr = pooler.get_db(db).cursor()
194         try:
195             try:
196                 res = self.exec_workflow_cr(cr, uid, obj, method, *args)
197                 cr.commit()
198             except Exception:
199                 cr.rollback()
200                 raise
201         finally:
202             cr.close()
203         return res
204
205 # deprecated - for backward compatibility.
206 osv = Model
207 osv_memory = TransientModel
208 osv_abstract = AbstractModel # ;-)
209
210
211 def start_object_proxy():
212     object_proxy()
213
214 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
215