From 5558d2a57873939ca76cb83e113981095272b4ff Mon Sep 17 00:00:00 2001 From: "Xavier BOL (xbo)" Date: Sat, 5 Mar 2022 12:47:20 +0100 Subject: [PATCH] [IMP] project_hr_expense: add action to get expense for profitability Before this commit, the expense costs are not clickable to see the expenses impacting the project profitability in that row. This commit adds the action to allow the user to click on the name on the row to see all the expenses related. task-2710808 --- addons/project_hr_expense/models/project.py | 47 +++++++++++++------ .../tests/test_project_profitability.py | 8 ++-- 2 files changed, 36 insertions(+), 19 deletions(-) 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.', )