From 0fc02b136b2f1eef5561717fbd385eb3ee37d989 Mon Sep 17 00:00:00 2001 From: "Nasreddin Boulif (bon)" Date: Thu, 25 Aug 2022 16:40:06 +0000 Subject: [PATCH] [FIX] sale_project: Allow user to cancel SO linked to project Steps to reproduce: - Go to Projects and find one having field "sale_line_id" set (example "AGR - S00048" with "S00048 - Senior Architect (Invoice on Timesheets)") - Go to Sales -> Quotations and : 1/ cancel 2/ set in draft 3/ edit Project to "AGR - SO00048" 4/ confirm again (see attached) - Alter Marc Demo access right to have: Sales -> Administrator Project -> User - With Marc Demo, go to Sales -> Quotations and open S00048 - Try to cancel it Issue: Access right error. Cause: When canceling a SO, we remove the related SO line on the project however the current user have access to edit SO but not the project. Solution: Use sudo(). opw-2959627 closes odoo/odoo#99736 X-original-commit: fab2bd4428dcfec26b644351951f0855ad98fcd9 Signed-off-by: Xavier Signed-off-by: Nasreddin Boulif (bon) --- addons/sale_project/models/sale_order.py | 2 +- .../sale_project/tests/test_sale_project.py | 53 ++++++++++++++----- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/addons/sale_project/models/sale_order.py b/addons/sale_project/models/sale_order.py index ddc559cf799..321db740f82 100644 --- a/addons/sale_project/models/sale_order.py +++ b/addons/sale_project/models/sale_order.py @@ -161,5 +161,5 @@ class SaleOrder(models.Model): def write(self, values): if 'state' in values and values['state'] == 'cancel': - self.project_id.sale_line_id = False + self.project_id.sudo().sale_line_id = False return super(SaleOrder, self).write(values) diff --git a/addons/sale_project/tests/test_sale_project.py b/addons/sale_project/tests/test_sale_project.py index f9050152d9a..e9061898ec0 100644 --- a/addons/sale_project/tests/test_sale_project.py +++ b/addons/sale_project/tests/test_sale_project.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests.common import TransactionCase +from odoo.tests.common import TransactionCase, users class TestSaleProject(TransactionCase): @@ -87,15 +87,17 @@ class TestSaleProject(TransactionCase): 'project_id': False, }) + # Create partner + cls.partner = cls.env['res.partner'].create({'name': "Mur en béton"}) + def test_sale_order_with_project_task(self): SaleOrder = self.env['sale.order'].with_context(tracking_disable=True) SaleOrderLine = self.env['sale.order.line'].with_context(tracking_disable=True) - partner = self.env['res.partner'].create({'name': "Mur en béton"}) sale_order = SaleOrder.create({ - 'partner_id': partner.id, - 'partner_invoice_id': partner.id, - 'partner_shipping_id': partner.id, + 'partner_id': self.partner.id, + 'partner_invoice_id': self.partner.id, + 'partner_shipping_id': self.partner.id, }) so_line_order_no_task = SaleOrderLine.create({ 'product_id': self.product_order_service1.id, @@ -138,9 +140,9 @@ class TestSaleProject(TransactionCase): self.assertEqual(self.project_global._get_sale_order_items(), self.project_global.sale_line_id | self.project_global.tasks.sale_line_id, 'The _get_sale_order_items should returns all the SOLs linked to the project and its active tasks.') sale_order_2 = SaleOrder.create({ - 'partner_id': partner.id, - 'partner_invoice_id': partner.id, - 'partner_shipping_id': partner.id, + 'partner_id': self.partner.id, + 'partner_invoice_id': self.partner.id, + 'partner_shipping_id': self.partner.id, }) sale_line_1_order_2 = SaleOrderLine.create({ 'product_id': self.product_order_service1.id, @@ -186,11 +188,10 @@ class TestSaleProject(TransactionCase): self.assertNotIn(note_sale_line_order_2.id, actual_sol_ids, 'The note Sales Order Item should not be takken into account in the Sales section of project.') def test_sol_product_type_update(self): - partner = self.env['res.partner'].create({'name': "Mur en brique"}) sale_order = self.env['sale.order'].with_context(tracking_disable=True).create({ - 'partner_id': partner.id, - 'partner_invoice_id': partner.id, - 'partner_shipping_id': partner.id, + 'partner_id': self.partner.id, + 'partner_invoice_id': self.partner.id, + 'partner_shipping_id': self.partner.id, }) self.product_order_service3.type = 'consu' sale_order_line = self.env['sale.order.line'].create({ @@ -205,3 +206,31 @@ class TestSaleProject(TransactionCase): self.product_order_service3.type = 'service' self.assertTrue(sale_order_line.is_service, "As the product is a service, the SOL should be a service") + + @users('demo') + def test_cancel_so_linked_to_project(self): + """ Test that cancelling a SO linked to a project will not raise an error """ + # Ensure user don't have edit right access to the project + group_sale_manager = self.env.ref('sales_team.group_sale_manager') + group_project_user = self.env.ref('project.group_project_user') + self.env.user.write({'groups_id': [(6, 0, [group_sale_manager.id, group_project_user.id])]}) + + sale_order = self.env['sale.order'].with_context(tracking_disable=True).create({ + 'partner_id': self.partner.id, + 'partner_invoice_id': self.partner.id, + 'partner_shipping_id': self.partner.id, + 'project_id': self.project_global.id, + }) + sale_order_line = self.env['sale.order.line'].create({ + 'name': self.product_order_service2.name, + 'product_id': self.product_order_service2.id, + 'order_id': sale_order.id, + }) + self.assertFalse(self.project_global.tasks.sale_line_id, "The project tasks should not be linked to the SOL") + + sale_order.action_confirm() + self.assertEqual(self.project_global.tasks.sale_line_id.id, sale_order_line.id, "The project tasks should be linked to the SOL from the SO") + + self.project_global.sale_line_id = sale_order_line + sale_order.with_context({'disable_cancel_warning': True}).action_cancel() + self.assertFalse(self.project_global.sale_line_id, "The project should not be linked to the SOL anymore")