Files
obsidian-vault/personal/projects/budget-app/excel-balances.md
T

4.6 KiB
Raw Blame History

Остатки счетов из Excel (Budget.xlsx)

Файл: ~/Downloads/Budget.xlsx Лист: транзакции

Балансы совпадают с API после фиксов (2026-06-23).

Счёт Баланс Валюта Последняя операция
Нал RUB 815 555.70 RUB 2025-05-16 (R6029, 60 000 deb)
Нал KGS 1 794.00 KGS 2025-06-17 (R6042, deb)
Нал KZT -22 820.00 KZT 2024-12-15 (R5505, deb)
Нал USD 1.00 USD 2025-02-04 (R5976, deb)
Нал AED 0.00 AED 2023-11-16 (R2335, deb)
Нал EUR EUR нет операций
Нал UZS 18 000.35 UZS 2024-03-31 (R3337, deb)
Demir ИП 192 308.54 KGS 2025-01-31 (R5943, deb)
Demir ИП USD 284 842.96 USD 2026-05-29 (R6053, cred)
Demir KGS 96 013.12 KGS 2025-01-31 (R5947, deb)
Demir USD 3 394.29 USD 2025-01-31 (R5950, cred)
Тинькофф Black 521 263.65 RUB 2025-01-31 (R5959, deb)
Тинькофф Кредитка 7 170.17 RUB 2025-01-28 (R5901, deb)
Сбер 266 905.98 RUB 2025-01-28 (R5898, deb)
Сбер Кредитка 3 059.00 RUB 2025-01-31 (R5946, deb)
Альфа 78 030.00 RUB 2025-01-28 (R5925, cred)
Альфа Кредитка 0.50 RUB 2025-01-28 (R5924, deb)
ВТБ 265 178.95 RUB 2025-01-27 (R5887, deb)
ВТБ Кредитка 297 189.00 RUB 2025-01-31 (R5952, deb)
Райффайзен RUB нет операций

Формула API

balance = incoming_transfer + incoming_income - outgoing + initial_balance

Где:

  • incoming_transferSUM(cross_rate * amount) где source IS NOT NULL (cross_rate конвертирует валюту source в валюту dest)
  • incoming_incomeSUM(amount) где source IS NULL (amount уже в валюте счёта, cross_rate — только для отчёта)
  • outgoingSUM(amount) (всегда в валюте счёта-источника)

Фиксы (2026-06-23)

1. incoming для income и transfer — разный расчёт

Было: incoming = SUM(COALESCE(cross_rate, 1.0) * amount) — cross_rate применялся ко всем включая income (где amount уже в валюте счёта). Для KGS-счетов с income-пополнениями (например "Конвертация USD по курсу 87") incoming умножался на 87, давая баланс ×87.

Стало: incoming разделён на две части:

  • incoming_transfer (source IS NOT NULL): cross_rate применяется (конвертирует валюту source→dest)
  • incoming_income (source IS NULL): просто amount (cross_rate — только для отчётной валюты)

2. import_hash и on_conflict_do_nothing — удалены

Было: дедупликация по import_hash = SHA256(date|amount|source|dest|comment). В Excel есть 141 дублирующаяся строка с одинаковыми этими полями (реальные повторные списания). on_conflict_do_nothing молча пропускал их, но tx_count врал что импортировал.

Стало: обычный session.add(tx). Колонка import_hash и уникальный индекс дропнуты из таблицы. Функция _make_import_hash удалена.

3. Accounts — идемпотентность

Было: каждый запуск импорта создавал 20 новых аккаунтов (session.add). После нескольких запусков — 40+ аккаунтов с разными UUID, транзакции привязаны к разным наборам.

Стало: UPSERT — ищет существующий account по name + user_id, переиспользует.

4. UserSettings — UPSERT

Было: session.add(UserSettings(...)) — падало с UniqueViolation при повторном запуске.

Стало: pg_insert(...).on_conflict_do_update(...).

5. Нюанс Excel col I/L

Для некоторых строк Excel не кеширует вычисленные значения col I (остаток деб) — показывает None. Это не баг, а особенность data_only=True — если Excel не пересчитал формулы перед сохранением, кеш пуст. В таких случаях последний корректный баланс берётся из предыдущей строки минус amount.