docs: refactor project docs and agents tasks
This commit is contained in:
@@ -55,8 +55,7 @@ CREATE UNIQUE INDEX ux_accounts_bank_number
|
||||
- `direction TEXT NOT NULL` — направление движения:
|
||||
- `"income"` — приход;
|
||||
- `"expense"` — расход;
|
||||
- `"transfer"` — перевод между своими счетами / на другие свои счета;
|
||||
- `"internal"` — служебные/внутренние движения (опционально, по мере необходимости).
|
||||
- `"transfer"` — перевод между своими счетами / на другие свои счета.
|
||||
- `fingerprint TEXT NOT NULL` — вычисляемый хэш для обеспечения идемпотентности импорта.
|
||||
- `category_id BIGINT` — ссылка на таблицу категорий (будет определена позже).
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время создания записи в БД.
|
||||
@@ -90,7 +89,7 @@ CREATE UNIQUE INDEX ux_transactions_account_fingerprint
|
||||
|
||||
ALTER TABLE transactions
|
||||
ADD CONSTRAINT chk_transactions_direction
|
||||
CHECK (direction IN ('income', 'expense', 'transfer', 'internal'));
|
||||
CHECK (direction IN ('income', 'expense', 'transfer'));
|
||||
```
|
||||
|
||||
## Таблица `category_rules`
|
||||
@@ -105,8 +104,13 @@ ALTER TABLE transactions
|
||||
- `"contains"` — простое вхождение подстроки;
|
||||
- `"starts_with"` — строка начинается с шаблона;
|
||||
- `"regex"` — регулярное выражение (формируется и/или проверяется в коде на основе пользовательского ввода).
|
||||
- MVP: при создании правила принимается только `"contains"`. Расширение до `"starts_with"` | `"regex"` запланировано.
|
||||
- `category_id BIGINT NOT NULL` — ссылка на категорию (таблица категорий будет описана отдельно).
|
||||
- `priority INT NOT NULL DEFAULT 0` — приоритет правила; чем выше число, тем раньше правило применяется при конфликте.
|
||||
- `requires_confirmation BOOLEAN NOT NULL DEFAULT FALSE` —
|
||||
если `TRUE`, транзакции, категоризированные этим правилом, получают `is_category_confirmed = FALSE`
|
||||
и требуют ручного подтверждения пользователем. Используется для правил с неоднозначным
|
||||
соответствием (например, маркетплейсы: OZON, WILDBERRIES).
|
||||
- `is_active BOOLEAN NOT NULL DEFAULT TRUE` — активно ли правило.
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время создания правила.
|
||||
|
||||
@@ -120,28 +124,37 @@ ALTER TABLE transactions
|
||||
|
||||
```sql
|
||||
CREATE TABLE category_rules (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
pattern TEXT NOT NULL,
|
||||
match_type TEXT NOT NULL,
|
||||
category_id BIGINT NOT NULL,
|
||||
priority INT NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
pattern TEXT NOT NULL,
|
||||
match_type TEXT NOT NULL,
|
||||
category_id BIGINT NOT NULL,
|
||||
priority INT NOT NULL DEFAULT 0,
|
||||
requires_confirmation BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
## Взаимосвязь JSON → БД при импорте
|
||||
|
||||
1. По полям `bank` и `statement.accountNumber` ищется или создаётся запись в `accounts`.
|
||||
Если счёт создан впервые, `alias = NULL`.
|
||||
2. Для каждой транзакции из `transactions`:
|
||||
- преобразуются суммы в копейки (`amountSigned`, `commission` → `BIGINT`);
|
||||
- вычисляется `fingerprint` на основе комбинации полей (например,
|
||||
`accountNumber + operationAt + amountSigned + commission + normalizedDescription`);
|
||||
- определяется `direction` по знаку суммы и/или шаблонам текста (приход/расход/перевод);
|
||||
- определяется `direction`:
|
||||
- `amountSigned > 0` → `"income"`;
|
||||
- `amountSigned < 0` → `"expense"`;
|
||||
- `"transfer"` — определяется по фиксированным ключевым фразам банка в `description`
|
||||
(например, для ВТБ: "Перевод", "между счетами" и т.п.).
|
||||
Если ключевые фразы не сработали — остаётся `"income"` / `"expense"` по знаку суммы;
|
||||
- выполняется попытка вставки в `transactions`;
|
||||
- при срабатывании уникального ограничения `(account_id, fingerprint)` запись считается дубликатом и пропускается.
|
||||
- При импорте новых транзакций is_category_confirmed всегда = FALSE.
|
||||
- При автокатегоризации обычными правилами, если правило “жёсткое” (не маркетплейс) — TRUE, для маркетплейсов — всегда FALSE.
|
||||
|
||||
3. Категория (`category_id`) пока может оставаться `NULL` и заполняться на следующих этапах
|
||||
(правила, ИИ-агент, ручное редактирование в SPA).
|
||||
- при срабатывании уникального ограничения `(account_id, fingerprint)` запись считается дубликатом и пропускается;
|
||||
- при импорте `is_category_confirmed` всегда = `FALSE`, `category_id = NULL`.
|
||||
3. Импорт атомарный: при ошибке валидации любой транзакции весь файл откатывается (возвращается `422`).
|
||||
4. Категория (`category_id`) заполняется на следующих этапах:
|
||||
- автокатегоризация по `category_rules`:
|
||||
- если у сработавшего правила `requires_confirmation = FALSE` → `is_category_confirmed = TRUE`;
|
||||
- если `requires_confirmation = TRUE` → `is_category_confirmed = FALSE` (требуется ручное подтверждение);
|
||||
- ручное редактирование в SPA.
|
||||
|
||||
Reference in New Issue
Block a user