You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
16 KiB

  1. # Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
  3. from datetime import datetime
  4. from dateutil.relativedelta import relativedelta
  5. from decimal import Decimal
  6. import json
  7. from unittest import mock
  8. from odoo.tests import common
  9. from odoo import fields
  10. _module_ns = 'odoo.addons.account_bank_statement_import_online_transferwise'
  11. _provider_class = (
  12. _module_ns
  13. + '.models.online_bank_statement_provider_transferwise'
  14. + '.OnlineBankStatementProviderTransferwise'
  15. )
  16. class TestAccountBankAccountStatementImportOnlineTransferwise(
  17. common.TransactionCase
  18. ):
  19. def setUp(self):
  20. super().setUp()
  21. self.now = fields.Datetime.now()
  22. self.currency_eur = self.env.ref('base.EUR')
  23. self.currency_usd = self.env.ref('base.USD')
  24. self.AccountJournal = self.env['account.journal']
  25. self.OnlineBankStatementProvider = self.env[
  26. 'online.bank.statement.provider'
  27. ]
  28. self.AccountBankStatement = self.env['account.bank.statement']
  29. self.AccountBankStatementLine = self.env['account.bank.statement.line']
  30. Provider = self.OnlineBankStatementProvider
  31. self.transferwise_parse_transaction = lambda payload: (
  32. Provider._transferwise_transaction_to_lines(
  33. Provider._transferwise_preparse_transaction(
  34. json.loads(
  35. payload,
  36. parse_float=Decimal,
  37. )
  38. )
  39. )
  40. )
  41. def test_values_transferwise_profile(self):
  42. mocked_response = json.loads(
  43. """[
  44. {
  45. "id": 1234567890,
  46. "type": "personal",
  47. "details": {
  48. "firstName": "Alexey",
  49. "lastName": "Pelykh"
  50. }
  51. },
  52. {
  53. "id": 1234567891,
  54. "type": "business",
  55. "details": {
  56. "name": "Brainbean Apps OÜ"
  57. }
  58. }
  59. ]""", parse_float=Decimal)
  60. values_transferwise_profile = []
  61. with mock.patch(
  62. _provider_class + '._transferwise_retrieve',
  63. return_value=mocked_response,
  64. ):
  65. values_transferwise_profile = (
  66. self.OnlineBankStatementProvider.with_context({
  67. 'api_base': 'https://example.com',
  68. 'api_key': 'dummy',
  69. }).values_transferwise_profile()
  70. )
  71. self.assertEqual(
  72. values_transferwise_profile,
  73. [
  74. ('1234567890', 'Alexey Pelykh (personal)'),
  75. ('1234567891', 'Brainbean Apps OÜ'),
  76. ]
  77. )
  78. def test_pull(self):
  79. journal = self.AccountJournal.create({
  80. 'name': 'Bank',
  81. 'type': 'bank',
  82. 'code': 'BANK',
  83. 'currency_id': self.currency_eur.id,
  84. 'bank_statements_source': 'online',
  85. 'online_bank_statement_provider': 'transferwise',
  86. })
  87. provider = journal.online_bank_statement_provider_id
  88. provider.origin = '1234567891'
  89. def mock_response(url, api_key):
  90. if '/borderless-accounts?profileId=1234567891' in url:
  91. payload = """[
  92. {
  93. "id": 42,
  94. "balances": [
  95. {
  96. "currency": "EUR"
  97. }
  98. ]
  99. }
  100. ]"""
  101. elif '/borderless-accounts/42/statement.json' in url:
  102. payload = """{
  103. "transactions": [],
  104. "endOfStatementBalance": {
  105. "value": 42.00,
  106. "currency": "EUR"
  107. }
  108. }"""
  109. return json.loads(payload, parse_float=Decimal)
  110. with mock.patch(
  111. _provider_class + '._transferwise_retrieve',
  112. side_effect=mock_response,
  113. ):
  114. data = provider._obtain_statement_data(
  115. self.now - relativedelta(hours=1),
  116. self.now,
  117. )
  118. self.assertEqual(len(data[0]), 0)
  119. self.assertEqual(data[1]['balance_start'], 42.0)
  120. self.assertEqual(data[1]['balance_end_real'], 42.0)
  121. def test_transaction_parse_1(self):
  122. lines = self.transferwise_parse_transaction("""{
  123. "type": "CREDIT",
  124. "date": "2000-01-01T00:00:00.000Z",
  125. "amount": {
  126. "value": 0.42,
  127. "currency": "EUR"
  128. },
  129. "totalFees": {
  130. "value": 0.00,
  131. "currency": "EUR"
  132. },
  133. "details": {
  134. "type": "DEPOSIT",
  135. "description": "Received money from SENDER with reference REF-XYZ",
  136. "senderName": "SENDER",
  137. "senderAccount": "XX00 0000 0000 0000",
  138. "paymentReference": "REF-XYZ"
  139. },
  140. "exchangeDetails": null,
  141. "runningBalance": {
  142. "value": 0.42,
  143. "currency": "EUR"
  144. },
  145. "referenceNumber": "TRANSFER-123456789"
  146. }""")
  147. self.assertEqual(len(lines), 1)
  148. self.assertEqual(lines[0], {
  149. 'date': datetime(2000, 1, 1),
  150. 'amount': '0.42',
  151. 'name': 'REF-XYZ',
  152. 'note': (
  153. 'TRANSFER-123456789: Received money from SENDER with reference'
  154. ' REF-XYZ'
  155. ),
  156. 'partner_name': 'SENDER',
  157. 'account_number': 'XX00 0000 0000 0000',
  158. 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800',
  159. })
  160. def test_transaction_parse_2(self):
  161. lines = self.transferwise_parse_transaction("""{
  162. "type": "DEBIT",
  163. "date": "2000-01-01T00:00:00.000Z",
  164. "amount": {
  165. "value": -200.60,
  166. "currency": "EUR"
  167. },
  168. "totalFees": {
  169. "value": 0.60,
  170. "currency": "EUR"
  171. },
  172. "details": {
  173. "type": "TRANSFER",
  174. "description": "Sent money to John Doe",
  175. "recipient": {
  176. "name": "John Doe",
  177. "bankAccount": "XX00 0000 0000 0000"
  178. },
  179. "paymentReference": "INVOICE 42-01"
  180. },
  181. "exchangeDetails": null,
  182. "runningBalance": {
  183. "value": 100.42,
  184. "currency": "EUR"
  185. },
  186. "referenceNumber": "TRANSFER-123456789"
  187. }""")
  188. self.assertEqual(len(lines), 2)
  189. self.assertEqual(lines[0], {
  190. 'date': datetime(2000, 1, 1),
  191. 'amount': '-200.00',
  192. 'name': 'INVOICE 42-01',
  193. 'note': 'TRANSFER-123456789: Sent money to John Doe',
  194. 'partner_name': 'John Doe',
  195. 'account_number': 'XX00 0000 0000 0000',
  196. 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800',
  197. })
  198. self.assertEqual(lines[1], {
  199. 'date': datetime(2000, 1, 1),
  200. 'amount': '-0.60',
  201. 'name': 'Fee for TRANSFER-123456789',
  202. 'note': 'Transaction fee for TRANSFER-123456789',
  203. 'partner_name': 'TransferWise',
  204. 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
  205. })
  206. def test_transaction_parse_3(self):
  207. lines = self.transferwise_parse_transaction("""{
  208. "type": "DEBIT",
  209. "date": "2000-01-01T00:00:00.000Z",
  210. "amount": {
  211. "value": -123.45,
  212. "currency": "USD"
  213. },
  214. "totalFees": {
  215. "value": 0.00,
  216. "currency": "USD"
  217. },
  218. "details": {
  219. "type": "CARD",
  220. "description":
  221. "Card transaction of 1234.56 USD issued by Paypal *XX CITY",
  222. "amount": {
  223. "value": 1234.56,
  224. "currency": "USD"
  225. },
  226. "category": "Professional Services not elsewh",
  227. "merchant": {
  228. "name": "Paypal *XX",
  229. "firstLine": null,
  230. "postCode": "12345",
  231. "city": "CITY",
  232. "state": null,
  233. "country": "GB",
  234. "category": "Professional Services not elsewh"
  235. }
  236. },
  237. "exchangeDetails": null,
  238. "runningBalance": {
  239. "value": 0.00,
  240. "currency": "USD"
  241. },
  242. "referenceNumber": "CARD-123456789"
  243. }""")
  244. self.assertEqual(len(lines), 1)
  245. self.assertEqual(lines[0], {
  246. 'date': datetime(2000, 1, 1),
  247. 'amount': '-123.45',
  248. 'name': (
  249. 'Card transaction of 1234.56 USD issued by Paypal *XX CITY'
  250. ),
  251. 'note': (
  252. 'CARD-123456789: Card transaction of 1234.56 USD issued by '
  253. 'Paypal *XX CITY'
  254. ),
  255. 'partner_name': 'Paypal *XX',
  256. 'unique_import_id': 'DEBIT-CARD-123456789-946684800',
  257. })
  258. def test_transaction_parse_4(self):
  259. lines = self.transferwise_parse_transaction("""{
  260. "type": "DEBIT",
  261. "date": "2000-01-01T00:00:00.000Z",
  262. "amount": {
  263. "value": -456.78,
  264. "currency": "EUR"
  265. },
  266. "totalFees": {
  267. "value": 1.23,
  268. "currency": "EUR"
  269. },
  270. "details": {
  271. "type": "CARD",
  272. "description":
  273. "Card transaction of 1234.56 USD issued by Paypal *XX CITY",
  274. "amount": {
  275. "value": 1234.56,
  276. "currency": "USD"
  277. },
  278. "category": "Professional Services not elsewh",
  279. "merchant": {
  280. "name": "Paypal *XX",
  281. "firstLine": null,
  282. "postCode": "12345",
  283. "city": "CITY",
  284. "state": null,
  285. "country": "GB",
  286. "category": "Professional Services not elsewh"
  287. }
  288. },
  289. "exchangeDetails": {
  290. "toAmount": {
  291. "value": 567.89,
  292. "currency": "USD"
  293. },
  294. "fromAmount": {
  295. "value": 456.78,
  296. "currency": "EUR"
  297. },
  298. "rate": 1.12260
  299. },
  300. "runningBalance": {
  301. "value": 0.00,
  302. "currency": "EUR"
  303. },
  304. "referenceNumber": "CARD-123456789"
  305. }""")
  306. self.assertEqual(len(lines), 2)
  307. self.assertEqual(lines[0], {
  308. 'date': datetime(2000, 1, 1),
  309. 'amount': '-455.55',
  310. 'name': (
  311. 'Card transaction of 1234.56 USD issued by Paypal *XX CITY'
  312. ),
  313. 'note': (
  314. 'CARD-123456789: Card transaction of 1234.56 USD issued by'
  315. ' Paypal *XX CITY'
  316. ),
  317. 'partner_name': 'Paypal *XX',
  318. 'unique_import_id': 'DEBIT-CARD-123456789-946684800',
  319. 'amount_currency': '-567.89',
  320. 'currency_id': self.currency_usd.id,
  321. })
  322. self.assertEqual(lines[1], {
  323. 'date': datetime(2000, 1, 1),
  324. 'amount': '-1.23',
  325. 'name': 'Fee for CARD-123456789',
  326. 'note': 'Transaction fee for CARD-123456789',
  327. 'partner_name': 'TransferWise',
  328. 'unique_import_id': 'DEBIT-CARD-123456789-946684800-FEE',
  329. })
  330. def test_transaction_parse_5(self):
  331. lines = self.transferwise_parse_transaction("""{
  332. "type": "DEBIT",
  333. "date": "2000-01-01T00:00:00.000Z",
  334. "amount": {
  335. "value": -270.55,
  336. "currency": "EUR"
  337. },
  338. "totalFees": {
  339. "value": 5.21,
  340. "currency": "EUR"
  341. },
  342. "details": {
  343. "type": "TRANSFER",
  344. "description": "Sent money to Jane Doe",
  345. "recipient": {
  346. "name": "Jane Doe",
  347. "bankAccount": "(ADBCDEF) 0000000000000000"
  348. },
  349. "paymentReference": "Invoice A from DD MMM YYYY"
  350. },
  351. "exchangeDetails": {
  352. "toAmount": {
  353. "value": 297.00,
  354. "currency": "USD"
  355. },
  356. "fromAmount": {
  357. "value": 265.34,
  358. "currency": "EUR"
  359. },
  360. "rate": 1.11930
  361. },
  362. "runningBalance": {
  363. "value": 2360.43,
  364. "currency": "EUR"
  365. },
  366. "referenceNumber": "TRANSFER-123456789"
  367. }""")
  368. self.assertEqual(len(lines), 2)
  369. self.assertEqual(lines[0], {
  370. 'date': datetime(2000, 1, 1),
  371. 'name': 'Invoice A from DD MMM YYYY',
  372. 'note': 'TRANSFER-123456789: Sent money to Jane Doe',
  373. 'partner_name': 'Jane Doe',
  374. 'account_number': '(ADBCDEF) 0000000000000000',
  375. 'amount': '-265.34',
  376. 'amount_currency': '-297.00',
  377. 'currency_id': self.currency_usd.id,
  378. 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800',
  379. })
  380. self.assertEqual(lines[1], {
  381. 'date': datetime(2000, 1, 1),
  382. 'name': 'Fee for TRANSFER-123456789',
  383. 'note': 'Transaction fee for TRANSFER-123456789',
  384. 'partner_name': 'TransferWise',
  385. 'amount': '-5.21',
  386. 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
  387. })
  388. def test_transaction_parse_6(self):
  389. lines = self.transferwise_parse_transaction("""{
  390. "type": "CREDIT",
  391. "date": "2000-01-01T00:00:00.000Z",
  392. "amount": {
  393. "value": 5000.00,
  394. "currency": "EUR"
  395. },
  396. "totalFees": {
  397. "value": 0.00,
  398. "currency": "EUR"
  399. },
  400. "details": {
  401. "type": "MONEY_ADDED",
  402. "description": "Topped up balance"
  403. },
  404. "exchangeDetails": null,
  405. "runningBalance": {
  406. "value": 7071.13,
  407. "currency": "EUR"
  408. },
  409. "referenceNumber": "TRANSFER-123456789"
  410. }""")
  411. self.assertEqual(len(lines), 1)
  412. self.assertEqual(lines[0], {
  413. 'date': datetime(2000, 1, 1),
  414. 'name': 'Topped up balance',
  415. 'note': 'TRANSFER-123456789: Topped up balance',
  416. 'amount': '5000.00',
  417. 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800',
  418. })
  419. def test_transaction_parse_7(self):
  420. lines = self.transferwise_parse_transaction("""{
  421. "type": "CREDIT",
  422. "date": "2000-01-01T00:00:00.000Z",
  423. "amount": {
  424. "value": 6.93,
  425. "currency": "EUR"
  426. },
  427. "totalFees": {
  428. "value": 0.00,
  429. "currency": "EUR"
  430. },
  431. "details": {
  432. "type": "CONVERSION",
  433. "description": "Converted 7.93 USD to 6.93 EUR",
  434. "sourceAmount": {
  435. "value": 7.93,
  436. "currency": "USD"
  437. },
  438. "targetAmount": {
  439. "value": 6.93,
  440. "currency": "EUR"
  441. },
  442. "rate": 0.87944162
  443. },
  444. "exchangeDetails": {
  445. "toAmount": {
  446. "value": 6.93,
  447. "currency": "EUR"
  448. },
  449. "fromAmount": {
  450. "value": 7.93,
  451. "currency": "USD"
  452. },
  453. "rate": 0.87944
  454. },
  455. "runningBalance": {
  456. "value": 255.00,
  457. "currency": "EUR"
  458. },
  459. "referenceNumber": "BALANCE-123456789"
  460. }""")
  461. self.assertEqual(len(lines), 1)
  462. self.assertEqual(lines[0], {
  463. 'date': datetime(2000, 1, 1),
  464. 'name': 'Converted 7.93 USD to 6.93 EUR',
  465. 'note': 'BALANCE-123456789: Converted 7.93 USD to 6.93 EUR',
  466. 'amount': '6.93',
  467. 'amount_currency': '7.93',
  468. 'currency_id': self.currency_usd.id,
  469. 'unique_import_id': 'CREDIT-BALANCE-123456789-946684800',
  470. })
  471. def test_transaction_parse_8(self):
  472. lines = self.transferwise_parse_transaction("""{
  473. "type": "DEBIT",
  474. "date": "2000-01-01T00:00:00.000Z",
  475. "amount": {
  476. "value": -7.93,
  477. "currency": "USD"
  478. },
  479. "totalFees": {
  480. "value": 0.05,
  481. "currency": "USD"
  482. },
  483. "details": {
  484. "type": "CONVERSION",
  485. "description": "Converted 7.93 USD to 6.93 EUR",
  486. "sourceAmount": {
  487. "value": 7.93,
  488. "currency": "USD"
  489. },
  490. "targetAmount": {
  491. "value": 6.93,
  492. "currency": "EUR"
  493. },
  494. "rate": 0.87944162
  495. },
  496. "exchangeDetails": {
  497. "toAmount": {
  498. "value": 6.93,
  499. "currency": "EUR"
  500. },
  501. "fromAmount": {
  502. "value": 7.93,
  503. "currency": "USD"
  504. },
  505. "rate": 0.87944
  506. },
  507. "runningBalance": {
  508. "value": 0.00,
  509. "currency": "USD"
  510. },
  511. "referenceNumber": "BALANCE-123456789"
  512. }""")
  513. self.assertEqual(len(lines), 2)
  514. self.assertEqual(lines[0], {
  515. 'date': datetime(2000, 1, 1),
  516. 'name': 'Converted 7.93 USD to 6.93 EUR',
  517. 'note': 'BALANCE-123456789: Converted 7.93 USD to 6.93 EUR',
  518. 'amount': '-7.88',
  519. 'amount_currency': '-6.93',
  520. 'currency_id': self.currency_eur.id,
  521. 'unique_import_id': 'DEBIT-BALANCE-123456789-946684800',
  522. })
  523. self.assertEqual(lines[1], {
  524. 'date': datetime(2000, 1, 1),
  525. 'name': 'Fee for BALANCE-123456789',
  526. 'note': 'Transaction fee for BALANCE-123456789',
  527. 'amount': '-0.05',
  528. 'partner_name': 'TransferWise',
  529. 'unique_import_id': 'DEBIT-BALANCE-123456789-946684800-FEE',
  530. })