# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import date, datetime, timedelta from odoo.fields import Command from odoo.tests import Form, TransactionCase from odoo.tools import mute_logger from odoo.exceptions import UserError class TestProcRule(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.uom_unit = cls.env.ref('uom.product_uom_unit') cls.product = cls.env['product.product'].create({ 'name': 'Desk Combination', 'type': 'consu', }) cls.partner = cls.env['res.partner'].create({'name': 'Partner'}) def test_qty_to_order_remainder_decimal(self): """Test case for when remainder is decimal""" self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations') orderpoint_form = Form(self.env['stock.warehouse.orderpoint']) orderpoint_form.product_id = self.product orderpoint_form.location_id = self.env.ref('stock.stock_location_stock') orderpoint_form.product_min_qty = 4.0 orderpoint_form.product_max_qty = 5.1 orderpoint_form.qty_multiple = 0.1 orderpoint = orderpoint_form.save() self.assertAlmostEqual(orderpoint.qty_to_order, orderpoint.product_max_qty) def test_endless_loop_rules_from_location(self): """ Creates and configure a rule the way, when trying to get rules from location, it goes in a state where the found rule tries to trigger another rule but finds nothing else than itself and so get stuck in a recursion error.""" warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) reception_route = warehouse.reception_route_id self.product.is_storable = True # Creates a delivery for this product, that way, this product will be to resupply. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = warehouse.out_type_id with picking_form.move_ids_without_package.new() as move_line: move_line.product_id = self.product move_line.product_uom_qty = 10 delivery = picking_form.save() delivery.action_confirm() self.product._compute_quantities() # Computes `outgoing_qty` to have the orderpoint. # Then, creates a rule and adds it into the route's rules. reception_route.rule_ids.action_archive() self.env['stock.rule'].create({ 'name': 'Looping Rule', 'route_id': reception_route.id, 'location_dest_id': warehouse.lot_stock_id.id, 'location_src_id': warehouse.lot_stock_id.id, 'action': 'pull_push', 'procure_method': 'make_to_order', 'picking_type_id': warehouse.int_type_id.id, }) # Tries to open the Replenishment view -> It should raise an UserError. with self.assertRaises(UserError): self.env['stock.warehouse.orderpoint'].action_open_orderpoints() def test_proc_rule(self): # Create a product route containing a stock rule that will # generate a move from Stock for every procurement created in Output product_route = self.env['stock.route'].create({ 'name': 'Stock -> output route', 'product_selectable': True, 'rule_ids': [(0, 0, { 'name': 'Stock -> output rule', 'action': 'pull', 'picking_type_id': self.ref('stock.picking_type_internal'), 'location_src_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_output'), 'location_dest_from_rule': True, })], }) # Set this route on `product.product_product_3` self.product.write({ 'route_ids': [(4, product_route.id)]}) # Create Delivery Order of 10 `product.product_product_3` from Output -> Customer product = self.product vals = { 'name': 'Delivery order for procurement', 'partner_id': self.partner.id, 'picking_type_id': self.ref('stock.picking_type_out'), 'location_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_customers'), 'move_ids': [(0, 0, { 'name': '/', 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 10.00, 'procure_method': 'make_to_order', 'location_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_customers'), })], 'state': 'draft', } pick_output = self.env['stock.picking'].create(vals) pick_output.move_ids._onchange_product_id() # Confirm delivery order. pick_output.action_confirm() # I run the scheduler. # Note: If purchase if already installed, the method _run_buy will be called due # to the purchase demo data. As we update the stock module to run this test, the # method won't be an attribute of stock.procurement at this moment. For that reason # we mute the logger when running the scheduler. with mute_logger('odoo.addons.stock.models.procurement'): self.env['procurement.group'].run_scheduler() # Check that a picking was created from stock to output. moves = self.env['stock.move'].search([ ('product_id', '=', self.product.id), ('location_id', '=', self.ref('stock.stock_location_stock')), ('location_dest_id', '=', self.ref('stock.stock_location_output')), ('move_dest_ids', 'in', [pick_output.move_ids[0].id]) ]) self.assertEqual(len(moves.ids), 1, "It should have created a picking from Stock to Output with the original picking as destination") def test_get_rule_respects_sequence_order(self): """Test that _get_rule selects the rule associated with the route of the lowest sequence.""" # Create a warehouse and a product warehouse = self.env['stock.warehouse'].search([], limit=1) product = self.env['product.product'].create({'name': 'Test Product', 'is_storable': True}) # Create routes with different sequences to simulate prioritization. route_low_priority = self.env['stock.route'].create({'name': 'Route 1', 'sequence': 10}) rule_low_priority = self.env['stock.rule'].create({ 'name': 'Rule for Route 1', 'route_id': route_low_priority.id, 'action': 'pull', 'location_src_id': warehouse.lot_stock_id.id, 'location_dest_id': warehouse.lot_stock_id.id, 'picking_type_id': warehouse.out_type_id.id, 'sequence': 20, }) # Create a second route with higher priority (lower sequence). route_high_priority = self.env['stock.route'].create({'name': 'Route 2', 'sequence': 5}) rule_high_priority = self.env['stock.rule'].create({ 'name': 'Rule for Route 2', 'route_id': route_high_priority.id, 'action': 'pull', 'location_src_id': warehouse.lot_stock_id.id, 'location_dest_id': warehouse.lot_stock_id.id, 'picking_type_id': warehouse.out_type_id.id, 'sequence': 20, }) # Assign both routes to the product. This order is set so that the method # will be forced to sort the routes by their sequence. product.write({'route_ids': [(4, route_low_priority.id), (4, route_high_priority.id)]}) # Create a procurement group for testing rule selection. procurement_group = self.env['procurement.group'].create({'name': 'Test Procurement Group'}) # Call the _get_rule method to simulate rule selection. rule = procurement_group._get_rule( product_id=product, location_id=warehouse.lot_stock_id, values={ 'warehouse_id': warehouse, 'route_ids': product.route_ids, } ) # Assert that the selected rule corresponds to the route with the lowest sequence. self.assertEqual(rule, rule_high_priority, "The rule associated with the route having the lowest sequence " "(high_priority) should be selected.") def test_propagate_deadline_move(self): deadline = datetime.now() move_dest = self.env['stock.move'].create({ 'name': 'move_dest', 'product_id': self.product.id, 'product_uom': self.uom_unit.id, 'date_deadline': deadline, 'location_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_customers'), }) move_orig = self.env['stock.move'].create({ 'name': 'move_orig', 'product_id': self.product.id, 'product_uom': self.uom_unit.id, 'date_deadline': deadline, 'move_dest_ids': [(4, move_dest.id)], 'location_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_output'), 'quantity': 10, 'picked': True }) new_deadline = move_orig.date_deadline - timedelta(days=6) move_orig.date_deadline = new_deadline self.assertEqual(move_dest.date_deadline, new_deadline, msg='deadline date should be propagated') move_orig._action_done() self.assertAlmostEqual(move_orig.date, datetime.now(), delta=timedelta(seconds=10), msg='date should be now') self.assertEqual(move_orig.date_deadline, new_deadline, msg='deadline date should be unchanged') self.assertEqual(move_dest.date_deadline, new_deadline, msg='deadline date should be unchanged') def test_reordering_rule_1(self): # Required for `location_id` to be visible in the view self.product.is_storable = True self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations') warehouse = self.env['stock.warehouse'].search([], limit=1) orderpoint_form = Form(self.env['stock.warehouse.orderpoint']) orderpoint_form.product_id = self.product orderpoint_form.product_min_qty = 0.0 orderpoint_form.product_max_qty = 5.0 orderpoint = orderpoint_form.save() # get auto-created pull rule from when warehouse is created rule = self.env['stock.rule'].search([ ('route_id', '=', warehouse.reception_route_id.id), ('location_dest_id', '=', warehouse.lot_stock_id.id), ('location_src_id', '=', self.env.ref('stock.stock_location_suppliers').id), ('action', '=', 'pull'), ('procure_method', '=', 'make_to_stock'), ('picking_type_id', '=', warehouse.in_type_id.id)]) # add a delay [i.e. lead days] so procurement will be triggered based on forecasted stock rule.delay = 9.0 delivery_move = self.env['stock.move'].create({ 'name': 'Delivery', 'date': datetime.today() + timedelta(days=5), 'product_id': self.product.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 12.0, 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), }) delivery_move._action_confirm() orderpoint._compute_qty() self.env['procurement.group'].run_scheduler() receipt_move = self.env['stock.move'].search([ ('product_id', '=', self.product.id), ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id) ]) self.assertTrue(receipt_move) self.assertEqual(receipt_move.date.date(), date.today()) self.assertEqual(receipt_move.product_uom_qty, 17.0) def test_reordering_rule_2(self): """Test when there is not enough product to assign a picking => automatically run reordering rule (RR). Add extra product to already confirmed picking => automatically run another RR """ # Required for `location_id` to be visible in the view self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations') self.productA = self.env['product.product'].create({ 'name': 'Desk Combination', 'is_storable': True, }) self.productB = self.env['product.product'].create({ 'name': 'Desk Decoration', 'is_storable': True, }) warehouse = self.env['stock.warehouse'].search([], limit=1) orderpoint_form = Form(self.env['stock.warehouse.orderpoint']) orderpoint_form.product_id = self.productA orderpoint_form.product_min_qty = 0.0 orderpoint_form.product_max_qty = 5.0 orderpoint = orderpoint_form.save() self.env['stock.warehouse.orderpoint'].create({ 'name': 'ProductB RR', 'product_id': self.productB.id, 'product_min_qty': 0, 'product_max_qty': 5, }) self.env['stock.rule'].create({ 'name': 'Rule Supplier', 'route_id': warehouse.reception_route_id.id, 'location_dest_id': warehouse.lot_stock_id.id, 'location_src_id': self.env.ref('stock.stock_location_suppliers').id, 'action': 'pull', 'delay': 9.0, 'procure_method': 'make_to_stock', 'picking_type_id': warehouse.in_type_id.id, }) delivery_picking = self.env['stock.picking'].create({ 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'picking_type_id': self.ref('stock.picking_type_out'), }) delivery_move = self.env['stock.move'].create({ 'name': 'Delivery', 'product_id': self.productA.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 12.0, 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'picking_id': delivery_picking.id, }) delivery_picking.action_confirm() delivery_picking.action_assign() receipt_move = self.env['stock.move'].search([ ('product_id', '=', self.productA.id), ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id) ]) self.assertTrue(receipt_move) self.assertEqual(receipt_move.date.date(), date.today()) self.assertEqual(receipt_move.product_uom_qty, 17.0) delivery_picking.write({'move_ids': [(0, 0, { 'name': 'Extra Move', 'product_id': self.productB.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 5.0, 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'picking_id': delivery_picking.id, 'additional': True })]}) receipt_move2 = self.env['stock.move'].search([ ('product_id', '=', self.productB.id), ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id) ]) self.assertTrue(receipt_move2) self.assertEqual(receipt_move2.date.date(), date.today()) self.assertEqual(receipt_move2.product_uom_qty, 10.0) def test_reordering_rule_3(self): """Test how qty_multiple affects qty_to_order""" stock_location = self.stock_location = self.env.ref('stock.stock_location_stock') self.productA = self.env['product.product'].create({ 'name': 'Desk Combination', 'is_storable': True, }) self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.productA.id, 'location_id': stock_location.id, 'inventory_quantity': 14.5, }).action_apply_inventory() orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'name': 'ProductA RR', 'product_id': self.productA.id, 'product_min_qty': 15.0, 'product_max_qty': 30.0, 'qty_multiple': 10, }) self.assertEqual(orderpoint.qty_to_order, 10.0) # 15.0 < 14.5 + 10 <= 30.0 # Test search on computed field rr = self.env['stock.warehouse.orderpoint'].search([ ('qty_to_order', '>', 0), ('product_id', '=', self.productA.id), ]) self.assertTrue(rr) orderpoint.write({ 'qty_multiple': 1, }) self.assertEqual(orderpoint.qty_to_order, 15.0) # 15.0 < 14.5 + 15 <= 30.0 orderpoint.write({ 'qty_multiple': 0, }) self.assertEqual(orderpoint.qty_to_order, 15.5) # 15.0 < 14.5 + 15.5 <= 30.0 def test_orderpoint_replenishment_view_1(self): """ Create two warehouses + two moves verify that the replenishment view is consistent""" warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) warehouse_2, warehouse_3 = self.env['stock.warehouse'].create([{ 'name': 'Warehouse Two', 'code': 'WH2', 'resupply_wh_ids': [warehouse_1.id], }, { 'name': 'Warehouse Three', 'code': 'WH3', 'resupply_wh_ids': [warehouse_1.id], }]) route_2 = self.env['stock.route'].search([ ('supplied_wh_id', '=', warehouse_2.id), ('supplier_wh_id', '=', warehouse_1.id), ]) route_3 = self.env['stock.route'].search([ ('supplied_wh_id', '=', warehouse_3.id), ('supplier_wh_id', '=', warehouse_1.id), ]) product = self.env['product.product'].create({ 'name': 'Super Product', 'is_storable': True, 'route_ids': [route_2.id, route_3.id] }) moves = self.env['stock.move'].create([{ 'name': 'Move WH2', 'location_id': warehouse_2.lot_stock_id.id, 'location_dest_id': self.partner.property_stock_customer.id, 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 1, }, { 'name': 'Move WH3', 'location_id': warehouse_3.lot_stock_id.id, 'location_dest_id': self.partner.property_stock_customer.id, 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 1, }]) moves._action_confirm() # activate action of opening the replenishment view self.env.flush_all() self.env['stock.warehouse.orderpoint'].action_open_orderpoints() replenishments = self.env['stock.warehouse.orderpoint'].search([ ('product_id', '=', product.id), ]) # Verify that the location and the route make sense self.assertRecordValues(replenishments, [ {'location_id': warehouse_2.lot_stock_id.id, 'route_id': route_2.id}, {'location_id': warehouse_3.lot_stock_id.id, 'route_id': route_3.id}, ]) def test_orderpoint_replenishment_view_2(self): """ Create a warehouse + location to replenish warehouse instead of main location verify that the orderpoints created are for the replenish locations not the warehouse main location""" warehouse_1 = self.env['stock.warehouse'].create({ 'name': 'Warehouse 1', 'code': 'WH1', }) warehouse_1.lot_stock_id.replenish_location = False replenish_loc = self.env['stock.location'].create({ 'name': 'Replenish Location', 'location_id': warehouse_1.lot_stock_id.id, 'replenish_location': True, }) product = self.env['product.product'].create({ 'name': 'Rep Product', 'is_storable': True, }) move = self.env['stock.move'].create({ 'name': 'Move WH2', 'location_id': replenish_loc.id, 'location_dest_id': self.partner.property_stock_customer.id, 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 3, }) move._action_confirm() # activate action of opening the replenishment view self.env.flush_all() self.env['stock.warehouse.orderpoint'].action_open_orderpoints() replenishments = self.env['stock.warehouse.orderpoint'].search([ ('product_id', '=', product.id), ]) # Verify the location and the qty self.assertRecordValues(replenishments, [ {'location_id': replenish_loc.id, 'qty_to_order': 3}, ]) def test_orderpoint_replenishment_view_3(self): """ Create a selectable on product route and a product without routes. Verify that the orderpoint created to replenish that product did not set the new route by default. """ stock_location = self.env.ref('stock.stock_location_stock') interdimensional_protal = self.env['stock.location'].create({ 'name': 'Interdimensional portal', 'usage': 'internal', 'location_id': stock_location.location_id.id, }) lovely_route = self.env['stock.route'].create({ 'name': 'Lovely Route', 'product_selectable': True, 'product_categ_selectable': True, 'sequence': 1, 'rule_ids': [Command.create({ 'name': 'Interdimensional portal -> Stock', 'action': 'pull', 'picking_type_id': self.ref('stock.picking_type_internal'), 'location_src_id': interdimensional_protal.id, 'location_dest_id': stock_location.id, })], }) lovely_category = self.env['product.category'].create({ 'name': 'Lovely Category', 'route_ids': [Command.set(lovely_route.ids)] }) products = self.env['product.product'].create([ { 'name': 'Lovely product', 'is_storable': True, 'route_ids': [Command.set([])], }, { 'name': 'Lovely product with route', 'is_storable': True, 'route_ids': [Command.set(lovely_route.ids)], }, { 'name': 'Lovely product with categ route', 'is_storable': True, 'route_ids': [Command.set([])], 'categ_id': lovely_category.id, }, ]) moves = self.env['stock.move'].create([ { 'name': 'Create a demand move', 'location_id': stock_location.id, 'location_dest_id': self.partner.property_stock_customer.id, 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 1, } for product in products ]) moves._action_confirm() # activate action of opening the replenishment view self.env.flush_all() self.env['stock.warehouse.orderpoint'].action_open_orderpoints() replenishments = self.env['stock.warehouse.orderpoint'].search([ ('product_id', 'in', products.ids), ]) # Verify that the route is unset self.assertRecordValues(replenishments.sorted(lambda r: r.product_id.id), [ {'product_id': products[0].id, 'location_id': stock_location.id, 'route_id': False}, {'product_id': products[1].id, 'location_id': stock_location.id, 'route_id': lovely_route.id}, {'product_id': products[2].id, 'location_id': stock_location.id, 'route_id': lovely_route.id}, ]) def test_orderpoint_compute_warehouse_location(self): warehouse_a = self.env['stock.warehouse'].search([], limit=1) warehouse_b = self.env['stock.warehouse'].create({ 'name': 'Test Warehouse', 'code': 'TWH' }) # No warehouse specified, no location specified # Must choose default/first warehouse and the `lot_stock_id` of that warehouse orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'product_id': self.product.id, }) self.assertEqual(orderpoint.warehouse_id, warehouse_a) self.assertEqual(orderpoint.location_id, warehouse_a.lot_stock_id) orderpoint.unlink() # Warehouse specified, must choose the `lot_stock_id` of that warehouse by default orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'product_id': self.product.id, 'warehouse_id': warehouse_b.id, }) self.assertEqual(orderpoint.warehouse_id, warehouse_b) self.assertEqual(orderpoint.location_id, warehouse_b.lot_stock_id) orderpoint.unlink() # Location specified, must choose the warehouse of that location by default orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'product_id': self.product.id, 'location_id': warehouse_b.lot_stock_id.id, }) self.assertEqual(orderpoint.warehouse_id, warehouse_b) self.assertEqual(orderpoint.location_id, warehouse_b.lot_stock_id) orderpoint.unlink() # Warehouse specified, location specified, must let them and not overwrite them with a default location = warehouse_b.lot_stock_id.copy() orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'product_id': self.product.id, 'warehouse_id': warehouse_b.id, 'location_id': location.id, }) self.assertEqual(orderpoint.warehouse_id, warehouse_b) self.assertEqual(orderpoint.location_id, location) orderpoint.unlink() def test_replenishment_order_to_max(self): warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1) self.product.is_storable = True self.env['stock.quant']._update_available_quantity(self.product, warehouse.lot_stock_id, 10) orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'name': 'ProductB RR', 'product_id': self.product.id, 'product_min_qty': 5, 'product_max_qty': 200, }) self.assertEqual(orderpoint.qty_forecast, 10.0) # above minimum qty => nothing to order orderpoint.action_replenish() self.assertEqual(orderpoint.qty_forecast, 10.0) orderpoint.action_replenish(force_to_max=True) self.assertEqual(orderpoint.qty_forecast, 200.0) def test_orderpoint_location_archive(self): warehouse = self.env['stock.warehouse'].create({ 'name': 'Test Warehouse', 'code': 'TWH' }) stock_loc = warehouse.lot_stock_id shelf1 = self.env['stock.location'].create({ 'location_id': stock_loc.id, 'usage': 'internal', 'name': 'shelf1' }) product = self.env['product.product'].create({'name': 'Test Product', 'is_storable': True}) stock_move = self.env['stock.move'].create({ 'name': 'Test Move', 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': 1, 'location_id': shelf1.id, 'location_dest_id': self.env.ref('stock.stock_location_customers').id, }) stock_move._action_confirm() shelf1.active = False # opening the replenishment should not raise a KeyError even if the location is archived self.env['stock.warehouse.orderpoint'].action_open_orderpoints() def test_compute_qty_to_order(self): """ Check that the quantity to order is updated in the orderpoint when a new demand is created. """ orderpoint = self.env['stock.warehouse.orderpoint'].create({ 'name': 'auto orderpoint', 'product_id': self.product.id, 'product_min_qty': 5, 'product_max_qty': 5, 'qty_to_order': 5, 'trigger': 'auto', }) self.assertEqual(orderpoint.qty_to_order, 5) stock_move = self.env['stock.move'].create({ 'name': 'Test Move', 'product_id': self.product.id, 'product_uom': self.product.uom_id.id, 'product_uom_qty': 1, 'location_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_customers'), }) stock_move._action_confirm() self.assertEqual(orderpoint.qty_to_order, 6) def test_rule_help_message_mto_mtso(self): """Verify that the rule's help message correctly displays all relevant information when the procurement method is MTO or MTSO. """ mto_rule = self.env.ref('stock.route_warehouse0_mto').rule_ids[0] source_mto = mto_rule.location_src_id.display_name self.assertIn( f'
A need is created in {source_mto} and a rule will be triggered to fulfill it.', mto_rule.rule_message, 'The help message should correctly display information for MTO.' ) # Switch to MTSO mto_rule.procure_method = 'mts_else_mto' source_mtso = mto_rule.location_src_id.display_name self.assertIn( f'
If the products are not available in {source_mtso}, a rule will be triggered to bring the missing quantity in this location.', mto_rule.rule_message, 'The help message should correctly display information for MTSO.' ) def test_replenishment_creation(self): """Test that the default replenishment order values are computed correctly in the tree view.""" orderpoint_list_view = Form(self.env['stock.warehouse.orderpoint'], view='stock.view_warehouse_orderpoint_tree_editable') self.assertEqual(orderpoint_list_view.qty_to_order, 0) self.assertFalse(orderpoint_list_view.product_id) class TestProcRuleLoad(TransactionCase): def setUp(cls): super(TestProcRuleLoad, cls).setUp() cls.skipTest("Performance test, too heavy to run.") def test_orderpoint_1(self): """ Try 500 products with a 1000 RR(stock -> shelf1 and stock -> shelf2) Also randomly include 4 miss configuration. """ warehouse = self.env['stock.warehouse'].create({ 'name': 'Test Warehouse', 'code': 'TWH' }) warehouse.reception_steps = 'three_steps' supplier_loc = self.env.ref('stock.stock_location_suppliers') stock_loc = warehouse.lot_stock_id shelf1 = self.env['stock.location'].create({ 'location_id': stock_loc.id, 'usage': 'internal', 'name': 'shelf1' }) shelf2 = self.env['stock.location'].create({ 'location_id': stock_loc.id, 'usage': 'internal', 'name': 'shelf2' }) products = self.env['product.product'].create([{'name': i, 'is_storable': True} for i in range(500)]) self.env['stock.warehouse.orderpoint'].create([{ 'product_id': products[i // 2].id, 'location_id': (i % 2 == 0) and shelf1.id or shelf2.id, 'warehouse_id': warehouse.id, 'product_min_qty': 5, 'product_max_qty': 10, } for i in range(1000)]) self.env['stock.rule'].create({ 'name': 'Rule Shelf1', 'route_id': warehouse.reception_route_id.id, 'location_dest_id': shelf1.id, 'location_src_id': stock_loc.id, 'action': 'pull', 'procure_method': 'make_to_order', 'picking_type_id': warehouse.int_type_id.id, }) self.env['stock.rule'].create({ 'name': 'Rule Shelf2', 'route_id': warehouse.reception_route_id.id, 'location_dest_id': shelf2.id, 'location_src_id': stock_loc.id, 'action': 'pull', 'procure_method': 'make_to_order', 'picking_type_id': warehouse.int_type_id.id, }) self.env['stock.rule'].create({ 'name': 'Rule Supplier', 'route_id': warehouse.reception_route_id.id, 'location_dest_id': warehouse.wh_input_stock_loc_id.id, 'location_src_id': supplier_loc.id, 'action': 'pull', 'procure_method': 'make_to_stock', 'picking_type_id': warehouse.in_type_id.id, }) wrong_route = self.env['stock.route'].create({ 'name': 'Wrong Route', }) self.env['stock.rule'].create({ 'name': 'Trap Rule', 'route_id': wrong_route.id, 'location_dest_id': warehouse.wh_input_stock_loc_id.id, 'location_src_id': supplier_loc.id, 'action': 'pull', 'procure_method': 'make_to_order', 'picking_type_id': warehouse.in_type_id.id, }) (products[50] | products[99] | products[150] | products[199]).write({ 'route_ids': [(4, wrong_route.id)] }) self.env['procurement.group'].run_scheduler() self.assertTrue(self.env['stock.move'].search([('product_id', 'in', products.ids)])) for index in [50, 99, 150, 199]: self.assertTrue(self.env['mail.activity'].search([ ('res_id', '=', products[index].product_tmpl_id.id), ('res_model_id', '=', self.env.ref('product.model_product_template').id) ]))