Esempio n. 1
0
    def test_product_produce_uom(self):
        plastic_laminate = self.env.ref('mrp.product_product_plastic_laminate')
        bom = self.env.ref('mrp.mrp_bom_plastic_laminate')
        dozen = self.env.ref('uom.product_uom_dozen')
        unit = self.env.ref('uom.product_uom_unit')

        plastic_laminate.tracking = 'serial'

        mo = self.env['mrp.production'].create({
            'name': 'Dozen Plastic Laminate',
            'product_id': plastic_laminate.id,
            'product_uom_id': dozen.id,
            'product_qty': 1,
            'bom_id': bom.id,
        })

        final_product_lot = self.env['stock.production.lot'].create({
            'name': 'lot1',
            'product_id': plastic_laminate.id,
        })

        mo.action_assign()
        self.assertEqual(mo.move_raw_ids.product_qty, 12, '12 units should be reserved.')

        # produce product
        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id': mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.lot_id = final_product_lot
        product_produce = produce_form.save()
        self.assertEqual(product_produce.product_qty, 1)
        self.assertEqual(product_produce.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
        product_produce.lot_id = final_product_lot.id

        product_produce.do_produce()
        move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done)
        self.assertEqual(move_line_raw.qty_done, 1)
        self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')

        move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done)
        self.assertEqual(move_line_finished.qty_done, 1)
        self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
Esempio n. 2
0
    def test_02_different_uom_on_bomlines(self):
        """ Testing bill of material with different unit of measure."""
        route_manufacture = self.warehouse.manufacture_pull_id.route_id.id
        route_mto = self.warehouse.mto_pull_id.route_id.id
        unit = self.ref("uom.product_uom_unit")
        dozen = self.ref("uom.product_uom_dozen")
        kg = self.ref("uom.product_uom_kgm")
        gm = self.ref("uom.product_uom_gram")
        # Create Product A, B, C
        product_A = self.env['product.product'].create({
            'name':
            'Product A',
            'type':
            'product',
            'tracking':
            'lot',
            'uom_id':
            dozen,
            'uom_po_id':
            dozen,
            'route_ids': [(6, 0, [route_manufacture, route_mto])]
        })
        product_B = self.env['product.product'].create({
            'name': 'Product B',
            'type': 'product',
            'tracking': 'lot',
            'uom_id': dozen,
            'uom_po_id': dozen
        })
        product_C = self.env['product.product'].create({
            'name': 'Product C',
            'type': 'product',
            'tracking': 'lot',
            'uom_id': kg,
            'uom_po_id': kg
        })

        # Bill of materials
        # -----------------

        #===================================
        # Product A 1 Unit
        #     Product B 4 Unit
        #     Product C 600 gram
        # -----------------------------------

        bom_a = self.env['mrp.bom'].create({
            'product_tmpl_id':
            product_A.product_tmpl_id.id,
            'product_qty':
            2,
            'product_uom_id':
            unit,
            'bom_line_ids': [(0, 0, {
                'product_id': product_B.id,
                'product_qty': 4,
                'product_uom_id': unit
            }),
                             (0, 0, {
                                 'product_id': product_C.id,
                                 'product_qty': 600,
                                 'product_uom_id': gm
                             })]
        })

        # Create production order with product A 10 Unit.
        # -----------------------------------------------

        mo_custom_product = self.env['mrp.production'].create({
            'product_id':
            product_A.id,
            'product_qty':
            10,
            'product_uom_id':
            unit,
            'bom_id':
            bom_a.id
        })

        move_product_b = mo_custom_product.move_raw_ids.filtered(
            lambda x: x.product_id == product_B)
        move_product_c = mo_custom_product.move_raw_ids.filtered(
            lambda x: x.product_id == product_C)

        # Check move correctly created or not.
        self.assertEqual(move_product_b.product_uom_qty, 20)
        self.assertEqual(move_product_b.product_uom.id, unit)
        self.assertEqual(move_product_c.product_uom_qty, 3000)
        self.assertEqual(move_product_c.product_uom.id, gm)

        # Lot create for product B and product C
        # ---------------------------------------
        lot_a = self.env['stock.production.lot'].create(
            {'product_id': product_A.id})
        lot_b = self.env['stock.production.lot'].create(
            {'product_id': product_B.id})
        lot_c = self.env['stock.production.lot'].create(
            {'product_id': product_C.id})

        # Inventory Update
        # ----------------
        inventory = self.env['stock.inventory'].create({
            'name':
            'Inventory Product B and C',
            'filter':
            'partial',
            'line_ids': [(0, 0, {
                'product_id': product_B.id,
                'product_uom_id': product_B.uom_id.id,
                'product_qty': 3,
                'prod_lot_id': lot_b.id,
                'location_id': self.source_location_id
            }),
                         (0, 0, {
                             'product_id': product_C.id,
                             'product_uom_id': product_C.uom_id.id,
                             'product_qty': 3,
                             'prod_lot_id': lot_c.id,
                             'location_id': self.source_location_id
                         })]
        })
        # inventory.action_start()
        inventory.action_validate()

        # Start Production ...
        # --------------------

        mo_custom_product.action_assign()
        context = {
            "active_ids": [mo_custom_product.id],
            "active_id": mo_custom_product.id
        }
        produce_form = Form(
            self.env['mrp.product.produce'].with_context(context))
        produce_form.product_qty = 10.00
        produce_form.lot_id = lot_a
        product_consume = produce_form.save()
        # laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
        self.assertEquals(len(product_consume.produce_line_ids), 2)
        product_consume.produce_line_ids.filtered(
            lambda x: x.product_id == product_C).write({'qty_done': 3000})
        product_consume.produce_line_ids.filtered(
            lambda x: x.product_id == product_B).write({'qty_done': 20})
        product_consume.do_produce()
        mo_custom_product.post_inventory()
