Пример #1
0
    def test_procurement_2(self):
        """Check that a manufacturing order create the right procurements when the route are set on
        a parent category of a product"""
        # find a child category id
        all_categ_id = self.env['product.category'].search(
            [('parent_id', '=', None)], limit=1)
        child_categ_id = self.env['product.category'].search(
            [('parent_id', '=', all_categ_id.id)], limit=1)

        # set the product of `self.bom_1` to this child category
        for bom_line_id in self.bom_1.bom_line_ids:
            # check that no routes are defined on the product
            self.assertEquals(len(bom_line_id.product_id.route_ids), 0)
            # set the category of the product to a child category
            bom_line_id.product_id.categ_id = child_categ_id

        # set the MTO route to the parent category (all)
        self.warehouse = self.env.ref('stock.warehouse0')
        mto_route = self.warehouse.mto_pull_id.route_id
        mto_route.product_categ_selectable = True
        all_categ_id.write({'route_ids': [(6, 0, [mto_route.id])]})

        # create MO, but check it raises error as components are in make to order and not everyone has
        with self.assertRaises(UserError):
            production_form = Form(self.env['mrp.production'])
            production_form.product_id = self.product_4
            production_form.product_uom_id = self.product_4.uom_id
            production_form.product_qty = 1
            production_product_4 = production_form.save()
            production_product_4.action_confirm()
Пример #2
0
 def _make_mo(self, bom, quantity=1):
     mo_form = Form(self.env['mrp.production'])
     mo_form.product_id = bom.product_id
     mo_form.bom_id = bom
     mo_form.product_qty = quantity
     mo = mo_form.save()
     mo.action_confirm()
     return mo
Пример #3
0
 def new_mo_laptop(self):
     form = Form(self.env['mrp.production'])
     form.product_id = self.laptop
     form.product_qty = 1
     form.bom_id = self.bom_laptop
     p = form.save()
     p.action_confirm()
     p.action_assign()
     return p
    def setUp(self):
        super(TestMultistepManufacturing, self).setUp()

        self.MrpProduction = self.env['mrp.production']
        # Create warehouse
        warehouse_form = Form(self.env['stock.warehouse'])
        warehouse_form.name = 'Test'
        warehouse_form.code = 'Test'
        self.warehouse = warehouse_form.save()

        self.uom_unit = self.env.ref('uom.product_uom_unit')

        # Create manufactured product
        product_form = Form(self.env['product.product'])
        product_form.name = 'Stick'
        product_form.uom_id = self.uom_unit
        product_form.uom_po_id = self.uom_unit
        product_form.route_ids.clear()
        product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id)
        product_form.route_ids.add(self.warehouse.mto_pull_id.route_id)
        self.product_manu = product_form.save()

        # Create raw product for manufactured product
        product_form = Form(self.env['product.product'])
        product_form.name = 'Raw Stick'
        product_form.uom_id = self.uom_unit
        product_form.uom_po_id = self.uom_unit
        self.product_raw = product_form.save()

        # Create bom for manufactured product
        bom_product_form = Form(self.env['mrp.bom'])
        bom_product_form.product_id = self.product_manu
        bom_product_form.product_tmpl_id = self.product_manu.product_tmpl_id
        bom_product_form.product_qty = 1.0
        bom_product_form.type = 'normal'
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.product_raw
            bom_line.product_qty = 2.0
        self.bom_prod_manu = bom_product_form.save()

        # Create sale order
        sale_form = Form(self.env['sale.order'])
        sale_form.partner_id = self.env.ref('base.res_partner_1')
        sale_form.picking_policy = 'direct'
        sale_form.warehouse_id = self.warehouse
        with sale_form.order_line.new() as line:
            line.name = self.product_manu.name
            line.product_id = self.product_manu
            line.product_uom_qty = 1.0
            line.product_uom = self.uom_unit
            line.price_unit = 10.0
        self.sale_order = sale_form.save()
Пример #5
0
    def setUp(self):
        super(TestMultistepManufacturingWarehouse, self).setUp()
        # Create warehouse
        self.customer_location = self.env['ir.model.data'].xmlid_to_res_id(
            'stock.stock_location_customers')
        warehouse_form = Form(self.env['stock.warehouse'])
        warehouse_form.name = 'Test Warehouse'
        warehouse_form.code = 'TWH'
        self.warehouse = warehouse_form.save()

        self.uom_unit = self.env.ref('uom.product_uom_unit')

        # Create manufactured product
        product_form = Form(self.env['product.product'])
        product_form.name = 'Stick'
        product_form.uom_id = self.uom_unit
        product_form.uom_po_id = self.uom_unit
        product_form.type = 'product'
        product_form.route_ids.clear()
        product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id)
        product_form.route_ids.add(self.warehouse.mto_pull_id.route_id)
        self.finished_product = product_form.save()

        # Create raw product for manufactured product
        product_form = Form(self.env['product.product'])
        product_form.name = 'Raw Stick'
        product_form.type = 'product'
        product_form.uom_id = self.uom_unit
        product_form.uom_po_id = self.uom_unit
        self.raw_product = product_form.save()

        # Create bom for manufactured product
        bom_product_form = Form(self.env['mrp.bom'])
        bom_product_form.product_id = self.finished_product
        bom_product_form.product_tmpl_id = self.finished_product.product_tmpl_id
        bom_product_form.product_qty = 1.0
        bom_product_form.type = 'normal'
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.raw_product
            bom_line.product_qty = 2.0

        self.bom = bom_product_form.save()
Пример #6
0
    def test_unbuild_with_routes(self):
        """ This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock
        is created with Warehouse -> True.
        The unbuild order should revert the consumed components into QC/Unbuild location for quality check
        and then a picking should be generated for transferring components from QC/Unbuild location to stock.
        """
        StockQuant = self.env['stock.quant']
        ProductObj = self.env['product.product']
        # Create new QC/Unbuild location
        warehouse = self.env.ref('stock.warehouse0')
        unbuild_location = self.env['stock.location'].create({
            'name':
            'QC/Unbuild',
            'usage':
            'internal',
            'location_id':
            warehouse.view_location_id.id
        })

        # Create a product route containing a stock rule that will move product from QC/Unbuild location to stock
        product_route = self.env['stock.location.route'].create({
            'name':
            'QC/Unbuild -> Stock',
            'warehouse_selectable':
            True,
            'warehouse_ids': [(4, warehouse.id)],
            'rule_ids': [(0, 0, {
                'name':
                'Send Matrial QC/Unbuild -> Stock',
                'action':
                'push',
                'picking_type_id':
                self.ref('stock.picking_type_internal'),
                'location_src_id':
                unbuild_location.id,
                'location_id':
                self.stock_location.id,
            })],
        })

        # Create a stockable product and its components
        finshed_product = ProductObj.create({
            'name': 'Table',
            'type': 'product',
        })
        component1 = ProductObj.create({
            'name': 'Table head',
            'type': 'product',
        })
        component2 = ProductObj.create({
            'name': 'Table stand',
            'type': 'product',
        })

        # Create bom and add components
        bom = self.env['mrp.bom'].create({
            'product_id':
            finshed_product.id,
            'product_tmpl_id':
            finshed_product.product_tmpl_id.id,
            'product_uom_id':
            self.uom_unit.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'bom_line_ids': [(0, 0, {
                'product_id': component1.id,
                'product_qty': 1
            }), (0, 0, {
                'product_id': component2.id,
                'product_qty': 1
            })]
        })

        # Set on hand quantity
        StockQuant._update_available_quantity(component1, self.stock_location,
                                              1)
        StockQuant._update_available_quantity(component2, self.stock_location,
                                              1)

        # Create mo
        mo_form = Form(self.env['mrp.production'])
        mo_form.product_id = finshed_product
        mo_form.bom_id = bom
        mo_form.product_uom_id = finshed_product.uom_id
        mo_form.product_qty = 1.0
        mo = mo_form.save()
        self.assertEqual(len(mo), 1, 'MO should have been created')
        mo.action_confirm()
        mo.action_assign()

        # Produce the final product
        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.qty_producing = 1.0
        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(
            StockQuant._get_available_quantity(finshed_product,
                                               self.stock_location), 1,
            'Table should be available in stock')
        self.assertEqual(
            StockQuant._get_available_quantity(component1,
                                               self.stock_location), 0,
            'Table head should not be available in stock')
        self.assertEqual(
            StockQuant._get_available_quantity(component2,
                                               self.stock_location), 0,
            'Table stand should not be available in stock')

        # ---------------------------------------------------
        #       Unbuild
        # ---------------------------------------------------

        # Create an unbuild order of the finished product and set the destination loacation = QC/Unbuild
        x = Form(self.env['mrp.unbuild'])
        x.product_id = finshed_product
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 1
        x.location_id = self.stock_location
        x.location_dest_id = unbuild_location
        x.save().action_unbuild()

        # Check the available quantity of components and final product in stock
        self.assertEqual(
            StockQuant._get_available_quantity(finshed_product,
                                               self.stock_location), 0,
            'Table should not be available in stock as it is unbuild')
        self.assertEqual(
            StockQuant._get_available_quantity(component1,
                                               self.stock_location), 0,
            'Table head should not be available in stock as it is in QC/Unbuild location'
        )
        self.assertEqual(
            StockQuant._get_available_quantity(component2,
                                               self.stock_location), 0,
            'Table stand should not be available in stock as it is in QC/Unbuild location'
        )

        # Find new generated picking
        picking = self.env['stock.picking'].search([
            ('product_id', 'in', [component1.id, component2.id])
        ])
        self.assertEqual(picking.location_id.id, unbuild_location.id,
                         'Wrong source location in picking')
        self.assertEqual(picking.location_dest_id.id, self.stock_location.id,
                         'Wrong destination location in picking')

        # Transfer it
        for ml in picking.move_ids_without_package:
            ml.quantity_done = 1
        picking.action_done()

        # Check the available quantity of components and final product in stock
        self.assertEqual(
            StockQuant._get_available_quantity(finshed_product,
                                               self.stock_location), 0,
            'Table should not be available in stock')
        self.assertEqual(
            StockQuant._get_available_quantity(component1,
                                               self.stock_location), 1,
            'Table head should be available in stock as the picking is transferred'
        )
        self.assertEqual(
            StockQuant._get_available_quantity(component2,
                                               self.stock_location), 1,
            'Table stand should be available in stock as the picking is transferred'
        )
