[IMP] add new handler for addons configuration, corresponding menu item
[odoo/odoo.git] / bin / addons / base / ir / ir_actions.py
index a544048..f8c6dfa 100644 (file)
@@ -1,21 +1,20 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 ##############################################################################
 #
-#    OpenERP, Open Source Management Solution    
-#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
-#    $Id$
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
 #
 #    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation, either version 3 of the License, or
-#    (at your option) any later version.
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
 #
 #    This program is distributed in the hope that it will be useful,
 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
+#    GNU Affero General Public License for more details.
 #
-#    You should have received a copy of the GNU General Public License
+#    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
@@ -24,8 +23,12 @@ from osv import fields,osv
 import tools
 import time
 from tools.config import config
+from tools.translate import _
 import netsvc
 import re
+import copy
+import sys
+from xml import dom
 
 class actions(osv.osv):
     _name = 'ir.actions.actions'
@@ -50,7 +53,7 @@ class report_custom(osv.osv):
         'model':fields.char('Object', size=64, required=True),
         'report_id': fields.integer('Report Ref.', required=True),
         'usage': fields.char('Action Usage', size=32),
-        '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.")
+        'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view.")
     }
     _defaults = {
         'multi': lambda *a: False,
@@ -112,17 +115,20 @@ class report_xml(osv.osv):
         'header': fields.boolean('Add RML header',
             help="Add or not the coporate RML header"),
         '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."),
+            help="If set to true, the action will not be displayed on the right toolbar of a form view."),
         'report_type': fields.selection([
             ('pdf', 'pdf'),
             ('html', 'html'),
             ('raw', 'raw'),
             ('sxw', 'sxw'),
+            ('txt', 'txt'),
             ('odt', 'odt'),
+            ('html2html','HTML from HTML'),
+            ('mako2html','HTML from HTML(Mako)'),
             ], string='Type', required=True),
         'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
