# 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` — если токен отсутствует).

**Запрос:**

```json
{
  "username": "логин",
  "password": "пароль"
}
```

**Ответ `200`:**

```json
{
  "token": "jwt..."
}
```

**Ошибка `401`:**

```json
{
  "error": "описание ошибки"
}
```

---

### 2. Сводный прайс


|           |                                          |
| --------- | ---------------------------------------- |
| **Метод** | `GET`                                    |
| **Путь**  | `/api/supplier-prices/summary`           |
| **Query** | `supplier_id` (опц.), `region_id` (опц.) |


**Где в приложении:** меню **Обмен → Загрузить**, кнопка обновления прайса в главном окне (`HF_DownloadDataFromServer`).

**Примеры:**

```http
GET /api/supplier-prices/summary
GET /api/supplier-prices/summary?region_id={RegionId}
GET /api/supplier-prices/summary?supplier_id={uuid}
```

**Ответ `200`:**

```json
{
  "markup_percent": 15.5,
  "summary": [
    {
      "supplier_price_id": "uuid",
      "guid_es": "uuid",
      "supplier_id": "uuid",
      "supplier_name": "Катрен",
      "drug_name": "Название препарата",
      "inn": "МНН",
      "cure_form": "таб.",
      "barcode": "...",
      "price": 123.45,
      "markup_percent": 15.5,
      "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):

```json
{
  "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`:** массив объектов

```json
[
  {
    "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

**Логин:**

```bash
curl -X POST "{ApiBaseUrl}/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"user","password":"pass"}'
```

**Прайс:**

```bash
curl "{ApiBaseUrl}/api/supplier-prices/summary" \
  -H "Authorization: Bearer {token}"
```

**Отправка заказа:**

```bash
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}]}'
```

**Накладные:**

```bash
curl "{ApiBaseUrl}/api/buyer/invoices" \
  -H "Authorization: Bearer {token}"
```

