[IMP] ir_actions_server: added doc + comments + recursion check
orm: added _check_m2my_recursion, to check loops in many2many recursive fields tools: removed sms_send bzr revid: tde@openerp.com-20130725104914-dutxfon3odp8z167
This commit is contained in:
parent
7c5429e0d1
commit
f737eb63d0
|
@ -6,7 +6,12 @@ Changelog
|
|||
`trunk`
|
||||
-------
|
||||
|
||||
- Cleaned and slightly refactored ``ir.actions.server``
|
||||
- Cleaned and slightly refactored ``ir.actions.server``. The ``loop``, ``sms``
|
||||
and ``dummy`` server actions have been removed; ``object_create`` and
|
||||
``object_copy`` have been merged into ``object_create``; ``other`` is now ``multi``
|
||||
and raises in case of loops. See :ref:`ir-actions-server` for more details.
|
||||
- Removed ``sms_send`` method.
|
||||
- Added checking of recursions in many2many loops using ``_check_m2m_recursion``.
|
||||
- Added MONTHS attribute on fields.date and fields.datetime, holding the list
|
||||
(month_number, month_name)
|
||||
- Almost removed ``LocalService()``. For reports,
|
||||
|
|
|
@ -19,6 +19,7 @@ OpenERP Server
|
|||
deployment-gunicorn
|
||||
deployment-mod-wsgi
|
||||
form-view-guidelines
|
||||
ir_actions
|
||||
|
||||
OpenERP Command
|
||||
'''''''''''''''
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from functools import partial
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
|
@ -448,6 +449,27 @@ class server_object_lines(osv.osv):
|
|||
# Actions that are run on the server side
|
||||
#
|
||||
class 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
|
||||
menu.
|
||||
|
||||
Since OpenERP 8.0 a button 'Create Menu Action' button is available on the
|
||||
action form view. It creates an entry in the More menu of the base model.
|
||||
This allows to create server actions and run them in mass mode easily through
|
||||
the interface.
|
||||
|
||||
The available actions are :
|
||||
|
||||
- 'Execute Python Code': a block of python code that will be executed
|
||||
- 'Trigger a Workflow Signal': send a signal to a workflow
|
||||
- 'Run a Client Action': choose a client action to launch
|
||||
- 'Create or Copy a new Record': create a new record with new values, or
|
||||
copy an existing record in your database
|
||||
- 'Write on a Record': update the values of a record
|
||||
- 'Execute several actions': define an action that triggers several other
|
||||
server actions
|
||||
"""
|
||||
_name = 'ir.actions.server'
|
||||
_table = 'ir_act_server'
|
||||
_inherit = 'ir.actions.actions'
|
||||
|
@ -487,9 +509,9 @@ class actions_server(osv.osv):
|
|||
"- 'Execute Python Code': a block of python code that will be executed\n"
|
||||
"- 'Trigger a Workflow Signal': send a signal to a workflow\n"
|
||||
"- 'Run a Client Action': choose a client action to launch\n"
|
||||
"- 'Create or Copy a new Record': create a new record with new values, or copy an existing record in your database\n"
|
||||
"- 'Create or Copy a new Record': create a new record with new values, or copy an existing record in your database\n"
|
||||
"- 'Write on a Record': update the values of a record\n"
|
||||
"- 'Execute several actions': define an action that triggers several other sever actions\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),
|
||||
|
@ -628,6 +650,9 @@ class actions_server(osv.osv):
|
|||
(_check_write_expression,
|
||||
'Incorrect Write Record Expression',
|
||||
['write_expression']),
|
||||
(partial(osv.Model._check_m2m_recursion, field_name='child_ids'),
|
||||
'Recursion found in child server actions',
|
||||
['child_ids']),
|
||||
]
|
||||
|
||||
def on_change_model_id(self, cr, uid, ids, model_id, wkf_model_id, crud_model_id, context=None):
|
||||
|
@ -895,21 +920,23 @@ class actions_server(osv.osv):
|
|||
self.pool[action.model_id.model].write(cr, uid, [context.get('active_id')], {action.link_field_id.name: res_id})
|
||||
|
||||
def run(self, cr, uid, ids, context=None):
|
||||
""" Run the server action, by check the condition and then calling
|
||||
run_action_<STATE>, i.e. run_action_code, allowing easy inheritance
|
||||
of the server actions.
|
||||
""" Run the server action. For each server action, the condition is
|
||||
checked. Note that A void (aka 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.
|
||||
|
||||
A void (aka False) condition is considered as always valid.
|
||||
:param dict context: context should contain following keys
|
||||
|
||||
Note coming from previous implementation: FIXME: refactor all the eval()
|
||||
calls in run()!
|
||||
- active_id: id of the current object (single mode)
|
||||
- active_model: current model that should equal the action's model
|
||||
|
||||
:param dict context: context should contain following keys:
|
||||
- active_id: current id of the object
|
||||
- active_model: current model that should equal the action's model
|
||||
- TDE: ?? ids: original ids
|
||||
The following keys are optional:
|
||||
|
||||
:return: False: finished correctly or action_id: action to lanch
|
||||
- active_ids: ids of the current records (mass mode). If active_ids
|
||||
and active_id are present, active_ids is given precedence.
|
||||
|
||||
:return: an action_id to be executed, or False is finished correctly without
|
||||
return action
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import unittest2
|
||||
|
||||
from openerp.osv.orm import except_orm
|
||||
import openerp.tests.common as common
|
||||
from openerp.tools import mute_logger
|
||||
|
||||
|
||||
class TestServerActionsBase(common.TransactionCase):
|
||||
|
@ -354,6 +356,7 @@ self.pool["res.partner"].create(cr, uid, {"name": partner_name}, context=context
|
|||
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'NewCountry')])
|
||||
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
|
||||
def test_60_multi(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
|
@ -385,6 +388,11 @@ self.pool["res.partner"].create(cr, uid, {"name": partner_name}, context=context
|
|||
# Test: action returned
|
||||
self.assertEqual(res.get('type'), 'ir.actions.act_window', '')
|
||||
|
||||
# Test loops
|
||||
self.assertRaises(except_orm, self.ir_actions_server.write, cr, uid, [self.act_id], {
|
||||
'child_ids': [(6, 0, [self.act_id])]
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
|
|
@ -5114,6 +5114,40 @@ class BaseModel(object):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _check_m2m_recursion(self, cr, uid, ids, field_name):
|
||||
"""
|
||||
Verifies that there is no loop in a hierarchical structure of records,
|
||||
by following the parent relationship using the **parent** field until a loop
|
||||
is detected or until a top-level record is found.
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: list of ids of records to check
|
||||
:param field_name: field to check
|
||||
:return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected.
|
||||
"""
|
||||
|
||||
field = self._all_columns.get(field_name)
|
||||
field = field.column if field else None
|
||||
if not field or field._type != 'many2many' or field._obj != self._name:
|
||||
# field must be a many2many on itself
|
||||
raise ValueError('invalid field_name: %r' % (field_name,))
|
||||
|
||||
query = 'SELECT distinct "%s" FROM "%s" WHERE "%s" IN %%s' % (field._id2, field._rel, field._id1)
|
||||
ids_parent = ids[:]
|
||||
while ids_parent:
|
||||
ids_parent2 = []
|
||||
for i in range(0, len(ids_parent), cr.IN_MAX):
|
||||
j = i + cr.IN_MAX
|
||||
sub_ids_parent = ids_parent[i:j]
|
||||
cr.execute(query, (tuple(sub_ids_parent),))
|
||||
ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall())))
|
||||
ids_parent = ids_parent2
|
||||
for i in ids_parent:
|
||||
if i in ids:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_external_ids(self, cr, uid, ids, *args, **kwargs):
|
||||
"""Retrieve the External ID(s) of any database record.
|
||||
|
||||
|
|
|
@ -272,18 +272,6 @@ def reverse_enumerate(l):
|
|||
"""
|
||||
return izip(xrange(len(l)-1, -1, -1), reversed(l))
|
||||
|
||||
#----------------------------------------------------------
|
||||
# SMS
|
||||
#----------------------------------------------------------
|
||||
# text must be latin-1 encoded
|
||||
def sms_send(user, password, api_id, text, to):
|
||||
import urllib
|
||||
url = "http://api.urlsms.com/SendSMS.aspx"
|
||||
#url = "http://196.7.150.220/http/sendmsg"
|
||||
params = urllib.urlencode({'UserID': user, 'Password': password, 'SenderID': api_id, 'MsgText': text, 'RecipientMobileNo':to})
|
||||
urllib.urlopen(url+"?"+params)
|
||||
# FIXME: Use the logger if there is an error
|
||||
return True
|
||||
|
||||
class UpdateableStr(local):
|
||||
""" Class that stores an updateable string (used in wizards)
|
||||
|
|
Loading…
Reference in New Issue