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