test_proc_rule.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from datetime import date, datetime, timedelta
  4. from odoo.tests.common import Form, TransactionCase
  5. from odoo.tools import mute_logger
  6. from odoo.exceptions import UserError
  7. class TestProcRule(TransactionCase):
  8. @classmethod
  9. def setUpClass(cls):
  10. super().setUpClass()
  11. cls.uom_unit = cls.env.ref('uom.product_uom_unit')
  12. cls.product = cls.env['product.product'].create({
  13. 'name': 'Desk Combination',
  14. 'type': 'consu',
  15. })
  16. cls.partner = cls.env['res.partner'].create({'name': 'Partner'})
  17. def test_endless_loop_rules_from_location(self):
  18. """ Creates and configure a rule the way, when trying to get rules from
  19. location, it goes in a state where the found rule tries to trigger another
  20. rule but finds nothing else than itself and so get stuck in a recursion error."""
  21. warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
  22. reception_route = warehouse.reception_route_id
  23. self.product.type = 'product'
  24. # Creates a delivery for this product, that way, this product will be to resupply.
  25. picking_form = Form(self.env['stock.picking'])
  26. picking_form.picking_type_id = warehouse.out_type_id
  27. with picking_form.move_ids_without_package.new() as move_line:
  28. move_line.product_id = self.product
  29. move_line.product_uom_qty = 10
  30. delivery = picking_form.save()
  31. delivery.action_confirm()
  32. self.product._compute_quantities() # Computes `outgoing_qty` to have the orderpoint.
  33. # Then, creates a rule and adds it into the route's rules.
  34. reception_route.rule_ids.action_archive()
  35. self.env['stock.rule'].create({
  36. 'name': 'Looping Rule',
  37. 'route_id': reception_route.id,
  38. 'location_dest_id': warehouse.lot_stock_id.id,
  39. 'location_src_id': warehouse.lot_stock_id.id,
  40. 'action': 'pull_push',
  41. 'procure_method': 'make_to_order',
  42. 'picking_type_id': warehouse.int_type_id.id,
  43. })
  44. # Tries to open the Replenishment view -> It should raise an UserError.
  45. with self.assertRaises(UserError):
  46. self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
  47. def test_proc_rule(self):
  48. # Create a product route containing a stock rule that will
  49. # generate a move from Stock for every procurement created in Output
  50. product_route = self.env['stock.route'].create({
  51. 'name': 'Stock -> output route',
  52. 'product_selectable': True,
  53. 'rule_ids': [(0, 0, {
  54. 'name': 'Stock -> output rule',
  55. 'action': 'pull',
  56. 'picking_type_id': self.ref('stock.picking_type_internal'),
  57. 'location_src_id': self.ref('stock.stock_location_stock'),
  58. 'location_dest_id': self.ref('stock.stock_location_output'),
  59. })],
  60. })
  61. # Set this route on `product.product_product_3`
  62. self.product.write({
  63. 'route_ids': [(4, product_route.id)]})
  64. # Create Delivery Order of 10 `product.product_product_3` from Output -> Customer
  65. product = self.product
  66. vals = {
  67. 'name': 'Delivery order for procurement',
  68. 'partner_id': self.partner.id,
  69. 'picking_type_id': self.ref('stock.picking_type_out'),
  70. 'location_id': self.ref('stock.stock_location_output'),
  71. 'location_dest_id': self.ref('stock.stock_location_customers'),
  72. 'move_ids': [(0, 0, {
  73. 'name': '/',
  74. 'product_id': product.id,
  75. 'product_uom': product.uom_id.id,
  76. 'product_uom_qty': 10.00,
  77. 'procure_method': 'make_to_order',
  78. 'location_id': self.ref('stock.stock_location_output'),
  79. 'location_dest_id': self.ref('stock.stock_location_customers'),
  80. })],
  81. }
  82. pick_output = self.env['stock.picking'].create(vals)
  83. pick_output.move_ids._onchange_product_id()
  84. # Confirm delivery order.
  85. pick_output.action_confirm()
  86. # I run the scheduler.
  87. # Note: If purchase if already installed, the method _run_buy will be called due
  88. # to the purchase demo data. As we update the stock module to run this test, the
  89. # method won't be an attribute of stock.procurement at this moment. For that reason
  90. # we mute the logger when running the scheduler.
  91. with mute_logger('odoo.addons.stock.models.procurement'):
  92. self.env['procurement.group'].run_scheduler()
  93. # Check that a picking was created from stock to output.
  94. moves = self.env['stock.move'].search([
  95. ('product_id', '=', self.product.id),
  96. ('location_id', '=', self.ref('stock.stock_location_stock')),
  97. ('location_dest_id', '=', self.ref('stock.stock_location_output')),
  98. ('move_dest_ids', 'in', [pick_output.move_ids[0].id])
  99. ])
  100. self.assertEqual(len(moves.ids), 1, "It should have created a picking from Stock to Output with the original picking as destination")
  101. def test_propagate_deadline_move(self):
  102. deadline = datetime.now()
  103. move_dest = self.env['stock.move'].create({
  104. 'name': 'move_dest',
  105. 'product_id': self.product.id,
  106. 'product_uom': self.uom_unit.id,
  107. 'date_deadline': deadline,
  108. 'location_id': self.ref('stock.stock_location_output'),
  109. 'location_dest_id': self.ref('stock.stock_location_customers'),
  110. })
  111. move_orig = self.env['stock.move'].create({
  112. 'name': 'move_orig',
  113. 'product_id': self.product.id,
  114. 'product_uom': self.uom_unit.id,
  115. 'date_deadline': deadline,
  116. 'move_dest_ids': [(4, move_dest.id)],
  117. 'location_id': self.ref('stock.stock_location_stock'),
  118. 'location_dest_id': self.ref('stock.stock_location_output'),
  119. 'quantity_done': 10,
  120. })
  121. new_deadline = move_orig.date_deadline - timedelta(days=6)
  122. move_orig.date_deadline = new_deadline
  123. self.assertEqual(move_dest.date_deadline, new_deadline, msg='deadline date should be propagated')
  124. move_orig._action_done()
  125. self.assertAlmostEqual(move_orig.date, datetime.now(), delta=timedelta(seconds=10), msg='date should be now')
  126. self.assertEqual(move_orig.date_deadline, new_deadline, msg='deadline date should be unchanged')
  127. self.assertEqual(move_dest.date_deadline, new_deadline, msg='deadline date should be unchanged')
  128. def test_reordering_rule_1(self):
  129. # Required for `location_id` to be visible in the view
  130. self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations')
  131. warehouse = self.env['stock.warehouse'].search([], limit=1)
  132. orderpoint_form = Form(self.env['stock.warehouse.orderpoint'])
  133. orderpoint_form.product_id = self.product
  134. orderpoint_form.product_min_qty = 0.0
  135. orderpoint_form.product_max_qty = 5.0
  136. orderpoint = orderpoint_form.save()
  137. # get auto-created pull rule from when warehouse is created
  138. rule = self.env['stock.rule'].search([
  139. ('route_id', '=', warehouse.reception_route_id.id),
  140. ('location_dest_id', '=', warehouse.lot_stock_id.id),
  141. ('location_src_id', '=', self.env.ref('stock.stock_location_suppliers').id),
  142. ('action', '=', 'pull'),
  143. ('procure_method', '=', 'make_to_stock'),
  144. ('picking_type_id', '=', warehouse.in_type_id.id)])
  145. # add a delay [i.e. lead days] so procurement will be triggered based on forecasted stock
  146. rule.delay = 9.0
  147. delivery_move = self.env['stock.move'].create({
  148. 'name': 'Delivery',
  149. 'date': datetime.today() + timedelta(days=5),
  150. 'product_id': self.product.id,
  151. 'product_uom': self.uom_unit.id,
  152. 'product_uom_qty': 12.0,
  153. 'location_id': warehouse.lot_stock_id.id,
  154. 'location_dest_id': self.ref('stock.stock_location_customers'),
  155. })
  156. delivery_move._action_confirm()
  157. orderpoint._compute_qty()
  158. self.env['procurement.group'].run_scheduler()
  159. receipt_move = self.env['stock.move'].search([
  160. ('product_id', '=', self.product.id),
  161. ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id)
  162. ])
  163. self.assertTrue(receipt_move)
  164. self.assertEqual(receipt_move.date.date(), date.today())
  165. self.assertEqual(receipt_move.product_uom_qty, 17.0)
  166. def test_reordering_rule_2(self):
  167. """Test when there is not enough product to assign a picking => automatically run
  168. reordering rule (RR). Add extra product to already confirmed picking => automatically
  169. run another RR
  170. """
  171. # Required for `location_id` to be visible in the view
  172. self.env.user.groups_id += self.env.ref('stock.group_stock_multi_locations')
  173. self.productA = self.env['product.product'].create({
  174. 'name': 'Desk Combination',
  175. 'type': 'product',
  176. })
  177. self.productB = self.env['product.product'].create({
  178. 'name': 'Desk Decoration',
  179. 'type': 'product',
  180. })
  181. warehouse = self.env['stock.warehouse'].search([], limit=1)
  182. orderpoint_form = Form(self.env['stock.warehouse.orderpoint'])
  183. orderpoint_form.product_id = self.productA
  184. orderpoint_form.product_min_qty = 0.0
  185. orderpoint_form.product_max_qty = 5.0
  186. orderpoint = orderpoint_form.save()
  187. self.env['stock.warehouse.orderpoint'].create({
  188. 'name': 'ProductB RR',
  189. 'product_id': self.productB.id,
  190. 'product_min_qty': 0,
  191. 'product_max_qty': 5,
  192. })
  193. self.env['stock.rule'].create({
  194. 'name': 'Rule Supplier',
  195. 'route_id': warehouse.reception_route_id.id,
  196. 'location_dest_id': warehouse.lot_stock_id.id,
  197. 'location_src_id': self.env.ref('stock.stock_location_suppliers').id,
  198. 'action': 'pull',
  199. 'delay': 9.0,
  200. 'procure_method': 'make_to_stock',
  201. 'picking_type_id': warehouse.in_type_id.id,
  202. })
  203. delivery_picking = self.env['stock.picking'].create({
  204. 'location_id': warehouse.lot_stock_id.id,
  205. 'location_dest_id': self.ref('stock.stock_location_customers'),
  206. 'picking_type_id': self.ref('stock.picking_type_out'),
  207. })
  208. delivery_move = self.env['stock.move'].create({
  209. 'name': 'Delivery',
  210. 'product_id': self.productA.id,
  211. 'product_uom': self.uom_unit.id,
  212. 'product_uom_qty': 12.0,
  213. 'location_id': warehouse.lot_stock_id.id,
  214. 'location_dest_id': self.ref('stock.stock_location_customers'),
  215. 'picking_id': delivery_picking.id,
  216. })
  217. delivery_picking.action_confirm()
  218. delivery_picking.action_assign()
  219. receipt_move = self.env['stock.move'].search([
  220. ('product_id', '=', self.productA.id),
  221. ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id)
  222. ])
  223. self.assertTrue(receipt_move)
  224. self.assertEqual(receipt_move.date.date(), date.today())
  225. self.assertEqual(receipt_move.product_uom_qty, 17.0)
  226. delivery_picking.write({'move_ids': [(0, 0, {
  227. 'name': 'Extra Move',
  228. 'product_id': self.productB.id,
  229. 'product_uom': self.uom_unit.id,
  230. 'product_uom_qty': 5.0,
  231. 'location_id': warehouse.lot_stock_id.id,
  232. 'location_dest_id': self.ref('stock.stock_location_customers'),
  233. 'picking_id': delivery_picking.id,
  234. 'additional': True
  235. })]})
  236. receipt_move2 = self.env['stock.move'].search([
  237. ('product_id', '=', self.productB.id),
  238. ('location_id', '=', self.env.ref('stock.stock_location_suppliers').id)
  239. ])
  240. self.assertTrue(receipt_move2)
  241. self.assertEqual(receipt_move2.date.date(), date.today())
  242. self.assertEqual(receipt_move2.product_uom_qty, 10.0)
  243. def test_fixed_procurement_01(self):
  244. """ Run a procurement for 5 products when there are only 4 in stock then
  245. check that MTO is applied on the moves when the rule is set to 'mts_else_mto'
  246. """
  247. self.partner = self.env['res.partner'].create({'name': 'Partner'})
  248. warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1)
  249. warehouse.delivery_steps = 'pick_ship'
  250. final_location = self.partner.property_stock_customer
  251. # Create a product and add 10 units in stock
  252. product_a = self.env['product.product'].create({
  253. 'name': 'ProductA',
  254. 'type': 'product',
  255. })
  256. self.env['stock.quant']._update_available_quantity(product_a, warehouse.lot_stock_id, 10.0)
  257. # Create a route which will allows 'wave picking'
  258. wave_pg = self.env['procurement.group'].create({'name': 'Wave PG'})
  259. wave_route = self.env['stock.route'].create({
  260. 'name': 'Wave for ProductA',
  261. 'product_selectable': True,
  262. 'sequence': 1,
  263. 'rule_ids': [(0, 0, {
  264. 'name': 'Stock -> output rule',
  265. 'action': 'pull',
  266. 'picking_type_id': self.ref('stock.picking_type_internal'),
  267. 'location_src_id': self.ref('stock.stock_location_stock'),
  268. 'location_dest_id': self.ref('stock.stock_location_output'),
  269. 'group_propagation_option': 'fixed',
  270. 'group_id': wave_pg.id,
  271. })],
  272. })
  273. # Set this route on `product_a`
  274. product_a.write({
  275. 'route_ids': [(4, wave_route.id)]
  276. })
  277. # Create a procurement for 2 units
  278. pg = self.env['procurement.group'].create({'name': 'Wave 1'})
  279. self.env['procurement.group'].run([
  280. pg.Procurement(
  281. product_a,
  282. 2.0,
  283. product_a.uom_id,
  284. final_location,
  285. 'wave_part_1',
  286. 'wave_part_1',
  287. warehouse.company_id,
  288. {
  289. 'warehouse_id': warehouse,
  290. 'group_id': pg
  291. }
  292. )
  293. ])
  294. # 2 pickings should be created: 1 for pick, 1 for ship
  295. picking_pick = self.env['stock.picking'].search([('group_id', '=', wave_pg.id)])
  296. picking_ship = self.env['stock.picking'].search([('group_id', '=', pg.id)])
  297. self.assertAlmostEqual(picking_pick.move_ids.product_uom_qty, 2.0)
  298. self.assertAlmostEqual(picking_ship.move_ids.product_uom_qty, 2.0)
  299. # Create a procurement for 3 units
  300. pg = self.env['procurement.group'].create({'name': 'Wave 2'})
  301. self.env['procurement.group'].run([
  302. pg.Procurement(
  303. product_a,
  304. 3.0,
  305. product_a.uom_id,
  306. final_location,
  307. 'wave_part_2',
  308. 'wave_part_2',
  309. warehouse.company_id,
  310. {
  311. 'warehouse_id': warehouse,
  312. 'group_id': pg
  313. }
  314. )
  315. ])
  316. # The picking for the pick operation should be reused and the lines merged.
  317. picking_ship = self.env['stock.picking'].search([('group_id', '=', pg.id)])
  318. self.assertAlmostEqual(picking_pick.move_ids.product_uom_qty, 5.0)
  319. self.assertAlmostEqual(picking_ship.move_ids.product_uom_qty, 3.0)
  320. def test_orderpoint_replenishment_view_1(self):
  321. """ Create two warehouses + two moves
  322. verify that the replenishment view is consistent"""
  323. warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
  324. warehouse_2, warehouse_3 = self.env['stock.warehouse'].create([{
  325. 'name': 'Warehouse Two',
  326. 'code': 'WH2',
  327. 'resupply_wh_ids': [warehouse_1.id],
  328. }, {
  329. 'name': 'Warehouse Three',
  330. 'code': 'WH3',
  331. 'resupply_wh_ids': [warehouse_1.id],
  332. }])
  333. route_2 = self.env['stock.route'].search([
  334. ('supplied_wh_id', '=', warehouse_2.id),
  335. ('supplier_wh_id', '=', warehouse_1.id),
  336. ])
  337. route_3 = self.env['stock.route'].search([
  338. ('supplied_wh_id', '=', warehouse_3.id),
  339. ('supplier_wh_id', '=', warehouse_1.id),
  340. ])
  341. product = self.env['product.product'].create({
  342. 'name': 'Super Product',
  343. 'type': 'product',
  344. 'route_ids': [route_2.id, route_3.id]
  345. })
  346. moves = self.env['stock.move'].create([{
  347. 'name': 'Move WH2',
  348. 'location_id': warehouse_2.lot_stock_id.id,
  349. 'location_dest_id': self.partner.property_stock_customer.id,
  350. 'product_id': product.id,
  351. 'product_uom': product.uom_id.id,
  352. 'product_uom_qty': 1,
  353. }, {
  354. 'name': 'Move WH3',
  355. 'location_id': warehouse_3.lot_stock_id.id,
  356. 'location_dest_id': self.partner.property_stock_customer.id,
  357. 'product_id': product.id,
  358. 'product_uom': product.uom_id.id,
  359. 'product_uom_qty': 1,
  360. }])
  361. moves._action_confirm()
  362. # activate action of opening the replenishment view
  363. self.env.flush_all()
  364. self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
  365. replenishments = self.env['stock.warehouse.orderpoint'].search([
  366. ('product_id', '=', product.id),
  367. ])
  368. # Verify that the location and the route make sense
  369. self.assertRecordValues(replenishments, [
  370. {'location_id': warehouse_2.lot_stock_id.id, 'route_id': route_2.id},
  371. {'location_id': warehouse_3.lot_stock_id.id, 'route_id': route_3.id},
  372. ])
  373. def test_orderpoint_replenishment_view_2(self):
  374. """ Create a warehouse + location to replenish warehouse instead of main location
  375. verify that the orderpoints created are for the replenish locations not the warehouse main location"""
  376. warehouse_1 = self.env['stock.warehouse'].create({
  377. 'name': 'Warehouse 1',
  378. 'code': 'WH1',
  379. })
  380. warehouse_1.lot_stock_id.replenish_location = False
  381. replenish_loc = self.env['stock.location'].create({
  382. 'name': 'Replenish Location',
  383. 'location_id': warehouse_1.lot_stock_id.id,
  384. 'replenish_location': True,
  385. })
  386. product = self.env['product.product'].create({
  387. 'name': 'Rep Product',
  388. 'type': 'product',
  389. })
  390. move = self.env['stock.move'].create({
  391. 'name': 'Move WH2',
  392. 'location_id': replenish_loc.id,
  393. 'location_dest_id': self.partner.property_stock_customer.id,
  394. 'product_id': product.id,
  395. 'product_uom': product.uom_id.id,
  396. 'product_uom_qty': 3,
  397. })
  398. move._action_confirm()
  399. # activate action of opening the replenishment view
  400. self.env.flush_all()
  401. self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
  402. replenishments = self.env['stock.warehouse.orderpoint'].search([
  403. ('product_id', '=', product.id),
  404. ])
  405. # Verify the location and the qty
  406. self.assertRecordValues(replenishments, [
  407. {'location_id': replenish_loc.id, 'qty_to_order': 3},
  408. ])
  409. def test_orderpoint_compute_warehouse_location(self):
  410. warehouse_a = self.env['stock.warehouse'].search([], limit=1)
  411. warehouse_b = self.env['stock.warehouse'].create({
  412. 'name': 'Test Warehouse',
  413. 'code': 'TWH'
  414. })
  415. # No warehouse specified, no location specified
  416. # Must choose default/first warehouse and the `lot_stock_id` of that warehouse
  417. orderpoint = self.env['stock.warehouse.orderpoint'].create({
  418. 'product_id': self.product.id,
  419. })
  420. self.assertEqual(orderpoint.warehouse_id, warehouse_a)
  421. self.assertEqual(orderpoint.location_id, warehouse_a.lot_stock_id)
  422. orderpoint.unlink()
  423. # Warehouse specified, must choose the `lot_stock_id` of that warehouse by default
  424. orderpoint = self.env['stock.warehouse.orderpoint'].create({
  425. 'product_id': self.product.id,
  426. 'warehouse_id': warehouse_b.id,
  427. })
  428. self.assertEqual(orderpoint.warehouse_id, warehouse_b)
  429. self.assertEqual(orderpoint.location_id, warehouse_b.lot_stock_id)
  430. orderpoint.unlink()
  431. # Location specified, must choose the warehouse of that location by default
  432. orderpoint = self.env['stock.warehouse.orderpoint'].create({
  433. 'product_id': self.product.id,
  434. 'location_id': warehouse_b.lot_stock_id.id,
  435. })
  436. self.assertEqual(orderpoint.warehouse_id, warehouse_b)
  437. self.assertEqual(orderpoint.location_id, warehouse_b.lot_stock_id)
  438. orderpoint.unlink()
  439. # Warehouse specified, location specified, must let them and not overwrite them with a default
  440. location = warehouse_b.lot_stock_id.copy()
  441. orderpoint = self.env['stock.warehouse.orderpoint'].create({
  442. 'product_id': self.product.id,
  443. 'warehouse_id': warehouse_b.id,
  444. 'location_id': location.id,
  445. })
  446. self.assertEqual(orderpoint.warehouse_id, warehouse_b)
  447. self.assertEqual(orderpoint.location_id, location)
  448. orderpoint.unlink()
  449. class TestProcRuleLoad(TransactionCase):
  450. def setUp(cls):
  451. super(TestProcRuleLoad, cls).setUp()
  452. cls.skipTest("Performance test, too heavy to run.")
  453. def test_orderpoint_1(self):
  454. """ Try 500 products with a 1000 RR(stock -> shelf1 and stock -> shelf2)
  455. Also randomly include 4 miss configuration.
  456. """
  457. warehouse = self.env['stock.warehouse'].create({
  458. 'name': 'Test Warehouse',
  459. 'code': 'TWH'
  460. })
  461. warehouse.reception_steps = 'three_steps'
  462. supplier_loc = self.env.ref('stock.stock_location_suppliers')
  463. stock_loc = warehouse.lot_stock_id
  464. shelf1 = self.env['stock.location'].create({
  465. 'location_id': stock_loc.id,
  466. 'usage': 'internal',
  467. 'name': 'shelf1'
  468. })
  469. shelf2 = self.env['stock.location'].create({
  470. 'location_id': stock_loc.id,
  471. 'usage': 'internal',
  472. 'name': 'shelf2'
  473. })
  474. products = self.env['product.product'].create([{'name': i, 'type': 'product'} for i in range(500)])
  475. self.env['stock.warehouse.orderpoint'].create([{
  476. 'product_id': products[i // 2].id,
  477. 'location_id': (i % 2 == 0) and shelf1.id or shelf2.id,
  478. 'warehouse_id': warehouse.id,
  479. 'product_min_qty': 5,
  480. 'product_max_qty': 10,
  481. } for i in range(1000)])
  482. self.env['stock.rule'].create({
  483. 'name': 'Rule Shelf1',
  484. 'route_id': warehouse.reception_route_id.id,
  485. 'location_dest_id': shelf1.id,
  486. 'location_src_id': stock_loc.id,
  487. 'action': 'pull',
  488. 'procure_method': 'make_to_order',
  489. 'picking_type_id': warehouse.int_type_id.id,
  490. })
  491. self.env['stock.rule'].create({
  492. 'name': 'Rule Shelf2',
  493. 'route_id': warehouse.reception_route_id.id,
  494. 'location_dest_id': shelf2.id,
  495. 'location_src_id': stock_loc.id,
  496. 'action': 'pull',
  497. 'procure_method': 'make_to_order',
  498. 'picking_type_id': warehouse.int_type_id.id,
  499. })
  500. self.env['stock.rule'].create({
  501. 'name': 'Rule Supplier',
  502. 'route_id': warehouse.reception_route_id.id,
  503. 'location_dest_id': warehouse.wh_input_stock_loc_id.id,
  504. 'location_src_id': supplier_loc.id,
  505. 'action': 'pull',
  506. 'procure_method': 'make_to_stock',
  507. 'picking_type_id': warehouse.in_type_id.id,
  508. })
  509. wrong_route = self.env['stock.route'].create({
  510. 'name': 'Wrong Route',
  511. })
  512. self.env['stock.rule'].create({
  513. 'name': 'Trap Rule',
  514. 'route_id': wrong_route.id,
  515. 'location_dest_id': warehouse.wh_input_stock_loc_id.id,
  516. 'location_src_id': supplier_loc.id,
  517. 'action': 'pull',
  518. 'procure_method': 'make_to_order',
  519. 'picking_type_id': warehouse.in_type_id.id,
  520. })
  521. (products[50] | products[99] | products[150] | products[199]).write({
  522. 'route_ids': [(4, wrong_route.id)]
  523. })
  524. self.env['procurement.group'].run_scheduler()
  525. self.assertTrue(self.env['stock.move'].search([('product_id', 'in', products.ids)]))
  526. for index in [50, 99, 150, 199]:
  527. self.assertTrue(self.env['mail.activity'].search([
  528. ('res_id', '=', products[index].product_tmpl_id.id),
  529. ('res_model_id', '=', self.env.ref('product.model_product_template').id)
  530. ]))