Skip to content

Commit

Permalink
[FIX] mrp: force kit to be consumable product
Browse files Browse the repository at this point in the history
Kits must be consumable products, they might never be in stock.
Setting kits as storable products can create some issues if the user
creates some reordering rules.

Because kits might never be in stock, it's impossible to fullfil the
quantity of a reordering rule: asking a mini/maxi quantity on a kit
makes no sense.

Odoo will continuously propose to order more to reach the quantity
necessary for the kit, but will never reach the asked qty, as it's a
kit, not a manufactured product.

OPW-2448878

closes #65339

Signed-off-by: Steve Van Essche <svs-odoo@users.noreply.github.com>
  • Loading branch information
adwid committed Feb 23, 2021
1 parent b730d42 commit 3d34d58
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 12 deletions.
10 changes: 2 additions & 8 deletions addons/mrp/data/mrp_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@
<field name="categ_id" ref="product.product_category_5"/>
<field name="standard_price">600.0</field>
<field name="list_price">147.0</field>
<field name="type">product</field>
<field name="type">consu</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="description">Table kit</field>
Expand All @@ -407,6 +407,7 @@
</record>

<record id="product_product_table_kit_product_template" model="product.template">
<field name="type">consu</field>
<field name="route_ids" eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]"/>
</record>

Expand Down Expand Up @@ -543,13 +544,6 @@
<field name="product_qty">30</field>
<field name="location_id" ref="stock.stock_location_14"/>
</record>
<record id="stock_inventory_line_product_table_kit" model="stock.inventory.line">
<field name="product_id" ref="product_product_table_kit"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="inventory_id" ref="stock_inventory_drawer"/>
<field name="product_qty">30</field>
<field name="location_id" ref="stock.stock_location_14"/>
</record>

<function model="stock.inventory" name="_action_done">
<function eval="[[('state','=','draft'), ('id', '=', ref('stock_inventory_drawer'))]]" model="stock.inventory" name="search"/>
Expand Down
12 changes: 12 additions & 0 deletions addons/mrp/i18n/mrp.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,12 @@ msgstr ""
msgid "Followers (Partners)"
msgstr ""

#. module: mrp
#: code:addons/mrp/models/mrp_bom.py:86
#, python-format
msgid "For %s to be a kit, its product type must be 'Consumable'."
msgstr ""

#. module: mrp
#: model_terms:ir.ui.view,arch_db:mrp.report_mrporder
msgid "From"
Expand Down Expand Up @@ -3374,6 +3380,12 @@ msgstr ""
msgid "The operations for producing this BoM. When a routing is specified, the production orders will be executed through work orders, otherwise everything is processed in the production order itself. "
msgstr ""

#. module: mrp
#: code:addons/mrp/models/product.py:30
#, python-format
msgid "The product type of %s must be 'Consumable' because it has at least one kit-type bill of materials."
msgstr ""

#. module: mrp
#: code:addons/mrp/wizard/mrp_product_produce.py:56
#, python-format
Expand Down
7 changes: 7 additions & 0 deletions addons/mrp/models/mrp_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def _check_product_recursion(self):
if bom.bom_line_ids.filtered(lambda x: x.product_id.product_tmpl_id == bom.product_tmpl_id):
raise ValidationError(_('BoM line product %s should not be same as BoM product.') % bom.display_name)

@api.constrains('product_tmpl_id', 'product_id', 'type')
def _check_kit_is_consumable(self):
for bom in self.filtered(lambda b: b.type == 'phantom'):
if (bom.product_id and bom.product_id.type or bom.product_tmpl_id.type) != "consu":
raise ValidationError(_("For %s to be a kit, its product type must be 'Consumable'."
% (bom.product_id and bom.product_id.display_name or bom.product_tmpl_id.display_name)))

@api.onchange('product_uom_id')
def onchange_product_uom_id(self):
res = {}
Expand Down
9 changes: 8 additions & 1 deletion addons/mrp/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from datetime import timedelta
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.tools.float_utils import float_round
from odoo.exceptions import ValidationError


