Products
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 598f457b8bb..cc39e0b881a 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -200,6 +200,14 @@ class SaleOrder(models.Model):
('confirmation_date_conditional_required', "CHECK( (state IN ('sale', 'done') AND confirmation_date IS NOT NULL) OR state NOT IN ('sale', 'done') )", "A confirmed sales order requires a confirmation date."),
]
+ @api.constrains('company_id', 'order_line')
+ def _check_order_line_company_id(self):
+ for order in self:
+ companies = order.order_line.product_id.company_id
+ if companies and companies != order.company_id:
+ bad_products = order.order_line.product_id.filtered(lambda p: p.company_id and p.company_id != order.company_id)
+ raise ValidationError((_("Your quotation contains products from company %s whereas your quotation belongs to company %s. \n Please change the company of your quotation or remove the products from other companies (%s).") % (', '.join(companies.mapped('display_name')), order.company_id.display_name, ', '.join(bad_products.mapped('display_name')))))
+
@api.depends('pricelist_id', 'date_order', 'company_id')
def _compute_currency_rate(self):
for order in self:
diff --git a/addons/sale/tests/test_access_rights.py b/addons/sale/tests/test_access_rights.py
index 7bf99f104cd..98aec26b9cc 100644
--- a/addons/sale/tests/test_access_rights.py
+++ b/addons/sale/tests/test_access_rights.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.exceptions import AccessError, UserError
+from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tests import tagged
from .test_sale_common import TestCommonSaleNoChart
@@ -103,7 +103,8 @@ class TestAccessRights(TestCommonSaleNoChart):
# Salesperson can change a Sales Team of SO
self.order.with_user(self.user_salesperson_1).write({'team_id': self.sales_channel.id})
# Salesperson can't create the SO of other salesperson
- with self.assertRaises(AccessError):
+ # raise ValidationError instead of AccessError due to constraint
+ with self.assertRaises(ValidationError):
self.env['sale.order'].with_user(self.user_salesperson_1).create({
'partner_id': self.partner_customer_usd.id,
'user_id': self.user_salesperson.id
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index 1efb27f088c..541855846b4 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -313,6 +313,7 @@
0:
diff --git a/addons/sale_management/models/sale_order_template.py b/addons/sale_management/models/sale_order_template.py
index 8e3dc818ef4..ee21cbce5d6 100644
--- a/addons/sale_management/models/sale_order_template.py
+++ b/addons/sale_management/models/sale_order_template.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import api, fields, models
-from odoo.exceptions import UserError
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError, ValidationError
class SaleOrderTemplate(models.Model):
@@ -28,6 +28,22 @@ class SaleOrderTemplate(models.Model):
domain=[('model', '=', 'sale.order')],
help="This e-mail template will be sent on confirmation. Leave empty to send nothing.")
active = fields.Boolean(default=True, help="If unchecked, it will allow you to hide the quotation template without removing it.")
+ company_id = fields.Many2one('res.company', string='Company')
+
+ @api.constrains('company_id', 'sale_order_template_line_ids', 'sale_order_template_option_ids')
+ def _check_company_id(self):
+ for template in self:
+ companies = template.mapped('sale_order_template_line_ids.product_id.company_id') | template.mapped('sale_order_template_option_ids.product_id.company_id')
+ if len(companies) > 1:
+ raise ValidationError(_("Your template cannot contain products from multiple companies."))
+ elif companies and companies != template.company_id:
+ raise ValidationError((_("Your template contains products from company %s whereas your template belongs to company %s. \n Please change the company of your template or remove the products from other companies.") % (companies.mapped('display_name'), template.company_id.display_name)))
+
+ @api.onchange('sale_order_template_line_ids', 'sale_order_template_option_ids')
+ def _onchange_template_line_ids(self):
+ companies = self.mapped('sale_order_template_option_ids.product_id.company_id') | self.mapped('sale_order_template_line_ids.product_id.company_id')
+ if companies and self.company_id not in companies:
+ self.company_id = companies[0]
def write(self, vals):
if 'active' in vals and not vals.get('active'):
diff --git a/addons/sale_management/security/sale_management_security.xml b/addons/sale_management/security/sale_management_security.xml
index 0d838862462..2ec489b6650 100644
--- a/addons/sale_management/security/sale_management_security.xml
+++ b/addons/sale_management/security/sale_management_security.xml
@@ -6,4 +6,11 @@
+
+ Quotation Template multi-company
+
+
+ ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
+
+
diff --git a/addons/sale_management/views/sale_order_template_views.xml b/addons/sale_management/views/sale_order_template_views.xml
index 73c79688c53..16b282899cc 100644
--- a/addons/sale_management/views/sale_order_template_views.xml
+++ b/addons/sale_management/views/sale_order_template_views.xml
@@ -33,6 +33,9 @@
days
+
+
+
diff --git a/addons/sale_management/views/sale_order_views.xml b/addons/sale_management/views/sale_order_views.xml
index e4e78db4a1b..94d5c1ca2c1 100644
--- a/addons/sale_management/views/sale_order_views.xml
+++ b/addons/sale_management/views/sale_order_views.xml
@@ -11,7 +11,7 @@