diff --git a/addons/mrp/data/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml index 0c922662370ad536c341880941f2a637242fb9f4..3a7276be0ad26e0b710fe3b6bbeb8d8699106e73 100644 --- a/addons/mrp/data/mrp_demo.xml +++ b/addons/mrp/data/mrp_demo.xml @@ -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> @@ -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> @@ -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"/> diff --git a/addons/mrp/i18n/mrp.pot b/addons/mrp/i18n/mrp.pot index 8fb1dc5f2134e49972c80c441dca4aed888c984f..bd5a8eef310189a293462c82a1e1631ef32adf22 100644 --- a/addons/mrp/i18n/mrp.pot +++ b/addons/mrp/i18n/mrp.pot @@ -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" @@ -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 diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 43a178c479fd1ef74179fe2c55062715061afc7a..4b4585b63bb5ea1773a6b28ef1f86b0493564658 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -79,6 +79,13 @@ class MrpBom(models.Model): 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 = {} diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index 9d77fc44a78d9ac6413781d2f4b8f360d93ba0c6..560ce82eeb74e01471532d050c0a00869ee4def2 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -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): @@ -22,6 +23,12 @@ class ProductTemplate(models.Model): 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: diff --git a/addons/mrp/tests/common.py b/addons/mrp/tests/common.py index 47cb622de23d9926ca4360eecc3eeb437ba2ccfc..8e3b8bb56c480a43a6ddcf932ca731b19836c222 100644 --- a/addons/mrp/tests/common.py +++ b/addons/mrp/tests/common.py @@ -55,9 +55,12 @@ class TestMrpCommon(common2.TestStockCommon): 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}) diff --git a/addons/mrp_bom_cost/tests/test_bom_price.py b/addons/mrp_bom_cost/tests/test_bom_price.py index c91f6203bca5dc68fdef73035d47dfabfcd8aafc..5846b1461efb2995d1a1f940b7eab5edd7bddeee 100644 --- a/addons/mrp_bom_cost/tests/test_bom_price.py +++ b/addons/mrp_bom_cost/tests/test_bom_price.py @@ -25,6 +25,7 @@ class TestBom(common.TransactionCase): # 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) diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 27eebfae4434e57d191b7c74656c70550da28858..ced79634ab8baeb124f8b8fdeb84b5ac60a2fe7a 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -66,6 +66,9 @@ class TestSaleMrpFlow(common.TransactionCase): 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]) # ------------------------------------------------------------------------------------------ @@ -332,7 +335,6 @@ class TestSaleMrpFlow(common.TransactionCase): """ 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 @@ -417,7 +419,7 @@ class TestSaleMrpFlow(common.TransactionCase): 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})