docs: refactor project docs and agents tasks

This commit is contained in:
vakabunga
2026-03-02 00:30:56 +03:00
parent 9551b93a09
commit 9d12702688
14 changed files with 1146 additions and 87 deletions

255
docs/backlog/api_rules.md Normal file
View 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`.