-        '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.'),
-        '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.')
+        'attachment': fields.char('Save As Attachment Prefix', size=128, help='This is the filename of the attachment used to store the printing result. Keep empty to not save the printed reports. You can use a python expression with the object and time variables.'),
+        'attachment_use': fields.boolean('Reload from Attachment', help='If you check this, then the second time the user prints with same attachment name, it returns the previous report.')
     }
     _defaults = {
         'type': lambda *a: 'ir.actions.report.xml',
@@ -141,25 +147,26 @@ class act_window(osv.osv):
     _table = 'ir_act_window'
     _sequence = 'ir_actions_id_seq'
 
-#    def search(self, cr, uid, args, offset=0, limit=2000, order=None,
-#            context=None, count=False):
-#        if context is None:
-#            context = {}
-#        ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order,
-#                context=context)
-#        if uid==1:
-#            return ids
-#        user_groups = self.pool.get('res.users').read(cr, uid, [uid])[0]['groups_id']
-#        result = []
-#        for act in self.browse(cr, uid, ids):
-#            if not len(act.groups_id):
-#                result.append(act.id)
-#                continue
-#            for g in act.groups_id:
-#                if g.id in user_groups:
-#                    result.append(act.id)
-#                    break
-#        return result
+    def _check_model(self, cr, uid, ids, context={}):
+        for action in self.browse(cr, uid, ids, context):
+            if not self.pool.get(action.res_model):
+                return False
+            if action.src_model and not self.pool.get(action.src_model):
+                return False
+        return True
+    _constraints = [
+        (_check_model, 'Invalid model name in the action definition.', ['res_model','src_model'])
+    ]
+
+    def get_filters(self, cr, uid, model):
+        cr.execute('select id from ir_act_window a where a.id not in (select act_id from ir_act_window_user_rel) and a.res_model=\''+model+'\' and a.filter=\'1\';')
+        all_ids = cr.fetchall()
+        filter_ids =  map(lambda x:x[0],all_ids)
+        act_ids = self.search(cr,uid,[('res_model','=',model),('filter','=',1),('default_user_ids','in',(','.join(map(str,[uid,])),))])
+        act_ids += filter_ids
+        act_ids = list(set(act_ids))
+        my_acts = self.read(cr, uid, act_ids, ['name', 'domain'])
+        return my_acts
 
     def _views_get_fnc(self, cr, uid, ids, name, arg, context={}):
         res={}
@@ -177,6 +184,46 @@ class act_window(osv.osv):
                     res[act.id].append((False, t))
         return res
 
+    def _search_view(self, cr, uid, ids, name, arg, context={}):
+        res = {}
+        def encode(s):
+            if isinstance(s, unicode):
+                return s.encode('utf8')
+            return s
+        for act in self.browse(cr, uid, ids):
+            fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid)
+            if act.search_view_id:
+                field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, act.search_view_id.id, 'search', context)
+                fields_from_fields_get.update(field_get['fields'])
+                field_get['fields'] = fields_from_fields_get
+                res[act.id] = str(field_get)
+            else:
+                def process_child(node, new_node, doc):
+                    for child in node.childNodes:
+                        if child.localName=='field' and child.hasAttribute('select') and child.getAttribute('select')=='1':
+                            if child.childNodes:
+                                fld = doc.createElement('field')
+                                for attr in child.attributes.keys():
+                                    fld.setAttribute(attr, child.getAttribute(attr))
+                                new_node.appendChild(fld)
+                            else:
+                                new_node.appendChild(child)
+                        elif child.localName in ('page','group','notebook'):
+                            process_child(child, new_node, doc)
+
+                form_arch = self.pool.get(act.res_model).fields_view_get(cr, uid, False, 'form', context)
+                dom_arc = dom.minidom.parseString(encode(form_arch['arch']))
+                new_node = copy.deepcopy(dom_arc)
+                for child_node in new_node.childNodes[0].childNodes:
+                    if child_node.nodeType == child_node.ELEMENT_NODE:
+                        new_node.childNodes[0].removeChild(child_node)
+                process_child(dom_arc.childNodes[0],new_node.childNodes[0],dom_arc)
+
+                form_arch['arch'] = new_node.toxml()
+                form_arch['fields'].update(fields_from_fields_get)
+                res[act.id] = str(form_arch)
+        return res
+
     _columns = {
         'name': fields.char('Action Name', size=64, translate=True),
         'type': fields.char('Action Type', size=32, required=True),
@@ -186,8 +233,8 @@ class act_window(osv.osv):
         'res_model': fields.char('Object', size=64),
         'src_model': fields.char('Source Object', size=64),
         'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'),
-        'view_type': fields.selection((('tree','Tree'),('form','Form')),string='Type of view'),
-        'view_mode': fields.char('Mode of view', size=250),
+        'view_type': fields.selection((('tree','Tree'),('form','Form')),string='View Type'),
+        'view_mode': fields.char('View Mode', size=250),
         'usage': fields.char('Action Usage', size=32),
         'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'),
         'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'),
@@ -196,6 +243,11 @@ class act_window(osv.osv):
             help='Add an auto-refresh on the view'),
         'groups_id': fields.many2many('res.groups', 'ir_act_window_group_rel',
             'act_id', 'gid', 'Groups'),
+        'search_view_id': fields.many2one('ir.ui.view', 'Search View Ref.'),
+        'filter': fields.boolean('Filter'),
+        'default_user_ids': fields.many2many('res.users', 'ir_act_window_user_rel', 'act_id', 'uid', 'Users'),
+        'search_view' : fields.function(_search_view, type='text', method=True, string='Search View'),
+        'menus': fields.char('Menus', size=4096)
     }
     _defaults = {
         'type': lambda *a: 'ir.actions.act_window',
@@ -220,10 +272,10 @@ class act_window_view(osv.osv):
             ('form', 'Form'),
             ('graph', 'Graph'),
             ('calendar', 'Calendar'),
-            ('gantt', 'Gantt')), string='Type of view', required=True),
+            ('gantt', 'Gantt')), string='View Type', required=True),
         'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'),
