diff --git a/addons/project_hr_expense/models/project.py b/addons/project_hr_expense/models/project.py index 1a4107a5568..ae5f81575f3 100644 --- a/addons/project_hr_expense/models/project.py +++ b/addons/project_hr_expense/models/project.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models, _ +import json + +from odoo import api, fields, models, _, _lt from odoo.osv import expression class Project(models.Model): @@ -24,43 +26,58 @@ class Project(models.Model): # Actions # ---------------------------- - def action_open_project_expenses(self): - expenses = self.env['hr.expense'].search([ - ('analytic_account_id', '!=', False), - ('analytic_account_id', 'in', self.analytic_account_id.ids) - ]) + def _get_expense_action(self, domain=None, expense_ids=None): + if not domain and not expense_ids: + return {} action = self.env["ir.actions.actions"]._for_xml_id("hr_expense.hr_expense_actions_all") action.update({ 'display_name': _('Expenses'), 'views': [[False, 'tree'], [False, 'form'], [False, 'kanban'], [False, 'graph'], [False, 'pivot']], 'context': {'default_analytic_account_id': self.analytic_account_id.id}, - 'domain': [('id', 'in', expenses.ids)] + 'domain': domain or [('id', 'in', expense_ids)], }) - if(len(expenses) == 1): + if len(expense_ids) == 1: action["views"] = [[False, 'form']] - action["res_id"] = expenses.id + action["res_id"] = expense_ids[0] return action + def action_open_project_expenses(self): + if not self.analytic_account_id: + return {'type': 'ir.actions.act_window_close'} + expense_ids = self.env['hr.expense']._search([ + ('analytic_account_id', 'in', self.analytic_account_id.ids) + ]) + return self._get_expense_action(expense_ids=expense_ids) + + def action_profitability_items(self, section_name, domain=None, res_id=False): + if section_name == 'expenses': + return self._get_expense_action(domain, [res_id] if res_id else []) + return super().action_profitability_items(section_name, domain, res_id) + # ---------------------------- # Project Update # ---------------------------- - def _get_expenses_profitability_items(self): + def _get_expenses_profitability_items(self, with_action=True): if not self.analytic_account_id: return {} + can_see_expense = with_action and self.user_has_groups('hr_expense.group_hr_expense_team_approver') expenses_read_group = self.env['hr.expense'].sudo()._read_group( [('analytic_account_id', 'in', self.analytic_account_id.ids), ('is_refused', '=', False), ('state', 'in', ['approved', 'done'])], - ['untaxed_amount'], + ['untaxed_amount', 'ids:array_agg(id)'], [], ) if not expenses_read_group or not expenses_read_group[0]['__count']: return {} expense_data = expenses_read_group[0] section_id = 'expenses' - return { + expense_profitability_items = { 'costs': {'id': section_id, 'billed': -expense_data['untaxed_amount'], 'to_bill': 0.0}, } + if can_see_expense: + expense_profitability_items['action'] = {'name': 'action_profitability_items', 'type': 'object', 'section': section_id, 'domain': json.dumps([('id', 'in', expense_data['ids'])])} + return expense_profitability_items def _get_profitability_aal_domain(self): return expression.AND([ @@ -68,9 +85,9 @@ class Project(models.Model): ['|', ('move_id', '=', False), ('move_id.expense_id', '=', False)], ]) - def _get_profitability_items(self): - profitability_data = super()._get_profitability_items() - expenses_data = self._get_expenses_profitability_items() + def _get_profitability_items(self, with_action=True): + profitability_data = super()._get_profitability_items(with_action) + expenses_data = self._get_expenses_profitability_items(with_action) if expenses_data: if 'revenues' in expenses_data: revenues = profitability_data['revenues'] diff --git a/addons/project_hr_expense/tests/test_project_profitability.py b/addons/project_hr_expense/tests/test_project_profitability.py index de290406ed9..fdbd4b2b671 100644 --- a/addons/project_hr_expense/tests/test_project_profitability.py +++ b/addons/project_hr_expense/tests/test_project_profitability.py @@ -8,7 +8,7 @@ from odoo.addons.project.tests.test_project_profitability import TestProjectProf class TestProjectHrExpenseProfitabilityCommon(TestExpenseCommon): def check_project_profitability_before_creating_and_approving_expense_sheet(self, expense, project, project_profitability_items_empty): self.assertDictEqual( - project._get_profitability_items(), + project._get_profitability_items(False), project_profitability_items_empty, 'No data should be found since the expense is not approved yet.', ) @@ -21,7 +21,7 @@ class TestProjectHrExpenseProfitabilityCommon(TestExpenseCommon): self.assertEqual(expense_sheet.state, 'submit') self.assertDictEqual( - project._get_profitability_items(), + project._get_profitability_items(False), project_profitability_items_empty, 'No data should be found since the sheet is not approved yet.', ) @@ -48,7 +48,7 @@ class TestProjectHrExpenseProfitability(TestProjectProfitabilityCommon, TestProj self.project_profitability_items_empty) self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), { 'costs': { 'data': [{'id': 'expenses', 'to_bill': 0.0, 'billed': -expense.untaxed_amount}], @@ -60,7 +60,7 @@ class TestProjectHrExpenseProfitability(TestProjectProfitabilityCommon, TestProj expense_sheet.refuse_sheet('Test cancel expense') self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), self.project_profitability_items_empty, 'No data should be found since the sheet is not approved yet.', )