02b4fa6da6eb4bc2168d74fbff9e7a9a8ad0e95e
[odoo/odoo.git] / bin / addons / base / ir / ir_actions.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution    
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 from osv import fields,osv
24 import tools
25 import time
26 from tools.config import config
27 import netsvc
28 import re
29
30 class actions(osv.osv):
31     _name = 'ir.actions.actions'
32     _table = 'ir_actions'
33     _columns = {
34         'name': fields.char('Action Name', required=True, size=64),
35         'type': fields.char('Action Type', required=True, size=32),
36         'usage': fields.char('Action Usage', size=32),
37     }
38     _defaults = {
39         'usage': lambda *a: False,
40     }
41 actions()
42
43 class report_custom(osv.osv):
44     _name = 'ir.actions.report.custom'
45     _table = 'ir_act_report_custom'
46     _sequence = 'ir_actions_id_seq'
47     _columns = {
48         'name': fields.char('Report Name', size=64, required=True, translate=True),
49         'type': fields.char('Report Type', size=32, required=True),
50         'model':fields.char('Object', size=64, required=True),
51         'report_id': fields.integer('Report Ref.', required=True),
52         'usage': fields.char('Action Usage', size=32),
53         'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form views.")
54     }
55     _defaults = {
56         'multi': lambda *a: False,
57         'type': lambda *a: 'ir.actions.report.custom',
58     }
59 report_custom()
60
61 class report_xml(osv.osv):
62
63     def _report_content(self, cursor, user, ids, name, arg, context=None):
64         res = {}
65         for report in self.browse(cursor, user, ids, context=context):
66             data = report[name + '_data']
67             if not data and report[name[:-8]]:
68                 try:
69                     fp = tools.file_open(report[name[:-8]], mode='rb')
70                     data = fp.read()
71                 except:
72                     data = False
73             res[report.id] = data
74         return res
75
76     def _report_content_inv(self, cursor, user, id, name, value, arg, context=None):
77         self.write(cursor, user, id, {name+'_data': value}, context=context)
78
79     def _report_sxw(self, cursor, user, ids, name, arg, context=None):
80         res = {}
81         for report in self.browse(cursor, user, ids, context=context):
82             if report.report_rml:
83                 res[report.id] = report.report_rml.replace('.rml', '.sxw')
84             else:
85                 res[report.id] = False
86         return res
87
88     _name = 'ir.actions.report.xml'
89     _table = 'ir_act_report_xml'
90     _sequence = 'ir_actions_id_seq'
91     _columns = {
92         'name': fields.char('Name', size=64, required=True, translate=True),
93         'type': fields.char('Report Type', size=32, required=True),
94         'model': fields.char('Object', size=64, required=True),
95         'report_name': fields.char('Internal Name', size=64, required=True),
96         'report_xsl': fields.char('XSL path', size=256),
97         'report_xml': fields.char('XML path', size=256),
98         'report_rml': fields.char('RML path', size=256,
99             help="The .rml path of the file or NULL if the content is in report_rml_content"),
100         'report_sxw': fields.function(_report_sxw, method=True, type='char',
101             string='SXW path'),
102         'report_sxw_content_data': fields.binary('SXW content'),
103         'report_rml_content_data': fields.binary('RML content'),
104         'report_sxw_content': fields.function(_report_content,
105             fnct_inv=_report_content_inv, method=True,
106             type='binary', string='SXW content',),
107         'report_rml_content': fields.function(_report_content,
108             fnct_inv=_report_content_inv, method=True,
109             type='binary', string='RML content'),
110         'auto': fields.boolean('Automatic XSL:RML', required=True),
111         'usage': fields.char('Action Usage', size=32),
112         'header': fields.boolean('Add RML header',
113             help="Add or not the coporate RML header"),
114         'multi': fields.boolean('On multiple doc.',
115             help="If set to true, the action will not be displayed on the right toolbar of a form views."),
116         'report_type': fields.selection([
117             ('pdf', 'pdf'),
118             ('html', 'html'),
119             ('raw', 'raw'),
120             ('sxw', 'sxw'),
121             ('odt', 'odt'),
122             ], string='Type', required=True),
123         'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
124         'attachment': fields.char('Save As Attachment Prefix', size=128, help='This is the filename of the attachment to store the printing result. Keep empty to not save the printed reports. You can use python expression using the object and time variables.'),
125         'attachment_use': fields.boolean('Reload from Attachment', help='If you check this, the second time the user print with same attachment name, it returns the previour report.')
126     }
127     _defaults = {
128         'type': lambda *a: 'ir.actions.report.xml',
129         'multi': lambda *a: False,
130         'auto': lambda *a: True,
131         'header': lambda *a: True,
132         'report_sxw_content': lambda *a: False,
133         'report_type': lambda *a: 'pdf',
134         'attachment': lambda *a: False,
135     }
136
137 report_xml()
138
139 class act_window(osv.osv):
140     _name = 'ir.actions.act_window'
141     _table = 'ir_act_window'
142     _sequence = 'ir_actions_id_seq'
143
144 #    def search(self, cr, uid, args, offset=0, limit=2000, order=None,
145 #            context=None, count=False):
146 #        if context is None:
147 #            context = {}
148 #        ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order,
149 #                context=context)
150 #        if uid==1:
151 #            return ids
152 #        user_groups = self.pool.get('res.users').read(cr, uid, [uid])[0]['groups_id']
153 #        result = []
154 #        for act in self.browse(cr, uid, ids):
155 #            if not len(act.groups_id):
156 #                result.append(act.id)
157 #                continue
158 #            for g in act.groups_id:
159 #                if g.id in user_groups:
160 #                    result.append(act.id)
161 #                    break
162 #        return result
163
164     def _views_get_fnc(self, cr, uid, ids, name, arg, context={}):
165         res={}
166         for act in self.browse(cr, uid, ids):
167             res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids]
168             modes = act.view_mode.split(',')
169             if len(modes)>len(act.view_ids):
170                 find = False
171                 if act.view_id:
172                     res[act.id].append((act.view_id.id, act.view_id.type))
173                 for t in modes[len(act.view_ids):]:
174                     if act.view_id and (t == act.view_id.type) and not find:
175                         find = True
176                         continue
177                     res[act.id].append((False, t))
178         return res
179
180     _columns = {
181         'name': fields.char('Action Name', size=64, translate=True),
182         'type': fields.char('Action Type', size=32, required=True),
183         'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'),
184         'domain': fields.char('Domain Value', size=250),
185         'context': fields.char('Context Value', size=250),
186         'res_model': fields.char('Object', size=64),
187         'src_model': fields.char('Source Object', size=64),
188         'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'),
189         'view_type': fields.selection((('tree','Tree'),('form','Form')),string='Type of view'),
190         'view_mode': fields.char('Mode of view', size=250),
191         'usage': fields.char('Action Usage', size=32),
192         'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'),
193         'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'),
194         'limit': fields.integer('Limit', help='Default limit for the list view'),
195         'auto_refresh': fields.integer('Auto-Refresh',
196             help='Add an auto-refresh on the view'),
197         'groups_id': fields.many2many('res.groups', 'ir_act_window_group_rel',
198             'act_id', 'gid', 'Groups'),
199     }
200     _defaults = {
201         'type': lambda *a: 'ir.actions.act_window',
202         'view_type': lambda *a: 'form',
203         'view_mode': lambda *a: 'tree,form',
204         'context': lambda *a: '{}',
205         'limit': lambda *a: 80,
206         'target': lambda *a: 'current',
207         'auto_refresh': lambda *a: 0,
208     }
209 act_window()
210
211 class act_window_view(osv.osv):
212     _name = 'ir.actions.act_window.view'
213     _table = 'ir_act_window_view'
214     _rec_name = 'view_id'
215     _columns = {
216         'sequence': fields.integer('Sequence'),
217         'view_id': fields.many2one('ir.ui.view', 'View'),
218         'view_mode': fields.selection((
219             ('tree', 'Tree'),
220             ('form', 'Form'),
221             ('graph', 'Graph'),
222             ('calendar', 'Calendar'),
223             ('gantt', 'Gantt')), string='Type of view', required=True),
224         'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'),
225         'multi': fields.boolean('On multiple doc.',
226             help="If set to true, the action will not be displayed on the right toolbar of a form views."),
227     }
228     _defaults = {
229         'multi': lambda *a: False,
230     }
231     _order = 'sequence'
232 act_window_view()
233
234 class act_wizard(osv.osv):
235     _name = 'ir.actions.wizard'
236     _table = 'ir_act_wizard'
237     _sequence = 'ir_actions_id_seq'
238     _columns = {
239         'name': fields.char('Wizard info', size=64, required=True, translate=True),
240         'type': fields.char('Action type', size=32, required=True),
241         'wiz_name': fields.char('Wizard name', size=64, required=True),
242         'multi': fields.boolean('Action on multiple doc.', help="If set to true, the wizard will not be displayed on the right toolbar of a form views."),
243         'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups'),
244         'model': fields.char('Object', size=64),
245     }
246     _defaults = {
247         'type': lambda *a: 'ir.actions.wizard',
248         'multi': lambda *a: False,
249     }
250 act_wizard()
251
252 class act_url(osv.osv):
253     _name = 'ir.actions.url'
254     _table = 'ir_act_url'
255     _sequence = 'ir_actions_id_seq'
256     _columns = {
257         'name': fields.char('Action Name', size=64, translate=True),
258         'type': fields.char('Action Type', size=32, required=True),
259         'url': fields.text('Action Url',required=True),
260         'target': fields.selection((
261             ('new', 'New Window'),
262             ('self', 'This Window')),
263             'Action Target', required=True
264         )
265     }
266     _defaults = {
267         'type': lambda *a: 'ir.actions.act_url',
268         'target': lambda *a: 'new'
269     }
270 act_url()
271
272 def model_get(self, cr, uid, context={}):
273     wkf_pool = self.pool.get('workflow')
274     ids = wkf_pool.search(cr, uid, [])
275     osvs = wkf_pool.read(cr, uid, ids, ['osv'])
276
277     res = []
278     mpool = self.pool.get('ir.model')
279     for osv in osvs:
280         model = osv.get('osv')
281         id = mpool.search(cr, uid, [('model','=',model)])
282         name = mpool.read(cr, uid, id)[0]['name']
283         res.append((model, name))
284
285     return res
286
287 class ir_model_fields(osv.osv):
288     _inherit = 'ir.model.fields'
289     _rec_name = 'field_description'
290     _columns = {
291         'complete_name': fields.char('Complete Name', size=64, select=1),
292     }
293
294     def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=800):
295         def get_fields(cr, uid, field, rel):
296             result = []
297             mobj = self.pool.get('ir.model')
298             id = mobj.search(cr, uid, [('model','=',rel)])
299
300             obj = self.pool.get('ir.model.fields')
301             ids = obj.search(cr, uid, [('model_id','in',id)])
302             records = obj.read(cr, uid, ids)
303             for record in records:
304                 id = record['id']
305                 fld = field + '/' + record['name']
306
307                 result.append((id, fld))
308             return result
309
310         if not args:
311             args=[]
312         if not context:
313             context={}
314             return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
315
316         if context.get('key') != 'server_action':
317             return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
318
319         result = []
320         obj = self.pool.get('ir.model.fields')
321         ids = obj.search(cr, uid, args)
322         records = obj.read(cr, uid, ids)
323         for record in records:
324             id = record['id']
325             field = record['name']
326
327             if record['ttype'] == 'many2one':
328                 rel = record['relation']
329                 res = get_fields(cr, uid, field, record['relation'])
330                 for rs in res:
331                     result.append(rs)
332
333             result.append((id, field))
334
335         for rs in result:
336             obj.write(cr, uid, [rs[0]], {'complete_name':rs[1]})
337
338         iids = []
339         for rs in result:
340             iids.append(rs[0])
341
342         result = super(ir_model_fields, self).name_search(cr, uid, name, [('complete_name','ilike',name), ('id','in',iids)], operator, context, limit)
343
344         return result
345
346 ir_model_fields()
347
348 class server_object_lines(osv.osv):
349     _name = 'ir.server.object.lines'
350     _sequence = 'ir_actions_id_seq'
351     _columns = {
352         'server_id': fields.many2one('ir.actions.server', 'Object Mapping'),
353         'col1': fields.many2one('ir.model.fields', 'Destination', required=True),
354         'value': fields.text('Value', required=True),
355         'type': fields.selection([
356             ('value','Value'),
357             ('equation','Formula')
358         ], 'Type', required=True, size=32, change_default=True),
359     }
360     _defaults = {
361         'type': lambda *a: 'equation',
362     }
363 server_object_lines()
364
365 ##
366 # Actions that are run on the server side
367 #
368 class actions_server(osv.osv):
369
370     def _select_signals(self, cr, uid, context={}):
371         cr.execute("select distinct t.signal as key, t.signal || ' - [ ' || w.osv || ' ] ' as val from wkf w, wkf_activity a, wkf_transition t "\
372                         " where w.id = a.wkf_id " \
373                         " and t.act_from = a.wkf_id " \
374                         " or t.act_to = a.wkf_id and t.signal not in (null, NULL)")
375         result = cr.fetchall() or []
376         res = []
377         for rs in result:
378             if not rs[0] == None and not rs[1] == None:
379                 res.append(rs)
380         return res
381
382     _name = 'ir.actions.server'
383     _table = 'ir_act_server'
384     _sequence = 'ir_actions_id_seq'
385     _order = 'sequence'
386     _columns = {
387         'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name i.e. One Sales Order -> Many Invoice"),
388         'condition' : fields.char('Condition', size=256, required=True, help="Condition that is to be test before execute action,  i.e : object.list_price > object.cost_price"),
389         'state': fields.selection([
390             ('client_action','Client Action'),
391             ('dummy','Dummy'),
392             ('loop','Iteration'),
393             ('code','Python Code'),
394             ('trigger','Trigger'),
395             ('email','Email'),
396             ('sms','SMS'),
397             ('object_create','Create Object'),
398             ('object_write','Write Object'),
399             ('other','Multi Actions'),
400         ], 'Action Type', required=True, size=32, help="Type of the Action that is to be execute"),
401         'code':fields.text('Python Code', help="python code to be execute"),
402         'sequence': fields.integer('Sequence', help="Important when you deal with the multi action, the execution order will be decided based on this, low number higher priority"),
403         'model_id': fields.many2one('ir.model', 'Object', required=True, help="select the obect on which the action will work (read, write, create)"),
404         'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Ation Window, Report, Wizard to be execute"),
405         'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128, help="Select the Signal name that is to be "),
406         'wkf_model_id': fields.many2one('ir.model', 'Workflow on', help="Workflow to be execute on which model"),
407         'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On', help="select the object from the model on which the workflow will execute"),
408         'email': fields.char('Email Address', size=512, help="provides the fiels that will refer to the tiny to fetch the email address, i.e. you select the invoice, then `object.invoice_address_id.email` is the field which give the correct address"),
409         'subject': fields.char('Subject', size=1024, translate=True, help="Specify the subject, you can use the fields from the object. like `Hello [[ object.partner_id.name ]]`"),
410         'message': fields.text('Message', translate=True, help="Specify the Message, you can use the fields from the object. like `Dear [[ object.partner_id.name ]]`"),
411         'mobile': fields.char('Mobile No', size=512, help="provides the fiels that will refer to the tiny to fetch the mobile number, i.e. you select the invoice, then `object.invoice_address_id.mobile` is the field which give the correct mobile number"),
412         'sms': fields.char('SMS', size=160, translate=True),
413         'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Others Actions'),
414         'usage': fields.char('Action Usage', size=32),
415         'type': fields.char('Action Type', size=32, required=True),
416         'srcmodel_id': fields.many2one('ir.model', 'Model', help="In which object you want to create / write the object if its empty refer to the Object field"),
417         'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Fields Mapping'),
418         'record_id':fields.many2one('ir.model.fields', 'Create Id', help="Provide the field name from where the record id stores after the create operations, if its empty, you can not track the new record"),
419         'write_id':fields.char('Write Id', size=256, help="Provide the field name from where the record id refer for the write operation, if its empty it will refer to the active id of the object"),
420         'loop_action':fields.many2one('ir.actions.server', 'Loop Action', help="select the action, which will be executes. Loop action will not be avaliable inside loop"),
421         'expression':fields.char('Loop Expression', size=512, help="enter the field/expression that will return the list, i.e. select the sale order in Object, and we can have loop on sales order line. Expression = `object.order_line`"),
422     }
423     _defaults = {
424         'state': lambda *a: 'dummy',
425         'condition': lambda *a: 'True',
426         'type': lambda *a: 'ir.actions.server',
427         'sequence': lambda *a: 5,
428         'code': lambda *a: """# You can use the following variables
429 #    - object
430 #    - object2
431 #    - time
432 #    - cr
433 #    - uid
434 #    - ids
435 # If you plan to return an action, assign: action = {...}
436 """,
437     }
438
439     def get_email(self, cr, uid, action, context):
440         logger = netsvc.Logger()
441         obj_pool = self.pool.get(action.model_id.model)
442         id = context.get('active_id')
443         obj = obj_pool.browse(cr, uid, id)
444
445         fields = None
446
447         if '/' in action.email.complete_name:
448             fields = action.email.complete_name.split('/')
449         elif '.' in action.email.complete_name:
450             fields = action.email.complete_name.split('.')
451
452         for field in fields:
453             try:
454                 obj = getattr(obj, field)
455             except Exception,e :
456                 logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
457
458         return obj
459
460     def get_mobile(self, cr, uid, action, context):
461         logger = netsvc.Logger()
462         obj_pool = self.pool.get(action.model_id.model)
463         id = context.get('active_id')
464         obj = obj_pool.browse(cr, uid, id)
465
466         fields = None
467
468         if '/' in action.mobile.complete_name:
469             fields = action.mobile.complete_name.split('/')
470         elif '.' in action.mobile.complete_name:
471             fields = action.mobile.complete_name.split('.')
472
473         for field in fields:
474             try:
475                 obj = getattr(obj, field)
476             except Exception,e :
477                 logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
478
479         return obj
480
481     def merge_message(self, cr, uid, keystr, action, context):
482         logger = netsvc.Logger()
483         def merge(match):
484             obj_pool = self.pool.get(action.model_id.model)
485             id = context.get('active_id')
486             obj = obj_pool.browse(cr, uid, id)
487             exp = str(match.group()[2:-2]).strip()
488             result = eval(exp, {'object':obj, 'context': context,'time':time})
489             if result in (None, False):
490                 return str("--------")
491             return str(result)
492         
493         com = re.compile('(\[\[.+?\]\])')
494         message = com.sub(merge, keystr)
495         
496         return message
497
498     # Context should contains:
499     #   ids : original ids
500     #   id  : current id of the object
501     # OUT:
502     #   False : Finnished correctly
503     #   ACTION_ID : Action to launch
504     
505     def run(self, cr, uid, ids, context={}):
506         logger = netsvc.Logger()
507         
508         for action in self.browse(cr, uid, ids, context):
509             obj_pool = self.pool.get(action.model_id.model)
510             obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
511             cxt = {
512                 'context':context, 
513                 'object': obj, 
514                 'time':time,
515                 'cr': cr,
516                 'pool' : self.pool,
517                 'uid' : uid
518             }
519             expr = eval(str(action.condition), cxt)
520             if not expr:
521                 continue
522             
523             if action.state=='client_action':
524                 if not action.action_id:
525                     raise osv.except_osv(_('Error'), _("Please specify an action to launch !")) 
526                 result = self.pool.get(action.action_id.type).read(cr, uid, action.action_id.id, context=context)
527                 return result
528
529             if action.state=='python':
530                 localdict = {
531                     'self': self.pool.get(action.model_id.model),
532                     'context': context,
533                     'time': time,
534                     'ids': ids,
535                     'cr': cr,
536                     'uid': uid
537                 }
538                 exec action.code in localdict
539                 if 'action' in localdict:
540                     return localdict['action']
541
542             if action.state == 'email':
543                 user = config['email_from']
544                 address = str(action.email)
545                 try:
546                     address =  eval(str(action.email), cxt)
547                 except:
548                     pass
549                 
550                 if not address:
551                     raise osv.except_osv(_('Error'), _("Please specify the Partner Email address !"))
552                 if not user:
553                     raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
554                 
555                 subject = self.merge_message(cr, uid, str(action.subject), action, context)
556                 body = self.merge_message(cr, uid, str(action.message), action, context)
557                 
558                 if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
559                     logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
560                 else:
561                     logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
562
563             if action.state == 'trigger':
564                 wf_service = netsvc.LocalService("workflow")
565                 model = action.wkf_model_id.model
566                 obj_pool = self.pool.get(action.model_id.model)
567                 res_id = self.pool.get(action.model_id.model).read(cr, uid, [context.get('active_id')], [action.trigger_obj_id.name])
568                 id = res_id [0][action.trigger_obj_id.name]
569                 wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr)
570
571             if action.state == 'sms':
572                 #TODO: set the user and password from the system
573                 # for the sms gateway user / password
574                 api_id = ''
575                 text = action.sms
576                 to = self.get_mobile(cr, uid, action, context)
577                 #TODO: Apply message mearge with the field
578                 if tools.sms_send(user, password, api_id, text, to) == True:
579                     logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address))
580                 else:
581                     logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address))
582             
583             if action.state == 'other':
584                 res = []
585                 for act in action.child_ids:
586                     context['active_id'] = context['active_ids'][0]
587                     result = self.run(cr, uid, [act.id], context)
588                     if result:
589                         res.append(result)
590                     
591                 return res
592             
593             if action.state == 'loop':
594                 obj_pool = self.pool.get(action.model_id.model)
595                 obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
596                 cxt = {
597                     'context':context, 
598                     'object': obj, 
599                     'time':time,
600                     'cr': cr,
601                     'pool' : self.pool,
602                     'uid' : uid
603                 }
604                 expr = eval(str(action.expression), cxt)
605                 context['object'] = obj
606                 for i in expr:
607                     context['active_id'] = i.id
608                     result = self.run(cr, uid, [action.loop_action.id], context)
609             
610             if action.state == 'object_write':
611                 res = {}
612                 for exp in action.fields_lines:
613                     euq = exp.value
614                     if exp.type == 'equation':
615                         obj_pool = self.pool.get(action.model_id.model)
616                         obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
617                         cxt = {'context':context, 'object': obj, 'time':time}
618                         expr = eval(euq, cxt)
619                     else:
620                         expr = exp.value
621                     res[exp.col1.name] = expr
622
623                 if not action.write_id:
624                     if not action.srcmodel_id:
625                         obj_pool = self.pool.get(action.model_id.model)
626                         obj_pool.write(cr, uid, [context.get('active_id')], res)
627                     else:
628                         write_id = context.get('active_id')
629                         obj_pool = self.pool.get(action.srcmodel_id.model)
630                         obj_pool.write(cr, uid, [write_id], res)
631                         
632                 elif action.write_id:
633                     obj_pool = self.pool.get(action.srcmodel_id.model)
634                     rec = self.pool.get(action.model_id.model).browse(cr, uid, context.get('active_id'))
635                     id = eval(action.write_id, {'object': rec})
636                     try:
637                         id = int(id)
638                     except:
639                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
640                     
641                     if type(id) != type(1):
642                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
643                     write_id = id
644                     obj_pool.write(cr, uid, [write_id], res)
645
646             if action.state == 'object_create':
647                 res = {}
648                 for exp in action.fields_lines:
649                     euq = exp.value
650                     if exp.type == 'equation':
651                         obj_pool = self.pool.get(action.model_id.model)
652                         obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
653                         expr = eval(euq, {'context':context, 'object': obj, 'time':time})
654                     else:
655                         expr = exp.value
656                     res[exp.col1.name] = expr
657
658                 obj_pool = None
659                 res_id = False
660                 obj_pool = self.pool.get(action.srcmodel_id.model)
661                 res_id = obj_pool.create(cr, uid, res)
662                 cr.commit()
663                 if action.record_id:
664                     self.pool.get(action.model_id.model).write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id})
665
666         return False
667
668 actions_server()
669
670 class act_window_close(osv.osv):
671     _name = 'ir.actions.act_window_close'
672     _table = 'ir_actions'
673     _sequence = 'ir_actions_id_seq'
674     _columns = {
675         'name': fields.char('Action Name', size=64, translate=True),
676         'type': fields.char('Action Type', size=32, required=True),
677     }
678     _defaults = {
679         'type': lambda *a: 'ir.actions.act_window_close',
680     }
681 act_window_close()
682
683 # This model use to register action services.
684 # if action type is 'configure', it will be start on configuration wizard.
685 # if action type is 'service',
686 #                - if start_type= 'at once', it will be start at one time on start date
687 #                - if start_type='auto', it will be start on auto starting from start date, and stop on stop date
688 #                - if start_type="manual", it will start and stop on manually 
689 class ir_actions_todo(osv.osv):
690     _name = 'ir.actions.todo'    
691     _columns={
692         'name':fields.char('Name',size=64,required=True, select=True),
693         'note':fields.text('Text', translate=True),
694         'start_date': fields.datetime('Start Date'),
695         'end_date': fields.datetime('End Date'),
696         'action_id':fields.many2one('ir.actions.act_window', 'Action', select=True,required=True, ondelete='cascade'),
697         'sequence':fields.integer('Sequence'),
698         'active': fields.boolean('Active'),
699         'type':fields.selection([('configure', 'Configure'),('service', 'Service'),('other','Other')], string='Type', required=True),
700         'start_on':fields.selection([('at_once', 'At Once'),('auto', 'Auto'),('manual','Manual')], string='Start On'),
701         'groups_id': fields.many2many('res.groups', 'res_groups_act_todo_rel', 'act_todo_id', 'group_id', 'Groups'),
702         'users_id': fields.many2many('res.users', 'res_users_act_todo_rel', 'act_todo_id', 'user_id', 'Users'),
703         'state':fields.selection([('open', 'Not Started'),('done', 'Done'),('skip','Skipped'),('cancel','Cancel')], string='State', required=True)
704     }
705     _defaults={
706         'state': lambda *a: 'open',
707         'sequence': lambda *a: 10,
708         'active':lambda *a:True,
709         'type':lambda *a:'configure'
710     }
711     _order="sequence"
712 ir_actions_todo()
713
714 # This model to use run all configuration actions
715 class ir_actions_configuration_wizard(osv.osv_memory):
716     _name='ir.actions.configuration.wizard'
717     def next_configuration_action(self,cr,uid,context={}):
718         item_obj = self.pool.get('ir.actions.todo')
719         item_ids = item_obj.search(cr, uid, [('type','=','configure'),('state', '=', 'open'),('active','=',True)], limit=1, context=context)
720         if item_ids and len(item_ids):
721             item = item_obj.browse(cr, uid, item_ids[0], context=context)
722             return item
723         return False
724     def _get_action_name(self, cr, uid, context={}):
725         next_action=self.next_configuration_action(cr,uid,context=context)        
726         if next_action:
727             return next_action.note
728         else:
729             return "Your database is now fully configured.\n\nClick 'Continue' and enjoy your OpenERP experience..."
730         return False
731
732     def _get_action(self, cr, uid, context={}):
733         next_action=self.next_configuration_action(cr,uid,context=context)
734         if next_action:           
735             return next_action.id
736         return False
737
738     def _progress_get(self,cr,uid, context={}):
739         total = self.pool.get('ir.actions.todo').search_count(cr, uid, [], context)
740         todo = self.pool.get('ir.actions.todo').search_count(cr, uid, [('type','=','configure'),('active','=',True),('state','<>','open')], context)
741         return max(5.0,round(todo*100/total))
742
743     _columns = {
744         'name': fields.text('Next Wizard',readonly=True),
745         'progress': fields.float('Configuration Progress', readonly=True),
746         'item_id':fields.many2one('ir.actions.todo', 'Next Configuration Wizard',invisible=True, readonly=True),
747     }
748     _defaults={
749         'progress': _progress_get,
750         'item_id':_get_action,
751         'name':_get_action_name,
752     }
753     def button_next(self,cr,uid,ids,context=None):
754         user_action=self.pool.get('res.users').browse(cr,uid,uid)
755         act_obj=self.pool.get(user_action.menu_id.type)
756         action_ids=act_obj.search(cr,uid,[('name','=',user_action.menu_id.name)])
757         action_open=act_obj.browse(cr,uid,action_ids)[0]
758         if context.get('menu',False):
759             return{
760                 'view_type': action_open.view_type,
761                 'view_id':action_open.view_id and [action_open.view_id.id] or False,
762                 'res_model': action_open.res_model,
763                 'type': action_open.type,
764                 'domain':action_open.domain
765             }
766         return {'type':'ir.actions.act_window_close'}
767
768     def button_skip(self,cr,uid,ids,context=None):
769         item_obj = self.pool.get('ir.actions.todo')
770         item_id=self.read(cr,uid,ids)[0]['item_id']
771         if item_id:
772             item = item_obj.browse(cr, uid, item_id, context=context)
773             item_obj.write(cr, uid, item.id, {
774                 'state': 'skip',
775                 }, context=context)
776             return{
777                 'view_type': 'form',
778                 "view_mode": 'form',
779                 'res_model': 'ir.actions.configuration.wizard',
780                 'type': 'ir.actions.act_window',
781                 'target':'new',
782             }
783         return self.button_next(cr, uid, ids, context)
784
785     def button_continue(self, cr, uid, ids, context=None):
786         item_obj = self.pool.get('ir.actions.todo')
787         item_id=self.read(cr,uid,ids)[0]['item_id']
788         if item_id:
789             item = item_obj.browse(cr, uid, item_id, context=context)
790             item_obj.write(cr, uid, item.id, {
791                 'state': 'done',
792                 }, context=context)
793             return{
794                   'view_mode': item.action_id.view_mode,
795                   'view_type': item.action_id.view_type,
796                   'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False,
797                   'res_model': item.action_id.res_model,
798                   'type': item.action_id.type,
799                   'target':item.action_id.target,
800             }
801         return self.button_next(cr, uid, ids, context)
802 ir_actions_configuration_wizard()
803
804 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
805