-        '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."),
+        'multi': fields.boolean('On Multiple Doc.',
+            help="If set to true, the action will not be displayed on the right toolbar of a form view."),
     }
     _defaults = {
         'multi': lambda *a: False,
@@ -237,10 +289,10 @@ class act_wizard(osv.osv):
     _table = 'ir_act_wizard'
     _sequence = 'ir_actions_id_seq'
     _columns = {
-        'name': fields.char('Wizard info', size=64, required=True, translate=True),
-        'type': fields.char('Action type', size=32, required=True),
-        'wiz_name': fields.char('Wizard name', size=64, required=True),
-        '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."),
+        'name': fields.char('Wizard Info', size=64, required=True, translate=True),
+        'type': fields.char('Action Type', size=32, required=True),
+        'wiz_name': fields.char('Wizard Name', size=64, required=True),
+        '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 view."),
         'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups'),
         'model': fields.char('Object', size=64),
     }
@@ -257,7 +309,7 @@ class act_url(osv.osv):
     _columns = {
         'name': fields.char('Action Name', size=64, translate=True),
         'type': fields.char('Action Type', size=32, required=True),
-        'url': fields.text('Action Url',required=True),
+        'url': fields.text('Action URL',required=True),
         'target': fields.selection((
             ('new', 'New Window'),
             ('self', 'This Window')),
@@ -291,59 +343,6 @@ class ir_model_fields(osv.osv):
     _columns = {
         'complete_name': fields.char('Complete Name', size=64, select=1),
     }
-
-    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=800):
-        def get_fields(cr, uid, field, rel):
-            result = []
-            mobj = self.pool.get('ir.model')
-            id = mobj.search(cr, uid, [('model','=',rel)])
-
-            obj = self.pool.get('ir.model.fields')
-            ids = obj.search(cr, uid, [('model_id','in',id)])
-            records = obj.read(cr, uid, ids)
-            for record in records:
-                id = record['id']
-                fld = field + '/' + record['name']
-
-                result.append((id, fld))
-            return result
-
-        if not args:
-            args=[]
-        if not context:
-            context={}
-            return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
-
-        if context.get('key') != 'server_action':
-            return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
-
-        result = []
-        obj = self.pool.get('ir.model.fields')
-        ids = obj.search(cr, uid, args)
-        records = obj.read(cr, uid, ids)
-        for record in records:
-            id = record['id']
-            field = record['name']
-
-            if record['ttype'] == 'many2one':
-                rel = record['relation']
-                res = get_fields(cr, uid, field, record['relation'])
-                for rs in res:
-                    result.append(rs)
-
-            result.append((id, field))
-
-        for rs in result:
-            obj.write(cr, uid, [rs[0]], {'complete_name':rs[1]})
-
-        iids = []
-        for rs in result:
-            iids.append(rs[0])
-
-        result = super(ir_model_fields, self).name_search(cr, uid, name, [('complete_name','ilike',name), ('id','in',iids)], operator, context, limit)
-
-        return result
-
 ir_model_fields()
 
 class server_object_lines(osv.osv):
@@ -371,12 +370,12 @@ class actions_server(osv.osv):
     def _select_signals(self, cr, uid, context={}):
         cr.execute("select distinct t.signal as key, t.signal || ' - [ ' || w.osv || ' ] ' as val from wkf w, wkf_activity a, wkf_transition t "\
                         " where w.id = a.wkf_id " \
-                        " and t.act_from = a.wkf_id " \
-                        " or t.act_to = a.wkf_id and t.signal not in (null, NULL)")
+                        " and t.act_from = a.id " \
+                        " or t.act_to = a.id and t.signal not in (null, NULL)")
         result = cr.fetchall() or []
         res = []
         for rs in result:
-            if not rs[0] == None and not rs[1] == None:
+            if rs[0] is not None and rs[1] is not None:
                 res.append(rs)
         return res
 
