[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
This commit is contained in:
Xavier BOL (xbo)
2022-04-01 15:33:00 +02:00
committed by Laurent Stukkens (LTU)
parent 4d20d315e9
commit 5558d2a578
2 changed files with 36 additions and 19 deletions
+32 -15
View File
@@ -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']
@@ -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.',
)