From 70ed8e7e3028e5291318bbb75b0f0f600e978e05 Mon Sep 17 00:00:00 2001 From: Florent de Labarre Date: Thu, 16 Apr 2020 16:40:42 +0200 Subject: [PATCH 1/3] [ADD] account_bank_statement_import_online_qonto --- .../README.rst | 103 ++++++++++++ .../__init__.py | 1 + .../__manifest__.py | 14 ++ ...unt_bank_statement_import_online_qonto.pot | 63 +++++++ .../models/__init__.py | 4 + .../online_bank_statement_provider_qonto.py | 157 ++++++++++++++++++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../tests/__init__.py | 3 + ...ount_bank_statement_import_online_qonto.py | 120 +++++++++++++ .../view/online_bank_statement_provider.xml | 16 ++ 10 files changed, 481 insertions(+) create mode 100644 account_bank_statement_import_online_qonto/README.rst create mode 100644 account_bank_statement_import_online_qonto/__init__.py create mode 100644 account_bank_statement_import_online_qonto/__manifest__.py create mode 100644 account_bank_statement_import_online_qonto/i18n/account_bank_statement_import_online_qonto.pot create mode 100644 account_bank_statement_import_online_qonto/models/__init__.py create mode 100644 account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py create mode 100644 account_bank_statement_import_online_qonto/static/description/icon.png create mode 100644 account_bank_statement_import_online_qonto/tests/__init__.py create mode 100644 account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py create mode 100644 account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml diff --git a/account_bank_statement_import_online_qonto/README.rst b/account_bank_statement_import_online_qonto/README.rst new file mode 100644 index 00000000..3c6f16fc --- /dev/null +++ b/account_bank_statement_import_online_qonto/README.rst @@ -0,0 +1,103 @@ +============================= +Online Bank Statements: Qonto +============================= + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github + :target: https://github.com/OCA/bank-statement-import/tree/12.0/account_bank_statement_import_online_qonto + :alt: OCA/bank-statement-import +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/bank-statement-import-12-0/bank-statement-import-12-0-account_bank_statement_import_online_qonto + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/174/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module provides online bank statements from Qonto Bank. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure online bank statements provider: + +#. Go to *Invoicing > Configuration > Bank Accounts* +#. Open bank account to configure and edit it +#. Set *Bank Feeds* to *Online* +#. Select *Qonto.eu* as online bank statements provider in + *Online Bank Statements (OCA)* section +#. Save the bank account +#. Click on provider and configure provider-specific settings. + +or, alternatively: + +#. Go to *Invoicing > Overview* +#. Open settings of the corresponding journal account +#. Switch to *Bank Account* tab +#. Set *Bank Feeds* to *Online* +#. Select *Qonto.eu* as online bank statements provider in + *Online Bank Statements (OCA)* section +#. Save the bank account +#. Click on provider and configure provider-specific settings. + +To obtain *Login* and *Key*: + +#. Open `Qonto.eu `_. +#. Go to *Settings* +#. Go to *Api Integration*, click *Generate Key* + +Usage +===== + +To pull historical bank statements: + +#. Go to *Invoicing > Configuration > Bank Accounts* +#. Select specific bank accounts +#. Launch *Actions > Online Bank Statements Pull Wizard* +#. Configure date interval and click *Pull* + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Florent de Labarre + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/bank-statement-import `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_bank_statement_import_online_qonto/__init__.py b/account_bank_statement_import_online_qonto/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/account_bank_statement_import_online_qonto/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_bank_statement_import_online_qonto/__manifest__.py b/account_bank_statement_import_online_qonto/__manifest__.py new file mode 100644 index 00000000..dfbc1d01 --- /dev/null +++ b/account_bank_statement_import_online_qonto/__manifest__.py @@ -0,0 +1,14 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Online Bank Statements: Qonto.eu", + "version": "12.0.1.0.0", + "category": "Account", + "website": "https://github.com/OCA/bank-statement-import", + "author": "Florent de Labarre, Odoo Community Association (OCA)", + "license": "AGPL-3", + "installable": True, + "depends": ["account_bank_statement_import_online"], + "data": [ + "view/online_bank_statement_provider.xml" + ], +} diff --git a/account_bank_statement_import_online_qonto/i18n/account_bank_statement_import_online_qonto.pot b/account_bank_statement_import_online_qonto/i18n/account_bank_statement_import_online_qonto.pot new file mode 100644 index 00000000..c9402cdb --- /dev/null +++ b/account_bank_statement_import_online_qonto/i18n/account_bank_statement_import_online_qonto.pot @@ -0,0 +1,63 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_bank_statement_import_online_qonto +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_bank_statement_import_online_qonto +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:63 +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:92 +#, python-format +msgid "%s \n" +"\n" +" %s" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:115 +#, python-format +msgid "Currency %s used in transaction ID %s doesn't exist in Odoo." +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: model_terms:ir.ui.view,arch_db:account_bank_statement_import_online_qonto.online_bank_statement_provider_form +msgid "Key" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: model_terms:ir.ui.view,arch_db:account_bank_statement_import_online_qonto.online_bank_statement_provider_form +msgid "Login" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: model:ir.model,name:account_bank_statement_import_online_qonto.model_online_bank_statement_provider +msgid "Online Bank Statement Provider" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:50 +#, python-format +msgid "Please fill login and key" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:138 +#, python-format +msgid "Qonto : wrong configuration, unknow account %s" +msgstr "" + +#. module: account_bank_statement_import_online_qonto +#: code:addons/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py:111 +#, python-format +msgid "Transaction ID %s has not local_currency. This should never happen." +msgstr "" + diff --git a/account_bank_statement_import_online_qonto/models/__init__.py b/account_bank_statement_import_online_qonto/models/__init__.py new file mode 100644 index 00000000..7a092b3c --- /dev/null +++ b/account_bank_statement_import_online_qonto/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2020 Florent de Labarre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import online_bank_statement_provider_qonto diff --git a/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py new file mode 100644 index 00000000..e421fc77 --- /dev/null +++ b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py @@ -0,0 +1,157 @@ +# Copyright 2020 Florent de Labarre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import requests +import json +from datetime import datetime +import pytz + +from odoo import api, models, _ +from odoo.exceptions import UserError +from odoo.addons.base.models.res_bank import sanitize_account_number + +QONTO_ENDPOINT = 'https://thirdparty.qonto.eu/v2' + + +class OnlineBankStatementProviderQonto(models.Model): + _inherit = 'online.bank.statement.provider' + + @api.model + def _get_available_services(self): + return super()._get_available_services() + [ + ('qonto', 'Qonto.eu'), + ] + + def _obtain_statement_data(self, date_since, date_until): + self.ensure_one() + if self.service != 'qonto': + return super()._obtain_statement_data( + date_since, + date_until, + ) + return self._qonto_obtain_statement_data(date_since, date_until) + + def _get_statement_date(self, date_since, date_until): + self.ensure_one() + if self.service != 'qonto': + return super()._get_statement_date( + date_since, + date_until, + ) + return date_since.astimezone(pytz.timezone('Europe/Paris')).date() + + ######### + # qonto # + ######### + + def _qonto_header(self): + self.ensure_one() + if self.username and self.password: + return {'Authorization': '%s:%s' % (self.username, self.password)} + raise UserError(_('Please fill login and key')) + + def _qonto_get_slug(self): + self.ensure_one() + url = QONTO_ENDPOINT + '/organizations/%7Bid%7D' + response = requests.get(url, verify=False, headers=self._qonto_header()) + if response.status_code == 200: + data = json.loads(response.text) + res = {} + for account in data.get('organization', {}).get('bank_accounts', []): + iban = sanitize_account_number(account.get('iban', '')) + res[iban] = account.get('slug') + return res + raise UserError(_('%s \n\n %s') % (response.status_code, response.text)) + + def _qonto_obtain_transactions(self, slug, date_since, date_until): + self.ensure_one() + url = QONTO_ENDPOINT + '/transactions' + params = {'slug': slug, 'iban': self.account_number} + + if date_since: + params['settled_at_from'] = date_since.replace( + microsecond=0).isoformat() + 'Z' + if date_until: + params['settled_at_to'] = date_until.replace( + microsecond=0).isoformat() + 'Z' + transactions = [] + current_page = 1 + total_pages = 1 + while current_page <= total_pages: + params['current_page'] = current_page + data = self._qonto_get_transactions(url, params) + transactions.extend(data.get('transactions', [])) + total_pages = data['meta']['total_pages'] + current_page += 1 + return transactions + + def _qonto_get_transactions(self, url, params): + response = requests.get(url, verify=False, params=params, + headers=self._qonto_header()) + if response.status_code == 200: + return json.loads(response.text) + raise UserError(_('%s \n\n %s') % (response.status_code, response.text)) + + def _qonto_prepare_statement_line( + self, transaction, sequence, journal_currency, currencies_code2id): + date = datetime.strptime(transaction['settled_at'], '%Y-%m-%dT%H:%M:%S.%fZ') + side = 1 if transaction['side'] == 'credit' else -1 + name = transaction['label'] or '/' + if transaction['reference']: + name = '%s %s' % (name, transaction['reference']) + vals_line = { + 'sequence': sequence, + 'date': date, + 'name': name, + 'ref': transaction['reference'], + 'unique_import_id': transaction['transaction_id'], + 'amount': transaction['amount'] * side, + } + + if not transaction['local_currency']: + raise UserError(_( + "Transaction ID %s has not local_currency. " + "This should never happen.") % transaction['transaction_id']) + if transaction['local_currency'] not in currencies_code2id: + raise UserError(_( + "Currency %s used in transaction ID %s doesn't exist " + "in Odoo.") % ( + transaction['local_currency'], + transaction['transaction_id'])) + + line_currency_id = currencies_code2id[transaction['local_currency']] + + if journal_currency.id != line_currency_id: + vals_line.update({ + 'currency_id': line_currency_id, + 'amount_currency': transaction['local_amount'] * side, + }) + return vals_line + + def _qonto_obtain_statement_data(self, date_since, date_until): + self.ensure_one() + journal = self.journal_id + + slugs = self._qonto_get_slug() + slug = slugs.get(self.account_number) + if not slug: + raise UserError( + _('Qonto : wrong configuration, unknow account %s') + % journal.bank_account_id.acc_number) + + transactions = self._qonto_obtain_transactions(slug, date_since, date_until) + + journal_currency = journal.currency_id or journal.company_id.currency_id + + all_currencies = self.env['res.currency'].search_read([], ['name']) + currencies_code2id = dict([(x['name'], x['id']) for x in all_currencies]) + new_transactions = [] + sequence = 0 + for transaction in transactions: + sequence += 1 + vals_line = self._qonto_prepare_statement_line( + transaction, sequence, journal_currency, currencies_code2id) + new_transactions.append(vals_line) + + if new_transactions: + return new_transactions, {} + return diff --git a/account_bank_statement_import_online_qonto/static/description/icon.png b/account_bank_statement_import_online_qonto/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_bank_statement_import_online_qonto/tests/__init__.py b/account_bank_statement_import_online_qonto/tests/__init__.py new file mode 100644 index 00000000..148eb15d --- /dev/null +++ b/account_bank_statement_import_online_qonto/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_account_bank_statement_import_online_qonto diff --git a/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py b/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py new file mode 100644 index 00000000..98b38560 --- /dev/null +++ b/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py @@ -0,0 +1,120 @@ +# Copyright 2020 Florent de Labarre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from datetime import datetime +from unittest import mock + +from odoo import fields +from odoo.tests import common + +_module_ns = 'odoo.addons.account_bank_statement_import_online_qonto' +_provider_class = ( + _module_ns + + '.models.online_bank_statement_provider_qonto' + + '.OnlineBankStatementProviderQonto' +) + + +class TestAccountBankAccountStatementImportOnlineQonto( + common.TransactionCase +): + + def setUp(self): + super().setUp() + + self.now = fields.Datetime.now() + self.currency_eur = self.env.ref('base.EUR') + self.currency_usd = self.env.ref('base.USD') + self.AccountJournal = self.env['account.journal'] + self.ResPartnerBank = self.env['res.partner.bank'] + self.OnlineBankStatementProvider = self.env[ + 'online.bank.statement.provider' + ] + self.AccountBankStatement = self.env['account.bank.statement'] + self.AccountBankStatementLine = self.env['account.bank.statement.line'] + + self.bank_account = self.ResPartnerBank.create( + {'acc_number': 'FR0214508000302245362775K46', + 'partner_id': self.env.user.company_id.partner_id.id}) + self.journal = self.AccountJournal.create({ + 'name': 'Bank', + 'type': 'bank', + 'code': 'BANK', + 'currency_id': self.currency_eur.id, + 'bank_statements_source': 'online', + 'online_bank_statement_provider': 'qonto', + 'bank_account_id': self.bank_account.id, + }) + self.provider = self.journal.online_bank_statement_provider_id + + self.mock_slug = lambda: mock.patch( + _provider_class + '._qonto_get_slug', + return_value={'FR0214508000302245362775K46': 'qonto-1234-bank-account-1'}, + ) + self.mock_transaction = lambda: mock.patch( + _provider_class + '._qonto_get_transactions', + return_value={ + "transactions": [ + {"transaction_id": "qonto-1234-1-transaction-3", + "amount": 1200.0, + "amount_cents": 120000, + "attachment_ids": [], + "local_amount": 1200.0, + "local_amount_cents": 120000, + "side": "credit", + "operation_type": "income", + "currency": "EUR", + "local_currency": "EUR", + "label": "INVOICE A", + "settled_at": "2020-04-16T07:01:55.503Z", + "emitted_at": "2020-04-16T05:01:55.000Z", + "updated_at": "2020-04-16T07:04:02.792Z", + "status": "completed", + "note": None, + "reference": "Ref 1233", + "vat_amount": None, + "vat_amount_cents": None, + "vat_rate": None, + "initiator_id": None, + "label_ids": [], + "attachment_lost": False, + "attachment_required": True}, + {"transaction_id": "qonto-1234-1-transaction-2", + "amount": 1128.36, "amount_cents": 112836, + "attachment_ids": [], + "local_amount": 1128.36, + "local_amount_cents": 112836, + "side": "debit", + "operation_type": "transfer", + "currency": "EUR", + "local_currency": "EUR", + "label": "BILL A", + "settled_at": "2020-04-16T07:00:30.979Z", + "emitted_at": "2020-04-15T18:22:30.296Z", + "updated_at": "2020-04-16T07:03:01.125Z", + "status": "completed", + "note": None, + "reference": "Invoice", + "vat_amount": None, + "vat_amount_cents": None, + "vat_rate": None, + "initiator_id": "9b783957-85a6-404a-8320-a298781cb5fa", + "label_ids": [], + "attachment_lost": False, + "attachment_required": True}], + "meta": {"current_page": 1, + "next_page": None, + "prev_page": None, + "total_pages": 1, + "total_count": 2, + "per_page": 100}}, + ) + + def test_qonto(self): + with self.mock_transaction(), self.mock_slug(): + lines, statement_values = self.provider._obtain_statement_data( + datetime(2020, 4, 15), + datetime(2020, 4, 17), + ) + + self.assertEqual(len(lines), 2) diff --git a/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml b/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml new file mode 100644 index 00000000..7bc06c91 --- /dev/null +++ b/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml @@ -0,0 +1,16 @@ + + + + online.bank.statement.provider.form + online.bank.statement.provider + + + + + + + + + + + From dba95fb13f3dc23a10fe6863474881723a66d718 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sun, 10 Apr 2022 15:56:13 +0200 Subject: [PATCH 2/3] [IMP] account_bank_statement_import_online_qonto: black, isort, prettier --- .../__manifest__.py | 4 +- .../online_bank_statement_provider_qonto.py | 143 +++++++------- ...ount_bank_statement_import_online_qonto.py | 178 +++++++++--------- .../view/online_bank_statement_provider.xml | 9 +- ...account_bank_statement_import_online_qonto | 1 + .../setup.py | 6 + 6 files changed, 182 insertions(+), 159 deletions(-) create mode 120000 setup/account_bank_statement_import_online_qonto/odoo/addons/account_bank_statement_import_online_qonto create mode 100644 setup/account_bank_statement_import_online_qonto/setup.py diff --git a/account_bank_statement_import_online_qonto/__manifest__.py b/account_bank_statement_import_online_qonto/__manifest__.py index dfbc1d01..3f532eea 100644 --- a/account_bank_statement_import_online_qonto/__manifest__.py +++ b/account_bank_statement_import_online_qonto/__manifest__.py @@ -8,7 +8,5 @@ "license": "AGPL-3", "installable": True, "depends": ["account_bank_statement_import_online"], - "data": [ - "view/online_bank_statement_provider.xml" - ], + "data": ["view/online_bank_statement_provider.xml"], } diff --git a/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py index e421fc77..bad99979 100644 --- a/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py +++ b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py @@ -1,43 +1,39 @@ # Copyright 2020 Florent de Labarre # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -import requests import json from datetime import datetime -import pytz -from odoo import api, models, _ +import pytz +import requests + +from odoo import _, api, models from odoo.exceptions import UserError + from odoo.addons.base.models.res_bank import sanitize_account_number -QONTO_ENDPOINT = 'https://thirdparty.qonto.eu/v2' +QONTO_ENDPOINT = "https://thirdparty.qonto.eu/v2" class OnlineBankStatementProviderQonto(models.Model): - _inherit = 'online.bank.statement.provider' + _inherit = "online.bank.statement.provider" @api.model def _get_available_services(self): return super()._get_available_services() + [ - ('qonto', 'Qonto.eu'), + ("qonto", "Qonto.eu"), ] def _obtain_statement_data(self, date_since, date_until): self.ensure_one() - if self.service != 'qonto': - return super()._obtain_statement_data( - date_since, - date_until, - ) + if self.service != "qonto": + return super()._obtain_statement_data(date_since, date_until,) return self._qonto_obtain_statement_data(date_since, date_until) def _get_statement_date(self, date_since, date_until): self.ensure_one() - if self.service != 'qonto': - return super()._get_statement_date( - date_since, - date_until, - ) - return date_since.astimezone(pytz.timezone('Europe/Paris')).date() + if self.service != "qonto": + return super()._get_statement_date(date_since, date_until,) + return date_since.astimezone(pytz.timezone("Europe/Paris")).date() ######### # qonto # @@ -46,85 +42,94 @@ class OnlineBankStatementProviderQonto(models.Model): def _qonto_header(self): self.ensure_one() if self.username and self.password: - return {'Authorization': '%s:%s' % (self.username, self.password)} - raise UserError(_('Please fill login and key')) + return {"Authorization": "{}:{}".format(self.username, self.password)} + raise UserError(_("Please fill login and key")) def _qonto_get_slug(self): self.ensure_one() - url = QONTO_ENDPOINT + '/organizations/%7Bid%7D' + url = QONTO_ENDPOINT + "/organizations/%7Bid%7D" response = requests.get(url, verify=False, headers=self._qonto_header()) if response.status_code == 200: data = json.loads(response.text) res = {} - for account in data.get('organization', {}).get('bank_accounts', []): - iban = sanitize_account_number(account.get('iban', '')) - res[iban] = account.get('slug') + for account in data.get("organization", {}).get("bank_accounts", []): + iban = sanitize_account_number(account.get("iban", "")) + res[iban] = account.get("slug") return res - raise UserError(_('%s \n\n %s') % (response.status_code, response.text)) + raise UserError(_("%s \n\n %s") % (response.status_code, response.text)) def _qonto_obtain_transactions(self, slug, date_since, date_until): self.ensure_one() - url = QONTO_ENDPOINT + '/transactions' - params = {'slug': slug, 'iban': self.account_number} + url = QONTO_ENDPOINT + "/transactions" + params = {"slug": slug, "iban": self.account_number} if date_since: - params['settled_at_from'] = date_since.replace( - microsecond=0).isoformat() + 'Z' + params["settled_at_from"] = ( + date_since.replace(microsecond=0).isoformat() + "Z" + ) if date_until: - params['settled_at_to'] = date_until.replace( - microsecond=0).isoformat() + 'Z' + params["settled_at_to"] = ( + date_until.replace(microsecond=0).isoformat() + "Z" + ) transactions = [] current_page = 1 total_pages = 1 while current_page <= total_pages: - params['current_page'] = current_page + params["current_page"] = current_page data = self._qonto_get_transactions(url, params) - transactions.extend(data.get('transactions', [])) - total_pages = data['meta']['total_pages'] + transactions.extend(data.get("transactions", [])) + total_pages = data["meta"]["total_pages"] current_page += 1 return transactions def _qonto_get_transactions(self, url, params): - response = requests.get(url, verify=False, params=params, - headers=self._qonto_header()) + response = requests.get( + url, verify=False, params=params, headers=self._qonto_header() + ) if response.status_code == 200: return json.loads(response.text) - raise UserError(_('%s \n\n %s') % (response.status_code, response.text)) + raise UserError(_("%s \n\n %s") % (response.status_code, response.text)) def _qonto_prepare_statement_line( - self, transaction, sequence, journal_currency, currencies_code2id): - date = datetime.strptime(transaction['settled_at'], '%Y-%m-%dT%H:%M:%S.%fZ') - side = 1 if transaction['side'] == 'credit' else -1 - name = transaction['label'] or '/' - if transaction['reference']: - name = '%s %s' % (name, transaction['reference']) + self, transaction, sequence, journal_currency, currencies_code2id + ): + date = datetime.strptime(transaction["settled_at"], "%Y-%m-%dT%H:%M:%S.%fZ") + side = 1 if transaction["side"] == "credit" else -1 + name = transaction["label"] or "/" + if transaction["reference"]: + name = "{} {}".format(name, transaction["reference"]) vals_line = { - 'sequence': sequence, - 'date': date, - 'name': name, - 'ref': transaction['reference'], - 'unique_import_id': transaction['transaction_id'], - 'amount': transaction['amount'] * side, + "sequence": sequence, + "date": date, + "name": name, + "ref": transaction["reference"], + "unique_import_id": transaction["transaction_id"], + "amount": transaction["amount"] * side, } - if not transaction['local_currency']: - raise UserError(_( - "Transaction ID %s has not local_currency. " - "This should never happen.") % transaction['transaction_id']) - if transaction['local_currency'] not in currencies_code2id: - raise UserError(_( - "Currency %s used in transaction ID %s doesn't exist " - "in Odoo.") % ( - transaction['local_currency'], - transaction['transaction_id'])) + if not transaction["local_currency"]: + raise UserError( + _( + "Transaction ID %s has not local_currency. " + "This should never happen." + ) + % transaction["transaction_id"] + ) + if transaction["local_currency"] not in currencies_code2id: + raise UserError( + _("Currency %s used in transaction ID %s doesn't exist " "in Odoo.") + % (transaction["local_currency"], transaction["transaction_id"]) + ) - line_currency_id = currencies_code2id[transaction['local_currency']] + line_currency_id = currencies_code2id[transaction["local_currency"]] if journal_currency.id != line_currency_id: - vals_line.update({ - 'currency_id': line_currency_id, - 'amount_currency': transaction['local_amount'] * side, - }) + vals_line.update( + { + "currency_id": line_currency_id, + "amount_currency": transaction["local_amount"] * side, + } + ) return vals_line def _qonto_obtain_statement_data(self, date_since, date_until): @@ -135,21 +140,23 @@ class OnlineBankStatementProviderQonto(models.Model): slug = slugs.get(self.account_number) if not slug: raise UserError( - _('Qonto : wrong configuration, unknow account %s') - % journal.bank_account_id.acc_number) + _("Qonto : wrong configuration, unknow account %s") + % journal.bank_account_id.acc_number + ) transactions = self._qonto_obtain_transactions(slug, date_since, date_until) journal_currency = journal.currency_id or journal.company_id.currency_id - all_currencies = self.env['res.currency'].search_read([], ['name']) - currencies_code2id = dict([(x['name'], x['id']) for x in all_currencies]) + all_currencies = self.env["res.currency"].search_read([], ["name"]) + currencies_code2id = {x["name"]: x["id"] for x in all_currencies} new_transactions = [] sequence = 0 for transaction in transactions: sequence += 1 vals_line = self._qonto_prepare_statement_line( - transaction, sequence, journal_currency, currencies_code2id) + transaction, sequence, journal_currency, currencies_code2id + ) new_transactions.append(vals_line) if new_transactions: diff --git a/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py b/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py index 98b38560..e5c5209e 100644 --- a/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py +++ b/account_bank_statement_import_online_qonto/tests/test_account_bank_statement_import_online_qonto.py @@ -7,114 +7,122 @@ from unittest import mock from odoo import fields from odoo.tests import common -_module_ns = 'odoo.addons.account_bank_statement_import_online_qonto' +_module_ns = "odoo.addons.account_bank_statement_import_online_qonto" _provider_class = ( _module_ns - + '.models.online_bank_statement_provider_qonto' - + '.OnlineBankStatementProviderQonto' + + ".models.online_bank_statement_provider_qonto" + + ".OnlineBankStatementProviderQonto" ) -class TestAccountBankAccountStatementImportOnlineQonto( - common.TransactionCase -): - +class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase): def setUp(self): super().setUp() self.now = fields.Datetime.now() - self.currency_eur = self.env.ref('base.EUR') - self.currency_usd = self.env.ref('base.USD') - self.AccountJournal = self.env['account.journal'] - self.ResPartnerBank = self.env['res.partner.bank'] - self.OnlineBankStatementProvider = self.env[ - 'online.bank.statement.provider' - ] - self.AccountBankStatement = self.env['account.bank.statement'] - self.AccountBankStatementLine = self.env['account.bank.statement.line'] + self.currency_eur = self.env.ref("base.EUR") + self.currency_usd = self.env.ref("base.USD") + self.AccountJournal = self.env["account.journal"] + self.ResPartnerBank = self.env["res.partner.bank"] + self.OnlineBankStatementProvider = self.env["online.bank.statement.provider"] + self.AccountBankStatement = self.env["account.bank.statement"] + self.AccountBankStatementLine = self.env["account.bank.statement.line"] self.bank_account = self.ResPartnerBank.create( - {'acc_number': 'FR0214508000302245362775K46', - 'partner_id': self.env.user.company_id.partner_id.id}) - self.journal = self.AccountJournal.create({ - 'name': 'Bank', - 'type': 'bank', - 'code': 'BANK', - 'currency_id': self.currency_eur.id, - 'bank_statements_source': 'online', - 'online_bank_statement_provider': 'qonto', - 'bank_account_id': self.bank_account.id, - }) + { + "acc_number": "FR0214508000302245362775K46", + "partner_id": self.env.user.company_id.partner_id.id, + } + ) + self.journal = self.AccountJournal.create( + { + "name": "Bank", + "type": "bank", + "code": "BANK", + "currency_id": self.currency_eur.id, + "bank_statements_source": "online", + "online_bank_statement_provider": "qonto", + "bank_account_id": self.bank_account.id, + } + ) self.provider = self.journal.online_bank_statement_provider_id self.mock_slug = lambda: mock.patch( - _provider_class + '._qonto_get_slug', - return_value={'FR0214508000302245362775K46': 'qonto-1234-bank-account-1'}, + _provider_class + "._qonto_get_slug", + return_value={"FR0214508000302245362775K46": "qonto-1234-bank-account-1"}, ) self.mock_transaction = lambda: mock.patch( - _provider_class + '._qonto_get_transactions', + _provider_class + "._qonto_get_transactions", return_value={ "transactions": [ - {"transaction_id": "qonto-1234-1-transaction-3", - "amount": 1200.0, - "amount_cents": 120000, - "attachment_ids": [], - "local_amount": 1200.0, - "local_amount_cents": 120000, - "side": "credit", - "operation_type": "income", - "currency": "EUR", - "local_currency": "EUR", - "label": "INVOICE A", - "settled_at": "2020-04-16T07:01:55.503Z", - "emitted_at": "2020-04-16T05:01:55.000Z", - "updated_at": "2020-04-16T07:04:02.792Z", - "status": "completed", - "note": None, - "reference": "Ref 1233", - "vat_amount": None, - "vat_amount_cents": None, - "vat_rate": None, - "initiator_id": None, - "label_ids": [], - "attachment_lost": False, - "attachment_required": True}, - {"transaction_id": "qonto-1234-1-transaction-2", - "amount": 1128.36, "amount_cents": 112836, - "attachment_ids": [], - "local_amount": 1128.36, - "local_amount_cents": 112836, - "side": "debit", - "operation_type": "transfer", - "currency": "EUR", - "local_currency": "EUR", - "label": "BILL A", - "settled_at": "2020-04-16T07:00:30.979Z", - "emitted_at": "2020-04-15T18:22:30.296Z", - "updated_at": "2020-04-16T07:03:01.125Z", - "status": "completed", - "note": None, - "reference": "Invoice", - "vat_amount": None, - "vat_amount_cents": None, - "vat_rate": None, - "initiator_id": "9b783957-85a6-404a-8320-a298781cb5fa", - "label_ids": [], - "attachment_lost": False, - "attachment_required": True}], - "meta": {"current_page": 1, - "next_page": None, - "prev_page": None, - "total_pages": 1, - "total_count": 2, - "per_page": 100}}, + { + "transaction_id": "qonto-1234-1-transaction-3", + "amount": 1200.0, + "amount_cents": 120000, + "attachment_ids": [], + "local_amount": 1200.0, + "local_amount_cents": 120000, + "side": "credit", + "operation_type": "income", + "currency": "EUR", + "local_currency": "EUR", + "label": "INVOICE A", + "settled_at": "2020-04-16T07:01:55.503Z", + "emitted_at": "2020-04-16T05:01:55.000Z", + "updated_at": "2020-04-16T07:04:02.792Z", + "status": "completed", + "note": None, + "reference": "Ref 1233", + "vat_amount": None, + "vat_amount_cents": None, + "vat_rate": None, + "initiator_id": None, + "label_ids": [], + "attachment_lost": False, + "attachment_required": True, + }, + { + "transaction_id": "qonto-1234-1-transaction-2", + "amount": 1128.36, + "amount_cents": 112836, + "attachment_ids": [], + "local_amount": 1128.36, + "local_amount_cents": 112836, + "side": "debit", + "operation_type": "transfer", + "currency": "EUR", + "local_currency": "EUR", + "label": "BILL A", + "settled_at": "2020-04-16T07:00:30.979Z", + "emitted_at": "2020-04-15T18:22:30.296Z", + "updated_at": "2020-04-16T07:03:01.125Z", + "status": "completed", + "note": None, + "reference": "Invoice", + "vat_amount": None, + "vat_amount_cents": None, + "vat_rate": None, + "initiator_id": "9b783957-85a6-404a-8320-a298781cb5fa", + "label_ids": [], + "attachment_lost": False, + "attachment_required": True, + }, + ], + "meta": { + "current_page": 1, + "next_page": None, + "prev_page": None, + "total_pages": 1, + "total_count": 2, + "per_page": 100, + }, + }, ) def test_qonto(self): with self.mock_transaction(), self.mock_slug(): lines, statement_values = self.provider._obtain_statement_data( - datetime(2020, 4, 15), - datetime(2020, 4, 17), + datetime(2020, 4, 15), datetime(2020, 4, 17), ) self.assertEqual(len(lines), 2) diff --git a/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml b/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml index 7bc06c91..0862a5af 100644 --- a/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml +++ b/account_bank_statement_import_online_qonto/view/online_bank_statement_provider.xml @@ -3,12 +3,15 @@ online.bank.statement.provider.form online.bank.statement.provider - + - - + + diff --git a/setup/account_bank_statement_import_online_qonto/odoo/addons/account_bank_statement_import_online_qonto b/setup/account_bank_statement_import_online_qonto/odoo/addons/account_bank_statement_import_online_qonto new file mode 120000 index 00000000..7928bb92 --- /dev/null +++ b/setup/account_bank_statement_import_online_qonto/odoo/addons/account_bank_statement_import_online_qonto @@ -0,0 +1 @@ +../../../../account_bank_statement_import_online_qonto \ No newline at end of file diff --git a/setup/account_bank_statement_import_online_qonto/setup.py b/setup/account_bank_statement_import_online_qonto/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/account_bank_statement_import_online_qonto/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 76a3ef544fe66eead27ea16f59cbdec7a278c5d4 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sun, 10 Apr 2022 16:08:18 +0200 Subject: [PATCH 3/3] [MIG] account_bank_statement_import_online_qonto: Migration to 13.0 - Standard procedure - Module icon - Adjust naming and domain to .com, as stated in Qonto API page (https://api-doc.qonto.com/docs/business-api/ZG9jOjI5NjA5OQ-getting-started) - Better line label TT35884 --- .../README.rst | 31 +- .../__manifest__.py | 4 +- .../online_bank_statement_provider_qonto.py | 23 +- .../readme/CONFIGURE.rst | 26 + .../readme/CONTRIBUTORS.rst | 4 + .../readme/DESCRIPTION.rst | 1 + .../readme/USAGE.rst | 6 + .../static/description/icon.png | Bin 9455 -> 119787 bytes .../static/description/index.html | 465 ++++++++++++++++++ 9 files changed, 534 insertions(+), 26 deletions(-) create mode 100644 account_bank_statement_import_online_qonto/readme/CONFIGURE.rst create mode 100644 account_bank_statement_import_online_qonto/readme/CONTRIBUTORS.rst create mode 100644 account_bank_statement_import_online_qonto/readme/DESCRIPTION.rst create mode 100644 account_bank_statement_import_online_qonto/readme/USAGE.rst create mode 100644 account_bank_statement_import_online_qonto/static/description/index.html diff --git a/account_bank_statement_import_online_qonto/README.rst b/account_bank_statement_import_online_qonto/README.rst index 3c6f16fc..4d49d135 100644 --- a/account_bank_statement_import_online_qonto/README.rst +++ b/account_bank_statement_import_online_qonto/README.rst @@ -2,6 +2,11 @@ Online Bank Statements: Qonto ============================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta @@ -9,16 +14,16 @@ Online Bank Statements: Qonto :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github - :target: https://github.com/OCA/bank-statement-import/tree/12.0/account_bank_statement_import_online_qonto + :target: https://github.com/OCA/bank-statement-import/tree/13.0/account_bank_statement_import_online_qonto :alt: OCA/bank-statement-import .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/bank-statement-import-12-0/bank-statement-import-12-0-account_bank_statement_import_online_qonto + :target: https://translation.odoo-community.org/projects/bank-statement-import-13-0/bank-statement-import-13-0-account_bank_statement_import_online_qonto :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/174/12.0 + :target: https://runbot.odoo-community.org/runbot/174/13.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module provides online bank statements from Qonto Bank. @@ -35,7 +40,7 @@ To configure online bank statements provider: #. Go to *Invoicing > Configuration > Bank Accounts* #. Open bank account to configure and edit it #. Set *Bank Feeds* to *Online* -#. Select *Qonto.eu* as online bank statements provider in +#. Select *Qonto* as online bank statements provider in *Online Bank Statements (OCA)* section #. Save the bank account #. Click on provider and configure provider-specific settings. @@ -46,14 +51,14 @@ or, alternatively: #. Open settings of the corresponding journal account #. Switch to *Bank Account* tab #. Set *Bank Feeds* to *Online* -#. Select *Qonto.eu* as online bank statements provider in +#. Select *Qonto* as online bank statements provider in *Online Bank Statements (OCA)* section #. Save the bank account #. Click on provider and configure provider-specific settings. To obtain *Login* and *Key*: -#. Open `Qonto.eu `_. +#. Open `Qonto `__. #. Go to *Settings* #. Go to *Api Integration*, click *Generate Key* @@ -73,7 +78,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -85,6 +90,14 @@ Authors * Florent de Labarre +Contributors +~~~~~~~~~~~~ + +* Florent de Labarre +* `Tecnativa `__: + + * Pedro M. Baeza + Maintainers ~~~~~~~~~~~ @@ -98,6 +111,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/bank-statement-import `_ project on GitHub. +This module is part of the `OCA/bank-statement-import `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_bank_statement_import_online_qonto/__manifest__.py b/account_bank_statement_import_online_qonto/__manifest__.py index 3f532eea..0dacf02e 100644 --- a/account_bank_statement_import_online_qonto/__manifest__.py +++ b/account_bank_statement_import_online_qonto/__manifest__.py @@ -1,7 +1,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - "name": "Online Bank Statements: Qonto.eu", - "version": "12.0.1.0.0", + "name": "Online Bank Statements: Qonto", + "version": "13.0.1.0.0", "category": "Account", "website": "https://github.com/OCA/bank-statement-import", "author": "Florent de Labarre, Odoo Community Association (OCA)", diff --git a/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py index bad99979..9cad1f42 100644 --- a/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py +++ b/account_bank_statement_import_online_qonto/models/online_bank_statement_provider_qonto.py @@ -11,7 +11,7 @@ from odoo.exceptions import UserError from odoo.addons.base.models.res_bank import sanitize_account_number -QONTO_ENDPOINT = "https://thirdparty.qonto.eu/v2" +QONTO_ENDPOINT = "https://thirdparty.qonto.com/v2" class OnlineBankStatementProviderQonto(models.Model): @@ -20,7 +20,7 @@ class OnlineBankStatementProviderQonto(models.Model): @api.model def _get_available_services(self): return super()._get_available_services() + [ - ("qonto", "Qonto.eu"), + ("qonto", "Qonto"), ] def _obtain_statement_data(self, date_since, date_until): @@ -95,18 +95,18 @@ class OnlineBankStatementProviderQonto(models.Model): ): date = datetime.strptime(transaction["settled_at"], "%Y-%m-%dT%H:%M:%S.%fZ") side = 1 if transaction["side"] == "credit" else -1 - name = transaction["label"] or "/" - if transaction["reference"]: - name = "{} {}".format(name, transaction["reference"]) + payment_ref_list = [ + transaction["label"], + transaction["reference"], + ] vals_line = { "sequence": sequence, "date": date, - "name": name, + "name": " - ".join([x for x in payment_ref_list if x]) or "/", "ref": transaction["reference"], "unique_import_id": transaction["transaction_id"], "amount": transaction["amount"] * side, } - if not transaction["local_currency"]: raise UserError( _( @@ -117,12 +117,10 @@ class OnlineBankStatementProviderQonto(models.Model): ) if transaction["local_currency"] not in currencies_code2id: raise UserError( - _("Currency %s used in transaction ID %s doesn't exist " "in Odoo.") + _("Currency %s used in transaction ID %s doesn't exist in Odoo.") % (transaction["local_currency"], transaction["transaction_id"]) ) - line_currency_id = currencies_code2id[transaction["local_currency"]] - if journal_currency.id != line_currency_id: vals_line.update( { @@ -135,7 +133,6 @@ class OnlineBankStatementProviderQonto(models.Model): def _qonto_obtain_statement_data(self, date_since, date_until): self.ensure_one() journal = self.journal_id - slugs = self._qonto_get_slug() slug = slugs.get(self.account_number) if not slug: @@ -143,11 +140,8 @@ class OnlineBankStatementProviderQonto(models.Model): _("Qonto : wrong configuration, unknow account %s") % journal.bank_account_id.acc_number ) - transactions = self._qonto_obtain_transactions(slug, date_since, date_until) - journal_currency = journal.currency_id or journal.company_id.currency_id - all_currencies = self.env["res.currency"].search_read([], ["name"]) currencies_code2id = {x["name"]: x["id"] for x in all_currencies} new_transactions = [] @@ -158,7 +152,6 @@ class OnlineBankStatementProviderQonto(models.Model): transaction, sequence, journal_currency, currencies_code2id ) new_transactions.append(vals_line) - if new_transactions: return new_transactions, {} return diff --git a/account_bank_statement_import_online_qonto/readme/CONFIGURE.rst b/account_bank_statement_import_online_qonto/readme/CONFIGURE.rst new file mode 100644 index 00000000..781fb2f2 --- /dev/null +++ b/account_bank_statement_import_online_qonto/readme/CONFIGURE.rst @@ -0,0 +1,26 @@ +To configure online bank statements provider: + +#. Go to *Invoicing > Configuration > Bank Accounts* +#. Open bank account to configure and edit it +#. Set *Bank Feeds* to *Online* +#. Select *Qonto* as online bank statements provider in + *Online Bank Statements (OCA)* section +#. Save the bank account +#. Click on provider and configure provider-specific settings. + +or, alternatively: + +#. Go to *Invoicing > Overview* +#. Open settings of the corresponding journal account +#. Switch to *Bank Account* tab +#. Set *Bank Feeds* to *Online* +#. Select *Qonto* as online bank statements provider in + *Online Bank Statements (OCA)* section +#. Save the bank account +#. Click on provider and configure provider-specific settings. + +To obtain *Login* and *Key*: + +#. Open `Qonto `__. +#. Go to *Settings* +#. Go to *Api Integration*, click *Generate Key* diff --git a/account_bank_statement_import_online_qonto/readme/CONTRIBUTORS.rst b/account_bank_statement_import_online_qonto/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..077a3ecf --- /dev/null +++ b/account_bank_statement_import_online_qonto/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Florent de Labarre +* `Tecnativa `__: + + * Pedro M. Baeza diff --git a/account_bank_statement_import_online_qonto/readme/DESCRIPTION.rst b/account_bank_statement_import_online_qonto/readme/DESCRIPTION.rst new file mode 100644 index 00000000..c47a5301 --- /dev/null +++ b/account_bank_statement_import_online_qonto/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module provides online bank statements from Qonto Bank. diff --git a/account_bank_statement_import_online_qonto/readme/USAGE.rst b/account_bank_statement_import_online_qonto/readme/USAGE.rst new file mode 100644 index 00000000..03845f13 --- /dev/null +++ b/account_bank_statement_import_online_qonto/readme/USAGE.rst @@ -0,0 +1,6 @@ +To pull historical bank statements: + +#. Go to *Invoicing > Configuration > Bank Accounts* +#. Select specific bank accounts +#. Launch *Actions > Online Bank Statements Pull Wizard* +#. Configure date interval and click *Pull* diff --git a/account_bank_statement_import_online_qonto/static/description/icon.png b/account_bank_statement_import_online_qonto/static/description/icon.png index 3a0328b516c4980e8e44cdb63fd945757ddd132d..f316d83877c30593d08fef0df569ce6005b1e0d6 100644 GIT binary patch literal 119787 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 K4fA&Wbml*>NNw~0 literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I diff --git a/account_bank_statement_import_online_qonto/static/description/index.html b/account_bank_statement_import_online_qonto/static/description/index.html new file mode 100644 index 00000000..7312fd0c --- /dev/null +++ b/account_bank_statement_import_online_qonto/static/description/index.html @@ -0,0 +1,465 @@ + + + + + + +Online Bank Statements: Qonto + + + +
+

