test_robustness.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo.exceptions import UserError, ValidationError
  4. from odoo.tests.common import TransactionCase
  5. class TestRobustness(TransactionCase):
  6. @classmethod
  7. def setUpClass(cls):
  8. super(TestRobustness, cls).setUpClass()
  9. cls.stock_location = cls.env.ref('stock.stock_location_stock')
  10. cls.customer_location = cls.env.ref('stock.stock_location_customers')
  11. cls.uom_unit = cls.env.ref('uom.product_uom_unit')
  12. cls.uom_dozen = cls.env.ref('uom.product_uom_dozen')
  13. cls.product1 = cls.env['product.product'].create({
  14. 'name': 'Product A',
  15. 'type': 'product',
  16. 'categ_id': cls.env.ref('product.product_category_all').id,
  17. })
  18. def test_uom_factor(self):
  19. """ Changing the factor of a unit of measure shouldn't be allowed while
  20. quantities are reserved, else the existing move lines won't be consistent
  21. with the `reserved_quantity` on quants.
  22. """
  23. # make some stock
  24. self.env['stock.quant']._update_available_quantity(
  25. self.product1,
  26. self.stock_location,
  27. 12,
  28. )
  29. # reserve a dozen
  30. move1 = self.env['stock.move'].create({
  31. 'name': 'test_uom_rounding',
  32. 'location_id': self.stock_location.id,
  33. 'location_dest_id': self.customer_location.id,
  34. 'product_id': self.product1.id,
  35. 'product_uom': self.uom_dozen.id,
  36. 'product_uom_qty': 1,
  37. })
  38. move1._action_confirm()
  39. move1._action_assign()
  40. self.assertEqual(move1.state, 'assigned')
  41. quant = self.env['stock.quant']._gather(
  42. self.product1,
  43. self.stock_location,
  44. )
  45. # assert the reservation
  46. self.assertEqual(quant.reserved_quantity, 12)
  47. self.assertEqual(move1.product_qty, 12)
  48. # change the factor
  49. with self.assertRaises(UserError):
  50. with self.cr.savepoint():
  51. move1.product_uom.factor = 0.05
  52. # assert the reservation
  53. self.assertEqual(quant.reserved_quantity, 12)
  54. self.assertEqual(move1.state, 'assigned')
  55. self.assertEqual(move1.product_qty, 12)
  56. # unreserve
  57. move1._do_unreserve()
  58. def test_location_usage(self):
  59. """ Changing the usage of a location shouldn't be allowed while
  60. quantities are reserved, else the existing move lines won't be
  61. consistent with the `reserved_quantity` on the quants.
  62. """
  63. # change stock usage
  64. test_stock_location = self.env['stock.location'].create({
  65. 'name': "Test Location",
  66. 'location_id': self.stock_location.id,
  67. })
  68. test_stock_location.scrap_location = True
  69. # make some stock
  70. self.env['stock.quant']._update_available_quantity(
  71. self.product1,
  72. test_stock_location,
  73. 1,
  74. )
  75. # reserve a unit
  76. move1 = self.env['stock.move'].create({
  77. 'name': 'test_location_archive',
  78. 'location_id': test_stock_location.id,
  79. 'location_dest_id': self.customer_location.id,
  80. 'product_id': self.product1.id,
  81. 'product_uom': self.uom_unit.id,
  82. 'product_uom_qty': 1,
  83. })
  84. move1._action_confirm()
  85. move1._action_assign()
  86. self.assertEqual(move1.state, 'assigned')
  87. quant = self.env['stock.quant']._gather(
  88. self.product1,
  89. test_stock_location,
  90. )
  91. # assert the reservation
  92. self.assertEqual(quant.reserved_quantity, 0) # reservation is bypassed in scrap location
  93. self.assertEqual(move1.product_qty, 1)
  94. # change the stock usage
  95. with self.assertRaises(UserError):
  96. with self.cr.savepoint():
  97. test_stock_location.scrap_location = False
  98. # unreserve
  99. move1._do_unreserve()
  100. def test_package_unpack(self):
  101. """ Unpack a package that contains quants with a reservation
  102. should also remove the package on the reserved move lines.
  103. """
  104. package = self.env['stock.quant.package'].create({
  105. 'name': 'Shell Helix HX7 10W30',
  106. })
  107. self.env['stock.quant']._update_available_quantity(
  108. self.product1,
  109. self.stock_location,
  110. 10,
  111. package_id=package
  112. )
  113. # reserve a dozen
  114. move1 = self.env['stock.move'].create({
  115. 'name': 'test_uom_rounding',
  116. 'location_id': self.stock_location.id,
  117. 'location_dest_id': self.customer_location.id,
  118. 'product_id': self.product1.id,
  119. 'product_uom': self.uom_unit.id,
  120. 'product_uom_qty': 10,
  121. })
  122. move1._action_confirm()
  123. move1._action_assign()
  124. self.assertEqual(move1.move_line_ids.package_id, package)
  125. package.unpack()
  126. self.assertEqual(move1.move_line_ids.package_id, self.env['stock.quant.package'])
  127. # unreserve
  128. move1._do_unreserve()
  129. self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1)
  130. self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, package_id=package)), 0)
  131. self.assertEqual(self.env['stock.quant']._gather(self.product1, self.stock_location).reserved_quantity, 0)
  132. def test_lot_id_product_id_mix(self):
  133. """ Make sure it isn't possible to create a move line with a lot incompatible with its
  134. product.
  135. """
  136. product1 = self.env['product.product'].create({
  137. 'name': 'Product 1',
  138. 'type': 'product',
  139. 'categ_id': self.env.ref('product.product_category_all').id,
  140. 'tracking': 'lot',
  141. })
  142. product2 = self.env['product.product'].create({
  143. 'name': 'Product 2',
  144. 'type': 'product',
  145. 'categ_id': self.env.ref('product.product_category_all').id,
  146. 'tracking': 'lot',
  147. })
  148. lot1 = self.env['stock.lot'].create({
  149. 'name': 'lot1',
  150. 'product_id': product1.id,
  151. 'company_id': self.env.company.id,
  152. })
  153. lot2 = self.env['stock.lot'].create({
  154. 'name': 'lot2',
  155. 'product_id': product2.id,
  156. 'company_id': self.env.company.id,
  157. })
  158. self.env['stock.quant']._update_available_quantity(product1, self.stock_location, 1, lot_id=lot1)
  159. self.env['stock.quant']._update_available_quantity(product2, self.stock_location, 1, lot_id=lot2)
  160. move1 = self.env['stock.move'].create({
  161. 'name': 'test_lot_id_product_id_mix_move_1',
  162. 'location_id': self.stock_location.id,
  163. 'location_dest_id': self.customer_location.id,
  164. 'product_id': product1.id,
  165. 'product_uom': self.uom_unit.id,
  166. 'product_uom_qty': 1.0,
  167. })
  168. move2 = self.env['stock.move'].create({
  169. 'name': 'test_lot_id_product_id_mix_move_2',
  170. 'location_id': self.stock_location.id,
  171. 'location_dest_id': self.customer_location.id,
  172. 'product_id': product2.id,
  173. 'product_uom': self.uom_unit.id,
  174. 'product_uom_qty': 1.0,
  175. })
  176. (move1 + move2)._action_confirm()
  177. with self.assertRaises(ValidationError):
  178. move1.write({'move_line_ids': [(0, 0, {
  179. 'product_id': product1.id,
  180. 'product_uom_id': self.uom_unit.id,
  181. 'qty_done': 1,
  182. 'lot_id': lot2.id,
  183. 'location_id': move1.location_id.id,
  184. 'location_dest_id': move1.location_dest_id.id,
  185. })]})
  186. with self.assertRaises(ValidationError):
  187. move2.write({'move_line_ids': [(0, 0, {
  188. 'product_id': product2.id,
  189. 'product_uom_id': self.uom_unit.id,
  190. 'qty_done': 1,
  191. 'lot_id': lot1.id,
  192. 'location_id': move2.location_id.id,
  193. 'location_dest_id': move2.location_dest_id.id,
  194. })]})
  195. def test_lot_quantity_remains_unchanged_after_done(self):
  196. """ Make sure the method _set_lot_ids does not change the quantities of lots to 1 once they are done.
  197. """
  198. productA = self.env['product.product'].create({
  199. 'name': 'ProductA',
  200. 'type': 'product',
  201. 'categ_id': self.env.ref('product.product_category_all').id,
  202. 'tracking': 'lot',
  203. })
  204. lotA = self.env['stock.lot'].create({
  205. 'name': 'lotA',
  206. 'product_id': productA.id,
  207. 'company_id': self.env.company.id,
  208. })
  209. self.env['stock.quant']._update_available_quantity(productA, self.stock_location, 5, lot_id=lotA)
  210. moveA = self.env['stock.move'].create({
  211. 'name': 'TEST_A',
  212. 'location_id': self.stock_location.id,
  213. 'location_dest_id': self.customer_location.id,
  214. 'product_id': productA.id,
  215. 'product_uom': self.uom_unit.id,
  216. 'product_uom_qty': 5.0,
  217. })
  218. moveA._action_confirm()
  219. moveA.write({'move_line_ids': [(0, 0, {
  220. 'product_id': productA.id,
  221. 'product_uom_id': self.uom_unit.id,
  222. 'qty_done': 5,
  223. 'lot_id': lotA.id,
  224. 'location_id': moveA.location_id.id,
  225. 'location_dest_id': moveA.location_dest_id.id,
  226. })]})
  227. moveA._action_done()
  228. moveA._set_lot_ids()
  229. self.assertEqual(moveA.quantity_done, 5)