diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index d8ea1839935..054f6557a96 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -1823,24 +1823,19 @@ class MrpProduction(models.Model): return action def _pre_button_mark_done(self): - immediate_productions = self.browse() for production in self: - if not float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding) or any( - not float_is_zero(ml.qty_done, precision_digits=ml.product_uom_id.rounding) for - ml in production.move_raw_ids.move_line_ids.filtered(lambda m: m.state not in ('done', 'cancel'))): - immediate_productions |= production - continue - production._set_quantities() + if float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding): + production._set_quantities() - for production in immediate_productions: + for production in self: if float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding): raise UserError(_('The quantity to produce must be positive!')) if production.move_raw_ids and not any(production.move_raw_ids.mapped('quantity_done')): raise UserError(_("You must indicate a non-zero amount consumed for at least one of your components")) - consumption_issues = immediate_productions._get_consumption_issues() + consumption_issues = self._get_consumption_issues() if consumption_issues: - return immediate_productions._action_generate_consumption_wizard(consumption_issues) + return self._action_generate_consumption_wizard(consumption_issues) quantity_issues = self._get_quantity_produced_issues() if quantity_issues: @@ -2212,15 +2207,11 @@ class MrpProduction(models.Model): else: self.qty_producing = self.product_qty - self.qty_produced self._set_qty_producing() + for move in self.move_raw_ids.filtered(lambda m: m.state not in ['done', 'cancel']): rounding = move.product_uom.rounding - for move_line in move.move_line_ids: - if move_line.reserved_uom_qty: - move_line.qty_done = min(move_line.reserved_uom_qty, move_line.move_id.should_consume_qty) - if float_compare(move.quantity_done, move.should_consume_qty, precision_rounding=rounding) >= 0: - break - if float_compare(move.product_uom_qty, move.quantity_done, precision_rounding=move.product_uom.rounding) == 1: - if move.has_tracking in ('serial', 'lot'): + if move.manual_consumption: + if move.has_tracking in ('serial', 'lot') and float_is_zero(move.quantity_done, precision_rounding=rounding): missing_lot_id_products += "\n - %s" % move.product_id.display_name if missing_lot_id_products: error_msg = _('You need to supply Lot/Serial Number for products:') + missing_lot_id_products diff --git a/addons/mrp/tests/common_consume_tracked_component.py b/addons/mrp/tests/common_consume_tracked_component.py index 1e5f37a70d6..e3c11fdb1af 100644 --- a/addons/mrp/tests/common_consume_tracked_component.py +++ b/addons/mrp/tests/common_consume_tracked_component.py @@ -1,7 +1,7 @@ import copy from odoo.exceptions import UserError -from odoo.tests import common, Form +from odoo.tests import common class TestConsumeTrackedComponentCommon(common.TransactionCase): @@ -196,6 +196,7 @@ class TestConsumeTrackedComponentCommon(common.TransactionCase): isSerial = tracking == 'serial' isAvailable = all(move.state == 'assigned' for move in mrp_productions.move_raw_ids) + isComponentTracking = any(move.has_tracking != 'none' for move in mrp_productions.move_raw_ids) countOk = True length = len(mrp_productions) @@ -220,4 +221,13 @@ class TestConsumeTrackedComponentCommon(common.TransactionCase): i += 1 if isAvailable: - mrp_productions[i].button_mark_done() + error = False + try: + mrp_productions[i].button_mark_done() + except UserError: + error = True + + if isComponentTracking and not mrp_productions[i].picking_type_id.use_auto_consume_components_lots: + self.assertTrue(error, "Immediate Production shall raise an error when tracked product are not provided.") + else: + self.assertFalse(error, "Immediate Production shall not raise an error.") diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index e763cc34d2a..227a2474a33 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -1496,18 +1496,22 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(mo.move_finished_ids.quantity_done, 1) def test_immediate_validate_2(self): - """ In a production with a single available move raw, clicking on mark as done after filling quantity - for a stock move only will trigger an error as qty_producing is left to 0.""" + """ In a production non-tracked components, clicking on mark as done after filling quantity for a move be + processed as immediate production. The manual filling quantity will be kept.""" mo, bom, p_final, p1, p2 = self.generate_mo(qty_final=1, qty_base_1=1, qty_base_2=1) self.env['stock.quant']._update_available_quantity(p1, self.stock_location_components, 5.0) self.env['stock.quant']._update_available_quantity(p2, self.stock_location_components, 5.0) mo.action_assign() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 1 + ml.qty_done = 2 details_operation_form.save() - with self.assertRaises(UserError): - res_dict = mo.button_mark_done() + mo.move_raw_ids.filtered('quantity_done').manual_consumption = True # this should be set automatically in front end + mo.button_mark_done() + self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', 'done']) + self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [2, 1]) + self.assertEqual(mo.move_finished_ids.state, 'done') + self.assertEqual(mo.move_finished_ids.quantity_done, 1) def test_immediate_validate_3(self): """ In a production with a serial number tracked product. Check that the immediate production only creates @@ -1753,6 +1757,9 @@ class TestMrpOrder(TestMrpCommon): caused by decimal rounding conversions. """ + picking_type = self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')])[0] + picking_type.use_auto_consume_components_lots = True + # the overall decimal accuracy is set to 3 digits precision = self.env.ref('product.decimal_product_uom') precision.digits = 3 diff --git a/addons/mrp/tests/test_traceability.py b/addons/mrp/tests/test_traceability.py index 80403f9a98a..946d4ef98b3 100644 --- a/addons/mrp/tests/test_traceability.py +++ b/addons/mrp/tests/test_traceability.py @@ -376,6 +376,8 @@ class TestTraceability(TestMrpCommon): the user can produce several productA and can then produce some productB again """ stock_location = self.env.ref('stock.stock_location_stock') + picking_type = self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')])[0] + picking_type.use_auto_consume_components_lots = True productA, productB, productC = self.env['product.product'].create([{ 'name': 'Product A', diff --git a/addons/mrp_repair/tests/test_tracability.py b/addons/mrp_repair/tests/test_tracability.py index 19f0c1cde3e..bdc93f4e227 100644 --- a/addons/mrp_repair/tests/test_tracability.py +++ b/addons/mrp_repair/tests/test_tracability.py @@ -12,6 +12,8 @@ class TestRepairTraceability(TestMrpCommon): Test that removing a tracked component with a repair does not block the flow of using that component in another bom """ + picking_type = self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')])[0] + picking_type.use_auto_consume_components_lots = True product_to_repair = self.env['product.product'].create({ 'name': 'product first serial to act repair', 'tracking': 'serial', @@ -93,6 +95,9 @@ class TestRepairTraceability(TestMrpCommon): mo.button_mark_done() return mo + picking_type = self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')])[0] + picking_type.use_auto_consume_components_lots = True + stock_location = self.env.ref('stock.stock_location_stock') finished, component = self.env['product.product'].create([{