aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorivar <i@oiee.no>2025-12-26 22:19:09 +0100
committerivar <i@oiee.no>2025-12-26 22:19:09 +0100
commitabf37599c877a8cc83e5a24c775c3999a9879abf (patch)
tree234fbafdadda08735c4cbf6d3092bebd1d725691
parentfe0fe074ec8e8959bbdeff0ccc7f68d20b30e963 (diff)
downloadsparebank1-actualbudget-abf37599c877a8cc83e5a24c775c3999a9879abf.tar.xz
sparebank1-actualbudget-abf37599c877a8cc83e5a24c775c3999a9879abf.zip
Remove delta from form, wip import method
-rw-r--r--app/src/lib/server/actual.ts21
-rw-r--r--app/src/lib/server/importer.ts12
-rw-r--r--app/src/lib/shared.d.ts5
-rw-r--r--app/src/routes/+page.svelte23
-rw-r--r--app/src/routes/methods.remote.ts20
-rw-r--r--app/src/routes/sb1.remote.ts38
6 files changed, 64 insertions, 55 deletions
diff --git a/app/src/lib/server/actual.ts b/app/src/lib/server/actual.ts
index 0875273..389f390 100644
--- a/app/src/lib/server/actual.ts
+++ b/app/src/lib/server/actual.ts
@@ -3,6 +3,9 @@ import * as actual from "@actual-app/api"
import { existsSync, mkdirSync } from "node:fs";
import path from "node:path"
import process from "node:process";
+import type { Sb1Transaction } from "./sb1";
+import type { ImportTransactionEntity } from "@actual-app/api/@types/loot-core/src/types/models/import-transaction";
+import { Temporal } from "temporal-polyfill";
async function init_actual() {
const dataDir = path.resolve(process.cwd(), "data/actualDataDir")
@@ -17,6 +20,24 @@ async function init_actual() {
})
}
+export async function import_transactions(account: string, transactions: Sb1Transaction[], dryRun: boolean) {
+
+ function parsedDate(date: number) {
+ const instant = Temporal.Instant.fromEpochMilliseconds(date)
+ return instant.toString({ timeZone: "Europe/Oslo" }).split("T")[0]
+ }
+
+ const actualMappedTransactions: ImportTransactionEntity[] = transactions.filter(c => c.bookingStatus === "BOOKED").map(c => ({
+ account,
+ date: parsedDate(c.date),
+ amount: c.amount,
+ notes: c.description,
+ payee_name: c.cleanedDescription
+ }))
+
+ actual.importTransactions(account, actualMappedTransactions, { dryRun })
+}
+
export async function get_budgets() {
await init_actual()
return actual.getBudgets()
diff --git a/app/src/lib/server/importer.ts b/app/src/lib/server/importer.ts
index 91adce0..02cb94c 100644
--- a/app/src/lib/server/importer.ts
+++ b/app/src/lib/server/importer.ts
@@ -1,9 +1,11 @@
-import type { Temporal } from "temporal-polyfill";
import sb1 from "./sb1";
+import * as actual from "./actual";
+import type { ImportForm } from "$lib/shared";
-async function importSince(account: string, date: Temporal.Instant) {
- const accounts = await sb1.data.get_accounts()
- for (const account of accounts?.accounts ?? []) {
- const transactions = await sb1.data.get_transactions(account.key);
+async function importSince(form: ImportForm) {
+ for (const mapping of form.mappings) {
+ const transactions = await sb1.data.get_transactions(mapping.sb1Id);
+ if (!transactions?.length) continue
+
}
} \ No newline at end of file
diff --git a/app/src/lib/shared.d.ts b/app/src/lib/shared.d.ts
new file mode 100644
index 0000000..e63904b
--- /dev/null
+++ b/app/src/lib/shared.d.ts
@@ -0,0 +1,5 @@
+export type ImportForm = {
+ budgetId: string,
+ mappings: Array<{ sb1Id: string, actualId: string }>,
+ dryRun: boolean
+} \ No newline at end of file
diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte
index 7b0a495..6f257f6 100644
--- a/app/src/routes/+page.svelte
+++ b/app/src/routes/+page.svelte
@@ -1,18 +1,19 @@
<script lang="ts">
import Button from "$lib/ui/button.svelte";
- import { clear_auth_session, init_auth_session } from "./sb1.remote";
+ import { clear_auth_session, init_auth_session } from "./methods.remote";
import type { PageProps } from "./$types";
import { Temporal } from "temporal-polyfill";
import { instantAsHtmlInputValueString } from "$lib/helpers";
+ import type { ImportForm } from "$lib/shared";
let { data }: PageProps = $props();
let navigating = $state(false);
- let form = $state({
+ let form = $state<ImportForm>({
budgetId: "",
- mappings: [] as Array<{ sb1: string; actual: string }>,
- delta: instantAsHtmlInputValueString(Temporal.Now.instant().subtract("PT24H")),
- dry: true,
+ mappings: [],
+ dryRun: true,
});
+
async function run() {}
async function authorize() {
@@ -27,10 +28,10 @@
location.reload();
}
- function mappingChanged(sb1: string, actual: string) {
+ function onMappingChanged(sb1Id: string, actualId: string) {
let mappings = form.mappings;
- if (mappings.find((c) => c.sb1 === sb1)) mappings = mappings.filter((c) => c.sb1 !== sb1);
- mappings.push({ sb1, actual });
+ if (mappings.find((c) => c.sb1Id === sb1Id)) mappings = mappings.filter((c) => c.sb1Id !== sb1Id);
+ mappings.push({ sb1Id, actualId });
form.mappings = mappings;
}
@@ -55,7 +56,7 @@
<code>{account.name}</code>
<span>&#8594;</span>
<label for={actualId}>Actual</label>
- <select name={actualId} id={actualId} onchange={(e) => mappingChanged(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}>
@@ -66,9 +67,7 @@
</div>
{/each}
<h4>Ellers</h4>
- <label for="delta">Importer transaksjoner siden</label>
- <input type="date" id="delta" bind:value={form.delta} /><br />
- <input type="checkbox" id="dry" bind:checked={form.dry} /><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>
diff --git a/app/src/routes/methods.remote.ts b/app/src/routes/methods.remote.ts
new file mode 100644
index 0000000..c669164
--- /dev/null
+++ b/app/src/routes/methods.remote.ts
@@ -0,0 +1,20 @@
+import { db } from "$lib/server/db";
+import { syncSession } from "$lib/server/db/schema";
+import * as v from "valibot"
+import { command, query } from "$app/server";
+import sb1 from "$lib/server/sb1";
+
+const init_auth_session = command(async () => {
+ return await sb1.auth.init_auth_session()
+})
+
+
+const clear_auth_session = query(async () => {
+ await db.delete(syncSession)
+})
+
+
+export {
+ init_auth_session,
+ clear_auth_session
+} \ No newline at end of file
diff --git a/app/src/routes/sb1.remote.ts b/app/src/routes/sb1.remote.ts
deleted file mode 100644
index 1ee0155..0000000
--- a/app/src/routes/sb1.remote.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { db } from "$lib/server/db";
-import { syncSession } from "$lib/server/db/schema";
-import * as v from "valibot"
-import { command, query } from "$app/server";
-import sb1 from "$lib/server/sb1";
-
-const init_auth_session = command(async () => {
- return await sb1.auth.init_auth_session()
-})
-
-const get_accounts = query(() => {
- return sb1.data.get_accounts()
-})
-
-const get_transactions = query(v.string(), (accountKey: string) => {
- return sb1.data.get_transactions(accountKey)
-})
-
-const clear_auth_session = query(async () => {
- await db.delete(syncSession)
-})
-
-const get_auth_info = query(() => {
- return sb1.auth.get_auth_info()
-})
-
-const refresh_tokem = command(async () => {
- await sb1.auth.refresh_token()
-})
-
-export {
- refresh_tokem,
- init_auth_session,
- get_accounts,
- get_transactions,
- clear_auth_session,
- get_auth_info,
-} \ No newline at end of file