@@ -385,8 +384,8 @@ class actions_server(osv.osv):
     _sequence = 'ir_actions_id_seq'
     _order = 'sequence'
     _columns = {
-        'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name i.e. One Sales Order -> Many Invoice"),
-        '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"),
+        'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name e.g. One Sales Order -> Many Invoices", translate=True),
+        'condition' : fields.char('Condition', size=256, required=True, help="Condition that is to be tested before action is executed, e.g. object.list_price > object.cost_price"),
         'state': fields.selection([
             ('client_action','Client Action'),
             ('dummy','Dummy'),
@@ -398,28 +397,28 @@ class actions_server(osv.osv):
             ('object_create','Create Object'),
             ('object_write','Write Object'),
             ('other','Multi Actions'),
-        ], 'Action Type', required=True, size=32, help="Type of the Action that is to be execute"),
-        'code':fields.text('Python Code', help="python code to be execute"),
-        '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"),
-        'model_id': fields.many2one('ir.model', 'Object', required=True, help="select the obect on which the action will work (read, write, create)"),
-        'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Ation Window, Report, Wizard to be execute"),
-        'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128, help="Select the Signal name that is to be "),
-        'wkf_model_id': fields.many2one('ir.model', 'Workflow on', help="Workflow to be execute on which model"),
-        'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On', help="select the object from the model on which the workflow will execute"),
-        '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"),
-        '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 ]]`"),
-        'message': fields.text('Message', translate=True, help="Specify the Message, you can use the fields from the object. like `Dear [[ object.partner_id.name ]]`"),
-        '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"),
+        ], 'Action Type', required=True, size=32, help="Type of the Action that is to be executed"),
+        'code':fields.text('Python Code', help="Python code to be executed"),
+        'sequence': fields.integer('Sequence', help="Important when you deal with multiple actions, the execution order will be decided based on this, low number is higher priority."),
+        'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create)."),
+        'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Action Window, Report, Wizard to be executed."),
+        'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128, help="Select the Signal name that is to be used as the trigger."),
+        'wkf_model_id': fields.many2one('ir.model', 'Workflow On', help="Workflow to be executed on this model."),
+        'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On', help="Select the object from the model on which the workflow will executed."),
+        'email': fields.char('Email Address', size=512, help="Provides the fields that will be used to fetch the email address, e.g. when you select the invoice, then `object.invoice_address_id.email` is the field which gives the correct address"),
+        'subject': fields.char('Subject', size=1024, translate=True, help="Specify the subject. You can use fields from the object, e.g. `Hello [[ object.partner_id.name ]]`"),
+        'message': fields.text('Message', translate=True, help="Specify the message. You can use the fields from the object. e.g. `Dear [[ object.partner_id.name ]]`"),
+        'mobile': fields.char('Mobile No', size=512, help="Provides fields that be used to fetch the mobile number, e.g. you select the invoice, then `object.invoice_address_id.mobile` is the field which gives the correct mobile number"),
         'sms': fields.char('SMS', size=160, translate=True),
-        'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Others Actions'),
+        'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Other Actions'),
         'usage': fields.char('Action Usage', size=32),
         'type': fields.char('Action Type', size=32, required=True),
