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

23 KiB
Raw Blame History

Импорт банковских выписок за последний год

Создано: 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

Запуск для каждого банка:

./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

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).
  • Через 12 месяца: по собранным данным наличных трат вычислить реальные помесячные паттерны.
  • Экстраполировать на пропущенный год с этими паттернами.
  • Плюс: база для экстраполяции будет основана на реальных данных, а не на исторических.

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.109apps.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):

  • Парсер выписок СберБизнес (production-ready) — читает xlsx/сsv из уже выгруженного файла
  • Заглушки для Альфа, ВТБ, Тинькофф
  • Это парсер уже скачанных файлов, не скрапер

bank_scrapers (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 не была в скрипте:

# 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 от банков