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.

175 lines
6.4 KiB

  1. # Copyright 2020 Florent de Labarre
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
  3. import requests
  4. import json
  5. from datetime import datetime
  6. import pytz
  7. from odoo import _, api, fields, models
  8. from odoo.exceptions import UserError
  9. from odoo.addons.base.models.res_bank import sanitize_account_number
  10. QONTO_ENDPOINT = "https://thirdparty.qonto.eu/v2"
  11. class OnlineBankStatementProviderQonto(models.Model):
  12. _inherit = "online.bank.statement.provider"
  13. @api.model
  14. def _get_available_services(self):
  15. return super()._get_available_services() + [
  16. ("qonto", "Qonto.eu"),
  17. ]
  18. def _obtain_statement_data(self, date_since, date_until):
  19. self.ensure_one()
  20. if self.service != "qonto":
  21. return super()._obtain_statement_data(
  22. date_since,
  23. date_until,
  24. )
  25. return self._qonto_obtain_statement_data(date_since, date_until)
  26. def _get_statement_date(self, date_since, date_until):
  27. self.ensure_one()
  28. if self.service != "qonto":
  29. return super()._get_statement_date(
  30. date_since,
  31. date_until,
  32. )
  33. return date_since.astimezone(pytz.timezone("Europe/Paris")).date()
  34. #########
  35. # qonto #
  36. #########
  37. def _qonto_header(self):
  38. self.ensure_one()
  39. if self.username and self.password:
  40. return {"Authorization": "%s:%s" % (self.username, self.password)}
  41. raise UserError(_("Please fill login and key"))
  42. def _qonto_get_slug(self):
  43. self.ensure_one()
  44. url = QONTO_ENDPOINT + "/organizations/%7Bid%7D"
  45. response = requests.get(url, verify=False, headers=self._qonto_header())
  46. if response.status_code == 200:
  47. data = json.loads(response.text)
  48. res = {}
  49. for account in data.get("organization", {}).get("bank_accounts", []):
  50. iban = sanitize_account_number(account.get("iban", ""))
  51. res[iban] = account.get("slug")
  52. return res
  53. raise UserError(_("%s \n\n %s") % (response.status_code, response.text))
  54. def _qonto_obtain_transactions(self, slug, date_since, date_until):
  55. self.ensure_one()
  56. url = QONTO_ENDPOINT + "/transactions"
  57. params = {"slug": slug, "iban": self.account_number}
  58. # settled_at_to param isn't well formatted (ISO 8601) or year is out of range".
  59. # We set the last day of the year in such case.
  60. if date_since and date_until and date_since.year != date_until.year:
  61. date_until = fields.Datetime.from_string(
  62. "%s-12-31 23:59:59" % date_since.year
  63. )
  64. if date_since:
  65. params["settled_at_from"] = (
  66. date_since.replace(microsecond=0).isoformat() + "Z"
  67. )
  68. if date_until:
  69. if date_until == datetime(2022, 1, 1):
  70. date_until = datetime(2021, 12, 31)
  71. params["settled_at_to"] = (
  72. date_until.replace(microsecond=0).isoformat() + "Z"
  73. )
  74. transactions = []
  75. current_page = 1
  76. total_pages = 1
  77. while current_page <= total_pages:
  78. params["current_page"] = current_page
  79. data = self._qonto_get_transactions(url, params)
  80. transactions.extend(data.get("transactions", []))
  81. total_pages = data["meta"]["total_pages"]
  82. current_page += 1
  83. return transactions
  84. def _qonto_get_transactions(self, url, params):
  85. response = requests.get(
  86. url, verify=False, params=params, headers=self._qonto_header()
  87. )
  88. if response.status_code == 200:
  89. return json.loads(response.text)
  90. raise UserError(_("%s \n\n %s") % (response.status_code, response.text))
  91. def _qonto_prepare_statement_line(
  92. self, transaction, sequence, journal_currency, currencies_code2id
  93. ):
  94. date = datetime.strptime(transaction["settled_at"], "%Y-%m-%dT%H:%M:%S.%fZ")
  95. side = 1 if transaction["side"] == "credit" else -1
  96. name = transaction["label"] or "/"
  97. if transaction["reference"]:
  98. name = "%s %s" % (name, transaction["reference"])
  99. vals_line = {
  100. "sequence": sequence,
  101. "date": date,
  102. "name": name,
  103. "ref": transaction["reference"],
  104. "unique_import_id": transaction["transaction_id"],
  105. "amount": transaction["amount"] * side,
  106. }
  107. if not transaction["local_currency"]:
  108. raise UserError(
  109. _(
  110. "Transaction ID %s has not local_currency. "
  111. "This should never happen."
  112. )
  113. % transaction["transaction_id"]
  114. )
  115. if transaction["local_currency"] not in currencies_code2id:
  116. raise UserError(
  117. _("Currency %s used in transaction ID %s doesn't exist " "in Odoo.")
  118. % (transaction["local_currency"], transaction["transaction_id"])
  119. )
  120. line_currency_id = currencies_code2id[transaction["local_currency"]]
  121. if journal_currency.id != line_currency_id:
  122. vals_line.update(
  123. {
  124. "currency_id": line_currency_id,
  125. "amount_currency": transaction["local_amount"] * side,
  126. }
  127. )
  128. return vals_line
  129. def _qonto_obtain_statement_data(self, date_since, date_until):
  130. self.ensure_one()
  131. journal = self.journal_id
  132. slugs = self._qonto_get_slug()
  133. slug = slugs.get(self.account_number)
  134. if not slug:
  135. raise UserError(
  136. _("Qonto : wrong configuration, unknow account %s")
  137. % journal.bank_account_id.acc_number
  138. )
  139. transactions = self._qonto_obtain_transactions(slug, date_since, date_until)
  140. journal_currency = journal.currency_id or journal.company_id.currency_id
  141. all_currencies = self.env["res.currency"].search_read([], ["name"])
  142. currencies_code2id = dict([(x["name"], x["id"]) for x in all_currencies])
  143. new_transactions = []
  144. sequence = 0
  145. for transaction in transactions:
  146. sequence += 1
  147. vals_line = self._qonto_prepare_statement_line(
  148. transaction, sequence, journal_currency, currencies_code2id
  149. )
  150. new_transactions.append(vals_line)
  151. if new_transactions:
  152. return new_transactions, {}
  153. return