-        '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"),
-        'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Fields Mapping'),
-        '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"),
-        '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"),
-        '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"),
-        '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`"),
+        'srcmodel_id': fields.many2one('ir.model', 'Model', help="Object in which you want to create / write the object. If it is empty then refer to the Object field."),
+        'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Field Mappings.'),
+        'record_id':fields.many2one('ir.model.fields', 'Create Id', help="Provide the field name where the record id is stored after the create operations. If it is empty, you can not track the new record."),
+        'write_id':fields.char('Write Id', size=256, help="Provide the field name that the record id refers to for the write operation. If it is empty it will refer to the active id of the object."),
+        'loop_action':fields.many2one('ir.actions.server', 'Loop Action', help="Select the action that will be executed. Loop action will not be avaliable inside loop."),
+        'expression':fields.char('Loop Expression', size=512, help="Enter the field/expression that will return the list. E.g. select the sale order in Object, and you can have loop on the sales order line. Expression = `object.order_line`."),
     }
     _defaults = {
         'state': lambda *a: 'dummy',
@@ -490,10 +489,10 @@ class actions_server(osv.osv):
             if result in (None, False):
                 return str("--------")
             return str(result)
-        
+
         com = re.compile('(\[\[.+?\]\])')
         message = com.sub(merge, keystr)
-        
+
         return message
 
     # Context should contains:
@@ -502,16 +501,16 @@ class actions_server(osv.osv):
     # OUT:
     #   False : Finnished correctly
     #   ACTION_ID : Action to launch
-    
+
     def run(self, cr, uid, ids, context={}):
         logger = netsvc.Logger()
-        
+
         for action in self.browse(cr, uid, ids, context):
             obj_pool = self.pool.get(action.model_id.model)
             obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
             cxt = {
-                'context':context, 
-                'object': obj, 
+                'context':context,
+                'object': obj,
                 'time':time,
                 'cr': cr,
                 'pool' : self.pool,
@@ -520,21 +519,22 @@ class actions_server(osv.osv):
             expr = eval(str(action.condition), cxt)
             if not expr:
                 continue
-            
+
             if action.state=='client_action':
                 if not action.action_id:
-                    raise osv.except_osv(_('Error'), _("Please specify an action to launch !")) 
-                result = self.pool.get(action.action_id.type).read(cr, uid, action.action_id.id, context=context)
-                return result
+                    raise osv.except_osv(_('Error'), _("Please specify an action to launch !"))
+                return self.pool.get(action.action_id.type)\
+                    .read(cr, uid, action.action_id.id, context=context)
 
-            if action.state=='python':
+            if action.state == 'code':
                 localdict = {
                     'self': self.pool.get(action.model_id.model),
                     'context': context,
                     'time': time,
                     'ids': ids,
                     'cr': cr,
-                    'uid': uid
+                    'uid': uid,
+                    'obj':obj
                 }
                 exec action.code in localdict
                 if 'action' in localdict:
@@ -547,15 +547,16 @@ class actions_server(osv.osv):
                     address =  eval(str(action.email), cxt)
                 except:
                     pass
-                
+
                 if not address:
-                    raise osv.except_osv(_('Error'), _("Please specify the Partner Email address !"))
+                    logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!')
+                    continue
                 if not user:
                     raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
-                
+
                 subject = self.merge_message(cr, uid, str(action.subject), action, context)
                 body = self.merge_message(cr, uid, str(action.message), action, context)
-                
+
                 if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
                     logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
                 else:
@@ -580,7 +581,7 @@ class actions_server(osv.osv):
                     logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address))
                 else:
                     logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address))
-            
+
             if action.state == 'other':
                 res = []
                 for act in action.child_ids:
@@ -588,15 +589,15 @@ class actions_server(osv.osv):
                     result = self.run(cr, uid, [act.id], context)
                     if result:
                         res.append(result)
-                    
+
                 return res
