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