From ca8aaa0a0d3ffee10358c4d33141ad1a43ad3de2 Mon Sep 17 00:00:00 2001 From: "Xavier BOL (xbo)" Date: Mon, 7 Mar 2022 08:14:57 +0100 Subject: [PATCH] [IMP] sale_project: add action to get all SOLs related to revenues Before this commit, the materials row displayed in the revenues of the project profitability in the right side panel shows only the result of all SOLs containing a material product. The user cannot see which SOLs give that result. This commit adds an action to see all SOLs related to that row. If the user has access to the Sales app then the name in that row (that is, 'Material') will be clickable to redirect the user to the view of SOLs found for that row. task-2710808 --- addons/sale_project/models/project.py | 41 ++++++++++++++++--- .../tests/test_project_profitability.py | 14 +++---- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/addons/sale_project/models/project.py b/addons/sale_project/models/project.py index 0f7f7572526..89e9e390955 100644 --- a/addons/sale_project/models/project.py +++ b/addons/sale_project/models/project.py @@ -105,6 +105,24 @@ class Project(models.Model): }) return action_window + def action_profitability_items(self, section_name, domain=None, res_id=False): + if section_name in ['service_revenues', 'other_revenues']: + view_types = ['list', 'kanban', 'form'] + action = { + 'name': _('Sales Order Items'), + 'type': 'ir.actions.act_window', + 'res_model': 'sale.order.line', + 'context': {'no_create': True}, + } + if res_id: + action['res_id'] = res_id + view_types = ['form'] + else: + action['domain'] = domain + action['views'] = [(False, v) for v in view_types] + return action + return super().action_profitability_items(section_name, domain, res_id) + @api.depends('sale_order_id.invoice_status', 'tasks.sale_order_id.invoice_status') def _compute_has_any_so_with_nothing_to_invoice(self): """Has any Sale Order whose invoice_status is set as No""" @@ -282,7 +300,7 @@ class Project(models.Model): 'delivered_manual': 'service_revenues', } - def _get_revenues_items_from_sol(self): + def _get_revenues_items_from_sol(self, with_action=True): sale_line_read_group = self.env['sale.order.line'].read_group( [ ('order_id', 'in', self._get_sale_orders().ids), @@ -292,9 +310,10 @@ class Project(models.Model): ('state', 'in', ['sale', 'done']), '|', ('qty_to_invoice', '>', 0), ('qty_invoiced', '>', 0), ], - ['product_id', 'untaxed_amount_to_invoice', 'untaxed_amount_invoiced'], + ['product_id', 'ids:array_agg(id)', 'untaxed_amount_to_invoice', 'untaxed_amount_invoiced'], ['product_id'], ) + display_sol_action = with_action and len(self) == 1 and self.user_has_groups('sales_team.group_sale_salesman') revenues_dict = {} total_to_invoice = total_invoiced = 0.0 if sale_line_read_group: @@ -328,14 +347,26 @@ class Project(models.Model): total_to_invoice += amount_to_invoice revenue['invoiced'] += amount_invoiced total_invoiced += amount_invoiced + if display_sol_action and invoice_type in ['service_revenues', 'other_revenues']: + revenue.setdefault('record_ids', []).extend(sol_ids) + + if display_sol_action: + section_name = 'other_revenues' + other_revenues = revenues_dict.get(section_name, {}) + sol_ids = other_revenues.pop('record_ids', []) + if sol_ids: + action_params = {'name': 'action_profitability_items', 'type': 'object', 'section': section_name, 'domain': json.dumps(domain)} + if len(sol_ids) == 1: + action_params['res_id'] = sol_ids[0] + other_revenues['action'] = action_params return { 'data': [{'id': invoice_type, **vals} for invoice_type, vals in revenues_dict.items()], 'total': {'to_invoice': total_to_invoice, 'invoiced': total_invoiced}, } - def _get_profitability_items(self): - profitability_items = super()._get_profitability_items() - revenue_items_from_sol = self._get_revenues_items_from_sol() + def _get_profitability_items(self, with_action=True): + profitability_items = super()._get_profitability_items(with_action) + revenue_items_from_sol = self._get_revenues_items_from_sol(with_action) revenues = profitability_items['revenues'] revenues['data'] += revenue_items_from_sol['data'] revenues['total']['to_invoice'] += revenue_items_from_sol['total']['to_invoice'] diff --git a/addons/sale_project/tests/test_project_profitability.py b/addons/sale_project/tests/test_project_profitability.py index 14dcda4242a..e5fffadd6e3 100644 --- a/addons/sale_project/tests/test_project_profitability.py +++ b/addons/sale_project/tests/test_project_profitability.py @@ -57,7 +57,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo def test_project_profitability(self): self.assertFalse(self.project.allow_billable, 'The project should be non billable.') self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), self.project_profitability_items_empty, 'No data for the project profitability should be found since the project is not billable, so no SOL is linked to the project.' ) @@ -65,7 +65,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo self.assertTrue(self.project.allow_billable, 'The project should be billable.') self.project.sale_line_id = self.delivery_service_order_line self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), self.project_profitability_items_empty, 'No data for the project profitability should be found since no product is delivered in the SO linked.' ) @@ -77,7 +77,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo ['billable_manual', 'service_revenues'], 'invoice_type="billable_manual" if sale_timesheet is installed otherwise it is equal to "service_revenues"') self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), { 'revenues': { 'data': [ @@ -122,7 +122,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo ['billable_manual', 'service_revenues'], 'invoice_type="billable_manual" if sale_timesheet is installed otherwise it is equal to "service_revenues"') self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), { 'revenues': { 'data': [ @@ -163,7 +163,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo ['billable_manual', 'service_revenues'], 'invoice_type="billable_manual" if sale_timesheet is installed otherwise it is equal to "service_revenues"') self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), { 'revenues': { 'data': [ @@ -197,7 +197,7 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo credit_notes = invoices._reverse_moves() credit_notes.action_post() self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), { 'revenues': { 'data': [ @@ -226,6 +226,6 @@ class TestSaleProjectProfitability(TestProjectProfitabilityCommon, TestSaleCommo self.sale_order._action_cancel() self.assertDictEqual( - self.project._get_profitability_items(), + self.project._get_profitability_items(False), self.project_profitability_items_empty, )