Пример #7
0
    def test_00_production_order_with_accounting(self):
        self.product_table_sheet.standard_price = 20.0
        self.product_table_leg.standard_price = 15.0
        self.product_bolt.standard_price = 10.0
        self.product_table_leg.tracking = 'none'
        self.product_table_sheet.tracking = 'none'
        inventory = self.env['stock.inventory'].create({
            'name':
            'Inventory Product Table',
            'line_ids': [
                (
                    0,
                    0,
                    {
                        'product_id':
                        self.product_table_sheet.id,  # tracking serial
                        'product_uom_id': self.product_table_sheet.uom_id.id,
                        'product_qty': 20,
                        'location_id': self.source_location_id
                    }),
                (
                    0,
                    0,
                    {
                        'product_id':
                        self.product_table_leg.id,  # tracking lot
                        'product_uom_id': self.product_table_leg.uom_id.id,
                        'product_qty': 20,
                        'location_id': self.source_location_id
                    }),
                (0, 0, {
                    'product_id': self.product_bolt.id,
                    'product_uom_id': self.product_bolt.uom_id.id,
                    'product_qty': 20,
                    'location_id': self.source_location_id
                }),
                (0, 0, {
                    'product_id': self.product_screw.id,
                    'product_uom_id': self.product_screw.uom_id.id,
                    'product_qty': 200000,
                    'location_id': self.source_location_id
                }),
            ]
        })
        inventory.action_validate
        bom = self.env.ref('mrp.mrp_bom_desk').copy()
        bom.routing_id = False  # TODO: extend the test later with the necessary operations
        production_table_form = Form(self.env['mrp.production'])
        production_table_form.product_id = self.dining_table
        production_table_form.bom_id = bom
        production_table_form.product_qty = 5.0
        production_table = production_table_form.save()

        production_table.extra_cost = 20
        production_table.action_confirm()

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            production_table.id,
            'active_ids': [production_table.id],
        }))
        produce_form.qty_producing = 1.0
        produce_wizard = produce_form.save()
        produce_wizard.do_produce()
        production_table.post_inventory()
        move_value = production_table.move_finished_ids.filtered(
            lambda x: x.state == "done").stock_valuation_layer_ids.value

        # 1 table head at 20 + 4 table leg at 15 + 4 bolt at 10 + 10 screw at 10 + 1*20 (extra cost)
        self.assertEqual(move_value, 141,
                         'Thing should have the correct price')
Пример #8
0
    def test_22_bom_report_recursive_bom(self):
        """ Test report with recursive BoM and different quantities.
        BoM 1:
        product = Finished (units)
        quantity = 100 units
        - Semi-Finished 5 kg

        BoM 2:
        product = Semi-Finished (kg)
        quantity = 11 kg
        - Assembly 2 dozens

        BoM 3:
        product = Assembly (dozens)
        quantity = 5 dozens
        - Raw Material 4 litres (product.product 5$/litre)

        Check the Price for 80 units of Finished -> 2.92$:
        """
        # Create a products templates
        uom_unit = self.env.ref('uom.product_uom_unit')
        uom_kg = self.env.ref('uom.product_uom_kgm')
        uom_dozen = self.env.ref('uom.product_uom_dozen')
        uom_litre = self.env.ref('uom.product_uom_litre')

        finished = self.env['product.product'].create({
            'name': 'Finished',
            'type': 'product',
            'uom_id': uom_unit.id,
            'uom_po_id': uom_unit.id,
        })

        semi_finished = self.env['product.product'].create({
            'name':
            'Semi-Finished',
            'type':
            'product',
            'uom_id':
            uom_kg.id,
            'uom_po_id':
            uom_kg.id,
        })

        assembly = self.env['product.product'].create({
            'name':
            'Assembly',
            'type':
            'product',
            'uom_id':
            uom_dozen.id,
            'uom_po_id':
            uom_dozen.id,
        })

        raw_material = self.env['product.product'].create({
            'name':
            'Raw Material',
            'type':
            'product',
            'uom_id':
            uom_litre.id,
            'uom_po_id':
            uom_litre.id,
            'standard_price':
            5,
        })

        #Create bom
        bom_finished = Form(self.env['mrp.bom'])
        bom_finished.product_tmpl_id = finished.product_tmpl_id
        bom_finished.product_qty = 100
        with bom_finished.bom_line_ids.new() as line:
            line.product_id = semi_finished
            line.product_uom_id = uom_kg
            line.product_qty = 5
        bom_finished = bom_finished.save()

        bom_semi_finished = Form(self.env['mrp.bom'])
        bom_semi_finished.product_tmpl_id = semi_finished.product_tmpl_id
        bom_semi_finished.product_qty = 11
        with bom_semi_finished.bom_line_ids.new() as line:
            line.product_id = assembly
            line.product_uom_id = uom_dozen
            line.product_qty = 2
        bom_semi_finished = bom_semi_finished.save()

        bom_assembly = Form(self.env['mrp.bom'])
        bom_assembly.product_tmpl_id = assembly.product_tmpl_id
        bom_assembly.product_qty = 5
        with bom_assembly.bom_line_ids.new() as line:
            line.product_id = raw_material
            line.product_uom_id = uom_litre
            line.product_qty = 4
        bom_assembly = bom_assembly.save()

        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_finished.id, searchQty=80)

        self.assertAlmostEqual(report_values['lines']['total'], 2.92)
