Purpose of this commit ====================== The Burndown Chart report was very slow on big databases as it was not possible for Postgresql to optimize the query as it was based on a view that was using several generate series. This commit aims to improve the performance by injecting the constraints at a lower level than it was in the past, lowering the amount of data processed in the higher level of the query. /!\ Important note ------------------ Overwriting the `read_group_raw` is really not a good practice and should be avoided in most case. If you fall on this implementation by grepping the source code, please be advised that this is not the right way of doing things. Implementation details ---------------------- - The report is now run by generating the `SQL` that is executed by the `read_group_raw`. This allows inserting `SQL` constraints at a lower level and simnifically improves performance. As there is no other way to do it, the code is unfortunately a modified copy of the actual `read_group_raw`. - The pivot view has been removed as it had no meaning and was creating confusing data. - The `Group By` menu has been limited to `stage_id` and `date` as bringing more data trough the different `GROUP BY` statements up to the higher level is costly. Further more, additional `Group By` did not bring added value as the Chart was less readable. - The JS code has been adapted in order to force a group by both `stage_id` and `date` so that the date displayed is always making sense. - The sort ascending and descending options have been removed as creating confusing data. - The compare with previous period has also been removed as the chart only really make sense when seen chronologically. - A lot of tests have been added in order to ensure that changes that would be harmful for the report will trigger test fails. task-2845729 Co-authored-by: Yannick Tivisse <yti@odoo.com>
137 lines
6.2 KiB
Python
137 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import datetime
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class MailTracking(models.Model):
|
|
_name = 'mail.tracking.value'
|
|
_description = 'Mail Tracking Value'
|
|
_rec_name = 'field'
|
|
_order = 'tracking_sequence asc'
|
|
|
|
field = fields.Many2one('ir.model.fields', required=True, readonly=1, index=True, ondelete='cascade')
|
|
field_desc = fields.Char('Field Description', required=True, readonly=1)
|
|
field_type = fields.Char('Field Type')
|
|
field_groups = fields.Char(compute='_compute_field_groups')
|
|
|
|
old_value_integer = fields.Integer('Old Value Integer', readonly=1)
|
|
old_value_float = fields.Float('Old Value Float', readonly=1)
|
|
old_value_monetary = fields.Float('Old Value Monetary', readonly=1)
|
|
old_value_char = fields.Char('Old Value Char', readonly=1)
|
|
old_value_text = fields.Text('Old Value Text', readonly=1)
|
|
old_value_datetime = fields.Datetime('Old Value DateTime', readonly=1)
|
|
|
|
new_value_integer = fields.Integer('New Value Integer', readonly=1)
|
|
new_value_float = fields.Float('New Value Float', readonly=1)
|
|
new_value_monetary = fields.Float('New Value Monetary', readonly=1)
|
|
new_value_char = fields.Char('New Value Char', readonly=1)
|
|
new_value_text = fields.Text('New Value Text', readonly=1)
|
|
new_value_datetime = fields.Datetime('New Value Datetime', readonly=1)
|
|
|
|
currency_id = fields.Many2one('res.currency', 'Currency', readonly=True, ondelete='set null',
|
|
help="Used to display the currency when tracking monetary values")
|
|
|
|
mail_message_id = fields.Many2one('mail.message', 'Message ID', required=True, index=True, ondelete='cascade')
|
|
|
|
tracking_sequence = fields.Integer('Tracking field sequence', readonly=1, default=100)
|
|
|
|
def _compute_field_groups(self):
|
|
for tracking in self:
|
|
model = self.env[tracking.mail_message_id.model]
|
|
field = model._fields.get(tracking.field.name)
|
|
tracking.field_groups = field.groups if field else 'base.group_system'
|
|
|
|
@api.model
|
|
def create_tracking_values(self, initial_value, new_value, col_name, col_info, tracking_sequence, model_name):
|
|
tracked = True
|
|
|
|
field = self.env['ir.model.fields']._get(model_name, col_name)
|
|
if not field:
|
|
return
|
|
|
|
values = {'field': field.id, 'field_desc': col_info['string'], 'field_type': col_info['type'], 'tracking_sequence': tracking_sequence}
|
|
|
|
if col_info['type'] in ['integer', 'float', 'char', 'text', 'datetime', 'monetary']:
|
|
values.update({
|
|
'old_value_%s' % col_info['type']: initial_value,
|
|
'new_value_%s' % col_info['type']: new_value
|
|
})
|
|
elif col_info['type'] == 'date':
|
|
values.update({
|
|
'old_value_datetime': initial_value and fields.Datetime.to_string(datetime.combine(fields.Date.from_string(initial_value), datetime.min.time())) or False,
|
|
'new_value_datetime': new_value and fields.Datetime.to_string(datetime.combine(fields.Date.from_string(new_value), datetime.min.time())) or False,
|
|
})
|
|
elif col_info['type'] == 'boolean':
|
|
values.update({
|
|
'old_value_integer': initial_value,
|
|
'new_value_integer': new_value
|
|
})
|
|
elif col_info['type'] == 'selection':
|
|
values.update({
|
|
'old_value_char': initial_value and dict(col_info['selection']).get(initial_value, initial_value) or '',
|
|
'new_value_char': new_value and dict(col_info['selection'])[new_value] or ''
|
|
})
|
|
elif col_info['type'] == 'many2one':
|
|
values.update({
|
|
'old_value_integer': initial_value and initial_value.id or 0,
|
|
'new_value_integer': new_value and new_value.id or 0,
|
|
'old_value_char': initial_value and initial_value.sudo().name_get()[0][1] or '',
|
|
'new_value_char': new_value and new_value.sudo().name_get()[0][1] or ''
|
|
})
|
|
else:
|
|
tracked = False
|
|
|
|
if tracked:
|
|
return values
|
|
return {}
|
|
|
|
def _tracking_value_format(self):
|
|
tracking_values = [{
|
|
'changedField': tracking.field_desc,
|
|
'id': tracking.id,
|
|
'newValue': [('insert-and-replace', {
|
|
'currencyId': tracking.currency_id.id,
|
|
'fieldType': tracking.field_type,
|
|
'value': tracking._get_new_display_value()[0]})],
|
|
'oldValue': [('insert-and-replace', {
|
|
'currencyId': tracking.currency_id.id,
|
|
'fieldType': tracking.field_type,
|
|
'value': tracking._get_old_display_value()[0]})],
|
|
} for tracking in self]
|
|
return tracking_values
|
|
|
|
def _get_display_value(self, prefix):
|
|
assert prefix in ('new', 'old')
|
|
result = []
|
|
for record in self:
|
|
if record.field_type in ['integer', 'float', 'char', 'text', 'monetary']:
|
|
result.append(record[f'{prefix}_value_{record.field_type}'])
|
|
elif record.field_type == 'datetime':
|
|
if record[f'{prefix}_value_datetime']:
|
|
new_datetime = record[f'{prefix}_value_datetime']
|
|
result.append(f'{new_datetime}Z')
|
|
else:
|
|
result.append(record[f'{prefix}_value_datetime'])
|
|
elif record.field_type == 'date':
|
|
if record[f'{prefix}_value_datetime']:
|
|
new_date = record[f'{prefix}_value_datetime']
|
|
result.append(fields.Date.to_string(new_date))
|
|
else:
|
|
result.append(record[f'{prefix}_value_datetime'])
|
|
elif record.field_type == 'boolean':
|
|
result.append(bool(record[f'{prefix}_value_integer']))
|
|
else:
|
|
result.append(record[f'{prefix}_value_char'])
|
|
return result
|
|
|
|
def _get_old_display_value(self):
|
|
# grep : # old_value_integer | old_value_datetime | old_value_char
|
|
return self._get_display_value('old')
|
|
|
|
def _get_new_display_value(self):
|
|
# grep : # new_value_integer | new_value_datetime | new_value_char
|
|
return self._get_display_value('new')
|