test_account_move_in_refund.py 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. # -*- coding: utf-8 -*-
  2. # pylint: disable=bad-whitespace
  3. from lxml import etree
  4. from odoo.addons.account.tests.common import AccountTestInvoicingCommon
  5. from odoo.tests.common import Form
  6. from odoo.tests import tagged
  7. from odoo import fields, Command
  8. from collections import defaultdict
  9. @tagged('post_install', '-at_install')
  10. class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
  11. @classmethod
  12. def setUpClass(cls, chart_template_ref=None):
  13. super().setUpClass(chart_template_ref=chart_template_ref)
  14. cls.invoice = cls.init_invoice('in_refund', products=cls.product_a+cls.product_b)
  15. cls.product_line_vals_1 = {
  16. 'name': cls.product_a.name,
  17. 'product_id': cls.product_a.id,
  18. 'account_id': cls.product_a.property_account_expense_id.id,
  19. 'partner_id': cls.partner_a.id,
  20. 'product_uom_id': cls.product_a.uom_id.id,
  21. 'quantity': 1.0,
  22. 'discount': 0.0,
  23. 'price_unit': 800.0,
  24. 'price_subtotal': 800.0,
  25. 'price_total': 920.0,
  26. 'tax_ids': cls.product_a.supplier_taxes_id.ids,
  27. 'tax_line_id': False,
  28. 'currency_id': cls.company_data['currency'].id,
  29. 'amount_currency': -800.0,
  30. 'debit': 0.0,
  31. 'credit': 800.0,
  32. 'date_maturity': False,
  33. }
  34. cls.product_line_vals_2 = {
  35. 'name': cls.product_b.name,
  36. 'product_id': cls.product_b.id,
  37. 'account_id': cls.product_b.property_account_expense_id.id,
  38. 'partner_id': cls.partner_a.id,
  39. 'product_uom_id': cls.product_b.uom_id.id,
  40. 'quantity': 1.0,
  41. 'discount': 0.0,
  42. 'price_unit': 160.0,
  43. 'price_subtotal': 160.0,
  44. 'price_total': 208.0,
  45. 'tax_ids': cls.product_b.supplier_taxes_id.ids,
  46. 'tax_line_id': False,
  47. 'currency_id': cls.company_data['currency'].id,
  48. 'amount_currency': -160.0,
  49. 'debit': 0.0,
  50. 'credit': 160.0,
  51. 'date_maturity': False,
  52. }
  53. cls.tax_line_vals_1 = {
  54. 'name': cls.tax_purchase_a.name,
  55. 'product_id': False,
  56. 'account_id': cls.company_data['default_account_tax_purchase'].id,
  57. 'partner_id': cls.partner_a.id,
  58. 'product_uom_id': False,
  59. 'quantity': False,
  60. 'discount': 0.0,
  61. 'price_unit': 0.0,
  62. 'price_subtotal': 0.0,
  63. 'price_total': 0.0,
  64. 'tax_ids': [],
  65. 'tax_line_id': cls.tax_purchase_a.id,
  66. 'currency_id': cls.company_data['currency'].id,
  67. 'amount_currency': -144.0,
  68. 'debit': 0.0,
  69. 'credit': 144.0,
  70. 'date_maturity': False,
  71. }
  72. cls.tax_line_vals_2 = {
  73. 'name': cls.tax_purchase_b.name,
  74. 'product_id': False,
  75. 'account_id': cls.company_data['default_account_tax_purchase'].id,
  76. 'partner_id': cls.partner_a.id,
  77. 'product_uom_id': False,
  78. 'quantity': False,
  79. 'discount': 0.0,
  80. 'price_unit': 0.0,
  81. 'price_subtotal': 0.0,
  82. 'price_total': 0.0,
  83. 'tax_ids': [],
  84. 'tax_line_id': cls.tax_purchase_b.id,
  85. 'currency_id': cls.company_data['currency'].id,
  86. 'amount_currency': -24.0,
  87. 'debit': 0.0,
  88. 'credit': 24.0,
  89. 'date_maturity': False,
  90. }
  91. cls.term_line_vals_1 = {
  92. 'name': '',
  93. 'product_id': False,
  94. 'account_id': cls.company_data['default_account_payable'].id,
  95. 'partner_id': cls.partner_a.id,
  96. 'product_uom_id': False,
  97. 'quantity': False,
  98. 'discount': 0.0,
  99. 'price_unit': 0.0,
  100. 'price_subtotal': 0.0,
  101. 'price_total': 0.0,
  102. 'tax_ids': [],
  103. 'tax_line_id': False,
  104. 'currency_id': cls.company_data['currency'].id,
  105. 'amount_currency': 1128.0,
  106. 'debit': 1128.0,
  107. 'credit': 0.0,
  108. 'date_maturity': fields.Date.from_string('2019-01-01'),
  109. }
  110. cls.move_vals = {
  111. 'partner_id': cls.partner_a.id,
  112. 'currency_id': cls.company_data['currency'].id,
  113. 'journal_id': cls.company_data['default_journal_purchase'].id,
  114. 'invoice_date': fields.Date.from_string('2019-01-01'),
  115. 'fiscal_position_id': False,
  116. 'payment_reference': '',
  117. 'invoice_payment_term_id': cls.pay_terms_a.id,
  118. 'amount_untaxed': 960.0,
  119. 'amount_tax': 168.0,
  120. 'amount_total': 1128.0,
  121. }
  122. def setUp(self):
  123. super(TestAccountMoveInRefundOnchanges, self).setUp()
  124. self.assertInvoiceValues(self.invoice, [
  125. self.product_line_vals_1,
  126. self.product_line_vals_2,
  127. self.tax_line_vals_1,
  128. self.tax_line_vals_2,
  129. self.term_line_vals_1,
  130. ], self.move_vals)
  131. def test_in_refund_line_onchange_product_1(self):
  132. move_form = Form(self.invoice)
  133. with move_form.invoice_line_ids.edit(0) as line_form:
  134. line_form.product_id = self.product_b
  135. move_form.save()
  136. self.assertInvoiceValues(self.invoice, [
  137. {
  138. **self.product_line_vals_1,
  139. 'name': self.product_b.name,
  140. 'product_id': self.product_b.id,
  141. 'product_uom_id': self.product_b.uom_id.id,
  142. 'account_id': self.product_b.property_account_expense_id.id,
  143. 'price_unit': 160.0,
  144. 'price_subtotal': 160.0,
  145. 'price_total': 208.0,
  146. 'tax_ids': self.product_b.supplier_taxes_id.ids,
  147. 'amount_currency': -160.0,
  148. 'credit': 160.0,
  149. },
  150. self.product_line_vals_2,
  151. {
  152. **self.tax_line_vals_1,
  153. 'amount_currency': -48.0,
  154. 'credit': 48.0,
  155. },
  156. {
  157. **self.tax_line_vals_2,
  158. 'amount_currency': -48.0,
  159. 'credit': 48.0,
  160. },
  161. {
  162. **self.term_line_vals_1,
  163. 'amount_currency': 416.0,
  164. 'debit': 416.0,
  165. },
  166. ], {
  167. **self.move_vals,
  168. 'amount_untaxed': 320.0,
  169. 'amount_tax': 96.0,
  170. 'amount_total': 416.0,
  171. })
  172. def test_in_refund_line_onchange_business_fields_1(self):
  173. move_form = Form(self.invoice)
  174. with move_form.invoice_line_ids.edit(0) as line_form:
  175. # Current price_unit is 800.
  176. # We set quantity = 4, discount = 50%, price_unit = 400. The debit/credit fields don't change because (4 * 400) * 0.5 = 800.
  177. line_form.quantity = 4
  178. line_form.discount = 50
  179. line_form.price_unit = 400
  180. move_form.save()
  181. self.assertInvoiceValues(self.invoice, [
  182. {
  183. **self.product_line_vals_1,
  184. 'quantity': 4,
  185. 'discount': 50.0,
  186. 'price_unit': 400.0,
  187. },
  188. self.product_line_vals_2,
  189. self.tax_line_vals_1,
  190. self.tax_line_vals_2,
  191. self.term_line_vals_1,
  192. ], self.move_vals)
  193. move_form = Form(self.invoice)
  194. with move_form.invoice_line_ids.edit(0) as line_form:
  195. # Reset field except the discount that becomes 100%.
  196. # /!\ The modification is made on the accounting tab.
  197. line_form.quantity = 1
  198. line_form.discount = 100
  199. line_form.price_unit = 800
  200. move_form.save()
  201. self.assertInvoiceValues(self.invoice, [
  202. {
  203. **self.product_line_vals_1,
  204. 'discount': 100.0,
  205. 'price_subtotal': 0.0,
  206. 'price_total': 0.0,
  207. 'amount_currency': 0.0,
  208. 'credit': 0.0,
  209. },
  210. self.product_line_vals_2,
  211. {
  212. **self.tax_line_vals_1,
  213. 'amount_currency': -24.0,
  214. 'credit': 24.0,
  215. },
  216. self.tax_line_vals_2,
  217. {
  218. **self.term_line_vals_1,
  219. 'amount_currency': 208.0,
  220. 'debit': 208.0,
  221. },
  222. ], {
  223. **self.move_vals,
  224. 'amount_untaxed': 160.0,
  225. 'amount_tax': 48.0,
  226. 'amount_total': 208.0,
  227. })
  228. def test_in_refund_line_onchange_partner_1(self):
  229. move_form = Form(self.invoice)
  230. move_form.partner_id = self.partner_b
  231. move_form.payment_reference = 'turlututu'
  232. move_form.save()
  233. self.assertInvoiceValues(self.invoice, [
  234. {
  235. **self.product_line_vals_1,
  236. 'partner_id': self.partner_b.id,
  237. },
  238. {
  239. **self.product_line_vals_2,
  240. 'partner_id': self.partner_b.id,
  241. },
  242. {
  243. **self.tax_line_vals_1,
  244. 'partner_id': self.partner_b.id,
  245. },
  246. {
  247. **self.tax_line_vals_2,
  248. 'partner_id': self.partner_b.id,
  249. },
  250. {
  251. **self.term_line_vals_1,
  252. 'name': 'turlututu',
  253. 'partner_id': self.partner_b.id,
  254. 'account_id': self.partner_b.property_account_payable_id.id,
  255. 'amount_currency': 338.4,
  256. 'debit': 338.4,
  257. },
  258. {
  259. **self.term_line_vals_1,
  260. 'name': 'turlututu',
  261. 'partner_id': self.partner_b.id,
  262. 'account_id': self.partner_b.property_account_payable_id.id,
  263. 'amount_currency': 789.6,
  264. 'debit': 789.6,
  265. 'date_maturity': fields.Date.from_string('2019-02-28'),
  266. },
  267. ], {
  268. **self.move_vals,
  269. 'partner_id': self.partner_b.id,
  270. 'payment_reference': 'turlututu',
  271. 'fiscal_position_id': self.fiscal_pos_a.id,
  272. 'invoice_payment_term_id': self.pay_terms_b.id,
  273. 'amount_untaxed': 960.0,
  274. 'amount_tax': 168.0,
  275. 'amount_total': 1128.0,
  276. })
  277. # Remove lines and recreate them to apply the fiscal position.
  278. move_form = Form(self.invoice)
  279. move_form.invoice_line_ids.remove(0)
  280. move_form.invoice_line_ids.remove(0)
  281. with move_form.invoice_line_ids.new() as line_form:
  282. line_form.product_id = self.product_a
  283. with move_form.invoice_line_ids.new() as line_form:
  284. line_form.product_id = self.product_b
  285. move_form.save()
  286. self.assertInvoiceValues(self.invoice, [
  287. {
  288. **self.product_line_vals_1,
  289. 'account_id': self.product_b.property_account_expense_id.id,
  290. 'partner_id': self.partner_b.id,
  291. 'tax_ids': self.tax_purchase_b.ids,
  292. },
  293. {
  294. **self.product_line_vals_2,
  295. 'partner_id': self.partner_b.id,
  296. 'price_total': 184.0,
  297. 'tax_ids': self.tax_purchase_b.ids,
  298. },
  299. {
  300. **self.tax_line_vals_1,
  301. 'name': self.tax_purchase_b.name,
  302. 'partner_id': self.partner_b.id,
  303. 'tax_line_id': self.tax_purchase_b.id,
  304. },
  305. {
  306. **self.term_line_vals_1,
  307. 'name': 'turlututu',
  308. 'account_id': self.partner_b.property_account_payable_id.id,
  309. 'partner_id': self.partner_b.id,
  310. 'amount_currency': 331.2,
  311. 'debit': 331.2,
  312. },
  313. {
  314. **self.term_line_vals_1,
  315. 'name': 'turlututu',
  316. 'account_id': self.partner_b.property_account_payable_id.id,
  317. 'partner_id': self.partner_b.id,
  318. 'amount_currency': 772.8,
  319. 'debit': 772.8,
  320. 'date_maturity': fields.Date.from_string('2019-02-28'),
  321. },
  322. ], {
  323. **self.move_vals,
  324. 'partner_id': self.partner_b.id,
  325. 'payment_reference': 'turlututu',
  326. 'fiscal_position_id': self.fiscal_pos_a.id,
  327. 'invoice_payment_term_id': self.pay_terms_b.id,
  328. 'amount_untaxed': 960.0,
  329. 'amount_tax': 144.0,
  330. 'amount_total': 1104.0,
  331. })
  332. def test_in_refund_line_onchange_taxes_1(self):
  333. move_form = Form(self.invoice)
  334. with move_form.invoice_line_ids.edit(0) as line_form:
  335. line_form.price_unit = 960
  336. line_form.tax_ids.add(self.tax_armageddon)
  337. move_form.save()
  338. child_tax_1 = self.tax_armageddon.children_tax_ids[0]
  339. child_tax_2 = self.tax_armageddon.children_tax_ids[1]
  340. self.assertInvoiceValues(self.invoice, [
  341. {
  342. **self.product_line_vals_1,
  343. 'price_unit': 960.0,
  344. 'price_subtotal': 800.0,
  345. 'price_total': 1176.0,
  346. 'tax_ids': (self.tax_purchase_a + self.tax_armageddon).ids,
  347. },
  348. self.product_line_vals_2,
  349. self.tax_line_vals_1,
  350. self.tax_line_vals_2,
  351. {
  352. 'name': child_tax_1.name,
  353. 'product_id': False,
  354. 'account_id': self.company_data['default_account_expense'].id,
  355. 'partner_id': self.partner_a.id,
  356. 'product_uom_id': False,
  357. 'quantity': False,
  358. 'discount': 0.0,
  359. 'price_unit': 0.0,
  360. 'price_subtotal': 0.0,
  361. 'price_total': 0.0,
  362. 'tax_ids': child_tax_2.ids,
  363. 'tax_line_id': child_tax_1.id,
  364. 'currency_id': self.company_data['currency'].id,
  365. 'amount_currency': -96.0,
  366. 'debit': 0.0,
  367. 'credit': 96.0,
  368. 'date_maturity': False,
  369. },
  370. {
  371. 'name': child_tax_1.name,
  372. 'product_id': False,
  373. 'account_id': self.company_data['default_account_tax_sale'].id,
  374. 'partner_id': self.partner_a.id,
  375. 'product_uom_id': False,
  376. 'quantity': False,
  377. 'discount': 0.0,
  378. 'price_unit': 0.0,
  379. 'price_subtotal': 0.0,
  380. 'price_total': 0.0,
  381. 'tax_ids': child_tax_2.ids,
  382. 'tax_line_id': child_tax_1.id,
  383. 'currency_id': self.company_data['currency'].id,
  384. 'amount_currency': -64.0,
  385. 'debit': 0.0,
  386. 'credit': 64.0,
  387. 'date_maturity': False,
  388. },
  389. {
  390. 'name': child_tax_2.name,
  391. 'product_id': False,
  392. 'account_id': child_tax_2.cash_basis_transition_account_id.id,
  393. 'partner_id': self.partner_a.id,
  394. 'product_uom_id': False,
  395. 'quantity': False,
  396. 'discount': 0.0,
  397. 'price_unit': 0.0,
  398. 'price_subtotal': 0.0,
  399. 'price_total': 0.0,
  400. 'tax_ids': [],
  401. 'tax_line_id': child_tax_2.id,
  402. 'currency_id': self.company_data['currency'].id,
  403. 'amount_currency': -96.0,
  404. 'debit': 0.0,
  405. 'credit': 96.0,
  406. 'date_maturity': False,
  407. },
  408. {
  409. **self.term_line_vals_1,
  410. 'price_unit': 0.0,
  411. 'price_subtotal': 0.0,
  412. 'price_total': 0.0,
  413. 'amount_currency': 1384.0,
  414. 'debit': 1384.0,
  415. },
  416. ], {
  417. **self.move_vals,
  418. 'amount_untaxed': 960.0,
  419. 'amount_tax': 424.0,
  420. 'amount_total': 1384.0,
  421. })
  422. def test_in_refund_line_onchange_cash_rounding_1(self):
  423. # Required for `invoice_cash_rounding_id` to be visible in the view
  424. self.env.user.groups_id += self.env.ref('account.group_cash_rounding')
  425. # Test 'add_invoice_line' rounding
  426. move_form = Form(self.invoice)
  427. # Add a cash rounding having 'add_invoice_line'.
  428. move_form.invoice_cash_rounding_id = self.cash_rounding_a
  429. move_form.save()
  430. # The cash rounding does nothing as the total is already rounded.
  431. self.assertInvoiceValues(self.invoice, [
  432. self.product_line_vals_1,
  433. self.product_line_vals_2,
  434. self.tax_line_vals_1,
  435. self.tax_line_vals_2,
  436. self.term_line_vals_1,
  437. ], self.move_vals)
  438. move_form = Form(self.invoice)
  439. with move_form.invoice_line_ids.edit(0) as line_form:
  440. line_form.price_unit = 799.99
  441. move_form.save()
  442. self.assertInvoiceValues(self.invoice, [
  443. {
  444. **self.product_line_vals_1,
  445. 'price_unit': 799.99,
  446. 'price_subtotal': 799.99,
  447. 'price_total': 919.99,
  448. 'amount_currency': -799.99,
  449. 'credit': 799.99,
  450. },
  451. self.product_line_vals_2,
  452. self.tax_line_vals_1,
  453. self.tax_line_vals_2,
  454. {
  455. 'name': 'add_invoice_line',
  456. 'product_id': False,
  457. 'account_id': self.cash_rounding_a.profit_account_id.id,
  458. 'partner_id': self.partner_a.id,
  459. 'product_uom_id': False,
  460. 'quantity': False,
  461. 'discount': 0.0,
  462. 'price_unit': 0.0,
  463. 'price_subtotal': 0.0,
  464. 'price_total': 0.0,
  465. 'tax_ids': [],
  466. 'tax_line_id': False,
  467. 'currency_id': self.company_data['currency'].id,
  468. 'amount_currency': -0.01,
  469. 'debit': 0.0,
  470. 'credit': 0.01,
  471. 'date_maturity': False,
  472. },
  473. self.term_line_vals_1,
  474. ], self.move_vals)
  475. # Test 'biggest_tax' rounding
  476. self.company_data['company'].country_id = self.env.ref('base.us')
  477. # Add a tag to product_a's default tax
  478. tax_line_tag = self.env['account.account.tag'].create({
  479. 'name': "Tax tag",
  480. 'applicability': 'taxes',
  481. 'country_id': self.company_data['company'].country_id.id,
  482. })
  483. repartition_line = self.tax_purchase_a.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
  484. repartition_line.write({'tag_ids': [(4, tax_line_tag.id, 0)]})
  485. # Create the invoice
  486. biggest_tax_invoice = self.env['account.move'].create({
  487. 'move_type': 'in_refund',
  488. 'invoice_date': '2019-01-01',
  489. 'partner_id': self.partner_a.id,
  490. 'invoice_cash_rounding_id': self.cash_rounding_b.id,
  491. 'invoice_payment_term_id': self.pay_terms_a.id,
  492. 'invoice_line_ids': [
  493. (0, 0, {
  494. 'product_id': self.product_a.id,
  495. 'price_unit': 799.99,
  496. 'tax_ids': [(6, 0, self.product_a.supplier_taxes_id.ids)],
  497. 'product_uom_id': self.product_a.uom_id.id,
  498. }),
  499. (0, 0, {
  500. 'product_id': self.product_b.id,
  501. 'price_unit': self.product_b.standard_price,
  502. 'tax_ids': [(6, 0, self.product_b.supplier_taxes_id.ids)],
  503. 'product_uom_id': self.product_b.uom_id.id,
  504. }),
  505. ],
  506. })
  507. self.assertInvoiceValues(biggest_tax_invoice, [
  508. {
  509. **self.product_line_vals_1,
  510. 'price_unit': 799.99,
  511. 'price_subtotal': 799.99,
  512. 'price_total': 919.99,
  513. 'amount_currency': -799.99,
  514. 'credit': 799.99,
  515. 'tax_repartition_line_id': None,
  516. 'tax_tag_ids': [],
  517. },
  518. {
  519. **self.product_line_vals_2,
  520. 'tax_repartition_line_id': None,
  521. 'tax_tag_ids': [],
  522. },
  523. {
  524. **self.tax_line_vals_1,
  525. 'tax_repartition_line_id': repartition_line.id,
  526. 'tax_tag_ids': tax_line_tag.ids,
  527. },
  528. {
  529. **self.tax_line_vals_2,
  530. 'tax_repartition_line_id': self.tax_purchase_b.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
  531. 'tax_tag_ids': [],
  532. },
  533. {
  534. 'name': '%s (rounding)' % self.tax_purchase_a.name,
  535. 'product_id': False,
  536. 'account_id': self.company_data['default_account_tax_purchase'].id,
  537. 'partner_id': self.partner_a.id,
  538. 'product_uom_id': False,
  539. 'quantity': False,
  540. 'discount': 0.0,
  541. 'price_unit': 0.0,
  542. 'price_subtotal': 0.0,
  543. 'price_total': 0.0,
  544. 'tax_ids': [],
  545. 'tax_line_id': self.tax_purchase_a.id,
  546. 'tax_repartition_line_id': repartition_line.id,
  547. 'tax_tag_ids': tax_line_tag.ids,
  548. 'currency_id': self.company_data['currency'].id,
  549. 'amount_currency': 0.04,
  550. 'debit': 0.04,
  551. 'credit': 0.0,
  552. 'date_maturity': False,
  553. },
  554. {
  555. **self.term_line_vals_1,
  556. 'price_unit': 0.0,
  557. 'price_subtotal': 0.0,
  558. 'price_total': 0.0,
  559. 'amount_currency': 1127.95,
  560. 'debit': 1127.95,
  561. 'tax_repartition_line_id': None,
  562. 'tax_tag_ids': [],
  563. },
  564. ], {
  565. **self.move_vals,
  566. 'amount_untaxed': 959.99,
  567. 'amount_tax': 167.96,
  568. 'amount_total': 1127.95,
  569. })
  570. def test_in_refund_line_onchange_currency_1(self):
  571. move_form = Form(self.invoice)
  572. move_form.currency_id = self.currency_data['currency']
  573. move_form.save()
  574. self.assertInvoiceValues(self.invoice, [
  575. {
  576. **self.product_line_vals_1,
  577. 'currency_id': self.currency_data['currency'].id,
  578. 'amount_currency': -800.0,
  579. 'credit': 400.0,
  580. },
  581. {
  582. **self.product_line_vals_2,
  583. 'currency_id': self.currency_data['currency'].id,
  584. 'amount_currency': -160.0,
  585. 'credit': 80.0,
  586. },
  587. {
  588. **self.tax_line_vals_1,
  589. 'currency_id': self.currency_data['currency'].id,
  590. 'amount_currency': -144.0,
  591. 'credit': 72.0,
  592. },
  593. {
  594. **self.tax_line_vals_2,
  595. 'currency_id': self.currency_data['currency'].id,
  596. 'amount_currency': -24.0,
  597. 'credit': 12.0,
  598. },
  599. {
  600. **self.term_line_vals_1,
  601. 'currency_id': self.currency_data['currency'].id,
  602. 'amount_currency': 1128.0,
  603. 'debit': 564.0,
  604. },
  605. ], {
  606. **self.move_vals,
  607. 'currency_id': self.currency_data['currency'].id,
  608. })
  609. # Change the date to get another rate: 1/3 instead of 1/2.
  610. with Form(self.invoice) as move_form:
  611. move_form.invoice_date = fields.Date.from_string('2016-01-01')
  612. move_form.date = fields.Date.from_string('2016-01-01')
  613. self.assertInvoiceValues(self.invoice, [
  614. {
  615. **self.product_line_vals_1,
  616. 'currency_id': self.currency_data['currency'].id,
  617. 'amount_currency': -800.0,
  618. 'credit': 266.67,
  619. },
  620. {
  621. **self.product_line_vals_2,
  622. 'currency_id': self.currency_data['currency'].id,
  623. 'amount_currency': -160.0,
  624. 'credit': 53.33,
  625. },
  626. {
  627. **self.tax_line_vals_1,
  628. 'currency_id': self.currency_data['currency'].id,
  629. 'amount_currency': -144.0,
  630. 'credit': 48.0,
  631. },
  632. {
  633. **self.tax_line_vals_2,
  634. 'currency_id': self.currency_data['currency'].id,
  635. 'amount_currency': -24.0,
  636. 'credit': 8.0,
  637. },
  638. {
  639. **self.term_line_vals_1,
  640. 'currency_id': self.currency_data['currency'].id,
  641. 'amount_currency': 1128.0,
  642. 'debit': 376.0,
  643. 'date_maturity': fields.Date.from_string('2016-01-01'),
  644. },
  645. ], {
  646. **self.move_vals,
  647. 'currency_id': self.currency_data['currency'].id,
  648. 'date': fields.Date.from_string('2016-01-01'),
  649. 'invoice_date': fields.Date.from_string('2016-01-01'),
  650. })
  651. move_form = Form(self.invoice)
  652. with move_form.invoice_line_ids.edit(0) as line_form:
  653. # 0.045 * 0.1 = 0.0045. As the foreign currency has a 0.001 rounding,
  654. # the result should be 0.005 after rounding.
  655. line_form.quantity = 0.1
  656. line_form.price_unit = 0.045
  657. move_form.save()
  658. self.assertInvoiceValues(self.invoice, [
  659. {
  660. **self.product_line_vals_1,
  661. 'quantity': 0.1,
  662. 'price_unit': 0.05,
  663. 'price_subtotal': 0.005,
  664. 'price_total': 0.006,
  665. 'currency_id': self.currency_data['currency'].id,
  666. 'amount_currency': -0.005,
  667. 'credit': 0.0,
  668. },
  669. {
  670. **self.product_line_vals_2,
  671. 'currency_id': self.currency_data['currency'].id,
  672. 'amount_currency': -160.0,
  673. 'credit': 53.33,
  674. },
  675. {
  676. **self.tax_line_vals_1,
  677. 'currency_id': self.currency_data['currency'].id,
  678. 'amount_currency': -24.001,
  679. 'credit': 8.0,
  680. },
  681. {
  682. **self.tax_line_vals_2,
  683. 'currency_id': self.currency_data['currency'].id,
  684. 'amount_currency': -24.0,
  685. 'credit': 8.0,
  686. },
  687. {
  688. **self.term_line_vals_1,
  689. 'currency_id': self.currency_data['currency'].id,
  690. 'amount_currency': 208.006,
  691. 'debit': 69.33,
  692. 'date_maturity': fields.Date.from_string('2016-01-01'),
  693. },
  694. ], {
  695. **self.move_vals,
  696. 'currency_id': self.currency_data['currency'].id,
  697. 'date': fields.Date.from_string('2016-01-01'),
  698. 'invoice_date': fields.Date.from_string('2016-01-01'),
  699. 'amount_untaxed': 160.005,
  700. 'amount_tax': 48.001,
  701. 'amount_total': 208.006,
  702. })
  703. # Exit the multi-currencies.
  704. move_form = Form(self.invoice)
  705. move_form.currency_id = self.company_data['currency']
  706. move_form.save()
  707. self.assertInvoiceValues(self.invoice, [
  708. {
  709. **self.product_line_vals_1,
  710. 'quantity': 0.1,
  711. 'price_unit': 0.05,
  712. 'price_subtotal': 0.01,
  713. 'price_total': 0.01,
  714. 'amount_currency': -0.01,
  715. 'credit': 0.01,
  716. },
  717. self.product_line_vals_2,
  718. {
  719. **self.tax_line_vals_1,
  720. 'amount_currency': -24.0,
  721. 'credit': 24.0,
  722. },
  723. self.tax_line_vals_2,
  724. {
  725. **self.term_line_vals_1,
  726. 'amount_currency': 208.01,
  727. 'debit': 208.01,
  728. 'date_maturity': fields.Date.from_string('2016-01-01'),
  729. },
  730. ], {
  731. **self.move_vals,
  732. 'currency_id': self.company_data['currency'].id,
  733. 'date': fields.Date.from_string('2016-01-01'),
  734. 'invoice_date': fields.Date.from_string('2016-01-01'),
  735. 'amount_untaxed': 160.01,
  736. 'amount_tax': 48.0,
  737. 'amount_total': 208.01,
  738. })
  739. def test_in_refund_onchange_past_invoice_1(self):
  740. copy_invoice = self.invoice.copy()
  741. if self.env.ref('purchase.group_purchase_manager', raise_if_not_found=False):
  742. # `purchase` adds a view which makes `invoice_vendor_bill_id` invisible
  743. # for purchase users
  744. # https://github.com/odoo/odoo/blob/385884afd31f25d61e99d139ecd4c574d99a1863/addons/purchase/views/account_move_views.xml#L26
  745. self.env.user.groups_id -= self.env.ref('purchase.group_purchase_manager')
  746. self.env.user.groups_id -= self.env.ref('purchase.group_purchase_user')
  747. # 'invisible': ['|', ('state', '!=', 'draft'), ('move_type', '!=', 'in_invoice')]
  748. # This is an in_refund invoice, `invoice_vendor_bill_id` is not supposed to be visible
  749. # and therefore not supposed to be changed.
  750. view = self.env.ref('account.view_move_form')
  751. tree = etree.fromstring(view.arch)
  752. for node in tree.xpath('//field[@name="invoice_vendor_bill_id"]'):
  753. del node.attrib['attrs']
  754. view.arch = etree.tostring(tree)
  755. move_form = Form(self.invoice)
  756. move_form.invoice_line_ids.remove(0)
  757. move_form.invoice_line_ids.remove(0)
  758. move_form.invoice_vendor_bill_id = copy_invoice
  759. move_form.save()
  760. self.assertInvoiceValues(self.invoice, [
  761. self.product_line_vals_1,
  762. self.product_line_vals_2,
  763. self.tax_line_vals_1,
  764. self.tax_line_vals_2,
  765. self.term_line_vals_1,
  766. ], self.move_vals)
  767. def test_in_refund_create_1(self):
  768. # Test creating an account_move with the least information.
  769. move = self.env['account.move'].create({
  770. 'move_type': 'in_refund',
  771. 'partner_id': self.partner_a.id,
  772. 'invoice_date': fields.Date.from_string('2019-01-01'),
  773. 'currency_id': self.currency_data['currency'].id,
  774. 'invoice_payment_term_id': self.pay_terms_a.id,
  775. 'invoice_line_ids': [
  776. Command.create({
  777. 'product_id': self.product_line_vals_1['product_id'],
  778. 'product_uom_id': self.product_line_vals_1['product_uom_id'],
  779. 'price_unit': self.product_line_vals_1['price_unit'],
  780. 'tax_ids': [Command.set(self.product_line_vals_1['tax_ids'])],
  781. }),
  782. Command.create({
  783. 'product_id': self.product_line_vals_2['product_id'],
  784. 'product_uom_id': self.product_line_vals_2['product_uom_id'],
  785. 'price_unit': self.product_line_vals_2['price_unit'],
  786. 'tax_ids': [Command.set(self.product_line_vals_2['tax_ids'])],
  787. }),
  788. ],
  789. })
  790. self.assertInvoiceValues(move, [
  791. {
  792. **self.product_line_vals_1,
  793. 'currency_id': self.currency_data['currency'].id,
  794. 'amount_currency': -800.0,
  795. 'credit': 400.0,
  796. },
  797. {
  798. **self.product_line_vals_2,
  799. 'currency_id': self.currency_data['currency'].id,
  800. 'amount_currency': -160.0,
  801. 'credit': 80.0,
  802. },
  803. {
  804. **self.tax_line_vals_1,
  805. 'currency_id': self.currency_data['currency'].id,
  806. 'amount_currency': -144.0,
  807. 'credit': 72.0,
  808. },
  809. {
  810. **self.tax_line_vals_2,
  811. 'currency_id': self.currency_data['currency'].id,
  812. 'amount_currency': -24.0,
  813. 'credit': 12.0,
  814. },
  815. {
  816. **self.term_line_vals_1,
  817. 'currency_id': self.currency_data['currency'].id,
  818. 'amount_currency': 1128.0,
  819. 'debit': 564.0,
  820. },
  821. ], {
  822. **self.move_vals,
  823. 'currency_id': self.currency_data['currency'].id,
  824. })
  825. def test_in_refund_write_1(self):
  826. # Test creating an account_move with the least information.
  827. move = self.env['account.move'].create({
  828. 'move_type': 'in_refund',
  829. 'partner_id': self.partner_a.id,
  830. 'invoice_date': fields.Date.from_string('2019-01-01'),
  831. 'currency_id': self.currency_data['currency'].id,
  832. 'invoice_payment_term_id': self.pay_terms_a.id,
  833. 'invoice_line_ids': [
  834. Command.create({
  835. 'product_id': self.product_line_vals_1['product_id'],
  836. 'product_uom_id': self.product_line_vals_1['product_uom_id'],
  837. 'price_unit': self.product_line_vals_1['price_unit'],
  838. 'tax_ids': [Command.set(self.product_line_vals_1['tax_ids'])],
  839. }),
  840. ],
  841. })
  842. move.write({
  843. 'invoice_line_ids': [
  844. Command.create({
  845. 'product_id': self.product_line_vals_2['product_id'],
  846. 'product_uom_id': self.product_line_vals_2['product_uom_id'],
  847. 'price_unit': self.product_line_vals_2['price_unit'],
  848. 'tax_ids': [Command.set(self.product_line_vals_2['tax_ids'])],
  849. }),
  850. ],
  851. })
  852. self.assertInvoiceValues(move, [
  853. {
  854. **self.product_line_vals_1,
  855. 'currency_id': self.currency_data['currency'].id,
  856. 'amount_currency': -800.0,
  857. 'credit': 400.0,
  858. },
  859. {
  860. **self.product_line_vals_2,
  861. 'currency_id': self.currency_data['currency'].id,
  862. 'amount_currency': -160.0,
  863. 'credit': 80.0,
  864. },
  865. {
  866. **self.tax_line_vals_1,
  867. 'currency_id': self.currency_data['currency'].id,
  868. 'amount_currency': -144.0,
  869. 'credit': 72.0,
  870. },
  871. {
  872. **self.tax_line_vals_2,
  873. 'currency_id': self.currency_data['currency'].id,
  874. 'amount_currency': -24.0,
  875. 'credit': 12.0,
  876. },
  877. {
  878. **self.term_line_vals_1,
  879. 'currency_id': self.currency_data['currency'].id,
  880. 'amount_currency': 1128.0,
  881. 'debit': 564.0,
  882. },
  883. ], {
  884. **self.move_vals,
  885. 'currency_id': self.currency_data['currency'].id,
  886. })
  887. def test_in_refund_create_storno(self):
  888. # Test creating an account_move refund (credit note)
  889. # with multiple lines while in Storno accounting
  890. self.env.company.account_storno = True
  891. move = self.env['account.move'].create({
  892. 'move_type': 'in_refund',
  893. 'partner_id': self.partner_a.id,
  894. 'invoice_date': fields.Date.from_string('2019-01-01'),
  895. 'currency_id': self.currency_data['currency'].id,
  896. 'invoice_payment_term_id': self.pay_terms_a.id,
  897. 'invoice_line_ids': [
  898. Command.create({
  899. 'product_id': self.product_line_vals_1['product_id'],
  900. 'product_uom_id': self.product_line_vals_1['product_uom_id'],
  901. 'price_unit': self.product_line_vals_1['price_unit'],
  902. 'tax_ids': [Command.set(self.product_line_vals_1['tax_ids'])],
  903. }),
  904. Command.create({
  905. 'product_id': self.product_line_vals_2['product_id'],
  906. 'product_uom_id': self.product_line_vals_2['product_uom_id'],
  907. 'price_unit': self.product_line_vals_2['price_unit'],
  908. 'tax_ids': [Command.set(self.product_line_vals_2['tax_ids'])],
  909. }),
  910. ]
  911. })
  912. self.assertInvoiceValues(move, [
  913. {
  914. **self.product_line_vals_1,
  915. 'currency_id': self.currency_data['currency'].id,
  916. 'amount_currency': -800.0,
  917. 'balance': -400.0,
  918. 'debit': -400.0,
  919. 'credit': 0.0,
  920. },
  921. {
  922. **self.product_line_vals_2,
  923. 'currency_id': self.currency_data['currency'].id,
  924. 'amount_currency': -160.0,
  925. 'balance': -80.0,
  926. 'debit': -80.0,
  927. 'credit': 0.0,
  928. },
  929. {
  930. **self.tax_line_vals_1,
  931. 'currency_id': self.currency_data['currency'].id,
  932. 'amount_currency': -144.0,
  933. 'balance': -72.0,
  934. 'debit': -72.0,
  935. 'credit': 0.0,
  936. },
  937. {
  938. **self.tax_line_vals_2,
  939. 'currency_id': self.currency_data['currency'].id,
  940. 'amount_currency': -24.0,
  941. 'balance': -12.0,
  942. 'debit': -12.0,
  943. 'credit': 0.0,
  944. },
  945. {
  946. **self.term_line_vals_1,
  947. 'currency_id': self.currency_data['currency'].id,
  948. 'amount_currency': 1128.0,
  949. 'balance': 564.0,
  950. 'debit': 0.0,
  951. 'credit': -564.0,
  952. },
  953. ], {
  954. **self.move_vals,
  955. 'currency_id': self.currency_data['currency'].id,
  956. })
  957. def test_in_refund_reverse_caba(self):
  958. tax_waiting_account = self.env['account.account'].create({
  959. 'name': 'TAX_WAIT',
  960. 'code': 'TWAIT',
  961. 'account_type': 'liability_current',
  962. 'reconcile': True,
  963. 'company_id': self.company_data['company'].id,
  964. })
  965. tax_final_account = self.env['account.account'].create({
  966. 'name': 'TAX_TO_DEDUCT',
  967. 'code': 'TDEDUCT',
  968. 'account_type': 'asset_current',
  969. 'company_id': self.company_data['company'].id,
  970. })
  971. tax_base_amount_account = self.env['account.account'].create({
  972. 'name': 'TAX_BASE',
  973. 'code': 'TBASE',
  974. 'account_type': 'asset_current',
  975. 'company_id': self.company_data['company'].id,
  976. })
  977. self.env.company.account_cash_basis_base_account_id = tax_base_amount_account
  978. self.env.company.tax_exigibility = True
  979. tax_tags = defaultdict(dict)
  980. for line_type, repartition_type in [(l, r) for l in ('invoice', 'refund') for r in ('base', 'tax')]:
  981. tax_tags[line_type][repartition_type] = self.env['account.account.tag'].create({
  982. 'name': '%s %s tag' % (line_type, repartition_type),
  983. 'applicability': 'taxes',
  984. 'country_id': self.env.ref('base.us').id,
  985. })
  986. tax = self.env['account.tax'].create({
  987. 'name': 'cash basis 10%',
  988. 'type_tax_use': 'purchase',
  989. 'amount': 10,
  990. 'tax_exigibility': 'on_payment',
  991. 'cash_basis_transition_account_id': tax_waiting_account.id,
  992. 'invoice_repartition_line_ids': [
  993. (0, 0, {
  994. 'repartition_type': 'base',
  995. 'tag_ids': [(6, 0, tax_tags['invoice']['base'].ids)],
  996. }),
  997. (0, 0, {
  998. 'repartition_type': 'tax',
  999. 'account_id': tax_final_account.id,
  1000. 'tag_ids': [(6, 0, tax_tags['invoice']['tax'].ids)],
  1001. }),
  1002. ],
  1003. 'refund_repartition_line_ids': [
  1004. (0, 0, {
  1005. 'repartition_type': 'base',
  1006. 'tag_ids': [(6, 0, tax_tags['refund']['base'].ids)],
  1007. }),
  1008. (0, 0, {
  1009. 'repartition_type': 'tax',
  1010. 'account_id': tax_final_account.id,
  1011. 'tag_ids': [(6, 0, tax_tags['refund']['tax'].ids)],
  1012. }),
  1013. ],
  1014. })
  1015. # create invoice
  1016. move_form = Form(self.env['account.move'].with_context(default_move_type='in_refund'))
  1017. move_form.partner_id = self.partner_a
  1018. move_form.invoice_date = fields.Date.from_string('2017-01-01')
  1019. with move_form.invoice_line_ids.new() as line_form:
  1020. line_form.product_id = self.product_a
  1021. line_form.tax_ids.clear()
  1022. line_form.tax_ids.add(tax)
  1023. invoice = move_form.save()
  1024. invoice.action_post()
  1025. # make payment
  1026. self.env['account.payment.register'].with_context(active_model='account.move', active_ids=invoice.ids).create({
  1027. 'payment_date': invoice.date,
  1028. })._create_payments()
  1029. # check caba move
  1030. partial_rec = invoice.mapped('line_ids.matched_credit_ids')
  1031. caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', '=', partial_rec.id)])
  1032. expected_values = [
  1033. {
  1034. 'tax_line_id': False,
  1035. 'tax_repartition_line_id': False,
  1036. 'tax_ids': [],
  1037. 'tax_tag_ids': [],
  1038. 'account_id': tax_base_amount_account.id,
  1039. 'debit': 800.0,
  1040. 'credit': 0.0,
  1041. },
  1042. {
  1043. 'tax_line_id': False,
  1044. 'tax_repartition_line_id': False,
  1045. 'tax_ids': tax.ids,
  1046. 'tax_tag_ids': tax_tags['refund']['base'].ids,
  1047. 'account_id': tax_base_amount_account.id,
  1048. 'debit': 0.0,
  1049. 'credit': 800.0,
  1050. },
  1051. {
  1052. 'tax_line_id': False,
  1053. 'tax_repartition_line_id': False,
  1054. 'tax_ids': [],
  1055. 'tax_tag_ids': [],
  1056. 'account_id': tax_waiting_account.id,
  1057. 'debit': 80.0,
  1058. 'credit': 0.0,
  1059. },
  1060. {
  1061. 'tax_line_id': tax.id,
  1062. 'tax_repartition_line_id': tax.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
  1063. 'tax_ids': [],
  1064. 'tax_tag_ids': tax_tags['refund']['tax'].ids,
  1065. 'account_id': tax_final_account.id,
  1066. 'debit': 0.0,
  1067. 'credit': 80.0,
  1068. },
  1069. ]
  1070. self.assertRecordValues(caba_move.line_ids, expected_values)
  1071. # unreconcile
  1072. debit_aml = invoice.line_ids.filtered('debit')
  1073. debit_aml.remove_move_reconcile()
  1074. # check caba move reverse is same as caba move with only debit/credit inverted
  1075. reversed_caba_move = self.env['account.move'].search([('reversed_entry_id', '=', caba_move.id)])
  1076. for value in expected_values:
  1077. value.update({
  1078. 'debit': value['credit'],
  1079. 'credit': value['debit'],
  1080. })
  1081. self.assertRecordValues(reversed_caba_move.line_ids, expected_values)