diff --git a/mrp_no_partial/__init__.py b/mrp_no_partial/__init__.py index 0650744f6..aee8895e7 100644 --- a/mrp_no_partial/__init__.py +++ b/mrp_no_partial/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/mrp_no_partial/readme/DESCRIPTION.rst b/mrp_no_partial/readme/DESCRIPTION.rst index b7f838ecf..14a12996b 100644 --- a/mrp_no_partial/readme/DESCRIPTION.rst +++ b/mrp_no_partial/readme/DESCRIPTION.rst @@ -1,2 +1,4 @@ With this module, you can restrict Production Orders validation if all product quantities don't correspond to the quantities to do. + +It restricts also the produce action and does not allow void values on lots. diff --git a/mrp_no_partial/tests/test_no_partial.py b/mrp_no_partial/tests/test_no_partial.py index a4a225e72..632fe9477 100644 --- a/mrp_no_partial/tests/test_no_partial.py +++ b/mrp_no_partial/tests/test_no_partial.py @@ -14,6 +14,7 @@ class TestNoPartial(common.TransactionCase): self.bom_obj = self.env['mrp.bom'] self.location = self.env.ref('stock.stock_location_stock') self.picking_type = self.env.ref('mrp.picking_type_manufacturing') + self.produce_wizard_obj = self.env['mrp.product.produce'] self.picking_type.mrp_no_partial = True @@ -24,6 +25,12 @@ class TestNoPartial(common.TransactionCase): 'type': 'product', 'uom_id': self.uom.id, }) + + self.product_2 = self.env['product.product'].create({ + 'name': 'Product MRP 2', + 'type': 'product', + 'uom_id': self.uom.id, + }) self.product_raw_material = self.env['product.product'].create({ 'name': 'Raw Material', 'type': 'product', @@ -50,6 +57,20 @@ class TestNoPartial(common.TransactionCase): }) product_qty.change_product_qty() + self.product_raw_material_lot = self.env['product.product'].create({ + 'name': 'Raw Material Lotted', + 'tracking': 'lot', + 'type': 'product', + 'uom_id': self.uom.id, + }) + + product_qty = self.env['stock.change.product.qty'].create({ + 'location_id': self.location.id, + 'product_id': self.product_raw_material_lot.id, + 'new_quantity': 100.0, + }) + product_qty.change_product_qty() + self.bom = self.env['mrp.bom'].create({ 'product_id': self.product.id, 'product_tmpl_id': self.product.product_tmpl_id.id, @@ -67,6 +88,28 @@ class TestNoPartial(common.TransactionCase): ]) }) + self.bom_2 = self.env['mrp.bom'].create({ + 'product_id': self.product_2.id, + 'product_tmpl_id': self.product_2.product_tmpl_id.id, + 'bom_line_ids': ([ + (0, 0, { + 'product_id': self.product_raw_material.id, + 'product_qty': 5, + 'product_uom_id': self.uom.id + }), + (0, 0, { + 'product_id': self.product_raw_material2.id, + 'product_qty': 4, + 'product_uom_id': self.uom.id + }), + (0, 0, { + 'product_id': self.product_raw_material_lot.id, + 'product_qty': 3, + 'product_uom_id': self.uom.id + }), + ]) + }) + # Create Production Order vals = { 'picking_type_id': self.picking_type.id, @@ -78,6 +121,17 @@ class TestNoPartial(common.TransactionCase): } self.production = self.production_obj.create(vals) + # Create Production Order with lot + vals = { + 'picking_type_id': self.picking_type.id, + 'product_id': self.product_2.id, + 'product_qty': 1, + 'product_uom_id': self.uom.id, + 'bom_id': self.bom_2.id + + } + self.production_2 = self.production_obj.create(vals) + def test_no_partial(self): self.production.action_assign() self.assertEquals( @@ -102,3 +156,17 @@ class TestNoPartial(common.TransactionCase): self.picking_type.mrp_no_partial = False self.production.action_assign() self.production.button_mark_done() + + def test_partial_with_lots(self): + # Assign production order + # Launch production wizard + # Check if error is raised + self.production_2.action_assign() + wizard = self.produce_wizard_obj.with_context( + active_id=self.production_2.id).create({}) + self.assertEquals( + 1, + len(wizard.consume_line_ids) + ) + with self.assertRaises(ValidationError): + wizard.do_produce() diff --git a/mrp_no_partial/wizards/__init__.py b/mrp_no_partial/wizards/__init__.py new file mode 100644 index 000000000..216003589 --- /dev/null +++ b/mrp_no_partial/wizards/__init__.py @@ -0,0 +1 @@ +from . import mrp_product_produce diff --git a/mrp_no_partial/wizards/mrp_product_produce.py b/mrp_no_partial/wizards/mrp_product_produce.py new file mode 100644 index 000000000..8852143c0 --- /dev/null +++ b/mrp_no_partial/wizards/mrp_product_produce.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ +from odoo.tools import float_compare +from odoo.exceptions import ValidationError + + +class MrpProductProduce(models.TransientModel): + + _inherit = 'mrp.product.produce' + + @api.multi + def do_produce(self): + if self.production_id.picking_type_id.mrp_no_partial: + incomplete_move_lots = self.consume_line_ids.filtered( + lambda l: float_compare( + l.quantity, + l.quantity_done, + precision_rounding=l.product_id.uom_id.rounding)) + if incomplete_move_lots: + raise ValidationError( + _('Please fill in every lot quantity for this ' + 'Production Order. You cannot validate a ' + 'Production Order with not done quantities!')) + return super(MrpProductProduce, self).do_produce()