Esempio n. 3
0
    def test_01_without_workorder(self):
        """ Testing consume quants and produced quants without workorder """
        unit = self.ref("uom.product_uom_unit")
        custom_laptop = self.env.ref("product.product_product_27")
        custom_laptop.tracking = 'lot'

        # Create new product charger and keybord
        # --------------------------------------
        product_charger = self.env['product.product'].create({
            'name': 'Charger',
            'type': 'product',
            'tracking': 'lot',
            'uom_id': unit,
            'uom_po_id': unit
        })
        product_keybord = self.env['product.product'].create({
            'name': 'Usb Keybord',
            'type': 'product',
            'tracking': 'lot',
            'uom_id': unit,
            'uom_po_id': unit
        })

        # Create bill of material for customized laptop.

        bom_custom_laptop = self.env['mrp.bom'].create({
            'product_tmpl_id':
            custom_laptop.product_tmpl_id.id,
            'product_qty':
            10,
            'product_uom_id':
            unit,
            'bom_line_ids': [(0, 0, {
                'product_id': product_charger.id,
                'product_qty': 20,
                'product_uom_id': unit
            }),
                             (0, 0, {
                                 'product_id': product_keybord.id,
                                 'product_qty': 20,
                                 'product_uom_id': unit
                             })]
        })

        # Create production order for customize laptop.

        mo_custom_laptop = self.env['mrp.production'].create({
            'product_id':
            custom_laptop.id,
            'product_qty':
            10,
            'product_uom_id':
            unit,
            'bom_id':
            bom_custom_laptop.id
        })

        # Assign component to production order.
        mo_custom_laptop.action_assign()

        # Check production order status of availablity

        self.assertEqual(mo_custom_laptop.availability, 'waiting')

        # --------------------------------------------------
        # Set inventory for rawmaterial charger and keybord
        # --------------------------------------------------

        lot_charger = self.env['stock.production.lot'].create(
            {'product_id': product_charger.id})
        lot_keybord = self.env['stock.production.lot'].create(
            {'product_id': product_keybord.id})

        # Initialize Inventory
        # --------------------
        inventory = self.env['stock.inventory'].create({
            'name':
            'Inventory Product Table',
            'filter':
            'partial',
            'line_ids': [(0, 0, {
                'product_id': product_charger.id,
                'product_uom_id': product_charger.uom_id.id,
                'product_qty': 20,
                'prod_lot_id': lot_charger.id,
                'location_id': self.source_location_id
            }),
                         (0, 0, {
                             'product_id': product_keybord.id,
                             'product_uom_id': product_keybord.uom_id.id,
                             'product_qty': 20,
                             'prod_lot_id': lot_keybord.id,
                             'location_id': self.source_location_id
                         })]
        })
        # inventory.action_start()
        inventory.action_validate()

        # Check consumed move status
        mo_custom_laptop.action_assign()
        self.assertEqual(mo_custom_laptop.availability, 'assigned')

        # Check current status of raw materials.
        for move in mo_custom_laptop.move_raw_ids:
            self.assertEqual(
                move.product_uom_qty, 20,
                "Wrong consume quantity of raw material %s: %s instead of %s" %
                (move.product_id.name, move.product_uom_qty, 20))
            self.assertEqual(
                move.quantity_done, 0,
                "Wrong produced quantity on raw material %s: %s instead of %s"
                % (move.product_id.name, move.quantity_done, 0))

        # -----------------
        # Start production
        # -----------------

        # Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger)
        context = {
            "active_ids": [mo_custom_laptop.id],
            "active_id": mo_custom_laptop.id
        }
        product_form = Form(
            self.env['mrp.product.produce'].with_context(context))
        product_form.product_qty = 6.00
        laptop_lot_001 = self.env['stock.production.lot'].create(
            {'product_id': custom_laptop.id})
        product_form.lot_id = laptop_lot_001
        product_consume = product_form.save()
        product_consume.produce_line_ids[0].qty_done = 12
        product_consume.do_produce()

        # Check consumed move after produce 6 quantity of customized laptop.
        for move in mo_custom_laptop.move_raw_ids:
            self.assertEqual(
                move.quantity_done, 12,
                "Wrong produced quantity on raw material %s" %
                (move.product_id.name))
        self.assertEqual(len(mo_custom_laptop.move_raw_ids), 2)
        mo_custom_laptop.post_inventory()
        self.assertEqual(len(mo_custom_laptop.move_raw_ids), 4)

        # Check done move and confirmed move quantity.

        charger_done_move = mo_custom_laptop.move_raw_ids.filtered(
            lambda x: x.product_id.id == product_charger.id and x.state ==
            'done')
        keybord_done_move = mo_custom_laptop.move_raw_ids.filtered(
            lambda x: x.product_id.id == product_keybord.id and x.state ==
            'done')
        self.assertEquals(charger_done_move.product_uom_qty, 12)
        self.assertEquals(keybord_done_move.product_uom_qty, 12)

        # Produce remaining 4 quantity
        # ----------------------------

        # Produce 4 Unit of custom laptop will consume ( 8 Unit of keybord and 8 Unit of charger).
        context = {
            "active_ids": [mo_custom_laptop.id],
            "active_id": mo_custom_laptop.id
        }
        produce_form = Form(
            self.env['mrp.product.produce'].with_context(context))
        produce_form.product_qty = 4.00
        laptop_lot_002 = self.env['stock.production.lot'].create(
            {'product_id': custom_laptop.id})
        produce_form.lot_id = laptop_lot_002
        product_consume = produce_form.save()
        self.assertEquals(len(product_consume.produce_line_ids), 2)
        product_consume.produce_line_ids[0].qty_done = 8
        product_consume.do_produce()
        charger_move = mo_custom_laptop.move_raw_ids.filtered(
            lambda x: x.product_id.id == product_charger.id and x.state !=
            'done')
        keybord_move = mo_custom_laptop.move_raw_ids.filtered(
            lambda x: x.product_id.id == product_keybord.id and x.state !=
            'done')
        self.assertEquals(
            charger_move.quantity_done, 8,
            "Wrong consumed quantity of %s" % charger_move.product_id.name)
        self.assertEquals(
            keybord_move.quantity_done, 8,
            "Wrong consumed quantity of %s" % keybord_move.product_id.name)

        # Post Inventory of production order.
        mo_custom_laptop.post_inventory()
