From 3bc6b9ab7c3161be12e5a2d85f6b9d34d9cd4e1d Mon Sep 17 00:00:00 2001 From: Casey Timm Date: Mon, 7 Jul 2025 18:35:00 -0400 Subject: [PATCH] Implement account and transaction upsert functionality; add updateAccounts API endpoint --- src/lib/db.js | 92 ++++++++++++++++ src/lib/simplefin.js | 15 ++- src/routes/+layout.svelte | 122 +++++++++++---------- src/routes/account/[slug]/+page.svelte | 2 +- src/routes/api/simplefin/update/+server.js | 11 ++ 5 files changed, 181 insertions(+), 61 deletions(-) create mode 100644 src/routes/api/simplefin/update/+server.js diff --git a/src/lib/db.js b/src/lib/db.js index 9750893..d02b6f0 100644 --- a/src/lib/db.js +++ b/src/lib/db.js @@ -166,5 +166,97 @@ export async function setTransactionNote(transactionId, note) { return result } +export async function updateAccounts(data) { + try { + console.log('Updating accounts with data:', data); + for (const account of data.accounts) { + // Upsert Org + console.log(`Upserting org for account: ${account.id}`, account.org); + await db` + insert into org (id, domain, name, sfin_url, url) + values (${account.org.id}, ${account.org.domain ?? null}, ${account.org.name ?? null}, ${account.org.sfin_url ?? null}, ${account.org.url ?? null}) + on conflict (id) do update set + domain = excluded.domain, + name = excluded.name, + sfin_url = excluded.sfin_url, + url = excluded.url + `; + console.log(`Upserting account: ${account.id} (${account.name})`); + // Upsert Account + await db` + insert into account (id, org_id, name, currency, balance, available_balance, balance_date) + values ( + ${account.id}, + ${account.org.id}, + ${account.name ?? null}, + ${account.currency ?? null}, + ${account.balance ?? null}, + ${account.available_balance ?? null}, + ${account.balance_date ?? null} + ) + on conflict (id) do update set + org_id = excluded.org_id, + name = excluded.name, + currency = excluded.currency, + balance = excluded.balance, + available_balance = excluded.available_balance, + balance_date = excluded.balance_date + `; + + // Upsert Transactions + if (account.transactions && account.transactions.length > 0) { + for (const txn of account.transactions) { + let extraId = null; + console.log(`Upserting transaction: ${txn.id} for account: ${account.id}`); + if (txn.extra) { + // Upsert TransactionExtra (insert only, update not needed for category) + const extraResult = await db` + insert into transaction_extra (category) + values (${txn.extra.category ?? null}) + on conflict (category) do nothing + returning id + `; + if (extraResult.length > 0) { + extraId = extraResult[0].id; + } else { + // If already exists, fetch id + const existing = await db` + select id from transaction_extra where category = ${txn.extra.category ?? null} + `; + if (existing.length > 0) { + extraId = existing[0].id; + } + } + } + console.log(`Preparing to upsert transaction: ${txn.id} with data:`, txn); + await db` + insert into transaction (id, account_id, posted, amount, description, pending, extra_id) + values ( + ${txn.id}, + ${account.id}, + ${txn.posted}, + ${txn.amount ?? null}, + ${txn.description ?? null}, + ${txn.pending}, + ${extraId} + ) + on conflict (id) do update set + account_id = excluded.account_id, + posted = excluded.posted, + amount = excluded.amount, + description = excluded.description, + pending = excluded.pending, + extra_id = excluded.extra_id + `; + } + } + } + return true; + } catch (error) { + console.error('updateAccounts error:', error); + return false; + } +} + export default db \ No newline at end of file diff --git a/src/lib/simplefin.js b/src/lib/simplefin.js index 2919097..cb63754 100644 --- a/src/lib/simplefin.js +++ b/src/lib/simplefin.js @@ -1,5 +1,14 @@ -export async function updateAccounts() -{ - fetch +export async function fetchAccounts(url, startDate) { + const { username, password, origin, pathname } = new URL(url); + const start = Math.floor(startDate.getTime() / 1000); + const apiUrl = `${origin}${pathname}/accounts?start-date=${start}`; + const headers = {}; + + if (username && password) { + headers['Authorization'] = 'Basic ' + btoa(`${username}:${password}`); + } + + const response = await fetch(apiUrl, { headers }); + return await response.json(); } \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index f8921ae..a0cf94d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,38 +1,42 @@
-
+
{@render children()} @@ -40,14 +44,10 @@
- - - \ No newline at end of file + + diff --git a/src/routes/account/[slug]/+page.svelte b/src/routes/account/[slug]/+page.svelte index 7459068..14d9e9e 100644 --- a/src/routes/account/[slug]/+page.svelte +++ b/src/routes/account/[slug]/+page.svelte @@ -63,7 +63,7 @@

${currentTransaction?.amount}

{currentTransaction?.date?.toDateString()}

-
diff --git a/src/routes/api/simplefin/update/+server.js b/src/routes/api/simplefin/update/+server.js new file mode 100644 index 0000000..77c0e36 --- /dev/null +++ b/src/routes/api/simplefin/update/+server.js @@ -0,0 +1,11 @@ + +import { fetchAccounts } from '$lib/simplefin'; +import { updateAccounts } from '$lib/db' ; + +const url = "https://19443E0E8171E175EC5DA0C69B35DD50197F234B9A74C00D27FD606121257ECF:DAA3702E2100CFFD3B544251E6D755E86B1EDDFBFCC7F6FA9CE77AB3677E60DE@beta-bridge.simplefin.org/simplefin"; + +export async function POST() { + const res = await fetchAccounts(url, new Date("2025-07-01")) + return new Response(await updateAccounts(res)); + +} \ No newline at end of file