[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 9743 revid:qdp-launchpad...
authorDenis Ledoux <dle@openerp.com>
Wed, 8 Jan 2014 17:14:00 +0000 (18:14 +0100)
committerDenis Ledoux <dle@openerp.com>
Wed, 8 Jan 2014 17:14:00 +0000 (18:14 +0100)
bzr revid: chs@openerp.com-20140107141524-xzz39a2ym66swr0t
bzr revid: chs@openerp.com-20140107172248-zic9mqg0rigy2czb
bzr revid: chs@openerp.com-20140108160418-ph17jgy5hlejj9hr
bzr revid: dle@openerp.com-20140108171400-8r0fwv3wi36w2im0

12 files changed:
1  2 
addons/account/account_move_line.py
addons/account_voucher/account_voucher.py
addons/crm/crm_lead.py
addons/mrp/procurement.py
addons/project/project.py
openerp/addons/base/ir/ir_actions.py
openerp/addons/base/ir/ir_attachment.py
openerp/addons/base/res/res_users.py
openerp/osv/expression.py
openerp/osv/fields.py
openerp/osv/orm.py
openerp/tools/translate.py

Simple merge
Simple merge
Simple merge
@@@ -64,8 -83,8 +64,8 @@@ class project(osv.osv)
          """ Installation hook: aliases, project.project """
          # create aliases for all projects and avoid constraint errors
          alias_context = dict(context, alias_model_name='project.task')
-         self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(project, self)._auto_init,
+         return self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(project, self)._auto_init,
 -            self._columns['alias_id'], 'id', alias_prefix='project+', alias_defaults={'project_id':'id'}, context=alias_context)
 +            'project.task', self._columns['alias_id'], 'id', alias_prefix='project+', alias_defaults={'project_id':'id'}, context=alias_context)
  
      def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
          if user == 1:
@@@ -478,128 -411,85 +478,128 @@@ class actions_server(osv.osv)
  
      def _select_objects(self, cr, uid, context=None):
          model_pool = self.pool.get('ir.model')
