Skip to content

feat(plugins): add Wise plugin with multi-currency account grouping - #8

Open
CWZMorro wants to merge 6 commits into
james-yap:mainfrom
CWZMorro:main
Open

feat(plugins): add Wise plugin with multi-currency account grouping#8
CWZMorro wants to merge 6 commits into
james-yap:mainfrom
CWZMorro:main

Conversation

@CWZMorro

Copy link
Copy Markdown
Contributor

Summary

  • Adds a Wise plugin that scrapes transactions from both the /all-transactions and /home pages
  • Introduces TransactionGroup to support plugins returning multiple named account groups, with auto-wrapping for single-group plugins
  • Wise transactions are automatically split into separate groups per currency (e.g. "Wise CAD", "Wise MYR") so each can be exported independently to a different ActualBudget account
  • SyncAccountsButton now accepts a group prop and stages only that group's transactions before opening ActualBudget, enabling per-account export
  • Correctly handles currency conversions: "To CAD" creates a deposit in the target currency and a withdrawal in the source; auto-converted purchases (e.g. paying CNY from MYR balance) are recorded only against the wallet currency

Test plan

  • Open Wise /all-transactions — transactions appear split by currency group
  • Open Wise /home — recent transactions are scraped correctly
  • Perform "Export to X" on a single currency group — only that group's transactions are imported into ActualBudget
  • Download CSV — all groups are flattened into one file
  • Non-Wise plugins still work (single group, no regression)
  • Conversion transactions ("To CAD") appear as deposit in CAD group and withdrawal in source currency group
  • Auto-converted purchases appear only in wallet currency group (no phantom negative balances)

CWZMorro added 4 commits May 17, 2026 16:17
- Parses full "16 May 2026" / "11 March 2025" dates directly, no year
    inference that caused March 2025 transactions to land in 2026
  - Skips cancelled transactions via status element check
  - "To CAD"-style conversions emit two transactions: a deposit for the
    received currency and a withdrawal for the source currency
  - Supports both /home and /all-transactions pages
…rate accounts

Wise now returns TransactionGroup[] keyed by currency (e.g. "Wise
CAD", "Wise MYR"). App.tsx renders one table per group, each with its
own export button. Other plugins return Transaction[] and are
auto-wrapped into a single group with no UI change. CSV download
flattens all groups.
… only

When Wise auto-converts a purchase (e.g. paying 18 CNY from an MYR
balance), only the MYR deduction is recorded. The CNY merchant amount is
kept in the notes field for reference but no longer creates a phantom
CNY withdrawal.
Copilot AI review requested due to automatic review settings May 17, 2026 23:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Wise scraper plugin and extends the transaction model to support multiple named account groups (e.g., per-currency Wise balances), enabling per-group export to ActualBudget while keeping CSV export as a flattened list.

Changes:

  • Added Wise plugin that scrapes /all-transactions with a /home fallback and groups transactions by currency.
  • Introduced TransactionGroup and migrated the frontend storage/UI to use transactionStore.groups (auto-wrapping single-group plugins).
  • Updated export flow to stage only a selected group’s transactions via exportGroup in Chrome storage, and updated CSV export to flatten groups.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/plugins/src/Wise.ts New Wise scraper that returns multiple currency-based transaction groups.
packages/plugins/src/index.ts Exports the new Wise plugin from the plugins package.
packages/frontend/src/config.ts Registers Wise plugin and broadens scrape return type to allow grouped results.
packages/frontend/src/components/EmptyState/EmptyState.tsx Adds Wise to the supported plugin list shown in the empty state.
packages/frontend/src/components/ActionButtons/SyncAccountsButton.tsx Makes export per-group by writing exportGroup before opening ActualBudget.
packages/frontend/src/components/ActionButtons/DownloadCsvButton.tsx Flattens grouped transactions before CSV export.
packages/frontend/src/components/ActionButtons/ActionButtons.tsx Removes global export button (now per-group in App.tsx), leaving only CSV.
packages/frontend/src/App.tsx Adapts UI/storage to grouped transactions and renders per-group tables + export buttons.
packages/core/src/types/openbanker.ts Adds TransactionGroup, changes TransactionList to groups, adds exportGroup in storage type.
packages/chrome/src/background.ts Exports CSV from either exportGroup (preferred) or flattened transactionStore.groups, then clears exportGroup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/src/types/openbanker.ts Outdated
Comment thread packages/frontend/src/config.ts Outdated
Comment thread packages/frontend/src/components/ActionButtons/ActionButtons.tsx Outdated
…up type, storage error guard, dead ButtonGroup

@james-yap james-yap left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Code Review

Overall this is a clean, well-structured feature. The TransactionGroup abstraction is solid and the Wise plugin handles a lot of date format edge cases nicely. Two issues need addressing before merge — see inline comments.

🔴 Warnings (should fix)

  • Wise.ts — Negative amount regex may silently drop withdrawal transactions
  • background.tsexportGroup cleanup is fire-and-forget with no error handling

💡 Suggestions (non-blocking)

  • App.tsx — Duck-typing for group detection is fragile long-term
  • Wise.ts — Transactions with unparseable dates are still added (empty date string)
  • Wise.ts — Silent catch (_) {} on base64 decode swallows errors

✅ Looks Good

  • NGPF /g flag removal is a correct bugfix (stateful regex reuse bug)
  • Write-then-open sequencing in SyncAccountsButton is properly async
  • Per-group CSV fallback logic in background.ts is clean
  • Date parsing covers a solid range of Wise format variants

Reviewed by Hermes Agent

@james-yap

Copy link
Copy Markdown
Owner

Inline notes

packages/plugins/src/Wise.tsparseAmount negative regex

cleaned still contains a leading - for withdrawals (e.g. - 150.00 CAD), but the regex only matches digits [\d,]+, so it silently drops those transactions. Consider:

const cleaned = rawText.replace(/^[+\-]\s*/, '').trim();

Then derive isDeposit from the stripped sign rather than just the positive span.


packages/chrome/src/background.ts — fire-and-forget remove

chrome.storage[CHROME_STORAGE_STRATEGY].remove("exportGroup");

No callback / await. If the service worker is terminated before it completes, stale exportGroup persists and corrupts the next per-group export. The handler is already async, so:

await chrome.storage[CHROME_STORAGE_STRATEGY].remove("exportGroup");

packages/frontend/src/App.tsx — duck-typing for group detection

if (Array.isArray(raw) && raw.length > 0 && 'account' in raw[0])

Works today, but breaks silently if Transaction ever gains an account field. Better to have all plugins return TransactionGroup[] and wrap flat-array plugins at the plugin layer.


packages/plugins/src/Wise.ts — empty-date transactions

parseDate returns "" with a console.warn on failure, but the transaction is still added. Consider if (!date) continue; to avoid passing empty dates to ActualBudget.


Reviewed by Hermes Agent


}, []);

const hasData = transactionStore.pluginName !== "";

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

great refactorization!

@james-yap

Copy link
Copy Markdown
Owner
image

Initial smoke tests are good, except for one regression on the extension UI

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.

3 participants