Схемы Что нового

Банковские выписки

Автоматический разбор банковских выписок: банки и парсеры, очередь модерации спорных платежей, журнал импортов.

Содержание раздела

Банковские выписки (автообработка)

Обучающее видео 2:43

Как разбирать поступления по безналу: очередь платежей, привязка платежа к клиенту, сводные реестры приёмщиков платежей, вкладка «Банк» в карточке клиента и работа с телефона.

1920×1080 с озвучкой Скачать

Модуль автоматического приёма и зачисления платежей из банковских выписок. Письма с CSV/TXT-вложениями приходят на собственный почтовый ящик bank@вашдомен.ru (на почтовом сервере компании), парсер раскладывает операции в очередь модерации, matching сопоставляет с клиентами по 4 стратегиям (ИНН/лицевой счёт/назначение/fuzzy), оператор подтверждает или система автоматически зачисляет high-confidence матчи. Для юр.лиц генерируются PDF счёт-фактуры и акты (WeasyPrint), опционально загружаются в GCS и отправляются клиенту на email через noreply@вашдомен.ru.

Полная карта URL:

Архитектура (8 фаз)

  1. SmitMailServ на demo-сервере сервер приложения — 4 ящика (bank/noreply/info/support) на почтовом поддомене, MX/SPF/DKIM/DMARC в cloudns.net
  2. IMAP-приёмник (billing/services/imap_client.py) — Celery beat bank-statements-pull ежечасно, реально работает в час/минуту из BANK_PULL_SCHEDULE_HOUR/MINUTE (default 6:00)
  3. Парсеры (billing/services/bank_parsers/) — sber.py (TXT, cp1251, разделитель ;), alfa.py (CSV, cp1251, разделитель \t), dispatcher.py определяет формат по filename + первым байтам
  4. Matching (bank_matching.py) — 4 стратегии в порядке приоритета: inn → account → purpose → fuzzy (отключаем для prod). AmbiguousMatch исключение если несколько кандидатов — операция уходит в очередь модерации
  5. Credit (bank_credit.py) — transaction.atomic + select_for_update на AdminAccounts, kill-switch BANK_AUTO_CREDIT_ENABLED (default OFF), auto-unblock b_negbal при ostatok ≥ 0
  6. PDF счёт+акт (bank_documents.py, WeasyPrint) — атомарная нумерация СЧ-2026-NNNN/АКТ-2026-NNNN через BankDocumentCounter, опциональная GCS-загрузка, учёт per-abonent настроек (send_act, auto_account, next_auto_acount)
  7. Уведомления (bank_notifications.py) — Telegram-сводка по unmatched с anti-spam (cache 24h) + email клиенту с PDF-attached (до 8MB)
  8. Admin UI — четыре страницы раздела

Главная страница настроек

Настройки модуля автообработки выписок
Настройки модуля автообработки выписок

Структура страницы:

Все тогглы (детальное описание)

КлючDefaultНазначение
BANK_AUTO_CREDIT_ENABLEDfalseГлавный kill-switch автозачисления. Без него модуль работает в режиме «модерация-только»
BANK_AUTO_GENERATE_DOCSfalseАвто-генерация PDF счёт+акт после credit (только для юр.лиц)
BANK_EMAIL_TO_ABONENTfalseОтправлять email клиенту со ссылкой/PDF attached
BANK_NOREPLY_FROMnoreply@вашдомен.ruFROM-адрес для писем клиентам
BANK_TG_ALERT_ENABLEDtrueTelegram-сводка по unmatched-платежам
BANK_TG_ALERT_CHAT_ID / BANK_TELEGRAM_ALERT_CHAT_ID—Chat ID (priority: TG_ → TELEGRAM_); thread_id опционально
BANK_TG_ALERT_MIN1Минимум pending для алерта
BANK_PULL_SCHEDULE_HOUR/MINUTE6/0Час+минута ежедневного забора писем (Celery beat ежечасно с фильтром ±10мин)
BANK_DOC_DEFAULT_SEND_DAY0День месяца отправки акта если у клиента не задан next_auto_acount. 0 = выпускать сразу
BANK_DOC_UPLOAD_GCStrueKill-switch GCS-загрузки PDF
BANK_CREDIT_WARN_AMOUNT50000Сумма ₽ выше которой credit-модалка показывает warning

