from openerp import SUPERUSER_ID
from openerp import tools
from openerp import workflow
+import openerp.api
from openerp.osv import fields, osv
from openerp.osv.orm import browse_record
import openerp.report.interface
_logger = logging.getLogger(__name__)
+
class actions(osv.osv):
_name = 'ir.actions.actions'
_table = 'ir_actions'
_order = 'name'
_columns = {
- 'name': fields.char('Name', size=64, required=True),
- 'type': fields.char('Action Type', required=True, size=32),
- 'usage': fields.char('Action Usage', size=32),
+ 'name': fields.char('Name', required=True),
+ 'type': fields.char('Action Type', required=True),
+ 'usage': fields.char('Action Usage'),
'help': fields.text('Action description',
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',
translate=True),
_defaults = {
'usage': lambda *a: False,
}
-actions()
+ def unlink(self, cr, uid, ids, context=None):
+ """unlink ir.action.todo which are related to actions which will be deleted.
+ NOTE: ondelete cascade will not work on ir.actions.actions so we will need to do it manually."""
+ todo_obj = self.pool.get('ir.actions.todo')
+ if not ids:
+ return True
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ todo_ids = todo_obj.search(cr, uid, [('action_id', 'in', ids)], context=context)
+ todo_obj.unlink(cr, uid, todo_ids, context=context)
+ return super(actions, self).unlink(cr, uid, ids, context=context)
-class report_xml(osv.osv):
+class ir_actions_report_xml(osv.osv):
def _report_content(self, cursor, user, ids, name, arg, context=None):
res = {}
cr.execute("SELECT * FROM ir_act_report_xml WHERE report_name=%s", (name,))
r = cr.dictfetchone()
if r:
- if r['report_rml'] or r['report_rml_content_data']:
+ if r['report_type'] in ['qweb-pdf', 'qweb-html']:
+ return r['report_name']
+ elif r['report_rml'] or r['report_rml_content_data']:
if r['parser']:
kwargs = { 'parser': operator.attrgetter(r['parser'])(openerp.addons) }
else:
else:
raise Exception, "Unhandled report type: %s" % r
else:
- raise Exception, "Required report does not exist: %s" % r
+ raise Exception, "Required report does not exist: %s" % name
return new_report
Look up a report definition and render the report for the provided IDs.
"""
new_report = self._lookup_report(cr, name)
- return new_report.create(cr, uid, res_ids, data, context)
+
+ if isinstance(new_report, (str, unicode)): # Qweb report
+ # The only case where a QWeb report is rendered with this method occurs when running
+ # yml tests originally written for RML reports.
+ if openerp.tools.config['test_enable'] and not tools.config['test_report_directory']:
+ # Only generate the pdf when a destination folder has been provided.
+ return self.pool['report'].get_html(cr, uid, res_ids, new_report, data=data, context=context), 'html'
+ else:
+ return self.pool['report'].get_pdf(cr, uid, res_ids, new_report, data=data, context=context), 'pdf'
+ else:
+ return new_report.create(cr, uid, res_ids, data, context)
_name = 'ir.actions.report.xml'
_inherit = 'ir.actions.actions'
_sequence = 'ir_actions_id_seq'
_order = 'name'
_columns = {
- 'name': fields.char('Name', size=64, required=True, translate=True),
- 'model': fields.char('Object', size=64, required=True),
- 'type': fields.char('Action Type', size=32, required=True),
- 'report_name': fields.char('Service Name', size=64, required=True),
- 'usage': fields.char('Action Usage', size=32),
- 'report_type': fields.char('Report Type', size=32, required=True, help="Report Type, e.g. pdf, html, raw, sxw, odt, html2html, mako2html, ..."),
+ 'type': fields.char('Action Type', required=True),
+ 'name': fields.char('Name', required=True, translate=True),
+
+ 'model': fields.char('Model', required=True),
+ 'report_type': fields.selection([('qweb-pdf', 'PDF'),
+ ('qweb-html', 'HTML'),
+ ('controller', 'Controller'),
+ ('pdf', 'RML pdf (deprecated)'),
+ ('sxw', 'RML sxw (deprecated)'),
+ ('webkit', 'Webkit (deprecated)'),
+ ], 'Report Type', required=True, help="HTML will open the report directly in your browser, PDF will use wkhtmltopdf to render the HTML into a PDF file and let you download it, Controller allows you to define the url of a custom controller outputting any kind of report."),
+ 'report_name': fields.char('Template Name', required=True, help="For QWeb reports, name of the template used in the rendering. The method 'render_html' of the model 'report.template_name' will be called (if any) to give the html. For RML reports, this is the LocalService name."),
'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
+
+ # options
'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."),
- '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.'),
- 'auto': fields.boolean('Custom Python Parser'),
+ 'attachment': fields.char('Save as Attachment Prefix', 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.'),
+ # Deprecated rml stuff
+ 'usage': fields.char('Action Usage'),
'header': fields.boolean('Add RML Header', help="Add or not the corporate RML header"),
+ 'parser': fields.char('Parser Class'),
+ 'auto': fields.boolean('Custom Python Parser'),
- 'report_xsl': fields.char('XSL Path', size=256),
- 'report_xml': fields.char('XML Path', size=256, help=''),
+ 'report_xsl': fields.char('XSL Path'),
+ 'report_xml': fields.char('XML Path'),
- # Pending deprecation... to be replaced by report_file as this object will become the default report object (not so specific to RML anymore)
- 'report_rml': fields.char('Main Report File Path', size=256, help="The path to the main report file (depending on Report Type) or NULL if the content is in another data field"),
- # temporary related field as report_rml is pending deprecation - this field will replace report_rml after v6.0
- 'report_file': fields.related('report_rml', type="char", size=256, required=False, readonly=False, string='Report File', help="The path to the main report file (depending on Report Type) or NULL if the content is in another field", store=True),
+ 'report_rml': fields.char('Main Report File Path/controller', help="The path to the main report file/controller (depending on Report Type) or NULL if the content is in another data field"),
+ 'report_file': fields.related('report_rml', type="char", required=False, readonly=False, string='Report File', help="The path to the main report file (depending on Report Type) or NULL if the content is in another field", store=True),
'report_sxw': fields.function(_report_sxw, type='char', string='SXW Path'),
'report_sxw_content_data': fields.binary('SXW Content'),
'report_rml_content_data': fields.binary('RML Content'),
'report_sxw_content': fields.function(_report_content, fnct_inv=_report_content_inv, type='binary', string='SXW Content',),
'report_rml_content': fields.function(_report_content, fnct_inv=_report_content_inv, type='binary', string='RML Content'),
-
- 'parser': fields.char('Parser Class'),
}
_defaults = {
'type': 'ir.actions.report.xml',
'attachment': False,
}
-report_xml()
-class act_window(osv.osv):
+class ir_actions_act_window(osv.osv):
_name = 'ir.actions.act_window'
_table = 'ir_act_window'
_inherit = 'ir.actions.actions'
return res
_columns = {
- 'name': fields.char('Action Name', size=64, translate=True),
- 'type': fields.char('Action Type', size=32, required=True),
- 'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'),
+ 'name': fields.char('Action Name', translate=True),
+ 'type': fields.char('Action Type', required=True),
+ 'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='set null'),
'domain': fields.char('Domain Value',
help="Optional domain filtering of the destination data, as a Python expression"),
'context': fields.char('Context Value', required=True,
help="Context dictionary as Python expression, empty by default (Default: {})"),
'res_id': fields.integer('Record ID', help="Database ID of record to open in form view, when ``view_mode`` is set to 'form' only"),
- 'res_model': fields.char('Destination Model', size=64, required=True,
+ 'res_model': fields.char('Destination Model', required=True,
help="Model name of the object to open in the view window"),
- 'src_model': fields.char('Source Model', size=64,
+ 'src_model': fields.char('Source Model',
help="Optional model name of the objects on which this action should be visible"),
'target': fields.selection([('current','Current Window'),('new','New Window'),('inline','Inline Edit'),('inlineview','Inline View')], 'Target Window'),
- 'view_mode': fields.char('View Mode', size=250, required=True,
+ 'view_mode': fields.char('View Mode', required=True,
help="Comma-separated list of allowed view modes, such as 'form', 'tree', 'calendar', etc. (Default: tree,form)"),
'view_type': fields.selection((('tree','Tree'),('form','Form')), string='View Type', required=True,
help="View type: Tree type to use for the tree view, set to 'tree' for a hierarchical tree view, or 'form' for a regular list view"),
- 'usage': fields.char('Action Usage', size=32,
+ 'usage': fields.char('Action Usage',
help="Used to filter menu and home actions from the user form."),
'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'),
'views': fields.function(_views_get_fnc, type='binary', string='Views',
'filter': fields.boolean('Filter'),
'auto_search':fields.boolean('Auto Search'),
'search_view' : fields.function(_search_view, type='text', string='Search View'),
- 'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"),
+ 'multi': fields.boolean('Restrict to lists', help="If checked and the action is bound to a model, it will only appear in the More menu on list views"),
}
_defaults = {
ids_int = isinstance(ids, (int, long))
if ids_int:
ids = [ids]
- results = super(act_window, self).read(cr, uid, ids, fields=fields, context=context, load=load)
-
- if not fields or 'help' in fields:
- context = dict(context or {})
- eval_dict = {
- 'active_model': context.get('active_model'),
- 'active_id': context.get('active_id'),
- 'active_ids': context.get('active_ids'),
- 'uid': uid,
- }
- for res in results:
- model = res.get('res_model')
- if model and self.pool.get(model):
- try:
- with tools.mute_logger("openerp.tools.safe_eval"):
- eval_context = eval(res['context'] or "{}", eval_dict) or {}
- except Exception:
- continue
+ results = super(ir_actions_act_window, self).read(cr, uid, ids, fields=fields, context=context, load=load)
+
+ context = dict(context or {})
+ eval_dict = {
+ 'active_model': context.get('active_model'),
+ 'active_id': context.get('active_id'),
+ 'active_ids': context.get('active_ids'),
+ 'uid': uid,
+ 'context': context,
+ }
+ for res in results:
+ model = res.get('res_model')
+ if model in self.pool:
+ try:
+ with tools.mute_logger("openerp.tools.safe_eval"):
+ eval_context = eval(res['context'] or "{}", eval_dict) or {}
+ res['context'] = str(eval_context)
+ except Exception:
+ continue
+ if not fields or 'help' in fields:
custom_context = dict(context, **eval_context)
- res['help'] = self.pool.get(model).get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
+ res['help'] = self.pool[model].get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
if ids_int:
return results[0]
return results
dataobj = self.pool.get('ir.model.data')
data_id = dataobj._get_id (cr, SUPERUSER_ID, module, xml_id)
res_id = dataobj.browse(cr, uid, data_id, context).res_id
- return self.read(cr, uid, res_id, [], context)
-
-act_window()
+ return self.read(cr, uid, [res_id], [], context)[0]
VIEW_TYPES = [
('tree', 'Tree'),
('calendar', 'Calendar'),
('gantt', 'Gantt'),
('kanban', 'Kanban')]
-class act_window_view(osv.osv):
+class ir_actions_act_window_view(osv.osv):
_name = 'ir.actions.act_window.view'
_table = 'ir_act_window_view'
_rec_name = 'view_id'
'multi': False,
}
def _auto_init(self, cr, context=None):
- super(act_window_view, self)._auto_init(cr, context)
+ super(ir_actions_act_window_view, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'act_window_view_unique_mode_per_action\'')
if not cr.fetchone():
cr.execute('CREATE UNIQUE INDEX act_window_view_unique_mode_per_action ON ir_act_window_view (act_window_id, view_mode)')
-act_window_view()
-class act_wizard(osv.osv):
- _name = 'ir.actions.wizard'
+
+class ir_actions_act_window_close(osv.osv):
+ _name = 'ir.actions.act_window_close'
_inherit = 'ir.actions.actions'
- _table = 'ir_act_wizard'
- _sequence = 'ir_actions_id_seq'
- _order = 'name'
- _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 view."),
- 'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups'),
- 'model': fields.char('Object', size=64),
- }
+ _table = 'ir_actions'
_defaults = {
- 'type': 'ir.actions.wizard',
- 'multi': False,
+ 'type': 'ir.actions.act_window_close',
}
-act_wizard()
-class act_url(osv.osv):
+
+class ir_actions_act_url(osv.osv):
_name = 'ir.actions.act_url'
_table = 'ir_act_url'
_inherit = 'ir.actions.actions'
_sequence = 'ir_actions_id_seq'
_order = 'name'
_columns = {
- 'name': fields.char('Action Name', size=64, translate=True),
- 'type': fields.char('Action Type', size=32, required=True),
+ 'name': fields.char('Action Name', translate=True),
+ 'type': fields.char('Action Type', required=True),
'url': fields.text('Action URL',required=True),
'target': fields.selection((
('new', 'New Window'),
'type': 'ir.actions.act_url',
'target': 'new'
}
-act_url()
-
-def model_get(self, cr, uid, context=None):
- wkf_pool = self.pool.get('workflow')
- ids = wkf_pool.search(cr, uid, [])
- osvs = wkf_pool.read(cr, uid, ids, ['osv'])
-
- res = []
- mpool = self.pool.get('ir.model')
- for osv in osvs:
- model = osv.get('osv')
- id = mpool.search(cr, uid, [('model','=',model)])
- name = mpool.read(cr, uid, id)[0]['name']
- res.append((model, name))
-
- return res
-
-class ir_model_fields(osv.osv):
- _inherit = 'ir.model.fields'
- _rec_name = 'field_description'
- _columns = {
- 'complete_name': fields.char('Complete Name', size=64, select=1),
- }
-ir_model_fields()
-
-class server_object_lines(osv.osv):
- _name = 'ir.server.object.lines'
- _sequence = 'ir_actions_id_seq'
- _columns = {
- 'server_id': fields.many2one('ir.actions.server', 'Related Server Action'),
- 'col1': fields.many2one('ir.model.fields', 'Field', required=True),
- 'value': fields.text('Value', required=True, help="Expression containing a value specification. \n"
- "When Formula type is selected, this field may be a Python expression "
- " that can use the same values as for the condition field on the server action.\n"
- "If Value type is selected, the value will be used directly without evaluation."),
- 'type': fields.selection([
- ('value', 'Value'),
- ('equation', 'Python expression')
- ], 'Evaluation Type', required=True, change_default=True),
- }
- _defaults = {
- 'type': 'value',
- }
-##
-# Actions that are run on the server side
-#
-class actions_server(osv.osv):
+class ir_actions_server(osv.osv):
""" Server actions model. Server action work on a base model and offer various
type of actions that can be executed automatically, for example using base
action rules, of manually, by adding the action in the 'More' contextual
return self._get_states(cr, uid, context)
_columns = {
- 'name': fields.char('Action Name', required=True, size=64, translate=True),
+ 'name': fields.char('Action Name', required=True, translate=True),
'condition': fields.char('Condition',
help="Condition verified before executing the server action. If it "
"is not verified, the action will not be executed. The condition is "
"- 'Write on a Record': update the values of a record\n"
"- 'Execute several actions': define an action that triggers several other server actions\n"
"- 'Send Email': automatically send an email (available in email_template)"),
- 'usage': fields.char('Action Usage', size=32),
- 'type': fields.char('Action Type', size=32, required=True),
+ 'usage': fields.char('Action Usage'),
+ 'type': fields.char('Action Type', required=True),
# Generic
'sequence': fields.integer('Sequence',
help="When dealing with multiple actions, the execution order is "
"based on the sequence. Low number means high priority."),
'model_id': fields.many2one('ir.model', 'Base Model', required=True, ondelete='cascade',
help="Base model on which the server action runs."),
+ 'model_name': fields.related('model_id', 'model', type='char',
+ string='Model Name', readonly=True),
'menu_ir_values_id': fields.many2one('ir.values', 'More Menu entry', readonly=True,
- help='More menu entry.'),
+ help='More menu entry.', copy=False),
# Client Action
'action_id': fields.many2one('ir.actions.actions', 'Client Action',
help="Select the client action that has to be executed."),
help="Provide an expression that, applied on the current record, gives the field to update."),
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id',
string='Value Mapping',
- help=""),
+ copy=True),
# Fake fields used to implement the placeholder assistant
'model_object_field': fields.many2one('ir.model.fields', string="Field",
'sequence': 5,
'code': """# You can use the following variables:
# - self: ORM model of the record on which the action is triggered
-# - object: browse_record of the record on which the action is triggered if there is one, otherwise None
+# - object: Record on which the action is triggered if there is one, otherwise None
# - pool: ORM model pool (i.e. self.pool)
# - cr: database cursor
# - uid: current user id
# - context: current context
# - time: Python time module
+# - workflow: Workflow engine
# If you plan to return an action, assign: action = {...}""",
'use_relational_model': 'base',
'use_create': 'new',
# analyze path
while path:
step = path.pop(0)
- column_info = self.pool[current_model_name]._all_columns.get(step)
- if not column_info:
+ field = self.pool[current_model_name]._fields.get(step)
+ if not field:
return (False, None, 'Part of the expression (%s) is not recognized as a column in the model %s.' % (step, current_model_name))
- column_type = column_info.column._type
- if column_type not in ['many2one', 'int']:
- return (False, None, 'Part of the expression (%s) is not a valid column type (is %s, should be a many2one or an int)' % (step, column_type))
- if column_type == 'int' and path:
+ ftype = field.type
+ if ftype not in ['many2one', 'int']:
+ return (False, None, 'Part of the expression (%s) is not a valid column type (is %s, should be a many2one or an int)' % (step, ftype))
+ if ftype == 'int' and path:
return (False, None, 'Part of the expression (%s) is an integer field that is only allowed at the end of an expression' % (step))
- if column_type == 'many2one':
- current_model_name = column_info.column._obj
+ if ftype == 'many2one':
+ current_model_name = field.comodel_name
return (True, current_model_name, None)
def _check_write_expression(self, cr, uid, ids, context=None):
'wkf_field_id': False,
'crud_model_id': model_id,
}
+
+ if model_id:
+ values['model_name'] = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
+
return {'value': values}
def on_change_wkf_wonfig(self, cr, uid, ids, use_relational_model, wkf_field_id, wkf_model_id, model_id, context=None):
def on_change_write_expression(self, cr, uid, ids, write_expression, model_id, context=None):
""" Check the write_expression and update crud_model_id accordingly """
values = {}
- valid, model_name, message = self._check_expression(cr, uid, write_expression, model_id, context=context)
- if valid:
+ if write_expression:
+ valid, model_name, message = self._check_expression(cr, uid, write_expression, model_id, context=context)
+ else:
+ valid, model_name, message = True, None, False
+ if model_id:
+ model_name = self.pool['ir.model'].browse(cr, uid, model_id, context).model
+ if not valid:
+ return {
+ 'warning': {
+ 'title': 'Incorrect expression',
+ 'message': message or 'Invalid expression',
+ }
+ }
+ if model_name:
ref_model_id = self.pool['ir.model'].search(cr, uid, [('model', '=', model_name)], context=context)[0]
values['crud_model_id'] = ref_model_id
return {'value': values}
- if not message:
- message = 'Invalid expression'
- return {
- 'warning': {
- 'title': 'Incorrect expression',
- 'message': message,
- }
- }
+ return {'value': {}}
def on_change_crud_model_id(self, cr, uid, ids, crud_model_id, context=None):
""" When changing the CRUD model, update its stored name also """
def run_action_client_action(self, cr, uid, action, eval_context=None, context=None):
if not action.action_id:
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
- return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
+ return self.pool[action.action_id.type].read(cr, uid, [action.action_id.id], context=context)[0]
def run_action_code_multi(self, cr, uid, action, eval_context=None, context=None):
eval(action.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
def run_action_trigger(self, cr, uid, action, eval_context=None, context=None):
""" Trigger a workflow signal, depending on the use_relational_model:
- - `base`: base_model_pool.signal_<TRIGGER_NAME>(cr, uid, context.get('active_id'))
+ - `base`: base_model_pool.signal_workflow(cr, uid, context.get('active_id'), <TRIGGER_NAME>)
- `relational`: find the related model and object, using the relational
- field, then target_model_pool.signal_<TRIGGER_NAME>(cr, uid, target_id)
+ field, then target_model_pool.signal_workflow(cr, uid, target_id, <TRIGGER_NAME>)
"""
- obj_pool = self.pool[action.model_id.model]
- if action.use_relational_model == 'base':
- target_id = context.get('active_id')
- target_pool = obj_pool
- else:
- value = getattr(obj_pool.browse(cr, uid, context.get('active_id'), context=context), action.wkf_field_id.name)
- if action.wkf_field_id.ttype == 'many2one':
- target_id = value.id
- else:
- target_id = value
- target_pool = self.pool[action.wkf_model_id.model]
+ # weird signature and calling -> no self.env, use action param's
+ record = action.env[action.model_id.model].browse(context['active_id'])
+ if action.use_relational_model == 'relational':
+ record = getattr(record, action.wkf_field_id.name)
+ if not isinstance(record, openerp.models.BaseModel):
+ record = action.env[action.wkf_model_id.model].browse(record)
- trigger_name = action.wkf_transition_id.signal
-
- workflow.trg_validate(uid, target_pool._name, target_id, trigger_name, cr)
+ record.signal_workflow(action.wkf_transition_id.signal)
def run_action_multi(self, cr, uid, action, eval_context=None, context=None):
- res = []
+ res = False
for act in action.child_ids:
- result = self.run(cr, uid, [act.id], context)
+ result = self.run(cr, uid, [act.id], context=context)
if result:
- res.append(result)
- return res and res[0] or False
+ res = result
+ return res
def run_action_object_write(self, cr, uid, action, eval_context=None, context=None):
""" Write server action.
"""
res = {}
for exp in action.fields_lines:
- if exp.type == 'equation':
- expr = eval(exp.value, eval_context)
- else:
- expr = exp.value
- res[exp.col1.name] = expr
+ res[exp.col1.name] = exp.eval_value(eval_context=eval_context)[exp.id]
if action.use_write == 'current':
model = action.model_id.model
"""
res = {}
for exp in action.fields_lines:
- if exp.type == 'equation':
- expr = eval(exp.value, eval_context)
- else:
- expr = exp.value
- res[exp.col1.name] = expr
+ res[exp.col1.name] = exp.eval_value(eval_context=eval_context)[exp.id]
if action.use_create in ['new', 'copy_current']:
model = action.model_id.model
if action.link_new_record and action.link_field_id:
self.pool[action.model_id.model].write(cr, uid, [context.get('active_id')], {action.link_field_id.name: res_id})
- def _eval_context_for_action(self, cr, uid, action, context=None):
- if context is None:
- context = {}
- model = self.pool[action.model_id.model]
- active_id = context.get('active_id')
- active_ids = context.get('active_ids', [active_id] if active_id else [])
- if context.get('active_model') == action.model_id.model and active_id:
- context = dict(context, active_ids=active_ids, active_id=active_id)
- target_record = model.browse(cr, uid, active_id, context=context) if active_id else None
- user = self.pool['res.users'].browse(cr, uid, uid)
- eval_context = {
- 'self': model,
- 'object': target_record,
- 'obj': target_record,
+ def _get_eval_context(self, cr, uid, action, context=None):
+ """ Prepare the context used when evaluating python code, like the
+ condition or code server actions.
+
+ :param action: the current server action
+ :type action: browse record
+ :returns: dict -- evaluation context given to (safe_)eval """
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ obj_pool = self.pool[action.model_id.model]
+ obj = None
+ if context.get('active_model') == action.model_id.model and context.get('active_id'):
+ obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
+ return {
+ 'self': obj_pool,
+ 'object': obj,
+ 'obj': obj,
'pool': self.pool,
'time': time,
'datetime': datetime,
'uid': uid,
'user': user,
'context': context,
+ 'workflow': workflow,
+ 'Warning': openerp.exceptions.Warning,
}
- return eval_context
-
def run(self, cr, uid, ids, context=None):
- """ Run the server action. For each server action, the condition is
- checked. Note that A void (aka False) condition is considered as always
+ """ Runs the server action. For each server action, the condition is
+ checked. Note that a void (``False``) condition is considered as always
valid. If it is verified, the run_action_<STATE> method is called. This
- allows easy inheritance of the server actions.
+ allows easy overriding of the server actions.
:param dict context: context should contain following keys
context = {}
res = False
for action in self.browse(cr, uid, ids, context):
- eval_context = self._eval_context_for_action(cr, uid, action, context)
+ eval_context = self._get_eval_context(cr, uid, action, context=context)
condition = action.condition
if condition is False:
# Void (aka False) conditions are considered as True
return res
-class act_window_close(osv.osv):
- _name = 'ir.actions.act_window_close'
- _inherit = 'ir.actions.actions'
- _table = 'ir_actions'
+class ir_server_object_lines(osv.osv):
+ _name = 'ir.server.object.lines'
+ _description = 'Server Action value mapping'
+ _sequence = 'ir_actions_id_seq'
+
+ _columns = {
+ 'server_id': fields.many2one('ir.actions.server', 'Related Server Action'),
+ 'col1': fields.many2one('ir.model.fields', 'Field', required=True),
+ 'value': fields.text('Value', required=True, help="Expression containing a value specification. \n"
+ "When Formula type is selected, this field may be a Python expression "
+ " that can use the same values as for the condition field on the server action.\n"
+ "If Value type is selected, the value will be used directly without evaluation."),
+ 'type': fields.selection([
+ ('value', 'Value'),
+ ('equation', 'Python expression')
+ ], 'Evaluation Type', required=True, change_default=True),
+ }
+
_defaults = {
- 'type': 'ir.actions.act_window_close',
+ 'type': 'value',
}
-act_window_close()
-# This model use to register action services.
+ def eval_value(self, cr, uid, ids, eval_context=None, context=None):
+ res = dict.fromkeys(ids, False)
+ for line in self.browse(cr, uid, ids, context=context):
+ expr = line.value
+ if line.type == 'equation':
+ expr = eval(line.value, eval_context)
+ elif line.col1.ttype in ['many2one', 'integer']:
+ try:
+ expr = int(line.value)
+ except Exception:
+ pass
+ res[line.id] = expr
+ return res
+
+
TODO_STATES = [('open', 'To Do'),
('done', 'Done')]
TODO_TYPES = [('manual', 'Launch Manually'),('once', 'Launch Manually Once'),
'ir.actions.actions', 'Action', select=True, required=True),
'sequence': fields.integer('Sequence'),
'state': fields.selection(TODO_STATES, string='Status', required=True),
- 'name': fields.char('Name', size=64),
+ 'name': fields.char('Name'),
'type': fields.selection(TODO_TYPES, 'Type', required=True,
help="""Manual: Launched manually.
Automatic: Runs whenever the system is reconfigured.
}
_order="sequence,id"
+ def name_get(self, cr, uid, ids, context=None):
+ return [(rec.id, rec.action_id.name) for rec in self.browse(cr, uid, ids, context=context)]
+
+ def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
+ if args is None:
+ args = []
+ if name:
+ ids = self.search(cr, user, [('action_id', operator, name)] + args, limit=limit)
+ return self.name_get(cr, user, ids, context=context)
+ return super(ir_actions_todo, self).name_search(cr, user, name, args=args, operator=operator, context=context, limit=limit)
+
+
def action_launch(self, cr, uid, ids, context=None):
""" Launch Action of Wizard"""
wizard_id = ids and ids[0] or False
wizard.write({'state': 'done'})
# Load action
- act_type = self.pool.get('ir.actions.actions').read(cr, uid, wizard.action_id.id, ['type'], context=context)
+ act_type = wizard.action_id.type
- res = self.pool[act_type['type']].read(cr, uid, wizard.action_id.id, [], context=context)
- if act_type['type'] != 'ir.actions.act_window':
+ res = self.pool[act_type].read(cr, uid, [wizard.action_id.id], [], context=context)[0]
+ if act_type != 'ir.actions.act_window':
return res
res.setdefault('context','{}')
res['nodestroy'] = True
'todo': len(total) - len(done)
}
-ir_actions_todo()
-class act_client(osv.osv):
+class ir_actions_act_client(osv.osv):
_name = 'ir.actions.client'
_inherit = 'ir.actions.actions'
_table = 'ir_act_client'
def _get_params(self, cr, uid, ids, field_name, arg, context):
result = {}
+ # Need to remove bin_size from context, to obtains the binary and not the length.
+ context = dict(context, bin_size_params_store=False)
for record in self.browse(cr, uid, ids, context=context):
result[record.id] = record.params_store and eval(record.params_store, {'uid': uid}) or False
return result
self.write(cr, uid, id, {'params_store': field_value}, context=context)
_columns = {
- 'name': fields.char('Action Name', required=True, size=64, translate=True),
- 'tag': fields.char('Client action tag', size=64, required=True,
+ 'name': fields.char('Action Name', required=True, translate=True),
+ 'tag': fields.char('Client action tag', required=True,
help="An arbitrary string, interpreted by the client"
" according to its own needs and wishes. There "
"is no central tag repository across clients."),
- 'res_model': fields.char('Destination Model', size=64,
+ 'res_model': fields.char('Destination Model',
help="Optional model, mostly used for needactions."),
- 'context': fields.char('Context Value', size=250, required=True,
+ 'context': fields.char('Context Value', required=True,
help="Context dictionary as Python expression, empty by default (Default: {})"),
'params': fields.function(_get_params, fnct_inv=_set_params,
type='binary',
'context': '{}',
}
-act_client()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: