370 lines
8.9 KiB
Markdown
370 lines
8.9 KiB
Markdown
# 📘 Финансовый модуль
|
||
|
||
## 1. Общее описание
|
||
|
||
Финансовый модуль предназначен для учёта:
|
||
- доходов
|
||
- расходов
|
||
- категорий
|
||
|
||
Поддерживает три способа ввода расходов:
|
||
1. Ручной ввод
|
||
2. Ввод номера и даты чека
|
||
3. Загрузка фото чека
|
||
|
||
---
|
||
|
||
## 2. Глоссарий
|
||
|
||
**Доход (Income)**
|
||
Денежное поступление (зарплата, перевод, подарок и т.д.)
|
||
|
||
**Расход (Expense)**
|
||
Факт траты денег
|
||
|
||
**Категория (Category)**
|
||
Классификация доходов и расходов (например: еда, транспорт, зарплата)
|
||
|
||
**Чек (Receipt)**
|
||
Документ, содержащий информацию о покупке (дата, позиции, сумма)
|
||
|
||
**Позиция (Position)**
|
||
Отдельная строка в чеке (товар или услуга)
|
||
|
||
---
|
||
|
||
## 3. Доменная модель
|
||
|
||
### 3.1 Positions
|
||
|
||
```
|
||
positions (
|
||
id,
|
||
receipt_number,
|
||
operation_date,
|
||
gtin_code,
|
||
product_name,
|
||
product_count,
|
||
amount,
|
||
discount,
|
||
name_spd,
|
||
category_id,
|
||
family_id,
|
||
family_member_id,
|
||
created_at,
|
||
updated_at
|
||
)
|
||
```
|
||
|
||
**Описание полей:**
|
||
- `receipt_number` — номер чека (nullable для ручного ввода)
|
||
- `operation_date` — дата операции
|
||
- `gtin_code` — код товара
|
||
- `product_name` — название товара
|
||
- `product_count` — количество
|
||
- `amount` — сумма позиции
|
||
- `discount` — скидка
|
||
- `name_spd` — продавец
|
||
- `category_id` — категория
|
||
- `family_id` — семья
|
||
- `family_member_id` — участник
|
||
|
||
---
|
||
|
||
### 3.2 Receipts
|
||
|
||
```
|
||
receipts (
|
||
id,
|
||
receipt_number,
|
||
ui,
|
||
status,
|
||
issued_at,
|
||
total_amount,
|
||
payment_amount,
|
||
cash_amount,
|
||
another_amount,
|
||
clearing_amount,
|
||
margin,
|
||
currency,
|
||
payment_type,
|
||
cashbox_number,
|
||
cashier,
|
||
name_spd,
|
||
name_to,
|
||
name_np,
|
||
type_np,
|
||
street_to,
|
||
house_to,
|
||
kod_soato,
|
||
oblast_soato,
|
||
rayon_soato,
|
||
selsovet_soato,
|
||
doc_num,
|
||
skno_number,
|
||
unp,
|
||
success,
|
||
family_id,
|
||
family_member_id,
|
||
created_at
|
||
)
|
||
```
|
||
|
||
**Описание:**
|
||
- хранит агрегированную информацию о чеке
|
||
- используется при сканировании QR
|
||
- связывается с positions через `receipt_number`
|
||
|
||
---
|
||
|
||
### 3.3 Categories
|
||
|
||
```
|
||
categories (
|
||
id,
|
||
name,
|
||
type,
|
||
family_id,
|
||
family_member_id,
|
||
created_at
|
||
)
|
||
```
|
||
|
||
**Описание полей:**
|
||
- `type` — income | expense
|
||
- категория принадлежит семье
|
||
|
||
---
|
||
|
||
## 4. Бизнес-логика
|
||
|
||
### 4.1 Добавление расхода вручную
|
||
|
||
- пользователь вводит:
|
||
- сумму
|
||
- описание
|
||
- категорию
|
||
- создаётся запись в `positions`
|
||
- `receipt_number = NULL`
|
||
|
||
---
|
||
|
||
### 4.2 Добавление дохода
|
||
|
||
- аналогично расходу
|
||
- используется категория типа `income`
|
||
|
||
---
|
||
|
||
### 4.3 Добавление расхода по номеру и дате чека
|
||
|
||
#### Поток:
|
||
1. Пользователь вводит номер и дату чека
|
||
2. Backend получает данные чека через внешний сервис
|
||
3. Создаётся запись в `receipts`
|
||
4. Создаётся связанная транзакция в `transactions`
|
||
5. Для каждой позиции создаётся запись в `positions`
|
||
|
||
---
|
||
|
||
### 4.4 Добавление расхода по фото чека
|
||
|
||
#### Поток:
|
||
1. Пользователь загружает фото чека
|
||
2. Backend извлекает текст через OCR
|
||
3. Backend извлекает из текста номер и дату чека
|
||
4. Backend получает данные чека через внешний сервис
|
||
5. Создаётся запись в `receipts`
|
||
6. Создаётся связанная транзакция в `transactions`
|
||
7. Для каждой позиции создаётся запись в `positions`
|
||
|
||
---
|
||
|
||
## 5. Потоки
|
||
|
||
### Ручной ввод
|
||
|
||
```text
|
||
User → API POST /transactions → transactions
|
||
```
|
||
|
||
### Доход
|
||
|
||
```text
|
||
User → API POST /transactions → transactions
|
||
```
|
||
|
||
### Чек по номеру и дате
|
||
|
||
```text
|
||
User → API POST /transactions → receipt provider → receipts + positions + transactions
|
||
```
|
||
|
||
### Чек по фото
|
||
|
||
```text
|
||
User → API POST /transactions multipart/form-data → OCR → receipt provider → receipts + positions + transactions
|
||
```
|
||
|
||
---
|
||
|
||
## 6. API (черновик)
|
||
|
||
### Создание транзакции вручную
|
||
|
||
```http
|
||
POST /api/v1/transactions
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"family_id": 1,
|
||
"created_by": 2,
|
||
"type": "expense",
|
||
"category": "groceries",
|
||
"amount": 1000,
|
||
"description": "Продукты",
|
||
"datetime": "2026-01-21T10:11:12Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Создание транзакции по номеру и дате чека
|
||
|
||
```http
|
||
POST /api/v1/transactions
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"family_id": 1,
|
||
"created_by": 2,
|
||
"receipt_number": "0123456789ABCDEFGHIJKLMN",
|
||
"receipt_date": "21.01.2026"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Создание транзакции по фото чека
|
||
|
||
```http
|
||
POST /api/v1/transactions
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
Поля формы:
|
||
- `photo` — файл изображения чека, обязательно
|
||
- `family_id` — ID семьи, обязательно
|
||
- `created_by` — ID пользователя, обязательно
|
||
- `type` — тип транзакции, опционально, по умолчанию `expense`
|
||
- `category` — категория, опционально, по умолчанию `receipt`
|
||
- `description` — описание транзакции, опционально
|
||
|
||
---
|
||
|
||
### Получение транзакций
|
||
|
||
```http
|
||
GET /api/v1/transactions
|
||
```
|
||
|
||
Фильтры:
|
||
- дата
|
||
- категория
|
||
- тип
|
||
- family_id
|
||
|
||
---
|
||
|
||
### Правила для `POST /api/v1/transactions`
|
||
|
||
Используется ровно один сценарий создания:
|
||
|
||
1. Ручная транзакция:
|
||
обязательны `family_id`, `created_by`, `type`, `category`, `amount`, `datetime`
|
||
2. Транзакция по чеку:
|
||
обязательны `family_id`, `created_by`, `receipt_number`, `receipt_date`
|
||
3. Транзакция по фото:
|
||
обязательны `photo`, `family_id`, `created_by`
|
||
|
||
Нельзя смешивать ручные поля транзакции (`amount`, `datetime`, `receipt_id`) с полями чека (`receipt_number`, `receipt_date`) в одном JSON-запросе.
|
||
|
||
---
|
||
|
||
## 7. Задачи для разработки
|
||
|
||
### Этап 1 — База
|
||
|
||
- [ ] Переписать SQL-миграции (positions, receipts, categories)
|
||
|
||
---
|
||
|
||
### Этап 2 — Категории
|
||
|
||
- [ ] CRUD категорий
|
||
- [ ] Валидация типа (income/expense)
|
||
|
||
---
|
||
|
||
### Этап 3 — Транзакции
|
||
|
||
- [x] Endpoint создания транзакции
|
||
- [x] Endpoint получения списка
|
||
- [ ] Фильтрация
|
||
|
||
---
|
||
|
||
### Этап 4 — Доходы/расходы
|
||
|
||
- [ ] Определение типа через категорию
|
||
- [ ] Валидация соответствия
|
||
|
||
---
|
||
|
||
### Этап 5 — Чеки
|
||
|
||
- [x] Endpoint загрузки фото чека через `POST /transactions`
|
||
- [ ] Интеграция с сервисом чеков
|
||
- [x] Создание receipts
|
||
- [x] Создание транзакции по номеру и дате чека
|
||
- [x] Создание транзакции по фото чека
|
||
- [ ] Создание positions
|
||
|
||
---
|
||
|
||
### Этап 6 — Telegram интеграция
|
||
|
||
- [ ] Команды добавления дохода/расхода
|
||
- [ ] Обработка QR
|
||
|
||
---
|
||
|
||
### Этап 7 — Дополнительно
|
||
|
||
- [ ] Автокатегоризация
|
||
- [ ] Статистика
|
||
- [ ] Лимиты
|
||
|
||
---
|
||
|
||
## 8. Архитектурные решения
|
||
- Transaction — основная сущность финансов
|
||
- Receipt — дополняет транзакцию
|
||
- Position - позиции из чека
|
||
- Категории определяют тип операции
|
||
- Поддержка multi-tenant через family_id
|
||
|
||
---
|
||
|
||
## 9. Открытые вопросы
|
||
|
||
- [ ] Нужна ли мультивалютность?
|
||
- [ ] Можно ли редактировать чек?
|
||
- [ ] Как обрабатывать ошибки OCR?
|
||
- [ ] Нужен ли отдельный endpoint для повторной привязки чека к существующей транзакции?
|
||
- [ ] Нужны ли роли внутри семьи?
|