added front - react+ts

ocr improved
This commit is contained in:
2025-12-11 05:20:53 +03:00
parent 73b1477368
commit 02681340c5
39 changed files with 6286 additions and 267 deletions

View File

@@ -1,161 +1,164 @@
# API Documentation: RMSER Backend
# RMSER Backend API Documentation (v2.0)
## 1. Общая информация
**Дата обновления:** Текущая
**Base URL:** `http://localhost:8080/api`
* **Base URL:** `http://localhost:8080/api`
* **Формат данных:** JSON.
* **CORS:** Разрешены запросы с `localhost:5173` (и любых других источников в режиме dev).
* **Auth:** На данный момент эндпоинты открыты. В будущем предполагается передача токена в заголовке `Authorization: Bearer <token>`.
## 1. Типы данных (TypeScript Interfaces)
### Обработка ошибок
В случае ошибки сервер возвращает HTTP код 4xx/5xx и JSON:
```json
{
"error": "Описание ошибки"
}
```
---
## 2. TypeScript Интерфейсы (Models)
Используйте эти интерфейсы для типизации данных на клиенте.
Используйте эти интерфейсы для строгой типизации на клиенте.
```typescript
// --- Общие типы ---
// UUID (строка)
// Базовые типы
type UUID = string;
// Decimal (деньги/вес приходят строкой, чтобы избежать потери точность в JS)
type Decimal = string;
// --- Каталог и Фасовки ---
// --- Каталог и OCR ---
export interface MeasureUnit {
id: UUID;
name: string; // "кг", "л", "шт", "порц"
code: string;
}
export interface ProductContainer {
id: UUID;
name: string; // "Коробка", "Пачка 200г"
count: number; // Коэффициент пересчета в базовые единицы (напр. 0.2 или 12.0)
}
// Товар для выпадающего списка (Autosuggest)
export interface CatalogItem {
id: UUID;
name: string;
code: string; // Артикул или код быстрого набора
code: string;
measure_unit: string; // Название базовой единицы (из MainUnit)
containers: ProductContainer[]; // Доступные фасовки
}
// Связь "Текст из чека" -> "Товар iiko"
export interface ProductMatch {
raw_name: string; // Текст из OCR (ключ)
product_id: UUID;
product?: CatalogItem; // Вложенный объект (при чтении)
updated_at: string; // ISO Date
// --- Матчинг (Обучение) ---
export interface MatchRequest {
raw_name: string; // Текст из чека
product_id: UUID; // ID товара iiko
quantity: number; // Количество (по умолчанию 1.0)
container_id?: UUID; // Опционально: ID фасовки, если выбрана
}
// --- Рекомендации (Аналитика) ---
export interface Recommendation {
ID: UUID;
Type: string; // Код проблемы (UNUSED_IN_RECIPES, NO_INCOMING, etc.)
ProductID: UUID;
ProductName: string;
Reason: string; // Человекочитаемое описание проблемы
CreatedAt: string;
export interface SavedMatch {
raw_name: string;
product: CatalogItem; // Вложенный объект товара
quantity: number; // Сохраненный коэффициент/количество
container_id?: UUID;
container?: ProductContainer; // Вложенный объект фасовки (для отображения имени)
updated_at: string;
}
// --- Накладные ---
// --- Нераспознанное ---
export interface InvoiceItemRequest {
product_id: UUID;
amount: number; // или string, если нужна высокая точность
price: number;
}
export interface CreateInvoiceRequest {
document_number: string;
date_incoming: string; // Format: "YYYY-MM-DD"
supplier_id: UUID;
store_id: UUID;
items: InvoiceItemRequest[];
export interface UnmatchedItem {
raw_name: string;
count: number; // Сколько раз встречалось в чеках
last_seen: string; // ISO Date
}
```
---
## 3. Эндпоинты
## 2. Эндпоинты: OCR и Обучение
### 📊 Аналитика (Рекомендации)
### `GET /ocr/catalog`
Получить полный справочник товаров для поиска.
Теперь включает **единицы измерения** и **фасовки**.
#### Получить список рекомендаций
Возвращает список выявленных проблем в учете (товары без техкарт, ингредиенты без закупок и т.д.). Данные обновляются фоновым процессом на бэкенде.
* **URL:** `/recommendations`
* **Method:** `GET`
* **Response:** `200 OK`
```json
[
{
"ID": "uuid...",
"Type": "UNUSED_IN_RECIPES",
"ProductID": "uuid...",
"ProductName": "Петрушка с",
"Reason": "Товар не используется ни в одной техкарте",
"CreatedAt": "2023-10-27T10:00:00Z"
}
]
```
---
### 👁 OCR и Обучение (Matching)
#### 1. Получить справочник товаров (для селекта)
Используется для построения локального индекса поиска (autocomplete) на фронтенде, чтобы привязывать позиции. Возвращает только активные товары (`GOODS`).
* **URL:** `/ocr/catalog`
* **Method:** `GET`
* **Response:** `200 OK`
```json
[
{
"id": "607a1e96-f539-45d2-8709-3919f94bdc3e",
"name": "Молоко Домик в Деревне 3.2%",
"code": "00123"
"id": "uuid-butter...",
"name": "Масло сливочное 82%",
"code": "00123",
"measure_unit": "кг",
"containers": [
{
"id": "uuid-pack...",
"name": "Пачка 180г",
"count": 0.180
},
{
"id": "uuid-box...",
"name": "Коробка (20 пачек)",
"count": 3.6
}
]
},
...
{
"id": "uuid-milk...",
"name": "Молоко 3.2%",
"code": "00124",
"measure_unit": "л",
"containers": [] // Пустой массив, если фасовок нет
}
]
```
#### 2. Получить таблицу обучения (Matches)
Возвращает список уже созданных связей "Грязное название из чека" -> "Чистый товар iiko".
---
### `GET /ocr/matches`
Получить список уже обученных позиций. Используется для отображения таблицы "Ранее сохраненные связи".
* **URL:** `/ocr/matches`
* **Method:** `GET`
* **Response:** `200 OK`
```json
[
{
"raw_name": "молоко двд 3,2",
"product_id": "607a1e96-f539-45d2-8709-3919f94bdc3e",
"raw_name": "Масло слив. 3 пачки",
"product_id": "uuid-butter...",
"product": {
"ID": "607a1e96-f539-45d2-8709-3919f94bdc3e",
"Name": "Молоко Домик в Деревне 3.2%",
...
"id": "uuid-butter...",
"name": "Масло сливочное 82%",
"measure_unit": "кг"
// ...остальные поля product
},
"updated_at": "..."
"quantity": 3,
"container_id": "uuid-pack...",
"container": {
"id": "uuid-pack...",
"name": "Пачка 180г",
"count": 0.180
},
"updated_at": "2023-10-27T10:00:00Z"
}
]
```
> **Логика отображения на фронте:**
> Если `container` пришел (не null) -> отображаем: `Quantity` x `Container.Name` (3 x Пачка 180г).
> Если `container` null -> отображаем: `Quantity` `Product.MeasureUnit` (0.54 кг).
#### 3. Создать/Обновить привязку
Сохраняет правило: "Если в чеке встретишь этот текст, считай это вот этим товаром".
---
### `POST /ocr/match`
Создать или обновить привязку.
* **URL:** `/ocr/match`
* **Method:** `POST`
* **Body:**
**Вариант 1: Базовая единица (без фасовки)**
Пользователь выбрал "Петрушка", ввел "0.5" (кг).
```json
{
"raw_name": "молоко двд 3,2",
"product_id": "607a1e96-f539-45d2-8709-3919f94bdc3e"
"raw_name": "петрушка вес",
"product_id": "uuid-parsley...",
"quantity": 0.5
// container_id не передаем или null
}
```
**Вариант 2: С фасовкой**
Пользователь выбрал "Масло", выбрал фасовку "Коробка", ввел "2" (штуки).
```json
{
"raw_name": "масло коробка",
"product_id": "uuid-butter...",
"quantity": 2,
"container_id": "uuid-box..."
}
```
@@ -163,78 +166,79 @@ export interface CreateInvoiceRequest {
---
### 📄 Накладные (Invoices)
### `GET /ocr/unmatched`
Получить список частых нераспознанных товаров.
Используется для автодополнения (Suggest) в поле ввода "Текст из чека", чтобы пользователь не вводил название вручную.
#### Отправить накладную в iikoRMS
Создает черновик приходной накладной в системе iiko.
* **Response:** `200 OK`
* **URL:** `/invoices/send`
* **Method:** `POST`
* **Body:**
```json
[
{
"raw_name": "Хлеб Бородинский нар.",
"count": 12,
"last_seen": "2023-10-27T12:00:00Z"
},
{
"raw_name": "Пакет майка",
"count": 5,
"last_seen": "2023-10-26T09:00:00Z"
}
]
```
---
## 3. Эндпоинты: Накладные (Invoices)
### `POST /invoices/send`
Отправка накладной в iiko.
**Важно:** При формировании накладной на фронте, вы должны пересчитывать количество в базовые единицы, либо (в будущем) мы научим бэкенд принимать ID фасовки в `items`.
На данный момент API ожидает `amount` и `price` уже приведенными к единому знаменателю, либо iiko сама разберется, если мы передадим ContainerID (этот функционал в разработке на стороне `rms_client`, пока шлите базовые единицы).
```json
{
"document_number": "INV-12345",
"document_number": "INV-100",
"date_incoming": "2023-10-27",
"supplier_id": "uuid-supplier...",
"store_id": "uuid-store...",
"supplier_id": "uuid...",
"store_id": "uuid...",
"items": [
{
"product_id": "uuid-product...",
"amount": 10.5,
"price": 150.00
"product_id": "uuid...",
"amount": 1.5, // 1.5 кг
"price": 100 // Цена за кг
}
]
}
```
* **Response:** `200 OK`
---
## 4. Эндпоинты: Аналитика
### `GET /recommendations`
Возвращает список проблем (товары без техкарт, без закупок и т.д.).
```json
{
"status": "ok",
"created_number": "000123" // Номер документа, присвоенный iiko
}
[
{
"ID": "uuid...",
"Type": "UNUSED_IN_RECIPES",
"ProductID": "uuid...",
"ProductName": "Лист салата",
"Reason": "Товар не используется ни в одной техкарте",
"CreatedAt": "..."
}
]
```
---
### ⚙️ System
#### Healthcheck
Проверка доступности API.
* **URL:** `http://localhost:8080/health`
* **Method:** `GET`
* **Response:**
## 5. System
### `GET /health`
Проверка статуса.
```json
{
"status": "ok",
"time": "2023-10-27T12:34:56+03:00"
}
```
---
## 4. Сценарии использования (Frontend Workflow)
### Сценарий А: "Обучение" (раздел Settings / OCR Learning)
1. При загрузке страницы вызвать `GET /api/ocr/matches` и отобразить таблицу.
2. Вызвать `GET /api/ocr/catalog` и сохранить в памяти для быстрого поиска (Combobox/Select).
3. Пользователь может добавить новую связь вручную:
* Вводит текст (например, "Хлеб Бородинский").
* Выбирает товар из выпадающего списка.
* Нажимает "Save" -> вызывается `POST /api/ocr/match`.
* Таблица обновляется.
### Сценарий Б: "Дашборд аналитика"
1. При загрузке главной страницы вызвать `GET /api/recommendations`.
2. Сгруппировать массив по полю `Type` или `Reason`.
3. Отобразить карточки: "Товары без техкарт (5 шт)", "Ингредиенты без закупок (12 шт)".
### Сценарий В: "Создание накладной" (Пока ручное)
*Пока нет загрузки фото через Web, предполагается ручной ввод или редактирование данных, полученных иным путем.*
1. Форма ввода номера, даты.
2. Таблица товаров (добавление строк через поиск по каталогу).
3. Кнопка "Отправить в iiko" -> `POST /api/invoices/send`.
{"status": "ok", "time": "..."}
```