Anwar
Anwar

Reputation: 4246

Odoo _check_concurrency never triggered?

With our team, we would like to implement a feature where the user is warned whenever the record he/she is updating have also been updated by one of his collegue since he/she opened the record.

We dug into the source code because we did not find any official documentation, only some module that did not fit our Odoo version (11).

We found in the file /odoo/odoo/models.py the method def _check_concurrency(self): with the following code:

    @api.multi
    def _check_concurrency(self):
        if not (self._log_access and self._context.get(self.CONCURRENCY_CHECK_FIELD)):
            return
        check_clause = "(id = %s AND %s < COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp)"
        for sub_ids in self._cr.split_for_in_conditions(self.ids):
            nclauses = 0
            params = []
            for id in sub_ids:
                id_ref = "%s,%s" % (self._name, id)
                update_date = self._context[self.CONCURRENCY_CHECK_FIELD].pop(id_ref, None)
                if update_date:
                    nclauses += 1
                    params.extend([id, update_date])
            if not nclauses:
                continue
            query = "SELECT id FROM %s WHERE %s" % (self._table, " OR ".join([check_clause] * nclauses))
            self._cr.execute(query, tuple(params))
            res = self._cr.fetchone()
            if res:
                # mention the first one only to keep the error message readable
                raise ValidationError(_('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0]))

=> This method is called before any "write". It compares :

PROBLEM

We did not find anywhere in the code (python or javascript) the value set in the context => NOTHING HAPPENS ! THe function returns from the beginning.

When we tried to hardcode it in the context, the function check_concurrency seems to work properly.

QUESTION

Does anyone one know where the __last_update is set or SHOULD BE set in the context ? And how ? I would e.g. imagine setting it somehow when clicking on edit button of a record ??? Or at read time ??

Upvotes: 3

Views: 424

Answers (1)

Hilar AK
Hilar AK

Reputation: 1675

CONCURRENCY_CHECK_FIELD = '__last_update'

concurrency field is a dynamic field which computing method defined dynamically and also you can see this is updated by last_modified_name = 'compute_concurrency_field_with_access' or last_modified_name = 'compute_concurrency_field' according to access and later added to the class. Following functions will take part in the workaround.

@api.model
    def _add_magic_fields(self):
        """ Introduce magic fields on the current class

        * id is a "normal" field (with a specific getter)
        * create_uid, create_date, write_uid and write_date have become
          "normal" fields
        * $CONCURRENCY_CHECK_FIELD is a computed field with its computing
          method defined dynamically. Uses ``str(datetime.datetime.utcnow())``
          to get the same structure as the previous
          ``(now() at time zone 'UTC')::timestamp``::

              # select (now() at time zone 'UTC')::timestamp;
                        timezone
              ----------------------------
               2013-06-18 08:30:37.292809

              >>> str(datetime.datetime.utcnow())
              '2013-06-18 08:31:32.821177'
        """
        def add(name, field):
            """ add ``field`` with the given ``name`` if it does not exist yet """
            if name not in self._fields:
                self._add_field(name, field)

        # cyclic import
        from . import fields

        # this field 'id' must override any other column or field
        self._add_field('id', fields.Id(automatic=True))

        add('display_name', fields.Char(string='Display Name', automatic=True,
            compute='_compute_display_name'))

        if self._log_access:
            add('create_uid', fields.Many2one('res.users', string='Created by', automatic=True))
            add('create_date', fields.Datetime(string='Created on', automatic=True))
            add('write_uid', fields.Many2one('res.users', string='Last Updated by', automatic=True))
            add('write_date', fields.Datetime(string='Last Updated on', automatic=True))
            last_modified_name = 'compute_concurrency_field_with_access'
        else:
            last_modified_name = 'compute_concurrency_field'

        # this field must override any other column or field
        self._add_field(self.CONCURRENCY_CHECK_FIELD, fields.Datetime(
            string='Last Modified on', compute=last_modified_name, automatic=True))

    def compute_concurrency_field(self):
        for record in self:
            record[self.CONCURRENCY_CHECK_FIELD] = odoo.fields.Datetime.now()

    @api.depends('create_date', 'write_date')
    def compute_concurrency_field_with_access(self):
        for record in self:
            record[self.CONCURRENCY_CHECK_FIELD] = \
                record.write_date or record.create_date or odoo.fields.Datetime.now()

Upvotes: 1

Related Questions