[FIX] sale_timesheet: fix pricing_type search method

Before this commit, when a project has no SOL set and has an employee
mapping, then the domain builds in the search method of `pricing_type`
is the following:
`[
    ('sale_line_id', '!=', False),
    ('sale_line_employee_ids', '!=', False),
    ('allow_billable', '=', True),
]`
The problem is we don't need to have a SOL set into the project to
define the pricing_type as employee rate. We should find the project
with at least an employee mapping set in that project.
Moreover, this commit fixes the domain built in the
`_search_pricing_type` method.

Some unit tests are added to check the domain built in the
`_search_pricing_type` method.

The issue has been found during the development of task-2680425

closes odoo/odoo#83874

X-original-commit: 490747c85f2d2e390a44e8f375e24dc3a8425409
Related: odoo/enterprise#23951
Signed-off-by: Laurent Stukkens (ltu) <ltu@odoo.com>
This commit is contained in:
Xavier BOL (xbo)
2022-02-02 21:49:05 +00:00
parent f4ca7290ac
commit 3d8d97e2bd
2 changed files with 45 additions and 5 deletions
+5 -5
View File
@@ -78,9 +78,9 @@ class Project(models.Model):
- value = 'employee_rate': [('sale_line_employee_ids', '!=', False), ('allow_billable', '=', True)]
- value is False: [('allow_billable', '=', False)]
- operator = '!=':
- value = 'task_rate': ['|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '!=', False), ('allow_billable', '=', True)]
- value = 'fixed_rate': ['|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '=', False), ('allow_billable', '=', True)]
- value = 'employee_rate': [('sale_line_employee_ids', '=', False), ('allow_billable', '=', True)]
- value = 'task_rate': ['|', '|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '!=', False), ('allow_billable', '=', False)]
- value = 'fixed_rate': ['|', '|', ('sale_line_employee_ids', '!=', False), ('sale_line_id', '=', False), ('allow_billable', '=', False)]
- value = 'employee_rate': ['|', ('sale_line_employee_ids', '=', False), ('allow_billable', '=', False)]
- value is False: [('allow_billable', '!=', False)]
:param operator: the supported operator is either '=' or '!='.
@@ -102,13 +102,13 @@ class Project(models.Model):
elif value == 'fixed_rate':
domain = [sol_cond, expression.NOT_OPERATOR, mapping_cond]
else: # value == 'employee_rate'
domain = [sol_cond, mapping_cond]
domain = [mapping_cond]
domain = expression.AND([domain, [('allow_billable', '=', True)]])
domain = expression.normalize_domain(domain)
if operator != '=':
domain.insert(0, expression.NOT_OPERATOR)
domain = expression.distribute_not(domain)
domain = expression.AND([domain, [('allow_billable', '=', True)]])
return domain
@api.depends('analytic_account_id', 'timesheet_ids')
@@ -23,6 +23,14 @@ class TestProjectPricingType(TestCommonSaleTimesheet):
project = self.project_non_billable
self.assertFalse(project.allow_billable, 'The allow_billable should be false if the project is non billable.')
self.assertFalse(project.pricing_type, 'The pricing type of this project should be equal to False since it is non billable.')
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'task_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'fixed_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'employee_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('=', False)))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'task_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'fixed_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'employee_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('!=', False)))
# 2) Set allow_billable to True and check if the pricing_type is equal to task_rate (if no SOL and no mappings)
project.write({
@@ -34,6 +42,14 @@ class TestProjectPricingType(TestCommonSaleTimesheet):
self.assertFalse(project.sale_line_id, 'The sales order item should be unset.')
self.assertFalse(project.sale_line_employee_ids, 'The employee mappings should be empty.')
self.assertEqual(project.pricing_type, 'task_rate', 'The pricing type should be equal to task_rate.')
self.assertTrue(project.filtered_domain(project._search_pricing_type('=', 'task_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'fixed_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'employee_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', False)))
self.assertFalse(project.filtered_domain(project._search_pricing_type('!=', 'task_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'fixed_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'employee_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', False)))
# 3) Set a customer and a SOL in the project and check if the pricing_type is equal to fixed_rate (project rate)
project.write({
@@ -44,6 +60,14 @@ class TestProjectPricingType(TestCommonSaleTimesheet):
self.assertEqual(project.sale_order_id, self.so, 'The sales order should be equal to the one set in the project.')
self.assertEqual(project.sale_line_id, self.so.order_line[0], 'The sales order item should be the one chosen.')
self.assertEqual(project.pricing_type, 'fixed_rate', 'The pricing type should be equal to fixed_rate since the project has a sales order item.')
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'task_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('=', 'fixed_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'employee_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', False)))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'task_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('!=', 'fixed_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'employee_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', False)))
# 4) Set a employee mapping and check if the pricing_type is equal to employee_rate
project.write({
@@ -55,6 +79,14 @@ class TestProjectPricingType(TestCommonSaleTimesheet):
self.assertEqual(len(project.sale_line_employee_ids), 1, 'The project should have an employee mapping.')
self.assertEqual(project.pricing_type, 'employee_rate', 'The pricing type should be equal to employee_rate since the project has an employee mapping.')
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'task_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'fixed_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('=', 'employee_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', False)))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'task_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'fixed_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('!=', 'employee_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', False)))
# Even if the project has no sales order item, since it has an employee mapping, the pricing type must be equal to employee_rate.
project.write({
@@ -63,3 +95,11 @@ class TestProjectPricingType(TestCommonSaleTimesheet):
self.assertFalse(project.sale_order_id, 'The sales order of the project should be empty.')
self.assertFalse(project.sale_line_id, 'The sales order item of the project should be empty.')
self.assertEqual(project.pricing_type, 'employee_rate', 'The pricing type should always be equal to employee_rate.')
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'task_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', 'fixed_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('=', 'employee_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('=', False)))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'task_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', 'fixed_rate')))
self.assertFalse(project.filtered_domain(project._search_pricing_type('!=', 'employee_rate')))
self.assertTrue(project.filtered_domain(project._search_pricing_type('!=', False)))