Odoo Mindscape. Understand Odoo faster. Explore modules, fields, dependencies and version differences in one place instead of digging through the source code.

Module: Discuss

Entity: mail.tracking.duration.mixin

Compute Method: _compute_duration_tracking

View 19.0 View 20.0
Inputs Method Outputs
  • duration_tracking
"""
-         Computes duration_tracking, a Json field stored as { <many2one_id (str)>: <duration_spent_in_seconds (int)> }
+         Track how long a record stays in different stages.

-             e.g. {"1": 1230, "2": 2220, "5": 14}
+         This method calculates elapsed time since the last stage change and stores the
+         information in the `duration_tracking` dictionary.

-         `_track_duration_field` must be present in the model that uses the mixin to specify on what
-         field to compute time spent. Besides, tracking must be activated for that field.
+         The dictionary keys are:

-             e.g.
-             class MyModel(models.Model):
-                 _name = 'my.model'
-                 _track_duration_field = "tracked_field"
+         *   d (datetime):
+                 The exact UTC datetime the record added the current state.
+         *   s (stage id):
+                 The unique ID of the *current* stage the record is in.

-                 tracked_field = fields.Many2one('tracked.model', tracking=True)
+         <stage_id> (int):
+                 A running total of accumulated time (in minutes) spent in previous states, mapping
+                   stage IDs to minutes.
+ 
+         Example:
+             {"d":"2025-11-21 15:34:28","s":3, "1":172814,"2":86401 , '0': 12 }
        """
- field = self.env['ir.model.fields'].sudo().search_fetch([
-             ('model', '=', self._name),
-             ('name', '=', self._track_duration_field),
-         ], ['id'], limit=1)
- if (
-             self._track_duration_field not in self._track_get_fields()
-             or self._fields[self._track_duration_field].type != 'many2one'
-         ):
-             self.duration_tracking = False
-             raise ValueError(_(
-                 'Field ā€œ%(field)sā€ on model ā€œ%(model)sā€ must be of type Many2one and have tracking=True for the computation of duration.',
-                 field=self._track_duration_field, model=self._name
-             ))
- if self.ids:
-             self.env['mail.tracking.value'].flush_model()
-             self.env['mail.message'].flush_model()
-             trackings = self.env.execute_query_dict(SQL("""
-                    SELECT m.res_id,
-                           v.create_date,
-                           v.old_value_integer
-                      FROM mail_tracking_value v
-                 LEFT JOIN mail_message m
-                        ON m.id = v.mail_message_id
-                       AND v.field_id = %(field_id)s
-                     WHERE m.model = %(model_name)s
-                       AND m.res_id IN %(record_ids)s
-                  ORDER BY v.id
-                 """,
-                 field_id=field.id, model_name=self._name, record_ids=tuple(self.ids),
-             ))
-         else:
-             trackings = []
+ now = self.env.cr.now()
for record in self:
-             record_trackings = [tracking for tracking in trackings if tracking['res_id'] == record._origin.id]
-             record.duration_tracking = record._get_duration_from_tracking(record_trackings)
+             duration_dict = record.duration_tracking or {}
+             last_stage_id = duration_dict.get('s')
+             if last_stage_id is not None and duration_dict.get('d'):
+                 old_update = fields.Datetime.to_datetime(duration_dict['d'])
+                 duration_dict[str(last_stage_id)] = duration_dict.get(str(last_stage_id), 0) + int((now - old_update) / timedelta(minutes=1))
+             # update last stage / datetime
+             duration_dict['s'] = record[record._track_duration_field].id or 0
+             duration_dict['d'] = self.env.cr.now()
+             record.duration_tracking = duration_dict
  • duration_tracking

Back to entity