Skip to content

Support Qonto as first bank integration - #13124

Merged
yabirgb merged 4 commits into
rotki:developfrom
LefterisJP:qonto
Sep 14, 2026
Merged

yabirgb merged 4 commits into
rotki:developfrom
LefterisJP:qonto

Conversation

@LefterisJP

Copy link
Copy Markdown
Member

Add Qonto as the first bank integration into rotki. Part of #13052

Some screenshots
2026-09-11_18-24
2026-09-11_17-21
2026-09-11_17-18

@codecov

codecov Bot commented Sep 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.00985% with 142 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.29%. Comparing base (fe32b32) to head (db3907a).
⚠️ Report is 5 commits behind head on develop.

Files with missing lines Patch % Lines
frontend/app/src/pages/balances/banks/index.vue 0.00% 34 Missing ⚠️
frontend/app/src/modules/banks/use-banks.ts 76.76% 33 Missing ⚠️
...rc/modules/history/refresh/HistoryRefreshBanks.vue 3.03% 32 Missing ⚠️
.../app/src/modules/dashboard/summary/BankSummary.vue 0.00% 14 Missing ⚠️
...tend/app/src/modules/dashboard/summary/BankBox.vue 0.00% 7 Missing ⚠️
...rc/modules/banks/components/BankConnectionForm.vue 86.04% 6 Missing ⚠️
frontend/app/src/pages/api-keys/banks/index.vue 92.18% 4 Missing and 1 partial ⚠️
...odules/history/refresh/HistoryRefreshSelection.vue 33.33% 4 Missing ⚠️
frontend/app/src/modules/banks/use-bank-data.ts 92.50% 3 Missing ⚠️
...modules/banks/components/BankConnectionActions.vue 88.88% 1 Missing ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #13124      +/-   ##
===========================================
- Coverage    85.30%   85.29%   -0.02%     
===========================================
  Files         3440     3456      +16     
  Lines       212647   213767    +1120     
  Branches     28484    28650     +166     
===========================================
+ Hits        181405   182325     +920     
- Misses       26605    26805     +200     
  Partials      4637     4637              
Flag Coverage Δ
frontend_integration 98.30% <91.21%> (-0.37%) ⬇️
frontend_unit 75.67% <71.42%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...rc/modules/balances/exchanges/use-exchange-data.ts 88.63% <100.00%> (+0.54%) ⬆️
...d/app/src/modules/balances/use-balance-fetching.ts 100.00% <100.00%> (ø)
...nd/app/src/modules/balances/use-balance-refresh.ts 76.11% <100.00%> (+1.51%) ⬆️
...tend/app/src/modules/banks/bank-connection-form.ts 100.00% <100.00%> (ø)
frontend/app/src/modules/banks/types.ts 100.00% <100.00%> (ø)
...pp/src/modules/banks/use-bank-connections-store.ts 100.00% <100.00%> (ø)
frontend/app/src/modules/banks/use-banks-api.ts 100.00% <100.00%> (ø)
frontend/app/src/modules/core/common/location.ts 100.00% <100.00%> (ø)
.../app/src/modules/core/common/use-location-store.ts 100.00% <100.00%> (ø)
...c/modules/core/table/use-remember-table-sorting.ts 94.79% <100.00%> (+0.05%) ⬆️
... and 67 more

... and 224 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Add Qonto as the first bank integration into rotki.
Part of rotki#13052
Scrolling the virtualised entry type menu by hand depended on which
wrapper overflows, which differs between Chromium builds and left swap
event unrendered in CI. Move the highlight with the arrow keys instead,
so the menu scrolls the option into view through its own virtual list.
Comment thread rotkehlchen/banks/connector.py Outdated
Comment on lines +262 to +265
def _cursor_updater(self, account_id: str, value: Timestamp) -> Callable[[DBCursor], None]:
def update(write_cursor: DBCursor) -> None:
self.set_cursor(write_cursor=write_cursor, account_id=account_id, value=value)
return update

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the wrapper needed? why not use a lambda?

Comment on lines +2871 to +2885
if new_name is not None:
write_cursor.execute( # keep the exchange-style range bookkeeping in sync
'UPDATE used_query_ranges SET name=? WHERE name=?',
(f'{location!s}_history_events_{new_name}', f'{location!s}_history_events_{name}'),
)
# and the per-connection caches (cursors, session)
old_prefix = f'{location!s}_{name}_'
escaped_prefix = (
old_prefix.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
)
write_cursor.execute(
'UPDATE key_value_cache SET name=? || substr(name, ?) '
"WHERE name LIKE ? ESCAPE '\\'",
(f'{location!s}_{new_name}_', len(old_prefix) + 1, f'{escaped_prefix}%'),
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existing events also need to be renamed so their location label points to the new name

Comment thread rotkehlchen/db/dbhandler.py Outdated
Comment on lines +2878 to +2880
escaped_prefix = (
old_prefix.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are to accounts with the same prefix, for example Buisness and Buisness Savings then renaming Buisness will change the name to both

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The escaping fixes SQL wildcard matching, but the prefix is still ambiguous when a connection name contains underscores. With connections named Business and Business_US, renaming Business to Renamed also renames qonto_Business_US_account_last_query_ts to qonto_Renamed_US_account_last_query_ts.

Could you check this case and add a regression test ensuring that renaming one connection leaves the other connection’s caches unchanged?

Comment on lines +256 to +259
event_queue.flush(
events=events,
cursor_update=self._cursor_updater(account_id=account.identifier, value=newest),
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it performs an update or ignore but if there are changes in the data we want an insert or update. Not sure if it is intentional but leaving it as question. I'm thinking on the case of repulling a range

Comment thread rotkehlchen/banks/normalization.py Outdated
Comment on lines +82 to +87
def bank_transaction_to_events(
transaction: BankTransaction,
location: Location,
location_label: str,
) -> list[BankTransactionEvent]:
"""Map a normalized bank transaction onto rotki history events.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why return a list instead of single event? Later we do extend and we could do appeand instead

Comment thread rotkehlchen/banks/qonto.py Outdated
Comment on lines +108 to +110
changed = super().edit_exchange_credentials(credentials)
if changed:
self._set_auth_header()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can use the walrus operator here

Comment thread rotkehlchen/banks/qonto.py Outdated
f'Qonto rate limited the {endpoint} query',
retry_after=retry_after,
)
wait = retry_after if retry_after is not None else 2.0 ** (3 - retries_left)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amounts smaller than a second are expected? when retries_left > 3

Comment thread rotkehlchen/banks/qonto.py Outdated
Comment on lines +190 to +192
asset = self._resolve_fiat(entry['currency'])
if asset is None:
return None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use walrus operator

Comment thread rotkehlchen/banks/qonto.py Outdated
Comment on lines +317 to +320

if next_page is None or len(rows) == 0:
break
params['current_page'] = next_page

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems an error unless I'm reading wrong. THe pagination is with page instead of current_page

Image

can be seen in the url and the ext above the example.

@yabirgb yabirgb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it looks fine already. LGMT

@yabirgb
yabirgb merged commit d36a2f5 into rotki:develop Sep 14, 2026
32 checks passed
@yabirgb
yabirgb deployed to cassette-merge September 14, 2026 15:00 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
cassette-merge — db3907a3 Deployed Sep 14, 2026 by yabirgb via merge #6301
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants