mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
added front - react+ts
ocr improved
This commit is contained in:
324
API_DOCS.md
324
API_DOCS.md
@@ -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": "..."}
|
||||
```
|
||||
Reference in New Issue
Block a user