Метод GET /api/v1/fridge/virtual-shortages

Документация API: Извлечение реестра отрицательных балансов (виртуальных минусов) для превентивного информирования пользователя

Published

July 1, 2026

1 Функциональное назначение

Метод предназначен для обслуживания и агрегации технических отрицательных состояний склада, которые возникли в семейном контуре (HOME) на этапах фиксации приготовленных блюд (Методы 1 и 4). Поскольку система разрешает пользователям уводить баланс продуктов в минус, чтобы не блокировать бытовые процессы, нам необходим специализированный эндпоинт для контроля этих задолженностей.

Метод решает следующие задачи:

  1. Аудит задолженностей: Сканирует таблицу инвентаря холодильника по home_group_id и вытягивает строки, у которых фактическое количество строго меньше нуля (quantity < 0).
  2. Формирование контекстных алертов: Готовит упорядоченный список продуктов, которые физически были съедены или потрачены, но технически еще не были внесены в систему (например, пользователь забыл отсканировать чек при покупке яблок).
  3. Триггер для UI: На основе ответа этого метода мобильное приложение Flutter выводит на главном экране предупреждающий информер: «Вы приготовили пирог, но мы виртуально списали 0.5 кг яблок. Пожалуйста, отсканируйте чек или подтвердите остатки».

2 Протокол взаимодействия (HTTP Контракт)

  • Метод: GET
  • Маршрут: /api/v1/fridge/virtual-shortages
  • Формат данных: application/json (только ответ)

2.1 Спецификация заголовков (HTTP Headers)

Заголовок Обязательный Описание Пример значения
Authorization Да Токен авторизации (Access Token). Бэкенд извлекает из него home_group_id и проверяет контекст HOME. Bearer eyJhbGciOiJIUzI1Ni...
X-Request-ID Да Сквозной ID запроса для связывания логов мониторинга долгов req-shortage-55dd

2.2 Спецификация структуры ответа (Response Body)

Метод возвращает массив объектов задолженностей. Значение deficit_quantity приводится к абсолютному положительному числу для удобства отображения в UI-баннерах на клиенте.

Поле Тип Описание Пример значения
fridge_item_id Integer ID конкретной записи поставки, ушедшей в минус 1042
product_name String Каноническое или пользовательское имя дефицитного продукта "Яблоки"
deficit_quantity Float Абсолютный объем задолженности, требующий покрытия 0.5000 (из исходного значения -0.5000)
unit String Единица измерения продукта "кг"

2.2.1 Пример JSON-ответа (Payload):

{
  "home_group_id": "group-772-alpha",
  "has_shortages": true,
  "total_shortages_count": 2,
  "shortages": [
    {
      "fridge_item_id": 1042,
      "product_name": "Яблоки",
      "deficit_quantity": 0.5,
      "unit": "кг",
      "created_at": "2026-07-01T21:50:00Z"
    },
    {
      "fridge_item_id": 1089,
      "product_name": "Мука",
      "deficit_quantity": 0.25,
      "unit": "кг",
      "created_at": "2026-07-01T21:55:00Z"
    }
  ]
}

3 Схема обработки запроса пользователя (Mermaid)

На диаграмме представлена логика сканирования балансов СУБД. Бэкенд изолирует отрицательные остатки конкретной семьи и передает их во Flutter для вывода интерфейсных предупреждений.

sequenceDiagram
    autonumber
    actor User as Пользователь (Главный экран)
    participant APP as Мобильное приложение (Flutter)
    participant API as Бэкенд-шлюз (FastAPI)
    participant DB as Реляционная СУБД (PostgreSQL)

    User->>APP: Открывает приложение / обновляет склад
    APP->>API: HTTP GET /api/v1/fridge/virtual-shortages (JWT в заголовке)
    activate API
    
    note over API: Шаг 3: Проверка account_type (Если OFFICE -> мгновенный пустой ответ)
    
    API->>DB: SELECT FROM fridge_inventory WHERE home_group_id = \$1 AND quantity < 0
    activate DB
    DB-->>API: Набор строк с отрицательным балансом (quantity, unit, product_name)
    deactivate DB
    
    note over API: Шаг 6: Конвертация отрицательных весов в абсолютные значения float
    
    API-->>APP: HTTP 200 OK (JSON с массивом shortages и флагом has_shortages: true)
    deactivate API
    note over APP: На главном экране всплывает информер: "Вы забыли внести 0.5 кг яблок!"

