From 69448e29a85cad3a94b3be3ad33efbc52764528f Mon Sep 17 00:00:00 2001 From: ivar Date: Mon, 9 Mar 2026 23:05:38 +0100 Subject: Add wip cli --- cli/src/actual.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 cli/src/actual.ts (limited to 'cli/src/actual.ts') diff --git a/cli/src/actual.ts b/cli/src/actual.ts new file mode 100644 index 0000000..a236013 --- /dev/null +++ b/cli/src/actual.ts @@ -0,0 +1,51 @@ +import * as actualApi from "@actual-app/api" +import { existsSync, mkdirSync } from "node:fs" +import { join } from "node:path" +import { Temporal } from "temporal-polyfill" +import { CONFIG_DIR } from "./config" +import type { Config } from "./config" +import type { Sb1Transaction, ActualAccount } from "./types" +import type { ImportTransactionEntity } from "@actual-app/api/@types/loot-core/src/types/models/import-transaction" + +let inited = false + +export async function initActual(config: Config["actual"]) { + if (inited) return + const dataDir = join(CONFIG_DIR, "actualDataDir") + if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true }) + process.env.ACTUAL_DATA_DIR = dataDir + await actualApi.init({ password: config.password, serverURL: config.host, dataDir }) + await actualApi.downloadBudget(config.fileId) + await actualApi.sync() + inited = true +} + +export async function getAccounts(config: Config["actual"]): Promise { + await initActual(config) + return actualApi.getAccounts() as Promise +} + +export async function importTransactions( + config: Config["actual"], + accountId: string, + transactions: Sb1Transaction[], + dryRun: boolean +) { + await initActual(config) + + const mapped: ImportTransactionEntity[] = transactions + .filter(t => t.bookingStatus === "BOOKED") + .map(t => ({ + account: accountId, + date: Temporal.Instant.fromEpochMilliseconds(t.date) + .toString({ timeZone: "Europe/Oslo" }) + .split("T")[0], + amount: Math.round(t.amount * 100), + payee_name: t.cleanedDescription, + notes: t.description?.toLowerCase().trim() !== t.cleanedDescription?.toLowerCase().trim() + ? t.description + : undefined + })) + + return actualApi.importTransactions(accountId, mapped, { dryRun }) +} -- cgit v1.3