Перейти к основному содержимому

obligations — реестр обязательных платежей

Коротко​

  • Что делает: реестр документов-оснований (кредиты, налоги, судебные решения, соглашения, цессии и т.д.) с графиком платежей, пеней, участниками и ИИ-разбором скана документа. Оплата строки графика идёт через обычный шорт-лист (payment_approvals), без 1С.
  • Страница: /obligations — «Календарь обязательств» (вкладки ?tab=calendar|list, ?doc=ID сразу открывает карточку документа).
  • Права: obligations — весь модуль; obligations.delete — удаление документа; admin — получатели уведомлений, ставки НБРК, замена основного файла и повторный ИИ-анализ.
  • Сущности: документ (obligation_documents) → строки графика (obligation_payments, каждая = запись payment) → шорт-листы на оплату (payment_approvals).

Карта файлов​

Клиент — client/src/​

pages/obligations/
ui/ObligationsPage.js вкладки + открывает карточку в сайдбаре; связывает виджеты

widgets/
obligations-calendar/ вкладка «Календарь»
ObligationsCalendarWidget месячный календарь (shared/ui/month-calendar) + загрузка
ObligationsDay платежи выбранного дня
ObligationsCalendarFilters фильтры
ObligationsOverview сводка над календарём
obligations-list/ вкладка «Список обязательств»
ObligationsListWidget загрузка + фильтры + таблица
ObligationsTable / Filters / Totals / UsdRateLine / ExportExcel
obligation-card/ карточка документа (в сайдбаре)
ObligationCard загрузка карточки, создание нового документа
CardTabs вкладки: Данные, Файлы, График, Пеня, История
CardTabContent раскладывает фичи по вкладкам

features/
obligation-document-form/ форма данных документа (+ lib/form.js, тест)
obligation-files/ прочие файлы документа
obligation-main-file/ основной файл + результат ИИ-анализа (поллинг статуса)
obligation-participants/ участники документа
obligation-amounts/ суммы, названные в документе (справочно)
obligation-schedule/ график: строки, «Оплатить N» (шорт-листы), платежи без даты, итоги
obligation-penalty/ пеня: правило, расшифровка расчёта
obligation-history/ журнал изменений (lib/describe.js — человекочитаемые события)
obligation-alert/ мигающий индикатор в шапке (подключён в app/index.js)
obligation-recipients/ Настройки → получатели уведомлений (admin)
obligation-key-rates/ Настройки → базовая ставка НБРК (admin)

entities/obligation/
api/obligation.api.js ВСЕ запросы модуля к серверу
lib/obligation.js форматирование денег/дат, валюты, статусы сроков
lib/heatmap.js теплокарта платежей
lib/useReference.js хук справочников (виды, роли, проекты, статьи…)
ui/ TypeBadge, DueBadge, MoneyToggle, DualMoney, PaymentsHeatmap

Виджеты друг друга не импортируют: календарь и список сообщают onOpenDocument(id), карточку открывает страница. После закрытия карточки страница увеличивает refreshKey и вызывает refreshObligationAlert().

Сервер — server/modules/obligations/​

obligations.routes.js основные роуты (документы, файлы, график, календарь, настройки)
obligations.extra.routes.js ИИ-анализ, участники, суммы, шорт-листы, extras, Excel, пеня
obligations.constants.js виды документов, роли сторон, окно уведомлений и т.п.

controllers/ HTTP + Joi (schemas.js — общие схемы), по одному на область

