From 60dc29e0df652cce15606bae68d11008e6a8f85a Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 27 Sep 2022 14:21:34 +0000 Subject: [PATCH] [FIX] hr_timesheet: _get_view dependent on company.timesheet_encode_uom_id `hr_timesheet`, and other timesheet related modules, overrides `_get_view` to change some timesheet time field label from "Hours" to "Days" according if the company uses hours as timesheet encoding UOM or days. Since odoo/odoo#99417, and the fact back-end views are cached, keys on which the model views depends on for the cache must be added through an override of the method `_get_view_cache_key`. Without this revision, if a database has one company using hours as timesheet encoding uom, and a second company using days as timesheet encoding uom, the label for one of the two company could be wrong according to which user/company accessed first the view, for which company the view has been put in the cache first. These overrides of `_get_view` should be replaced by another mechanism, for instance by a Javascript widget, as this mechanism doesn't really make sense when browsing multiple companies at a time (selecting multiple companies in the companies dropdown menu). When going from one record to another with the left/right arrows, the company can depend from one record to another, and therefore the label should change from one record to another according to its company, which is not the case with the current implementation. It could also be achieved by setting two different labels, with an invisible attrs based on a related field of company.timesheet_encode_uom_id. However, as we are close to release, and 16.0 is already frozen, no unstable changes should be done, hence I restore the behavior which was there before odoo/odoo#99417. closes odoo/odoo#101964 X-original-commit: b2a9868b4a0db5a091a1faae38b7d4de29dfd2f4 Related: odoo/enterprise#32198 Signed-off-by: Laurent Stukkens (ltu) Signed-off-by: Denis Ledoux (dle) --- addons/hr_timesheet/models/hr_timesheet.py | 7 +++++ addons/hr_timesheet/models/project.py | 14 ++++++++++ addons/hr_timesheet/report/project_report.py | 7 +++++ .../report/timesheets_analysis_report.py | 7 +++++ addons/hr_timesheet/tests/test_timesheet.py | 27 +++++++++++++++++++ .../tests/test_sale_timesheet.py | 9 +++++++ 6 files changed, 71 insertions(+) diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index 7b7051a9abb..0da0565304d 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -214,6 +214,13 @@ class AccountAnalyticLine(models.Model): self.filtered(lambda t: t.project_id)._timesheet_postprocess(values) return result + @api.model + def _get_view_cache_key(self, view_id=None, view_type='form', **options): + """The override of _get_view changing the time field labels according to the company timesheet encoding UOM + makes the view cache dependent on the company timesheet encoding uom""" + key = super()._get_view_cache_key(view_id, view_type, **options) + return key + (self.env.company.timesheet_encode_uom_id,) + @api.model def _get_view(self, view_id=None, view_type='form', **options): """ Set the correct label for `unit_amount`, depending on company UoM """ diff --git a/addons/hr_timesheet/models/project.py b/addons/hr_timesheet/models/project.py index a36fb8f921f..f4942e1672c 100644 --- a/addons/hr_timesheet/models/project.py +++ b/addons/hr_timesheet/models/project.py @@ -80,6 +80,13 @@ class Project(models.Model): operator_new = 'not inselect' return [('id', operator_new, (query, ()))] + @api.model + def _get_view_cache_key(self, view_id=None, view_type='form', **options): + """The override of _get_view changing the time field labels according to the company timesheet encoding UOM + makes the view cache dependent on the company timesheet encoding uom""" + key = super()._get_view_cache_key(view_id, view_type, **options) + return key + (self.env.company.timesheet_encode_uom_id,) + @api.model def _get_view(self, view_id=None, view_type='form', **options): arch, view = super()._get_view(view_id, view_type, **options) @@ -373,6 +380,13 @@ class Task(models.Model): return list(name_mapping.items()) return super().name_get() + @api.model + def _get_view_cache_key(self, view_id=None, view_type='form', **options): + """The override of _get_view changing the time field labels according to the company timesheet encoding UOM + makes the view cache dependent on the company timesheet encoding uom""" + key = super()._get_view_cache_key(view_id, view_type, **options) + return key + (self.env.company.timesheet_encode_uom_id,) + @api.model def _get_view(self, view_id=None, view_type='form', **options): """ Set the correct label for `unit_amount`, depending on company UoM """ diff --git a/addons/hr_timesheet/report/project_report.py b/addons/hr_timesheet/report/project_report.py index fc40365952a..e59c680d8a8 100644 --- a/addons/hr_timesheet/report/project_report.py +++ b/addons/hr_timesheet/report/project_report.py @@ -32,6 +32,13 @@ class ReportProjectTaskUser(models.Model): """ return super(ReportProjectTaskUser, self)._group_by() + group_by_append + @api.model + def _get_view_cache_key(self, view_id=None, view_type='form', **options): + """The override of _get_view changing the time field labels according to the company timesheet encoding UOM + makes the view cache dependent on the company timesheet encoding uom""" + key = super()._get_view_cache_key(view_id, view_type, **options) + return key + (self.env.company.timesheet_encode_uom_id,) + @api.model def _get_view(self, view_id=None, view_type='form', **options): arch, view = super()._get_view(view_id, view_type, **options) diff --git a/addons/hr_timesheet/report/timesheets_analysis_report.py b/addons/hr_timesheet/report/timesheets_analysis_report.py index 799390a1d99..3487a0c6ed5 100644 --- a/addons/hr_timesheet/report/timesheets_analysis_report.py +++ b/addons/hr_timesheet/report/timesheets_analysis_report.py @@ -56,6 +56,13 @@ class TimesheetsAnalysisReport(models.Model): def _where(self): return "WHERE A.project_id IS NOT NULL" + @api.model + def _get_view_cache_key(self, view_id=None, view_type='form', **options): + """The override of _get_view changing the time field labels according to the company timesheet encoding UOM + makes the view cache dependent on the company timesheet encoding uom""" + key = super()._get_view_cache_key(view_id, view_type, **options) + return key + (self.env.company.timesheet_encode_uom_id,) + @api.model def _get_view(self, view_id=None, view_type='form', **options): arch, view = super()._get_view(view_id, view_type, **options) diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py index acf756a49bb..2a2e5b2710c 100644 --- a/addons/hr_timesheet/tests/test_timesheet.py +++ b/addons/hr_timesheet/tests/test_timesheet.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from lxml import etree + from odoo.fields import Command from odoo.tests.common import TransactionCase from odoo.exceptions import AccessError, UserError, ValidationError @@ -90,6 +92,20 @@ class TestCommonTimesheet(TransactionCase): 'user_id': cls.user_manager.id, }) + def assert_get_view_timesheet_encode_uom(self, expected): + companies = self.env['res.company'].create([ + {'name': 'foo', 'timesheet_encode_uom_id': self.env.ref('uom.product_uom_hour').id}, + {'name': 'bar', 'timesheet_encode_uom_id': self.env.ref('uom.product_uom_day').id}, + ]) + for view_xml_id, xpath_expr, expected_labels in expected: + for company, expected_label in zip(companies, expected_labels): + view = self.env.ref(view_xml_id) + view = self.env[view.model].with_company(company).get_view(view.id, view.type) + tree = etree.fromstring(view['arch']) + field_node = tree.xpath(xpath_expr)[0] + self.assertEqual(field_node.get('string'), expected_label) + + class TestTimesheet(TestCommonTimesheet): def setUp(self): @@ -492,3 +508,14 @@ class TestTimesheet(TestCommonTimesheet): with self.assertRaises(UserError): timesheet.employee_id = self.empl_employee2 + + def test_get_view_timesheet_encode_uom(self): + """ Test the label of timesheet time spent fields according to the company encoding timesheet uom """ + self.assert_get_view_timesheet_encode_uom([ + ('hr_timesheet.hr_timesheet_line_form', '//field[@name="unit_amount"]', ['Hours Spent', 'Days Spent']), + ('hr_timesheet.project_invoice_form', '//field[@name="allocated_hours"]', [None, 'Allocated Days']), + ('hr_timesheet.view_task_form2_inherited', '//field[@name="unit_amount"]', ['Hours Spent', 'Days Spent']), + ('hr_timesheet.view_task_tree2_inherited', '//field[@name="planned_hours"]', [None, 'Initially Planned Days']), + ('hr_timesheet.view_task_project_user_graph_inherited', '//field[@name="hours_planned"]', [None, 'Planned Days']), + ('hr_timesheet.timesheets_analysis_report_pivot_employee', '//field[@name="unit_amount"]', [None, 'Days Spent']), + ]) diff --git a/addons/sale_timesheet/tests/test_sale_timesheet.py b/addons/sale_timesheet/tests/test_sale_timesheet.py index bc4a5ba7172..ac7dab83987 100644 --- a/addons/sale_timesheet/tests/test_sale_timesheet.py +++ b/addons/sale_timesheet/tests/test_sale_timesheet.py @@ -5,6 +5,7 @@ from datetime import date, timedelta from odoo.fields import Date from odoo.tools import float_is_zero from odoo.exceptions import UserError +from odoo.addons.hr_timesheet.tests.test_timesheet import TestCommonTimesheet from odoo.addons.sale_timesheet.tests.common import TestCommonSaleTimesheet from odoo.tests import tagged @@ -724,3 +725,11 @@ class TestSaleTimesheet(TestCommonSaleTimesheet): move.with_context(check_move_validity=False).line_ids[0].unlink() self.assertFalse(analytic_line.timesheet_invoice_id, "The timesheet should have been unlinked from move") + + +class TestSaleTimesheetView(TestCommonTimesheet): + def test_get_view_timesheet_encode_uom(self): + """ Test the label of timesheet time spent fields according to the company encoding timesheet uom """ + self.assert_get_view_timesheet_encode_uom([ + ('sale_timesheet.project_project_view_form', '//field[@name="display_cost"]', [None, 'Daily Cost']), + ])