IMAP-приёмник и whitelist

IMAP-настройки подключаются к SmitMailServ на mail.вашдомен.ru:993 (SSL). Ящик bank@billing.вашдомен читает все UNSEEN-письма, сохраняет вложения .csv/.txt в таблицу BankStatementImport. Whitelist отправителей в виде запятой-разделённой строки доменов: @alfabank.ru,@sberbank.ru,@sbrf.ru. Письма от других отправителей игнорируются (статус skipped).

Автонастройка ящика. Кнопка «Автонастроить ящик» создаёт почтовый ящик приёма выписок (по умолчанию bank@billing.smit34.ru — на домене почтовой инфраструктуры биллинга, а не на домене ЛК организации), прописывает DNS-записи и заполняет IMAP автоматически. Если ящик уже задан в IMAP_BANK_USER, берётся его адрес. Когда ящик настроен, блок сворачивается в строку «Ящик bank@billing.smit34.ru настроен для приёма выписок» с кнопкой «Изменить» — поля IMAP скрыты, чтобы не мешать; «Изменить» раскрывает их обратно.

Telegram-алерты по unmatched

Celery beat bank-statements-unmatched-alert запускается ежечасно. Если есть pending платежи ≥ BANK_TG_ALERT_MIN, отправляет сводку в чат: список последних 10 платежей (сумма, плательщик, банк, дата) + итоговая сумма + кнопка-ссылка на очередь модерации. Anti-spam: при стабильном количестве pending повторный алерт не отправляется (cache bank_unmatched_alert:last_count TTL 24h).

В UI кнопка «Добавить» парсит ссылку https://t.me/c/1234567890/42 → автоматически заполняет chat_id=-1001234567890 и thread_id=42. Поддерживаются 3 формата: /c/<chat>/<thread>, https://t.me/<username>, числовой -100....

Банки и парсеры

Раздел Банки и парсеры
Раздел Банки и парсеры

Регистр банков с привязкой к парсерам и email-доменам отправителей. По умолчанию загружены Сбербанк и АльфаБанк через миграцию 0144 (data-seed). Без активных записей здесь IMAP-приёмник игнорирует все входящие письма.

Колонки таблицы: # | Банк (название + slug) | Email-домен | Парсер (цветной чип) | Импортов (async-счётчик) | Статус (активен/выключен) | Заметки | Действия (редактировать/удалить).

Модалка «Добавить банк»:

Добавление нового банка (например, Тинькофф):

  1. Создать парсер в billing/services/bank_parsers/<slug>.py — наследовать от BankParser, реализовать parse(content: bytes) → List[ParsedOperation]
  2. Зарегистрировать в billing/services/bank_parsers/__init__.py::detect_and_parse() — добавить ветку определения формата
  3. Через UI «Добавить банк» создать BankParserBinding с новым parser_slug
  4. Whitelist в настройках обновится автоматически (читает из BankParserBinding)

Защита от потери данных: при удалении банк-парсера BankStatementImport с этим email_domain в БД сохраняются (FK не настроен). Новые письма с домена будут игнорироваться. Восстановить — повторное добавление с тем же доменом.

Очередь модерации

Очередь модерации платежей
Очередь модерации платежей

Ключевая страница для финансиста-оператора: через неё проходит 50–100 платежей в день. Один платёж разбирается в один клик через массовое действие или в два — через модальное окно.

Возможности

Credit-модалка (детально)

При клике «Зачислить» fetch'ит /admin/finance/bank_queue/<id>/credit_preview/ и собирает модалку с:

Onboarding-tooltip

Onboarding-tooltip 4 шага
Onboarding-tooltip 4 шага

При первом визите показывает 4-step tour: Bulk-операции → Keyboard shortcuts → Undo → Risk-проверка. Bump версии (v4 → v5 в JS) — снова покажется после новых фич. Shift+Click на help-иконку (клавиатура в toolbar) — перезапуск.

