test_purchase.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo.addons.account.tests.common import AccountTestInvoicingCommon
  4. from odoo.tests import tagged, Form
  5. from odoo import Command, fields
  6. from datetime import timedelta
  7. @tagged('-at_install', 'post_install')
  8. class TestPurchase(AccountTestInvoicingCommon):
  9. def test_date_planned(self):
  10. """Set a date planned on 2 PO lines. Check that the PO date_planned is the earliest PO line date
  11. planned. Change one of the dates so it is even earlier and check that the date_planned is set to
  12. this earlier date.
  13. """
  14. po = Form(self.env['purchase.order'])
  15. po.partner_id = self.partner_a
  16. with po.order_line.new() as po_line:
  17. po_line.product_id = self.product_a
  18. po_line.product_qty = 1
  19. po_line.price_unit = 100
  20. with po.order_line.new() as po_line:
  21. po_line.product_id = self.product_b
  22. po_line.product_qty = 10
  23. po_line.price_unit = 200
  24. po = po.save()
  25. # Check that the same date is planned on both PO lines.
  26. self.assertNotEqual(po.order_line[0].date_planned, False)
  27. self.assertAlmostEqual(po.order_line[0].date_planned, po.order_line[1].date_planned, delta=timedelta(seconds=10))
  28. self.assertAlmostEqual(po.order_line[0].date_planned, po.date_planned, delta=timedelta(seconds=10))
  29. orig_date_planned = po.order_line[0].date_planned
  30. # Set an earlier date planned on a PO line and check that the PO expected date matches it.
  31. new_date_planned = orig_date_planned - timedelta(hours=1)
  32. po.order_line[0].date_planned = new_date_planned
  33. self.assertAlmostEqual(po.order_line[0].date_planned, po.date_planned, delta=timedelta(seconds=10))
  34. # Set an even earlier date planned on the other PO line and check that the PO expected date matches it.
  35. new_date_planned = orig_date_planned - timedelta(hours=72)
  36. po.order_line[1].date_planned = new_date_planned
  37. self.assertAlmostEqual(po.order_line[1].date_planned, po.date_planned, delta=timedelta(seconds=10))
  38. def test_purchase_order_sequence(self):
  39. PurchaseOrder = self.env['purchase.order'].with_context(tracking_disable=True)
  40. company = self.env.user.company_id
  41. self.env['ir.sequence'].search([
  42. ('code', '=', 'purchase.order'),
  43. ]).write({
  44. 'use_date_range': True, 'prefix': 'PO/%(range_year)s/',
  45. })
  46. vals = {
  47. 'partner_id': self.partner_a.id,
  48. 'company_id': company.id,
  49. 'currency_id': company.currency_id.id,
  50. 'date_order': '2019-01-01',
  51. }
  52. purchase_order = PurchaseOrder.create(vals.copy())
  53. self.assertTrue(purchase_order.name.startswith('PO/2019/'))
  54. vals['date_order'] = '2020-01-01'
  55. purchase_order = PurchaseOrder.create(vals.copy())
  56. self.assertTrue(purchase_order.name.startswith('PO/2020/'))
  57. # In EU/BXL tz, this is actually already 01/01/2020
  58. vals['date_order'] = '2019-12-31 23:30:00'
  59. purchase_order = PurchaseOrder.with_context(tz='Europe/Brussels').create(vals.copy())
  60. self.assertTrue(purchase_order.name.startswith('PO/2020/'))
  61. def test_reminder_1(self):
  62. """Set to send reminder today, check if a reminder can be send to the
  63. partner.
  64. """
  65. po = Form(self.env['purchase.order'])
  66. po.partner_id = self.partner_a
  67. with po.order_line.new() as po_line:
  68. po_line.product_id = self.product_a
  69. po_line.product_qty = 1
  70. po_line.price_unit = 100
  71. with po.order_line.new() as po_line:
  72. po_line.product_id = self.product_b
  73. po_line.product_qty = 10
  74. po_line.price_unit = 200
  75. # set to send reminder today
  76. po.date_planned = fields.Datetime.now() + timedelta(days=1)
  77. po.receipt_reminder_email = True
  78. po.reminder_date_before_receipt = 1
  79. po = po.save()
  80. po.button_confirm()
  81. # check vendor is a message recipient
  82. self.assertTrue(po.partner_id in po.message_partner_ids)
  83. old_messages = po.message_ids
  84. po._send_reminder_mail()
  85. messages_send = po.message_ids - old_messages
  86. # check reminder send
  87. self.assertTrue(messages_send)
  88. self.assertTrue(po.partner_id in messages_send.mapped('partner_ids'))
  89. # check confirm button
  90. po.confirm_reminder_mail()
  91. self.assertTrue(po.mail_reminder_confirmed)
  92. def test_reminder_2(self):
  93. """Set to send reminder tomorrow, check if no reminder can be send.
  94. """
  95. po = Form(self.env['purchase.order'])
  96. po.partner_id = self.partner_a
  97. with po.order_line.new() as po_line:
  98. po_line.product_id = self.product_a
  99. po_line.product_qty = 1
  100. po_line.price_unit = 100
  101. with po.order_line.new() as po_line:
  102. po_line.product_id = self.product_b
  103. po_line.product_qty = 10
  104. po_line.price_unit = 200
  105. # set to send reminder tomorrow
  106. po.date_planned = fields.Datetime.now() + timedelta(days=2)
  107. po.receipt_reminder_email = True
  108. po.reminder_date_before_receipt = 1
  109. po = po.save()
  110. po.button_confirm()
  111. # check vendor is a message recipient
  112. self.assertTrue(po.partner_id in po.message_partner_ids)
  113. old_messages = po.message_ids
  114. po._send_reminder_mail()
  115. messages_send = po.message_ids - old_messages
  116. # check no reminder send
  117. self.assertFalse(messages_send)
  118. def test_update_date_planned(self):
  119. po = Form(self.env['purchase.order'])
  120. po.partner_id = self.partner_a
  121. with po.order_line.new() as po_line:
  122. po_line.product_id = self.product_a
  123. po_line.product_qty = 1
  124. po_line.price_unit = 100
  125. po_line.date_planned = '2020-06-06 00:00:00'
  126. with po.order_line.new() as po_line:
  127. po_line.product_id = self.product_b
  128. po_line.product_qty = 10
  129. po_line.price_unit = 200
  130. po_line.date_planned = '2020-06-06 00:00:00'
  131. po = po.save()
  132. po.button_confirm()
  133. # update first line
  134. po._update_date_planned_for_lines([(po.order_line[0], fields.Datetime.today())])
  135. self.assertEqual(po.order_line[0].date_planned, fields.Datetime.today())
  136. activity = self.env['mail.activity'].search([
  137. ('summary', '=', 'Date Updated'),
  138. ('res_model_id', '=', 'purchase.order'),
  139. ('res_id', '=', po.id),
  140. ])
  141. self.assertTrue(activity)
  142. self.assertIn(
  143. '<p>partner_a modified receipt dates for the following products:</p>\n'
  144. '<p> - product_a from 2020-06-06 to %s</p>' % fields.Date.today(),
  145. activity.note,
  146. )
  147. # update second line
  148. po._update_date_planned_for_lines([(po.order_line[1], fields.Datetime.today())])
  149. self.assertEqual(po.order_line[1].date_planned, fields.Datetime.today())
  150. self.assertIn(
  151. '<p>partner_a modified receipt dates for the following products:</p>\n'
  152. '<p> - product_a from 2020-06-06 to %(today)s</p>\n'
  153. '<p> - product_b from 2020-06-06 to %(today)s</p>' % {'today': fields.Date.today()},
  154. activity.note,
  155. )
  156. def test_onchange_packaging_00(self):
  157. """Create a PO and use packaging. Check we suggested suitable packaging
  158. according to the product_qty. Also check product_qty or product_packaging
  159. are correctly calculated when one of them changed.
  160. """
  161. # Required for `product_packaging_qty` to be visible in the view
  162. self.env.user.groups_id += self.env.ref('product.group_stock_packaging')
  163. packaging_single = self.env['product.packaging'].create({
  164. 'name': "I'm a packaging",
  165. 'product_id': self.product_a.id,
  166. 'qty': 1.0,
  167. })
  168. packaging_dozen = self.env['product.packaging'].create({
  169. 'name': "I'm also a packaging",
  170. 'product_id': self.product_a.id,
  171. 'qty': 12.0,
  172. })
  173. po = self.env['purchase.order'].create({
  174. 'partner_id': self.partner_a.id,
  175. })
  176. po_form = Form(po)
  177. with po_form.order_line.new() as line:
  178. line.product_id = self.product_a
  179. line.product_qty = 1.0
  180. po_form.save()
  181. self.assertEqual(po.order_line.product_packaging_id, packaging_single)
  182. self.assertEqual(po.order_line.product_packaging_qty, 1.0)
  183. with po_form.order_line.edit(0) as line:
  184. line.product_packaging_qty = 2.0
  185. po_form.save()
  186. self.assertEqual(po.order_line.product_qty, 2.0)
  187. with po_form.order_line.edit(0) as line:
  188. line.product_qty = 24.0
  189. po_form.save()
  190. self.assertEqual(po.order_line.product_packaging_id, packaging_dozen)
  191. self.assertEqual(po.order_line.product_packaging_qty, 2.0)
  192. with po_form.order_line.edit(0) as line:
  193. line.product_packaging_qty = 1.0
  194. po_form.save()
  195. self.assertEqual(po.order_line.product_qty, 12)
  196. # Do the same test but without form, to check the `product_packaging_id` and `product_packaging_qty` are set
  197. # without manual call to onchanges
  198. po = self.env['purchase.order'].create({
  199. 'partner_id': self.partner_a.id,
  200. 'order_line': [
  201. Command.create({'product_id': self.product_a.id, 'product_qty': 1.0}),
  202. ]
  203. })
  204. self.assertEqual(po.order_line.product_packaging_id, packaging_single)
  205. self.assertEqual(po.order_line.product_packaging_qty, 1.0)
  206. po.order_line.product_packaging_qty = 2.0
  207. self.assertEqual(po.order_line.product_qty, 2.0)
  208. po.order_line.product_qty = 24.0
  209. self.assertEqual(po.order_line.product_packaging_id, packaging_dozen)
  210. self.assertEqual(po.order_line.product_packaging_qty, 2.0)
  211. po.order_line.product_packaging_qty = 1.0
  212. self.assertEqual(po.order_line.product_qty, 12)
  213. def test_with_different_uom(self):
  214. """ This test ensures that the unit price is correctly computed"""
  215. # Required for `product_uom` to be visibile in the view
  216. self.env.user.groups_id += self.env.ref('uom.group_uom')
  217. uom_units = self.env.ref('uom.product_uom_unit')
  218. uom_dozens = self.env.ref('uom.product_uom_dozen')
  219. uom_pairs = self.env['uom.uom'].create({
  220. 'name': 'Pairs',
  221. 'category_id': uom_units.category_id.id,
  222. 'uom_type': 'bigger',
  223. 'factor_inv': 2,
  224. 'rounding': 1,
  225. })
  226. product_data = {
  227. 'name': 'SuperProduct',
  228. 'type': 'consu',
  229. 'uom_id': uom_units.id,
  230. 'uom_po_id': uom_pairs.id,
  231. 'standard_price': 100
  232. }
  233. product_01 = self.env['product.product'].create(product_data)
  234. product_02 = self.env['product.product'].create(product_data)
  235. po_form = Form(self.env['purchase.order'])
  236. po_form.partner_id = self.partner_a
  237. with po_form.order_line.new() as po_line:
  238. po_line.product_id = product_01
  239. with po_form.order_line.new() as po_line:
  240. po_line.product_id = product_02
  241. po_line.product_uom = uom_dozens
  242. po = po_form.save()
  243. self.assertEqual(po.order_line[0].price_unit, 200)
  244. self.assertEqual(po.order_line[1].price_unit, 1200)
  245. def test_on_change_quantity_description(self):
  246. """
  247. When a user changes the quantity of a product in a purchase order it
  248. should not change the description if the descritpion was changed by
  249. the user before
  250. """
  251. self.env.user.write({'company_id': self.company_data['company'].id})
  252. po = Form(self.env['purchase.order'])
  253. po.partner_id = self.partner_a
  254. with po.order_line.new() as pol:
  255. pol.product_id = self.product_a
  256. pol.product_qty = 1
  257. pol.name = "New custom description"
  258. pol.product_qty += 1
  259. self.assertEqual(pol.name, "New custom description")
  260. def test_purchase_multicurrency(self):
  261. """
  262. Purchase order lines should keep unit price precision of products
  263. Also the products having prices in different currencies should be
  264. correctly handled when creating a purchase order i-e product having a price of 100 usd
  265. and when purchasing in EUR company the correct conversion should be applied
  266. """
  267. self.env['decimal.precision'].search([
  268. ('name', '=', 'Product Price'),
  269. ]).digits = 5
  270. product = self.env['product.product'].create({
  271. 'name': 'product_test',
  272. 'uom_id': self.env.ref('uom.product_uom_unit').id,
  273. 'lst_price': 10.0,
  274. 'standard_price': 0.12345,
  275. })
  276. currency = self.env['res.currency'].create({
  277. 'name': 'Dark Chocolate Coin',
  278. 'symbol': '🍫',
  279. 'rounding': 0.001,
  280. 'position': 'after',
  281. 'currency_unit_label': 'Dark Choco',
  282. 'currency_subunit_label': 'Dark Cacao Powder',
  283. })
  284. currency_rate = self.env['res.currency.rate'].create({
  285. 'name': '2016-01-01',
  286. 'rate': 2,
  287. 'currency_id': currency.id,
  288. 'company_id': self.env.company.id,
  289. })
  290. po_form = Form(self.env['purchase.order'])
  291. po_form.partner_id = self.partner_a
  292. with po_form.order_line.new() as po_line:
  293. po_line.product_id = product
  294. purchase_order_usd = po_form.save()
  295. self.assertEqual(purchase_order_usd.order_line.price_unit, product.standard_price, "Value shouldn't be rounded $")
  296. po_form = Form(self.env['purchase.order'])
  297. po_form.partner_id = self.partner_a
  298. po_form.currency_id = currency
  299. with po_form.order_line.new() as po_line:
  300. po_line.product_id = product
  301. purchase_order_coco = po_form.save()
  302. self.assertEqual(purchase_order_coco.order_line.price_unit, currency_rate.rate * product.standard_price, "Value shouldn't be rounded 🍫")
  303. #check if the correct currency is set on the purchase order by comparing the expected price and actual price
  304. company_a = self.company_data['company']
  305. company_b = self.company_data_2['company']
  306. company_b.currency_id = currency
  307. self.env['res.currency.rate'].create({
  308. 'name': '2023-01-01',
  309. 'rate': 2,
  310. 'currency_id': currency.id,
  311. 'company_id': company_b.id,
  312. })
  313. product_b = self.env['product.product'].with_company(company_a).create({
  314. 'name': 'product_2',
  315. 'uom_id': self.env.ref('uom.product_uom_unit').id,
  316. 'standard_price': 0.0,
  317. })
  318. self.assertEqual(product_b.cost_currency_id, company_a.currency_id, 'The cost currency should be the one set on'
  319. ' the company')
  320. product_b = product_b.with_company(company_b)
  321. self.assertEqual(product_b.cost_currency_id, currency, 'The cost currency should be the one set on the company,'
  322. ' as the product is now opened in another company')
  323. product_b.supplier_taxes_id = False
  324. product_b.update({'standard_price': 10.0})
  325. #create a purchase order with the product from company B
  326. order_b = self.env['purchase.order'].with_company(company_b).create({
  327. 'partner_id': self.partner_a.id,
  328. 'order_line': [(0, 0, {
  329. 'product_id': product_b.id,
  330. 'product_qty': 1,
  331. 'product_uom': self.env.ref('uom.product_uom_unit').id,
  332. })],
  333. })
  334. self.assertEqual(order_b.order_line.price_unit, 10.0, 'The price unit should be 10.0')
  335. def test_purchase_not_creating_useless_product_vendor(self):
  336. """ This test ensures that the product vendor is not created when the
  337. product is not set on the purchase order line.
  338. """
  339. #create a contact of type contact
  340. contact = self.env['res.partner'].create({
  341. 'name': 'Contact',
  342. 'type': 'contact',
  343. })
  344. #create a contact of type Delivery Address lnked to the contact
  345. delivery_address = self.env['res.partner'].create({
  346. 'name': 'Delivery Address',
  347. 'type': 'delivery',
  348. 'parent_id': contact.id,
  349. })
  350. #create a product that use the delivery address as vendor
  351. product = self.env['product.product'].create({
  352. 'name': 'Product A',
  353. 'seller_ids': [(0, 0, {
  354. 'partner_id': delivery_address.id,
  355. 'min_qty': 1.0,
  356. 'price': 1.0,
  357. })]
  358. })
  359. #create a purchase order with the delivery address as partner
  360. po_form = Form(self.env['purchase.order'])
  361. po_form.partner_id = delivery_address
  362. with po_form.order_line.new() as po_line:
  363. po_line.product_id = product
  364. po_line.product_qty = 1.0
  365. po = po_form.save()
  366. po.button_confirm()
  367. self.assertEqual(po.order_line.product_id.seller_ids.mapped('partner_id'), delivery_address)
  368. def test_supplier_list_in_product_with_multicompany(self):
  369. """
  370. Check that a different supplier list can be added to a product for each company.
  371. """
  372. company_a = self.company_data['company']
  373. company_b = self.company_data_2['company']
  374. product = self.env['product.product'].create({
  375. 'name': 'product_test',
  376. })
  377. # create a purchase order in the company A
  378. self.env['purchase.order'].with_company(company_a).create({
  379. 'partner_id': self.partner_a.id,
  380. 'order_line': [(0, 0, {
  381. 'product_id': product.id,
  382. 'product_qty': 1,
  383. 'product_uom': self.env.ref('uom.product_uom_unit').id,
  384. 'price_unit': 1,
  385. })],
  386. }).button_confirm()
  387. self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
  388. self.assertEqual(product.seller_ids[0].company_id, company_a)
  389. # switch to the company B
  390. self.env['purchase.order'].with_company(company_b).create({
  391. 'partner_id': self.partner_b.id,
  392. 'order_line': [(0, 0, {
  393. 'product_id': product.id,
  394. 'product_qty': 1,
  395. 'product_uom': self.env.ref('uom.product_uom_unit').id,
  396. 'price_unit': 2,
  397. })],
  398. }).button_confirm()
  399. product = product.with_company(company_b)
  400. self.assertEqual(product.seller_ids[0].partner_id, self.partner_b)
  401. self.assertEqual(product.seller_ids[0].company_id, company_b)
  402. # Switch to the company A and check that the vendor list is still the same
  403. product = product.with_company(company_a)
  404. self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
  405. self.assertEqual(product.seller_ids[0].company_id, company_a)
  406. product._invalidate_cache()
  407. self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
  408. self.assertEqual(product.seller_ids[0].company_id, company_a)