summaryrefslogtreecommitdiff
path: root/addons/mrp/tests/test_backorder.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mrp/tests/test_backorder.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mrp/tests/test_backorder.py')
-rw-r--r--addons/mrp/tests/test_backorder.py360
1 files changed, 360 insertions, 0 deletions
diff --git a/addons/mrp/tests/test_backorder.py b/addons/mrp/tests/test_backorder.py
new file mode 100644
index 00000000..eadd7847
--- /dev/null
+++ b/addons/mrp/tests/test_backorder.py
@@ -0,0 +1,360 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.mrp.tests.common import TestMrpCommon
+from odoo.tests import Form
+from odoo.tests.common import SavepointCase
+
+
+class TestMrpProductionBackorder(TestMrpCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.stock_location = cls.env.ref('stock.stock_location_stock')
+
+ def setUp(self):
+ super().setUp()
+ warehouse_form = Form(self.env['stock.warehouse'])
+ warehouse_form.name = 'Test Warehouse'
+ warehouse_form.code = 'TWH'
+ self.warehouse = warehouse_form.save()
+
+ def test_no_tracking_1(self):
+ """Create a MO for 4 product. Produce 4. The backorder button should
+ not appear and hitting mark as done should not open the backorder wizard.
+ The name of the MO should be MO/001.
+ """
+ mo = self.generate_mo(qty_final=4)[0]
+
+ mo_form = Form(mo)
+ mo_form.qty_producing = 4
+ mo = mo_form.save()
+
+ # No backorder is proposed
+ self.assertTrue(mo.button_mark_done())
+ self.assertEqual(mo._get_quantity_to_backorder(), 0)
+ self.assertTrue("-001" not in mo.name)
+
+ def test_no_tracking_2(self):
+ """Create a MO for 4 product. Produce 1. The backorder button should
+ appear and hitting mark as done should open the backorder wizard. In the backorder
+ wizard, choose to do the backorder. A new MO for 3 self.untracked_bom should be
+ created.
+ The sequence of the first MO should be MO/001-01, the sequence of the second MO
+ should be MO/001-02.
+ Check that all MO are reachable through the procurement group.
+ """
+ production, _, _, product_to_use_1, _ = self.generate_mo(qty_final=4, qty_base_1=3)
+ self.assertEqual(production.state, 'confirmed')
+ self.assertEqual(production.reserve_visible, True)
+
+ # Make some stock and reserve
+ for product in production.move_raw_ids.product_id:
+ self.env['stock.quant'].with_context(inventory_mode=True).create({
+ 'product_id': product.id,
+ 'inventory_quantity': 100,
+ 'location_id': production.location_src_id.id,
+ })
+ production.action_assign()
+ self.assertEqual(production.state, 'confirmed')
+ self.assertEqual(production.reserve_visible, False)
+
+ mo_form = Form(production)
+ mo_form.qty_producing = 1
+ production = mo_form.save()
+
+ action = production.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+
+ # Two related MO to the procurement group
+ self.assertEqual(len(production.procurement_group_id.mrp_production_ids), 2)
+
+ # Check MO backorder
+ mo_backorder = production.procurement_group_id.mrp_production_ids[-1]
+ self.assertEqual(mo_backorder.product_id.id, production.product_id.id)
+ self.assertEqual(mo_backorder.product_qty, 3)
+ self.assertEqual(sum(mo_backorder.move_raw_ids.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_uom_qty")), 9)
+ self.assertEqual(mo_backorder.reserve_visible, False) # the reservation of the first MO should've been moved here
+
+ def test_no_tracking_pbm_1(self):
+ """Create a MO for 4 product. Produce 1. The backorder button should
+ appear and hitting mark as done should open the backorder wizard. In the backorder
+ wizard, choose to do the backorder. A new MO for 3 self.untracked_bom should be
+ created.
+ The sequence of the first MO should be MO/001-01, the sequence of the second MO
+ should be MO/001-02.
+ Check that all MO are reachable through the procurement group.
+ """
+ with Form(self.warehouse) as warehouse:
+ warehouse.manufacture_steps = 'pbm'
+
+ production, _, product_to_build, product_to_use_1, product_to_use_2 = self.generate_mo(qty_base_1=4, qty_final=4, picking_type_id=self.warehouse.manu_type_id)
+
+ move_raw_ids = production.move_raw_ids
+ self.assertEqual(len(move_raw_ids), 2)
+ self.assertEqual(set(move_raw_ids.mapped("product_id")), {product_to_use_1, product_to_use_2})
+
+ pbm_move = move_raw_ids.move_orig_ids
+ self.assertEqual(len(pbm_move), 2)
+ self.assertEqual(set(pbm_move.mapped("product_id")), {product_to_use_1, product_to_use_2})
+ self.assertFalse(pbm_move.move_orig_ids)
+
+ mo_form = Form(production)
+ mo_form.qty_producing = 1
+ production = mo_form.save()
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_qty")), 16)
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_2.id).mapped("product_qty")), 4)
+
+ action = production.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+
+ mo_backorder = production.procurement_group_id.mrp_production_ids[-1]
+ self.assertEqual(mo_backorder.delivery_count, 1)
+
+ pbm_move |= mo_backorder.move_raw_ids.move_orig_ids
+ # Check that quantity is correct
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_qty")), 16)
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_2.id).mapped("product_qty")), 4)
+
+ self.assertFalse(pbm_move.move_orig_ids)
+
+ def test_no_tracking_pbm_sam_1(self):
+ """Create a MO for 4 product. Produce 1. The backorder button should
+ appear and hitting mark as done should open the backorder wizard. In the backorder
+ wizard, choose to do the backorder. A new MO for 3 self.untracked_bom should be
+ created.
+ The sequence of the first MO should be MO/001-01, the sequence of the second MO
+ should be MO/001-02.
+ Check that all MO are reachable through the procurement group.
+ """
+ with Form(self.warehouse) as warehouse:
+ warehouse.manufacture_steps = 'pbm_sam'
+ production, _, product_to_build, product_to_use_1, product_to_use_2 = self.generate_mo(qty_base_1=4, qty_final=4, picking_type_id=self.warehouse.manu_type_id)
+
+ move_raw_ids = production.move_raw_ids
+ self.assertEqual(len(move_raw_ids), 2)
+ self.assertEqual(set(move_raw_ids.mapped("product_id")), {product_to_use_1, product_to_use_2})
+
+ pbm_move = move_raw_ids.move_orig_ids
+ self.assertEqual(len(pbm_move), 2)
+ self.assertEqual(set(pbm_move.mapped("product_id")), {product_to_use_1, product_to_use_2})
+ self.assertFalse(pbm_move.move_orig_ids)
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_qty")), 16)
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_2.id).mapped("product_qty")), 4)
+
+ sam_move = production.move_finished_ids.move_dest_ids
+ self.assertEqual(len(sam_move), 1)
+ self.assertEqual(sam_move.product_id.id, product_to_build.id)
+ self.assertEqual(sum(sam_move.mapped("product_qty")), 4)
+
+ mo_form = Form(production)
+ mo_form.qty_producing = 1
+ production = mo_form.save()
+
+ action = production.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+
+ mo_backorder = production.procurement_group_id.mrp_production_ids[-1]
+ self.assertEqual(mo_backorder.delivery_count, 2)
+
+ pbm_move |= mo_backorder.move_raw_ids.move_orig_ids
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_qty")), 16)
+ self.assertEqual(sum(pbm_move.filtered(lambda m: m.product_id.id == product_to_use_2.id).mapped("product_qty")), 4)
+
+ sam_move |= mo_backorder.move_finished_ids.move_orig_ids
+ self.assertEqual(sum(sam_move.mapped("product_qty")), 4)
+
+ def test_tracking_backorder_series_lot_1(self):
+ """ Create a MO of 4 tracked products. all component is tracked by lots
+ Produce one by one with one bakorder for each until end.
+ """
+ nb_product_todo = 4
+ production, _, p_final, p1, p2 = self.generate_mo(qty_final=nb_product_todo, tracking_final='lot', tracking_base_1='lot', tracking_base_2='lot')
+ lot_final = self.env['stock.production.lot'].create({
+ 'name': 'lot_final',
+ 'product_id': p_final.id,
+ 'company_id': self.env.company.id,
+ })
+ lot_1 = self.env['stock.production.lot'].create({
+ 'name': 'lot_consumed_1',
+ 'product_id': p1.id,
+ 'company_id': self.env.company.id,
+ })
+ lot_2 = self.env['stock.production.lot'].create({
+ 'name': 'lot_consumed_2',
+ 'product_id': p2.id,
+ 'company_id': self.env.company.id,
+ })
+
+ self.env['stock.quant']._update_available_quantity(p1, self.stock_location, nb_product_todo*4, lot_id=lot_1)
+ self.env['stock.quant']._update_available_quantity(p2, self.stock_location, nb_product_todo, lot_id=lot_2)
+
+ production.action_assign()
+ active_production = production
+ for i in range(nb_product_todo):
+
+ details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
+ with details_operation_form.move_line_ids.edit(0) as ml:
+ ml.qty_done = 4
+ ml.lot_id = lot_1
+ details_operation_form.save()
+ details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
+ with details_operation_form.move_line_ids.edit(0) as ml:
+ ml.qty_done = 1
+ ml.lot_id = lot_2
+ details_operation_form.save()
+
+ production_form = Form(active_production)
+ production_form.qty_producing = 1
+ production_form.lot_producing_id = lot_final
+ active_production = production_form.save()
+
+ active_production.button_mark_done()
+ if i + 1 != nb_product_todo: # If last MO, don't make a backorder
+ action = active_production.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+ active_production = active_production.procurement_group_id.mrp_production_ids[-1]
+
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot_final), nb_product_todo, f'You should have the {nb_product_todo} final product in stock')
+ self.assertEqual(len(production.procurement_group_id.mrp_production_ids), nb_product_todo)
+
+ def test_tracking_backorder_series_serial_1(self):
+ """ Create a MO of 4 tracked products (serial) with pbm_sam.
+ all component is tracked by serial
+ Produce one by one with one bakorder for each until end.
+ """
+ nb_product_todo = 4
+ production, _, p_final, p1, p2 = self.generate_mo(qty_final=nb_product_todo, tracking_final='serial', tracking_base_1='serial', tracking_base_2='serial', qty_base_1=1)
+ serials_final, serials_p1, serials_p2 = [], [], []
+ for i in range(nb_product_todo):
+ serials_final.append(self.env['stock.production.lot'].create({
+ 'name': f'lot_final_{i}',
+ 'product_id': p_final.id,
+ 'company_id': self.env.company.id,
+ }))
+ serials_p1.append(self.env['stock.production.lot'].create({
+ 'name': f'lot_consumed_1_{i}',
+ 'product_id': p1.id,
+ 'company_id': self.env.company.id,
+ }))
+ serials_p2.append(self.env['stock.production.lot'].create({
+ 'name': f'lot_consumed_2_{i}',
+ 'product_id': p2.id,
+ 'company_id': self.env.company.id,
+ }))
+ self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 1, lot_id=serials_p1[-1])
+ self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 1, lot_id=serials_p2[-1])
+
+ production.action_assign()
+ active_production = production
+ for i in range(nb_product_todo):
+
+ details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
+ with details_operation_form.move_line_ids.edit(0) as ml:
+ ml.qty_done = 1
+ ml.lot_id = serials_p1[i]
+ details_operation_form.save()
+ details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
+ with details_operation_form.move_line_ids.edit(0) as ml:
+ ml.qty_done = 1
+ ml.lot_id = serials_p2[i]
+ details_operation_form.save()
+
+ production_form = Form(active_production)
+ production_form.qty_producing = 1
+ production_form.lot_producing_id = serials_final[i]
+ active_production = production_form.save()
+
+ active_production.button_mark_done()
+ if i + 1 != nb_product_todo: # If last MO, don't make a backorder
+ action = active_production.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+ active_production = active_production.procurement_group_id.mrp_production_ids[-1]
+
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location), nb_product_todo, f'You should have the {nb_product_todo} final product in stock')
+ self.assertEqual(len(production.procurement_group_id.mrp_production_ids), nb_product_todo)
+
+ def test_backorder_name(self):
+ def produce_one(mo):
+ mo_form = Form(mo)
+ mo_form.qty_producing = 1
+ mo = mo_form.save()
+ action = mo.button_mark_done()
+ backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+ backorder.save().action_backorder()
+ return mo.procurement_group_id.mrp_production_ids[-1]
+
+ default_picking_type_id = self.env['mrp.production']._get_default_picking_type()
+ default_picking_type = self.env['stock.picking.type'].browse(default_picking_type_id)
+ mo_sequence = default_picking_type.sequence_id
+
+ mo_sequence.prefix = "WH-MO-"
+ initial_mo_name = mo_sequence.prefix + str(mo_sequence.number_next_actual).zfill(mo_sequence.padding)
+
+ production = self.generate_mo(qty_final=5)[0]
+ self.assertEqual(production.name, initial_mo_name)
+
+ backorder = produce_one(production)
+ self.assertEqual(production.name, initial_mo_name + "-001")
+ self.assertEqual(backorder.name, initial_mo_name + "-002")
+
+ backorder.backorder_sequence = 998
+
+ for seq in [998, 999, 1000]:
+ new_backorder = produce_one(backorder)
+ self.assertEqual(backorder.name, initial_mo_name + "-" + str(seq))
+ self.assertEqual(new_backorder.name, initial_mo_name + "-" + str(seq + 1))
+ backorder = new_backorder
+
+
+class TestMrpWorkorderBackorder(SavepointCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestMrpWorkorderBackorder, cls).setUpClass()
+ cls.uom_unit = cls.env['uom.uom'].search([
+ ('category_id', '=', cls.env.ref('uom.product_uom_categ_unit').id),
+ ('uom_type', '=', 'reference')
+ ], limit=1)
+ cls.finished1 = cls.env['product.product'].create({
+ 'name': 'finished1',
+ 'type': 'product',
+ })
+ cls.compfinished1 = cls.env['product.product'].create({
+ 'name': 'compfinished1',
+ 'type': 'product',
+ })
+ cls.compfinished2 = cls.env['product.product'].create({
+ 'name': 'compfinished2',
+ 'type': 'product',
+ })
+ cls.workcenter1 = cls.env['mrp.workcenter'].create({
+ 'name': 'workcenter1',
+ })
+ cls.workcenter2 = cls.env['mrp.workcenter'].create({
+ 'name': 'workcenter2',
+ })
+
+ cls.bom_finished1 = cls.env['mrp.bom'].create({
+ 'product_id': cls.finished1.id,
+ 'product_tmpl_id': cls.finished1.product_tmpl_id.id,
+ 'product_uom_id': cls.uom_unit.id,
+ 'product_qty': 1,
+ 'consumption': 'flexible',
+ 'type': 'normal',
+ 'bom_line_ids': [
+ (0, 0, {'product_id': cls.compfinished1.id, 'product_qty': 1}),
+ (0, 0, {'product_id': cls.compfinished2.id, 'product_qty': 1}),
+ ],
+ 'operation_ids': [
+ (0, 0, {'sequence': 1, 'name': 'finished operation 1', 'workcenter_id': cls.workcenter1.id}),
+ (0, 0, {'sequence': 2, 'name': 'finished operation 2', 'workcenter_id': cls.workcenter2.id}),
+ ],
+ })
+ cls.bom_finished1.bom_line_ids[0].operation_id = cls.bom_finished1.operation_ids[0].id
+ cls.bom_finished1.bom_line_ids[1].operation_id = cls.bom_finished1.operation_ids[1].id