aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-03-09 23:05:38 +0100
committerivar <i@oiee.no>2026-03-09 23:05:38 +0100
commit69448e29a85cad3a94b3be3ad33efbc52764528f (patch)
treec32b8c817322fdf26edbbb3fa75b9505a7020ae8 /app/src
parentb35302fa020ec82a9d67a6cb34379d42983d3cfc (diff)
downloadsparebank1-actualbudget-69448e29a85cad3a94b3be3ad33efbc52764528f.tar.xz
sparebank1-actualbudget-69448e29a85cad3a94b3be3ad33efbc52764528f.zip
Add wip cliHEADmaster
Diffstat (limited to 'app/src')
-rw-r--r--app/src/lib/server/actual.ts87
-rw-r--r--app/src/routes/+page.server.ts6
-rw-r--r--app/src/routes/+page.svelte35
-rw-r--r--app/src/routes/methods.remote.ts14
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>&#8594;</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
}