diff options
| author | ivar <i@oiee.no> | 2026-03-09 23:05:38 +0100 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2026-03-09 23:05:38 +0100 |
| commit | 69448e29a85cad3a94b3be3ad33efbc52764528f (patch) | |
| tree | c32b8c817322fdf26edbbb3fa75b9505a7020ae8 /app/src | |
| parent | b35302fa020ec82a9d67a6cb34379d42983d3cfc (diff) | |
| download | sparebank1-actualbudget-69448e29a85cad3a94b3be3ad33efbc52764528f.tar.xz sparebank1-actualbudget-69448e29a85cad3a94b3be3ad33efbc52764528f.zip | |
Diffstat (limited to 'app/src')
| -rw-r--r-- | app/src/lib/server/actual.ts | 87 | ||||
| -rw-r--r-- | app/src/routes/+page.server.ts | 6 | ||||
| -rw-r--r-- | app/src/routes/+page.svelte | 35 | ||||
| -rw-r--r-- | app/src/routes/methods.remote.ts | 14 |
4 files changed, 83 insertions, 59 deletions
diff --git a/app/src/lib/server/actual.ts b/app/src/lib/server/actual.ts index fb1a4fb..ca4d9c4 100644 --- a/app/src/lib/server/actual.ts +++ b/app/src/lib/server/actual.ts @@ -1,5 +1,5 @@ import { ACTUAL_FILE_ID, ACTUAL_HOST, ACTUAL_PASS } from "$env/static/private"; -import * as actual from "@actual-app/api" +import * as actualApi from "@actual-app/api" import { existsSync, mkdirSync } from "node:fs"; import path from "node:path" import process from "node:process"; @@ -7,53 +7,66 @@ import type { ImportTransactionEntity } from "@actual-app/api/@types/loot-core/s import { Temporal } from "temporal-polyfill"; import type { Sb1Transaction } from "$lib/shared"; -export async function init_actual() { +let inited = false + +async function init() { + if (inited) return const dataDir = path.resolve(process.cwd(), "data/actualDataDir") if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true }); - return actual.init({ + await actualApi.init({ password: ACTUAL_PASS, serverURL: ACTUAL_HOST, - dataDir: dataDir - }).then(async () => { - await actual.downloadBudget(ACTUAL_FILE_ID) - await actual.sync() + dataDir }) + await actualApi.downloadBudget(ACTUAL_FILE_ID) + await actualApi.sync() + inited = true } -export async function import_transactions(account: string, transactions: Sb1Transaction[], dryRun: boolean) { - await init_actual() - function parsedDate(date: number) { - const instant = Temporal.Instant.fromEpochMilliseconds(date) - return instant.toString({ timeZone: "Europe/Oslo" }).split("T")[0] - } +const budget = { + async get_budgets() { + await init() + return actualApi.getBudgets() + }, - function notes(transaction: Sb1Transaction) { - const { description, cleanedDescription } = transaction - if (description.toLowerCase().trim() === cleanedDescription.toLowerCase().trim()) return undefined - return description - } + async get_accounts() { + await init() + return actualApi.getAccounts() + }, - function amount(amount: number) { - return Math.round(amount * 100) - } + async import_transactions(account: string, transactions: Sb1Transaction[], dryRun: boolean) { + await init() - const actualMappedTransactions: ImportTransactionEntity[] = transactions.filter(c => c.bookingStatus === "BOOKED").map(c => ({ - account, - date: parsedDate(c.date), - amount: amount(c.amount), - notes: notes(c), - payee_name: c.cleanedDescription - })) + function parsedDate(date: number) { + return Temporal.Instant.fromEpochMilliseconds(date) + .toString({ timeZone: "Europe/Oslo" }) + .split("T")[0] + } - await actual.importTransactions(account, actualMappedTransactions, { dryRun }) -} + function notes(transaction: Sb1Transaction) { + const { description, cleanedDescription } = transaction + if (description?.toLowerCase().trim() === cleanedDescription?.toLowerCase().trim()) return undefined + return description + } -export async function get_budgets() { - await init_actual() - return actual.getBudgets() -} + function amount(amount: number) { + const res = Math.round(amount * 10000) + console.log(`${amount}->${res}`) + return res + } + + const mapped: ImportTransactionEntity[] = transactions + .filter(c => c.bookingStatus === "BOOKED") + .map(c => ({ + account, + date: parsedDate(c.date), + amount: amount(c.amount), + notes: notes(c), + payee_name: c.cleanedDescription + })) -export async function get_accounts() { - await init_actual() - return actual.getAccounts() + return actualApi.importTransactions(account, mapped, { dryRun }) + } } + +export default { init, budget } diff --git a/app/src/routes/+page.server.ts b/app/src/routes/+page.server.ts index 5d3e857..df076d7 100644 --- a/app/src/routes/+page.server.ts +++ b/app/src/routes/+page.server.ts @@ -1,12 +1,12 @@ import type { PageServerLoad } from './$types'; -import { get_accounts, get_budgets } from '$lib/server/actual'; +import actual from '$lib/server/actual'; import sb1 from "$lib/server/sb1" export const load = (async () => { return { actual: { - budgets: await get_budgets(), - accounts: await get_accounts(), + budgets: await actual.budget.get_budgets(), + accounts: await actual.budget.get_accounts(), }, sb1: { accounts: (await sb1.data.get_accounts())?.accounts diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index b12c471..693a430 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -1,6 +1,10 @@ <script lang="ts"> import Button from "$lib/ui/button.svelte"; - import { clear_auth_session, init_auth_session, do_import } from "./methods.remote"; + import { + clear_auth_session, + init_auth_session, + do_import, + } from "./methods.remote"; import type { PageProps } from "./$types"; import type { ImportForm } from "$lib/shared"; @@ -33,7 +37,8 @@ function onMappingChanged(sb1Id: string, actualId: string) { let mappings = form.mappings; - if (mappings.find((c) => c.sb1Id === sb1Id)) mappings = mappings.filter((c) => c.sb1Id !== sb1Id); + if (mappings.find((c) => c.sb1Id === sb1Id)) + mappings = mappings.filter((c) => c.sb1Id !== sb1Id); mappings.push({ sb1Id, actualId }); form.mappings = mappings; } @@ -44,12 +49,6 @@ <form onsubmit={run}> <h3>Importer</h3> <fieldset> - <h4>Budsjett</h4> - {#each data.actual.budgets as budget} - {@const id = `budget-${budget.id}`} - <input name="budget" {id} value={budget.id} type="radio" bind:group={form.budgetId} /> - <label for={id}>{budget.name}({budget.id})</label><br /> - {/each} <h4>Kontoer</h4> {#each data.sb1.accounts as account} {@const actualId = `mapping-${account.key}-actual`} @@ -57,7 +56,15 @@ <code>{account.name}</code> <span>→</span> <label for={actualId}>Actual</label> - <select name={actualId} id={actualId} onchange={(e) => onMappingChanged(account.key, e.currentTarget.value)}> + <select + name={actualId} + id={actualId} + onchange={(e) => + onMappingChanged( + account.key, + e.currentTarget.value, + )} + > <option value="-" selected>-</option> {#each data.actual.accounts as actual} <option value={actual.id}> @@ -68,7 +75,11 @@ </div> {/each} <h4>Ellers</h4> - <input type="checkbox" id="dry" bind:checked={form.dryRun} /><label for="dry">Tørrkjøring</label><br /><br /> + <input + type="checkbox" + id="dry" + bind:checked={form.dryRun} + /><label for="dry">Tørrkjøring</label><br /><br /> <input type="submit" /> </fieldset> </form> @@ -76,6 +87,8 @@ <Button onclick={logout} loading={navigating}>Logg ut</Button> <div></div> {:else} - <Button onclick={authorize} loading={navigating}>Autentisér hos Sparebanken 1</Button> + <Button onclick={authorize} loading={navigating} + >Autentisér hos Sparebanken 1</Button + > {/if} </main> diff --git a/app/src/routes/methods.remote.ts b/app/src/routes/methods.remote.ts index d9ba812..d6fd908 100644 --- a/app/src/routes/methods.remote.ts +++ b/app/src/routes/methods.remote.ts @@ -2,7 +2,7 @@ import { db } from "$lib/server/db"; import { SyncSessionTable } from "$lib/server/db/schema"; import { command, query } from "$app/server"; import sb1 from "$lib/server/sb1"; -import { import_transactions, init_actual } from "$lib/server/actual"; +import actual from "$lib/server/actual"; import { ImportForm } from "$lib/shared"; const init_auth_session = command(async () => { @@ -16,10 +16,8 @@ const clear_auth_session = query(async () => { const do_import = command(ImportForm, async (form) => { for (const mapping of form.mappings) { const transactions = await sb1.data.get_transactions(mapping.sb1Id) - console.log(transactions) - continue - // if (!transactions?.length) continue - // console.log(await import_transactions(mapping.actualId, transactions, form.dryRun)) + if (!transactions?.length) continue + console.log(await actual.budget.import_transactions(mapping.actualId, transactions, form.dryRun)) } }) @@ -27,14 +25,14 @@ const init_sb1 = command(async () => { return await sb1.init() }) -const _init_actual = command(async () => { - return await init_actual() +const init_actual = command(async () => { + return await actual.init() }) export { init_auth_session, do_import, - _init_actual as init_actual, + init_actual, init_sb1, clear_auth_session } |
