2 Commits

Author SHA1 Message Date
Anton
a50252d920 docs: specifies lang in tripple quotes 2026-03-02 11:36:16 +03:00
Anton
172246db0b fix: align docs and code with actual DB schema and format spec
- Rewrite db.md as canonical schema: add categories, sessions tables; add alias to accounts, is_category_confirmed/comment to transactions, FK references to categories(id); mark budgets as post-MVP

- Fix account masking to use fixed 6 asterisks (code + docs)

- Remove budgets from MVP requirements in agent_backend.md

- Add explicit 'not in MVP' note to analytics.md budgets section

- Fix test_Statement.json: convert amounts to kopecks (integers), remove fingerprint fields (computed by backend)

Made-with: Cursor
2026-03-02 11:34:00 +03:00
9 changed files with 208 additions and 181 deletions

View File

@@ -48,7 +48,7 @@ npm run dev -w backend
## Структура проекта
```
```text
backend/src/
├── app.ts — точка входа: Express, миграции, монтирование роутов
├── config.ts — чтение переменных окружения

View File

@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from 'express';
export function maskAccountNumber(num: string): string {
if (num.length <= 10) return num;
return num.slice(0, 6) + '*'.repeat(num.length - 10) + num.slice(-4);
return num.slice(0, 6) + '******' + num.slice(-4);
}
export function escapeLike(input: string): string {

View File

@@ -30,7 +30,7 @@
2 Миграции БД
- Реализовать таблицы и поля строго по `db.md`, `category.md`, `edit_and_rules.md`, `analytics.md`.
- Включить все описанные CHECK/UNIQUE/FOREIGN KEY/дополнительные поля (`is_category_confirmed`, `comment`, `alias` для accounts, `budgets` и т.д.).
- Включить все описанные CHECK/UNIQUE/FOREIGN KEY/дополнительные поля (`is_category_confirmed`, `comment`, `alias` для accounts и т.д.).
3 Авторизация и сессии

View File

@@ -136,6 +136,8 @@
## 5.3. Задел под бюджеты (лимиты)
> **Не входит в MVP.** Этот раздел описывает будущую функциональность; таблица `budgets` и связанная логика реализуются после MVP.
Для поддержки бюджетов по категориям на будущих этапах вводится сущность `budgets`.
### Таблица `budgets` (идея)

View File

@@ -175,7 +175,7 @@ accountNumber|operationAt|amountSigned|commission|normalizedDescription
{
"accountId": 1,
"isNewAccount": true,
"accountNumberMasked": "408178**********5611",
"accountNumberMasked": "408178******5611",
"imported": 28,
"duplicatesSkipped": 3,
"totalInFile": 31
@@ -197,8 +197,8 @@ accountNumber|operationAt|amountSigned|commission|normalizedDescription
- Первые 6 символов остаются открытыми.
- Последние 4 символа остаются открытыми.
- Промежуточные символы заменяются на `*`.
- Пример: `40817810825104025611``408178**********5611`.
- Промежуточные символы заменяются фиксированным набором из 6 символов `******` (количество звёздочек не раскрывает длину номера).
- Пример: `40817810825104025611``408178******5611`.
Маскирование применяется в ответе этого эндпоинта и в `GET /api/accounts`.

View File

@@ -5,8 +5,10 @@
- Используется PostgreSQL, развёрнутый локально (например, на Synology).
- Основные сущности:
- `accounts` — банковские счета пользователя;
- `categories` — категории расходов, доходов и переводов;
- `transactions` — движения средств по счетам;
- `category_rules` — правила автоматической категоризации транзакций (подготовка к SPA-редактору правил).
- `category_rules` — правила автоматической категоризации транзакций;
- `sessions` — серверные сессии авторизации.
- Все суммы хранятся в минорных единицах (копейки) как `BIGINT`.
- Время хранится в `TIMESTAMPTZ` (временная зона сохраняется).
@@ -20,6 +22,8 @@
- `bank TEXT NOT NULL` — код/имя банка (например, `"VTB"`).
- `account_number TEXT NOT NULL` — номер счёта в банке (как в выписке).
- `currency TEXT NOT NULL` — код валюты счёта (например, `"RUB"`).
- `alias TEXT` — человекочитаемый алиас счёта (например, `"Текущий"`, `"Накопительный"`).
При создании счёта через импорт `alias = NULL`; пользователь задаёт его позже через SPA.
Ограничения и индексы:
@@ -32,21 +36,57 @@ CREATE TABLE accounts (
id BIGSERIAL PRIMARY KEY,
bank TEXT NOT NULL,
account_number TEXT NOT NULL,
currency TEXT NOT NULL
currency TEXT NOT NULL,
alias TEXT
);
CREATE UNIQUE INDEX ux_accounts_bank_number
ON accounts(bank, account_number);
```
## Таблица `categories`
Предназначение: хранение категорий для классификации транзакций.
Структура:
- `id BIGSERIAL PRIMARY KEY` — идентификатор категории.
- `name TEXT NOT NULL` — отображаемое имя категории (например, `"Продукты"`, `"ЖКХ"`).
- `type TEXT NOT NULL` — тип категории:
- `"expense"` — расходная категория;
- `"income"` — доходная категория;
- `"transfer"` — переводы между собственными счетами.
- `is_active BOOLEAN NOT NULL DEFAULT TRUE` — используется ли категория.
Ограничения:
- CHECK-ограничение: `type IN ('expense', 'income', 'transfer')`.
Рекомендуемый DDL:
```sql
CREATE TABLE categories (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
ALTER TABLE categories
ADD CONSTRAINT chk_categories_type
CHECK (type IN ('expense', 'income', 'transfer'));
```
Начальный набор из 23 категорий заполняется seed-миграцией (см. `category.md`).
## Таблица `transactions`
Предназначение: хранение всех операций по счетам с привязкой к `accounts`.
Предназначение: хранение всех операций по счетам с привязкой к `accounts` и `categories`.
Структура:
- `id BIGSERIAL PRIMARY KEY` — внутренний идентификатор транзакции.
- `account_id BIGINT NOT NULL` — внешний ключ (FK) на `accounts(id)`;
- `account_id BIGINT NOT NULL REFERENCES accounts(id)`
каждая транзакция жёстко привязана к одному счёту.
- `operation_at TIMESTAMPTZ NOT NULL` — дата и время операции.
- `amount_signed BIGINT NOT NULL` — сумма операции в копейках; знак отражает тип движения (приход/расход).
@@ -57,31 +97,37 @@ CREATE UNIQUE INDEX ux_accounts_bank_number
- `"expense"` — расход;
- `"transfer"` — перевод между своими счетами / на другие свои счета.
- `fingerprint TEXT NOT NULL` — вычисляемый хэш для обеспечения идемпотентности импорта.
- `category_id BIGINT` — ссылка на таблицу категорий (будет определена позже).
- `category_id BIGINT REFERENCES categories(id)` — ссылка на категорию; `NULL` для некатегоризированных транзакций.
- `is_category_confirmed BOOLEAN NOT NULL DEFAULT FALSE`
признак того, что категория подтверждена пользователем (явно или неявно через правило без `requires_confirmation`).
- `comment TEXT` — пользовательский комментарий к транзакции.
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время создания записи в БД.
- `updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время последнего обновления записи.
Ограничения и индексы:
- Внешний ключ `account_id` ссылается на `accounts(id)` и обеспечивает целостность (нельзя создать транзакцию для несуществующего счёта).
- Внешний ключ `account_id` ссылается на `accounts(id)`.
- Внешний ключ `category_id` ссылается на `categories(id)`.
- Уникальный индекс `(account_id, fingerprint)` обеспечивает идемпотентность: одна и та же операция не может быть загружена дважды.
- Опционально — CHECK-ограничение на поле `direction`.
- CHECK-ограничение: `direction IN ('income', 'expense', 'transfer')`.
Рекомендуемый DDL:
```sql
CREATE TABLE transactions (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL REFERENCES accounts(id),
operation_at TIMESTAMPTZ NOT NULL,
amount_signed BIGINT NOT NULL,
commission BIGINT NOT NULL,
description TEXT NOT NULL,
direction TEXT NOT NULL,
fingerprint TEXT NOT NULL,
category_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL REFERENCES accounts(id),
operation_at TIMESTAMPTZ NOT NULL,
amount_signed BIGINT NOT NULL,
commission BIGINT NOT NULL,
description TEXT NOT NULL,
direction TEXT NOT NULL,
fingerprint TEXT NOT NULL,
category_id BIGINT REFERENCES categories(id),
is_category_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
comment TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX ux_transactions_account_fingerprint
@@ -105,7 +151,7 @@ ALTER TABLE transactions
- `"starts_with"` — строка начинается с шаблона;
- `"regex"` — регулярное выражение (формируется и/или проверяется в коде на основе пользовательского ввода).
- MVP: при создании правила принимается только `"contains"`. Расширение до `"starts_with"` | `"regex"` запланировано.
- `category_id BIGINT NOT NULL` — ссылка на категорию (таблица категорий будет описана отдельно).
- `category_id BIGINT NOT NULL REFERENCES categories(id)` — ссылка на категорию.
- `priority INT NOT NULL DEFAULT 0` — приоритет правила; чем выше число, тем раньше правило применяется при конфликте.
- `requires_confirmation BOOLEAN NOT NULL DEFAULT FALSE`
если `TRUE`, транзакции, категоризированные этим правилом, получают `is_category_confirmed = FALSE`
@@ -127,7 +173,7 @@ CREATE TABLE category_rules (
id BIGSERIAL PRIMARY KEY,
pattern TEXT NOT NULL,
match_type TEXT NOT NULL,
category_id BIGINT NOT NULL,
category_id BIGINT NOT NULL REFERENCES categories(id),
priority INT NOT NULL DEFAULT 0,
requires_confirmation BOOLEAN NOT NULL DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
@@ -135,19 +181,46 @@ CREATE TABLE category_rules (
);
```
## Таблица `sessions`
Предназначение: хранение серверных сессий авторизации с поддержкой таймаута по бездействию.
Структура:
- `id TEXT PRIMARY KEY` — идентификатор сессии (UUID).
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время создания сессии.
- `last_activity_at TIMESTAMPTZ NOT NULL DEFAULT NOW()` — время последней активности; обновляется при каждом запросе с действительной сессией.
- `is_active BOOLEAN NOT NULL DEFAULT TRUE` — флаг активности; устанавливается в `FALSE` при логауте или истечении таймаута.
Рекомендуемый DDL:
```sql
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_activity_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
```
## Таблица `budgets` (после MVP)
> **Не входит в MVP.** Таблица запланирована для поддержки бюджетов/лимитов по категориям
> на будущих этапах. Подробности см. в `analytics.md`, секция 5.3.
## Взаимосвязь JSON → БД при импорте
1. По полям `bank` и `statement.accountNumber` ищется или создаётся запись в `accounts`.
Если счёт создан впервые, `alias = NULL`.
2. Для каждой транзакции из `transactions`:
- преобразуются суммы в копейки (`amountSigned`, `commission``BIGINT`);
- суммы в JSON 1.0 уже в копейках — записываются как есть;
- вычисляется `fingerprint` на основе комбинации полей (например,
`accountNumber + operationAt + amountSigned + commission + normalizedDescription`);
- определяется `direction`:
- `amountSigned > 0``"income"`;
- `amountSigned < 0``"expense"`;
- `"transfer"` — определяется по фиксированным ключевым фразам банка в `description`
(например, для ВТБ: "Перевод", "между счетами" и т.п.).
(например, для ВТБ: "Перевод между своими счетами", "Внутри ВТБ" и т.п.).
Если ключевые фразы не сработали — остаётся `"income"` / `"expense"` по знаку суммы;
- выполняется попытка вставки в `transactions`;
- при срабатывании уникального ограничения `(account_id, fingerprint)` запись считается дубликатом и пропускается;

View File

@@ -4,227 +4,196 @@
"statement": {
"accountNumber": "40817810825104025611",
"currency": "RUB",
"openingBalance": 42561.67,
"closingBalance": 88459.38,
"openingBalance": 4256167,
"closingBalance": 8845938,
"exportedAt": "2026-02-27T13:23:00+03:00"
},
"transactions": [
{
"operationAt": "2026-02-26T14:06:57+03:00",
"amountSigned": -500.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. PAVELETSKAYA. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-26T14:06:57+03:00-500.000.00Оплата товаров и услуг. PAVELETSKAYA. по карте *8214"
"amountSigned": -50000,
"commission": 0,
"description": "Оплата товаров и услуг. PAVELETSKAYA. по карте *8214"
},
{
"operationAt": "2026-02-26T11:46:14+03:00",
"amountSigned": -83.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. MOS.TRANSPORT. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-26T11:46:14+03:00-83.000.00Оплата товаров и услуг. MOS.TRANSPORT. по карте *8214"
"amountSigned": -8300,
"commission": 0,
"description": "Оплата товаров и услуг. MOS.TRANSPORT. по карте *8214"
},
{
"operationAt": "2026-02-25T23:12:10+03:00",
"amountSigned": 477.0,
"commission": 0.0,
"description": "Зачисление кешбэка за покупки у партнеров. Перечисление бонусных рублей на счета получателя за покупки у партнеров согласно реестру Z_3507_20260225_1 согласно Договору МБ-14Х/25.",
"fingerprint": "sha256:408178108251040256112026-02-25T23:12:10+03:00477.000.00Зачисление кешбэка за покупки у партнеров. Перечисление бонусных рублей на счета получателя за покупки у партнеров согласно реестру Z_3507_20260225_1 согласно Договору МБ-14Х/25."
"amountSigned": 47700,
"commission": 0,
"description": "Зачисление кешбэка за покупки у партнеров. Перечисление бонусных рублей на счета получателя за покупки у партнеров согласно реестру Z_3507_20260225_1 согласно Договору МБ-14Х/25."
},
{
"operationAt": "2026-02-25T16:37:47+03:00",
"amountSigned": -449.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-25T16:37:47+03:00-449.000.00Оплата товаров и услуг. OZON.RU. по карте *2249"
"amountSigned": -44900,
"commission": 0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249"
},
{
"operationAt": "2026-02-25T12:09:51+03:00",
"amountSigned": -3700.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. Lab4uru App. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-25T12:09:51+03:00-3700.000.00Оплата товаров и услуг. Lab4uru App. по карте *2249"
"amountSigned": -370000,
"commission": 0,
"description": "Оплата товаров и услуг. Lab4uru App. по карте *2249"
},
{
"operationAt": "2026-02-25T11:18:38+03:00",
"amountSigned": -1821.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. WILDBERRIES. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-25T11:18:38+03:00-1821.000.00Оплата товаров и услуг. WILDBERRIES. по карте *2249"
"amountSigned": -182100,
"commission": 0,
"description": "Оплата товаров и услуг. WILDBERRIES. по карте *2249"
},
{
"operationAt": "2026-02-24T21:49:31+03:00",
"amountSigned": -100000.0,
"commission": 0.0,
"description": "Перевод между своими счетами. Перечисление ДС для приобретения ценных бумаг. Основной рынок. Субпозиция №460827 (НДС не обл.) Канал - ВТБО. Шестеров Антон Владимирович.",
"fingerprint": "sha256:408178108251040256112026-02-24T21:49:31+03:00-100000.000.00Перевод между своими счетами. Перечисление ДС для приобретения ценных бумаг. Основной рынок. Субпозиция №460827 (НДС не обл.) Канал - ВТБО. Шестеров Антон Владимирович."
"amountSigned": -10000000,
"commission": 0,
"description": "Перевод между своими счетами. Перечисление ДС для приобретения ценных бумаг. Основной рынок. Субпозиция №460827 (НДС не обл.) Канал - ВТБО. Шестеров Антон Владимирович."
},
{
"operationAt": "2026-02-24T17:31:00+03:00",
"amountSigned": -214.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. STOLOVAYA 2. по карте *9058",
"fingerprint": "sha256:408178108251040256112026-02-24T17:31:00+03:00-214.000.00Оплата товаров и услуг. STOLOVAYA 2. по карте *9058"
"amountSigned": -21400,
"commission": 0,
"description": "Оплата товаров и услуг. STOLOVAYA 2. по карте *9058"
},
{
"operationAt": "2026-02-24T17:03:39+03:00",
"amountSigned": -26000.0,
"commission": 0.0,
"description": "Перевод между своими счетами. Перечисление средств на счет 2631",
"fingerprint": "sha256:408178108251040256112026-02-24T17:03:39+03:00-26000.000.00Перевод между своими счетами. Перечисление средств на счет 2631"
"amountSigned": -2600000,
"commission": 0,
"description": "Перевод между своими счетами. Перечисление средств на счет 2631"
},
{
"operationAt": "2026-02-24T17:03:24+03:00",
"amountSigned": -50000.0,
"commission": 0.0,
"description": "Перевод между своими счетами. Перечисление средств на счет 0292",
"fingerprint": "sha256:408178108251040256112026-02-24T17:03:24+03:00-50000.000.00Перевод между своими счетами. Перечисление средств на счет 0292"
"amountSigned": -5000000,
"commission": 0,
"description": "Перевод между своими счетами. Перечисление средств на счет 0292"
},
{
"operationAt": "2026-02-24T16:11:21+03:00",
"amountSigned": 189520.22,
"commission": 0.0,
"description": "Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_001_01 от 20.02.2026. Без НДС.",
"fingerprint": "sha256:408178108251040256112026-02-24T16:11:21+03:00189520.220.00Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_001_01 от 20.02.2026. Без НДС."
"amountSigned": 18952022,
"commission": 0,
"description": "Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_001_01 от 20.02.2026. Без НДС."
},
{
"operationAt": "2026-02-24T16:09:15+03:00",
"amountSigned": 72393.53,
"commission": 0.0,
"description": "Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_010_01 от 20.02.2026. Без НДС.",
"fingerprint": "sha256:408178108251040256112026-02-24T16:09:15+03:0072393.530.00Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_010_01 от 20.02.2026. Без НДС."
"amountSigned": 7239353,
"commission": 0,
"description": "Поступление заработной платы. 0726 НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ \"ВЫСША Поступление заработной платы/иных выплат Salary по реестру Z_0000005862_20260220_010_01 от 20.02.2026. Без НДС."
},
{
"operationAt": "2026-02-24T14:29:53+03:00",
"amountSigned": -778.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. VKUSVILL_665_2. по карте *9058",
"fingerprint": "sha256:408178108251040256112026-02-24T14:29:53+03:00-778.000.00Оплата товаров и услуг. VKUSVILL_665_2. по карте *9058"
"amountSigned": -77800,
"commission": 0,
"description": "Оплата товаров и услуг. VKUSVILL_665_2. по карте *9058"
},
{
"operationAt": "2026-02-24T14:20:59+03:00",
"amountSigned": -6180.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. Lab4uru App. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-24T14:20:59+03:00-6180.000.00Оплата товаров и услуг. Lab4uru App. по карте *2249"
"amountSigned": -618000,
"commission": 0,
"description": "Оплата товаров и услуг. Lab4uru App. по карте *2249"
},
{
"operationAt": "2026-02-24T13:05:47+03:00",
"amountSigned": -280.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. STOLOVAYA.. по карте *9058",
"fingerprint": "sha256:408178108251040256112026-02-24T13:05:47+03:00-280.000.00Оплата товаров и услуг. STOLOVAYA.. по карте *9058"
"amountSigned": -28000,
"commission": 0,
"description": "Оплата товаров и услуг. STOLOVAYA.. по карте *9058"
},
{
"operationAt": "2026-02-24T11:35:33+03:00",
"amountSigned": -9824.3,
"commission": 0.0,
"description": "Оплата товаров и услуг. SKLAD MSK. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-24T11:35:33+03:00-9824.300.00Оплата товаров и услуг. SKLAD MSK. по карте *8214"
"amountSigned": -982430,
"commission": 0,
"description": "Оплата товаров и услуг. SKLAD MSK. по карте *8214"
},
{
"operationAt": "2026-02-24T09:32:11+03:00",
"amountSigned": -800.0,
"commission": 0.0,
"description": "Внутри ВТБ. Ахмедов Али Эльдар оглы.",
"fingerprint": "sha256:408178108251040256112026-02-24T09:32:11+03:00-800.000.00Внутри ВТБ. Ахмедов Али Эльдар оглы."
"amountSigned": -80000,
"commission": 0,
"description": "Внутри ВТБ. Ахмедов Али Эльдар оглы."
},
{
"operationAt": "2026-02-24T09:29:30+03:00",
"amountSigned": -5132.72,
"commission": 0.0,
"description": "Оплата товаров и услуг. RNAZK MC013. по карте *9058",
"fingerprint": "sha256:408178108251040256112026-02-24T09:29:30+03:00-5132.720.00Оплата товаров и услуг. RNAZK MC013. по карте *9058"
"amountSigned": -513272,
"commission": 0,
"description": "Оплата товаров и услуг. RNAZK MC013. по карте *9058"
},
{
"operationAt": "2026-02-23T20:58:02+03:00",
"amountSigned": -515.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. OZON. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-23T20:58:02+03:00-515.000.00Оплата товаров и услуг. OZON. по карте *2249"
"amountSigned": -51500,
"commission": 0,
"description": "Оплата товаров и услуг. OZON. по карте *2249"
},
{
"operationAt": "2026-02-23T15:31:37+03:00",
"amountSigned": -1105.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. ZOOMAGAZIN CHETYRE LAP. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-23T15:31:37+03:00-1105.000.00Оплата товаров и услуг. ZOOMAGAZIN CHETYRE LAP. по карте *8214"
"amountSigned": -110500,
"commission": 0,
"description": "Оплата товаров и услуг. ZOOMAGAZIN CHETYRE LAP. по карте *8214"
},
{
"operationAt": "2026-02-23T15:25:53+03:00",
"amountSigned": -225.97,
"commission": 0.0,
"description": "Оплата товаров и услуг. PYATEROCHKA 6993. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-23T15:25:53+03:00-225.970.00Оплата товаров и услуг. PYATEROCHKA 6993. по карте *8214"
"amountSigned": -22597,
"commission": 0,
"description": "Оплата товаров и услуг. PYATEROCHKA 6993. по карте *8214"
},
{
"operationAt": "2026-02-23T13:34:47+03:00",
"amountSigned": -3926.33,
"commission": 0.0,
"description": "Оплата товаров и услуг. DOSTAVKA PEREKRESTKA_S. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-23T13:34:47+03:00-3926.330.00Оплата товаров и услуг. DOSTAVKA PEREKRESTKA_S. по карте *2249"
"amountSigned": -392633,
"commission": 0,
"description": "Оплата товаров и услуг. DOSTAVKA PEREKRESTKA_S. по карте *2249"
},
{
"operationAt": "2026-02-22T22:32:47+03:00",
"amountSigned": -79.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-22T22:32:47+03:00-79.000.00Оплата товаров и услуг. OZON.RU. по карте *2249"
"amountSigned": -7900,
"commission": 0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249"
},
{
"operationAt": "2026-02-22T19:59:48+03:00",
"amountSigned": -493.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-22T19:59:48+03:00-493.000.00Оплата товаров и услуг. OZON.RU. по карте *2249"
"amountSigned": -49300,
"commission": 0,
"description": "Оплата товаров и услуг. OZON.RU. по карте *2249"
},
{
"operationAt": "2026-02-22T19:55:28+03:00",
"amountSigned": -536.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. OZON. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-22T19:55:28+03:00-536.000.00Оплата товаров и услуг. OZON. по карте *2249"
"amountSigned": -53600,
"commission": 0,
"description": "Оплата товаров и услуг. OZON. по карте *2249"
},
{
"operationAt": "2026-02-22T11:13:21+03:00",
"amountSigned": -61.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. CPPK-2000012-BPA20. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-22T11:13:21+03:00-61.000.00Оплата товаров и услуг. CPPK-2000012-BPA20. по карте *8214"
"amountSigned": -6100,
"commission": 0,
"description": "Оплата товаров и услуг. CPPK-2000012-BPA20. по карте *8214"
},
{
"operationAt": "2026-02-21T18:40:37+03:00",
"amountSigned": -1380.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. mkad. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-21T18:40:37+03:00-1380.000.00Оплата товаров и услуг. mkad. по карте *2249"
"amountSigned": -138000,
"commission": 0,
"description": "Оплата товаров и услуг. mkad. по карте *2249"
},
{
"operationAt": "2026-02-21T11:29:26+03:00",
"amountSigned": -1715.87,
"commission": 0.0,
"description": "Оплата товаров и услуг. MAGNIT MK STAROPOTAPOV. по карте *8214",
"fingerprint": "sha256:408178108251040256112026-02-21T11:29:26+03:00-1715.870.00Оплата товаров и услуг. MAGNIT MK STAROPOTAPOV. по карте *8214"
"amountSigned": -171587,
"commission": 0,
"description": "Оплата товаров и услуг. MAGNIT MK STAROPOTAPOV. по карте *8214"
},
{
"operationAt": "2026-02-21T10:03:16+03:00",
"amountSigned": -3700.0,
"commission": 0.0,
"description": "Оплата товаров и услуг. CP* SPIRITFIT.RU. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-21T10:03:16+03:00-3700.000.00Оплата товаров и услуг. CP* SPIRITFIT.RU. по карте *2249"
"amountSigned": -370000,
"commission": 0,
"description": "Оплата товаров и услуг. CP* SPIRITFIT.RU. по карте *2249"
},
{
"operationAt": "2026-02-21T09:55:24+03:00",
"amountSigned": -32.99,
"commission": 0.0,
"description": "Оплата товаров и услуг. GLOBUS KOROLEV. по карте *9058",
"fingerprint": "sha256:408178108251040256112026-02-21T09:55:24+03:00-32.990.00Оплата товаров и услуг. GLOBUS KOROLEV. по карте *9058"
"amountSigned": -3299,
"commission": 0,
"description": "Оплата товаров и услуг. GLOBUS KOROLEV. по карте *9058"
},
{
"operationAt": "2026-02-20T15:23:19+03:00",
"amountSigned": -1438.86,
"commission": 0.0,
"description": "Оплата товаров и услуг. DOSTAVKA IZ PYATEROCHK. по карте *2249",
"fingerprint": "sha256:408178108251040256112026-02-20T15:23:19+03:00-1438.860.00Оплата товаров и услуг. DOSTAVKA IZ PYATEROCHK. по карте *2249"
"amountSigned": -143886,
"commission": 0,
"description": "Оплата товаров и услуг. DOSTAVKA IZ PYATEROCHK. по карте *2249"
}
]
}

View File

@@ -12,7 +12,7 @@ React SPA для учёта семейного бюджета: импорт ба
## Структура
```
```text
frontend/
├── index.html — точка входа
├── vite.config.ts — конфигурация Vite (прокси на backend)

35
package-lock.json generated
View File

@@ -85,6 +85,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1368,6 +1369,7 @@
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -1436,6 +1438,7 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1569,6 +1572,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2571,6 +2575,7 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.19.0.tgz",
"integrity": "sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"pg-connection-string": "^2.11.0",
"pg-pool": "^3.12.0",
@@ -2668,6 +2673,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -2817,6 +2823,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2826,6 +2833,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -3278,7 +3286,6 @@
"os": [
"aix"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3296,7 +3303,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3314,7 +3320,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3332,7 +3337,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3350,7 +3354,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3368,7 +3371,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3386,7 +3388,6 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3404,7 +3405,6 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3422,7 +3422,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3440,7 +3439,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3458,7 +3456,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3476,7 +3473,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3494,7 +3490,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3512,7 +3507,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3530,7 +3524,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3548,7 +3541,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3566,7 +3558,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3584,7 +3575,6 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3602,7 +3592,6 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3620,7 +3609,6 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3638,7 +3626,6 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3656,7 +3643,6 @@
"os": [
"openharmony"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3674,7 +3660,6 @@
"os": [
"sunos"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3692,7 +3677,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3710,7 +3694,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3728,7 +3711,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3900,6 +3882,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",