import { ACTUAL_FILE_ID, ACTUAL_HOST, ACTUAL_PASS } from "$env/static/private"; import * as actualApi from "@actual-app/api" import { existsSync, mkdirSync } from "node:fs"; import path from "node:path" import process from "node:process"; import type { ImportTransactionEntity } from "@actual-app/api/@types/loot-core/src/types/models/import-transaction"; import { Temporal } from "temporal-polyfill"; import type { Sb1Transaction } from "$lib/shared"; let inited = false async function init() { if (inited) return const dataDir = path.resolve(process.cwd(), "data/actualDataDir") if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true }); await actualApi.init({ password: ACTUAL_PASS, serverURL: ACTUAL_HOST, dataDir }) await actualApi.downloadBudget(ACTUAL_FILE_ID) await actualApi.sync() inited = true } const budget = { async get_budgets() { await init() return actualApi.getBudgets() }, async get_accounts() { await init() return actualApi.getAccounts() }, async import_transactions(account: string, transactions: Sb1Transaction[], dryRun: boolean) { await init() function parsedDate(date: number) { return Temporal.Instant.fromEpochMilliseconds(date) .toString({ timeZone: "Europe/Oslo" }) .split("T")[0] } function notes(transaction: Sb1Transaction) { const { description, cleanedDescription } = transaction if (description?.toLowerCase().trim() === cleanedDescription?.toLowerCase().trim()) return undefined return description } 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 })) return actualApi.importTransactions(account, mapped, { dryRun }) } } export default { init, budget }