Пример #9
0
    def test_manufacturing_scrap(self):
        """
            Testing to do a scrap of consumed material.
        """

        # Update demo products
        (self.product_4 | self.product_2).write({
            'tracking': 'lot',
        })

        # Update Bill Of Material to remove product with phantom bom.
        self.bom_3.bom_line_ids.filtered(lambda x: x.product_id == self.product_5).unlink()

        # Create Inventory Adjustment For Stick and Stone Tools with lot.
        lot_product_4 = self.env['stock.production.lot'].create({
            'name': '0000000000001',
            'product_id': self.product_4.id,
            'company_id': self.env.company.id,
        })
        lot_product_2 = self.env['stock.production.lot'].create({
            'name': '0000000000002',
            'product_id': self.product_2.id,
            'company_id': self.env.company.id,
        })

        stock_inv_product_4 = self.env['stock.inventory'].create({
            'name': 'Stock Inventory for Stick',
            'product_ids': [(4, self.product_4.id)],
            'line_ids': [
                (0, 0, {'product_id': self.product_4.id, 'product_uom_id': self.product_4.uom_id.id, 'product_qty': 8, 'prod_lot_id': lot_product_4.id, 'location_id': self.ref('stock.stock_location_14')}),
            ]})

        stock_inv_product_2 = self.env['stock.inventory'].create({
            'name': 'Stock Inventory for Stone Tools',
            'product_ids': [(4, self.product_2.id)],
            'line_ids': [
                (0, 0, {'product_id': self.product_2.id, 'product_uom_id': self.product_2.uom_id.id, 'product_qty': 12, 'prod_lot_id': lot_product_2.id, 'location_id': self.ref('stock.stock_location_14')})
            ]})
        (stock_inv_product_4 | stock_inv_product_2)._action_start()
        stock_inv_product_2.action_validate()
        stock_inv_product_4.action_validate()

        #Create Manufacturing order.
        production_form = Form(self.env['mrp.production'])
        production_form.product_id = self.product_6
        production_form.bom_id = self.bom_3
        production_form.product_qty = 12
        production_form.product_uom_id = self.product_6.uom_id
        production_3 = production_form.save()
        production_3.action_confirm()
        production_3.action_assign()

        # Check Manufacturing order's availability.
        self.assertEqual(production_3.reservation_state, 'assigned', "Production order's availability should be Available.")

        location_id = production_3.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) and production_3.location_src_id.id or production_3.location_dest_id.id,

        # Scrap Product Wood without lot to check assert raise ?.
        scrap_id = self.env['stock.scrap'].with_context(active_model='mrp.production', active_id=production_3.id).create({'product_id': self.product_2.id, 'scrap_qty': 1.0, 'product_uom_id': self.product_2.uom_id.id, 'location_id': location_id, 'production_id': production_3.id})
        with self.assertRaises(except_orm):
            scrap_id.do_scrap()

        # Scrap Product Wood with lot.
        self.env['stock.scrap'].with_context(active_model='mrp.production', active_id=production_3.id).create({'product_id': self.product_2.id, 'scrap_qty': 1.0, 'product_uom_id': self.product_2.uom_id.id, 'location_id': location_id, 'lot_id': lot_product_2.id, 'production_id': production_3.id})
Пример #10
0
    def test_00_mrp_byproduct(self):
        """ Test by product with production order."""
        # Create BOM for product B
        # ------------------------
        bom_product_b = self.MrpBom.create({
            'product_tmpl_id':
            self.product_b.product_tmpl_id.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'product_uom_id':
            self.uom_unit_id,
            'bom_line_ids': [(0, 0, {
                'product_id': self.product_c_id,
                'product_uom_id': self.uom_unit_id,
                'product_qty': 2
            })]
        })

        # Create BOM for product A and set byproduct product B
        bom_product_a = self.MrpBom.create({
            'product_tmpl_id':
            self.product_a.product_tmpl_id.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'product_uom_id':
            self.uom_unit_id,
            'bom_line_ids': [(0, 0, {
                'product_id': self.product_c_id,
                'product_uom_id': self.uom_unit_id,
                'product_qty': 2
            })],
            'byproduct_ids': [(0, 0, {
                'product_id': self.product_b.id,
                'product_uom_id': self.uom_unit_id,
                'product_qty': 1
            })]
        })

        # Create production order for product A
        # -------------------------------------

        mnf_product_a_form = Form(self.env['mrp.production'])
        mnf_product_a_form.product_id = self.product_a
        mnf_product_a_form.bom_id = bom_product_a
        mnf_product_a_form.product_qty = 2.0
        mnf_product_a = mnf_product_a_form.save()
        mnf_product_a.action_confirm()

        # I compute the data of production order
        context = {
            "active_model": "mrp.production",
            "active_ids": [mnf_product_a.id],
            "active_id": mnf_product_a.id
        }

        # I confirm the production order.
        self.assertEqual(mnf_product_a.state, 'confirmed',
                         'Production order should be in state confirmed')

        # Now I check the stock moves for the byproduct I created in the bill of material.
        # This move is created automatically when I confirmed the production order.
        moves = mnf_product_a.move_raw_ids | mnf_product_a.move_finished_ids
        self.assertTrue(moves, 'No moves are created !')

        # I consume and produce the production of products.
        # I create record for selecting mode and quantity of products to produce.
        produce_form = Form(
            self.env['mrp.product.produce'].with_context(context))
        produce_form.qty_producing = 2.00
        product_consume = produce_form.save()
        # I finish the production order.
        self.assertEqual(len(mnf_product_a.move_raw_ids), 1,
                         "Wrong consume move on production order.")
        product_consume.do_produce()
        consume_move_c = mnf_product_a.move_raw_ids
        by_product_move = mnf_product_a.move_finished_ids.filtered(
            lambda x: x.product_id.id == self.product_b.id)
        # Check sub production produced quantity...
        self.assertEqual(consume_move_c.product_uom_qty, 4,
                         "Wrong consumed quantity of product c.")
        self.assertEqual(by_product_move.product_uom_qty, 2,
                         "Wrong produced quantity of sub product.")

        mnf_product_a.post_inventory()

        # I see that stock moves of External Hard Disk including Headset USB are done now.
        self.assertFalse(any(move.state != 'done' for move in moves),
                         'Moves are not done!')