Esempio n. 4
0
    def test_production_links_with_non_tracked_lots(self):
        """ This test produces an MO in two times and checks that the move lines are linked in a correct way
        """
        mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='lot',
                                                    tracking_base_1='none',
                                                    tracking_base_2='lot')
        lot_1 = self.env['stock.production.lot'].create({
            'name': 'lot_1',
            'product_id': p2.id,
        })

        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           3,
                                                           lot_id=lot_1)
        lot_finished_1 = self.env['stock.production.lot'].create({
            'name':
            'lot_finished_1',
            'product_id':
            p_final.id,
        })

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.product_qty = 3.0
        produce_form.lot_id = lot_finished_1
        produce_wizard = produce_form.save()
        produce_wizard.produce_line_ids[0].lot_id = lot_1
        produce_wizard.do_produce()

        lot_2 = self.env['stock.production.lot'].create({
            'name': 'lot_2',
            'product_id': p2.id,
        })

        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           4,
                                                           lot_id=lot_2)
        lot_finished_2 = self.env['stock.production.lot'].create({
            'name':
            'lot_finished_2',
            'product_id':
            p_final.id,
        })

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.product_qty = 2.0
        produce_form.lot_id = lot_finished_2

        produce_wizard = produce_form.save()
        produce_wizard.produce_line_ids[0].lot_id = lot_2
        produce_wizard.do_produce()
        mo.button_mark_done()
        ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(
            lambda m: m.product_id == p1 and m.lot_produced_id ==
            lot_finished_1)
        self.assertEqual(ml[0].qty_done, 12.0,
                         'Should have consumed 12 for the first lot')
        ml = mo.finished_move_line_ids[1].consume_line_ids.filtered(
            lambda m: m.product_id == p1 and m.lot_produced_id ==
            lot_finished_2)
        self.assertEqual(ml[0].qty_done, 8.0,
                         'Should have consumed 8 for the second lot')
