diff --git a/addons/account/models/account_move_line.py b/addons/account/models/account_move_line.py index 64c9eef2044..9e5cd312f03 100644 --- a/addons/account/models/account_move_line.py +++ b/addons/account/models/account_move_line.py @@ -6,7 +6,7 @@ from functools import lru_cache from odoo import api, fields, models, Command, _ from odoo.exceptions import ValidationError, UserError -from odoo.tools import frozendict, formatLang, format_date, float_is_zero, float_compare +from odoo.tools import frozendict, formatLang, format_date, float_is_zero from odoo.tools.sql import create_index from odoo.addons.web.controllers.utils import clean_action @@ -2352,32 +2352,21 @@ class AccountMoveLine(models.Model): # ANALYTIC # ------------------------------------------------------------------------- - def _validate_distribution(self): + def _validate_analytic_distribution(self): for line in self.filtered(lambda line: line.display_type == 'product'): - mandatory_plans_ids = [plan['id'] for plan in self.env['account.analytic.plan'].sudo().get_relevant_plans(**{ + line._validate_distribution(**{ 'product': line.product_id.id, 'account': line.account_id.id, 'business_domain': line.move_id.move_type in ['out_invoice', 'out_refund', 'out_receipt'] and 'invoice' or line.move_id.move_type in ['in_invoice', 'in_refund', 'in_receipt'] and 'bill' or 'general', - 'company_id': self.company_id.id, - }) if plan['applicability'] == 'mandatory'] - if not mandatory_plans_ids: - continue - distribution_by_root_plan = {} - for analytic_account_id, percentage in (line.analytic_distribution or {}).items(): - root_plan = self.env['account.analytic.account'].browse(int(analytic_account_id)).root_plan_id - distribution_by_root_plan[root_plan.id] = distribution_by_root_plan.get(root_plan.id, 0) + percentage - - for plan_id in mandatory_plans_ids: - if float_compare(distribution_by_root_plan.get(plan_id, 0), 100, precision_digits=2) != 0: - raise ValidationError(_("One or more lines require a 100% analytic distribution.")) + 'company_id': line.company_id.id, + }) def _create_analytic_lines(self): """ Create analytic items upon validation of an account.move.line having an analytic distribution. """ - if self.env.context.get('validate_analytic', False): - self._validate_distribution() + self._validate_analytic_distribution() analytic_line_vals = [] for line in self: analytic_line_vals.extend(line._prepare_analytic_lines()) diff --git a/addons/analytic/models/analytic_mixin.py b/addons/analytic/models/analytic_mixin.py index abea3663027..7247d93b89e 100644 --- a/addons/analytic/models/analytic_mixin.py +++ b/addons/analytic/models/analytic_mixin.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, fields, api -from odoo.tools.float_utils import float_round - +from odoo import models, fields, api, _ +from odoo.tools.float_utils import float_round, float_compare +from odoo.exceptions import ValidationError class AnalyticMixin(models.AbstractModel): _name = 'analytic.mixin' @@ -47,6 +47,21 @@ class AnalyticMixin(models.AbstractModel): vals_list = [self._sanitize_values(vals, decimal_precision) for vals in vals_list] return super().create(vals_list) + def _validate_distribution(self, **kwargs): + if self.env.context.get('validate_analytic', False): + mandatory_plans_ids = [plan['id'] for plan in self.env['account.analytic.plan'].sudo().get_relevant_plans(**kwargs) if plan['applicability'] == 'mandatory'] + if not mandatory_plans_ids: + return + decimal_precision = self.env['decimal.precision'].precision_get('Percentage Analytic') + distribution_by_root_plan = {} + for analytic_account_id, percentage in (self.analytic_distribution or {}).items(): + root_plan = self.env['account.analytic.account'].browse(int(analytic_account_id)).root_plan_id + distribution_by_root_plan[root_plan.id] = distribution_by_root_plan.get(root_plan.id, 0) + percentage + + for plan_id in mandatory_plans_ids: + if float_compare(distribution_by_root_plan.get(plan_id, 0), 100, precision_digits=decimal_precision) != 0: + raise ValidationError(_("One or more lines require a 100% analytic distribution.")) + def _sanitize_values(self, vals, decimal_precision): """ Normalize the float of the distribution """ if 'analytic_distribution' in vals: diff --git a/addons/analytic/tests/test_analytic_account.py b/addons/analytic/tests/test_analytic_account.py index dbb15093f5e..4dfd0cfda4a 100644 --- a/addons/analytic/tests/test_analytic_account.py +++ b/addons/analytic/tests/test_analytic_account.py @@ -100,7 +100,7 @@ class TestAnalyticAccount(TransactionCase): plans_json = self.env['account.analytic.plan'].get_relevant_plans(**kwargs) self.assertEqual(1, len(plans_json), "Plan 1 should be unavailable") - kwargs = {'business_domain': 'purchase'} + kwargs = {'business_domain': 'purchase_order'} plans_json = self.env['account.analytic.plan'].get_relevant_plans(**kwargs) self.assertEqual(2, len(plans_json), "Both plans should be available") diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index c9943c38419..53badbf1991 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -1154,6 +1154,7 @@ class HrExpenseSheet(models.Model): def approve_expense_sheets(self): self._check_can_approve() + self._validate_analytic_distribution() duplicates = self.expense_line_ids.duplicate_expense_ids.filtered(lambda exp: exp.state in ['approved', 'done']) if duplicates: action = self.env["ir.actions.act_window"]._for_xml_id('hr_expense.hr_expense_approve_duplicate_action') @@ -1161,6 +1162,14 @@ class HrExpenseSheet(models.Model): return action self._do_approve() + def _validate_analytic_distribution(self): + for line in self.expense_line_ids: + line._validate_distribution(**{ + 'account': line.account_id.id, + 'business_domain': 'expense', + 'company_id': line.company_id.id, + }) + def _do_approve(self): self._check_can_approve() diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 9088fba41b7..b4029474ca2 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -662,6 +662,7 @@ string="Approve" type="object" data-hotkey="v" + context="{'validate_analytic': True}" attrs="{'invisible': ['|', ('can_approve', '=', False), ('state', '!=', 'submit')]}" class="oe_highlight o_expense_sheet_approve"/>