# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import time from odoo.tests import Form, TransactionCase from odoo.tools import mute_logger from odoo import Command class TestSaleMrpProcurement(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.env.ref('base.group_user').write({'implied_ids': [(4, cls.env.ref('product.group_product_variant').id)]}) def test_sale_mrp(self): # Required for `uom_id` to be visible in the view self.env.user.groups_id += self.env.ref('uom.group_uom') self.env.ref('stock.route_warehouse0_mto').active = True warehouse0 = self.env.ref('stock.warehouse0') # In order to test the sale_mrp module in OpenERP, I start by creating a new product 'Slider Mobile' # I define product category Mobile Products Sellable. with mute_logger('odoo.tests.common.onchange'): # Suppress warning on "Changing your cost method" when creating a # product category pc = Form(self.env['product.category']) pc.name = 'Mobile Products Sellable' product_category_allproductssellable0 = pc.save() uom_unit = self.env.ref('uom.product_uom_unit') self.assertIn("seller_ids", self.env['product.template'].fields_get()) # I define product for Slider Mobile. product = Form(self.env['product.template']) product.categ_id = product_category_allproductssellable0 product.list_price = 200.0 product.name = 'Slider Mobile' product.is_storable = True product.uom_id = uom_unit product.uom_po_id = uom_unit product.route_ids.clear() product.route_ids.add(warehouse0.manufacture_pull_id.route_id) product.route_ids.add(warehouse0.mto_pull_id.route_id) product_template_slidermobile0 = product.save() product_template_slidermobile0.standard_price = 189 product_component = Form(self.env['product.product']) product_component.name = 'Battery' product_product_bettery = product_component.save() with Form(self.env['mrp.bom']) as bom: bom.product_tmpl_id = product_template_slidermobile0 with bom.bom_line_ids.new() as line: line.product_id = product_product_bettery line.product_qty = 4 # I create a sale order for product Slider mobile so_form = Form(self.env['sale.order']) so_form.partner_id = self.env['res.partner'].create({'name': 'Another Test Partner'}) with so_form.order_line.new() as line: line.product_id = product_template_slidermobile0.product_variant_ids line.price_unit = 200 line.product_uom_qty = 500.0 line.customer_lead = 7.0 sale_order_so0 = so_form.save() # I confirm the sale order sale_order_so0.action_confirm() # I verify that a manufacturing order has been generated, and that its name and reference are correct mo = self.env['mrp.production'].search([('origin', 'like', sale_order_so0.name)], limit=1) self.assertTrue(mo, 'Manufacturing order has not been generated') # Check the mo is displayed on the so self.assertEqual(mo.id, sale_order_so0.action_view_mrp_production()['res_id']) def test_sale_mrp_pickings(self): """ Test sale of multiple mrp products in MTO to avoid generating multiple deliveries to the customer location """ # Required for `uom_id` to be visible in the view self.env.user.groups_id += self.env.ref('uom.group_uom') # Required for `manufacture_step` to be visible in the view self.env.user.groups_id += self.env.ref('stock.group_adv_location') self.env.ref('stock.route_warehouse0_mto').active = True # Create warehouse self.customer_location = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers') self.warehouse = self.env['stock.warehouse'].create({ 'name': 'Test Warehouse', 'code': 'TWH' }) self.uom_unit = self.env.ref('uom.product_uom_unit') # Create raw product for manufactured product product_form = Form(self.env['product.product']) product_form.name = 'Raw Stick' product_form.is_storable = True product_form.uom_id = self.uom_unit product_form.uom_po_id = self.uom_unit self.raw_product = product_form.save() # 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.is_storable = True 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 manifactured product which uses another manifactured product_form = Form(self.env['product.product']) product_form.name = 'Arrow' product_form.is_storable = True 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.complex_product = product_form.save() ## Create raw product for manufactured product product_form = Form(self.env['product.product']) product_form.name = 'Raw Iron' product_form.is_storable = True product_form.uom_id = self.uom_unit product_form.uom_po_id = self.uom_unit self.raw_product_2 = 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() ## Create bom for manufactured product bom_product_form = Form(self.env['mrp.bom']) bom_product_form.product_id = self.complex_product bom_product_form.product_tmpl_id = self.complex_product.product_tmpl_id with bom_product_form.bom_line_ids.new() as line: line.product_id = self.finished_product line.product_qty = 1.0 with bom_product_form.bom_line_ids.new() as line: line.product_id = self.raw_product_2 line.product_qty = 1.0 self.complex_bom = bom_product_form.save() with Form(self.warehouse) as warehouse: warehouse.manufacture_steps = 'pbm_sam' so_form = Form(self.env['sale.order']) so_form.partner_id = self.env['res.partner'].create({'name': 'Another Test Partner'}) with so_form.order_line.new() as line: line.product_id = self.complex_product line.price_unit = 1 line.product_uom_qty = 1 with so_form.order_line.new() as line: line.product_id = self.finished_product line.price_unit = 1 line.product_uom_qty = 1 sale_order_so0 = so_form.save() sale_order_so0.action_confirm() # Verify buttons are working as expected self.assertEqual(sale_order_so0.mrp_production_count, 3, "2 Mos for the 2 sale order line + 1 child Mo for the complex product") pickings = sale_order_so0.picking_ids # One delivery... self.assertEqual(len(pickings), 1) # ...with two products self.assertEqual(len(pickings[0].move_ids), 2) def test_post_prod_location_child_of_stock_location(self): """ 3-steps manufacturing, the post-prod location is a child of the stock location. Have a manufactured product with the manufacture route and a RR min=max=0. Confirm a SO with that product -> It should generate a MO """ warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) manufacture_route = warehouse.manufacture_pull_id.route_id warehouse.manufacture_steps = 'pbm_sam' warehouse.sam_loc_id.location_id = warehouse.lot_stock_id product, component = self.env['product.product'].create([{ 'name': 'Finished', 'is_storable': True, 'route_ids': [(6, 0, manufacture_route.ids)], }, { 'name': 'Component', 'type': 'consu', }]) self.env['mrp.bom'].create({ 'product_id': product.id, 'product_tmpl_id': product.product_tmpl_id.id, 'product_uom_id': product.uom_id.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': component.id, 'product_qty': 1}), ], }) self.env['stock.warehouse.orderpoint'].create({ 'name': product.name, 'location_id': warehouse.lot_stock_id.id, 'product_id': product.id, 'product_min_qty': 0, 'product_max_qty': 0, 'trigger': 'auto', }) so = self.env['sale.order'].create({ 'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id, 'order_line': [ (0, 0, { 'name': product.name, 'product_id': product.id, 'product_uom_qty': 1.0, 'product_uom': product.uom_id.id, 'price_unit': 1, })], }) so.action_confirm() self.assertEqual(so.state, 'sale') mo = self.env['mrp.production'].search([('product_id', '=', product.id)], order='id desc', limit=1) self.assertIn(so.name, mo.origin) def test_so_reordering_rule(self): kit_1, component_1 = self.env['product.product'].create([{ 'name': n, 'is_storable': True, } for n in ['Kit 1', 'Compo 1']]) self.env['mrp.bom'].create([{ 'product_tmpl_id': kit_1.product_tmpl_id.id, 'product_qty': 1, 'type': 'phantom', 'bom_line_ids': [ (0, 0, {'product_id': component_1.id, 'product_qty': 1}), ], }]) customer = self.env['res.partner'].create({ 'name': 'customer', }) so = self.env['sale.order'].create({ 'partner_id': customer.id, 'order_line': [ (0, 0, { 'product_id': kit_1.id, 'product_uom_qty': 1.0, })], }) so.action_confirm() self.env['stock.warehouse.orderpoint']._get_orderpoint_action() orderpoint_product = self.env['stock.warehouse.orderpoint'].search( [('product_id', '=', kit_1.id)]) self.assertFalse(orderpoint_product) def test_so_reordering_rule_02(self): """ Have a manufactured product in kg unit of measure with the manufacturing route and a reordering rule (RR) set to min=max=0, and a BoM in grams. Confirm a SO with that product in 510 grams -> It should generate a MO with 510g. Create a second SO with 510g -> It should update the MO to 1020g. """ warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) manufacture_route = warehouse.manufacture_pull_id.route_id uom_kg = self.env.ref('uom.product_uom_kgm') uom_gram = self.env.ref('uom.product_uom_gram') product, component = self.env['product.product'].create([{ 'name': 'Finished', 'is_storable': True, 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, 'route_ids': [(6, 0, manufacture_route.ids)], }, { 'name': 'Component', 'type': 'consu', }]) self.env['mrp.bom'].create({ 'product_id': product.id, 'product_tmpl_id': product.product_tmpl_id.id, 'product_uom_id': uom_gram.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': component.id, 'product_qty': 1}), ], }) self.env['stock.warehouse.orderpoint'].create({ 'name': product.name, 'location_id': warehouse.lot_stock_id.id, 'product_id': product.id, 'product_min_qty': 0, 'product_max_qty': 0, 'trigger': 'auto', 'qty_multiple': 0.01, }) so = self.env['sale.order'].create({ 'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id, 'order_line': [ (0, 0, { 'name': product.name, 'product_id': product.id, 'product_uom_qty': 510, 'product_uom': uom_gram.id, 'price_unit': 1, })], }) so.action_confirm() self.assertEqual(so.state, 'sale') mo = self.env['mrp.production'].search([('product_id', '=', product.id)], order='id desc', limit=1) self.assertIn(so.name, mo.origin) self.assertEqual(mo.product_uom_id, uom_gram) self.assertEqual(mo.product_qty, 510) so_2 = self.env['sale.order'].create({ 'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id, 'order_line': [ (0, 0, { 'name': product.name, 'product_id': product.id, 'product_uom_qty': 510, 'product_uom': uom_gram.id, 'price_unit': 1, })], }) so_2.action_confirm() self.assertEqual(so_2.state, 'sale') self.assertEqual(mo.product_uom_id, uom_gram) self.assertEqual(mo.product_qty, 1020) def test_sale_mrp_avoid_multiple_pickings(self): """ Test sale of multiple products. Avoid multiple pickings being generated when we are not in 3 steps manufacturing. """ warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) warehouse.sam_loc_id = warehouse.lot_stock_id so = self.env['sale.order'].create({ 'partner_id': self.env['res.partner'].create({'name': 'My Partner'}).id, 'order_line': [ Command.create({ 'name': 'sol_p1', 'product_id': self.env['product.product'].create({'name': 'p1'}).id, 'product_uom_qty': 1, 'product_uom': self.env.ref('uom.product_uom_unit').id, }), Command.create({ 'name': 'sol_p2', 'product_id': self.env['product.product'].create({'name': 'p2'}).id, 'product_uom_qty': 1, 'product_uom': self.env.ref('uom.product_uom_unit').id, }), ], }) so.action_confirm() self.assertEqual(len(so.picking_ids), 1) self.assertEqual(so.picking_ids.picking_type_id, warehouse.out_type_id)