[IMP] delivery, product, sale(_management): make company consistence
Purpose of the commit is to make the multi company consistence with product, sale order and order template. So the Common Product Catalog setting is removed from generel settings because its behavior wasn't really clean from a technical point of view (disabling the rule on products access) and wouldn't work as well with new multi-company logic. The default logic of sharing products will be kept, but when someone wants to limit products sharing, he will do so product by product, by setting the company_id. Also the default company_id on product will be blank so default product will be a sharable by multi company. and added company_id on sale templates so user can select his/her own company or the templates which are common. task-2025168 Closes: #34342
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import res_config_settings
|
||||
from . import delivery_carrier
|
||||
from . import delivery_grid
|
||||
from . import product_packaging
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
def set_values(self):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
rule = self.env.ref('delivery.delivery_carrier_comp_rule', False)
|
||||
if rule:
|
||||
rule.write({'active': not bool(self.company_share_product)})
|
||||
@@ -6,7 +6,6 @@
|
||||
<field name="model_id" ref="model_delivery_carrier"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force"> ['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -107,8 +107,7 @@ class ProductTemplate(models.Model):
|
||||
default=_get_default_uom_id, required=True,
|
||||
help="Default unit of measure used for purchase orders. It must be in the same category as the default unit of measure.")
|
||||
company_id = fields.Many2one(
|
||||
'res.company', 'Company',
|
||||
default=lambda self: self.env.company, index=1)
|
||||
'res.company', 'Company', index=1)
|
||||
packaging_ids = fields.One2many(
|
||||
'product.packaging', string="Product Packages", compute="_compute_packaging_ids", inverse="_set_packaging_ids",
|
||||
help="Gives the different ways to package the same product.")
|
||||
|
||||
@@ -7,11 +7,6 @@ from odoo import api, fields, models
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
company_share_product = fields.Boolean(
|
||||
'Share product to all companies',
|
||||
help="Share your product to all companies defined in your instance.\n"
|
||||
" * Checked : Product are visible for every company, even if a company is defined on the partner.\n"
|
||||
" * Unchecked : Each company can see only its product (product where company is defined). Product not related to a company are visible for all companies.")
|
||||
group_uom = fields.Boolean("Units of Measure", implied_group='uom.group_uom')
|
||||
group_product_variant = fields.Boolean("Variants", implied_group='product.group_product_variant')
|
||||
module_sale_product_configurator = fields.Boolean("Product Configurator")
|
||||
@@ -47,17 +42,3 @@ class ResConfigSettings(models.TransientModel):
|
||||
If the user enables the product configurator -> enable the product variants as well"""
|
||||
if self.module_sale_product_configurator and not self.group_product_variant:
|
||||
self.group_product_variant = True
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
product_rule = self.env.ref('product.product_comp_rule')
|
||||
res.update(
|
||||
company_share_product=not bool(product_rule.active),
|
||||
)
|
||||
return res
|
||||
|
||||
def set_values(self):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
product_rule = self.env.ref('product.product_comp_rule')
|
||||
product_rule.write({'active': not bool(self.company_share_product)})
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
<field name="model_id" ref="model_product_template"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force"> ['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="product_pricelist_comp_rule">
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='company_share_partner']" position="after">
|
||||
<field name="company_share_product" class="oe_inline"/>
|
||||
<label string="Common Product Catalog" class="o_light_label" for="company_share_product"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='multi_company']" position="before">
|
||||
<h2>Products</h2>
|
||||
<div class="row mt16 o_settings_container" id="product_general_settings">
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -313,6 +313,7 @@
|
||||
<group attrs="{'invisible': [('display_type', '!=', False)]}">
|
||||
<field name="product_updatable" invisible="1"/>
|
||||
<field name="product_id"
|
||||
domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'company_id': parent.company_id}"
|
||||
attrs="{
|
||||
'readonly': [('product_updatable', '=', False)],
|
||||
@@ -417,6 +418,7 @@
|
||||
'default_lst_price': price_unit,
|
||||
'default_description_sale': name
|
||||
}"
|
||||
domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"
|
||||
widget="product_configurator"
|
||||
/>
|
||||
<field name="product_template_id"
|
||||
@@ -436,6 +438,7 @@
|
||||
'default_lst_price': price_unit,
|
||||
'default_description_sale': name
|
||||
}"
|
||||
domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"
|
||||
widget="product_configurator"/>
|
||||
<field name="name" widget="section_and_note_text" optional="show"/>
|
||||
<field
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
@@ -13,12 +13,21 @@ class SaleOrder(models.Model):
|
||||
sale_order_template_id = fields.Many2one(
|
||||
'sale.order.template', 'Quotation Template',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]})
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
|
||||
sale_order_option_ids = fields.One2many(
|
||||
'sale.order.option', 'order_id', 'Optional Products Lines',
|
||||
copy=True, readonly=True,
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]})
|
||||
|
||||
@api.constrains('company_id', 'sale_order_option_ids')
|
||||
def _check_optional_product_company_id(self):
|
||||
for order in self:
|
||||
companies = order.sale_order_option_ids.product_id.company_id
|
||||
if companies and companies != order.company_id:
|
||||
bad_products = order.sale_order_option_ids.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.returns('self', lambda value: value.id)
|
||||
def copy(self, default=None):
|
||||
if self.sale_order_template_id and self.sale_order_template_id.number_of_days > 0:
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -6,4 +6,11 @@
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_template_rule_company" model="ir.rule">
|
||||
<field name="name">Quotation Template multi-company</field>
|
||||
<field name="model_id" ref="model_sale_order_template"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
<field name="number_of_days" class="oe_inline"/> days
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook name="main_book">
|
||||
<page string="Lines">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<field name="sale_order_option_ids" mode="tree,form,kanban">
|
||||
<form string="Optional Products">
|
||||
<group>
|
||||
<field name="product_id"/>
|
||||
<field name="product_id" domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"/>
|
||||
<field name="name"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom_id" groups="uom.group_uom"/>
|
||||
@@ -67,7 +67,7 @@
|
||||
<create name="add_product_control" string="Add a product"/>
|
||||
</control>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_id" domain="[('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]"/>
|
||||
<field name="name" optional="show"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom_id" string="UoM" groups="uom.group_uom" optional="show"/>
|
||||
|
||||
Reference in New Issue
Block a user