Пример #11
0
    def test_21_bom_report_variant(self):
        """ Test a sub BoM process with multiple variants.
        BOM 1:
        product template = car
        quantity = 5 units
        - red paint 50l -> red car (product.product)
        - blue paint 50l -> blue car
        - red dashboard with gps -> red car with GPS
        - red dashboard w/h gps -> red w/h GPS
        - blue dashboard with gps -> blue car with GPS
        - blue dashboard w/h gps -> blue w/h GPS

        BOM 2:
        product_tmpl = dashboard
        quantity = 2
        - red paint 1l -> red dashboard (product.product)
        - blue paint 1l -> blue dashboard
        - gps -> dashboard with gps

        Check the Price for a Blue Car with GPS -> 910$:
        10l of blue paint -> 200$
        1 blue dashboard GPS -> 710$:
            - 0.5l of blue paint -> 10$
            - GPS -> 700$

        Check the price for a red car -> 10.5l of red paint -> 210$
        """
        # Create a product template car with attributes gps(yes, no), color(red, blue)
        self.car = self.env['product.template'].create({
            'name': 'Car',
        })
        self.gps_attribute = self.env['product.attribute'].create({
            'name': 'GPS',
            'sequence': 1
        })
        self.gps_yes = self.env['product.attribute.value'].create({
            'name':
            'Yes',
            'attribute_id':
            self.gps_attribute.id,
            'sequence':
            1,
        })
        self.gps_no = self.env['product.attribute.value'].create({
            'name':
            'No',
            'attribute_id':
            self.gps_attribute.id,
            'sequence':
            2,
        })

        self.car_gps_attribute_line = self.env[
            'product.template.attribute.line'].create({
                'product_tmpl_id':
                self.car.id,
                'attribute_id':
                self.gps_attribute.id,
                'value_ids': [(6, 0, [self.gps_yes.id, self.gps_no.id])],
            })
        self.car_gps_yes = self.car_gps_attribute_line.product_template_value_ids[
            0]
        self.car_gps_no = self.car_gps_attribute_line.product_template_value_ids[
            1]

        self.color_attribute = self.env['product.attribute'].create({
            'name':
            'Color',
            'sequence':
            1
        })
        self.color_red = self.env['product.attribute.value'].create({
            'name':
            'Red',
            'attribute_id':
            self.color_attribute.id,
            'sequence':
            1,
        })
        self.color_blue = self.env['product.attribute.value'].create({
            'name':
            'Blue',
            'attribute_id':
            self.color_attribute.id,
            'sequence':
            2,
        })

        self.car_color_attribute_line = self.env[
            'product.template.attribute.line'].create({
                'product_tmpl_id':
                self.car.id,
                'attribute_id':
                self.color_attribute.id,
                'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])],
            })
        self.car_color_red = self.car_color_attribute_line.product_template_value_ids[
            0]
        self.car_color_blue = self.car_color_attribute_line.product_template_value_ids[
            1]

        # Blue and red paint
        uom_litre = self.env.ref('uom.product_uom_litre')
        self.paint = self.env['product.template'].create({
            'name':
            'Paint',
            'uom_id':
            uom_litre.id,
            'uom_po_id':
            uom_litre.id
        })
        self.paint_color_attribute_line = self.env[
            'product.template.attribute.line'].create({
                'product_tmpl_id':
                self.paint.id,
                'attribute_id':
                self.color_attribute.id,
                'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])],
            })
        self.paint_color_red = self.paint_color_attribute_line.product_template_value_ids[
            0]
        self.paint_color_blue = self.paint_color_attribute_line.product_template_value_ids[
            1]

        self.paint.product_variant_ids.write({'standard_price': 20})

        self.dashboard = self.env['product.template'].create({
            'name':
            'Dashboard',
            'standard_price':
            1000,
        })

        self.dashboard_gps_attribute_line = self.env[
            'product.template.attribute.line'].create({
                'product_tmpl_id':
                self.dashboard.id,
                'attribute_id':
                self.gps_attribute.id,
                'value_ids': [(6, 0, [self.gps_yes.id, self.gps_no.id])],
            })
        self.dashboard_gps_yes = self.dashboard_gps_attribute_line.product_template_value_ids[
            0]
        self.dashboard_gps_no = self.dashboard_gps_attribute_line.product_template_value_ids[
            1]

        self.dashboard_color_attribute_line = self.env[
            'product.template.attribute.line'].create({
                'product_tmpl_id':
                self.dashboard.id,
                'attribute_id':
                self.color_attribute.id,
                'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])],
            })
        self.dashboard_color_red = self.dashboard_color_attribute_line.product_template_value_ids[
            0]
        self.dashboard_color_blue = self.dashboard_color_attribute_line.product_template_value_ids[
            1]

        self.gps = self.env['product.product'].create({
            'name': 'GPS',
            'standard_price': 700,
        })

        bom_form_car = Form(self.env['mrp.bom'])
        bom_form_car.product_tmpl_id = self.car
        bom_form_car.product_qty = 5
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.paint._get_variant_for_combination(
                self.paint_color_red)
            line.product_uom_id = uom_litre
            line.product_qty = 50
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_red)
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.paint._get_variant_for_combination(
                self.paint_color_blue)
            line.product_uom_id = uom_litre
            line.product_qty = 50
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_blue)
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.dashboard._get_variant_for_combination(
                self.dashboard_gps_yes + self.dashboard_color_red)
            line.product_qty = 5
            line.bom_product_template_attribute_value_ids.add(self.car_gps_yes)
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_red)
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.dashboard._get_variant_for_combination(
                self.dashboard_gps_yes + self.dashboard_color_blue)
            line.product_qty = 5
            line.bom_product_template_attribute_value_ids.add(self.car_gps_yes)
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_blue)
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.dashboard._get_variant_for_combination(
                self.dashboard_gps_no + self.dashboard_color_red)
            line.product_qty = 5
            line.bom_product_template_attribute_value_ids.add(self.car_gps_no)
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_red)
        with bom_form_car.bom_line_ids.new() as line:
            line.product_id = self.dashboard._get_variant_for_combination(
                self.dashboard_gps_no + self.dashboard_color_blue)
            line.product_qty = 5
            line.bom_product_template_attribute_value_ids.add(self.car_gps_no)
            line.bom_product_template_attribute_value_ids.add(
                self.car_color_blue)
        bom_car = bom_form_car.save()

        bom_dashboard = Form(self.env['mrp.bom'])
        bom_dashboard.product_tmpl_id = self.dashboard
        bom_dashboard.product_qty = 2
        with bom_dashboard.bom_line_ids.new() as line:
            line.product_id = self.paint._get_variant_for_combination(
                self.paint_color_red)
            line.product_uom_id = uom_litre
            line.product_qty = 1
            line.bom_product_template_attribute_value_ids.add(
                self.dashboard_color_red)
        with bom_dashboard.bom_line_ids.new() as line:
            line.product_id = self.paint._get_variant_for_combination(
                self.paint_color_blue)
            line.product_uom_id = uom_litre
            line.product_qty = 1
            line.bom_product_template_attribute_value_ids.add(
                self.dashboard_color_blue)
        with bom_dashboard.bom_line_ids.new() as line:
            line.product_id = self.gps
            line.product_qty = 2
            line.bom_product_template_attribute_value_ids.add(
                self.dashboard_gps_yes)
        bom_dashboard = bom_dashboard.save()

        blue_car_with_gps = self.car._get_variant_for_combination(
            self.car_color_blue + self.car_gps_yes)

        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_car.id,
                searchQty=1,
                searchVariant=blue_car_with_gps.id)
        # Two lines. blue dashboard with gps and blue paint.
        self.assertEqual(len(report_values['lines']['components']), 2)

        # 10l of blue paint
        blue_paint = self.paint._get_variant_for_combination(
            self.paint_color_blue)
        self.assertEqual(blue_paint.id,
                         report_values['lines']['components'][0]['prod_id'])
        self.assertEqual(report_values['lines']['components'][0]['prod_qty'],
                         10)
        # 1 blue dashboard with GPS
        blue_dashboard_gps = self.dashboard._get_variant_for_combination(
            self.dashboard_color_blue + self.dashboard_gps_yes)
        self.assertEqual(blue_dashboard_gps.id,
                         report_values['lines']['components'][1]['prod_id'])
        self.assertEqual(report_values['lines']['components'][1]['prod_qty'],
                         1)
        component = report_values['lines']['components'][1]
        report_values_dashboad = self.env[
            'report.mrp.report_bom_structure']._get_bom(
                component['child_bom'], component['prod_id'],
                component['prod_qty'], component['line_id'],
                component['level'] + 1)

        self.assertEqual(len(report_values_dashboad['components']), 2)
        self.assertEqual(blue_paint.id,
                         report_values_dashboad['components'][0]['prod_id'])
        self.assertEqual(self.gps.id,
                         report_values_dashboad['components'][1]['prod_id'])

        # 0.5l of paint at price of 20$/litre -> 10$
        self.assertEqual(report_values_dashboad['components'][0]['total'], 10)
        # GPS 700$
        self.assertEqual(report_values_dashboad['components'][1]['total'], 700)

        # Dashboard blue with GPS should have a BoM cost of 710$
        self.assertEqual(report_values['lines']['components'][1]['total'], 710)
        # 10l of paint at price of 20$/litre -> 200$
        self.assertEqual(report_values['lines']['components'][0]['total'], 200)

        # Total cost of blue car with GPS: 10 + 700 + 200 = 910
        self.assertEqual(report_values['lines']['total'], 910)

        red_car_without_gps = self.car._get_variant_for_combination(
            self.car_color_red + self.car_gps_no)

        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_car.id,
                searchQty=1,
                searchVariant=red_car_without_gps.id)
        # Same math than before but without GPS
        self.assertEqual(report_values['lines']['total'], 210)