services/
documents.service.js CRUD документа, курс USD для списка
card.service.js сборка карточки целиком
payments.service.js строки графика (+ синхронизация с payment)
row.factory.js создание строки графика + её payment в транзакции
payment.sync.js деньги строки в её валюте ↔ payment в ₸
extras.service.js платежи без даты (kind = 'extra')
approvals.service.js «Оплатить N» → шорт-лист на один платёж, отметка оплаты
paid.parts.js датированные оплаты строк (для пени)
schedule.snapshot.js свежее состояние графика для клиента
schedule.totals.js итоги графика в KZT и USD (чистая функция)
currency.js правила валют строк
participants.service.js участники документа
amounts.service.js суммы из документа
files.service.js файлы на fileserver
history.service.js журнал изменений (дифф полей)
calendar.service.js календарь, сводка, индикатор в шапке
notifier.service.js крон-уведомления о сроках
export.service.js Excel «Кто падает в график»
export.paid.sheet.js лист Excel «Оплаты за период»
present.js / present.payment.js представление строк для клиента
reference.service.js справочники для форм
settings.service.js получатели уведомлений, ставки НБРК
penalty/
penalty.rules.js формат правила пени (jsonb)
penalty.calc.js чистый расчёт пени (без БД, покрыт тестами)
penalty.service.js пеня документа на дату: собирает данные и зовёт calc
ai/
analysis.service.js основной файл: загрузка, запуск анализа, статус
analysis.runner.js фоновый анализ: pending → analyzing → done | failed
analysis.mapping.js ответ ИИ → изменения документа (чистая функция)
analysis.lists.js ответ ИИ → участники и суммы (чистая функция)
counterparty.autocreate.js заводит контрагента по БИН из госреестров

model/
documents.model.js / documents.columns.js документы и агрегаты по платежам
payments.model.js строки графика
due.model.js «что и когда платить» по всем документам
participants.model.js, amounts.model.js, analyses.model.js, history.model.js
settings.model.js получатели уведомлений (key_values)
key_rates.model.js ставки НБРК (key_values, есть значения по умолчанию)
refs.model.js ШОВ: единственное чтение чужих данных (контрагенты, пользователи…)
finance.payments.model.js ШОВ: единственное место, где модуль пишет/читает таблицу payment
finance.approvals.model.js ШОВ: единственное место для payment_approvals(_payments)

Общий код вне модуля​

ФайлЗачем
server/shared/claude/claudeObligation*.js, obligationFileContent.jsпромпт, схема ответа и вызов Claude Haiku для разбора скана
server/shared/currency/nbrkRatesкурс USD НБРК (таблица currency_rates)
server/shared/settings/key_value.modelхранилище настроек key_values
server/shared/gov-webapisреквизиты контрагента по БИН
server/shared/notify/notification.modelуведомления пользователям
client/src/shared/ui/month-calendarобщий месячный календарь

Точки входа извне​

ГдеЧто
server/core/indexApi.jsмонтирование obligations.routes
server/core/cron.jsnotifyUpcomingPayments — каждый час 8–19 (Алматы)
client/src/app/routes/app.jsмаршрут /obligations
client/src/app/index.jsObligationAlert — индикатор в шапке
client/src/widgets/settings/ui/SettingsWidget.jsвкладки получателей и ставок НБРК

Данные​

Свои таблицы: obligation_documents, obligation_payments (поле kind: строка графика или extra), obligation_participants, obligation_document_amounts, obligation_ai_analyses (с file_hash для кеша), obligation_history.

Чужие таблицы — только через швы в model/:

ТаблицаЧерезЗачем
payment (model = 'obligation')finance.payments.model.jsкаждая строка графика — запись реестра платежей
payment_approvals, payment_approval_paymentsfinance.approvals.model.jsоплата строки = согласованный шорт-лист
контрагенты, пользователи, проекты, статьиrefs.model.jsимена и справочники
key_valuessettings.model.js, key_rates.model.jsполучатели (obligation_notify_users), ставки НБРК
currency_ratesshared/currency/nbrkRatesкурс USD

