feat(expenses): add import functionality for expenses and total pending calculation

This commit is contained in:
Jose Selesan
2026-05-29 14:30:10 -03:00
parent 4b17940134
commit b1968cece9
11 changed files with 600 additions and 19 deletions

View File

@@ -188,6 +188,10 @@ export function getExpenses(status?: string, page?: number, pageSize?: number):
return fetcher<PaginatedResponse<Expense>>(`/api/expenses${qs ? `?${qs}` : ""}`);
}
export function getTotalPending(): Promise<MonthlyExpensesTotal> {
return fetcher<MonthlyExpensesTotal>("/api/expenses/pending-total");
}
export function getMonthlyTotals(year?: number, month?: number): Promise<MonthlyTotals> {
const params = new URLSearchParams();
if (year) params.set("year", String(year));
@@ -202,3 +206,41 @@ export function createNonPeriodicExpense(data: CreateNonPeriodicExpenseInput): P
export function payExpense(id: number, data: PayExpenseInput): Promise<Expense> {
return mutator<Expense>(`/api/expenses/${id}/pay`, "PUT", data);
}
export type ImportResult = {
imported: number;
skipped: number;
skippedNoPeriods: number;
skippedDuplicate: number;
errors?: string[];
};
export type ImportExpensesResult = {
imported: number;
skipped: number;
errors?: string[];
};
export function importExpenses(file: File): Promise<ImportExpensesResult> {
const formData = new FormData();
formData.append("file", file);
return fetch("/api/expenses/import", {
method: "POST",
body: formData,
}).then((r) => {
if (!r.ok) throw new Error(`API error: ${r.status} ${r.statusText}`);
return r.json();
});
}
export function importPeriodicExpenses(file: File): Promise<ImportResult> {
const formData = new FormData();
formData.append("file", file);
return fetch("/api/periodic-expenses/import", {
method: "POST",
body: formData,
}).then((r) => {
if (!r.ok) throw new Error(`API error: ${r.status} ${r.statusText}`);
return r.json();
});
}