[IMP] digest, *: compute the KPI in batch

*: account, crm, hr_recruitement, im_livechat, point_of_sale,
   project, sale_management, website_sale

Purpose
=======
Now, the KPI are computed based on their `company_id` and not based
on the current company. If no company is set on the digest, we compute
it based on the current company.

The KPIs are computed based on their company. Most of the time, they
will all belong to the same company so we can improve the performance
by computing them in batch.

Task-2827996
See odoo/enterprise/pull/27658

Part-of: odoo/odoo#91945
This commit is contained in:
std-odoo
2023-04-05 15:11:06 +02:00
parent 1b76e2835d
commit df9e9ec261
18 changed files with 445 additions and 155 deletions
+26 -12
View File
@@ -14,19 +14,33 @@ class Digest(models.Model):
def _compute_kpi_account_total_revenue_value(self):
if not self.env.user.has_group('account.group_account_invoice'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
start, end, companies = self._get_kpi_compute_parameters()
self._cr.execute("""
SELECT line.company_id AS company, -SUM(line.balance) AS total
FROM account_move_line line
JOIN account_move move
ON move.id = line.move_id
JOIN account_account account
ON account.id = line.account_id
WHERE line.company_id = ANY(%s)
AND line.date > %s::DATE
AND line.date <= %s::DATE
AND account.internal_group = 'income'
AND move.state = 'posted'
GROUP BY line.company_id
""", [companies.ids, start, end])
result = self._cr.dictfetchall()
total_per_companies = {
values['company']: values['total']
for values in result
}
for record in self:
start, end, company = record._get_kpi_compute_parameters()
self._cr.execute('''
SELECT -SUM(line.balance)
FROM account_move_line line
JOIN account_move move ON move.id = line.move_id
JOIN account_account account ON account.id = line.account_id
WHERE line.company_id = %s AND line.date > %s::DATE AND line.date <= %s::DATE
AND account.internal_group = 'income'
AND move.state = 'posted'
''', [company.id, start, end])
query_res = self._cr.fetchone()
record.kpi_account_total_revenue_value = query_res and query_res[0] or 0.0
company = record.company_id or self.env.company
record.kpi_account_total_revenue_value = total_per_companies.get(company.id, 0)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+1
View File
@@ -22,6 +22,7 @@ from . import test_account_invoice_report
from . import test_account_move_line_tax_details
from . import test_account_journal_dashboard
from . import test_chart_template
from . import test_digest
from . import test_fiscal_position
from . import test_sequence_mixin
from . import test_tax
+66
View File
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.digest.tests.common import TestDigestCommon
from odoo.tools import mute_logger
from odoo.tests import tagged
@tagged('post_install')
class TestAccountDigest(TestDigestCommon):
@classmethod
@mute_logger('odoo.models.unlink')
def setUpClass(cls):
super().setUpClass()
account1 = cls.env['account.account'].search([('internal_group', '=', 'income')], limit=1)
account2 = cls.env['account.account'].search([('internal_group', '!=', 'income')], limit=1)
cls.env['account.journal'].with_company(cls.company_2).create({
'name': 'Test Journal',
'code': 'code',
'type': 'general',
})
comp2_account, comp2_account2 = cls.env['account.account'].create([{
'name': 'Account 1 Company 2',
'internal_group': 'income',
'company_id': cls.company_2.id,
'user_type_id': cls.env.ref('account.data_account_type_revenue').id,
'code': 'code_account_1_company_2',
}, {
'name': 'Account 2 Company 2',
'internal_group': 'expense',
'company_id': cls.company_2.id,
'user_type_id': cls.env.ref('account.data_account_type_expenses').id,
'code': 'code_account_2_company_2',
}])
moves = cls.env['account.move'].create({
'line_ids': [
(0, 0, {'debit': 5, 'credit': 0, 'account_id': account1.id}),
(0, 0, {'debit': 0, 'credit': 5, 'account_id': account2.id}),
(0, 0, {'debit': 8, 'credit': 0, 'account_id': account1.id}),
(0, 0, {'debit': 0, 'credit': 8, 'account_id': account2.id}),
],
})
moves |= cls.env['account.move'].with_company(cls.company_2).create({
'line_ids': [
(0, 0, {'debit': 0, 'credit': 2, 'account_id': comp2_account.id}),
(0, 0, {'debit': 2, 'credit': 0, 'account_id': comp2_account2.id}),
],
})
moves.state = 'posted'
def test_kpi_account_total_revenue_value(self):
self.assertEqual(int(self.digest_1.kpi_account_total_revenue_value), -13)
self.assertEqual(int(self.digest_2.kpi_account_total_revenue_value), -2)
self.assertEqual(int(self.digest_3.kpi_account_total_revenue_value), -13)
self.digest_3.invalidate_cache()
self.assertEqual(
int(self.digest_3.with_company(self.company_2).kpi_account_total_revenue_value),
-2,
msg='When no company is set, the KPI must be computed based on the current company',
)
+9 -16
View File
@@ -16,26 +16,19 @@ class Digest(models.Model):
def _compute_kpi_crm_lead_created_value(self):
if not self.env.user.has_group('sales_team.group_sale_salesman'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
record.kpi_crm_lead_created_value = self.env['crm.lead'].search_count([
('create_date', '>=', start),
('create_date', '<', end),
('company_id', '=', company.id)
])
self._calculate_company_based_kpi('crm.lead', 'kpi_crm_lead_created_value')
def _compute_kpi_crm_opportunities_won_value(self):
if not self.env.user.has_group('sales_team.group_sale_salesman'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
record.kpi_crm_opportunities_won_value = self.env['crm.lead'].search_count([
('type', '=', 'opportunity'),
('probability', '=', '100'),
('date_closed', '>=', start),
('date_closed', '<', end),
('company_id', '=', company.id)
])
self._calculate_company_based_kpi(
'crm.lead',
'kpi_crm_opportunities_won_value',
date_field='date_closed',
additional_domain=[('type', '=', 'opportunity'), ('probability', '=', '100')],
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+1
View File
@@ -14,6 +14,7 @@ from . import test_crm_lead_multicompany
from . import test_crm_lead_smart_calendar
from . import test_crm_ui
from . import test_crm_pls
from . import test_digest
from . import test_performances
from . import test_res_partner
from . import test_sales_team_ui
+55
View File
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from odoo.addons.digest.tests.common import TestDigestCommon
from odoo.tools import mute_logger
class TestCrmDigest(TestDigestCommon):
@classmethod
@mute_logger('odoo.models.unlink')
def setUpClass(cls):
super().setUpClass()
cls.env['crm.lead'].search([]).unlink()
cls.env['crm.lead'].create([{
'name': 'Lead 1',
'company_id': cls.company_1.id,
'probability': 100,
'type': 'opportunity',
'date_closed': datetime.now(),
}, {
'name': 'Lead 2',
'company_id': cls.company_1.id,
'probability': 90,
'type': 'opportunity',
'date_closed': datetime.now(),
}, {
'name': 'Lead 3',
'company_id': False,
'probability': 100,
'type': 'opportunity',
'date_closed': datetime.now(),
}, {
'name': 'Lead 4',
'company_id': cls.company_1.id,
'probability': 100,
'type': 'opportunity',
'date_closed': datetime.now() - timedelta(days=700),
}])
def test_kpi_crm_lead_created_value(self):
self.assertEqual(self.digest_1.kpi_crm_lead_created_value, 3)
self.assertEqual(self.digest_2.kpi_crm_lead_created_value, 0,
msg='This digest is in a different company')
self.assertEqual(self.digest_3.kpi_crm_lead_created_value, 3,
msg='This digest has no company, should take the current one')
self.digest_3.invalidate_recordset()
self.assertEqual(
self.digest_3.with_company(self.company_2).kpi_crm_lead_created_value,
0,
)
+64 -10
View File
@@ -12,6 +12,7 @@ from werkzeug.urls import url_join
from odoo import api, fields, models, tools, _
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
from odoo.exceptions import AccessError
from odoo.osv import expression
from odoo.tools.float_utils import float_round
_logger = logging.getLogger(__name__)
@@ -55,20 +56,33 @@ class Digest(models.Model):
digest.available_fields = ', '.join(kpis_values_fields)
def _get_kpi_compute_parameters(self):
return fields.Datetime.to_string(self._context.get('start_datetime')), fields.Datetime.to_string(self._context.get('end_datetime')), self.env.company
"""Get the parameters used to computed the KPI value."""
companies = self.company_id
if any(not digest.company_id for digest in self):
# No company: we will use the current company to compute the KPIs
companies |= self.env.company
return (
fields.Datetime.to_string(self.env.context.get('start_datetime')),
fields.Datetime.to_string(self.env.context.get('end_datetime')),
companies,
)
def _compute_kpi_res_users_connected_value(self):
for record in self:
start, end, company = record._get_kpi_compute_parameters()
user_connected = self.env['res.users'].search_count([('company_id', '=', company.id), ('login_date', '>=', start), ('login_date', '<', end)])
record.kpi_res_users_connected_value = user_connected
self._calculate_company_based_kpi(
'res.users',
'kpi_res_users_connected_value',
date_field='login_date',
)
def _compute_kpi_mail_message_total_value(self):
discussion_subtype_id = self.env.ref('mail.mt_comment').id
for record in self:
start, end, company = record._get_kpi_compute_parameters()
total_messages = self.env['mail.message'].search_count([('create_date', '>=', start), ('create_date', '<', end), ('subtype_id', '=', discussion_subtype_id), ('message_type', 'in', ['comment', 'email'])])
record.kpi_mail_message_total_value = total_messages
start, end, __ = self._get_kpi_compute_parameters()
self.kpi_mail_message_total_value = self.env['mail.message'].search_count([
('create_date', '>=', start),
('create_date', '<', end),
('subtype_id', '=', self.env.ref('mail.mt_comment').id),
('message_type', 'in', ('comment', 'email')),
])
@api.onchange('periodicity')
def _onchange_periodicity(self):
@@ -275,6 +289,9 @@ class Digest(models.Model):
if self._fields['%s_value' % field_name].type == 'monetary':
converted_amount = tools.format_decimalized_amount(compute_value)
compute_value = self._format_currency_amount(converted_amount, company.currency_id)
elif self._fields['%s_value' % field_name].type == 'float':
compute_value = "%.2f" % compute_value
kpi_values['kpi_col%s' % (col_index + 1)].update({
'value': compute_value,
'margin': margin,
@@ -374,6 +391,43 @@ class Digest(models.Model):
# FORMATTING / TOOLS
# ------------------------------------------------------------
def _calculate_company_based_kpi(self, model, digest_kpi_field, date_field='create_date',
additional_domain=None, sum_field=None):
"""Generic method that computes the KPI on a given model.
:param model: Model on which we will compute the KPI
This model must have a "company_id" field
:param digest_kpi_field: Field name on which we will write the KPI
:param date_field: Field used for the date range
:param additional_domain: Additional domain
:param sum_field: Field to sum to obtain the KPI,
if None it will count the number of records
"""
start, end, companies = self._get_kpi_compute_parameters()
base_domain = [
('company_id', 'in', companies.ids),
(date_field, '>=', start),
(date_field, '<', end),
]
if additional_domain:
base_domain = expression.AND([base_domain, additional_domain])
values = self.env[model]._read_group(
domain=base_domain,
fields=[f'{sum_field}:sum'] if sum_field else None,
groupby=['company_id'],
)
values_per_company = {
value['company_id'][0]: value[sum_field] if sum_field else value['company_id_count']
for value in values
}
for digest in self:
company = digest.company_id or self.env.company
digest[digest_kpi_field] = values_per_company.get(company.id, 0)
def _get_kpi_fields(self):
return [field_name for field_name, field in self._fields.items()
if field.type == 'boolean' and field_name.startswith(('kpi_', 'x_kpi_', 'x_studio_kpi_')) and self[field_name]
+78
View File
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
import random
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo import fields
from odoo.addons.mail.tests import common as mail_test
class TestDigestCommon(mail_test.MailCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company_1 = cls.env.company
cls.company_2 = cls.env['res.company'].create({'name': 'Digest Company 2'})
context = {
'start_datetime': datetime.now() - relativedelta(days=1),
'end_datetime': datetime.now() + relativedelta(days=1),
}
cls.all_digests = cls.env['digest.digest'].with_context(context).create([{
'name': 'Digest 1',
'company_id': cls.env.company.id,
'kpi_mail_message_total': True,
'kpi_res_users_connected': True,
'periodicity': 'daily',
}, {
'name': 'Digest 2',
'company_id': cls.company_2.id,
}, {
'name': 'Digest 3',
'company_id': False,
}])
cls.digest_1, cls.digest_2, cls.digest_3 = cls.all_digests
@classmethod
def _setup_messages(cls):
""" Remove all existing messages, then create a bunch of them on random
partners with the correct types in correct time-bucket:
- 3 in the previous 24h
- 5 more in the 6 days before that for a total of 8 in the previous week
- 7 more in the 20 days before *that* (because digest doc lies and is
based around weeks and months not days), for a total of 15 in the
previous month
"""
# regular employee can't necessarily access "private" addresses
partners = cls.env['res.partner'].search([('type', '!=', 'private')])
messages = cls.env['mail.message']
counter = itertools.count()
now = fields.Datetime.now()
for count, (low, high) in [
(3, (0 * 24, 1 * 24)),
(5, (1 * 24, 7 * 24)),
(7, (7 * 24, 27 * 24)),
]:
for __ in range(count):
create_date = now - relativedelta(hours=random.randint(low + 1, high - 1))
messages += random.choice(partners).message_post(
author_id=cls.partner_admin.id,
body=f"Awesome Partner! ({next(counter)})",
email_from=cls.partner_admin.email_formatted,
message_type='comment',
subtype_xmlid='mail.mt_comment',
# adjust top and bottom by 1h to avoid overlapping with the
# range limit and dropping out of the digest's selection thing
create_date=create_date,
)
cls.env.flush_all()
+21 -48
View File
@@ -1,21 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
import random
from dateutil.relativedelta import relativedelta
from lxml import html
from werkzeug.urls import url_encode, url_join
from odoo import fields, SUPERUSER_ID
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
from odoo.addons.mail.tests import common as mail_test
from odoo.addons.digest.tests.common import TestDigestCommon
from odoo.tests import tagged
from odoo.tests.common import users
class TestDigest(mail_test.MailCommon):
class TestDigest(TestDigestCommon):
@classmethod
def setUpClass(cls):
@@ -34,51 +31,27 @@ class TestDigest(mail_test.MailCommon):
# clean logs so that town down is activated
cls.env['res.users.log'].search([('create_uid', 'in', (cls.user_admin + cls.user_employee).ids)]).unlink()
cls.test_digest = cls.env['digest.digest'].create({
'kpi_mail_message_total': True,
'kpi_res_users_connected': True,
'name': "My Digest",
'periodicity': 'daily',
})
@users('admin')
def test_digest_kpi_res_users_connected_value(self):
self.env['res.users.log'].search([]).unlink()
# Sanity check
initial_values = self.all_digests.mapped('kpi_res_users_connected_value')
self.assertEqual(initial_values, [0, 0, 0])
@classmethod
def _setup_messages(cls):
""" Remove all existing messages, then create a bunch of them on random
partners with the correct types in correct time-bucket:
self.env['res.users.log'].with_user(self.user_employee).create({})
self.env['res.users.log'].with_user(self.user_admin).create({})
- 3 in the previous 24h
- 5 more in the 6 days before that for a total of 8 in the previous week
- 7 more in the 20 days before *that* (because digest doc lies and is
based around weeks and months not days), for a total of 15 in the
previous month
"""
# regular employee can't necessarily access "private" addresses
partners = cls.env['res.partner'].search([('type', '!=', 'private')])
messages = cls.env['mail.message']
counter = itertools.count()
self.all_digests.invalidate_recordset()
now = fields.Datetime.now()
for count, (low, high) in [(3, (0 * 24, 1 * 24)),
(5, (1 * 24, 7 * 24)),
(7, (7 * 24, 27 * 24)),
]:
for _ in range(count):
create_date = now - relativedelta(hours=random.randint(low + 1, high - 1))
messages += random.choice(partners).message_post(
author_id=cls.partner_admin.id,
body=f"Awesome Partner! ({next(counter)})",
email_from=cls.partner_admin.email_formatted,
message_type='comment',
subtype_xmlid='mail.mt_comment',
# adjust top and bottom by 1h to avoid overlapping with the
# range limit and dropping out of the digest's selection thing
create_date=create_date,
)
cls.env.flush_all()
self.assertEqual(self.digest_1.kpi_res_users_connected_value, 2)
self.assertEqual(self.digest_2.kpi_res_users_connected_value, 0,
msg='This KPI is in an other company')
self.assertEqual(self.digest_3.kpi_res_users_connected_value, 2,
msg='This KPI has no company, should take the current one')
@users('admin')
def test_digest_numbers(self):
digest = self.env['digest.digest'].browse(self.test_digest.ids)
digest = self.env['digest.digest'].browse(self.digest_1.ids)
digest._action_subscribe_users(self.user_employee)
# digest creates its mails in auto_delete mode so we need to capture
@@ -102,7 +75,7 @@ class TestDigest(mail_test.MailCommon):
@users('admin')
def test_digest_subscribe(self):
digest_user = self.test_digest.with_user(self.user_employee)
digest_user = self.digest_1.with_user(self.user_employee)
self.assertFalse(digest_user.is_subscribed)
# subscribe a user so at least one mail gets sent
@@ -115,7 +88,7 @@ class TestDigest(mail_test.MailCommon):
@users('admin')
def test_digest_tone_down(self):
digest = self.env['digest.digest'].browse(self.test_digest.ids)
digest = self.env['digest.digest'].browse(self.digest_1.ids)
digest._action_subscribe_users(self.user_employee)
# initial data
@@ -157,7 +130,7 @@ class TestDigest(mail_test.MailCommon):
""",
})
with self.mock_mail_gateway():
self.test_digest._action_send_to_user(self.user_employee)
self.digest_1._action_send_to_user(self.user_employee)
self.assertEqual(len(self._new_mails), 1, "A new Email should have been created")
sent_mail_body = html.fromstring(self._new_mails.body_html)
values_to_check = [
@@ -173,7 +146,7 @@ class TestDigest(mail_test.MailCommon):
@users('admin')
def test_digest_tone_down_wlogs(self):
digest = self.env['digest.digest'].browse(self.test_digest.ids)
digest = self.env['digest.digest'].browse(self.digest_1.ids)
digest._action_subscribe_users(self.user_employee)
# initial data
+5 -8
View File
@@ -14,14 +14,11 @@ class Digest(models.Model):
def _compute_kpi_hr_recruitment_new_colleagues_value(self):
if not self.env.user.has_group('hr_recruitment.group_hr_recruitment_user'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
new_colleagues = self.env['hr.employee'].search_count([
('create_date', '>=', start),
('create_date', '<', end),
('company_id', '=', company.id)
])
record.kpi_hr_recruitment_new_colleagues_value = new_colleagues
self._calculate_company_based_kpi(
'hr.employee',
'kpi_hr_recruitment_new_colleagues_value',
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+24 -25
View File
@@ -16,35 +16,34 @@ class Digest(models.Model):
def _compute_kpi_livechat_rating_value(self):
channels = self.env['mail.channel'].search([('livechat_operator_id', '=', self.env.user.partner_id.id)])
for record in self:
start, end, company = record._get_kpi_compute_parameters()
domain = [
('create_date', '>=', start), ('create_date', '<', end),
('rated_partner_id', '=', self.env.user.partner_id.id)
]
ratings = channels.rating_get_grades(domain)
record.kpi_livechat_rating_value = ratings['great'] * 100 / sum(ratings.values()) if sum(ratings.values()) else 0
start, end, __ = self._get_kpi_compute_parameters()
domain = [
('create_date', '>=', start),
('create_date', '<', end),
('rated_partner_id', '=', self.env.user.partner_id.id)
]
ratings = channels.rating_get_grades(domain)
self.kpi_livechat_rating_value = (
ratings['great'] * 100 / sum(ratings.values())
if sum(ratings.values()) else 0
)
def _compute_kpi_livechat_conversations_value(self):
for record in self:
start, end, company = record._get_kpi_compute_parameters()
record.kpi_livechat_conversations_value = self.env['mail.channel'].search_count([
('channel_type', '=', 'livechat'),
('livechat_operator_id', '=', self.env.user.partner_id.id),
('create_date', '>=', start), ('create_date', '<', end)
])
start, end, __ = self._get_kpi_compute_parameters()
self.kpi_livechat_conversations_value = self.env['mail.channel'].search_count([
('channel_type', '=', 'livechat'),
('livechat_operator_id', '=', self.env.user.partner_id.id),
('create_date', '>=', start), ('create_date', '<', end),
])
def _compute_kpi_livechat_response_value(self):
for record in self:
start, end, company = record._get_kpi_compute_parameters()
response_time = self.env['im_livechat.report.operator'].sudo()._read_group([
('start_date', '>=', start), ('start_date', '<', end),
('partner_id', '=', self.env.user.partner_id.id)], ['partner_id', 'time_to_answer'], ['partner_id'])
record.kpi_livechat_response_value = sum(
response['time_to_answer']
for response in response_time
if response['time_to_answer'] > 0
)
start, end, __ = self._get_kpi_compute_parameters()
response_time = self.env['im_livechat.report.channel'].sudo()._read_group([
('start_date', '>=', start),
('start_date', '<', end),
('partner_id', '=', self.env.user.partner_id.id)
], ['time_to_answer:avg'], [])
self.kpi_livechat_response_value = response_time[0]['time_to_answer']
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+1
View File
@@ -4,6 +4,7 @@
from . import chatbot_common
from . import test_chatbot_form_ui
from . import test_chatbot_internals
from . import test_digest
from . import test_get_mail_channel
from . import test_im_livechat_report
from . import test_im_livechat_support_page
+63
View File
@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.digest.tests.common import TestDigestCommon
from odoo.tools import mute_logger
from odoo.tests import tagged
@tagged('post_install')
class TestLiveChatDigest(TestDigestCommon):
@classmethod
@mute_logger('odoo.models.unlink')
def setUpClass(cls):
super().setUpClass()
other_partner = cls.env['res.partner'].create({'name': 'Other Partner'})
cls.channels = cls.env['mail.channel'].create([{
'name': 'Channel 1',
'livechat_operator_id': cls.env.user.partner_id.id,
'channel_type': 'livechat',
}, {
'name': 'Channel 2',
'livechat_operator_id': cls.env.user.partner_id.id,
'channel_type': 'livechat',
}, {
'name': 'Channel 3',
'livechat_operator_id': other_partner.id,
'channel_type': 'livechat',
}])
cls.env['rating.rating'].search([]).unlink()
cls.env['rating.rating'].create([{
'rated_partner_id': cls.env.user.partner_id.id,
'res_id': cls.channels[0].id,
'res_model_id': cls.env['ir.model']._get('mail.channel').id,
'consumed': True,
'rating': 5,
}, {
'rated_partner_id': cls.env.user.partner_id.id,
'res_id': cls.channels[0].id,
'res_model_id': cls.env['ir.model']._get('mail.channel').id,
'consumed': True,
'rating': 0,
}, {
'rated_partner_id': cls.env.user.partner_id.id,
'res_id': cls.channels[0].id,
'res_model_id': cls.env['ir.model']._get('mail.channel').id,
'consumed': True,
'rating': 3,
}, {
'rated_partner_id': cls.env.user.partner_id.id,
'res_id': cls.channels[0].id,
'res_model_id': cls.env['ir.model']._get('mail.channel').id,
'consumed': True,
'rating': 3,
}])
def test_kpi_livechat_rating_value(self):
# 1/3 of the ratings have 5/5 note (0 are ignored)
self.assertEqual(round(self.digest_1.kpi_livechat_rating_value, 2), 33.33)
+8 -8
View File
@@ -14,14 +14,14 @@ class Digest(models.Model):
def _compute_kpi_pos_total_value(self):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
record.kpi_pos_total_value = sum(self.env['pos.order'].search([
('date_order', '>=', start),
('date_order', '<', end),
('state', 'not in', ['draft', 'cancel', 'invoiced']),
('company_id', '=', company.id)
]).mapped('amount_total'))
self._calculate_company_based_kpi(
'pos.order',
'kpi_pos_total_value',
date_field='date_order',
additional_domain=[('state', 'not in', ['draft', 'cancel', 'invoiced'])],
sum_field='amount_total',
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+6 -9
View File
@@ -14,15 +14,12 @@ class Digest(models.Model):
def _compute_project_task_opened_value(self):
if not self.env.user.has_group('project.group_project_user'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
record.kpi_project_task_opened_value = self.env['project.task'].search_count([
('stage_id.fold', '=', False),
('create_date', '>=', start),
('create_date', '<', end),
('company_id', '=', company.id),
('project_id', '!=', False),
])
self._calculate_company_based_kpi(
'project.task',
'kpi_project_task_opened_value',
additional_domain=[('stage_id.fold', '=', False), ('project_id', '!=', False)],
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+1 -1
View File
@@ -134,7 +134,7 @@ class RatingMixin(models.AbstractModel):
if domain:
base_domain += domain
rg_data = self.env['rating.rating'].read_group(base_domain, ['rating'], ['rating', 'res_id'])
# init dict with all posible rate value, except 0 (no value for the rating)
# init dict with all possible rate value, except 0 (no value for the rating)
values = dict.fromkeys(range(1, 6), 0)
for rating_rg in rg_data:
rating_val_round = float_round(rating_rg['rating'], precision_digits=1)
+8 -8
View File
@@ -14,14 +14,14 @@ class Digest(models.Model):
def _compute_kpi_sale_total_value(self):
if not self.env.user.has_group('sales_team.group_sale_salesman_all_leads'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
all_channels_sales = self.env['sale.report']._read_group([
('date', '>=', start),
('date', '<', end),
('state', 'not in', ['draft', 'cancel', 'sent']),
('company_id', '=', company.id)], ['price_total'], ['company_id'])
record.kpi_all_sale_total_value = sum([channel_sale['price_total'] for channel_sale in all_channels_sales])
self._calculate_company_based_kpi(
'sale.report',
'kpi_all_sale_total_value',
date_field='date',
additional_domain=[('state', 'not in', ['draft', 'cancel', 'sent'])],
sum_field='price_total',
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)
+8 -10
View File
@@ -14,16 +14,14 @@ class Digest(models.Model):
def _compute_kpi_website_sale_total_value(self):
if not self.env.user.has_group('sales_team.group_sale_salesman_all_leads'):
raise AccessError(_("Do not have access, skip this data for user's digest email"))
for record in self:
start, end, company = record._get_kpi_compute_parameters()
confirmed_website_sales = self.env['sale.order'].search([
('date_order', '>=', start),
('date_order', '<', end),
('state', 'not in', ['draft', 'cancel', 'sent']),
('website_id', '!=', False),
('company_id', '=', company.id)
])
record.kpi_website_sale_total_value = sum(confirmed_website_sales.mapped('amount_total'))
self._calculate_company_based_kpi(
'sale.order',
'kpi_website_sale_total_value',
date_field='date_order',
additional_domain=[('state', 'not in', ['draft', 'cancel', 'sent']), ('website_id', '!=', False)],
sum_field='amount_total',
)
def _compute_kpis_actions(self, company, user):
res = super(Digest, self)._compute_kpis_actions(company, user)