Files
family_budget/docs/backlog/api_rules.md
2026-03-02 00:30:56 +03:00

12 KiB
Raw Permalink Blame History

Правила категоризации (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 — новый приоритет (целое, 01000).
  • requiresConfirmation: boolean — обновление флага.
  • isActive: boolean — активация/деактивация правила.

Поле matchType в MVP не обновляется. Если клиент передал matchType — возвращается 422 с сообщением "matchType update is not supported in MVP", чтобы фронтенд не считал, что значение было применено.

Валидация

  • Правило с указанным id должно существовать, иначе 404.
  • Если передано поле matchType422 (обновление не поддерживается в MVP).
  • К переданным полям применяются те же правила валидации, что и при создании.
  • Если ни одно допустимое поле не передано — 400.

Ответ (200 OK)

Обновлённый объект правила (структура как в GET).

Ошибки

Код Ситуация
400 Структурно невалидные данные или ни одного допустимого поля
401 Нет действующей сессии
404 Правило не найдено
422 Ошибка валидации (категория не найдена, передан matchType и пр.)

4) POST /api/category-rules/{id}/apply — применение правила к прошлым транзакциям

Назначение: ретроактивное применение конкретного правила к существующим транзакциям.

Параметры

  • id: number — идентификатор правила (path-параметр).

Тело запроса

Отсутствует.

Логика обработки

  1. Правило загружается из БД. Если не найдено — 404. Если неактивно (is_active = FALSE) — 422.
  2. Выполняется поиск транзакций, подходящих под данное правило:
    • Сопоставление pattern с description (регистронезависимый ILIKE '%pattern%' для matchType = "contains").
    • Затрагиваются только транзакции, у которых:
      • category_id IS NULL, или
      • is_category_confirmed = FALSE.
    • Транзакции с is_category_confirmed = TRUE не затрагиваются — пользовательские подтверждения неприкосновенны.
  3. Для найденных транзакций:
    • Устанавливается 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.