[ADD] sale_service: add additional information in SOL display name
- Impacted modules:
- sale_service (new module - installed only if sale
AND (project OR planning) are installed)
- sale
- sale_project
- Description: When looking for a SOL in services apps, the displayed
name is of the form "SO ref - SOL name - unit price (partner
ref/name)". The presence of some of those elements may vary:
- unit price is only displayed if it allows to clarify an
ambiguity (i.e. two SOL with identical name but different unit
price in the same SO).
- partner name is only used if the partner ref does not exist.
- partner name/ref is hidden in the name of planning slots to avoid
duplicates.
- Implementation: The behavior currently existing in sale needed to be
extended for services apps. This has been done with the following
changes:
- sale:
- sale.order.line: Current behavior for the name_get (display
partner ref) has been extended (display parnter name if has no
ref) and split a additional method that is extended in a new
module called..
- .. sale_service:
- sale.order.line: The additional behavior is extended to
display the unit price of the SOL when it avoids ambiguity. A
context parameter is used to keep this behavior inside
services apps.
task-3052812
Part-of: odoo/odoo#105470
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
@@ -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',
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import sale_order_line
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user