4 Расшифровка шагов

  • Шаг 1 (User -> APP): Пользователь запускает мобильное приложение или переходит на главный экран управления запасами. В этот момент Flutter-приложение инициирует фоновый опрос для проверки наличия задолженностей по продуктам.
  • Шаг 2 (APP -> API): Приложение отправляет запрос HTTP GET /api/v1/fridge/virtual-shortages. В заголовках передается стандартный токен авторизации и X-Request-ID. Пользователь не видит лоадеров, запрос выполняется незаметно для интерфейса.
  • Шаг 3 (API -> API): Бэкенд FastAPI расшифровывает JWT-токен и проверяет клейм account_type. Так как в корпоративном контуре OFFICE уход в минус технически заблокирован на уровне транзакций коммита, для офисных аккаунтов метод прерывает выполнение и мгновенно возвращает структуру has_shortages: false с пустым массивом, не нагружая базу данных лишними операциями.
  • Шаг 4 (API -> DB): Если тип аккаунта — HOME (семья), бэкенд обращается к СУБД PostgreSQL с целевым запросом фильтрации по индексу группы: SELECT item_id, product_name, quantity, unit, created_at FROM fridge_inventory WHERE home_group_id = $1 AND quantity < 0 ORDER BY created_at DESC.
  • Шаг 5 (DB -> API): База данных сканирует таблицу инвентаря и возвращает набор строк, баланс которых ушел в минус в процессе ручной или рецептурной готовки.
  • Шаг 6 (API -> API): Бэкенд принимает данные и выполняет математическое преобразование. Чтобы не путать пользователя знаками «минус» в интерфейсе, бэкенд пропускает значение quantity через функцию abs(), превращая, например, -0.5000 в чистый float-объем дефицита 0.5.
  • Шаг 7 (API -> APP): FastAPI рассчитывает общее количество дефицитных позиций, выставляет маркер has_shortages: true и возвращает статус HTTP 200 OK. Мобильное приложение Flutter принимает JSON-пакет. Если задолженности найдены, стейт-менеджер (Bloc/Provider) активирует на главном экране желтый информационный информер-алерт с перечнем продуктов, которые семья съела виртуально, призывая закрыть долг путем сканирования чека новой покупки.

5 Спецификация ответов сервера (Response Body) и ошибок

5.1 1. Контекстные ответы сервера (Success Responses)

Метод всегда возвращает статус 200 OK для авторизованных сессий, но структура данных меняется в зависимости от наличия задолженностей или типа аккаунта.

5.1.1 Пример JSON-ответа при отсутствии задолженностей (Идеальное состояние)

Возвращается, если все остатки в холодильнике строго >= 0, либо если запрос выполнен под учетной записью OFFICE. Флаг has_shortages: false сигнализирует Flutter, что выводить алерты и информеры на экран не нужно.

{
  "home_group_id": "group-772-alpha",
  "has_shortages": false,
  "total_shortages_count": 0,
  "shortages": []
}

5.2 2. Спецификация серверной логики фильтрации (SQL)

Запрос производит выборку строк инвентаря с отрицательным объемом для конкретного семейного контура.

SELECT 
    item_id AS fridge_item_id,
    product_name,
    -- Конвертируем отрицательное значение в абсолютное число на уровне СУБД
    ABS(quantity)::FLOAT AS deficit_quantity,
    unit,
    created_at
FROM fridge_inventory
WHERE home_group_id = \$1 
  AND quantity < 0 -- Критерий отбора: только технические минусы
ORDER BY created_at DESC;

5.3 3. Вилки исключений и обработка ошибок

5.3.1 Ошибка: Нарушение прав доступа к группе (HTTP 403 Forbidden)

Выбрасывается на Шаге 3, если home_group_id, извлеченный из токена, заблокирован, деактивирован или не совпадает с запрашиваемым контекстом безопасности базы данных.

{
  "error_code": "ERR-GROUP-ACCESS-DENIED",
  "message": "Доступ к инвентарю данной группы заблокирован или отсутствует в матрице прав.",
  "details": {
    "reason": "JWT home_group_id token claim signature mismatch or group is suspended."
  }
}

5.3.2 Ошибка: Сбой парсинга сессии авторизации (HTTP 401 Unauthorized)

Выбрасывается на Шаге 3 бэкенд-шлюзом FastAPI, если токен в заголовке Authorization поврежден, некорректно зашифрован или отсутствует. Это активирует на стороне Flutter ранее описанный механизм Dio Interceptor для обновления токенов.

{
  "error_code": "ERR-INVALID-AUTH-TOKEN",
  "message": "Неверный или просроченный токен доступа. Доступ к реестру задолженностей отклонен.",
  "details": {
    "action": "Trigger queued token refresh process using refresh_token."
  }
}