[FIX] orm: fix autogenerated tree view for object having no columns (taking 'id'...
[odoo/odoo.git] / bin / wizard / __init__.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 import netsvc
23 from tools import copy
24 from tools.misc import UpdateableStr, UpdateableDict
25 from tools.translate import translate
26 from lxml import etree
27
28 import ir
29 import pooler
30
31 from osv.osv import except_osv
32 from osv.orm import except_orm
33 from netsvc import Logger, LOG_ERROR
34 import sys
35
36 class except_wizard(Exception):
37     def __init__(self, name, value):
38         self.name = name
39         self.value = value
40         self.args = (name, value)
41
42 class interface(netsvc.Service):
43     states = {}
44
45     def __init__(self, name):
46         assert not self.exists('wizard.'+name), 'The wizard "%s" already exists!' % (name,)
47         super(interface, self).__init__('wizard.'+name)
48         self.exportMethod(self.execute)
49         self.wiz_name = name
50
51     def translate_view(self, cr, node, state, lang):
52         if node.get('string'):
53             trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.get('string').encode('utf8'))
54             if trans:
55                 node.set('string', trans)
56         for n in node:
57             self.translate_view(cr, n, state, lang)
58
59     def execute_cr(self, cr, uid, data, state='init', context=None):
60         if not context:
61             context={}
62         res = {}
63         try:
64             state_def = self.states[state]
65
66             result_def = state_def.get('result', {})
67             
68             actions_res = {}
69             # iterate through the list of actions defined for this state
70             for action in state_def.get('actions', []):
71                 # execute them
72                 action_res = action(self, cr, uid, data, context)
73                 assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary'
74                 actions_res.update(action_res)
75                 
76             res = copy.copy(result_def)
77             res['datas'] = actions_res
78             
79             lang = context.get('lang', False)
80             if result_def['type'] == 'action':
81                 res['action'] = result_def['action'](self, cr, uid, data, context)
82             elif result_def['type'] == 'form':
83                 fields = copy.deepcopy(result_def['fields'])
84                 arch = copy.copy(result_def['arch'])
85                 button_list = copy.copy(result_def['state'])
86
87                 if isinstance(fields, UpdateableDict):
88                     fields = fields.dict
89                 if isinstance(arch, UpdateableStr):
90                     arch = arch.string
91
92                 # fetch user-set defaut values for the field... shouldn't we pass it the uid?
93                 defaults = ir.ir_get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)])
94                 default_values = dict([(x[1], x[2]) for x in defaults])
95                 for val in fields.keys():
96                     if 'default' in fields[val]:
97                         # execute default method for this field
98                         if callable(fields[val]['default']):
99                             fields[val]['value'] = fields[val]['default'](uid, data, state)
100                         else:
101                             fields[val]['value'] = fields[val]['default']
102                         del fields[val]['default']
103                     else:
104                         # if user has set a default value for the field, use it
105                         if val in default_values:
106                             fields[val]['value'] = default_values[val]
107                     if 'selection' in fields[val]:
108                         if not isinstance(fields[val]['selection'], (tuple, list)):
109                             fields[val] = copy.copy(fields[val])
110                             fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context)
111                         elif lang:
112                             res_name = "%s,%s,%s" % (self.wiz_name, state, val)
113                             trans = lambda x: translate(cr, res_name, 'selection', lang, x) or x
114                             for idx, (key, val2) in enumerate(fields[val]['selection']):
115                                 fields[val]['selection'][idx] = (key, trans(val2))
116
117                 if lang:
118                     # translate fields
119                     for field in fields:
120                         res_name = "%s,%s,%s" % (self.wiz_name, state, field)
121
122                         trans = translate(cr, res_name, 'wizard_field', lang)
123                         if trans:
124                             fields[field]['string'] = trans
125
126                         if 'help' in fields[field]:
127                             t = translate(cr, res_name, 'help', lang, fields[field]['help']) 
128                             if t:
129                                 fields[field]['help'] = t
130
131                     # translate arch
132                     if not isinstance(arch, UpdateableStr):
133                         doc = etree.XML(arch)
134                         self.translate_view(cr, doc, state, lang)
135                         arch = etree.tostring(doc)
136
137                     # translate buttons
138                     button_list = list(button_list)
139                     for i, aa  in enumerate(button_list):
140                         button_name = aa[0]
141                         trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang)
142                         if trans:
143                             aa = list(aa)
144                             aa[1] = trans
145                             button_list[i] = aa
146                     
147                 res['fields'] = fields
148                 res['arch'] = arch
149                 res['state'] = button_list
150
151             elif result_def['type'] == 'choice':
152                 next_state = result_def['next_state'](self, cr, uid, data, context)
153                 return self.execute_cr(cr, uid, data, next_state, context)
154         
155         except Exception, e:
156             if isinstance(e, except_wizard) \
157                 or isinstance(e, except_osv) \
158                 or isinstance(e, except_orm):
159                 self.abortResponse(2, e.name, 'warning', e.value)
160             else:
161                 import traceback
162                 tb_s = reduce(lambda x, y: x+y, traceback.format_exception(
163                     sys.exc_type, sys.exc_value, sys.exc_traceback))
164                 logger = Logger()
165                 logger.notifyChannel("web-services", LOG_ERROR,
166                         'Exception in call: ' + tb_s)
167                 raise
168
169         return res
170
171     def execute(self, db, uid, data, state='init', context=None):
172         if not context:
173             context={}
174         cr = pooler.get_db(db).cursor()
175         try:
176             try:
177                 res = self.execute_cr(cr, uid, data, state, context)
178                 cr.commit()
179             except Exception:
180                 cr.rollback()
181                 raise
182         finally:
183             cr.close()
184         return res
185
186 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
187