From 0554bc29b8bf96a622652deec1d7bfa925c13229 Mon Sep 17 00:00:00 2001 From: Vincent Larcin Date: Mon, 20 Nov 2023 15:00:38 +0100 Subject: [PATCH] [FIX] sale_project: avoid opening form when creating SOLs When creating a SOL on the fly, if the name entered matches an existing service product, selecting 'Create' should create the SOL without opening a form view modal. We do that by fetching the `default_name` from the context. However, from the Many2one field, since the name will already be present in the create vals, in will not be in the default values in the context. To fix this, this commit adds a new context key when calling `name_create` that allows us to retrieve the name from the `default_get` Task-3553151 closes odoo/odoo#155191 Related: odoo/enterprise#57344 Signed-off-by: Xavier Bol (xbo) Signed-off-by: Vincent Larcin (vila) --- addons/sale_project/models/sale_order_line.py | 38 +++++++++++++++---- .../sale_project/tests/test_sale_project.py | 21 ++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/addons/sale_project/models/sale_order_line.py b/addons/sale_project/models/sale_order_line.py index 2dbd9f7bfa2..83ae42dfb3e 100644 --- a/addons/sale_project/models/sale_order_line.py +++ b/addons/sale_project/models/sale_order_line.py @@ -25,10 +25,7 @@ class SaleOrderLine(models.Model): def default_get(self, fields): res = super().default_get(fields) if self.env.context.get('form_view_ref') == 'sale_project.sale_order_line_view_form_editable': - default_values = { - 'name': _("New Sales Order Item"), - } - + default_values = dict() # If we can't add order lines to the default order, discard it if 'order_id' in res: try: @@ -58,16 +55,43 @@ class SaleOrderLine(models.Model): sale_order = self.env['sale.order'].create(so_create_values) sale_order.action_confirm() default_values['order_id'] = sale_order.id - if (name := self.env.context.get('default_name')): + if product_name := self.env.context.get('sol_product_name') or self.env.context.get('default_name'): product = self.env['product.product'].search([ - ('name', 'ilike', name), + ('name', 'ilike', product_name), ('company_id', 'in', [False, self.env.company.id]), ], limit=1) if product: default_values['product_id'] = product.id + # We need to remove the name from the defaults so that the + # name of the SOL is based on the full name of the product + # and not overwritten by what was typed in the field. + if "name" in res: + del res["name"] + else: + default_values['name'] = _("New Sales Order Item") return {**res, **default_values} return res + @api.model + def name_create(self, name): + # To get the right product when creating a SOL on the fly, we need to get + # the name that was entered in the field from the `default_get` method. + # The easiest way of doing that is to store it in the context. + if self.env.context.get('form_view_ref') == 'sale_project.sale_order_line_view_form_editable': + self = self.with_context(sol_product_name=name) + return super().name_create(name) + + @api.model + def _add_missing_default_values(self, values): + # When creating a SOL through the quick create, the name_create will be + # called with whatever was typed in the field. However, we don't want + # that value to overwrite the computed SOL name if we find a product. + defaults = super()._add_missing_default_values(values) + if self.env.context.get('form_view_ref') == 'sale_project.sale_order_line_view_form_editable': + if "name" in defaults and "product_id" in defaults: + del defaults["name"] + return defaults + @api.depends('product_id.type') def _compute_product_updatable(self): super()._compute_product_updatable() @@ -118,7 +142,7 @@ class SaleOrderLine(models.Model): line.order_id.message_post(body=msg_body) # Set a service SOL on the project, if any is given - if (project_id := self.env.context.get('link_to_project')): + if project_id := self.env.context.get('link_to_project'): assert (service_line := next((line for line in lines if line.is_service), False)) project = self.env['project.project'].browse(project_id) if not project.sale_line_id: diff --git a/addons/sale_project/tests/test_sale_project.py b/addons/sale_project/tests/test_sale_project.py index 6ca77b3ce2b..71e82bb9fee 100644 --- a/addons/sale_project/tests/test_sale_project.py +++ b/addons/sale_project/tests/test_sale_project.py @@ -627,3 +627,24 @@ class TestSaleProject(TestSaleProjectCommon): sol_form.qty_delivered = 1 self.assertEqual(sol_form.qty_delivered_method, 'manual') self.assertEqual(sol_form.qty_delivered, 1, 'quantity delivered is editable') + + def test_quick_create_sol(self): + """ + When creating a SOL on the fly through the quick create, use a product matching + what was typed in the field if there is one, and make sure the SOL name is computed correctly. + """ + product_service = self.env['product.product'].create({ + 'name': 'Signage', + 'type': 'service', + 'invoice_policy': 'order', + 'uom_id': self.env.ref('uom.product_uom_hour').id, + 'uom_po_id': self.env.ref('uom.product_uom_hour').id, + }) + sale_line_id, sale_line_name = self.env['sale.order.line'].with_context( + default_partner_id=self.partner.id, + form_view_ref='sale_project.sale_order_line_view_form_editable', + ).name_create('gnag') + + sale_line = self.env['sale.order.line'].browse(sale_line_id) + self.assertEqual(sale_line.product_id, product_service, 'The created SOL should use the right product.') + self.assertTrue(product_service.name in sale_line_name, 'The created SOL should use the full name of the product and not just what was typed.')