Document all client HTTP calls in docs/API.md, link from README, and delete dead GetPriceItem code. Co-authored-by: Cursor <cursoragent@cursor.com>
8.8 KiB
API — Электронная Фармация
Документация по HTTP-запросам, которые использует десктоп-клиент.
Реализация: src/ElectronicPharmacy/Classes/ApiClient.cs, DataSender.cs, InvoiceSyncService.cs.
Базовый URL
{ApiBaseUrl}
| Источник | Приоритет |
|---|---|
Настройки пользователя (Settings.ApiBaseUrl, окно Авторизация) |
1 |
Переменная окружения ELF_API_BASE_URL |
2 |
| Значение по умолчанию | http://195.34.241.84:9988 |
Таймаут: 30 секунд (ApiClient). Для отправки заказов (DataSender) таймаут не задан явно.
Формат: JSON, кодировка UTF-8.
Авторизация
Все запросы, кроме логина, требуют заголовок:
Authorization: Bearer {JWT}
Токен сохраняется в Settings.stringToken после успешного входа.
Эндпоинты
1. Вход
| Метод | POST |
| Путь | /auth/login |
| Авторизация | не требуется |
Где в приложении: окно Авторизация (HF_Registration), загрузка прайса (HF_DownloadDataFromServer — если токен отсутствует).
Запрос:
{
"username": "логин",
"password": "пароль"
}
Ответ 200:
{
"token": "jwt..."
}
Ошибка 401:
{
"error": "описание ошибки"
}
2. Сводный прайс
| Метод | GET |
| Путь | /api/supplier-prices/summary |
| Query | supplier_id (опц.), region_id (опц.) |
Где в приложении: меню Обмен → Загрузить, кнопка обновления прайса в главном окне (HF_DownloadDataFromServer).
Примеры:
GET /api/supplier-prices/summary
GET /api/supplier-prices/summary?region_id={RegionId}
GET /api/supplier-prices/summary?supplier_id={uuid}
Ответ 200:
{
"summary": [
{
"supplier_price_id": "uuid",
"guid_es": "uuid",
"supplier_id": "uuid",
"supplier_name": "Катрен",
"drug_name": "Название препарата",
"inn": "МНН",
"cure_form": "таб.",
"barcode": "...",
"price": 123.45,
"quantity": 10,
"region_id": "...",
"region_name": "...",
"last_price_date": "2026-07-14T00:00:00Z",
"match_method": "BARCODE",
"match_confidence": 0.95,
"trade_name": "...",
"dosage": "...",
"registry_price": 100.00,
"instruction_guid": "...",
"description": "...",
"storing_condition": "...",
"expiry_period": "12.2027",
"producer_name": "...",
"registry_date": "...",
"es_code": 123456,
"registry_status": "..."
}
]
}
После ответа: данные записываются в локальную SQLite-таблицу PriceList (полная перезапись).
Класс: ApiClient.GetPriceSummaryAsync
3. Отправка заказа
| Метод | POST |
| Путь | /api/buyer/orders |
| Content-Type | application/json |
Где в приложении:
- меню Обмен → Отправить — все заказы со статусом
НОВЫЙ(DataSender.Main); - страница Заказы, кнопка ОТПРАВИТЬ — один выбранный заказ (
DataSender.SendSingleOrderAsync).
Запрос (один заказ = один POST):
{
"location_id": "uuid точки из Settings.LocationId",
"comment": "комментарий или null",
"items": [
{
"supplier_price_id": "uuid позиции из прайса",
"qty": 5
}
]
}
Условия отправки:
- в локальной БД заказ имеет статус
НОВЫЙ; - у каждой позиции заполнен
supplier_price_id; qty> 0;- задан
location_idв настройках.
При успехе: локально Orders.OrderState → ОТПРАВЛЕН (только для отправленного заказа).
При ошибке: toast с HTTP-кодом и телом ответа сервера.
Класс: DataSender
4. Накладные и отказы
| Метод | GET |
| Путь | /api/buyer/invoices |
Где в приложении: кнопка Обновить с сервера на вкладках Накладные и Отказы (InvoiceSyncService).
Ответ 200: массив объектов
[
{
"InvoiceNumber": "12345",
"InvoiceDate": "2026-07-14",
"SupplierName": "Поставщик",
"ConsigneeName": "Грузополучатель",
"InvoiceSum": "10000.00",
"RefuseSum": "500.00"
}
]
После ответа: INSERT OR REPLACE в локальную таблицу Invoice.
Класс: ApiClient.GetInvoicesAsync
Сводная таблица
| Действие в UI | HTTP | Эндпоинт | Класс / метод |
|---|---|---|---|
| Авторизация | POST |
/auth/login |
ApiClient.LoginAsync |
| Загрузить прайс | GET |
/api/supplier-prices/summary |
ApiClient.GetPriceSummaryAsync |
| Отправить заказ(ы) | POST |
/api/buyer/orders |
DataSender.SendOrderAsync |
| Обновить накладные / отказы | GET |
/api/buyer/invoices |
ApiClient.GetInvoicesAsync |
Обработка ошибок
| Ситуация | Поведение клиента |
|---|---|
401 Unauthorized |
Сообщение об ошибке авторизации; для прайса токен сбрасывается в ApiClient |
| Таймаут (30 с) | HttpRequestException с текстом о превышении времени ожидания |
| Прочие HTTP-ошибки | Тело { "error": "..." } если сервер вернул JSON |
Нет location_id |
Отправка заказов блокируется до заполнения в настройках |
| Нет токена | Загрузка накладных и отправка заказов блокируются |
Что не реализовано
PUT,PATCH,DELETEк API- Получение статуса заказа с сервера
- Синхронизация справочников (поставщики, грузополучатели) через API
- Автоматический re-login при истечении токена
Примеры curl
Логин:
curl -X POST "{ApiBaseUrl}/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"pass"}'
Прайс:
curl "{ApiBaseUrl}/api/supplier-prices/summary" \
-H "Authorization: Bearer {token}"
Отправка заказа:
curl -X POST "{ApiBaseUrl}/api/buyer/orders" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"location_id":"...","comment":null,"items":[{"supplier_price_id":"...","qty":1}]}'
Накладные:
curl "{ApiBaseUrl}/api/buyer/invoices" \
-H "Authorization: Bearer {token}"