diff --git a/sale_timesheet_existing_project/__manifest__.py b/sale_timesheet_existing_project/__manifest__.py index 59d62a7ebb..3ae525a873 100644 --- a/sale_timesheet_existing_project/__manifest__.py +++ b/sale_timesheet_existing_project/__manifest__.py @@ -1,4 +1,5 @@ # Copyright 2019 Tecnativa - Pedro M. Baeza +# Copyright 2020 Sergio Corato # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { @@ -12,7 +13,9 @@ "views/product_template_views.xml", "views/sale_order_views.xml", ], - "author": "Tecnativa, Odoo Community Association (OCA)", + "author": "Tecnativa, " + "Sergio Corato, " + "Odoo Community Association (OCA)", "website": "https://github.com/OCA/timesheet", "license": "AGPL-3", "installable": True, diff --git a/sale_timesheet_existing_project/models/sale_order.py b/sale_timesheet_existing_project/models/sale_order.py index 61f692f769..33c0ada137 100644 --- a/sale_timesheet_existing_project/models/sale_order.py +++ b/sale_timesheet_existing_project/models/sale_order.py @@ -1,7 +1,9 @@ # Copyright 2019 Tecnativa - Pedro M. Baeza +# Copyright 2020 Sergio Corato # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models +from odoo import api, fields, models, _ +from odoo.exceptions import UserError class SaleOrder(models.Model): @@ -17,7 +19,8 @@ class SaleOrder(models.Model): " ('analytic_account_id', '!=', False)," " ('company_id', '=', company_id)]", readonly=True, - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, + states={'draft': [('readonly', False)], 'sent': [('readonly', False)], + 'sale': [('readonly', False)], 'done': [('readonly', False)]}, help='Select a non billable project on which tasks can be created.') @api.depends('order_line.product_id.service_tracking') @@ -32,6 +35,17 @@ def _compute_visible_project(self): in order.order_line.mapped('product_id.service_tracking') ) + @api.multi + def write(self, vals): + res = super(SaleOrder, self).write(vals) + for order in self: + if order.state == 'sale' and order.visible_project and not \ + order.project_id and any(order.mapped('order_line.project_id')): + raise UserError(_('Missing project in sale order, but almost one ' + 'line of the order have a project: assign one of ' + 'these projects to the sale order!')) + return res + class SaleOrderLine(models.Model): _inherit = "sale.order.line" diff --git a/sale_timesheet_existing_project/readme/DESCRIPTION.rst b/sale_timesheet_existing_project/readme/DESCRIPTION.rst index 42ce449b0a..92666d8b25 100644 --- a/sale_timesheet_existing_project/readme/DESCRIPTION.rst +++ b/sale_timesheet_existing_project/readme/DESCRIPTION.rst @@ -1,3 +1,7 @@ This module restores the behavior present in previous version (and restored again in v13) of being able to reuse existing projects for generating the tasks when confirming a sales order with service tracking. + +When sale order is confirmed, product generating task can be added, but only +the first without adding a project to the sale order, avoiding the creation +of multiple projects. diff --git a/sale_timesheet_existing_project/tests/test_sale_timesheet_existing_project.py b/sale_timesheet_existing_project/tests/test_sale_timesheet_existing_project.py index 3fa1194504..f3b6a97385 100644 --- a/sale_timesheet_existing_project/tests/test_sale_timesheet_existing_project.py +++ b/sale_timesheet_existing_project/tests/test_sale_timesheet_existing_project.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo.tests import common +from odoo.exceptions import UserError class TestSaleTimesheetExistingProject(common.SavepointCase): @@ -25,6 +26,13 @@ def setUpClass(cls): 'order_id': cls.order.id, 'product_id': cls.product.id, }) + cls.order1 = cls.env['sale.order'].create({ + 'partner_id': cls.partner.id, + }) + cls.line1 = cls.env['sale.order.line'].create({ + 'order_id': cls.order1.id, + 'product_id': cls.env.ref('product.product_product_10').id, + }) def test_onchange_product_service_tracking(self): self.product.project_id = self.project.id @@ -63,3 +71,26 @@ def test_sale_timesheet_new_project(self): self.assertNotEqual(self.line.task_id.project_id, self.project) self.assertIn(self.order.name, self.line.project_id.name) self.assertEqual(line2.project_id, self.line.project_id) + + def test_sale_timesheet_new_project_set_in_sale_state(self): + self.product.project_template_id = False + self.order1.write({ + 'order_line': [( + 0, 0, {'product_id': self.product.id} + )] + }) + self.order1.action_confirm() + self.assertEqual(self.order1.state, 'sale') + self.assertEqual(len(self.order1.mapped('order_line.project_id')), 1) + with self.assertRaises(UserError): + self.order1.write({ + 'order_line': [( + 0, 0, {'product_id': self.product.id} + )] + }) + self.order1.project_id = self.project.id + self.order1.write({ + 'order_line': [( + 0, 0, {'product_id': self.product.id} + )] + })