mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-16 20:46:39 +07:00
Пять доработок импорта транзакций из CSV: 1. Проверка дублей при повторном импорте — сверка с уже существующими транзакциями на счёте за диапазон дат файла (по дате+типу+сумме+ описанию), похожие исключаются из импорта по умолчанию с опцией всё равно их добавить. 2. Автоопределение кодировки файла (UTF-8 с откатом на Windows-1251) вместо жёстко зашитого UTF-8 — раньше кириллица из банковских выгрузок превращалась в кракозябры. Есть ручной выбор на случай неверного автоопределения. 3. Исправлен баг в parseAmount: суммы с несколькими разделителями тысяч и без дробной части (например "1,234,567") теряли цифры и отбрасывались как нераспознанные — теперь распознаются верно. 4. Добавлен явный выбор разделителя дробной части (точка/запятая) на случай, когда автоопределение не может разрешить неоднозначность. 5. Пример значения из файла показывается прямо под каждым выбором колонки при сопоставлении — не нужно доходить до превью, чтобы понять, что в колонке.
72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
import { api } from "@/api/client";
|
|
import type { Transaction, TransactionInput, TransactionPage } from "@/types";
|
|
|
|
export type TransactionSort = "date_desc" | "amount_desc" | "amount_asc";
|
|
|
|
export interface TransactionFilters {
|
|
year?: number;
|
|
month?: number;
|
|
start_date?: string;
|
|
end_date?: string;
|
|
account_id?: number;
|
|
category_id?: number;
|
|
tag_id?: number;
|
|
type?: string;
|
|
search?: string;
|
|
sort?: TransactionSort;
|
|
page?: number;
|
|
page_size?: number;
|
|
}
|
|
|
|
export function fetchTransactions(filters: TransactionFilters = {}) {
|
|
const params = new URLSearchParams();
|
|
Object.entries(filters).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null) params.set(key, String(value));
|
|
});
|
|
return api.get<TransactionPage>(`/transactions?${params.toString()}`);
|
|
}
|
|
|
|
// Used by CSV import's duplicate check (see pages/CsvImportPage.tsx) — needs
|
|
// every transaction in the imported date range for one account, not just a
|
|
// page of them, to know what's already there. Capped at 25 pages (5000 rows
|
|
// at the API's max page_size) so a huge date range can't turn one import
|
|
// into an unbounded fetch loop; beyond that, duplicate detection just
|
|
// silently covers less of the range instead of hanging.
|
|
const MAX_DUPLICATE_CHECK_PAGES = 25;
|
|
|
|
export async function fetchAllTransactionsInRange(filters: TransactionFilters): Promise<Transaction[]> {
|
|
const pageSize = 200;
|
|
const items: Transaction[] = [];
|
|
for (let page = 1; page <= MAX_DUPLICATE_CHECK_PAGES; page++) {
|
|
const result = await fetchTransactions({ ...filters, page, page_size: pageSize });
|
|
items.push(...result.items);
|
|
if (items.length >= result.total || result.items.length < pageSize) break;
|
|
}
|
|
return items;
|
|
}
|
|
|
|
/** Full range of years to offer in the year picker, from the earliest
|
|
* transaction through the current year (see backend for the "gap year"
|
|
* rationale). */
|
|
export function fetchTransactionYears() {
|
|
return api.get<number[]>("/transactions/years");
|
|
}
|
|
|
|
export function createTransaction(input: TransactionInput) {
|
|
return api.post<Transaction>("/transactions", input);
|
|
}
|
|
|
|
/** CSV import — see pages/CsvImportPage.tsx. All-or-nothing on the backend:
|
|
* either every row is created, or (on a validation error) none are. */
|
|
export function bulkCreateTransactions(items: TransactionInput[]) {
|
|
return api.post<{ created: number }>("/transactions/bulk", { items });
|
|
}
|
|
|
|
export function updateTransaction(id: number, input: Partial<TransactionInput>) {
|
|
return api.patch<Transaction>(`/transactions/${id}`, input);
|
|
}
|
|
|
|
export function deleteTransaction(id: number) {
|
|
return api.delete<void>(`/transactions/${id}`);
|
|
}
|