From ffa61dae81e26fca836ca62843273b7cb7bdbe4a Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Mon, 21 Sep 2020 08:49:23 +0200 Subject: [PATCH 1/6] [FIX] write project in sale state too to add other services --- .../models/sale_order.py | 16 +++++++++++-- .../test_sale_timesheet_existing_project.py | 23 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/sale_timesheet_existing_project/models/sale_order.py b/sale_timesheet_existing_project/models/sale_order.py index 61f692f769..be5960cd4d 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)]}, help='Select a non billable project on which tasks can be created.') @api.depends('order_line.product_id.service_tracking') @@ -32,6 +35,15 @@ 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: + raise UserError(_('Missing project in sale order!')) + return res + class SaleOrderLine(models.Model): _inherit = "sale.order.line" 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..68cb0897e6 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,18 @@ 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.action_confirm() + self.assertEqual(self.order1.state, 'sale') + with self.assertRaises(UserError): + self.env['sale.order.line'].create({ + 'order_id': self.order1.id, + 'product_id': self.product.id, + }) + self.order1.project_id = self.project.id + self.env['sale.order.line'].create({ + 'order_id': self.order1.id, + 'product_id': self.product.id, + }) From 102e7350fd705ed41c59d944b8b18cba85a208d9 Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Fri, 30 Oct 2020 08:53:48 +0100 Subject: [PATCH 2/6] [IMP] block only after creation of first project --- sale_timesheet_existing_project/models/sale_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sale_timesheet_existing_project/models/sale_order.py b/sale_timesheet_existing_project/models/sale_order.py index be5960cd4d..bd5dd931f2 100644 --- a/sale_timesheet_existing_project/models/sale_order.py +++ b/sale_timesheet_existing_project/models/sale_order.py @@ -40,8 +40,10 @@ 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: - raise UserError(_('Missing project in sale order!')) + 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 From 387a714eede7a5a93757f9c51517afc5da2cb021 Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Fri, 30 Oct 2020 11:27:39 +0100 Subject: [PATCH 3/6] [FIX] sale in done state --- sale_timesheet_existing_project/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sale_timesheet_existing_project/models/sale_order.py b/sale_timesheet_existing_project/models/sale_order.py index bd5dd931f2..33c0ada137 100644 --- a/sale_timesheet_existing_project/models/sale_order.py +++ b/sale_timesheet_existing_project/models/sale_order.py @@ -20,7 +20,7 @@ class SaleOrder(models.Model): " ('company_id', '=', company_id)]", readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)], - 'sale': [('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') From 60d998df3b949f0316a7614c5beb96d1a7bc8b04 Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Sat, 31 Oct 2020 14:44:51 +0100 Subject: [PATCH 4/6] [FIX] test with first task creating project --- .../__manifest__.py | 5 ++++- .../test_sale_timesheet_existing_project.py | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) 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/tests/test_sale_timesheet_existing_project.py b/sale_timesheet_existing_project/tests/test_sale_timesheet_existing_project.py index 68cb0897e6..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 @@ -74,15 +74,23 @@ def test_sale_timesheet_new_project(self): 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.env['sale.order.line'].create({ - 'order_id': self.order1.id, - 'product_id': self.product.id, + self.order1.write({ + 'order_line': [( + 0, 0, {'product_id': self.product.id} + )] }) self.order1.project_id = self.project.id - self.env['sale.order.line'].create({ - 'order_id': self.order1.id, - 'product_id': self.product.id, + self.order1.write({ + 'order_line': [( + 0, 0, {'product_id': self.product.id} + )] }) From 372112794ea803958a6681ed4a82229328b55c33 Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Sat, 31 Oct 2020 14:55:41 +0100 Subject: [PATCH 5/6] [FIX] add desc --- sale_timesheet_existing_project/readme/DESCRIPTION.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sale_timesheet_existing_project/readme/DESCRIPTION.rst b/sale_timesheet_existing_project/readme/DESCRIPTION.rst index 42ce449b0a..ac59f85c80 100644 --- a/sale_timesheet_existing_project/readme/DESCRIPTION.rst +++ b/sale_timesheet_existing_project/readme/DESCRIPTION.rst @@ -1,3 +1,6 @@ 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. From 1ccf1ac247c1feaddd39c913aa9eb41afa09e778 Mon Sep 17 00:00:00 2001 From: sergiocorato Date: Sat, 31 Oct 2020 15:03:56 +0100 Subject: [PATCH 6/6] [FIX] desc --- sale_timesheet_existing_project/readme/DESCRIPTION.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/sale_timesheet_existing_project/readme/DESCRIPTION.rst b/sale_timesheet_existing_project/readme/DESCRIPTION.rst index ac59f85c80..92666d8b25 100644 --- a/sale_timesheet_existing_project/readme/DESCRIPTION.rst +++ b/sale_timesheet_existing_project/readme/DESCRIPTION.rst @@ -1,6 +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.