Esempio n. 5
0
    def test_unbuild_with_everything_tracked(self):
        """ This test creates a MO and then creates 3 unbuild
        orders for the final product. All the products for this
        test are tracked. It checks the stock state after each order
        and ensure it is correct.
        """
        mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='lot',
                                                    tracking_base_2='lot',
                                                    tracking_base_1='lot')
        self.assertEqual(len(mo), 1, 'MO should have been created')

        lot_final = self.env['stock.production.lot'].create({
            'name':
            'lot_final',
            'product_id':
            p_final.id,
        })
        lot_1 = self.env['stock.production.lot'].create({
            'name': 'lot_consumed_1',
            'product_id': p1.id,
        })
        lot_2 = self.env['stock.production.lot'].create({
            'name': 'lot_consumed_2',
            'product_id': p2.id,
        })

        self.env['stock.quant']._update_available_quantity(p1,
                                                           self.stock_location,
                                                           100,
                                                           lot_id=lot_1)
        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           5,
                                                           lot_id=lot_2)
        mo.action_assign()

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.product_qty = 5.0
        produce_form.lot_id = lot_final
        produce_wizard = produce_form.save()

        produce_wizard.do_produce()

        mo.button_mark_done()
        self.assertEqual(mo.state, 'done',
                         "Production order should be in done state.")
        # Check quantity in stock before unbuild.
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, lot_id=lot_final), 5,
            'You should have the 5 final product in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location, lot_id=lot_1), 80,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_2), 0,
            'You should have consumed all the 5 product in stock')

        # ---------------------------------------------------
        #       unbuild
        # ---------------------------------------------------

        unbuild_order = self.env['mrp.unbuild'].create({
            'product_id':
            p_final.id,
            'bom_id':
            bom.id,
            'product_qty':
            3.0,
            'product_uom_id':
            self.uom_unit.id,
        })

        with self.assertRaises(UserError):
            unbuild_order.action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, lot_id=lot_final), 5,
            'You should have consumed 3 final product in stock')

        unbuild_order.mo_id = mo.id
        with self.assertRaises(UserError):
            unbuild_order.action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, lot_id=lot_final), 5,
            'You should have consumed 3 final product in stock')

        unbuild_order.lot_id = lot_final.id
        unbuild_order.action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, lot_id=lot_final), 2,
            'You should have consumed 3 final product in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location, lot_id=lot_1), 92,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_2), 3,
            'You should have consumed all the 5 product in stock')

        self.env['mrp.unbuild'].create({
            'product_id': p_final.id,
            'bom_id': bom.id,
            'product_qty': 2.0,
            'mo_id': mo.id,
            'lot_id': lot_final.id,
            'product_uom_id': self.uom_unit.id,
        }).action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, lot_id=lot_final), 0,
            'You should have 0 finalproduct in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location, lot_id=lot_1), 100,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_2), 5,
            'You should have consumed all the 5 product in stock')

        self.env['mrp.unbuild'].create({
            'product_id': p_final.id,
            'bom_id': bom.id,
            'product_qty': 5.0,
            'mo_id': mo.id,
            'lot_id': lot_final.id,
            'product_uom_id': self.uom_unit.id,
        }).action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final,
                self.stock_location,
                lot_id=lot_final,
                allow_negative=True), -5,
            'You should have negative quantity for final product in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location, lot_id=lot_1), 120,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_2), 10,
            'You should have consumed all the 5 product in stock')