-            
+
             if action.state == 'loop':
                 obj_pool = self.pool.get(action.model_id.model)
                 obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
                 cxt = {
-                    'context':context, 
-                    'object': obj, 
+                    'context':context,
+                    'object': obj,
                     'time':time,
                     'cr': cr,
                     'pool' : self.pool,
@@ -607,7 +608,7 @@ class actions_server(osv.osv):
                 for i in expr:
                     context['active_id'] = i.id
                     result = self.run(cr, uid, [action.loop_action.id], context)
-            
+
             if action.state == 'object_write':
                 res = {}
                 for exp in action.fields_lines:
@@ -629,7 +630,7 @@ class actions_server(osv.osv):
                         write_id = context.get('active_id')
                         obj_pool = self.pool.get(action.srcmodel_id.model)
                         obj_pool.write(cr, uid, [write_id], res)
-                        
+
                 elif action.write_id:
                     obj_pool = self.pool.get(action.srcmodel_id.model)
                     rec = self.pool.get(action.model_id.model).browse(cr, uid, context.get('active_id'))
@@ -638,7 +639,7 @@ class actions_server(osv.osv):
                         id = int(id)
                     except:
                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
-                    
+
                     if type(id) != type(1):
                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
                     write_id = id
@@ -682,9 +683,9 @@ act_window_close()
 # if action type is 'service',
 #                - if start_type= 'at once', it will be start at one time on start date
 #                - if start_type='auto', it will be start on auto starting from start date, and stop on stop date
-#                - if start_type="manual", it will start and stop on manually 
+#                - if start_type="manual", it will start and stop on manually
 class ir_actions_todo(osv.osv):
-    _name = 'ir.actions.todo'    
+    _name = 'ir.actions.todo'
     _columns={
         'name':fields.char('Name',size=64,required=True, select=True),
         'note':fields.text('Text', translate=True),
@@ -713,64 +714,77 @@ class ir_actions_configuration_wizard(osv.osv_memory):
     _name='ir.actions.configuration.wizard'
     def next_configuration_action(self,cr,uid,context={}):
         item_obj = self.pool.get('ir.actions.todo')
-        item_ids = item_obj.search(cr, uid, [('type','=','configure'),('state', '=', 'open'),('active','=',True)], limit=1, context=context)
-        if item_ids and len(item_ids):
-            item = item_obj.browse(cr, uid, item_ids[0], context=context)
-            return item
+        item_ids = item_obj.search(cr, uid,
+                                   [('type','=','configure'),
+                                    ('state', '=', 'open'),
+                                    ('active','=',True)],
+                                   limit=1, context=context)
+        if item_ids:
+            return item_obj.browse(cr, uid, item_ids[0], context=context)
         return False
     def _get_action_name(self, cr, uid, context={}):
-        next_action=self.next_configuration_action(cr,uid,context=context)        
+        next_action = self.next_configuration_action(cr,uid,context=context)
         if next_action:
             return next_action.note
         else:
             return "Your database is now fully configured.\n\nClick 'Continue' and enjoy your OpenERP experience..."
-        return False
 
     def _get_action(self, cr, uid, context={}):
-        next_action=self.next_configuration_action(cr,uid,context=context)
-        if next_action:           
+        next_action = self.next_configuration_action(cr, uid, context=context)
+        if next_action:
             return next_action.id
-        return False
-
-    def _progress_get(self,cr,uid, context={}):
-        total = self.pool.get('ir.actions.todo').search_count(cr, uid, [], context)
-        todo = self.pool.get('ir.actions.todo').search_count(cr, uid, [('type','=','configure'),('active','=',True),('state','<>','open')], context)
-        return max(5.0,round(todo*100/total))
+        return
+
+    def _progress_get(self, cr, uid, context=None):
+        total = self.pool.get('ir.actions.todo')\
+            .search_count(cr, uid, [], context)
+        todo = self.pool.get('ir.actions.todo')\
+            .search_count(cr, uid,[('type','=','configure'),
+                                   ('active','=',True),
+                                   ('state','<>','open')],
+                          context)
+        if total > 0.0:
+            return max(5.0, round(todo*100/total))
+        else:
+            return 100.0
 
     _columns = {
-        'name': fields.text('Next Wizard',readonly=True),
+        'name': fields.text('Next Wizard', readonly=True),
         'progress': fields.float('Configuration Progress', readonly=True),
-        'item_id':fields.many2one('ir.actions.todo', 'Next Configuration Wizard',invisible=True, readonly=True),
+        'item_id': fields.many2one('ir.actions.todo',
+                                  'Next Configuration Wizard',
+                                  invisible=True, readonly=True),
     }
     _defaults={
         'progress': _progress_get,
-        'item_id':_get_action,
-        'name':_get_action_name,
+        'item_id': _get_action,
+        'name': _get_action_name,
     }
     def button_next(self,cr,uid,ids,context=None):
-        user_action=self.pool.get('res.users').browse(cr,uid,uid)
-        act_obj=self.pool.get(user_action.menu_id.type)
-        action_ids=act_obj.search(cr,uid,[('name','=',user_action.menu_id.name)])
-        action_open=act_obj.browse(cr,uid,action_ids)[0]
-        if context.get('menu',False):
-            return{
+        user_action = self.pool.get('res.users').browse(cr,uid,uid)
+        act_obj = self.pool.get(user_action.menu_id.type)
+        action_ids = act_obj.search(cr,uid,[
+                ('name', '=', user_action.menu_id.name)])
+        action_open = act_obj.browse(cr, uid, action_ids)[0]
+        if context and 'menu' in context:
+            return {
                 'view_type': action_open.view_type,
-                'view_id':action_open.view_id and [action_open.view_id.id] or False,
+                'view_id': action_open.view_id and [action_open.view_id.id] or False,
                 'res_model': action_open.res_model,
                 'type': action_open.type,
-                'domain':action_open.domain
+                'domain': action_open.domain
             }
-        return {'type':'ir.actions.act_window_close'}
+        return {'type': 'ir.actions.act_window_close'}
 
     def button_skip(self,cr,uid,ids,context=None):
         item_obj = self.pool.get('ir.actions.todo')
-        item_id=self.read(cr,uid,ids)[0]['item_id']
+        item_id = self.read(cr,uid,ids)[0]['item_id']
         if item_id:
             item = item_obj.browse(cr, uid, item_id, context=context)
             item_obj.write(cr, uid, item.id, {
                 'state': 'skip',
                 }, context=context)
-            return{
+            return {
                 'view_type': 'form',
                 "view_mode": 'form',
                 'res_model': 'ir.actions.configuration.wizard',
@@ -781,13 +795,13 @@ class ir_actions_configuration_wizard(osv.osv_memory):
 
     def button_continue(self, cr, uid, ids, context=None):
         item_obj = self.pool.get('ir.actions.todo')
-        item_id=self.read(cr,uid,ids)[0]['item_id']
+        item_id = self.read(cr,uid,ids)[0]['item_id']
         if item_id:
             item = item_obj.browse(cr, uid, item_id, context=context)
             item_obj.write(cr, uid, item.id, {
                 'state': 'done',
                 }, context=context)
-            return{
+            return {
                   'view_mode': item.action_id.view_mode,
                   'view_type': item.action_id.view_type,
                   'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False,
@@ -798,5 +812,42 @@ class ir_actions_configuration_wizard(osv.osv_memory):
         return self.button_next(cr, uid, ids, context)
 ir_actions_configuration_wizard()
 
+class ir_actions_configurator(osv.osv_memory):
+    _name = 'ir.actions.configurator'
+    logger = netsvc.Logger()
+    def next_action(self, cr, uid, context=None):
+        todos = self.pool.get('ir.actions.todo')
+        self.logger.notifyChannel('actions', netsvc.LOG_INFO,
+                                  'getting next %s' % todos)
+        active_todos = todos.search(cr, uid,
+                                [('type','=','configure'),
+                                 ('state', '=', 'open'),
+                                 ('active','=',True)],
+                                limit=1, context=None)
+        if active_todos:
+            return todos.browse(cr, uid, active_todos[0], context=None)
+        return None
+
+    def next(self, cr, uid):
+        self.logger.notifyChannel('actions', netsvc.LOG_INFO,
+                                  'getting next operation')
+        next = self.next_action(cr, uid)
+        self.logger.notifyChannel('actions', netsvc.LOG_INFO,
+                                  'next action is %s' % next)
+        if next:
+            self.pool.get('ir.actions.todo').write(cr, uid, next.id, {
+                    'state':'done',
+                    }, context=None)
+            action = next.action_id
+            return {
+                'view_mode': action.view_mode,
+                'view_type': action.view_type,
+                'view_id': action.view_id and [action.view_id.id] or False,
+                'res_model': action.res_model,
+                'type': action.type,
+                'target': action.target,
+                }
+        return {'type': 'ir.actions.act_window_close'}
+ir_actions_configurator()
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: