123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- from datetime import datetime, timedelta
- from odoo.addons.mrp.tests.common import TestMrpCommon
- from odoo.tests import Form
- from odoo.tests.common import TransactionCase
- class TestMrpProductionBackorder(TestMrpCommon):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.stock_location = cls.env.ref('stock.stock_location_stock')
- warehouse_form = Form(cls.env['stock.warehouse'])
- warehouse_form.name = 'Test Warehouse'
- warehouse_form.code = 'TWH'
- cls.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,
- })._apply_inventory()
- 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.
- """
- # Required for `manufacture_steps` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_adv_location")
- 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.
- """
- # Required for `manufacture_steps` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_adv_location")
- 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.lot'].create({
- 'name': 'lot_final',
- 'product_id': p_final.id,
- 'company_id': self.env.company.id,
- })
- lot_1 = self.env['stock.lot'].create({
- 'name': 'lot_consumed_1',
- 'product_id': p1.id,
- 'company_id': self.env.company.id,
- })
- lot_2 = self.env['stock.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_lot_2(self):
- """
- Create a MO with component tracked by lots. Produce a part of the demand
- by using some specific lots (not the ones suggested by the onchange).
- The components' reservation of the backorder should consider which lots
- have been consumed in the initial MO
- """
- production, _, _, p1, p2 = self.generate_mo(tracking_base_2='lot')
- lot1, lot2 = self.env['stock.lot'].create([{
- 'name': f'lot_consumed_{i}',
- 'product_id': p2.id,
- 'company_id': self.env.company.id,
- } for i in range(2)])
- self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 20)
- self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 2, lot_id=lot2)
- production.action_assign()
- production_form = Form(production)
- production_form.qty_producing = 3
- details_operation_form = Form(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 * 3
- details_operation_form.save()
- # Consume 1 Product from lot1 and 2 from lot 2
- p2_smls = production.move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids
- self.assertEqual(len(p2_smls), 2, 'One for each lot')
- details_operation_form = Form(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 = lot1
- with details_operation_form.move_line_ids.edit(1) as ml:
- ml.qty_done = 2
- ml.lot_id = lot2
- details_operation_form.save()
- production = production_form.save()
- action = production.button_mark_done()
- backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
- backorder.save().action_backorder()
- p2_bo_mls = production.procurement_group_id.mrp_production_ids[-1].move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids
- self.assertEqual(len(p2_bo_mls), 1)
- self.assertEqual(p2_bo_mls.lot_id, lot1)
- self.assertEqual(p2_bo_mls.reserved_qty, 2)
- def test_uom_backorder(self):
- """
- test backorder component UoM different from the bom's UoM
- """
- product_finished = self.env['product.product'].create({
- 'name': 'Young Tom',
- 'type': 'product',
- })
- product_component = self.env['product.product'].create({
- 'name': 'Botox',
- 'type': 'product',
- 'uom_id': self.env.ref('uom.product_uom_kgm').id,
- 'uom_po_id': self.env.ref('uom.product_uom_kgm').id,
- })
- mo_form = Form(self.env['mrp.production'])
- mo_form.product_id = product_finished
- mo_form.bom_id = self.env['mrp.bom'].create({
- 'product_id': product_finished.id,
- 'product_tmpl_id': product_finished.product_tmpl_id.id,
- 'product_uom_id': self.uom_unit.id,
- 'product_qty': 1.0,
- 'type': 'normal',
- 'consumption': 'flexible',
- 'bom_line_ids': [(0, 0, {
- 'product_id': product_component.id,
- 'product_qty': 1,
- 'product_uom_id':self.env.ref('uom.product_uom_gram').id,
- }),]
- })
- mo_form.product_qty = 1000
- mo = mo_form.save()
- mo.action_confirm()
- self.env['stock.quant']._update_available_quantity(product_component, self.stock_location, 1000)
- mo.action_assign()
- production_form = Form(mo)
- production_form.qty_producing = 300
- mo = production_form.save()
- action = mo.button_mark_done()
- backorder_form = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
- backorder_form.save().action_backorder()
- # 300 Grams consumed and 700 reserved
- self.assertAlmostEqual(self.env['stock.quant']._gather(product_component, self.stock_location).reserved_quantity, 0.7)
- 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.lot'].create({
- 'name': f'lot_final_{i}',
- 'product_id': p_final.id,
- 'company_id': self.env.company.id,
- }))
- serials_p1.append(self.env['stock.lot'].create({
- 'name': f'lot_consumed_1_{i}',
- 'product_id': p1.id,
- 'company_id': self.env.company.id,
- }))
- serials_p2.append(self.env['stock.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_tracking_backorder_immediate_production_serial_1(self):
- """ Create a MO to build 2 of a SN tracked product.
- Build both the starting MO and its backorder as immediate productions
- (i.e. Mark As Done without setting SN/filling any quantities)
- """
- mo, _, p_final, p1, p2 = self.generate_mo(qty_final=2, tracking_final='serial', qty_base_1=2, qty_base_2=2)
- self.env['stock.quant']._update_available_quantity(p1, self.stock_location_components, 2.0)
- self.env['stock.quant']._update_available_quantity(p2, self.stock_location_components, 2.0)
- mo.action_assign()
- res_dict = mo.button_mark_done()
- self.assertEqual(res_dict.get('res_model'), 'mrp.immediate.production')
- immediate_wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
- res_dict = immediate_wizard.process()
- self.assertEqual(res_dict.get('res_model'), 'mrp.production.backorder')
- backorder_wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context']))
- # backorder should automatically open
- action = backorder_wizard.save().action_backorder()
- self.assertEqual(action.get('res_model'), 'mrp.production')
- backorder_mo_form = Form(self.env[action['res_model']].with_context(action['context']).browse(action['res_id']))
- backorder_mo = backorder_mo_form.save()
- res_dict = backorder_mo.button_mark_done()
- self.assertEqual(res_dict.get('res_model'), 'mrp.immediate.production')
- immediate_wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
- immediate_wizard.process()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location), 2, "Incorrect number of final product produced.")
- self.assertEqual(len(self.env['stock.lot'].search([('product_id', '=', p_final.id)])), 2, "Serial Numbers were not correctly produced.")
- 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_id(self.env.company.id)
- 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
- def test_backorder_name_without_procurement_group(self):
- production = self.generate_mo(qty_final=5)[0]
- mo_form = Form(production)
- mo_form.qty_producing = 1
- mo = mo_form.save()
- # Remove pg to trigger fallback on backorder name
- mo.procurement_group_id = False
- action = mo.button_mark_done()
- backorder_form = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
- backorder_form.save().action_backorder()
- # The pg is back
- self.assertTrue(production.procurement_group_id)
- backorder_ids = production.procurement_group_id.mrp_production_ids[1]
- self.assertEqual(production.name.split('-')[0], backorder_ids.name.split('-')[0])
- self.assertEqual(int(production.name.split('-')[1]) + 1, int(backorder_ids.name.split('-')[1]))
- def test_split_merge(self):
- # Change 'Units' rounding to 1 (integer only quantities)
- self.uom_unit.rounding = 1
- # Create a mo for 10 products
- mo, _, _, p1, p2 = self.generate_mo(qty_final=10)
- # Split in 3 parts
- action = mo.action_split()
- wizard = Form(self.env[action['res_model']].with_context(action['context']))
- wizard.counter = 3
- action = wizard.save().action_split()
- # Should have 3 mos
- self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 3)
- mo1 = mo.procurement_group_id.mrp_production_ids[0]
- mo2 = mo.procurement_group_id.mrp_production_ids[1]
- mo3 = mo.procurement_group_id.mrp_production_ids[2]
- # Check quantities
- self.assertEqual(mo1.product_qty, 3)
- self.assertEqual(mo2.product_qty, 3)
- self.assertEqual(mo3.product_qty, 4)
- # Check raw movew quantities
- self.assertEqual(mo1.move_raw_ids.filtered(lambda m: m.product_id == p1).product_qty, 12)
- self.assertEqual(mo2.move_raw_ids.filtered(lambda m: m.product_id == p1).product_qty, 12)
- self.assertEqual(mo3.move_raw_ids.filtered(lambda m: m.product_id == p1).product_qty, 16)
- self.assertEqual(mo1.move_raw_ids.filtered(lambda m: m.product_id == p2).product_qty, 3)
- self.assertEqual(mo2.move_raw_ids.filtered(lambda m: m.product_id == p2).product_qty, 3)
- self.assertEqual(mo3.move_raw_ids.filtered(lambda m: m.product_id == p2).product_qty, 4)
- # Merge them back
- expected_origin = ",".join([mo1.name, mo2.name, mo3.name])
- action = (mo1 + mo2 + mo3).action_merge()
- mo = self.env[action['res_model']].browse(action['res_id'])
- # Check origin & initial quantity
- self.assertEqual(mo.origin, expected_origin)
- self.assertEqual(mo.product_qty, 10)
- def test_reservation_method_w_mo(self):
- """ Create a MO for 2 units, Produce 1 and create a backorder.
- The MO and the backorder should be assigned according to the reservation method
- defined in the default manufacturing operation type
- """
- def create_mo(date_planned_start=False):
- mo_form = Form(self.env['mrp.production'])
- mo_form.product_id = self.bom_1.product_id
- mo_form.bom_id = self.bom_1
- mo_form.product_qty = 2
- if date_planned_start:
- mo_form.date_planned_start = date_planned_start
- mo = mo_form.save()
- mo.action_confirm()
- return mo
- 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]
- # Make some stock and reserve
- for product in self.bom_1.bom_line_ids.product_id:
- product.type = 'product'
- self.env['stock.quant'].with_context(inventory_mode=True).create({
- 'product_id': product.id,
- 'inventory_quantity': 100,
- 'location_id': self.stock_location.id,
- })._apply_inventory()
- default_picking_type_id = self.env['mrp.production']._get_default_picking_type_id(self.env.company.id)
- default_picking_type = self.env['stock.picking.type'].browse(default_picking_type_id)
- # make sure generated MO will auto-assign
- default_picking_type.reservation_method = 'at_confirm'
- production = create_mo()
- self.assertEqual(production.state, 'confirmed')
- self.assertEqual(production.reserve_visible, False)
- # check whether the backorder follows the same scenario as the original MO
- backorder = produce_one(production)
- self.assertEqual(backorder.state, 'confirmed')
- self.assertEqual(backorder.reserve_visible, False)
- # make sure generated MO will does not auto-assign
- default_picking_type.reservation_method = 'manual'
- production = create_mo()
- self.assertEqual(production.state, 'confirmed')
- self.assertEqual(production.reserve_visible, True)
- backorder = produce_one(production)
- self.assertEqual(backorder.state, 'confirmed')
- self.assertEqual(backorder.reserve_visible, True)
- # make sure generated MO auto-assigns according to scheduled date
- default_picking_type.reservation_method = 'by_date'
- default_picking_type.reservation_days_before = 2
- # too early for scheduled date => don't auto-assign
- production = create_mo(datetime.now() + timedelta(days=10))
- self.assertEqual(production.state, 'confirmed')
- self.assertEqual(production.reserve_visible, True)
- backorder = produce_one(production)
- self.assertEqual(backorder.state, 'confirmed')
- self.assertEqual(backorder.reserve_visible, True)
- # within scheduled date + reservation days before => auto-assign
- production = create_mo()
- self.assertEqual(production.state, 'confirmed')
- self.assertEqual(production.reserve_visible, False)
- backorder = produce_one(production)
- self.assertEqual(backorder.state, 'confirmed')
- self.assertEqual(backorder.reserve_visible, False)
- class TestMrpWorkorderBackorder(TransactionCase):
- @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
|