-         ids = model_pool.search(cr, uid, [('name', 'not ilike', '.')])
+         ids = model_pool.search(cr, uid, [], limit=None)
          res = model_pool.read(cr, uid, ids, ['model', 'name'])
 -        return [(r['model'], r['name']) for r in res] +  [('','')]
 -
 -    def change_object(self, cr, uid, ids, copy_object, state, context=None):
 -        if state == 'object_copy' and copy_object:
 -            if context is None:
 -                context = {}
 -            model_pool = self.pool.get('ir.model')
 -            model = copy_object.split(',')[0]
 -            mid = model_pool.search(cr, uid, [('model','=',model)])
 -            return {
 -                'value': {'srcmodel_id': mid[0]},
 -                'context': context
 -            }
 -        else:
 -            return {}
 +        return [(r['model'], r['name']) for r in res] + [('', '')]
 +
 +    def _get_states(self, cr, uid, context=None):
 +        """ Override me in order to add new states in the server action. Please
 +        note that the added key length should not be higher than already-existing
 +        ones. """
 +        return [('code', 'Execute Python Code'),
 +                ('trigger', 'Trigger a Workflow Signal'),
 +                ('client_action', 'Run a Client Action'),
 +                ('object_create', 'Create or Copy a new Record'),
 +                ('object_write', 'Write on a Record'),
 +                ('multi', 'Execute several actions')]
 +
 +    def _get_states_wrapper(self, cr, uid, context=None):
 +        return self._get_states(cr, uid, context)
  
 -    _name = 'ir.actions.server'
 -    _table = 'ir_act_server'
 -    _inherit = 'ir.actions.actions'
 -    _sequence = 'ir_actions_id_seq'
 -    _order = 'sequence,name'
      _columns = {
          'name': fields.char('Action Name', required=True, size=64, translate=True),
 -        'condition' : fields.char('Condition', size=256, required=True,
 -                                  help="Condition that is tested before the action is executed, "
 -                                       "and prevent execution if it is not verified.\n"
 -                                       "Example: object.list_price > 5000\n"
 -                                       "It is a Python expression that can use the following values:\n"
 -                                       " - self: ORM model of the record on which the action is triggered\n"
 -                                       " - object or obj: browse_record of the record on which the action is triggered\n"
 -                                       " - pool: ORM model pool (i.e. self.pool)\n"
 -                                       " - time: Python time module\n"
 -                                       " - cr: database cursor\n"
 -                                       " - uid: current user id\n"
 -                                       " - context: current context"),
 -        'state': fields.selection([
 -            ('client_action','Client Action'),
 -            ('dummy','Dummy'),
 -            ('loop','Iteration'),
 -            ('code','Python Code'),
 -            ('trigger','Trigger'),
 -            ('email','Email'),
 -            ('sms','SMS'),
 -            ('object_create','Create Object'),
 -            ('object_copy','Copy Object'),
 -            ('object_write','Write Object'),
 -            ('other','Multi Actions'),
 -        ], '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 if condition is met.\n"
 -                                               "It is a Python block that can use the same values as for the condition field"),
 -        '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).", ondelete='cascade'),
 -        '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 Signal', size=128, help="The workflow signal to trigger"),
 -        'wkf_model_id': fields.many2one('ir.model', 'Target Object', help="The object that should receive the workflow signal (must have an associated workflow)"),
 -        'trigger_obj_id': fields.many2one('ir.model.fields','Relation Field', help="The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID)"),
 -        'email': fields.char('Email Address', size=512, help="Expression that returns the email address to send to. Can be based on the same values as for the condition field.\n"
 -                                                             "Example: object.invoice_address_id.email, or 'me@example.com'"),
 -        'subject': fields.char('Subject', size=1024, translate=True, help="Email subject, may contain expressions enclosed in double brackets based on the same values as those "
 -                                                                          "available in the condition field, e.g. `Hello [[ object.partner_id.name ]]`"),
 -        'message': fields.text('Message', translate=True, help="Email contents, may contain expressions enclosed in double brackets based on the same values as those "
 -                                                                          "available in the condition field, 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', 'Other Actions'),
 +        '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 "
 +                                 "a Python expression, like 'object.list_price > 5000'. A void "
 +                                 "condition is considered as always True. Help about python expression "
 +                                 "is given in the help tab."),
 +        'state': fields.selection(_get_states_wrapper, 'Action To Do', required=True,
 +                                  help="Type of server action. The following values are available:\n"
 +                                  "- '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"
 +                                  "- '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),
 -        '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`."),
 -        'copy_object': fields.reference('Copy Of', selection=_select_objects, size=256),
 +        # 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."),
 +        'menu_ir_values_id': fields.many2one('ir.values', 'More Menu entry', readonly=True,
 +                                             help='More menu entry.'),
 +        # Client Action
 +        'action_id': fields.many2one('ir.actions.actions', 'Client Action',
 +                                     help="Select the client action that has to be executed."),
 +        # Python code
 +        'code': fields.text('Python Code',
 +                            help="Write Python code that the action will execute. Some variables are "
 +                            "available for use; help about pyhon expression is given in the help tab."),
 +        # Workflow signal
 +        'use_relational_model': fields.selection([('base', 'Use the base model of the action'),
 +                                                  ('relational', 'Use a relation field on the base model')],
 +                                                 string='Target Model', required=True),
 +        'wkf_transition_id': fields.many2one('workflow.transition', string='Signal to Trigger',
 +                                             help="Select the workflow signal to trigger."),
 +        'wkf_model_id': fields.many2one('ir.model', 'Target Model',
 +                                        help="The model that will receive the workflow signal. Note that it should have a workflow associated with it."),
 +        'wkf_model_name': fields.related('wkf_model_id', 'model', type='char', string='Target Model Name', store=True, readonly=True),
 +        'wkf_field_id': fields.many2one('ir.model.fields', string='Relation Field',
 +                                        oldname='trigger_obj_id',
 +                                        help="The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID)"),
 +        # Multi
 +        'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions',
 +                                      'server_id', 'action_id',
 +                                      string='Child Actions',
 +                                      help='Child server actions that will be executed. Note that the last return returned action value will be used as global return value.'),
 +        # Create/Copy/Write
 +        'use_create': fields.selection([('new', 'Create a new record in the Base Model'),
 +                                        ('new_other', 'Create a new record in another model'),
 +                                        ('copy_current', 'Copy the current record'),
 +                                        ('copy_other', 'Choose and copy a record in the database')],
 +                                       string="Creation Policy", required=True,
 +                                       help=""),
 +        'crud_model_id': fields.many2one('ir.model', 'Target Model',
 +                                         oldname='srcmodel_id',
 +                                         help="Model for record creation / update. Set this field only to specify a different model than the base model."),
 +        'crud_model_name': fields.related('crud_model_id', 'model', type='char',
 +                                          string='Create/Write Target Model Name',
 +                                          store=True, readonly=True),
 +        'ref_object': fields.reference('Reference record', selection=_select_objects, size=128,
 +                                       oldname='copy_object'),
 +        'link_new_record': fields.boolean('Attach the new record',
 +                                          help="Check this if you want to link the newly-created record "
 +                                          "to the current record on which the server action runs."),
 +        'link_field_id': fields.many2one('ir.model.fields', 'Link using field',
 +                                         oldname='record_id',
 +                                         help="Provide the field where the record id is stored after the operations."),
 +        'use_write': fields.selection([('current', 'Update the current record'),
 +                                       ('expression', 'Update a record linked to the current record using python'),
 +                                       ('other', 'Choose and Update a record in the database')],
 +                                      string='Update Policy', required=True,
 +                                      help=""),
 +        'write_expression': fields.char('Expression',
 +                                        oldname='write_id',
 +                                        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=""),
 +
 +        # Fake fields used to implement the placeholder assistant
 +        'model_object_field': fields.many2one('ir.model.fields', string="Field",
 +                                              help="Select target field from the related document model.\n"
 +                                                   "If it is a relationship field you will be able to select "
 +                                                   "a target field at the destination of the relationship."),
 +        'sub_object': fields.many2one('ir.model', 'Sub-model', readonly=True,
 +                                      help="When a relationship field is selected as first field, "
 +                                           "this field shows the document model the relationship goes to."),
 +        'sub_model_object_field': fields.many2one('ir.model.fields', 'Sub-field',
 +                                                  help="When a relationship field is selected as first field, "
 +                                                       "this field lets you select the target field within the "
 +                                                       "destination document model (sub-model)."),
 +        'copyvalue': fields.char('Placeholder Expression', help="Final placeholder expression, to be copy-pasted in the desired template field."),
 +        # Fake fields used to implement the ID finding assistant
 +        'id_object': fields.reference('Record', selection=_select_objects, size=128),
 +        'id_value': fields.char('Record ID'),
      }
 +
      _defaults = {
 -        'state': 'dummy',
 +        'state': 'code',
          'condition': 'True',
          'type': 'ir.actions.server',
          'sequence': 5,
@@@ -822,19 -802,15 +822,19 @@@ class users_view(osv.osv)
          return values
  
      def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
 -        if not fields:
 -            fields = self.fields_get(cr, uid, context=context).keys()
 -        group_fields, fields = partition(is_reified_group, fields)
 -        if not 'groups_id' in fields:
 +        fields_get = fields if fields is not None else self.fields_get(cr, uid, context=context).keys()
 +        group_fields, _ = partition(is_reified_group, fields_get)
 +
 +        inject_groups_id = group_fields and fields and 'groups_id' not in fields
 +        if inject_groups_id:
              fields.append('groups_id')
          res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
 -        if res:
 +
-         if group_fields:
++        if res and group_fields:
              for values in (res if isinstance(res, list) else [res]):
                  self._get_reified_groups(group_fields, values)
 +                if inject_groups_id:
 +                    values.pop('groups_id', None)
          return res
  
      def _get_reified_groups(self, fields, values):
Simple merge
@@@ -201,9 -202,10 +201,10 @@@ class reference(_column)
              # reference fields have a 'model,id'-like value, that we need to convert
              # to a real name
              model_name, res_id = value.split(',')
 -            model = obj.pool.get(model_name)
 -            if model and res_id:
 +            if model_name in obj.pool and res_id:
 +                model = obj.pool[model_name]
-                 return model.name_get(cr, uid, [int(res_id)], context=context)[0][1]
+                 names = model.name_get(cr, uid, [int(res_id)], context=context)
+                 return names[0][1] if names else False
          return tools.ustr(value)
  
  # takes a string (encoded in utf8) and returns a string (encoded in utf8)
@@@ -4560,13 -4490,15 +4563,15 @@@ class BaseModel(object)
          self._validate(cr, user, [id_new], context)
  
          if not context.get('no_store_function', False):
-             result += self._store_get_values(cr, user, [id_new], vals.keys(), context)
+             result += self._store_get_values(cr, user, [id_new],
+                 list(set(vals.keys() + self._inherits.values())),
+                 context)
              result.sort()
              done = []
 -            for order, object, ids, fields2 in result:
 -                if not (object, ids, fields2) in done:
 -                    self.pool.get(object)._store_set_values(cr, user, ids, fields2, context)
 -                    done.append((object, ids, fields2))
 +            for order, model_name, ids, fields2 in result:
 +                if not (model_name, ids, fields2) in done:
 +                    self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
 +                    done.append((model_name, ids, fields2))
  
          if self._log_create and not (context and context.get('no_store_function', False)):
              message = self._description + \
Simple merge