Esempio n. 6
0
    def test_product_produce_7(self):
        """ Check that for products tracked by lots,
        with component product UOM different from UOM used in the BOM,
        we do not create a new move line due to extra reserved quantity
        caused by decimal rounding conversions.
        """

        # the overall decimal accuracy is set to 3 digits
        precision = self.env.ref('product.decimal_product_uom')
        precision.write({
            'digits': 3
        })

        # define L and ml, L has rounding .001 but ml has rounding .01
        # when producing e.g. 187.5ml, it will be rounded to .188L
        categ_test = self.env['uom.category'].create({'name': 'Volume Test'})

        uom_L = self.env['uom.uom'].create({
            'name': 'Test Liters',
            'category_id': categ_test.id,
            'uom_type': 'reference',
            'rounding': 0.001
        })

        uom_ml = self.env['uom.uom'].create({
            'name': 'Test ml',
            'category_id': categ_test.id,
            'uom_type': 'smaller',
            'rounding': 0.01,
            'factor_inv': 0.001,
        })

        # create a product component and the final product using the component
        product_comp = self.env['product.product'].create({
            'name': 'Product Component',
            'type': 'product',
            'tracking': 'lot',
            'categ_id': self.env.ref('product.product_category_all').id,
            'uom_id': uom_L.id,
            'uom_po_id': uom_L.id,
        })

        product_final = self.env['product.product'].create({
            'name': 'Product Final',
            'type': 'product',
            'tracking': 'lot',
            'categ_id': self.env.ref('product.product_category_all').id,
            'uom_id': uom_L.id,
            'uom_po_id': uom_L.id,
        })

        # the products are tracked by lot, so we go through _generate_consumed_move_line
        lot_final = self.env['stock.production.lot'].create({
            'name': 'Lot Final',
            'product_id': product_final.id,
        })

        lot_comp = self.env['stock.production.lot'].create({
            'name': 'Lot Component',
            'product_id': product_comp.id,
        })

        # update the quantity on hand for Component, in a lot
        self.stock_location = self.env.ref('stock.stock_location_stock')
        self.env['stock.quant']._update_available_quantity(product_comp, self.stock_location, 1, lot_id=lot_comp)

        # create a BOM for Final, using Component
        test_bom = self.env['mrp.bom'].create({
            'product_id': product_final.id,
            'product_tmpl_id': product_final.product_tmpl_id.id,
            'product_uom_id': uom_L.id,
            'product_qty': 1.0,
            'type': 'normal',
            'bom_line_ids': [(0, 0, {
                'product_id': product_comp.id,
                'product_qty': 375.00,
                'product_uom_id': uom_ml.id
            })],
        })

        # create a MO for this BOM
        mo_product_final = self.env['mrp.production'].create({
            'product_id': product_final.id,
            'product_qty': 0.5,
            'product_uom_id': uom_L.id,
            'bom_id': test_bom.id,
        })
        mo_product_final.action_assign()
        self.assertEqual(mo_product_final.availability, 'assigned')

        # produce
        context = {"active_ids": [mo_product_final.id], "active_id": mo_product_final.id}
        produce_form = Form(self.env['mrp.product.produce'].with_context(context))
        produce_form.product_qty = 0.5
        produce_form.lot_id = lot_final
        produce_wizard = produce_form.save()
        produce_wizard.do_produce()

        # check that in _generate_consumed_move_line,
        # we do not create an extra move line because
        # of a conversion 187.5ml = 0.188L
        # thus creating an extra line with 'product_uom_qty': 0.5
        self.assertEqual(len(mo_product_final.move_raw_ids.move_line_ids), 1, 'One move line should exist for the MO.')
