From ffcf2ee1a3185ef73db93bfd95625844506692c5 Mon Sep 17 00:00:00 2001 From: "Gauthier Wala (gawa)" Date: Tue, 16 Apr 2024 16:19:49 +0200 Subject: [PATCH] [FIX] analytic: plan's applicability don't work without companies If you create an applicability and remove the company field, they are never used. An applicability like this should be valid for all companies. We put a 0.5 value for the company field so an applicability so it has a lesser priority than other fields. Same idea as the distribution models. opw-3847415 closes odoo/odoo#162152 Signed-off-by: Olivier Colson (oco) --- addons/account/tests/test_account_analytic.py | 28 +++++++++++++++++++ addons/analytic/models/analytic_plan.py | 14 ++++++++-- addons/analytic/tests/test_plan_operations.py | 22 +++++++++------ 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/addons/account/tests/test_account_analytic.py b/addons/account/tests/test_account_analytic.py index 0ae4a2f07a4..f320022390a 100644 --- a/addons/account/tests/test_account_analytic.py +++ b/addons/account/tests/test_account_analytic.py @@ -264,3 +264,31 @@ class TestAccountAnalyticAccount(AccountTestInvoicingCommon): self.assertEqual(self.analytic_account_a.invoice_count, 1) self.analytic_account_a._compute_vendor_bill_count() self.assertEqual(self.analytic_account_a.vendor_bill_count, 1) + + def test_applicability_score(self): + """ Tests which applicability is chosen if several ones are valid """ + applicability_without_company, applicability_with_company = self.env['account.analytic.applicability'].create([ + { + 'business_domain': 'invoice', + 'product_categ_id': self.product_a.categ_id.id, + 'applicability': 'mandatory', + 'analytic_plan_id': self.default_plan.id, + 'company_id': False, + }, + { + 'business_domain': 'invoice', + 'applicability': 'unavailable', + 'analytic_plan_id': self.default_plan.id, + 'company_id': self.env.company.id, + }, + ]) + + applicability = self.default_plan._get_applicability(business_domain='invoice', company_id=self.env.company.id, product=self.product_a.id) + self.assertEqual(applicability, 'mandatory', "product takes precedence over company") + + # If the model that asks for a validation does not have a company_id, + # the score shouldn't take into account the company of the applicability + score = applicability_without_company._get_score(business_domain='invoice', product=self.product_a.id) + self.assertEqual(score, 2) + score = applicability_with_company._get_score(business_domain='invoice', product=self.product_a.id) + self.assertEqual(score, 1) diff --git a/addons/analytic/models/analytic_plan.py b/addons/analytic/models/analytic_plan.py index 4d25a3fe9a1..04f22d628a0 100644 --- a/addons/analytic/models/analytic_plan.py +++ b/addons/analytic/models/analytic_plan.py @@ -233,7 +233,12 @@ class AccountAnalyticPlan(models.Model): else: score = 0 applicability = self.default_applicability - for applicability_rule in self.applicability_ids.filtered(lambda rule: rule.company_id == self.env.company): + for applicability_rule in self.applicability_ids.filtered( + lambda rule: + not rule.company_id + or not kwargs.get('company_id') + or rule.company_id.id == kwargs.get('company_id') + ): score_rule = applicability_rule._get_score(**kwargs) if score_rule > score: applicability = applicability_rule.applicability @@ -304,7 +309,10 @@ class AccountAnalyticApplicability(models.Model): def _get_score(self, **kwargs): """ Gives the score of an applicability with the parameters of kwargs """ self.ensure_one() + # 0.5 is because company is less important than other fields for an equal number of valid fields + # No company on the applicability and the kwargs together are not considered a more fitting rule + score = 0.5 if self.company_id and kwargs.get('company_id') else 0 if not kwargs.get('business_domain'): - return 0 + return score else: - return 1 if kwargs.get('business_domain') == self.business_domain else -1 + return score + 1 if kwargs.get('business_domain') == self.business_domain else -1 diff --git a/addons/analytic/tests/test_plan_operations.py b/addons/analytic/tests/test_plan_operations.py index 408afd28810..3054d4bef69 100644 --- a/addons/analytic/tests/test_plan_operations.py +++ b/addons/analytic/tests/test_plan_operations.py @@ -72,23 +72,29 @@ class TestAnalyticPlanOperations(TransactionCase): company_2 = self.env['res.company'].create({ 'name': 'company_2', }) - mandatory_plan = self.env['account.analytic.plan'].create([{ - 'name': 'Mandatory Plan', + plan = self.env['account.analytic.plan'].create([{ + 'name': 'Plan', 'default_applicability': 'optional', }]) - mandatory_plan.with_company(company_2).write({'default_applicability': 'mandatory'}) - self.env['account.analytic.applicability'].create({ + applicability = self.env['account.analytic.applicability'].create({ 'business_domain': 'general', - 'analytic_plan_id': mandatory_plan.id, + 'analytic_plan_id': plan.id, 'applicability': 'mandatory', 'company_id': company_2.id, }) self.env['account.analytic.account'].create([{ 'name': 'Mandatory Account', 'code': 'manda', - 'plan_id': mandatory_plan.id, + 'plan_id': plan.id, }]) distribution_model = self.env['account.analytic.distribution.model'].create({}).with_context(validate_analytic=True) - # only mandatory applicability is in company_2, should not raise - distribution_model._validate_distribution(business_domain='general') + # mandatory applicability is only in company_2, should not raise for company_1 + distribution_model._validate_distribution(business_domain='general', company_id=self.env.company.id) + + applicability.company_id = False + # It should apply for all companies now + with self.assertRaisesRegex(UserError, r'require a 100% analytic distribution'): + distribution_model._validate_distribution(business_domain='general', company_id=self.env.company.id) + with self.assertRaisesRegex(UserError, r'require a 100% analytic distribution'): + distribution_model._validate_distribution(business_domain='general')