feat: улучшить импорт CSV — дедупликация, кодировка, парсинг сумм

Пять доработок импорта транзакций из CSV:
1. Проверка дублей при повторном импорте — сверка с уже существующими
   транзакциями на счёте за диапазон дат файла (по дате+типу+сумме+
   описанию), похожие исключаются из импорта по умолчанию с опцией
   всё равно их добавить.
2. Автоопределение кодировки файла (UTF-8 с откатом на Windows-1251)
   вместо жёстко зашитого UTF-8 — раньше кириллица из банковских
   выгрузок превращалась в кракозябры. Есть ручной выбор на случай
   неверного автоопределения.
3. Исправлен баг в parseAmount: суммы с несколькими разделителями
   тысяч и без дробной части (например "1,234,567") теряли цифры и
   отбрасывались как нераспознанные — теперь распознаются верно.
4. Добавлен явный выбор разделителя дробной части (точка/запятая) на
   случай, когда автоопределение не может разрешить неоднозначность.
5. Пример значения из файла показывается прямо под каждым выбором
   колонки при сопоставлении — не нужно доходить до превью, чтобы
   понять, что в колонке.
This commit is contained in:
ZProger
2026-09-10 20:06:41 +03:00
parent 78826db33e
commit 777e3af725
5 changed files with 310 additions and 52 deletions

View File

@@ -3,6 +3,7 @@ import {
bulkCreateTransactions,
createTransaction,
deleteTransaction,
fetchAllTransactionsInRange,
fetchTransactions,
fetchTransactionYears,
updateTransaction,
@@ -33,6 +34,21 @@ export function useTransactions(filters: TransactionFilters) {
});
}
/** CSV import's duplicate check (see pages/CsvImportPage.tsx) — every
* existing transaction for one account within the imported date range, so
* re-importing an overlapping bank export can be told apart from new rows. */
export function useTransactionsForDuplicateCheck(
accountId: number | null,
startDate: string | null,
endDate: string | null
) {
return useQuery({
queryKey: ["transactions-duplicate-check", accountId, startDate, endDate],
queryFn: () => fetchAllTransactionsInRange({ account_id: accountId!, start_date: startDate!, end_date: endDate! }),
enabled: accountId !== null && startDate !== null && endDate !== null,
});
}
export function useTransactionYears() {
return useQuery({
queryKey: ["transaction-years"],