Миграции: server/migrations/*obligation* (первая — 20260924100000_create_obligations.js). Таблицы obligation_payment_transactions и obligation_settings больше не существуют.

API​

Все роуты под /api/obligations, право obligations, если не указано иное.

Метод и путьЧто
GET /referenceсправочники для форм
GET/POST /documents, GET/PATCH /documents/:idсписок, создание, карточка, правка
DELETE /documents/:idудаление (право obligations.delete)
GET /documents/:id/historyжурнал
POST /documents/:id/files, DELETE /documents/:id/files/:fileIdпрочие файлы
POST /documents/from-fileсоздать документ из скана (сразу запускает ИИ)
POST /documents/:id/main-file, GET .../analysis, POST .../analysis/rerunосновной файл и ИИ
POST /documents/:id/payments, PATCH/DELETE /payments/:paymentIdстроки графика
POST /documents/:id/extras, PATCH/DELETE /extras/:rowIdплатежи без даты
POST /payments/:paymentId/approvals, PATCH/DELETE /approvals/:linkId, POST /approvals/:linkId/paid«Оплатить N»
POST /documents/:id/participants, PATCH/DELETE /participants/:participantIdучастники
POST /documents/:id/amounts, DELETE /amounts/:amountIdсуммы из документа
GET /documents/:id/penaltyпеня на дату
GET /calendar, GET /overviewкалендарь и сводка
GET /alertsиндикатор в шапке (право user; данные отдаются только получателям)
GET /exportExcel за период
GET/PUT /settingsполучатели уведомлений (admin)
GET /key-rates (user), PUT /key-rates (admin)базовая ставка НБРК

obligations.extra.routes.js монтируется первым, иначе /documents/from-file перехватит /documents/:id.

Ключевые потоки​

Строка графика ↔ payment. row.factory создаёт строку obligation_payments и запись payment в одной транзакции. При смене срока/суммы обновляется та же запись payment — привязанные шорт-листы не теряются. Деньги строки — в её валюте (KZT/USD), payment — всегда в ₸ (payment.sync.js).

Оплата. «Оплатить N» → approvals.service создаёт отдельный шорт-лист на один платёж (USD переводится по курсу НБРК на сегодня). Оплаченный шорт-лист = датированная оплата строки (paid.parts.js), из них считаются остаток и пеня.

ИИ-анализ. Загрузка основного файла → запись в obligation_ai_analyses → analysis.runner в процессе сервера (без очереди) вызывает Claude Haiku → analysis.mapping применяет результат: поля документа, участники и суммы от прошлого анализа заменяются (ручные остаются), строки графика без оплат заменяются графиком ИИ. Клиент опрашивает статус (useAnalysisPolling). Если тот же файл (по sha256) уже разобран в другом документе аккаунта — результат берётся из кеша.

Пеня. penalty_mode: none | manual (сумма из penalty_sum) | rule (правило jsonb, см. penalty.rules.js). penalty.service собирает график, оплаты и ставки НБРК и зовёт чистый penalty.calc.

Уведомления. Крон находит неоплаченные платежи со сроком через ≤ ALERT_WINDOW_DAYS дней (и просроченные) и шлёт одно сообщение на аккаунт получателям из настроек. Отправка помечается в obligation_payments.notified_at; при переносе срока пометка сбрасывается.

Ловушки и решения​

  • Даты отдаются строкой YYYY-MM-DD: node-pg превращает DATE в Date по поясу сервера, и день «уезжает». Границы месяца в календаре тоже считаются строками.
  • numeric из pg приходит строкой — числа приводятся в present.js.
  • Чужие таблицы только через швы (refs, finance.*) — остальной код модуля знает лишь свои obligation_*.
  • Зависшие анализы (сервер перезапустился посреди анализа) помечаются ошибкой при чтении карточки — очереди нет.
  • Курс НБРК недоступен — берётся последний известный, клиент показывает дату курса.
  • Виды документов с legacy: true нельзя выбрать для нового документа, но старые открываются.
  • В клиенте entities импортируется только относительным путём (../../../entities/obligation).

Тесты​

Сервер: server/tests/obligations*.js (расчёт пени, валюты, файлы, шорт-листы, разбор ответа ИИ, автосоздание контрагента) — cd server && npm run test -- obligations. Клиент: entities/obligation/obligation.test.js, features/obligation-document-form/form.test.js.