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.js | notifyUpcomingPayments — каждый час 8–19 (Алматы) |
client/src/app/routes/app.js | маршрут /obligations |
client/src/app/index.js | ObligationAlert — индикатор в шапке |
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_payments | finance.approvals.model.js | оплата строки = согласованный шорт-лист |
| контрагенты, пользователи, проекты, статьи | refs.model.js | имена и справочники |
key_values | settings.model.js, key_rates.model.js | получатели (obligation_notify_users), ставки НБРК |
currency_rates | shared/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 /export | Excel за период |
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.