Пример #12
0
    def test_unbuild_standart(self):
        """ This test creates a MO and then creates 3 unbuild
        orders for the final product. None of 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()
        self.assertEqual(len(mo), 1, 'MO should have been created')

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

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.qty_producing = 5.0
        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), 5,
            'You should have the 5 final product in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location), 80,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location), 0,
            'You should have consumed all the 5 product in stock')

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

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_qty = 3
        x.product_uom_id = self.uom_unit
        x.save().action_unbuild()

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

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_qty = 2
        x.product_uom_id = self.uom_unit
        x.save().action_unbuild()

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

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_qty = 5
        x.product_uom_id = self.uom_unit
        x.save().action_unbuild()

        # Check quantity in stock after last unbuild.
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, 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), 120,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location), 10,
            'You should have consumed all the 5 product in stock')
Пример #13
0
    def test_tracking_on_byproducts(self):
        product_final = self.env['product.product'].create({
            'name':
            'Finished Product',
            'type':
            'product',
            'tracking':
            'serial',
        })
        product_1 = self.env['product.product'].create({
            'name': 'Raw 1',
            'type': 'product',
            'tracking': 'serial',
        })
        product_2 = self.env['product.product'].create({
            'name': 'Raw 2',
            'type': 'product',
            'tracking': 'serial',
        })
        byproduct_1 = self.env['product.product'].create({
            'name': 'Byproduct 1',
            'type': 'product',
            'tracking': 'serial',
        })
        byproduct_2 = self.env['product.product'].create({
            'name': 'Byproduct 2',
            'type': 'product',
            'tracking': 'serial',
        })
        bom_1 = self.env['mrp.bom'].create({
            'product_id':
            product_final.id,
            'product_tmpl_id':
            product_final.product_tmpl_id.id,
            'product_uom_id':
            self.uom_unit.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'bom_line_ids': [(0, 0, {
                'product_id': product_1.id,
                'product_qty': 1
            }), (0, 0, {
                'product_id': product_2.id,
                'product_qty': 1
            })],
            'byproduct_ids': [(0, 0, {
                'product_id': byproduct_1.id,
                'product_qty': 1,
                'product_uom_id': byproduct_1.uom_id.id
            }),
                              (0, 0, {
                                  'product_id': byproduct_2.id,
                                  'product_qty': 1,
                                  'product_uom_id': byproduct_2.uom_id.id
                              })]
        })
        mo_form = Form(self.env['mrp.production'])
        mo_form.product_id = product_final
        mo_form.bom_id = bom_1
        mo_form.product_qty = 2
        mo = mo_form.save()
        mo.action_confirm()

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.finished_lot_id = self.env['stock.production.lot'].create(
            {
                'product_id': product_final.id,
                'name': 'Final_lot_1',
                'company_id': self.env.company.id,
            })
        with produce_form.raw_workorder_line_ids.edit(0) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                product_1.id,
                'name':
                'Raw_1_lot_1',
                'company_id':
                self.env.company.id,
            })
        with produce_form.raw_workorder_line_ids.edit(1) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                product_2.id,
                'name':
                'Raw_2_lot_1',
                'company_id':
                self.env.company.id,
            })
        with produce_form.finished_workorder_line_ids.edit(0) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                byproduct_1.id,
                'name':
                'Byproduct_1_lot_1',
                'company_id':
                self.env.company.id,
            })
        with produce_form.finished_workorder_line_ids.edit(1) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                byproduct_2.id,
                'name':
                'Byproduct_2_lot_1',
                'company_id':
                self.env.company.id,
            })
        produce_wizard = produce_form.save()
        produce_wizard.continue_production()

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.finished_lot_id = self.env['stock.production.lot'].create(
            {
                'product_id': product_final.id,
                'name': 'Final_lot_2',
                'company_id': self.env.company.id,
            })
        with produce_form.raw_workorder_line_ids.edit(0) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                product_1.id,
                'name':
                'Raw_1_lot_2',
                'company_id':
                self.env.company.id,
            })
        with produce_form.raw_workorder_line_ids.edit(1) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                product_2.id,
                'name':
                'Raw_2_lot_2',
                'company_id':
                self.env.company.id,
            })
        with produce_form.finished_workorder_line_ids.edit(0) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                byproduct_1.id,
                'name':
                'Byproduct_1_lot_2',
                'company_id':
                self.env.company.id,
            })
        with produce_form.finished_workorder_line_ids.edit(1) as line:
            line.lot_id = self.env['stock.production.lot'].create({
                'product_id':
                byproduct_2.id,
                'name':
                'Byproduct_2_lot_2',
                'company_id':
                self.env.company.id,
            })
        produce_wizard = produce_form.save()
        produce_wizard.do_produce()
        mo.button_mark_done()

        self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 4)
        self.assertEqual(len(mo.move_finished_ids.mapped('move_line_ids')), 6)

        raw_move_lines = mo.move_raw_ids.mapped('move_line_ids')
        raw_line_raw_1_lot_1 = raw_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Raw_1_lot_1')
        self.assertEqual(
            set(raw_line_raw_1_lot_1.lot_produced_ids.mapped('name')),
            set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1']))
        raw_line_raw_1_lot_2 = raw_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Raw_1_lot_2')
        self.assertEqual(
            set(raw_line_raw_1_lot_2.lot_produced_ids.mapped('name')),
            set(['Final_lot_2', 'Byproduct_1_lot_2', 'Byproduct_2_lot_2']))
        raw_line_raw_2_lot_1 = raw_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Raw_2_lot_1')
        self.assertEqual(
            set(raw_line_raw_2_lot_1.lot_produced_ids.mapped('name')),
            set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1']))
        raw_line_raw_2_lot_2 = raw_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Raw_2_lot_2')
        self.assertEqual(
            set(raw_line_raw_2_lot_2.lot_produced_ids.mapped('name')),
            set(['Final_lot_2', 'Byproduct_1_lot_2', 'Byproduct_2_lot_2']))

        finished_move_lines = mo.move_finished_ids.mapped('move_line_ids')
        finished_move_line_lot_1 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Final_lot_1')
        self.assertEqual(finished_move_line_lot_1.consume_line_ids,
                         raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
        finished_move_line_lot_2 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Final_lot_2')
        self.assertEqual(finished_move_line_lot_2.consume_line_ids,
                         raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)

        byproduct_move_line_1_lot_1 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Byproduct_1_lot_1')
        self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids,
                         raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
        byproduct_move_line_1_lot_2 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Byproduct_1_lot_2')
        self.assertEqual(byproduct_move_line_1_lot_2.consume_line_ids,
                         raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)

        byproduct_move_line_2_lot_1 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Byproduct_2_lot_1')
        self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids,
                         raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
        byproduct_move_line_2_lot_2 = finished_move_lines.filtered(
            lambda ml: ml.lot_id.name == 'Byproduct_2_lot_2')
        self.assertEqual(byproduct_move_line_2_lot_2.consume_line_ids,
                         raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
Пример #14
0
    def test_procurement(self):
        """This test case when create production order check procurement is create"""
        # Update BOM
        self.bom_3.bom_line_ids.filtered(
            lambda x: x.product_id == self.product_5).unlink()
        self.bom_1.bom_line_ids.filtered(
            lambda x: x.product_id == self.product_1).unlink()

        # Update route
        self.warehouse = self.env.ref('stock.warehouse0')
        route_manufacture = self.warehouse.manufacture_pull_id.route_id.id
        route_mto = self.warehouse.mto_pull_id.route_id.id
        self.product_4.write(
            {'route_ids': [(6, 0, [route_manufacture, route_mto])]})

        # Create production order
        # -------------------------
        # Product6 Unit 24
        #    Product4 8 Dozen
        #    Product2 12 Unit
        # -----------------------

        production_form = Form(self.env['mrp.production'])
        production_form.product_id = self.product_6
        production_form.bom_id = self.bom_3
        production_form.product_qty = 24
        production_form.product_uom_id = self.product_6.uom_id
        production_product_6 = production_form.save()
        production_product_6.action_confirm()
        production_product_6.action_assign()

        # check production state is Confirmed
        self.assertEqual(production_product_6.state, 'confirmed',
                         'Production order should be for Confirmed state')

        # Check procurement for product 4 created or not.
        # Check it created a purchase order

        move_raw_product4 = production_product_6.move_raw_ids.filtered(
            lambda x: x.product_id == self.product_4)
        produce_product_4 = self.env['mrp.production'].search([
            ('product_id', '=', self.product_4.id),
            ('move_dest_ids', '=', move_raw_product4[0].id)
        ])
        # produce product
        self.assertEqual(produce_product_4.reservation_state, 'confirmed',
                         "Consume material not available")

        # Create production order
        # -------------------------
        # Product 4  96 Unit
        #    Product2 48 Unit
        # ---------------------
        # Update Inventory
        self.env['stock.quant'].with_context(inventory_mode=True).create({
            'product_id':
            self.product_2.id,
            'inventory_quantity':
            48,
            'location_id':
            self.warehouse.lot_stock_id.id,
        })
        produce_product_4.action_assign()
        self.assertEqual(produce_product_4.product_qty, 8,
                         "Wrong quantity of finish product.")
        self.assertEqual(produce_product_4.product_uom_id, self.uom_dozen,
                         "Wrong quantity of finish product.")
        self.assertEqual(produce_product_4.reservation_state, 'assigned',
                         "Consume material not available")

        # produce product4
        # ---------------

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            produce_product_4.id,
            'active_ids': [produce_product_4.id],
        }))
        produce_form.qty_producing = produce_product_4.product_qty
        product_produce = produce_form.save()
        product_produce.do_produce()
        produce_product_4.post_inventory()
        # Check procurement and Production state for product 4.
        produce_product_4.button_mark_done()
        self.assertEqual(produce_product_4.state, 'done',
                         'Production order should be in state done')

        # Produce product 6
        # ------------------

        # Update Inventory
        self.env['stock.quant'].with_context(inventory_mode=True).create({
            'product_id':
            self.product_2.id,
            'inventory_quantity':
            12,
            'location_id':
            self.warehouse.lot_stock_id.id,
        })
        production_product_6.action_assign()

        # ------------------------------------

        self.assertEqual(production_product_6.reservation_state, 'assigned',
                         "Consume material not available")
        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            production_product_6.id,
            'active_ids': [production_product_6.id],
        }))
        produce_form.qty_producing = production_product_6.product_qty
        product_produce = produce_form.save()
        product_produce.do_produce()
        production_product_6.post_inventory()
        # Check procurement and Production state for product 6.
        production_product_6.button_mark_done()
        self.assertEqual(production_product_6.state, 'done',
                         'Production order should be in state done')
        self.assertEqual(self.product_6.qty_available, 24,
                         'Wrong quantity available of finished product.')
Пример #15
0
    def test_unbuild_with_duplicate_move(self):
        """ This test creates a MO from 3 different lot on a consumed product (p2).
        The unbuild order should revert the correct quantity for each specific lot.
        """
        mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='none',
                                                    tracking_base_2='lot',
                                                    tracking_base_1='none')
        self.assertEqual(len(mo), 1, 'MO should have been created')

        lot_1 = self.env['stock.production.lot'].create({
            'name':
            'lot_1',
            'product_id':
            p2.id,
            'company_id':
            self.env.company.id,
        })
        lot_2 = self.env['stock.production.lot'].create({
            'name':
            'lot_2',
            'product_id':
            p2.id,
            'company_id':
            self.env.company.id,
        })
        lot_3 = self.env['stock.production.lot'].create({
            'name':
            'lot_3',
            'product_id':
            p2.id,
            'company_id':
            self.env.company.id,
        })
        self.env['stock.quant']._update_available_quantity(
            p1, self.stock_location, 100)
        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           1,
                                                           lot_id=lot_1)
        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           3,
                                                           lot_id=lot_2)
        self.env['stock.quant']._update_available_quantity(p2,
                                                           self.stock_location,
                                                           2,
                                                           lot_id=lot_3)
        mo.action_assign()

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.qty_producing = 5.0
        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), 5,
            'You should have the 5 final product in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location), 80,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_1), 0,
            'You should have consumed all the 1 product for lot 1 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 3 product for lot 2 in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_3), 1,
            'You should have consumed only 1 product for lot3 in stock')

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 5
        x.save().action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location), 0,
            'You should have no more final product in stock after unbuild')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p1, self.stock_location), 100,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_1), 1,
            'You should have get your product with lot 1 in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_2), 3,
            'You should have the 3 basic product for lot 2 in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location, lot_id=lot_3), 2,
            'You should have get one product back for lot 3')
Пример #16
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,
            '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,
                                                           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.qty_producing = 5.0
        produce_form.finished_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
        # ---------------------------------------------------

        x = Form(self.env['mrp.unbuild'])
        with self.assertRaises(AssertionError):
            x.product_id = p_final
            x.bom_id = bom
            x.product_uom_id = self.uom_unit
            x.product_qty = 3
            x.save()

        with self.assertRaises(AssertionError):
            x.product_id = p_final
            x.bom_id = bom
            x.product_uom_id = self.uom_unit
            x.product_qty = 3
            x.save()

        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')

        with self.assertRaises(AssertionError):
            x.product_id = p_final
            x.bom_id = bom
            x.product_uom_id = self.uom_unit
            x.mo_id = mo
            x.product_qty = 3
            x.save()

        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')

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 3
        x.lot_id = lot_final
        x.save().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')

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 2
        x.lot_id = lot_final
        x.save().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')

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 5
        x.lot_id = lot_final
        x.save().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')
Пример #17
0
    def test_unbuild_with_comnsumed_lot(self):
        """ This test creates a MO and then creates 3 unbuild
        orders for the final product. Only once of the two consumed
        product is tracked by lot. It checks the stock state after each
        order and ensure it is correct.
        """
        mo, bom, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot')
        self.assertEqual(len(mo), 1, 'MO should have been created')

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

        self.env['stock.quant']._update_available_quantity(p1,
                                                           self.stock_location,
                                                           100,
                                                           lot_id=lot)
        self.env['stock.quant']._update_available_quantity(
            p2, self.stock_location, 5)
        mo.action_assign()
        for ml in mo.move_raw_ids.mapped('move_line_ids'):
            if ml.product_id.tracking != 'none':
                self.assertEqual(ml.lot_id, lot, 'Wrong reserved lot.')

        produce_form = Form(self.env['mrp.product.produce'].with_context({
            'active_id':
            mo.id,
            'active_ids': [mo.id],
        }))
        produce_form.qty_producing = 5.0
        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), 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), 80,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location), 0,
            'You should have consumed all the 5 product in stock')

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

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_qty = 3
        x.product_uom_id = self.uom_unit
        unbuild_order = x.save()

        # This should fail since we do not provide the MO that we wanted to unbuild. (without MO we do not know which consumed lot we have to restore)
        with self.assertRaises(UserError):
            unbuild_order.action_unbuild()

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

        unbuild_order.mo_id = mo.id
        unbuild_order.action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location), 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), 92,
            'You should have 92 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location), 3,
            'You should have consumed all the 5 product in stock')

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 2
        x.save().action_unbuild()

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

        x = Form(self.env['mrp.unbuild'])
        x.product_id = p_final
        x.bom_id = bom
        x.product_uom_id = self.uom_unit
        x.mo_id = mo
        x.product_qty = 5
        x.save().action_unbuild()

        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p_final, self.stock_location, 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), 120,
            'You should have 80 products in stock')
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                p2, self.stock_location), 10,
            'You should have consumed all the 5 product in stock')
Пример #18
0
    def setUp(self):
        super(TestBom, self).setUp()
        self.Product = self.env['product.product']
        self.Bom = self.env['mrp.bom']
        self.Routing = self.env['mrp.routing']
        self.operation = self.env['mrp.routing.workcenter']

        # Products.
        self.dining_table = self._create_product('Dining Table', 1000)
        self.table_head = self._create_product('Table Head', 300)
        self.screw = self._create_product('Screw', 10)
        self.leg = self._create_product('Leg', 25)
        self.glass = self._create_product('Glass', 100)

        # Unit of Measure.
        self.unit = self.env.ref("uom.product_uom_unit")
        self.dozen = self.env.ref("uom.product_uom_dozen")

        # Bills Of Materials.
        # -------------------------------------------------------------------------------
        # Cost of BoM (Dining Table 1 Unit)
        # Component Cost =  Table Head   1 Unit * 300 = 300 (468.75 from it's components)
        #                   Screw        5 Unit *  10 =  50
        #                   Leg          4 Unit *  25 = 100
        #                   Glass        1 Unit * 100 = 100
        # Total = 550 [718.75 if components of Table Head considered] (for 1 Unit)
        # -------------------------------------------------------------------------------

        bom_form = Form(self.Bom)
        bom_form.product_id = self.dining_table
        bom_form.product_tmpl_id = self.dining_table.product_tmpl_id
        bom_form.product_qty = 1.0
        bom_form.product_uom_id = self.unit
        bom_form.type = 'normal'
        with bom_form.bom_line_ids.new() as line:
            line.product_id = self.table_head
            line.product_qty = 1
        with bom_form.bom_line_ids.new() as line:
            line.product_id = self.screw
            line.product_qty = 5
        with bom_form.bom_line_ids.new() as line:
            line.product_id = self.leg
            line.product_qty = 4
        with bom_form.bom_line_ids.new() as line:
            line.product_id = self.glass
            line.product_qty = 1
        self.bom_1 = bom_form.save()

        # Table Head's components.
        self.plywood_sheet = self._create_product('Plywood Sheet', 200)
        self.bolt = self._create_product('Bolt', 10)
        self.colour = self._create_product('Colour', 100)
        self.corner_slide = self._create_product('Corner Slide', 25)

        # -----------------------------------------------------------------
        # Cost of BoM (Table Head 1 Dozen)
        # Component Cost =  Plywood Sheet   12 Unit * 200 = 2400
        #                   Bolt            60 Unit *  10 =  600
        #                   Colour          12 Unit * 100 = 1200
        #                   Corner Slide    57 Unit * 25  = 1425
        #                                           Total = 5625
        #                          1 Unit price (5625/12) =  468.75
        # -----------------------------------------------------------------

        bom_form2 = Form(self.Bom)
        bom_form2.product_id = self.table_head
        bom_form2.product_tmpl_id = self.table_head.product_tmpl_id
        bom_form2.product_qty = 1.0
        bom_form2.product_uom_id = self.dozen
        bom_form2.type = 'phantom'
        with bom_form2.bom_line_ids.new() as line:
            line.product_id = self.plywood_sheet
            line.product_qty = 12
        with bom_form2.bom_line_ids.new() as line:
            line.product_id = self.bolt
            line.product_qty = 60
        with bom_form2.bom_line_ids.new() as line:
            line.product_id = self.colour
            line.product_qty = 12
        with bom_form2.bom_line_ids.new() as line:
            line.product_id = self.corner_slide
            line.product_qty = 57
        self.bom_2 = bom_form2.save()
Пример #19
0
 def generate_mo(self,
                 tracking_final='none',
                 tracking_base_1='none',
                 tracking_base_2='none',
                 qty_final=5,
                 qty_base_1=4,
                 qty_base_2=1):
     """ This function generate a manufacturing order with one final
     product and two consumed product. Arguments allows to choose
     the tracking/qty for each different products. It returns the
     MO, used bom and the tree products.
     """
     product_to_build = self.env['product.product'].create({
         'name':
         'Young Tom',
         'type':
         'product',
         'tracking':
         tracking_final,
     })
     product_to_use_1 = self.env['product.product'].create({
         'name':
         'Botox',
         'type':
         'product',
         'tracking':
         tracking_base_1,
     })
     product_to_use_2 = self.env['product.product'].create({
         'name':
         'Old Tom',
         'type':
         'product',
         'tracking':
         tracking_base_2,
     })
     bom_1 = self.env['mrp.bom'].create({
         'product_id':
         product_to_build.id,
         'product_tmpl_id':
         product_to_build.product_tmpl_id.id,
         'product_uom_id':
         self.uom_unit.id,
         'product_qty':
         1.0,
         'type':
         'normal',
         'bom_line_ids': [(0, 0, {
             'product_id': product_to_use_2.id,
             'product_qty': qty_base_2
         }),
                          (0, 0, {
                              'product_id': product_to_use_1.id,
                              'product_qty': qty_base_1
                          })]
     })
     mo_form = Form(self.env['mrp.production'])
     mo_form.product_id = product_to_build
     mo_form.bom_id = bom_1
     mo_form.product_qty = qty_final
     mo = mo_form.save()
     mo.action_confirm()
     return mo, bom_1, product_to_build, product_to_use_1, product_to_use_2
Пример #20
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_ids': [(4, stock_id)],
        })
        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,
                        'company_id':
                        self.env.company.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,
                        'company_id':
                        self.env.company.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,
                        'company_id':
                        self.env.company.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,
                        'company_id':
                        self.env.company.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_form = Form(self.env['mrp.production'])
            mo_form.product_id = finished_product
            mo_form.bom_id = bom
            mo_form.product_uom_id = self.env.ref('uom.product_uom_unit')
            mo_form.product_qty = 1
            mo = mo_form.save()
            mo.action_confirm()
            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.qty_producing = 1

            if finished_product.tracking != 'none':
                produce_form.finished_lot_id = self.env[
                    'stock.production.lot'].create({
                        'name':
                        'Serial or Lot finished',
                        'product_id':
                        finished_product.id,
                        'company_id':
                        self.env.company.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 Units",
                    '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))
Пример #21
0
    def test_20_bom_report(self):
        """ Simulate a crumble receipt with mrp and open the bom structure
        report and check that data insde are correct.
        """
        uom_kg = self.env.ref('uom.product_uom_kgm')
        uom_litre = self.env.ref('uom.product_uom_litre')
        crumble = self.env['product.product'].create({
            'name': 'Crumble',
            'type': 'product',
            'uom_id': uom_kg.id,
            'uom_po_id': uom_kg.id,
        })
        butter = self.env['product.product'].create({
            'name': 'Butter',
            'type': 'product',
            'uom_id': uom_kg.id,
            'uom_po_id': uom_kg.id,
            'standard_price': 7.01
        })
        biscuit = self.env['product.product'].create({
            'name': 'Biscuit',
            'type': 'product',
            'uom_id': uom_kg.id,
            'uom_po_id': uom_kg.id,
            'standard_price': 1.5
        })
        bom_form_crumble = Form(self.env['mrp.bom'])
        bom_form_crumble.product_tmpl_id = crumble.product_tmpl_id
        bom_form_crumble.product_qty = 11
        bom_form_crumble.product_uom_id = uom_kg
        bom_crumble = bom_form_crumble.save()

        with Form(bom_crumble) as bom:
            with bom.bom_line_ids.new() as line:
                line.product_id = butter
                line.product_uom_id = uom_kg
                line.product_qty = 5
            with bom.bom_line_ids.new() as line:
                line.product_id = biscuit
                line.product_uom_id = uom_kg
                line.product_qty = 6

        workcenter = self.env['mrp.workcenter'].create({
            'costs_hour': 10,
            'name': 'Deserts Table'
        })

        routing_form = Form(self.env['mrp.routing'])
        routing_form.name = "Crumble process"
        routing_crumble = routing_form.save()

        with Form(routing_crumble) as routing:
            with routing.operation_ids.new() as operation:
                operation.workcenter_id = workcenter
                operation.name = 'Prepare biscuits'
                operation.time_cycle_manual = 5
            with routing.operation_ids.new() as operation:
                operation.workcenter_id = workcenter
                operation.name = 'Prepare butter'
                operation.time_cycle_manual = 3
            with routing.operation_ids.new() as operation:
                operation.workcenter_id = workcenter
                operation.name = 'Mix manually'
                operation.time_cycle_manual = 5

        bom_crumble.routing_id = routing_crumble.id

        # TEST BOM STRUCTURE VALUE WITH BOM QUANTITY
        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_crumble.id, searchQty=11, searchVariant=False)
        # 5 min 'Prepare biscuits' + 3 min 'Prepare butter' + 5 min 'Mix manually' = 13 minutes
        self.assertEqual(
            report_values['lines']['operations_time'], 13.0,
            'Operation time should be the same for 1 unit or for the batch')
        # Operation cost is the sum of operation line.
        operation_cost = float_round(5 / 60 * 10,
                                     precision_digits=2) * 2 + float_round(
                                         3 / 60 * 10, precision_digits=2)
        self.assertEqual(
            float_compare(report_values['lines']['operations_cost'],
                          operation_cost,
                          precision_digits=2), 0,
            '13 minute for 10$/hours -> 2.16')

        for component_line in report_values['lines']['components']:
            # standard price * bom line quantity * current quantity / bom finished product quantity
            if component_line['prod_id'] == butter.id:
                # 5 kg of butter at 7.01$ for 11kg of crumble -> 35.05$
                self.assertEqual(
                    float_compare(component_line['total'], (7.01 * 5),
                                  precision_digits=2), 0)
            if component_line['prod_id'] == biscuit.id:
                # 6 kg of biscuits at 1.50$ for 11kg of crumble -> 9$
                self.assertEqual(
                    float_compare(component_line['total'], (1.5 * 6),
                                  precision_digits=2), 0)
        # total price = 35.05 + 9 + operation_cost(0.83 + 0.83 + 0.5 = 2.16) = 46,21
        self.assertEqual(
            float_compare(report_values['lines']['total'],
                          46.21,
                          precision_digits=2), 0,
            'Product Bom Price is not correct')
        self.assertEqual(
            float_compare(report_values['lines']['total'] / 11.0,
                          4.20,
                          precision_digits=2), 0,
            'Product Unit Bom Price is not correct')

        # TEST BOM STRUCTURE VALUE BY UNIT
        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_crumble.id, searchQty=1, searchVariant=False)
        # 5 min 'Prepare biscuits' + 3 min 'Prepare butter' + 5 min 'Mix manually' = 13 minutes
        self.assertEqual(
            report_values['lines']['operations_time'], 13.0,
            'Operation time should be the same for 1 unit or for the batch')
        # Operation cost is the sum of operation line.
        operation_cost = float_round(5 / 60 * 10,
                                     precision_digits=2) * 2 + float_round(
                                         3 / 60 * 10, precision_digits=2)
        self.assertEqual(
            float_compare(report_values['lines']['operations_cost'],
                          operation_cost,
                          precision_digits=2), 0,
            '13 minute for 10$/hours -> 2.16')

        for component_line in report_values['lines']['components']:
            # standard price * bom line quantity * current quantity / bom finished product quantity
            if component_line['prod_id'] == butter.id:
                # 5 kg of butter at 7.01$ for 11kg of crumble -> / 11 for price per unit (3.19)
                self.assertEqual(
                    float_compare(component_line['total'],
                                  (7.01 * 5) * (1 / 11),
                                  precision_digits=2), 0)
            if component_line['prod_id'] == biscuit.id:
                # 6 kg of biscuits at 1.50$ for 11kg of crumble -> / 11 for price per unit (0.82)
                self.assertEqual(
                    float_compare(component_line['total'],
                                  (1.5 * 6) * (1 / 11),
                                  precision_digits=2), 0)
        # total price = 3.19 + 0.82 + operation_cost(0.83 + 0.83 + 0.5 = 2.16) = 6,17
        self.assertEqual(
            float_compare(report_values['lines']['total'],
                          6.17,
                          precision_digits=2), 0,
            'Product Unit Bom Price is not correct')

        # TEST OPERATION COST WHEN PRODUCED QTY > BOM QUANTITY
        report_values_12 = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_crumble.id, searchQty=12, searchVariant=False)
        report_values_22 = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_crumble.id, searchQty=22, searchVariant=False)
        operation_cost = float_round(10 / 60 * 10,
                                     precision_digits=2) * 2 + float_round(
                                         6 / 60 * 10, precision_digits=2)
        # Both needs 2 operation cycle
        self.assertEqual(report_values_12['lines']['operations_cost'],
                         report_values_22['lines']['operations_cost'])
        self.assertEqual(report_values_22['lines']['operations_cost'],
                         operation_cost)
        report_values_23 = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_crumble.id, searchQty=23, searchVariant=False)
        operation_cost = float_round(15 / 60 * 10,
                                     precision_digits=2) * 2 + float_round(
                                         9 / 60 * 10, precision_digits=2)
        self.assertEqual(report_values_23['lines']['operations_cost'],
                         operation_cost)

        # Create a more complex BoM with a sub product
        cheese_cake = self.env['product.product'].create({
            'name': 'Cheese Cake 300g',
            'type': 'product',
        })
        cream = self.env['product.product'].create({
            'name': 'cream',
            'type': 'product',
            'uom_id': uom_litre.id,
            'uom_po_id': uom_litre.id,
            'standard_price': 5.17,
        })
        bom_form_cheese_cake = Form(self.env['mrp.bom'])
        bom_form_cheese_cake.product_tmpl_id = cheese_cake.product_tmpl_id
        bom_form_cheese_cake.product_qty = 60
        bom_form_cheese_cake.product_uom_id = self.uom_unit
        bom_cheese_cake = bom_form_cheese_cake.save()

        with Form(bom_cheese_cake) as bom:
            with bom.bom_line_ids.new() as line:
                line.product_id = cream
                line.product_uom_id = uom_litre
                line.product_qty = 3
            with bom.bom_line_ids.new() as line:
                line.product_id = crumble
                line.product_uom_id = uom_kg
                line.product_qty = 5.4

        workcenter_2 = self.env['mrp.workcenter'].create({
            'name': 'cake mounting',
            'costs_hour': 20,
            'time_start': 10,
            'time_stop': 15
        })

        routing_form = Form(self.env['mrp.routing'])
        routing_form.name = "Cheese cake process"
        routing_cheese = routing_form.save()

        with Form(routing_cheese) as routing:
            with routing.operation_ids.new() as operation:
                operation.workcenter_id = workcenter
                operation.name = 'Mix cheese and crumble'
                operation.time_cycle_manual = 10
            with routing.operation_ids.new() as operation:
                operation.workcenter_id = workcenter_2
                operation.name = 'Cake mounting'
                operation.time_cycle_manual = 5

        bom_cheese_cake.routing_id = routing_cheese.id

        # TEST CHEESE BOM STRUCTURE VALUE WITH BOM QUANTITY
        report_values = self.env[
            'report.mrp.report_bom_structure']._get_report_data(
                bom_id=bom_cheese_cake.id, searchQty=60, searchVariant=False)
        self.assertEqual(
            report_values['lines']['operations_time'], 40.0,
            'Operation time should be the same for 1 unit or for the batch')
        # Operation cost is the sum of operation line.
        operation_cost = float_round(
            10 / 60 * 10, precision_digits=2) + float_round(30 / 60 * 20,
                                                            precision_digits=2)
        self.assertEqual(
            float_compare(report_values['lines']['operations_cost'],
                          operation_cost,
                          precision_digits=2), 0)

        for component_line in report_values['lines']['components']:
            # standard price * bom line quantity * current quantity / bom finished product quantity
            if component_line['prod_id'] == cream.id:
                # 3 liter of cream at 5.17$ for 60 unit of cheese cake -> 15.51$
                self.assertEqual(
                    float_compare(component_line['total'], (3 * 5.17),
                                  precision_digits=2), 0)
            if component_line['prod_id'] == crumble.id:
                # 5.4 kg of crumble at the cost of a batch.
                crumble_cost = self.env[
                    'report.mrp.report_bom_structure']._get_report_data(
                        bom_id=bom_crumble.id,
                        searchQty=5.4,
                        searchVariant=False)['lines']['total']
                self.assertEqual(
                    float_compare(component_line['total'],
                                  crumble_cost,
                                  precision_digits=2), 0)
        # total price = 15.51 + crumble_cost + operation_cost(10 + 1.67 = 11.67) = 27.18 + crumble_cost
        self.assertEqual(
            float_compare(report_values['lines']['total'],
                          27.18 + crumble_cost,
                          precision_digits=2), 0,
            'Product Bom Price is not correct')