Esempio n. 7
0
    def test_tracking_types_on_mo(self):
        finished_no_track = self._create_product('none')
        finished_lot = self._create_product('lot')
        finished_serial = self._create_product('serial')
        consumed_no_track = self._create_product('none')
        consumed_lot = self._create_product('lot')
        consumed_serial = self._create_product('serial')
        stock_id = self.env.ref('stock.stock_location_stock').id
        inventory_adjustment = self.env['stock.inventory'].create({
            'name': 'Initial Inventory',
            'location_id': stock_id,
            'filter': 'partial',
        })
        inventory_adjustment.action_start()
        inventory_adjustment.write({
            'line_ids': [
                (0,0, {'product_id': consumed_no_track.id, 'product_qty': 3, 'location_id': stock_id}),
                (0,0, {'product_id': consumed_lot.id, 'product_qty': 3, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'L1', 'product_id': consumed_lot.id}).id, 'location_id': stock_id}),
                (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S1', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}),
                (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S2', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}),
                (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S3', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}),
            ]
        })
        inventory_adjustment.action_validate()
        for finished_product in [finished_no_track, finished_lot, finished_serial]:
            bom = self.env['mrp.bom'].create({
                'product_id': finished_product.id,
                'product_tmpl_id': finished_product.product_tmpl_id.id,
                'product_uom_id': self.env.ref('uom.product_uom_unit').id,
                'product_qty': 1.0,
                'type': 'normal',
                'bom_line_ids': [
                    (0, 0, {'product_id': consumed_no_track.id, 'product_qty': 1}),
                    (0, 0, {'product_id': consumed_lot.id, 'product_qty': 1}),
                    (0, 0, {'product_id': consumed_serial.id, 'product_qty': 1}),
                ],
            })
            
            mo = self.env['mrp.production'].create({
                'name': 'MO %s' % finished_product.tracking,
                'product_id': finished_product.id,
                'product_uom_id': self.env.ref('uom.product_uom_unit').id,
                'product_qty': 1,
                'bom_id': bom.id,
            })
            
            mo.action_assign()

            # Start MO production
            produce_form = Form(self.env['mrp.product.produce'].with_context({
                'active_id': mo.id,
                'active_ids': [mo.id],
            }))

            if finished_product.tracking != 'serial':
                produce_form.product_qty = 1

            if finished_product.tracking != 'none':
                produce_form.lot_id = self.env['stock.production.lot'].create({'name': 'Serial or Lot finished', 'product_id': finished_product.id})
            produce_wizard = produce_form.save()

            produce_wizard.do_produce()
            mo.button_mark_done()

            self.assertEqual(mo.state, 'done', "Production order should be in done state.")

            # Check results of traceability
            context = ({
                'active_id': mo.id,
                'model': 'mrp.production',
            })
            lines = self.env['stock.traceability.report'].with_context(context).get_lines()

            self.assertEqual(len(lines), 1, "Should always return 1 line : the final product")

            final_product = lines[0]
            self.assertEqual(final_product['unfoldable'], True, "Final product should always be unfoldable")

            # Find parts of the final products
            lines = self.env['stock.traceability.report'].get_lines(final_product['id'], **{
                'level': final_product['level'],
                'model_id': final_product['model_id'],
                'model_name': final_product['model'],
            })

            self.assertEqual(len(lines), 3, "There should be 3 lines. 1 for untracked, 1 for lot, and 1 for serial")

            for line in lines:
                tracking = line['columns'][1].split(' ')[1]
                self.assertEqual(
                    line['columns'][-1], "1.000 Unit(s)", 'Part with tracking type "%s", should have quantity = 1' % (tracking)
                )
                unfoldable = False if tracking == 'none' else True
                self.assertEqual(
                    line['unfoldable'],
                    unfoldable,
                    'Parts with tracking type "%s", should have be unfoldable : %s' % (tracking, unfoldable)
                )