Online Bank Statements: Qonto

+ + +

Beta License: AGPL-3 OCA/bank-statement-import Translate me on Weblate Try me on Runbot

+

This module provides online bank statements from Qonto Bank.

+

Table of contents

+ +
+

Configuration

+

To configure online bank statements provider:

+
    +
  1. Go to Invoicing > Configuration > Bank Accounts
  2. +
  3. Open bank account to configure and edit it
  4. +
  5. Set Bank Feeds to Online
  6. +
  7. Select Qonto as online bank statements provider in +Online Bank Statements (OCA) section
  8. +
  9. Save the bank account
  10. +
  11. Click on provider and configure provider-specific settings.
  12. +
+

or, alternatively:

+
    +
  1. Go to Invoicing > Overview
  2. +
  3. Open settings of the corresponding journal account
  4. +
  5. Switch to Bank Account tab
  6. +
  7. Set Bank Feeds to Online
  8. +
  9. Select Qonto as online bank statements provider in +Online Bank Statements (OCA) section
  10. +
  11. Save the bank account
  12. +
  13. Click on provider and configure provider-specific settings.
  14. +
+

To obtain Login and Key:

+
    +
  1. Open Qonto.
  2. +
  3. Go to Settings
  4. +
  5. Go to Api Integration, click Generate Key
  6. +
+
+
+

Usage

+

To pull historical bank statements:

+
    +
  1. Go to Invoicing > Configuration > Bank Accounts
  2. +
  3. Select specific bank accounts
  4. +
  5. Launch Actions > Online Bank Statements Pull Wizard
  6. +
  7. Configure date interval and click Pull
  8. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Florent de Labarre
  • +
+
+
+

Contributors

+
    +
  • Florent de Labarre
  • +
  • Tecnativa:
      +
    • Pedro M. Baeza
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/bank-statement-import project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ +