12 KiB
Правила категоризации (CRUD /api/category-rules)
Назначение
Набор эндпоинтов для управления правилами автоматической категоризации транзакций. Правила глобальные — без привязки к конкретному пользователю (userId не используется).
Общие требования
- Все эндпоинты требуют действующей сессии (cookie
sid), иначе401 Unauthorized. - Формат JSON-ответов использует
camelCase. - В MVP поддерживается только
matchType = "contains". Значения"starts_with"и"regex"зарезервированы для будущих версий.
1) GET /api/category-rules — список правил
Назначение: отображение списка правил на экране настроек (просмотр, активация/деактивация).
Параметры запроса (query)
Все параметры опциональны.
isActive: boolean— фильтр по активности:- Параметр не передан → все правила (активные и неактивные).
isActive=true→ только активные (is_active = TRUE).isActive=false→ только неактивные (is_active = FALSE).
categoryId: number— фильтр по категории (category_rules.category_id).search: string— поиск по подстроке в полеpattern(регистронезависимый,ILIKE '%search%').
Поведение сортировки
По умолчанию backend возвращает правила, отсортированные по priority DESC, затем по created_at DESC.
Ответ (200 OK)
Массив объектов правил.
[
{
"id": 1,
"pattern": "PYATEROCHKA",
"matchType": "contains",
"categoryId": 1,
"categoryName": "Продукты",
"priority": 100,
"requiresConfirmation": false,
"isActive": true,
"createdAt": "2026-02-20T12:00:00+03:00"
}
]
Поля:
id: number— идентификатор правила.pattern: string— строка-шаблон.matchType: string— тип сопоставления ("contains"в MVP).categoryId: number— ID категории.categoryName: string— имя категории (JOIN сcategories).priority: number— приоритет правила.requiresConfirmation: boolean— требуется ли ручное подтверждение категории.isActive: boolean— активно ли правило.createdAt: string— дата создания в формате ISO 8601.
Ошибки
401 Unauthorized— нет действующей сессии.
2) POST /api/category-rules — создание правила
Назначение: создание нового правила категоризации (из экрана настроек или при редактировании транзакции).
Тело запроса
{
"pattern": "PYATEROCHKA",
"matchType": "contains",
"categoryId": 1,
"priority": 100,
"requiresConfirmation": false
}
Поля:
pattern: string— обязательное. Строка-шаблон для сопоставления сdescriptionтранзакций.matchType: string— опциональное, по умолчанию"contains". В MVP принимается только"contains".categoryId: number— обязательное. ID категории из таблицыcategories.priority: number— опциональное, по умолчанию100. Целое число. Дефолт 100 означает, что пользовательские правила по умолчанию приоритетнее общих (seed/системных) правил с низким приоритетом.requiresConfirmation: boolean— опциональное, по умолчаниюfalse.
Валидация
pattern:- Не может быть пустым после
trim. - Максимальная длина — 200 символов (после
trim).
- Не может быть пустым после
matchType:- Если передан, должен быть строго
"contains"(MVP). - Неизвестные значения →
422.
- Если передан, должен быть строго
categoryId:- Должен ссылаться на существующую активную категорию.
- Несуществующая или неактивная категория →
422.
priority:- Целое число от
0до1000.
- Целое число от
Ответ (201 Created)
Созданный объект правила (структура как в GET).
{
"id": 5,
"pattern": "PYATEROCHKA",
"matchType": "contains",
"categoryId": 1,
"categoryName": "Продукты",
"priority": 100,
"requiresConfirmation": false,
"isActive": true,
"createdAt": "2026-02-28T15:30:00+03:00"
}
Ошибки
| Код | Ситуация |
|---|---|
| 400 | Структурно невалидные данные (пустой pattern, отсутствуют обязательные поля) |
| 401 | Нет действующей сессии |
| 422 | Ошибка валидации значений (неизвестный matchType, категория не найдена и пр.) |
3) PATCH /api/category-rules/{id} — частичное обновление правила
Назначение: изменение параметров правила, в том числе активация/деактивация.
Параметры
id: number— идентификатор правила (path-параметр).
Тело запроса
Частичное обновление: передаются только изменяемые поля.
{
"pattern": "PEREKRESTOK",
"categoryId": 1,
"priority": 150,
"requiresConfirmation": true,
"isActive": false
}
Допустимые поля для обновления:
pattern: string— новый шаблон (валидация как при создании).categoryId: number— новая категория (должна существовать и быть активной).priority: number— новый приоритет (целое,0–1000).requiresConfirmation: boolean— обновление флага.isActive: boolean— активация/деактивация правила.
Поле matchType в MVP не обновляется. Если клиент передал matchType — возвращается 422 с сообщением "matchType update is not supported in MVP", чтобы фронтенд не считал, что значение было применено.
Валидация
- Правило с указанным
idдолжно существовать, иначе404. - Если передано поле
matchType—422(обновление не поддерживается в MVP). - К переданным полям применяются те же правила валидации, что и при создании.
- Если ни одно допустимое поле не передано —
400.
Ответ (200 OK)
Обновлённый объект правила (структура как в GET).
Ошибки
| Код | Ситуация |
|---|---|
| 400 | Структурно невалидные данные или ни одного допустимого поля |
| 401 | Нет действующей сессии |
| 404 | Правило не найдено |
| 422 | Ошибка валидации (категория не найдена, передан matchType и пр.) |
4) POST /api/category-rules/{id}/apply — применение правила к прошлым транзакциям
Назначение: ретроактивное применение конкретного правила к существующим транзакциям.
Параметры
id: number— идентификатор правила (path-параметр).
Тело запроса
Отсутствует.
Логика обработки
- Правило загружается из БД. Если не найдено —
404. Если неактивно (is_active = FALSE) —422. - Выполняется поиск транзакций, подходящих под данное правило:
- Сопоставление
patternсdescription(регистронезависимыйILIKE '%pattern%'дляmatchType = "contains"). - Затрагиваются только транзакции, у которых:
category_id IS NULL, илиis_category_confirmed = FALSE.
- Транзакции с
is_category_confirmed = TRUEне затрагиваются — пользовательские подтверждения неприкосновенны.
- Сопоставление
- Для найденных транзакций:
- Устанавливается
category_idиз правила. is_category_confirmedустанавливается вFALSE(категория считается предварительной, требует подтверждения пользователем).- Обновляется
updated_at.
- Устанавливается
Ответ (200 OK)
{
"applied": 12
}
Поля:
applied: number— количество транзакций, к которым было применено правило.
Ошибки
| Код | Ситуация |
|---|---|
| 401 | Нет действующей сессии |
| 404 | Правило не найдено |
| 422 | Правило неактивно |
Связь с импортом
При импорте выписки (POST /api/import/statement) автокатегоризация выполняется автоматически:
- Ко всем импортированным транзакциям (с
category_id = NULL) применяются все активные правила. - Если для транзакции подходит несколько правил, выбирается правило с максимальным
priority. is_category_confirmedопределяется флагомrequires_confirmationсработавшего правила.
Подробнее см. api_import.md.
Связь с редактированием транзакций
При редактировании транзакции (PUT /api/transactions/{id}) с включённой опцией «Создать правило» фронтенд отправляет отдельный запрос POST /api/category-rules с заполненными полями pattern, categoryId и т.д.
Подробнее см. edit_and_rules.md.