class ProductTemplate(models.Model):
Expand All @@ -22,6 +23,12 @@ def _compute_bom_count(self):
for product in self:
product.bom_count = self.env['mrp.bom'].search_count([('product_tmpl_id', '=', product.id)])

@api.constrains('type')
def _check_phantom_bom_is_consumable_template(self):
for product_tmpl in self:
if product_tmpl.type != 'consu' and 'phantom' in product_tmpl.bom_ids.mapped('type'):
raise ValidationError(_("The product type of %s must be 'Consumable' because it has at least one kit-type bill of materials." % product_tmpl.display_name))

@api.multi
def _compute_used_in_bom_count(self):
for template in self:
Expand Down
5 changes: 4 additions & 1 deletion addons/mrp/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ def setUpClass(cls):
user_group_mrp_manager = cls.env.ref('mrp.group_mrp_manager')

# Update demo products
(cls.product_2 | cls.product_3 | cls.product_4 | cls.product_5 | cls.product_6 | cls.product_7 | cls.product_8).write({
(cls.product_2 | cls.product_3 | cls.product_4 | cls.product_6 | cls.product_7 | cls.product_8).write({
'type': 'product',
})
cls.product_5.write({
'type': 'consu',
})

# User Data: mrp user and mrp manager
Users = cls.env['res.users'].with_context({'no_reset_password': True, 'mail_create_nosubscribe': True})
Expand Down
1 change: 1 addition & 0 deletions addons/mrp_bom_cost/tests/test_bom_price.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def setUp(self):
# Products.
self.dining_table = self._create_product('Dining Table', 1000)
self.table_head = self._create_product('Table Head', 300)
self.table_head.type = 'consu'
self.screw = self._create_product('Screw', 10)
self.leg = self._create_product('Leg', 25)
self.glass = self._create_product('Glass', 100)
Expand Down
6 changes: 4 additions & 2 deletions addons/sale_mrp/tests/test_sale_mrp_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def create_product(name, uom_id, routes=()):
product_a = create_product('Product A', self.uom_unit, routes=[route_manufacture, route_mto])
product_c = create_product('Product C', self.uom_kg)
product_b = create_product('Product B', self.uom_dozen, routes=[route_manufacture, route_mto])
product_b.write({
'type': 'consu'
})
product_d = create_product('Product D', self.uom_unit, routes=[route_manufacture, route_mto])

# ------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -332,7 +335,6 @@ def test_01_sale_mrp_delivery_kit(self):
""" Test delivered quantity on SO based on delivered quantity in pickings."""
# intial so
product = self.env.ref('mrp.product_product_table_kit')
self.env['stock.quant']._update_available_quantity(product, self.stock_location, -product.qty_available)
product.type = 'consu'
product.invoice_policy = 'delivery'
# Remove the MTO route as purchase is not installed and since the procurement removal the exception is directly raised
Expand Down Expand Up @@ -417,7 +419,7 @@ def test_02_sale_mrp_anglo_saxon(self):
Product = self.env['product.product']
self.finished_product = Product.create({
'name': 'Finished product',
'type': 'product',
'type': 'consu',
'uom_id': self.uom_unit.id,
'invoice_policy': 'delivery',
'categ_id': self.category.id})
Expand Down

2 comments on commit 3d34d58

@Robert-Eliuk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @adwid ,
I don't know if this is the right channel at all. This commit is causing our company some issues though. We can now not make some kits that we want and can not save any edited kits that contain non-consumable parts. Would it not make more sense to block kits from reordering rules we already have many kits made that this new check breaks.

Do I have to find a way to bypass this check on our database or is there a resolution that can be made for all?

Thanks,

@adwid
Copy link
Contributor Author

@adwid adwid commented on 3d34d58 Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @Robert-Eliuk

This is intended. We want to prevent the creation of kits on storable products. Therefore, changing the product type to 'Storable' will raise an error if this product already has a kit BoM). Perhaps you can create a ticket so that the support team can help you reconfigure your database to meet this new requirement?

Have a nice day!

Please sign in to comment.