Путь платежа: от письма банка до баланса клиента

flowchart TD
  MAIL["Письмо банка
с выпиской"] --> PARSE["Разбор вложения"] PARSE --> ORG["Определение юрлица
по счёту получателя"] ORG --> CHK{"Сводное перечисление
приёмщика платежей?"} CHK -->|"да"| REG["Статус «Сводный реестр»
по клиентам не разносится"] CHK -->|"нет"| MATCH{"Сопоставление
с клиентом"} MATCH -->|"нашли одного"| MTD["Сопоставлена"] MATCH -->|"несколько или никого"| MAN["На модерации
оператор привязывает вручную"] MAN --> MTD MTD --> RISK{"Есть признаки
расхождения?"} RISK -->|"да"| ONE["Разбирается поштучно
в массовое зачисление не попадает"] RISK -->|"нет"| CRED["Зачисление на баланс"] ONE --> CRED CRED --> DOC["Счёт и акт для юрлица
от нужной организации"]

Сводные перечисления приёмщиков платежей

Вкладка «Реестры»: сводные перечисления Сбербанка и «Единой кассы»
Вкладка «Реестры»: сводные перечисления Сбербанка и «Единой кассы»

Клиент платит через кассу Сбербанка или терминал «Единой кассы», приёмщик копит оплаты за период и перечисляет провайдеру одной строкой. В назначении так и написано:

ЗА ИНТЕРНЕТ,ТВ,СВЯЗЬ; ПО ПЛАТЕЖАМ С 01/06/2026 ПО 01/06/2026, СУММА 98500.00, КОЛ-ВО 112, ЭЛ.РЕЕСТР EPS…

За такой строкой стоят 112 разных клиентов, и их оплаты уже зачислены другим каналом. Привязать перечисление к одному клиенту нельзя, а разнести повторно — задвоить деньги. Поэтому оно получает статус «Сводный реестр», уходит из очереди ручного разбора и остаётся в журнале как поступление на расчётный счёт.

Такие платежи были самыми крупными в очереди, и оператор упирался в них первыми. После разделения очередь ручного разбора стала посильной:

Очередь ручного разбораПлатежейСумма
До разделения701 250 000,00 ₽
После52140 000,00 ₽
Вынесено в «Реестры»181 110 000,00 ₽

Как отличается реестр от обычного платежа

flowchart LR
  P["Платёж из выписки"] --> A{"ИНН плательщика
в списке приёмщиков?"} A -->|"нет"| ORD["Обычный платёж
идёт на сопоставление"] A -->|"да"| B{"В назначении есть
признак реестра?"} B -->|"нет"| ORD B -->|"да"| REG["Сводный реестр"]

Признаков должно быть два сразу. Одного ИНН мало: со счёта того же банка приходят и обычные платежи юрлиц-клиентов — их распознавание пропускает дальше, на сопоставление. Признаки реестра в назначении: «ЭЛ.РЕЕСТР», «ПО ПЛАТЕЖАМ С …», «за приём платежей».

Список ИНН приёмщиков — настройка BANK_AGGREGATOR_INNS в модуле, а не константа в коде: появится третий приёмщик — оператор добавит его сам. По умолчанию там ПАО Сбербанк и РНКО «Единая касса».

Что даёт разбор назначения. Из строки вытаскивается количество оплат и период, и в списке видно содержание перечисления — «138 платежей за 01.06.2026». Это основа для сверки: приёмщик перечислил столько-то оплат за такой-то день, и это сравнимо с тем, что разнесено по клиентам в биллинге. Расхождение здесь означает либо потерянные деньги, либо двойное зачисление.

Разбор очереди, организации и аудит

Очередь модерации: чипы статусов со счётчиками, поиск, период, суммовой итог, колонка организации
Очередь модерации: чипы статусов со счётчиками, поиск, период, суммовой итог, колонка организации

Аудит модуля 24.08.2026 показал, что очередь стояла не из-за неудобств интерфейса, а из-за трёх дефектов.

Привязка платежа: форма раскрывается прямо под строкой, видны соседние платежи
Привязка платежа: форма раскрывается прямо под строкой, видны соседние платежи

Поиск клиента идёт по ФИО или названию, номеру договора, телефону, e-mail, ИНН и номеру клиента. Клиенты с совпавшим ИНН поднимаются в начало списка.

Форма раскрывается прямо под платежом — соседние строки остаются на виду, а именно по ним видно, что тот же плательщик заплатил дважды или что рядом лежит платёж того же юрлица. На телефоне вместо раскрытия выезжает панель. Форма существует в одном экземпляре и переносится между строкой и панелью, поэтому поиск и подсказки работают одинаково.

Видно сам платёж — сумму, дату, банк, плательщика с ИНН и назначение — и кнопки-подсказки с номерами из назначения: клиент часто пишет туда номер договора. Если ИНН плательщика в базе не найден, реквизиты подтягиваются из ФНС через DaData: название, адрес, руководитель и кнопка «Искать по названию из ФНС».

Организация платежа

Импорт и операция запоминают юрлицо-получателя — оно определяется по расчётному счёту из выписки. В очереди и журнале появилась колонка «Орг.», списки скоупятся переключателем организации в шапке и правами сотрудника.

Счёт и акт теперь выписываются от того юрлица, на чей счёт пришли деньги. У каждого юрлица своя серия номеров документов: сквозная нумерация оставляла бы в нумерации одной компании дыры на местах документов другой.

Флаги расхождений в сопоставлении

Три случая помечаются значком и подсветкой строки: совпадение по похожести ФИО, один ИНН у разных клиентов, расхождение ФИО плательщика и клиента. Такие строки не попадают в массовое выделение — их разбирают по одному. При установке галочки «выбрать все» оператор видит, сколько платежей пропущено и почему.

Поиск, период, постраничный вывод и итоги

Панель управления — в одну строку. Поиск, выбор периода и размер страницы сужены так, чтобы вместе с кнопкой «Применить» и инструментами раздела (горячие клавиши, настройка колонок, «Журнал» импортов, «Настройки») уместиться в одну линию. Итог по выборке прижат к правому краю той же строки.

Журнал аудита

Запись создаётся на каждое изменение: сохранение настроек с перечнем правок (включая выключатель автозачисления), ручной забор писем, повторный разбор импорта, автонастройка почтового ящика, массовые действия, ручная привязка с суммой и плательщиком, генерация документов с указанием организации, приём письма и определение организации.

Вкладка «Банк» в карточке клиента

Вкладка «Банк» в карточке клиента
Вкладка «Банк» в карточке клиента

Вкладка показывает платежи клиента из выписок — дата, банк, сумма, плательщик, назначение, статус и ссылка на финоперацию — и выписанные счета с актами с кнопкой открытия PDF. В шапке сумма зачисленного и переход в очередь, отфильтрованную по этому клиенту.

На телефоне

Очередь модерации на телефоне: карточки вместо таблицы
Очередь модерации на телефоне: карточки вместо таблицы

На телефоне строка разворачивается карточкой «поле — значение», сумма показана крупно, кнопки во всю ширину. Чекбоксы массового выбора на телефоне скрыты: платежи здесь разбирают по одному.

Журнал импортов

Журнал импортов выписок
Журнал импортов выписок

Полный лог писем от банков с возможностью отладки. По умолчанию показывает последние 30 дней (защита от бесконечной выдачи при N тысячах импортов).

Фильтры в одну строку: Банк (Альфа/Сбер/Неизвестно/Все) | Статус (Новые/Разобранные/Ошибки/Без вложений) | Период (7д/30д/90д/Все) | кастомный date-range. Meta-bar над таблицей показывает текущий период + количество найденных записей.

Колонки: # | Получено (datetime) | От (email) | Тема | Банк (chip) | Файл (filename) | Статус (с иконкой) | Операций (всего / сматч. / зачисл.) | Действия.

Действия:

Статусы письма-импорта

Колонка «Статус» в журнале — это статус письма с выпиской (модель BankStatementImport, набор IMPORT_STATUS_CHOICES). Всего 5 значений:

Статус (UI)КодКогда выставляется
🟡 НовыйnewДефолт. Письмо получено по IMAP, вложение сохранено, но ещё не разобрано парсером
🟢 РазобранparsedПарсер успешно прочёл вложение и создал записи BankStatementOperation
🔴 Ошибка парсингаerrorПарсер упал на файле — текст ошибки сохраняется в поле parser_error, виден при наведении
⚪ Без вложенийemptyВ письме нет файла выписки (.csv/.txt)
⚪ ПропущенskippedПисьмо не от банка — домен отправителя не входит в whitelist «Банки и парсеры»

Как меняется статус: «Новый» → «Разобран»

Переход выполняет фоновая задача Celery, оператор в нём не участвует. Цепочка:

  1. bank-statements-pull (Celery beat, ежечасно) — приёмник billing/services/imap_client.py читает UNSEEN-письма из ящика bank@вашдомен.ru и создаёт BankStatementImport со статусом new.
  2. bank-statements-process (Celery beat, каждые 5 минут) — функция process_pending_imports() в billing/tasks/bank_statements.py:
    • берёт BankStatementImport.objects.filter(status='new')[:20];
    • вызывает парсер по parser_slug банка (sber/alfa/generic);
    • на каждую строку выписки создаёт BankStatementOperation + запускает matching;
    • при успехе — imp.status = 'parsed'; при сбое парсера — status = 'error' + текст в parser_error.

То есть письмо в статусе Новый автоматически становится Разобран в течение ~5 минут после получения — на следующем тике bank-statements-process. Если статус не меняется дольше — см. блок «Диагностика» ниже.

«Разобран» ≠ «Зачислено». Статус письма (parsed) означает только, что вложение прочитано и операции созданы. Сами деньги имеют отдельный статус — это набор OPERATION_STATUS_CHOICES модели BankStatementOperation, видимый в /admin/finance/bank_queue/: Новая (не сопоставлена) → Сопоставлена / На модерации → Зачислена / Отклонена / Дубликат. Зачисление контролируется kill-switch BANK_AUTO_CREDIT_ENABLED (по умолчанию ВЫКЛ — операции ждут ручного подтверждения оператором).

Диагностика: статус долго не меняется

Если письма зависают в статусе Новый и не переходят в Разобран — задача bank-statements-process не выполняется. Что проверить:

Шрифтовая пара для PDF

В Брендинге → карточка «Документы» → селект «Шрифтовая пара для PDF». 5 кириллических вариантов:

⚠ Ограничение WeasyPrint: в Docker-контейнере нет доступа к fonts.googleapis.com, поэтому в PDF — fallback на DejaVu Sans. Шрифты применятся только при печати через браузер либо при переходе на Playwright/Chrome headless для PDF-генерации. Поле font_pair сохраняется корректно.

Per-abonent настройки документов

Учитываются существующие поля клиента (вкладка «Настройки» в карточке):

Если у юр.лица оба флага OFF — документы не генерируются даже при BANK_AUTO_GENERATE_DOCS=true. Если next_auto_acount не задан, используется fallback на BANK_DOC_DEFAULT_SEND_DAY (день месяца).

Единое оформление всех четырёх страниц

Все четыре страницы раздела — Настройки, Банки и парсеры, Очередь модерации и Журнал импортов — работают на одних и тех же элементах интерфейса: таблицы, фильтры, подтверждения и уведомления ведут себя одинаково.

Банковские выписки — очередь модерации (единые компоненты)
Банковские выписки — очередь модерации (единые компоненты)
Журнал импортов на SmitTable — настройка колонок
Журнал импортов на SmitTable — настройка колонок
SmitConfirm — подтверждение опасного забора писем
SmitConfirm — подтверждение опасного забора писем
Привязка платежа раскрытием строки (по образцу Xero)
Привязка платежа раскрытием строки (по образцу Xero)