food-market/docs/mobile-api.md
nns ad812ee2b5
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
docs(mobile-api): описание API для мобильного клиента + прогресс
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:39:37 +05:00

78 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API для мобильного «FM Склад»
Серверные возможности, добавленные для iOS-приложения (стенд `api/*`). Авторизация —
OpenIddict password/refresh grant, `client_id=food-market-web`, Bearer-токен. Все
ответы и ошибки — JSON; бизнес-ошибки отдают `{ error, field?, code? }`.
## 1. Остатки по группе товаров (с вложенными)
`GET /api/inventory/stock?groupId={guid}&storeId=&search=&includeZero=&page=&pageSize=&sort=&order=`
- `groupId` — фильтр по группе товаров **включая все вложенные подгруппы**
(по денормализованному `Path`). Комбинируется с `storeId`, `search`, сортировкой.
- Ответ: `PagedResult<StockRow>` — `{ items:[{ productId, productName, article, unitSymbol,
storeId, storeName, quantity, reservedQuantity, available }], total, page, pageSize }`.
## 2. Добавить штрихкод к товару
`POST /api/catalog/products/{id}/barcodes` — право `ProductsEdit`.
Тело: `{ "code": "4870001112223", "kind": "ean13" }`.
`kind` ∈ `ean13|ean8|code128|code39|upca|upce|other` (регистр не важен); если не задан —
тип определяется по длине/цифрам кода.
Ответы:
- `201 Created` → `{ id, productId, code, type, isPrimary }` (первый штрихкод товара
становится primary).
- `200 OK` — тот же код уже у **этого** товара (идемпотентно), тело как у 201.
- `409 Conflict` → `{ error:"Штрихкод уже у товара «…».", code:"barcode-duplicate",
conflictProduct:"…", field:"code" }` — код занят **другим** товаром организации.
- `400` — пустой/слишком длинный код; `404` — товар не найден.
Уникальность проверяется в пределах организации (`(OrganizationId, Code)`).
## 3. Права текущего пользователя
`GET /api/me` теперь возвращает `permissions` — эффективные права (имя флага →
bool) **с учётом роли и простого/расширенного режима**. Мобильное приложение и веб
гейтят UI по одному источнику (скрывают одно и то же).
```json
{
"sub":"…","name":"…","email":"…","roles":["Admin"],"orgId":"…",
"hasLiveOrg":true,"hasActiveEmployee":true,"advancedMode":false,
"permissions":{ "ProductsView":true,"ProductsEdit":true,"StocksView":true,
"DemandsView":false,"TransferEdit":false, … }
}
```
Правила: SuperAdmin — все права без ограничений; Admin — все, но в **простом режиме**
advanced-only скрыты; обычная роль — её флаги (в простом режиме advanced-only тоже
обнуляются). Advanced-only: `DemandsView/Edit/Post`, `TransferEdit`, `LoyaltyManage`,
`PromotionsManage`, `PriceTypesManage` (зеркалит серверный `[RequiresAdvancedMode]`).
Имена флагов — из `RolePermissions`. Веб использует тот же источник (`useCan()`).
## 4. Весовой признак в офлайн-каталоге
`GET /api/pos/v1/sync` (каталог для сканирования/офлайна) в `ProductSyncDto` отдаёт:
- `packaging` (1=штучный, 2=весовой, 3=разливной) и **`isWeighed`** (явный bool `packaging==2`),
- `unitCode` — единица измерения (шт/кг/л…).
## 5. Весовые этикетки (внутримагазинные EAN-13)
Как в МойСкладе: внутримагазинные EAN-13 с префиксом из диапазона 20–29, где зашиты
«код товара» (PLU, N цифр) и величина (вес в граммах → кг, либо цена).
**Настройка организации** — `GET/PUT /api/organization/settings` (PUT — право Admin):
- `weightBarcodeEnabled` (bool, по умолчанию true),
- `weightBarcodeWeightPrefixes` (CSV префиксов веса, null → `20,21,22,23,24`),
- `weightBarcodePricePrefixes` (CSV префиксов цены, null → `25,26,27,28,29`),
- `weightBarcodeCodeDigits` (длина PLU, по умолчанию 5).
Блок применяется, только если в PUT передан `weightBarcodeEnabled` (иначе настройки не трогаются).
**Разбор при поиске** — `GET /api/catalog/products/by-barcode/{value}`:
- Обычный штрихкод → товар (или `{ items:[…] }` при нескольких совпадениях), `404` если нет.
- **Весовая этикетка** (префикс весовой/ценовой) → `{ product:{…}, weighted:true,
valueKind:"WeightKg"|"Price", quantity:1.5, embeddedPrice:null }`. Товар ищется по
встроенному PLU (как короткий штрихкод товара либо как артикул); `quantity` — вес (кг),
`embeddedPrice` — зашитая цена (для ценовых этикеток).
Пример: этикетка `2112345015007` при дефолтных настройках → PLU `12345`, вес `1.500` кг.
Разбор единый для сервера и кассы — `foodmarket.Shared.Barcodes.WeightedBarcode`.