docs: refactor project docs and agents tasks
This commit is contained in:
255
docs/backlog/api_rules.md
Normal file
255
docs/backlog/api_rules.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Правила категоризации (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)
|
||||
|
||||
Массив объектов правил.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"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 — создание правила
|
||||
|
||||
Назначение: создание нового правила категоризации (из экрана настроек или при редактировании транзакции).
|
||||
|
||||
### Тело запроса
|
||||
|
||||
```json
|
||||
{
|
||||
"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`).
|
||||
|
||||
```json
|
||||
{
|
||||
"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-параметр).
|
||||
|
||||
### Тело запроса
|
||||
|
||||
Частичное обновление: передаются только изменяемые поля.
|
||||
|
||||
```json
|
||||
{
|
||||
"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-параметр).
|
||||
|
||||
### Тело запроса
|
||||
|
||||
Отсутствует.
|
||||
|
||||
### Логика обработки
|
||||
|
||||
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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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`.
|
||||
Reference in New Issue
Block a user