Add support for fetching only desired transactions related to specified profile

This commit is contained in:
2025-07-08 12:45:43 +02:00
parent 9b24a1d737
commit 32b8d50e5b
7 changed files with 33 additions and 16 deletions

View File

@@ -10,7 +10,7 @@ in
pname = "actual-importer"; pname = "actual-importer";
version = "0.0.1"; version = "0.0.1";
src = ./.; src = ./.;
npmDepsHash = "sha256-yU3LzlExba2hq4EfuEb05TWaIS3gza1r8lu9Q/myXqE="; npmDepsHash = "sha256-GHNfMeFg5UB/A031NYYjne9M8rXr1zeYCmBH2/FARt8=";
postInstall = '' postInstall = ''
mkdir -p $out/lib/node_modules/actual-importer/public mkdir -p $out/lib/node_modules/actual-importer/public

View File

@@ -14,9 +14,9 @@ export type SubmitOptions =
type ActualTransaction = { type ActualTransaction = {
id?: string; id?: string;
account?: string; account: string;
date: string; date: string;
amount?: number; amount: number;
payee?: string; payee?: string;
payee_name?: string; payee_name?: string;
imported_payee?: string; imported_payee?: string;
@@ -51,7 +51,7 @@ export class Actual {
} }
#map(transaction: Transaction, accounts: ActualAccount[], payees: ActualPayee[]): ActualTransaction { #map(transaction: Transaction, accounts: ActualAccount[], payees: ActualPayee[]): ActualTransaction {
const actualTransaction: ActualTransaction = { const actualTransaction: Omit<ActualTransaction, 'account'> & { account?: string } = {
imported_id: transaction.id, imported_id: transaction.id,
date: transaction.date, date: transaction.date,
amount: utils.amountToInteger(transaction.amount), amount: utils.amountToInteger(transaction.amount),
@@ -94,7 +94,7 @@ export class Actual {
break; break;
} }
return actualTransaction; return actualTransaction as ActualTransaction;
} }
async #api<T>(fn: () => Promise<T>): Promise<T> { async #api<T>(fn: () => Promise<T>): Promise<T> {
@@ -194,7 +194,8 @@ export class Actual {
const query = api.q('transactions') const query = api.q('transactions')
.limit(limit) .limit(limit)
.select(['*']); .select(['*'])
.options({ splits: 'grouped' });
const { data } = await api.runQuery(query) as { data: ActualTransaction[] }; const { data } = await api.runQuery(query) as { data: ActualTransaction[] };
@@ -209,7 +210,8 @@ export class Actual {
const query = api.q('transactions') const query = api.q('transactions')
.filter({ date: [{ $gte: start }, { $lte: end }] }) .filter({ date: [{ $gte: start }, { $lte: end }] })
.select(['*']); .select(['*'])
.options({ splits: 'grouped' });
const { data } = await api.runQuery(query) as { data: ActualTransaction[] }; const { data } = await api.runQuery(query) as { data: ActualTransaction[] };

View File

@@ -4,6 +4,7 @@ import express from "express";
import multer from "multer"; import multer from "multer";
import { Readable } from "stream"; import { Readable } from "stream";
import path from 'path'; import path from 'path';
import { Transaction } from "@/types/transaction";
export function serve(config: Config, port: number) { export function serve(config: Config, port: number) {
@@ -22,18 +23,23 @@ export function serve(config: Config, port: number) {
app.get("/servers/:server/transactions", async (req, res) => { app.get("/servers/:server/transactions", async (req, res) => {
const { start, end } = req.query; const { start, end } = req.query;
let data: Transaction[]|undefined;
if (start && end) { if (start && end) {
const data = await getTransactionsFromRange(req.params.server, config, start.toString(), end.toString()); data = await getTransactionsFromRange(req.params.server, config, start.toString(), end.toString());
res.json(data);
} }
else { else {
const limitParam = req.query.limit?.toString(); const limitParam = req.query.limit?.toString();
const limit = (limitParam && Number.parseInt(limitParam)) || undefined; const limit = (limitParam && Number.parseInt(limitParam)) || undefined;
const data = await getTransactions(req.params.server, config, limit); data = await getTransactions(req.params.server, config, limit);
res.json(data);
} }
const profile = req.query.profile?.toString() || undefined;
const supportedAccounts = profile !== undefined ? config.profiles[profile].supportedAccounts : undefined;
const filterAccounts = supportedAccounts !== undefined ? (t: Transaction) => supportedAccounts.includes(t.from) || supportedAccounts.includes(t.to) : () => true;
res.json(data.filter(filterAccounts))
}); });
app.post("/prepare", upload.single("file"), async (req, res) => { app.post("/prepare", upload.single("file"), async (req, res) => {

View File

@@ -11,6 +11,7 @@ export type ProfileConfig = {
encoding?: string; encoding?: string;
config?: ParserConfig; config?: ParserConfig;
csv?: Record<string, unknown>; csv?: Record<string, unknown>;
supportedAccounts?: string[];
}; };
export type ParserConfig = { export type ParserConfig = {

View File

@@ -7,5 +7,5 @@ buildNpmPackage {
pname = "actual-importer-frontend"; pname = "actual-importer-frontend";
version = "0.0.1"; version = "0.0.1";
src = ./.; src = ./.;
npmDepsHash = "sha256-/G8OZBAkCuc0LTmpB1v8HTgaWdYd9AJfZTC0/eUQIx0="; npmDepsHash = "sha256-Xh+zpYX8u+wKuvBjGc4hxUI2Ed4tiXKC3zk8kFExBbc=";
} }

View File

@@ -79,7 +79,7 @@ export default function PrepareTransactionsPage() {
// If no "from" filter active, pull the latest 10 transactions // If no "from" filter active, pull the latest 10 transactions
if (!debouncedUserFilter.from) { if (!debouncedUserFilter.from) {
const transactions = await getLatestTransactions(state.server, 10); const transactions = await getLatestTransactions(state.server, 10, state.profile);
setExistingTransactions(transactions); setExistingTransactions(transactions);
return; return;
} }
@@ -87,7 +87,7 @@ export default function PrepareTransactionsPage() {
const start = debouncedUserFilter.from.format("YYYY-MM-DD"); const start = debouncedUserFilter.from.format("YYYY-MM-DD");
const end = (debouncedUserFilter.to ?? dayjs()).format("YYYY-MM-DD"); const end = (debouncedUserFilter.to ?? dayjs()).format("YYYY-MM-DD");
const transactions = await getDateRangedTransactions(state.server, start, end); const transactions = await getDateRangedTransactions(state.server, start, end, state.profile);
setExistingTransactions(transactions); setExistingTransactions(transactions);
}, [enabledExistingTransactions, state.server, debouncedUserFilter]); }, [enabledExistingTransactions, state.server, debouncedUserFilter]);

View File

@@ -40,21 +40,29 @@ export async function submitTransactions(transactions: Transaction[], server: st
return data as SubmitResponse; return data as SubmitResponse;
} }
export async function getLatestTransactions(server: string, limit: number = 5): Promise<Transaction[]> { export async function getLatestTransactions(server: string, limit: number = 5, profile?: string): Promise<Transaction[]> {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append('limit', limit.toString()); params.append('limit', limit.toString());
if (profile !== undefined) {
params.append('profile', profile);
}
const response = await fetch(`/servers/${server}/transactions?${params.toString()}`); const response = await fetch(`/servers/${server}/transactions?${params.toString()}`);
const data = await response.json(); const data = await response.json();
return data as Transaction[]; return data as Transaction[];
} }
export async function getDateRangedTransactions(server: string, start: string, end: string): Promise<Transaction[]> { export async function getDateRangedTransactions(server: string, start: string, end: string, profile?: string): Promise<Transaction[]> {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append('start', start); params.append('start', start);
params.append('end', end); params.append('end', end);
if (profile !== undefined) {
params.append('profile', profile);
}
const response = await fetch(`/servers/${server}/transactions?${params.toString()}`); const response = await fetch(`/servers/${server}/transactions?${params.toString()}`);
const data = await response.json(); const data = await response.json();
return data as Transaction[]; return data as Transaction[];