diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index c283c7b6cc2..b50f610b893 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -1115,12 +1115,22 @@ class SaleOrderLine(models.Model): #=== CORE METHODS OVERRIDES ===# + def _additional_name_per_id(self): + return { + so_line.id: + '(%s)' % (so_line.order_partner_id.ref or so_line.order_partner_id.name) + for so_line in self + if so_line.order_partner_id.ref or so_line.order_partner_id.name + } + def name_get(self): result = [] + name_per_id = self._additional_name_per_id() for so_line in self.sudo(): name = '%s - %s' % (so_line.order_id.name, so_line.name and so_line.name.split('\n')[0] or so_line.product_id.name) - if so_line.order_partner_id.ref: - name = '%s (%s)' % (name, so_line.order_partner_id.ref) + additional_name = name_per_id.get(so_line.id) + if additional_name: + name = '%s %s' % (name, additional_name) result.append((so_line.id, name)) return result diff --git a/addons/sale_project/__manifest__.py b/addons/sale_project/__manifest__.py index b8ef1006e01..1fd54153e9c 100644 --- a/addons/sale_project/__manifest__.py +++ b/addons/sale_project/__manifest__.py @@ -9,7 +9,8 @@ Allows to create task from your sales order This module allows to generate a project/task from sales orders. """, 'category': 'Hidden', - 'depends': ['sale_management', 'project'], + 'depends': ['sale_management', 'sale_service', 'project'], + 'auto_install': ['sale_management', 'project'], 'data': [ 'security/ir.model.access.csv', 'security/sale_project_security.xml', @@ -25,6 +26,5 @@ This module allows to generate a project/task from sales orders. 'sale_project/static/src/components/project_right_side_panel/**/*', ], }, - 'auto_install': True, 'license': 'LGPL-3', } diff --git a/addons/sale_project/models/sale_order_line.py b/addons/sale_project/models/sale_order_line.py index f53ad40f0c3..09b2f516ba0 100644 --- a/addons/sale_project/models/sale_order_line.py +++ b/addons/sale_project/models/sale_order_line.py @@ -19,36 +19,8 @@ class SaleOrderLine(models.Model): 'project.task', 'Generated Task', index=True, copy=False) # used to know if generate a task and/or a project, depending on the product settings - is_service = fields.Boolean("Is a Service", compute='_compute_is_service', store=True, compute_sudo=True) reached_milestones_ids = fields.One2many('project.milestone', 'sale_line_id', string='Reached Milestones', domain=[('is_reached', '=', True)]) - def name_get(self): - res = super().name_get() - with_price_unit = self._context.get('with_price_unit', self._context.get('is_timesheet')) - if with_price_unit: - names = dict(res) - result = [] - sols_by_so_dict = defaultdict(lambda: self.env[self._name]) # key: (sale_order_id, product_id), value: sale order line - for line in self: - sols_by_so_dict[line.order_id.id, line.product_id.id] += line - - for sols in sols_by_so_dict.values(): - if len(sols) > 1 and all(sols.mapped('is_service')): - result += [( - line.id, - '%s - %s' % ( - names.get(line.id), format_amount(self.env, line.price_unit, line.currency_id)) - ) for line in sols] - else: - result += [(line.id, names.get(line.id)) for line in sols] - return result - return res - - @api.depends('product_id.type') - def _compute_is_service(self): - for so_line in self: - so_line.is_service = so_line.product_id.type == 'service' - @api.depends('product_id.type') def _compute_product_updatable(self): super()._compute_product_updatable() @@ -56,21 +28,6 @@ class SaleOrderLine(models.Model): if line.product_id.type == 'service' and line.state == 'sale': line.product_updatable = False - def _auto_init(self): - """ - Create column to stop ORM from computing it himself (too slow) - """ - if not column_exists(self.env.cr, 'sale_order_line', 'is_service'): - create_column(self.env.cr, 'sale_order_line', 'is_service', 'bool') - self.env.cr.execute(""" - UPDATE sale_order_line line - SET is_service = (pt.type = 'service') - FROM product_product pp - LEFT JOIN product_template pt ON pt.id = pp.product_tmpl_id - WHERE pp.id = line.product_id - """) - return super()._auto_init() - @api.depends('product_id') def _compute_qty_delivered_method(self): milestones_lines = self.filtered(lambda sol: diff --git a/addons/sale_service/__init__.py b/addons/sale_service/__init__.py new file mode 100644 index 00000000000..dc5e6b693d1 --- /dev/null +++ b/addons/sale_service/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models diff --git a/addons/sale_service/__manifest__.py b/addons/sale_service/__manifest__.py new file mode 100644 index 00000000000..41f52f72878 --- /dev/null +++ b/addons/sale_service/__manifest__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': "Sales - Service", + 'summary': "Interaction between Sales and services apps (project and planning)", + 'description': """ +Allows to display sale information in the SOL services apps +=========================================================== +Additional information is displayed in the name of the SOL when it is used in services apps (project and planning). +""", + 'category': 'Hidden', + 'depends': ['sale_management'], + 'license': 'LGPL-3', +} diff --git a/addons/sale_service/models/__init__.py b/addons/sale_service/models/__init__.py new file mode 100644 index 00000000000..83c06ee1343 --- /dev/null +++ b/addons/sale_service/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import sale_order_line diff --git a/addons/sale_service/models/sale_order_line.py b/addons/sale_service/models/sale_order_line.py new file mode 100644 index 00000000000..8fb5b56095f --- /dev/null +++ b/addons/sale_service/models/sale_order_line.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from itertools import groupby + +from odoo import api, fields, models +from odoo.tools import format_amount +from odoo.tools.sql import column_exists, create_column + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + # used to know if generate a task and/or a project, depending on the product settings + is_service = fields.Boolean("Is a Service", compute='_compute_is_service', store=True, compute_sudo=True) + + @api.depends('product_id.type') + def _compute_is_service(self): + for so_line in self: + so_line.is_service = so_line.product_id.type == 'service' + + def _auto_init(self): + """ + Create column to stop ORM from computing it himself (too slow) + """ + if not column_exists(self.env.cr, 'sale_order_line', 'is_service'): + create_column(self.env.cr, 'sale_order_line', 'is_service', 'bool') + self.env.cr.execute(""" + UPDATE sale_order_line line + SET is_service = (pt.type = 'service') + FROM product_product pp + LEFT JOIN product_template pt ON pt.id = pp.product_tmpl_id + WHERE pp.id = line.product_id + """) + return super()._auto_init() + + def _additional_name_per_id(self): + name_per_id = super()._additional_name_per_id() if not self.env.context.get('hide_partner_ref') else {} + if not self.env.context.get('with_price_unit'): + return name_per_id + + sols_list = [list(sols) for dummy, sols in groupby(self, lambda sol: (sol.order_id, sol.product_id))] + for sols in sols_list: + if len(sols) <= 1 or not all(sol.is_service for sol in sols): + continue + for line in sols: + additional_name = name_per_id.get(line.id) + name = format_amount(self.env, line.price_unit, line.currency_id) + if additional_name: + name += f' {additional_name}' + name_per_id[line.id] = f'- {name}' + + return name_per_id