Files
obsidian-vault/personal/projects/budget-app/bank-statement-import.md
T

356 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Импорт банковских выписок за последний год
**Создано:** 2026-06-23
**Цель:** Получить все транзакции за последний год (середина 2025 — июнь 2026) в Budget App, автоматизируя импорт банковских выписок как можно полнее.
## Проблема
В `Budget.xlsx` данные заканчиваются в **середине 2025** (май-июнь 2025, в зависимости от счёта). Последние ~12 месяцев транзакций не внесены в Excel. Вручную вспомнить каждую трату за год — нереалистично.
## Существующий инструмент: budget-bank-statement-converter
**Путь:** `~/Developer/budget-bank-statement-converter/`
**Язык:** Swift (macOS command-line tool)
**Формат вывода:** CSV с колонками `[дата, сумма, дебет, кредит, категория, комментарий, курс]` — совпадает с форматом Excel.
### Поддерживаемые банки
| Банк | Формат входа | Конфиг | Статус |
|------|-------------|--------|--------|
| Demir | CSV (из PDF → Adobe Extract → CSV) | `demir-config.json` | ✅ Работает |
| Сбер | CSV (выгрузка из СберБизнес) | `sber-config.json` | ✅ Работает |
| Тинькофф | CSV (выгрузка из Тинькофф) | `tinkoff-config.json` | ✅ Работает |
| ВТБ | CSV (из PDF → Adobe Extract → CSV) | `vtb-config.json` | ✅ Работает |
| Альфа | — | — | ❌ `fatalError("Alfa not implemented")` |
### Как работает
1. **PDF → CSV**: использует Adobe PDF Extract API (`pdfservices-api-credentials.json`) — загружает PDF, получает ZIP с CSV-таблицами.
2. **CSV → формат App**: разбирает CSV, маппит категории через regex-конфиг, нормализует double-entry (дебет/кредит/конверсии).
3. Если нужно — автоматически конкатенирует `fileoutpart0001.csv`… файлы.
4. Использует OpenAI GPT-3.5-turbo для AI-категоризации (закомментировано, `aiMaxTokens = 80`).
### Что нужно для использования
- Xcode (для сборки Swift-проекта)
- `OPENAI_API_KEY` в env (не обязательно, выключено)
- `pdfservices-api-credentials.json` для Adobe Extract
- JSON config для каждого банка: `Сбер config`, `Tinkoff config`, `Demir config`, `VTB config`
## План импорта
### Шаг 1: Получить выписки из банков
**Что нужно выгрузить за июнь 2025 — июнь 2026:**
| Счёт | Банк | Как получить выписку |
|------|------|---------------------|
| Нал RUB | Наличные | Ручной ввод (см. ниже про наличные) |
| Нал KGS | Наличные | Ручной ввод |
| Нал USD | Наличные | Ручной ввод |
| Нал KZT | Наличные | Ручной ввод |
| Demir ИП | Demir | CSV (интернет-банк/моб. приложение) |
| Demir ИП USD | Demir | CSV (интернет-банк/моб. приложение) |
| Demir KGS | Demir | CSV (интернет-банк/моб. приложение) |
| Demir USD | Demir | CSV (интернет-банк/моб. приложение) |
| Тинькофф Black | Тинькофф | CSV (выгрузка из Тинькофф) |
| Тинькофф Кредитка | Тинькофф | CSV (выгрузка из Тинькофф) |
| Сбер | Сбер | CSV (СберБизнес / PDF) |
| Сбер Кредитка | Сбер | CSV (СберБизнес / PDF) |
| Альфа | Альфа | CSV — но конвертер Альфу не поддерживает |
| Альфа Кредитка | Альфа | — |
| ВТБ | ВТБ | PDF → Adobe Extract → CSV |
| ВТБ Кредитка | ВТБ | PDF → Adobe Extract → CSV |
### Шаг 2: Конвертировать выписки в CSV формата App
Запуск для каждого банка:
```bash
./budget-bank-statement-converter --bank <bank> --account <account> <input.csv>
```
Выход: `_processed.csv` с колонками `дата, сумма, дебет, кредит, категория, комментарий, курс`.
### Шаг 3: Написать Python импортёр CSV → Budget App DB
Существующий `xlsx_import.py` читает из Excel. Нужен новый: `csv_bank_import.py`, который:
- Читает CSV в формате App (колонки из `csvHeaders` в `Common.swift`)
- Привязывает `дебет`/`кредит` к существующим счетам в БД (по имени)
- Маппит категории из CSV на существующие категории в БД (по имени подкатегории)
- Игнорирует дубликаты (hash по `date + amount + source + dest + comment`)
- Поддерживает несколько CSV-файлов за раз (много выписок)
- Выводит отчёт: сколько добавлено, сколько пропущено (дубликаты), какие категории не найдены
### Шаг 4: Импортировать в Budget App
```bash
cd ~/Developer/budget-app
uv run python src/budget/importers/csv_bank_import.py <output1.csv> <output2.csv> ...
```
### Шаг 5: Дописать недостающее в Swift-конвертере
- **AlfaToCSV**: реализовать парсер для Альфа-банка (CSV выгрузка из моб. банка/СберБизнес)
- **AI-категоризация**: раскомментировать и обновить (GPT-3.5 → DeepSeek/local LLM?)
## Наличные расходы — проблема и решение
### Проблема
Наличные траты не трекались последний год. У нас есть конечный остаток налички на руках сейчас, но нет истории по категориям.
### Подходы
#### A. Снять остаток наличных сейчас → счёт в БД (простой)
- Посчитать физическую наличку сейчас → записать как `initial_balance` для `Нал RUB`, `Нал KGS`, `Нал USD`, `Нал KZT`.
- Все траты наличными за год никогда не будут зафиксированы.
- **Минус:** дыра в данных большого объёма (вероятно значительная часть расходов).
#### B. Экстраполяция по историческим трендам (средний)
- Взять помесячные тренды наличных трат по категориям за 2023–первую половину 2025.
- Экстраполировать на июнь 2025 — июнь 2026 с учётом сезонности.
- Создать транзакции-плейсхолдеры с пометкой `import_id = 'cash_estimate'`.
- **Минус:** неточность, может не отражать реальные изменения.
#### C. Ретроспектива через месяц-два + экстраполяция (предпочтительный)
- **Сейчас:** начать трекать наличные расходы (вручную или через мобильный интерфейс Budget App).
- **Через 1–2 месяца:** по собранным данным наличных трат вычислить реальные помесячные паттерны.
- Экстраполировать на пропущенный год с этими паттернами.
- **Плюс:** база для экстраполяции будет основана на реальных данных, а не на исторических.
#### D. None of the above — принять дыру
- Сделать только безналичный импорт. Наличные начинаем трекать с сегодня.
- В аналитике отмечать периоды как "без наличных".
- **Плюс:** не надо ничего выдумывать.
### Рекомендация: C+D combined
1. Трекать наличку вручную через UI Budget App начиная с сегодня.
2. Через 2 месяца посчитать реальные тренды и решить, стоит ли экстраполировать на прошлый год.
3. Если нет — просто принять дыру и жить с хорошей аналитикой начиная с 2026-06.
## Что уже реализовано в Budget App для импорта
-`xlsx_import.py` — полный импорт из Budget.xlsx (34k строк)
- ✅ Все счета, категории, курсы, транзакции — в БД
- ✅ Идемпотентный UPSERT для счетов и курсов
- ✅ Транзакции добавляются обычным insert (без import_hash после фикса)
## Реализованный скрипт: bank_scraper
**Путь:** `~/Developer/budget-app/scripts/bank_scraper/`
Структура:
```
scripts/bank_scraper/
├── __init__.py
├── .gitignore # config.yaml + data/imports/ не коммитятся
├── config.example.yaml # шаблон для копирования в config.yaml
├── base_driver.py # base class BankDriver + load_config()
├── orchestrator.py # entry point (н.п.)
└── drivers/
└── demir.py # Demir IB драйвер (н.п.)
```
**Статус:** ✅ base + Demir driver написаны, Playwright установлен. **НО — Demir требует QR-логин через мобильное приложение**, не логин/пароль на сайте.
## Реальность Demir IB
Сайт `93.171.215.109``apps.demirbank.kg/ib/`) — **Flutter web SPA** с QR-аутентификацией. Нет формы логина с паролем — нужно сканировать QR мобильным приложением Demir.
**Варианты решения:**
### A. Продолжить с Playwright + session persistence
- Один раз залогиниться руками (QR → моб. приложение)
- Сохранить session cookies/storage в persistent context
- Дальше переиспользовать сессию для выгрузок (пока не протухнет)
- **Плюс:** минимум кода
- **Минус:** сессия рано или поздно протухнет, нужен ручной ре-логин
### B. Appium / ADB — эмуляция мобильного приложения
- Демонстратор Android/iOS эмулятора с мобильным приложением Demir
- Appium для UI automation внутри приложения
- **Плюс:** полный контроль
- **Минус:** сложно, накладно
### C. Заменить Demir на первый банк с логином/паролем
- Тинькофф имеет API для разработчиков (OAuth)
- Сбер — есть API SberBusinessAPI (хотя для юрлиц)
- Можно начать с Тинькофф: Tinkoff API → выписка без браузера
- **Плюс:** самый простой tech-wise
- **Минус:** Demir пока под вопросом
### D. Парсить CSV выписки, которые уже есть в mobile/email
- Возможно Demir присылает выписки на email
- Или можно скачать через мобильное приложение → экспорт → AirDrop/email себе
- Это полу-ручной подход (но быстрее чем QR scraping)
## Решение
**Рекомендация: A + D**
1. Самый ценный банк — **Тинькофф** (есть API) — начинаем с него
2. Demir — разово выгрузить через мобильное приложение (Export CSV/email)
3. Если сессия Demir долго живёт — Playwright persistent context отработает
### Новый порядок разработки
1. ✅ Demir driver (написан, но упирается в QR)
2. **Tinkoff API driver** — следующий приоритет (без браузера, REST API)
3. **Сбер / ВТБ / Альфа** — Playwright или Tinkoff-style API
4. **Parse Demir CSV** — Python-версия DemirToCSV для уже скачанных файлов
## Файл вывода Swift-конвертера
```
csvHeaders = ["дата", "сумма", "дебет", "кредит", "категория", "комментарий", "курс"]
```
- `дата``dd.MM.yyyy HH:mm` (формат EUR)
- `сумма` — строка с суммой (±знак)
- `дебет` — имя счёта-источника (пусто = доход извне)
- `кредит` — имя счёта-получателя (пусто = расход вовне)
- `категория` — имя подкатегории
- `комментарий` — очищенный текст
- `курс` — кросс-курс при внутреннем переводе между валютами
## Автоматизация выгрузки выписок из банков
### 1. Browser automation libraries (CV-driven)
| Библиотека | Язык | Браузеры | CV | 2FA/SMS |
|-----------|------|----------|----|---------|
| **Playwright** (MS) | Python, JS, Java, .NET ⭐ | Chromium, Firefox, WebKit | Есть (locator screenshots) | `page.wait_for_selector` на поле ввода кода |
| **Puppeteer** (Google) | JS (Python через pyppeteer) | Chromium | Есть | — |
| **Selenium** | Python, Java, JS и др. | Все major | Через сторонние утилиты | — |
**Рекомендация: Playwright Python** — де-факто стандарт в 2025, cross-browser, async, видит элементы даже в SPA, встроенные ожидания. Подходит и для РФ-банков (Сбер, Тинькофф, Альфа-клик — все на SPA).
### 2. Готовые решения на GitHub
**AploBankParsers** ([github.com/Zaurrex1/AploBankParsers](https://github.com/Zaurrex1/AploBankParsers)):
- Парсер выписок **СберБизнес** (production-ready) — читает xlsx/сsv из уже выгруженного файла
- Заглушки для Альфа, ВТБ, Тинькофф
- Это парсер **уже скачанных файлов**, не скрапер
**bank_scrapers** ([github.com/eebette/bank_scrapers](https://github.com/eebette/bank_scrapers)):
- Playwright-based для scraping bank websites
- Generic, не специфичен под РФ-банки
**Sber API** — официальный REST API Сбера:
- `developers.sber.ru/docs/ru/sber-api/specifications/statement/transactions`
- Получение выписки по счёту за 5 лет
- **Требует** корпоративного доступа (SberBusinessAPI / ДБО), не подойдёт для личного СберБанк
**Готового решения "под ключ" для РФ-банков** (Playwright → bank login → 2FA → CSV выписка) **нет** в открытом доступе. Каждый банк — свой уникальный UI и flow. Придётся писать самим.
### 3. Архитектура скрипта
```
┌─────────────────────────────────┐
│ Telegram Bot (Hermes/кит) │ ← запрашивает SMS-код
├─────────────────────────────────┤
│ Orchestrator (Python) │ ← запускает по крону / кнопке
│ ┌─────────────────────────┐ │
│ │ Playwright browser │ │ ← drives bank login page
│ │ - headless=false │ │ (visible для отладки)
│ │ - persistent context │ │ (сессия не слетает)
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ Bank drivers: │ │
│ │ - tinkoff.py │ │
│ │ - sber.py │ │
│ │ - alfa.py │ │
│ │ - demir.py │ │
│ │ - vtb.py │ │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ Output: CSV в формате │ │
│ │ budget-bank-statement- │ │
│ │ converter │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
```
### 4. Flow для каждого банка
```
1. Запустить headless Playwright (или visible=False для отладки)
2. Открыть страницу логина банка
3. Ввести credentials (из конфига, НЕ скрипта)
4. Если запрошен SMS-код:
→ отправить в Telegram: "Код из смс для {bank}:"
→ ждать ответа (polling/async)
→ ввести полученный код
5. Дождаться загрузки дашборда
6. Перейти на страницу выписок/истории
7. Указать период: 2025-06-01 — 2026-06-23
8. Скачать CSV/Excel
9. Сохранить в ~/Developer/budget-app/data/imports/{bank}/{date}.csv
10. Конвертировать через budget-bank-statement-converter (или Python-версию)
11. Импортировать в БД
12. Закрыть браузер
```
### 5. Обработка SMS-кодов (Telegram)
Скрипт не должен хранить сессию банка, каждый запуск — новая авторизация.
**Варианты:**
1. **Telegram Bot (inline keyboard)**: скрипт ждёт сообщение, когда нужен код — присылает кнопку "Отправить код для {bank}", пользователь вводит → скрипт вставляет
2. **Hermes-агент**: крон-джоб спрашивает в Telegram нужный код, ждёт ответа через webhook
3. **Простой stdin**: скрипт пишет "Введите код для Тинькофф:" и ждёт ввод (если запуск из терминала)
**Рекомендация: вариант 1** — TG bot минимальная зависимость, полный контроль.
Для реализации: существующий Hermes/Zulip может служить relay. Или простой скрипт на Python + python-telegram-bot с `await incoming_message`.
### 6. Чувствительность данных — ограничения
Скрипт будет:
- Знать **логины/пароли** банков (хранятся в локальном конфиге, НЕ в коде)
- Открывать **браузер на машине Алекса** (никаких VPN/прокси)
- Передавать только SMS-коды через TG — пароли не передаются
- Работать **локально**, без LLM/агентов в browser automation
Код пишем так, чтобы ни одна строка credentials не была в скрипте:
```python
# config.yaml (chmod 600)
banks:
tinkoff:
login: "7999..."
password: "..."
phone: "7999..."
sber:
login: "..."
password: "..."
```
### 7. Альтернатива: API банков (без browser)
| Банк | REST API для личных счетов | Комментарий |
|------|---------------------------|-------------|
| Тинькофф | Есть (Tinkoff API для разработчиков) | Требует регистрации приложения, OAuth |
| Сбер | Sber API для юрлиц, нет для личных | Не подходит |
| Альфа | Альфа-Бизнес API (юрлица) | Не подходит |
| Demir | Нет публичного API | — |
| ВТБ | Нет публичного API | — |
Тинькофф — единственный из списка, у кого есть адекватный API для физлиц (Tinkoff API / Tinkoff Invest API). Можно получить выписку через API, без browser. Остальные — только SPA scraping.
**Код:** 10 swift-файлов, ~2 400 строк.
**Что хорошо:**
- Хорошая архитектура: каждый банк = отдельный struct с чётким интерфейсом
- Конфиги вынесены из кода (JSON)
- Regex-маппинг категорий гибкий
- Умеет объединять multi-part CSV и извлекать из PDF через Adobe API
- Формат вывода совпадает со структурой Excel/Budget App
**Чего не хватает:**
- Парсер Альфа-банка (только заглушка)
- AI-категоризация закомментирована (GPT-3.5, устарела)
- Нет интеграции с Budget App (только → CSV, не → БД)
- Нет обработки для Demir ИП USD / Demir USD / Demir KGS отдельно (DemirToCSV один конфиг на все)
- PDF-парсер привязан к Adobe PDF Extract API (платный сервис, credentials нужны)
- Нет обработки для Сбер Кредитка как отдельного счёта (SberToCSV один конфиг)
- Нет автоматического определения новых форматов CSV от банков