test_purchase_stock_report.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo.tests.common import Form
  4. from odoo.addons.mail.tests.common import mail_new_test_user
  5. from odoo.addons.stock.tests.test_report import TestReportsCommon
  6. class TestPurchaseStockReports(TestReportsCommon):
  7. def test_report_forecast_1_purchase_order_multi_receipt(self):
  8. """ Create a PO for 5 product, receive them then increase the quantity to 10.
  9. """
  10. po_form = Form(self.env['purchase.order'])
  11. po_form.partner_id = self.partner
  12. with po_form.order_line.new() as line:
  13. line.product_id = self.product
  14. line.product_qty = 5
  15. po = po_form.save()
  16. # Checks the report.
  17. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  18. draft_picking_qty_in = docs['draft_picking_qty']['in']
  19. draft_purchase_qty = docs['draft_purchase_qty']
  20. pending_qty_in = docs['qty']['in']
  21. self.assertEqual(len(lines), 0, "Must have 0 line for now.")
  22. self.assertEqual(draft_picking_qty_in, 0)
  23. self.assertEqual(draft_purchase_qty, 5)
  24. self.assertEqual(pending_qty_in, 5)
  25. # Confirms the PO and checks the report again.
  26. po.button_confirm()
  27. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  28. draft_picking_qty_in = docs['draft_picking_qty']['in']
  29. draft_purchase_qty = docs['draft_purchase_qty']
  30. pending_qty_in = docs['qty']['in']
  31. self.assertEqual(len(lines), 1)
  32. self.assertEqual(lines[0]['document_in'].id, po.id)
  33. self.assertEqual(lines[0]['quantity'], 5)
  34. self.assertEqual(lines[0]['document_out'], False)
  35. self.assertEqual(draft_picking_qty_in, 0)
  36. self.assertEqual(draft_purchase_qty, 0)
  37. self.assertEqual(pending_qty_in, 0)
  38. # Receives 5 products.
  39. receipt = po.picking_ids
  40. res_dict = receipt.button_validate()
  41. wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
  42. wizard.process()
  43. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  44. draft_picking_qty_in = docs['draft_picking_qty']['in']
  45. draft_purchase_qty = docs['draft_purchase_qty']
  46. pending_qty_in = docs['qty']['in']
  47. self.assertEqual(len(lines), 0)
  48. self.assertEqual(draft_picking_qty_in, 0)
  49. self.assertEqual(draft_purchase_qty, 0)
  50. self.assertEqual(pending_qty_in, 0)
  51. # Increase the PO quantity to 10, so must create a second receipt.
  52. po_form = Form(po)
  53. with po_form.order_line.edit(0) as line:
  54. line.product_qty = 10
  55. po = po_form.save()
  56. # Checks the report.
  57. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  58. draft_picking_qty_in = docs['draft_picking_qty']['in']
  59. draft_purchase_qty = docs['draft_purchase_qty']
  60. pending_qty_in = docs['qty']['in']
  61. self.assertEqual(len(lines), 1, "Must have 1 line for now.")
  62. self.assertEqual(lines[0]['document_in'].id, po.id)
  63. self.assertEqual(lines[0]['quantity'], 5)
  64. self.assertEqual(draft_picking_qty_in, 0)
  65. self.assertEqual(draft_purchase_qty, 0)
  66. self.assertEqual(pending_qty_in, 0)
  67. def test_report_forecast_2_purchase_order_three_step_receipt(self):
  68. """ Create a PO for 4 product, receive them then increase the quantity
  69. to 10, but use three steps receipt.
  70. """
  71. grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
  72. grp_multi_routes = self.env.ref('stock.group_adv_location')
  73. self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]})
  74. self.env.user.write({'groups_id': [(4, grp_multi_routes.id)]})
  75. # Configure warehouse.
  76. warehouse = self.env.ref('stock.warehouse0')
  77. warehouse.reception_steps = 'three_steps'
  78. po_form = Form(self.env['purchase.order'])
  79. po_form.partner_id = self.partner
  80. with po_form.order_line.new() as line:
  81. line.product_id = self.product
  82. line.product_qty = 4
  83. po = po_form.save()
  84. # Checks the report -> Must be empty for now, just display some pending qty.
  85. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  86. draft_picking_qty_in = docs['draft_picking_qty']['in']
  87. draft_purchase_qty = docs['draft_purchase_qty']
  88. pending_qty_in = docs['qty']['in']
  89. self.assertEqual(len(lines), 0, "Must have 0 line for now.")
  90. self.assertEqual(draft_picking_qty_in, 0)
  91. self.assertEqual(draft_purchase_qty, 4)
  92. self.assertEqual(pending_qty_in, 4)
  93. # Confirms the PO and checks the report again.
  94. po.button_confirm()
  95. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  96. draft_picking_qty_in = docs['draft_picking_qty']['in']
  97. draft_purchase_qty = docs['draft_purchase_qty']
  98. pending_qty_in = docs['qty']['in']
  99. self.assertEqual(len(lines), 1)
  100. self.assertEqual(lines[0]['document_in'].id, po.id)
  101. self.assertEqual(lines[0]['quantity'], 4)
  102. self.assertEqual(lines[0]['document_out'], False)
  103. self.assertEqual(draft_picking_qty_in, 0)
  104. self.assertEqual(draft_purchase_qty, 0)
  105. self.assertEqual(pending_qty_in, 0)
  106. # Get back the different transfers.
  107. receipt = po.picking_ids
  108. # Receives 4 products.
  109. res_dict = receipt.button_validate()
  110. wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
  111. wizard.process()
  112. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  113. draft_picking_qty_in = docs['draft_picking_qty']['in']
  114. draft_purchase_qty = docs['draft_purchase_qty']
  115. pending_qty_in = docs['qty']['in']
  116. self.assertEqual(len(lines), 0)
  117. self.assertEqual(draft_picking_qty_in, 0)
  118. self.assertEqual(draft_purchase_qty, 0)
  119. self.assertEqual(pending_qty_in, 0)
  120. # Increase the PO quantity to 10, so must create a second receipt.
  121. po_form = Form(po)
  122. with po_form.order_line.edit(0) as line:
  123. line.product_qty = 10
  124. po = po_form.save()
  125. # Checks the report.
  126. report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
  127. draft_picking_qty_in = docs['draft_picking_qty']['in']
  128. draft_purchase_qty = docs['draft_purchase_qty']
  129. pending_qty_in = docs['qty']['in']
  130. self.assertEqual(len(lines), 1)
  131. self.assertEqual(lines[0]['document_in'].id, po.id)
  132. self.assertEqual(lines[0]['quantity'], 6)
  133. self.assertEqual(draft_picking_qty_in, 0)
  134. self.assertEqual(draft_purchase_qty, 0)
  135. self.assertEqual(pending_qty_in, 0)
  136. def test_report_forecast_3_report_line_corresponding_to_po_line_highlighted(self):
  137. """ When accessing the report from a PO line, checks if the correct PO line is highlighted in the report
  138. """
  139. # We create 2 identical PO
  140. po_form = Form(self.env['purchase.order'])
  141. po_form.partner_id = self.partner
  142. with po_form.order_line.new() as line:
  143. line.product_id = self.product
  144. line.product_qty = 5
  145. po1 = po_form.save()
  146. po1.button_confirm()
  147. po2 = po1.copy()
  148. po2.button_confirm()
  149. # Check for both PO if the highlight (is_matched) corresponds to the correct PO
  150. for po in [po1, po2]:
  151. context = po.order_line[0].action_product_forecast_report()['context']
  152. _, _, lines = self.get_report_forecast(product_template_ids=self.product_template.ids, context=context)
  153. for line in lines:
  154. if line['document_in'] == po:
  155. self.assertTrue(line['is_matched'], "The corresponding PO line should be matched in the forecast report.")
  156. else:
  157. self.assertFalse(line['is_matched'], "A line of the forecast report not linked to the PO shoud not be matched.")
  158. def test_approval_and_forecasted_qty(self):
  159. """
  160. When a PO is waiting for an approval, its quantities should be included
  161. in the draft quantity count
  162. """
  163. self.env.company.po_double_validation = 'two_step'
  164. self.env.company.po_double_validation_amount = 0
  165. basic_purchase_user = mail_new_test_user(
  166. self.env,
  167. login='basic_purchase_user',
  168. groups='base.group_user,purchase.group_purchase_user',
  169. )
  170. po_form = Form(self.env['purchase.order'])
  171. po_form.partner_id = self.partner
  172. with po_form.order_line.new() as line:
  173. line.product_id = self.product
  174. line.product_qty = 50
  175. po_form.save()
  176. po_form = Form(self.env['purchase.order'])
  177. po_form.partner_id = self.partner
  178. with po_form.order_line.new() as line:
  179. line.product_id = self.product
  180. line.product_qty = 100
  181. po = po_form.save()
  182. po.with_user(basic_purchase_user).button_confirm()
  183. docs = self.get_report_forecast(product_template_ids=self.product_template.ids)[1]
  184. self.assertEqual(docs['draft_purchase_qty'], 150)
  185. def test_vendor_delay_report_with_uom(self):
  186. """
  187. PO 12 units x P
  188. Receive 1 dozen x P
  189. -> 100% received
  190. """
  191. uom_12 = self.env.ref('uom.product_uom_dozen')
  192. po_form = Form(self.env['purchase.order'])
  193. po_form.partner_id = self.partner
  194. with po_form.order_line.new() as line:
  195. line.product_id = self.product
  196. line.product_qty = 12
  197. po = po_form.save()
  198. po.button_confirm()
  199. receipt = po.picking_ids
  200. receipt_move = receipt.move_ids
  201. receipt_move.move_line_ids.unlink()
  202. receipt_move.move_line_ids = [(0, 0, {
  203. 'location_id': receipt_move.location_id.id,
  204. 'location_dest_id': receipt_move.location_dest_id.id,
  205. 'product_id': self.product.id,
  206. 'product_uom_id': uom_12.id,
  207. 'qty_done': 1,
  208. 'picking_id': receipt.id,
  209. })]
  210. receipt.button_validate()
  211. data = self.env['vendor.delay.report'].read_group(
  212. [('partner_id', '=', self.partner.id)],
  213. ['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
  214. ['product_id'],
  215. )[0]
  216. self.assertEqual(data['qty_on_time'], 12)
  217. self.assertEqual(data['qty_total'], 12)
  218. self.assertEqual(data['on_time_rate'], 100)
  219. def test_vendor_delay_report_with_multi_location(self):
  220. """
  221. PO 10 units x P
  222. Receive
  223. - 6 x P in Child Location 01
  224. - 4 x P in Child Location 02
  225. -> 100% received
  226. """
  227. child_loc_01, child_loc_02 = self.stock_location.child_ids
  228. po_form = Form(self.env['purchase.order'])
  229. po_form.partner_id = self.partner
  230. with po_form.order_line.new() as line:
  231. line.product_id = self.product
  232. line.product_qty = 10
  233. po = po_form.save()
  234. po.button_confirm()
  235. receipt = po.picking_ids
  236. receipt_move = receipt.move_ids
  237. receipt_move.move_line_ids.unlink()
  238. receipt_move.move_line_ids = [(0, 0, {
  239. 'location_id': receipt_move.location_id.id,
  240. 'location_dest_id': child_loc_01.id,
  241. 'product_id': self.product.id,
  242. 'product_uom_id': self.product.uom_id.id,
  243. 'qty_done': 6,
  244. 'picking_id': receipt.id,
  245. }), (0, 0, {
  246. 'location_id': receipt_move.location_id.id,
  247. 'location_dest_id': child_loc_02.id,
  248. 'product_id': self.product.id,
  249. 'product_uom_id': self.product.uom_id.id,
  250. 'qty_done': 4,
  251. 'picking_id': receipt.id,
  252. })]
  253. receipt.button_validate()
  254. data = self.env['vendor.delay.report'].read_group(
  255. [('partner_id', '=', self.partner.id)],
  256. ['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
  257. ['product_id'],
  258. )[0]
  259. self.assertEqual(data['qty_on_time'], 10)
  260. self.assertEqual(data['qty_total'], 10)
  261. self.assertEqual(data['on_time_rate'], 100)
  262. def test_vendor_delay_report_with_backorder(self):
  263. """
  264. PO 10 units x P
  265. Receive 6 x P with backorder
  266. -> 60% received
  267. Process the backorder
  268. -> 100% received
  269. """
  270. po_form = Form(self.env['purchase.order'])
  271. po_form.partner_id = self.partner
  272. with po_form.order_line.new() as line:
  273. line.product_id = self.product
  274. line.product_qty = 10
  275. po = po_form.save()
  276. po.button_confirm()
  277. receipt01 = po.picking_ids
  278. receipt01_move = receipt01.move_ids
  279. receipt01_move.quantity_done = 6
  280. action = receipt01.button_validate()
  281. Form(self.env[action['res_model']].with_context(action['context'])).save().process()
  282. data = self.env['vendor.delay.report'].read_group(
  283. [('partner_id', '=', self.partner.id)],
  284. ['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
  285. ['product_id'],
  286. )[0]
  287. self.assertEqual(data['qty_on_time'], 6)
  288. self.assertEqual(data['qty_total'], 10)
  289. self.assertEqual(data['on_time_rate'], 60)
  290. receipt02 = receipt01.backorder_ids
  291. receipt02.move_ids.quantity_done = 4
  292. receipt02.button_validate()
  293. (receipt01 | receipt02).move_ids.invalidate_recordset()
  294. data = self.env['vendor.delay.report'].read_group(
  295. [('partner_id', '=', self.partner.id)],
  296. ['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
  297. ['product_id'],
  298. )[0]
  299. self.assertEqual(data['qty_on_time'], 10)
  300. self.assertEqual(data['qty_total'], 10)
  301. self.assertEqual(data['on_time_rate'], 100)
  302. def test_vendor_delay_report_without_backorder(self):
  303. """
  304. PO 10 units x P
  305. Receive 6 x P without backorder
  306. -> 60% received
  307. """
  308. po_form = Form(self.env['purchase.order'])
  309. po_form.partner_id = self.partner
  310. with po_form.order_line.new() as line:
  311. line.product_id = self.product
  312. line.product_qty = 10
  313. po = po_form.save()
  314. po.button_confirm()
  315. receipt01 = po.picking_ids
  316. receipt01_move = receipt01.move_ids
  317. receipt01_move.quantity_done = 6
  318. action = receipt01.button_validate()
  319. Form(self.env[action['res_model']].with_context(action['context'])).save().process_cancel_backorder()
  320. data = self.env['vendor.delay.report'].read_group(
  321. [('partner_id', '=', self.partner.id)],
  322. ['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
  323. ['product_id'],
  324. )[0]
  325. self.assertEqual(data['qty_on_time'], 6)
  326. self.assertEqual(data['qty_total'], 10)
  327. self.assertEqual(data['on_time_rate'], 60)