{"openapi":"3.1.0","info":{"title":"ProTarget — мобильный API торговых точек — API v1","description":"Legacy / backward-compatible API.\n\nv1 используется для обратной совместимости. Новые фичи и изменения добавляем в v2.","contact":{"name":"Protarget Team"},"version":"1.0"},"servers":[{"url":"/api/v1"}],"paths":{"/users/me":{"get":{"tags":["Users"],"summary":"Получить профиль текущего пользователя","description":"Возвращает профиль текущего пользователя на основе **access-токена**.\n\nПомимо базовых полей пользователя, возвращает список торговых точек (ТТ), к которым пользователь привязан (`outlets`). Для каждой ТТ отдается:\n- название (`name`)\n- код ProTarget (`code`, это `tbl_outlets.code`)\n- адресные поля (`street/office/entrance/floor`)\n- флаг владельца (`is_owner`)\n- владелец ТТ (`owner`), если найден по `m2m_user_outlets.is_owner=true`\n\n### Аутентификация\nТребуется заголовок `Authorization: Bearer <access_token>`.\n\nВажно:\n- Принимается **только access token**.\n- Handshake/refresh токены не должны использоваться как Bearer для бизнес-API.\n### Локализация\nЯзык ошибок определяется через заголовок `Accept-Language`.\nПоддерживаются: `ru`, `en`, `kk`.\n### Примечания по данным ТТ\n- Если ТТ у пользователя нет: `outlets=[]`.\n- В этом эндпоинте **не** фильтруем ТТ по `location_id`, чтобы не терять реальные точки.","operationId":"users_get_current","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Профиль пользователя + список торговых точек.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeResponseSchema"}}}},"401":{"description":"Ошибка аутентификации/токена.","content":{"application/json":{"examples":{"AUTH_REQUIRED":{"summary":"Нет Authorization","value":{"detail":{"code":100,"message":"Authorization header is required","key":"errors.auth.required","meta":{}}}},"INVALID_TOKEN":{"summary":"JWT недействителен","value":{"detail":{"code":100,"message":"Invalid access token","key":"errors.auth.invalid_token","meta":{}}}}},"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}},"headers":{"WWW-Authenticate":{"description":"Bearer realm=... (обязателен для 401)","schema":{"type":"string"}}}},"404":{"description":"Бизнес-профиль пользователя не найден или неактивен.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Users"],"summary":"Обновить профиль текущего пользователя","description":"Частично обновляет профиль текущего пользователя (только не чувствительные поля).\n\n### Аутентификация\nТребуется заголовок `Authorization: Bearer <access_token>`.\n\nВажно:\n- Принимается **только access token**.\n- Handshake/refresh токены не должны использоваться как Bearer для бизнес-API.\n### Локализация\nЯзык ошибок определяется через заголовок `Accept-Language`.\nПоддерживаются: `ru`, `en`, `kk`.\n### Что обновляется\n- Только поля, переданные в теле запроса.\n- Пароль и чувствительные поля не меняются здесь.\n\n### Поведение при пустом теле\n- Если запрос не содержит полей для обновления — возвращаем текущий профиль (без ошибки).\n\n### Пример запроса\n```bash\ncurl -X PATCH 'https://host/api/v1/users/me' \\\n  -H 'Authorization: Bearer <access_token>' \\\n  -H 'Accept-Language: ru' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"first_name\":\"Иван\",\"iin\":\"123456789012\"}'\n```\n\n### Пример ответа (200)\n```json\n{\n  \"uid\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n  \"first_name\": \"Иван\",\n  \"iin\": \"123456789012\",\n  \"is_active\": true,\n  \"outlets\": []\n}\n```\n\n### Возможные ошибки\n- Все ошибки из `GET /users/me` (аутентификация).\n- **500** `INTERNAL_ERROR` — сервис не смог обновить данные (или репозиторий вернул None).\n- **422** — ошибки валидации схемы `UserUpdateRequest`.\n\nФормат ошибок — через ваш `ErrorSpec` (локализованное `message`, стабильный `key`).","operationId":"users_update_current","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateRequestSchema","description":"Поля профиля для обновления (частично)."},"examples":{"first_last_name":{"summary":"Обновить имя/фамилию","value":{"first_name":"Иван","last_name":"Иванов"}},"iin_only":{"summary":"Обновить ИИН","value":{"iin":"990101300123"}},"phone_only":{"summary":"Обновить телефон (digits)","value":{"phone":"77015557788"}}}}}},"responses":{"200":{"description":"Актуальный профиль пользователя (после обновления или без изменений).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseSchema"}}}},"401":{"description":"Ошибка аутентификации/токена.","headers":{"WWW-Authenticate":{"description":"Bearer realm=... (обязателен для 401)","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Бизнес-профиль пользователя не найден или неактивен.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации тела запроса (Pydantic)."},"500":{"description":"Внутренняя ошибка при обновлении профиля.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/brands":{"get":{"tags":["Brands"],"summary":"Получить список брендов","description":"Возвращает список брендов.\n\nЕсли передан `organization_id`, то бренды выбираются не напрямую из таблицы Brand, а через цепочку:\nOrganization(включая всех потомков) -> ProductReference -> Product -> Brand.\n\nПримеры:\n- Все бренды: `GET /brands`\n- Активные: `GET /brands?is_active=true`\n- Поиск: `GET /brands?q=cola`\n- В контексте организации: `GET /brands?organization_id=<uuid>`","operationId":"list_brands_brands_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string","format":"uuid4"}},{"type":"null"}],"description":"Фильтр по списку UID брендов. Можно передать несколько раз: ?id=a&id=b","examples":[["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"]],"title":"Id"},"description":"Фильтр по списку UID брендов. Можно передать несколько раз: ?id=a&id=b"},{"name":"outlet_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Контекст торговой точки. Если задан — бренды выбираются по доступным портфелям и складам точки (остатки > 0).","examples":["f13a2f11-2a1b-4a54-9a52-1ab7e7d2b8d0"],"title":"Outlet Id"},"description":"Контекст торговой точки. Если задан — бренды выбираются по доступным портфелям и складам точки (остатки > 0)."},{"name":"organization_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Контекст организации. Если задан — бренды выбираются через цепочку Organization -> (descendants) -> ProductReference -> Product -> Brand.","examples":["6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"],"title":"Organization Id"},"description":"Контекст организации. Если задан — бренды выбираются через цепочку Organization -> (descendants) -> ProductReference -> Product -> Brand."},{"name":"category_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по категории товара. Если задан — учитываются товары этой категории и всех её дочерних категорий.","examples":["c2b3b9a0-8a77-4f2e-b4c2-7f5b8d8d3a12"],"title":"Category Id"},"description":"Фильтр по категории товара. Если задан — учитываются товары этой категории и всех её дочерних категорий."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по активности бренда.","examples":[true,false],"title":"Is Active"},"description":"Фильтр по активности бренда."},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}],"description":"Поиск по имени бренда (ILIKE %query%).","examples":["cola","pepsi"],"title":"Query"},"description":"Поиск по имени бренда (ILIKE %query%)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Список брендов","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandListResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Связанный ресурс не найден.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/categories":{"get":{"tags":["Categories"],"summary":"Получить список категорий для торговой точки","description":"Возвращает список категорий, доступных для указанной торговой точки (outlet), с возможностью фильтрации по родительской категории и подстроке в названии.\n\n**Основные правила работы фильтрации:**\n- Всегда требуется параметр `outlet_id` — категории строятся только для тех портфелей и складов, которые привязаны к выбранной точке.\n- Если параметр `category_id` **не указан**:\n    * возвращаются **родительские категории** (верхний уровень), у которых       есть дочерние категории с товарами, доступными в данной точке.\n- Если параметр `category_id` **указан**:\n    * возвращаются **дочерние категории** указанной родительской категории.\n- Параметр `search` позволяет ограничить результат категориями, название которых содержит указанную подстроку (поиск регистронезависимый).\n\n**Проверка доступа:**\n- Перед выборкой категорий сервис проверяет, что указанный `outlet_id` доступен текущему пользователю. Если точка не найдена или не принадлежит пользователю, возвращается HTTP-ошибка согласно коду `OUTLET_NOT_FOUND`.","operationId":"list_categories_categories_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"Идентификатор торговой точки (outlet), для которой необходимо получить список категорий.\n\nКатегории будут построены на основе портфелей, складов и товаров, связанных с данной точкой.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Outlet Id"},"description":"Идентификатор торговой точки (outlet), для которой необходимо получить список категорий.\n\nКатегории будут построены на основе портфелей, складов и товаров, связанных с данной точкой."},{"name":"category_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по родительской категории.\n\n- Если `category_id` указан — будут возвращены **дочерние категории** этой категории.\n- Если параметр не указан — будут возвращены **родительские категории**, которые имеют дочерние категории с доступными товарами.","examples":["1a2b3c4d-1111-2222-3333-444455556666",null],"title":"Category Id"},"description":"Фильтр по родительской категории.\n\n- Если `category_id` указан — будут возвращены **дочерние категории** этой категории.\n- Если параметр не указан — будут возвращены **родительские категории**, которые имеют дочерние категории с доступными товарами."},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Подстрока для поиска по названию категории. Поиск выполняется регистронезависимо (`ILIKE`). Если не указано — поиск по названию не применяется.","examples":["молоко","снеки",null],"title":"Query"},"description":"Подстрока для поиска по названию категории. Поиск выполняется регистронезависимо (`ILIKE`). Если не указано — поиск по названию не применяется."},{"name":"visibility","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CategoryVisibilityEnum"}},{"type":"null"}],"description":"Фильтр по видимости категории. Передаётся как list (повторяющиеся query-параметры). Пример: visibility=agent&visibility=outlet.\n\nВАЖНО: если фильтр задан и в нём нет 'all', то 'all' добавляется автоматически (чтобы не терять общие категории).","examples":[["all"],["outlet"],["all","outlet"],null],"title":"Visibility"},"description":"Фильтр по видимости категории. Передаётся как list (повторяющиеся query-параметры). Пример: visibility=agent&visibility=outlet.\n\nВАЖНО: если фильтр задан и в нём нет 'all', то 'all' добавляется автоматически (чтобы не терять общие категории)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Список категорий, отфильтрованный по указанным параметрам.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CategoryResponseSchema"},"title":"Response 200 List Categories Categories Get"}}}},"401":{"description":"Пользователь не авторизован или неактивен.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Торговая точка не найдена или недоступна пользователю (ошибка `OUTLET_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера при получении списка категорий.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products":{"get":{"tags":["Products"],"summary":"Получить список товаров","description":"Возвращает список товаров, доступных пользователю в рамках его торговой точки,\n        с учётом фильтров, пагинации, сортировок и промо-данных.\n    \n        ### Что делает этот метод\n        - проверяет пользователя (авторизация)\n        - определяет торговую точку и доступы\n        - применяет фильтры витрины (категории/бренды/поиск/портфель/промо/остатки/цены)\n        - применяет сортировку и пагинацию\n        - возвращает товары с остатками, ценами, промо, справочными данными и портфелями\n    \n        ### Параметры запроса (ProductListRequestSchema)\n        - `page_number`: номер страницы\n        - `page_size`: размер страницы\n        - `outlet_id`: ID точки (обязательный)\n        - `organization_id`: фильтр по организации (включая дочерние 1 уровня)\n        - `category_id`: фильтр по категории (включая дочерние 1 уровня)\n        - `brand_id`: фильтр по нескольким брендам (как `?brand_id=...&brand_id=...`)\n        - `portfolio_id`: фильтр по портфелю\n        - `query`: строка поиска\n        - `discount_only`: только товары со скидкой (витринные скидки)\n        - `bonus_only`: только товары с бонусным продуктом\n        - `promo_type`: тип витринного промо (`overhead` | `catalog`)\n        - `sort_by`: сортировка (`name`, `-price`, `price` и т.п.)\n    \n        ### ВАЖНО ПРО СКИДКИ / АКЦИИ\n        Скидки/акции в системе бывают разных типов, и **получаются разными эндпойнтами**:\n    \n        1) **Витринные промо** (для списка товаров): `overhead`, `catalog`\n           - фильтруются здесь через `promo_type=overhead|catalog`\n           - и/или через `discount_only=true`\n    \n        2) **Механики промо** (для корзины/ассортимента/МСЛ/брендов):\n           - `cart`, `assortment`, `msl`, `discount_sku_percentage`, `by_brand`\n           - их нужно получать через эндпойнт **/promotions**.\n\n        ### Проверка бюджета акции\n        - Если бюджет промо исчерпан, промо-модификатор к товару не применяется.\n        - Товар остаётся в каталоге по обычной цене (продажа товара не блокируется).\n        - После пополнения бюджета промо автоматически возвращается в карточку товара\n          без обновления приложения.\n    \n        ### Ответ\n        ProductListResponseSchema содержит:\n        - `total_pages`: общее количество страниц\n        - `total_items`: общее количество товаров\n        - `results`: список товаров (ProductResponseSchema)","operationId":"list_products_products_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page_number","in":"query","required":false,"schema":{"type":"integer","exclusiveMinimum":0,"description":"Номер страницы (начинается с 1)","examples":[1,2,5],"default":1,"title":"Page Number"},"description":"Номер страницы (начинается с 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"exclusiveMinimum":0,"description":"Количество элементов на одной странице (1–500)","examples":[20,50,100],"default":20,"title":"Page Size"},"description":"Количество элементов на одной странице (1–500)"},{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"Идентификатор торговой точки","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Outlet Id"},"description":"Идентификатор торговой точки"},{"name":"organization_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по организации (учитываются также дочерние организации)","examples":["3d2e1c2b-1e6c-4c68-9f5b-8b5c0a4f1111"],"title":"Organization Id"},"description":"Фильтр по организации (учитываются также дочерние организации)"},{"name":"category_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по категории товаров","examples":["1adbb83e-9f5f-4f07-b53e-f30fb0fc4a62"],"title":"Category Id"},"description":"Фильтр по категории товаров"},{"name":"brand_id","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string","format":"uuid4"}},{"type":"null"}],"description":"Фильтр по нескольким брендам. Передавай как повторяющийся query-параметр: `?brand_id=...&brand_id=...`","examples":[["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"]],"title":"Brand Id"},"description":"Фильтр по нескольким брендам. Передавай как повторяющийся query-параметр: `?brand_id=...&brand_id=...`"},{"name":"portfolio_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по портфелю (вернуть только товары, входящие в указанный портфель)","examples":["11111111-2222-3333-4444-555555555555"],"title":"Portfolio Id"},"description":"Фильтр по портфелю (вернуть только товары, входящие в указанный портфель)"},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Поисковая строка по имени товара","examples":["молоко","курица","сыр"],"title":"Query"},"description":"Поисковая строка по имени товара"},{"name":"discount_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр товаров со скидкой (true/false)","examples":[true],"title":"Discount Only"},"description":"Фильтр товаров со скидкой (true/false)"},{"name":"bonus_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр товаров с бонусом (reward_type=bonus_product)","examples":[true],"title":"Bonus Only"},"description":"Фильтр товаров с бонусом (reward_type=bonus_product)"},{"name":"promo_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/PromotionTypeProductFilterEnum"},{"type":"null"}],"description":"Тип промо: overhead, cart или catalog","examples":["cart"],"title":"Promo Type"},"description":"Тип промо: overhead, cart или catalog"},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"enum":["price","-price","name","-name"],"type":"string"},{"type":"null"}],"description":"Поле сортировки: 'name', '-name', 'price', '-price'. Минус перед полем — сортировка по убыванию.","examples":["name"],"title":"Sort By"},"description":"Поле сортировки: 'name', '-name', 'price', '-price'. Минус перед полем — сортировка по убыванию."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Успешный ответ со списком товаров.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductListResponseSchema"},"example":{"total_pages":3,"total_items":52,"results":[{"id":"6c3c1d04-9862-4e09-bf0d-6ad8a9fbef24","name":"Coca-Cola 1L","price":12000.0}]}}}},"400":{"description":"Некорректные параметры фильтрации/сортировки.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"401":{"description":"Неавторизованный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Торговая точка не найдена или недоступна.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации query-параметров."}}}},"/products/{product_id}":{"get":{"tags":["Products"],"summary":"Получить подробную информацию о товаре","description":"Позволяет получить полную информацию о товаре:\n\n        - основная информация (название, описание, изображения)\n        - справочные данные (`ProductReference`)\n        - остатки по складам\n        - цена, фиксированная цена, цена со скидкой\n        - промо-акции, применимые к этому товару\n        - информация по портфелям\n        - активные SKU-планы продаж (`sales_plans`)\n\n        ### Как считается `sales_plans.current_quantity/current_amount`\n        В факте учитываются только заказы в статусе `delivered`.\n        Если по позиции есть возврат в финальном статусе `returned`,\n        возвращённое количество вычитается из факта плана продаж.\n\n        ### Когда возвращается ошибка\n        - Если пользователь не авторизован → **401**\n        - Если у пользователя нет торговой точки → **404**\n        - Если товар недоступен или не найден → **404**","operationId":"get_product_products__product_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID товара, который нужно получить.","title":"Product Id"},"description":"UUID товара, который нужно получить.","example":"6c3c1d04-9862-4e09-bf0d-6ad8a9fbef24"},{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","title":"Outlet Id"}},{"name":"organization_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","title":"Organization Id"}},{"name":"portfolio_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","title":"Portfolio Id"}},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Детальная карточка товара с ценами, промо и активными SKU-планами продаж.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductResponseSchema"}}}},"401":{"description":"Неавторизованный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Товар или торговая точка не найдены","content":{"application/json":{"example":{"detail":"Product not found or not accessible"},"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации path/query-параметров."}}}},"/promotions":{"get":{"tags":["Promotions"],"summary":"Список промоакций для торговой точки","description":"Возвращает активные промоакции выбранного типа для торговой точки пользователя.\n\nОсобенности:\n- без пагинации\n- без search/category/brand фильтров\n- by_brand возвращает заполненный блок `brands`\n- gifts возвращаются в формате ProductGiftResponseSchema\n\nБюджетное правило:\n- Акции с исчерпанным бюджетом в ответ не попадают.\n- После пополнения бюджета акция автоматически снова появится в выдаче.\n\nВАЖНО ПРО СКИДКИ:\n- Для витринных промо (overhead/catalog), которые нужны именно в списке товаров, используй /products.\n- Для скидочных механик (cart/assortment/msl/discount_sku_percentage/by_brand) используй этот эндпойнт.","operationId":"list_promotions_promotions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID торговой точки, для которой нужно вернуть промоакции.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Outlet Id"},"description":"UUID торговой точки, для которой нужно вернуть промоакции."},{"name":"promotion_type","in":"query","required":true,"schema":{"$ref":"#/components/schemas/PromotionTypeDiscountPromoEnum","description":"Тип промо для выборки. Это enum, построенный из PromotionTypeEnum (fixed, overhead, catalog исключены).","examples":["cart","assortment","msl","discount_sku_percentage","by_brand"]},"description":"Тип промо для выборки. Это enum, построенный из PromotionTypeEnum (fixed, overhead, catalog исключены)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Список доступных акций.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PromotionListItemSchema"},"title":"Response List Promotions Promotions Get"},"example":[{"id":"2637676e-f700-4f40-b0ad-7f7e438ada5f","name":"Promo Summer","type":"cart","reward_type":"discount"}]}}},"400":{"description":"Некорректные параметры запроса.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"401":{"description":"Неавторизованный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Торговая точка не найдена/недоступна.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации query-параметров."}}}},"/carts":{"post":{"tags":["Carts"],"summary":"Добавить/обновить товар в корзине (0 — удалить)","description":"Добавляет товар в корзину текущего пользователя для выбранной торговой точки.\n\nПоведение по `quantity`:\n- `quantity > 0`: если товара нет — добавляет; если есть — обновляет количество.\n- `quantity = 0`: удаляет товар из корзины (если он там есть).\n\nОперация выполняется **в рамках портфеля** `portfolio_id`.","operationId":"add_product_to_cart_carts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddProductToCartRequestSchema"}}}},"responses":{"201":{"description":"Результат операции изменения корзины","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Не найдена торговая точка, организация, портфель или связанный ресурс.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Carts"],"summary":"[DEPRECATED] Получить содержимое корзины (v1)","description":"❌ **УСТАРЕВШИЙ МЕТОД**\n\nДанный эндпоинт **НЕ РЕКОМЕНДОВАН К ИСПОЛЬЗОВАНИЮ**.\n\nПричины:\n- формат ответа v1 больше не является актуальным;\n- возможны расхождения с текущей бизнес-логикой корзины и промо;\n- поддержка и развитие ведутся только для v2.\n\n➡ Используйте: `GET /v2/carts`","operationId":"get_cart_carts_get","deprecated":true,"security":[{"HTTPBearer":[]}],"parameters":[{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"Идентификатор торговой точки, для которой нужно получить корзину","examples":["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10"],"title":"Outlet Id"},"description":"Идентификатор торговой точки, для которой нужно получить корзину"},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Корзина успешно получена (устаревший формат v1).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartResponseV1Schema"}}}},"401":{"description":"Неавторизованный или неактивный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Не найдена торговая точка или связанные ресурсы корзины.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"410":{"description":"Эндпоинт устарел и больше не поддерживается. Используйте GET /v2/cart.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера при получении корзины.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/carts/products":{"delete":{"tags":["Carts"],"summary":"Удалить товар из корзины в рамках портфеля","description":"Удаляет конкретный товар из корзины текущего пользователя **в рамках указанного портфеля**.\n\n**Обязательно** передать:\n- `parent_organization_id` — UUID родительской организации\n- `outlet_id` — UUID торговой точки\n- `portfolio_id` — UUID портфеля\n- `product_id` — UUID товара","operationId":"remove_product_from_cart_carts_products_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveProductFromCartRequestSchema"}}}},"responses":{"200":{"description":"Результат операции удаления товара из корзины","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Не найдена торговая точка, организация, портфель или связанный ресурс.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/carts/portfolios":{"delete":{"tags":["Carts"],"summary":"Удалить из корзины все товары по портфелю","description":"Удаляет ВСЕ товары из корзины текущего пользователя, относящиеся к указанному portfolio_id, в рамках outlet_id и parent_organization_id.\n\nСемантика DELETE:\n- если корзина не найдена — вернём CART_NOT_FOUND;\n- если по портфелю нет товаров — вернём 200 OK (идемпотентность), deleted=0.","operationId":"clear_cart_by_portfolio_carts_portfolios_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClearCartByPortfolioRequestSchema"}}}},"responses":{"200":{"description":"Результат операции очистки корзины по портфелю","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Не найдена торговая точка, организация, портфель или связанный ресурс.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders":{"post":{"tags":["Orders"],"summary":"[DEPRECATED] Создание заказа","description":"❌ **УСТАРЕВШИЙ МЕТОД**\n\nДанный эндпоинт **НЕ РЕКОМЕНДОВАН К ИСПОЛЬЗОВАНИЮ**.\n\nПричины:\n- обнаружены ошибки в бизнес-логике;\n- некорректная обработка промо и лимитов;\n- возможные расхождения с актуальной моделью заказа.\n\n➡ Используйте: `POST /v2/orders`","operationId":"create_order_orders_post","deprecated":true,"security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequestV1Schema"}}}},"responses":{"201":{"description":"Заказ успешно создан.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"400":{"description":"Ошибка в данных запроса или нарушение бизнес-ограничений (минимальная сумма заказа, лимиты по портфелям, подаркам и т.п.).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"401":{"description":"Неавторизованный или неактивный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Не найдена торговая точка, организация, портфель или связанный ресурс.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"409":{"description":"Конфликт при создании заказа (например, изменения остатков или состояния корзины в момент сохранения).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"410":{"description":"Эндпоинт устарел и больше не поддерживается. Используйте POST /v2/orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера при обработке заказа.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Orders"],"summary":"Получить список заказов текущего пользователя","description":"Возвращает **пагинированный** список заказов для указанной торговой точки.\n\n## Правила выборки\n- Возвращаются заказы, созданные как самой торговой точкой,   так и агентами от её имени.\n- Можно отфильтровать заказы по конкретному типу инициатора (поле `initiator`).\n- Фильтры в запросе являются опциональными: если поле не передано —   соответствующее ограничение не применяется.\n\n## Пагинация\n- Используются параметры `page_number` и `page_size` (см. `PaginationSchema`).\n- Ответ содержит `total_items` и `total_pages` для удобства навигации.\n\n## Фильтры\n**По дате создания заказа (`created_at`)**\n- `created_from` — нижняя граница по дате создания.\n  Если параметр не передан, по умолчанию используется значение   `now() - 7 days` (UTC).\n- `created_to` — верхняя граница по дате создания.\n- Дополнительно действует правило: если `created_from` передан,   сервис использует `max(created_from, now() - 7 days)`.\n\n**По статусам и оплате**\n- `statuses` — фильтр по статусам заказа.\n- `payment_methods` — фильтр по способам оплаты.\n\n**По сумме заказа (`final_price`)**\n- `amount_min` — минимальная сумма.\n- `amount_max` — максимальная сумма.\n\n**По дате доставки (рейс/Trip)**\n- `delivered_from` / `delivered_to` применяются к дате рейса доставки   (`Trip.trip_date`).\n- Эти параметры **не фильтруют заказы по created_at**, а управляют тем,   какие рейсы будут подгружены/попадут в результат как объект `trip`.\n\n## Валидация диапазонов\n- Если задано `delivered_from` и `delivered_to`, то `delivered_to >= delivered_from`.\n- Если задано `created_from` и `created_to`, то `created_to >= created_from`.","operationId":"list_orders_orders_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page_number","in":"query","required":false,"schema":{"type":"integer","exclusiveMinimum":0,"description":"Номер страницы (начинается с 1)","examples":[1,2,5],"default":1,"title":"Page Number"},"description":"Номер страницы (начинается с 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"exclusiveMinimum":0,"description":"Количество элементов на одной странице (1–500)","examples":[20,50,100],"default":20,"title":"Page Size"},"description":"Количество элементов на одной странице (1–500)"},{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"Идентификатор торговой точки (outlet), по которой нужно отфильтровать заказы.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Outlet Id"},"description":"Идентификатор торговой точки (outlet), по которой нужно отфильтровать заказы."},{"name":"statuses","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/OrderStatusEnum"}},{"type":"null"}],"description":"Список статусов заказов для фильтрации. Если не указан — по статусу фильтрация не выполняется.","examples":[["pending","cancelled"],null],"title":"Statuses"},"description":"Список статусов заказов для фильтрации. Если не указан — по статусу фильтрация не выполняется."},{"name":"payment_methods","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethodEnum"}},{"type":"null"}],"description":"Список способов оплаты для фильтрации заказов. Если не указан — по способу оплаты фильтрация не выполняется.","examples":[["cash","pos_terminal"],["bank"],null],"title":"Payment Methods"},"description":"Список способов оплаты для фильтрации заказов. Если не указан — по способу оплаты фильтрация не выполняется."},{"name":"amount_min","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Минимальная сумма заказа (по итоговой сумме `final_price`). Если не указано — нижняя граница суммы не применяется.","examples":[1000.0,null],"title":"Amount Min"},"description":"Минимальная сумма заказа (по итоговой сумме `final_price`). Если не указано — нижняя граница суммы не применяется."},{"name":"amount_max","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Максимальная сумма заказа (по итоговой сумме `final_price`). Если не указано — верхняя граница суммы не применяется.","examples":[50000.0,null],"title":"Amount Max"},"description":"Максимальная сумма заказа (по итоговой сумме `final_price`). Если не указано — верхняя граница суммы не применяется."},{"name":"initiator","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/InitiatorEnum"},{"type":"null"}],"description":"Инициатор заказа (outlet или agent). Если не передан — фильтр по инициатору не применяется.","examples":["outlet","agent"],"title":"Initiator"},"description":"Инициатор заказа (outlet или agent). Если не передан — фильтр по инициатору не применяется."},{"name":"delivered_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Начальная дата планируемой доставки (фильтр по дате рейса `Trip.trip_date`). Применяется только к объектам доставки, сами заказы при этом не отфильтровываются.","examples":["2025-06-01",null],"title":"Delivered From"},"description":"Начальная дата планируемой доставки (фильтр по дате рейса `Trip.trip_date`). Применяется только к объектам доставки, сами заказы при этом не отфильтровываются."},{"name":"delivered_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Конечная дата планируемой доставки (фильтр по дате рейса `Trip.trip_date`). Применяется только к объектам доставки, сами заказы при этом не отфильтровываются.","examples":["2025-06-30",null],"title":"Delivered To"},"description":"Конечная дата планируемой доставки (фильтр по дате рейса `Trip.trip_date`). Применяется только к объектам доставки, сами заказы при этом не отфильтровываются."},{"name":"created_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Начальная дата/время создания заказа. На уровне сервиса всегда действует дополнительное ограничение на последние 7 дней, поэтому эффективно используется максимум из этого значения и (now - 7 дней).","examples":["2025-06-01T00:00:00Z",null],"title":"Created From"},"description":"Начальная дата/время создания заказа. На уровне сервиса всегда действует дополнительное ограничение на последние 7 дней, поэтому эффективно используется максимум из этого значения и (now - 7 дней)."},{"name":"created_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Конечная дата/время создания заказа. Если не указано — верхняя граница по дате создания не применяется.","examples":["2025-06-30T23:59:59Z",null],"title":"Created To"},"description":"Конечная дата/время создания заказа. Если не указано — верхняя граница по дате создания не применяется."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Список заказов, отфильтрованный по указанным параметрам.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderListResponseSchema"}}}},"401":{"description":"Неавторизованный или неактивный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера при получении списка заказов.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}":{"get":{"tags":["Orders"],"summary":"[DEPRECATED] Получить детализацию заказа","description":"❌ **УСТАРЕВШИЙ МЕТОД**\n\nДанный эндпоинт **НЕ РЕКОМЕНДОВАН К ИСПОЛЬЗОВАНИЮ**.\n\nПричины:\n- формат ответа v1 больше не является актуальным;\n- возможны расхождения с текущей моделью заказа и включаемыми данными.\n\n➡ Используйте: `GET /v2/orders/{order_id}`","operationId":"get_order_orders__order_id__get","deprecated":true,"security":[{"HTTPBearer":[]}],"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID заказа","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Order Id"},"description":"UUID заказа"},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Детализация заказа (устаревший формат v1).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDetailResponseV1Schema"}}}},"401":{"description":"Неавторизованный или неактивный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Заказ не найден или недоступен текущему пользователю.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"410":{"description":"Эндпоинт устарел и больше не поддерживается. Используйте GET /v2/orders/{order_id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера при получении детализации заказа.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Orders"],"summary":"[DEPRECATED] Обновить pending-заказ","description":"❌ **УСТАРЕВШИЙ МЕТОД**\n\nДанный эндпоинт **НЕ РЕКОМЕНДОВАН К ИСПОЛЬЗОВАНИЮ**.\n\nПричины:\n- логика v1 больше не поддерживается;\n- возможны расхождения в расчёте промо/лимитов и пересборке транзакций;\n- поддержка и развитие ведутся только для v2.\n\n➡ Используйте: `PATCH /v2/orders/{order_id}`","operationId":"update_order_orders__order_id__patch","deprecated":true,"security":[{"HTTPBearer":[]}],"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID заказа","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Order Id"},"description":"UUID заказа"},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrderRequestSchema","description":"Данные обновления заказа:\n- product_id: товар для изменения;\n- quantity: новое количество (в упаковках);\n- selected_gifts: выбранные подарки или null для переиспользования."}}}},"responses":{"200":{"description":"Заказ успешно обновлён (v1).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"400":{"description":"Нарушение бизнес-ограничений.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"401":{"description":"Неавторизованный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Заказ не найден/недоступен/не pending/не свежий.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"409":{"description":"Конфликт состояния при пересборке.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"410":{"description":"Эндпоинт устарел и больше не поддерживается. Используйте PATCH /v2/orders/{order_id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/cancel":{"post":{"tags":["Orders"],"summary":"Отменить заказ","description":"Отменяет заказ текущего пользователя.\n\n## Логика\n1) Заказ должен принадлежать текущему пользователю;\n2) Заказ должен быть в статусе: pending / approved / cancelled;\n3) Если заказ уже cancelled — возвращается ошибка конфликта;\n4) Статус заказа обновляется на cancelled;\n5) Добавляется запись в историю статусов (OrderStatusHistory);\n6) Всё выполняется атомарно в рамках `OrderUnitOfWork`.\n\n⚠️ Dispatcher-логика и вызов внешних сервисов здесь не используются.","operationId":"cancel_order_orders__order_id__cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID заказа","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"],"title":"Order Id"},"description":"UUID заказа"},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderRequestSchema","description":"Данные для отмены заказа.\n\n- `organization_id` — организация, от имени которой пользователь отменяет заказ;\n- `reason` — причина отмены (опционально)."}}}},"responses":{"200":{"description":"Заказ успешно отменён.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseSchema"}}}},"401":{"description":"Неавторизованный пользователь.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Заказ не найден или не принадлежит пользователю.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"409":{"description":"Заказ уже отменён или отмена невозможна по текущему статусу.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"500":{"description":"Внутренняя ошибка сервера.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations":{"get":{"tags":["Organizations"],"summary":"Получить список организаций","description":"Возвращает список организаций по фильтрам.\n\nКлючевой параметр:\n- `parents_only=true` — вернуть только корневые организации (parent_id IS NULL).\n\nПримеры:\n- Все организации: `GET /organizations`\n- Только корневые: `GET /organizations?parents_only=true`\n- По списку id: `GET /organizations?id=<uuid>&id=<uuid>`\n- По group_id: `GET /organizations?group_id=<uuid>`","operationId":"list_organizations_organizations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"parents_only","in":"query","required":false,"schema":{"type":"boolean","description":"Если true — вернуть только «родительские» организации: если parent существует и не удалён — берём parent, иначе берём саму организацию. Результат дедуплицируется по id.","examples":[false,true],"default":false,"title":"Parents Only"},"description":"Если true — вернуть только «родительские» организации: если parent существует и не удалён — берём parent, иначе берём саму организацию. Результат дедуплицируется по id."},{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string","format":"uuid4"}},{"type":"null"}],"description":"Фильтр по списку UID организаций. Можно передать несколько раз: ?id=a&id=b","examples":[["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"]],"title":"Id"},"description":"Фильтр по списку UID организаций. Можно передать несколько раз: ?id=a&id=b"},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"description":"Фильтр по group_id.","examples":["3f5b4bb0-7c20-4f7e-9d42-2d99f78b3cbe"],"title":"Group Id"},"description":"Фильтр по group_id."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по активности организации.","examples":[true,false],"title":"Is Active"},"description":"Фильтр по активности организации."},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}],"description":"Поиск по названию организации (подстрока, регистр не важен).","examples":["example","ТОО","Market"],"title":"Query"},"description":"Поиск по названию организации (подстрока, регистр не важен)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Список организаций","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationListResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"403":{"description":"Доступ к ресурсу запрещён для текущего пользователя","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Связанный ресурс не найден.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/outlets":{"get":{"tags":["Outlets"],"summary":"Получить список торговых точек пользователя","description":"Возвращает список торговых точек (аутлетов), к которым привязан текущий пользователь.\n\n        ### Что делает этот метод\n        - проверяет пользователя (авторизация)\n        - находит все аутлеты, к которым он привязан (по связке `UserOutlets`)\n        - фильтрует только актуальные точки:\n            - у аутлета есть `location_id`\n            - аутлет не помечен как удалённый (`deleted_at IS NULL`)\n        - по желанию подгружает банковские реквизиты (`bank_details`)\n\n        ### Параметры запроса\n        - `include_bank_details` (query, bool, опциональный):\n            - `false` (по умолчанию) — вернуть только базовую информацию об аутлетах\n            - `true` — дополнительно подгрузить банковские реквизиты (`Outlet.bank_details`)\n\n        ### Ответ\n        Возвращается список `OutletResponseSchema`, где каждый элемент описывает одну торговую точку,\n        доступную пользователю.\n\n        Если у пользователя нет ни одной доступной точки — вернётся пустой список.","operationId":"list_outlets_outlets_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include_bank_details","in":"query","required":false,"schema":{"type":"boolean","description":"Подгружать ли банковские реквизиты для каждой торговой точки.","default":false,"title":"Include Bank Details"},"description":"Подгружать ли банковские реквизиты для каждой торговой точки.","example":false},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OutletResponseSchema"},"title":"Response List Outlets Outlets Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/outlets/{outlet_id}":{"get":{"tags":["Outlets"],"summary":"Получить подробную информацию о торговой точке","description":"Позволяет получить полную информацию по одной конкретной торговой точке (аутлету),\n        к которой привязан текущий пользователь.\n\n        ### Что делает этот метод\n        - проверяет пользователя (авторизация)\n        - проверяет, что пользователь действительно привязан к этому `outlet_id`\n        - убеждается, что аутлет актуален:\n            - `location_id` не пустой\n            - `deleted_at IS NULL`\n        - по желанию подгружает банковские реквизиты (`bank_details`)\n\n        ### Параметры\n        - `outlet_id` (path):\n            UUID торговой точки, которую нужно получить.\n        - `include_bank_details` (query, bool, опциональный):\n            - `false` — без банковских реквизитов\n            - `true` — с коллекцией `bank_details`\n\n        ### Когда возвращается ошибка\n        Конкретные коды и тексты ошибок зависят от реализации сервиса `OutletService`, но типичные случаи:\n\n        - Если пользователь не привязан к указанной торговой точке → **404**\n        - Если аутлет помечен как удалённый или не привязан к локации → **404**\n\n        В качестве тела ошибки обычно возвращается объект вида:\n        `{ \"detail\": \"Outlet not found or not accessible\" }`.","operationId":"get_outlet_outlets__outlet_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"outlet_id","in":"path","required":true,"schema":{"type":"string","format":"uuid4","description":"UUID торговой точки, которую нужно получить.","title":"Outlet Id"},"description":"UUID торговой точки, которую нужно получить.","example":"5af8c223-d882-4351-a33b-b0dc50eedd53"},{"name":"include_bank_details","in":"query","required":false,"schema":{"type":"boolean","description":"Подгружать ли банковские реквизиты для этой торговой точки.","default":false,"title":"Include Bank Details"},"description":"Подгружать ли банковские реквизиты для этой торговой точки.","example":false},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutletResponseSchema"}}}},"404":{"description":"Торговая точка не найдена или недоступна для пользователя","content":{"application/json":{"example":{"detail":"Outlet not found or not accessible"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/missions":{"get":{"tags":["Missions"],"summary":"Получить список заданий (планы продаж)","description":"Возвращает задания для торговой точки.\n\nСейчас реализован только `plan_sales`.\n\n## Бизнес-правила расчёта fact в плане продаж\n- В расчёт попадают только заказы в статусе `delivered`.\n- Если по позиции заказа есть возврат в финальном статусе `returned`,\n  возвращённое количество вычитается из факта выполнения.\n- Для суммы вычитание выполняется пропорционально возвращённому количеству.\n\nПример: в заказе 11 шт, возвращено 5 шт в статусе `returned` — в плане учитывается 6 шт.\n\n## Вкладки\n- status=active: активные планы (status=active и finish_date >= today), кроме уже выполненных на 100%\n- status=completed: завершённые планы за последние 35 дней, включая просроченные active (finish_date < today), а также active-планы с 100% прогрессом; но только с участием ТТ (progress > 0)\nДля completed применяется пагинация (page_number/page_size), по умолчанию page_size=30.\n\nДля active пагинация не применяется.","operationId":"list_missions_missions_get","parameters":[{"name":"page_number","in":"query","required":false,"schema":{"type":"integer","exclusiveMinimum":0,"description":"Номер страницы (начинается с 1)","examples":[1,2,5],"default":1,"title":"Page Number"},"description":"Номер страницы (начинается с 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"exclusiveMinimum":0,"description":"Размер страницы для completed-выдачи (1-100).","default":30,"title":"Page Size"},"description":"Размер страницы для completed-выдачи (1-100)."},{"name":"outlet_id","in":"query","required":true,"schema":{"type":"string","format":"uuid4","description":"ID торговой точки.","title":"Outlet Id"},"description":"ID торговой точки."},{"name":"status","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MissionListStatusEnum","description":"Статус листинга (active или completed)."},"description":"Статус листинга (active или completed)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Успешный ответ со списком задач.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MissionListResponseSchema"}}}},"401":{"description":"Пользователь не авторизован или токен недействителен.","content":{"application/json":{"example":{"detail":{"code":401,"key":"errors.auth.unauthorized","message":"Unauthorized"}},"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации query-параметров.","content":{"application/json":{"example":{"detail":[{"loc":["query","outlet_id"],"msg":"Field required","type":"missing"}]}}}},"500":{"description":"Внутренняя ошибка сервиса при построении списка заданий.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/devices/register":{"post":{"tags":["Notifications"],"summary":"Зарегистрировать FCM-токен устройства (OUTLET)","description":"Регистрирует или обновляет FCM-токен текущего пользователя для OUTLET-приложения.\n\nИспользуется на старте приложения и после логина.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_register_device","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationRegisterDeviceRequestSchema","description":"Данные устройства для регистрации push-токена.","examples":[{"platform":"android","fcm_token":"fcm_token_xxxxxxxxxxxxxxxxxxxx","device_id":"8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76","app_version":"2.14.0","timezone":"Asia/Qyzylorda"}]}}}},"responses":{"200":{"description":"UID записи токена устройства в notification-service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationRegisterDeviceResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации запроса или ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/devices/unregister":{"post":{"tags":["Notifications"],"summary":"Деактивировать FCM-токен устройства (OUTLET)","description":"Деактивирует токен текущего пользователя в notification-service.\n\nИспользуется на logout.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_unregister_device","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationUnregisterDeviceRequestSchema","description":"Данные устройства для деактивации токена (device-aware).","examples":[{"device_id":"8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76"},{"device_id":"8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76","fcm_token":"fcm_token_xxxxxxxxxxxxxxxxxxxx"}]}}}},"responses":{"200":{"description":"Результат деактивации токена устройства.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationUnregisterDeviceResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации запроса или ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/settings":{"get":{"tags":["Notifications"],"summary":"Получить настройки уведомлений устройства (OUTLET)","description":"Возвращает настройки уведомлений текущего пользователя строго для одного устройства.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_get_settings","security":[{"HTTPBearer":[]}],"parameters":[{"name":"device_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Обязательный идентификатор устройства.","examples":["8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76"],"title":"Device Id"},"description":"Обязательный идентификатор устройства."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Текущие настройки уведомлений для указанного device_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationSettingsResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации запроса или ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}},"patch":{"tags":["Notifications"],"summary":"Обновить настройки уведомлений устройства (OUTLET)","description":"Частично обновляет настройки уведомлений текущего пользователя строго для одного устройства.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_update_settings","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationUpdateSettingsRequestSchema","description":"Обновление настроек уведомлений устройства (device-aware).","examples":[{"device_id":"8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76","marketing_enabled":false}]}}}},"responses":{"200":{"description":"Обновлённые настройки уведомлений для указанного device_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationSettingsResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации запроса или ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications":{"get":{"tags":["Notifications"],"summary":"Получить ленту уведомлений","description":"Возвращает ленту уведомлений Центра уведомлений с курсорной пагинацией.\n\nПоддерживаемые фильтры:\n- текстовый поиск (`search`)\n- период (`created_from`, `created_to`)\n- инициатор (`initiator_uid`, `initiator_type`, `initiator_name`)\n- непрочитанные (`only_unread`)\n- `notification_type`, `content_type`, `action`, `recipient_type`.\n\nКаждый элемент `items[]` отдается строго в новом контракте:\n`id`, `schema_version`, `app_type`, `recipient_type`, `notification_type`, `content_type`, `title`, `body`, `images`, `action`, `data`, `is_read`, `sent_at`.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_list","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Размер страницы.","examples":[20],"default":20,"title":"Limit"},"description":"Размер страницы."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":512},{"type":"null"}],"description":"Курсор следующей страницы.","title":"Cursor"},"description":"Курсор следующей страницы."},{"name":"notification_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationType"},{"type":"null"}],"description":"Фильтр по типу уведомления.","title":"Notification Type"},"description":"Фильтр по типу уведомления."},{"name":"content_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationContentType"},{"type":"null"}],"description":"Фильтр по типу контента.","title":"Content Type"},"description":"Фильтр по типу контента."},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationAction"},{"type":"null"}],"description":"Фильтр по action.","title":"Action"},"description":"Фильтр по action."},{"name":"recipient_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationRecipientType"},{"type":"null"}],"description":"Фильтр по типу получателя.","title":"Recipient Type"},"description":"Фильтр по типу получателя."},{"name":"only_unread","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Если true — только непрочитанные.","title":"Only Unread"},"description":"Если true — только непрочитанные."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Поиск по title/body.","title":"Search"},"description":"Поиск по title/body."},{"name":"created_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Нижняя граница периода отправки (inclusive).","title":"Created From"},"description":"Нижняя граница периода отправки (inclusive)."},{"name":"created_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Верхняя граница периода отправки (inclusive).","title":"Created To"},"description":"Верхняя граница периода отправки (inclusive)."},{"name":"initiator_uid","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Точный фильтр по UID инициатора.","title":"Initiator Uid"},"description":"Точный фильтр по UID инициатора."},{"name":"initiator_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Точный фильтр по типу инициатора.","title":"Initiator Type"},"description":"Точный фильтр по типу инициатора."},{"name":"initiator_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"description":"Точный фильтр по имени инициатора.","title":"Initiator Name"},"description":"Точный фильтр по имени инициатора."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Страница уведомлений с `next_cursor`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка валидации фильтров или ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/unread-count":{"get":{"tags":["Notifications"],"summary":"Получить количество непрочитанных уведомлений","description":"Используется мобильным клиентом для отображения badge в меню.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_unread_count","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Количество непрочитанных уведомлений текущего пользователя.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationUnreadCountResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/{id}/read":{"post":{"tags":["Notifications"],"summary":"Пометить уведомление как прочитанное","description":"Помечает одно уведомление как прочитанное.\nИспользуется после открытия карточки в Notification Center или по push-click.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_mark_read","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"UID уведомления (`NotificationItem.id`).","examples":["notif-123","8a43f988-9405-4c72-b7a0-d2a5cf4ef6d8"],"title":"Id"},"description":"UID уведомления (`NotificationItem.id`)."},{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Результат обновления read-состояния одного уведомления.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationMarkReadResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"404":{"description":"Уведомление не найдено.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}},"/notifications/read-all":{"post":{"tags":["Notifications"],"summary":"Пометить все уведомления как прочитанные","description":"Помечает всю ленту текущего пользователя как прочитанную.\n\nТребуется `Authorization: Bearer <access_token>`.\nЯзык сообщений ошибок определяется `Accept-Language` (`ru`, `en`, `kk`).","operationId":"notifications_mark_all_read","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accept-Language"}}],"responses":{"200":{"description":"Количество уведомлений, переведённых в read-состояние.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationMarkAllReadResponseSchema"}}}},"401":{"description":"Ошибка авторизации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}},"422":{"description":"Ошибка интеграции.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseSchema"}}}}}}}},"components":{"schemas":{"AddProductToCartRequestSchema":{"properties":{"parent_organization_id":{"type":"string","format":"uuid4","title":"Parent Organization Id","description":"UUID родительской организации."},"outlet_id":{"type":"string","format":"uuid4","title":"Outlet Id","description":"UUID торговой точки."},"portfolio_id":{"type":"string","format":"uuid4","title":"Portfolio Id","description":"UUID портфеля, в рамках которого лежит товар в корзине."},"product_id":{"type":"string","format":"uuid4","title":"Product Id","description":"UUID товара."},"quantity":{"type":"integer","minimum":0.0,"title":"Quantity","description":"Количество товара. 0 — удалить товар из корзины.","examples":[3,0]}},"type":"object","required":["parent_organization_id","outlet_id","portfolio_id","product_id","quantity"],"title":"Upsert Product In Cart Request Schema","description":"Схема запроса для добавления/обновления товара в корзине.\n\nПоведение:\n  - quantity > 0: добавить/обновить количество\n  - quantity = 0: удалить товар из корзины (если есть)\n\nПоле `organization_id` принимает значение из ключа `parent_organization_id`."},"BrandExtendedResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"UID бренда.","examples":["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10"]},"name":{"type":"string","title":"Name","description":"Название бренда.","examples":["Coca-Cola"]},"internal_brand_id":{"type":"integer","title":"Internal Brand Id","description":"Автоинкрементный числовой идентификатор бренда (внутренний).","examples":[123]},"logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo","description":"Ссылка/путь к логотипу бренда.","examples":["https://cdn.example.kz/brands/coca-cola.png"]},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Описание бренда.","examples":["Известный бренд напитков."]},"is_active":{"type":"boolean","title":"Is Active","description":"Активен ли бренд.","examples":[true]}},"type":"object","required":["id","name","internal_brand_id","is_active"],"title":"Brand Extended Response Schema","description":"Расширенная схема бренда — содержит все поля из модели Brand\n(и стандартные поля TimestampMixin, чтобы можно было легко убрать лишнее).","examples":[{"description":"Известный бренд напитков.","id":"8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","internal_brand_id":123,"is_active":true,"logo":"https://cdn.example.kz/brands/coca-cola.png","name":"Coca-Cola"}]},"BrandListResponseSchema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BrandExtendedResponseSchema"},"type":"array","title":"Items","description":"Список брендов по фильтрам."}},"type":"object","title":"Brands List Response Schema","description":"Ответ со списком брендов.","examples":[{"items":[{"external_id":"EXT-COCA-COLA-001","id":"8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","id_brand":123,"is_active":true,"name":"Coca-Cola"}]}]},"CancelOrderRequestSchema":{"properties":{"organization_id":{"type":"string","format":"uuid4","title":"Organization Id","description":"Идентификатор родительской организации, в рамках которой отменяется заказ."},"reason":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Reason","description":"Причина отмены заказа (опционально).","examples":["Клиент передумал","Ошибка в заказе"]}},"type":"object","required":["organization_id"],"title":"Order Base Schema","description":"Тело запроса на отмену заказа пользователем.\n\n- reason: причина отмены (опционально). Пустые/пробельные строки превращаем в None."},"CartContractSchema":{"properties":{"type":{"$ref":"#/components/schemas/ContractTypeEnum","description":"Тип контракта (credit, mix, cash, prepayment и т.д.).","examples":["credit","mix"]},"number":{"type":"string","title":"Number","description":"Номер контракта, по которому будет оформлен заказ.","examples":["CT-2024-004121","MX-5521"]}},"type":"object","required":["type","number"],"title":"Cart Contract Schema","description":"Схема контракта, привязанного к корзине.\n\nИспользуется для отображения информации о том,\nпо какому контракту будет оформлен заказ:\n  - тип контракта (credit, mix, cash и т.п.)\n  - номер контракта\n\nЭти данные приходят из слоя бизнес-логики расчёта корзины."},"CartGiftResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор товара (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара."},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"Список URL-адресов изображений товара."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке."},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Флаг активности товара (если False — скрыт из каталога).","examples":[true]},"category_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Category Id","description":"UUID категории товара.","examples":["a72f0c62-9869-4db2-b20c-45d8b3bd2c68"]},"id_product":{"type":"integer","title":"Id Product","description":"Внутренний числовой ID товара.","examples":[103030]},"brand_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Brand Id","description":"UUID бренда товара.","examples":["c81d3c93-faa4-4632-b9ad-21d9d8e1463e"]},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining","description":"Доступный остаток данного подарочного товара.","examples":[5,20]},"warehouse_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Warehouse Id","description":"Склад, с которого может быть выдан подарок.","examples":["73f8cc2f-c7eb-49fe-9a6a-77f92437d2b1"]},"base_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price","description":"Базовая цена товара. Используется для расчётов стоимости подарков, бюджетных ограничений и аналитики.","examples":[120.5]},"reference_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference Code","description":"Внутренний справочный код товара в рамках организации.","examples":["CHC-50-CL"]}},"type":"object","required":["id","id_product"],"title":"Cart Gift Response Schema","description":"Схема товара-подарка, используемого в промоакциях.\n\nНе содержит всей товарной информации (как ProductResponseSchema),\nа только минимальный набор данных, необходимых для отображения списка подарков."},"CartOrganizationGroupV1Schema":{"properties":{"organization":{"$ref":"#/components/schemas/OrganizationResponseSchema","description":"Родительская организация, к которой относится эта группа корзины."},"store_types":{"items":{"$ref":"#/components/schemas/CartStoreTypeGroupSchema"},"type":"array","title":"Store Types","description":"Группы корзины в разрезе store_type для данной организации."}},"type":"object","required":["organization","store_types"],"title":"Cart Organization Group Schema","description":"Группа корзины для одной parent-организации.\n\nВнутри — несколько store_type-групп (food, nonfood, ref...),\nчтобы не дублировать данные организации."},"CartPortfolioBlockV1Schema":{"properties":{"portfolio":{"$ref":"#/components/schemas/PortfolioResponseSchema","description":"Портфель, к которому относятся товары."},"sum":{"type":"number","title":"Sum","description":"Сумма по портфелю без учёта скидок.","examples":[150600.0]},"discount_sum":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Sum","description":"Суммарная скидка по портфелю (если есть).","examples":[10000.0,null]},"products":{"items":{"$ref":"#/components/schemas/ProductCheckoutSchema"},"type":"array","title":"Products","description":"Товарные позиции по данному портфелю."},"gifts":{"anyOf":[{"items":{"$ref":"#/components/schemas/PromotionGiftsSchema"},"type":"array"},{"type":"null"}],"title":"Gifts","description":"Подарки по промо, относящиеся к этому портфелю."},"contracts":{"anyOf":[{"items":{"$ref":"#/components/schemas/CartContractSchema"},"type":"array"},{"type":"null"}],"title":"Contracts","description":"Контракты, доступные для оформления заказа по данному портфелю."}},"type":"object","required":["portfolio","sum"],"title":"Cart Portfolio Block V1 Schema","description":"Блок корзины для одного портфеля.\n\nЭто «чистый» блок портфеля без полей, которые зависят от store_type\n(маршруты/способы оплаты/настройки торговой точки)."},"CartResponseV1Schema":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/CartOrganizationGroupV1Schema"},"type":"array","title":"Groups","description":"Список групп корзины по parent-организациям."}},"type":"object","title":"Cart Grouped By Parent Organization And Store Type Response Schema","description":"Ответ корзины.\n\nКорзина состоит из нескольких групп:\n  - каждая группа относится к одной parent-организации;\n  - внутри группы — несколько store_type-групп;\n  - внутри store_type-группы — портфели, товары, подарки и агрегаты."},"CartStoreTypeGroupSchema":{"properties":{"store_type":{"$ref":"#/components/schemas/StoreTypeEnum","description":"Тип магазина/канала (food, nonfood, ref, mixed).","examples":["food","nonfood"]},"total_items":{"type":"integer","title":"Total Items","description":"Общее количество единиц товара по всем портфелям группы.","examples":[232]},"total_sum":{"type":"number","title":"Total Sum","description":"Общая сумма по группе без скидок.","examples":[150600.0]},"total_discount_sum":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Discount Sum","description":"Общая сумма скидки по группе.","examples":[10000.0,null]},"portfolios":{"items":{"$ref":"#/components/schemas/CartPortfolioBlockV1Schema"},"type":"array","title":"Portfolios","description":"Портфели, относящиеся к данной организации и store_type."},"routes":{"anyOf":[{"items":{"$ref":"#/components/schemas/ScheduledRouteResponseSchema"},"type":"array"},{"type":"null"}],"title":"Routes","description":"Маршруты, релевантные для данного store_type."},"payment_methods":{"items":{"$ref":"#/components/schemas/PaymentMethodEnum"},"type":"array","title":"Payment Methods","description":"Доступные способы оплаты для данной группы корзины. Всегда содержит 'cash' и 'pos_terminal'. 'bank' доступен только если у всех портфелей есть контракт типа bank или mix. 'credit' доступен только если у всех портфелей есть контракт типа credit или mix."},"outlet_setting":{"anyOf":[{"$ref":"#/components/schemas/OutletSettingResponseSchema"},{"type":"null"}],"description":"Настройки торговой точки, релевантные для этого store_type."}},"type":"object","required":["store_type","total_items","total_sum","portfolios","payment_methods"],"title":"Cart Store Type Group Schema","description":"Группа корзины для конкретного store_type внутри одной организации."},"CategoryResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"UUID4 категории.","examples":["a72f0c62-9869-4db2-b20c-45d8b3bd2c68"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Название категории.","examples":["Молочные продукты"]},"logo":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Logo","description":"Логотип категории. Может быть:\n- строкой с URL;\n- списком URL (если поддерживается несколько изображений);\n- `null`, если логотип не задан.","examples":["https://cdn.example.com/category/milk.png",["https://cdn.example.com/category/milk.png"],null]},"parent_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Parent Id","description":"UUID4 родительской категории. Если значение `null` — категория является корневой.","examples":["1a2b3c4d-1111-2222-3333-444455556666",null]},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position","description":"Позиция категории в сортировке. Чем меньше число — тем выше категория в списке.","examples":[1,10]}},"type":"object","required":["id"],"title":"Category Response Schema","description":"Схема категории, возвращаемая в ответе API.\n\nИспользуется для отображения дерева категорий в клиенте.\nВключает только ту информацию, которая необходима\nдля построения навигации и визуального представления."},"CategoryVisibilityEnum":{"type":"string","enum":["all","agent","outlet"],"title":"CategoryVisibilityEnum"},"ClearCartByPortfolioRequestSchema":{"properties":{"parent_organization_id":{"type":"string","format":"uuid4","title":"Parent Organization Id","description":"UUID родительской организации (parent org), от имени которой ведётся корзина.","examples":["3f45ac49-9b4a-4b5e-9035-4a8267a1f0de"]},"outlet_id":{"type":"string","format":"uuid4","title":"Outlet Id","description":"UUID торговой точки (контекст корзины).","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"portfolio_id":{"type":"string","format":"uuid4","title":"Portfolio Id","description":"UUID портфеля, по которому нужно удалить все товары из корзины.","examples":["d2a1126f-5a0f-48ea-b1fc-8a04f0cd5671"]}},"type":"object","required":["parent_organization_id","outlet_id","portfolio_id"],"title":"Clear Cart By Portfolio Request Schema","description":"Очистить корзину по портфелю:\nудаляет ВСЕ товары в корзине пользователя, относящиеся к portfolio_id,\nв рамках outlet_id и parent_organization_id (organization_id в модели корзины).\n\nПримечание:\n  - parent_organization_id приходит с фронта под ключом `parent_organization_id`,\n    а в коде используем `organization_id`."},"ContractTypeEnum":{"type":"string","enum":["bank","credit","mix"],"title":"ContractTypeEnum"},"CreateOrderRequestV1Schema":{"properties":{"outlet_id":{"type":"string","format":"uuid4","title":"Outlet Id","description":"Идентификатор торговой точки, для которой создаётся заказ.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"organization_id":{"type":"string","format":"uuid4","title":"Organization Id","description":"Идентификатор родительской организации, от имени которой создаётся заказ.","examples":["2a0f01f4-9b7b-4f2e-8a21-7e40ba2cbf0b"]},"payment_method":{"$ref":"#/components/schemas/PaymentMethodEnum","description":"Способ оплаты заказа (наличные/безнал/отсрочка и т.п.).","examples":["cash","pos_terminal","bank","credit"]},"selected_gifts":{"anyOf":[{"items":{"$ref":"#/components/schemas/OrderGiftRequestSchema"},"type":"array"},{"type":"null"}],"title":"Selected Gifts","description":"Список выбранных пользователем подарков по доступным промоакциям. Для каждой пары (promotion_id, product_id) допускается только одна запись.","examples":[[{"product_id":"304e7c03-69d5-4032-ac44-e56951e06f89","promotion_id":"25356657-88c9-4e85-b5dc-d856e67bef32","quantity":1}],null]},"delivery_date":{"type":"string","format":"date","title":"Delivery Date","description":"Дата планируемой доставки заказа (локальное время клиента).","examples":["2025-06-15"]},"transfer_type":{"$ref":"#/components/schemas/StoreTypeEnum","description":"Тип магазина/трансфера, для которого создаётся заказ. Определяет ассортимент, промо-правила и доступные портфели.","examples":["food","nonfood","ref","mixed"]},"portfolio_contracts":{"anyOf":[{"items":{"$ref":"#/components/schemas/OrderPortfolioContractSchema"},"type":"array"},{"type":"null"}],"title":"Portfolio Contracts","description":"Список связок (портфель, номер договора), по которым проводится заказ. Требования к заполнению зависят от политики для выбранного способа оплаты.","examples":[[{"contract_number":"CN-2025-00123","portfolio_id":"4e301256-1808-4212-8abc-2b2aedac3e7f"},{"contract_number":"CN-2025-00456","portfolio_id":"7b4e7651-6b4a-4e3a-9b77-2ba77f9da30e"}],null]}},"type":"object","required":["outlet_id","organization_id","payment_method","delivery_date","transfer_type"],"title":"Order Create Schema","description":"Схема запроса на создание заказа.\n\nНаследует базовые поля заказа из `OrderBaseSchema` (суммы, комментарии и т.п.)\nи дополняет их:\n  * привязкой к организации и торговой точке;\n  * типом трансфера / магазина (food, nonfood, ref, mixed);\n  * способом оплаты;\n  * выбранными подарками по промоакциям;\n  * привязкой портфелей к договорам;\n  * датой планируемой доставки.\n\nОсобенности V1:\n  - transfer_type обязателен (food/nonfood/ref/mixed);\n  - portfolio_contracts — список связок (portfolio_id, contract_number),\n    где requirements зависят от payment_method."},"DeliveryResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор рейса доставки (UUID). Используется как основной идентификатор в API.","examples":["73f8cc2f-c7eb-49fe-9a6a-77f92437d2b1"]},"trip_id":{"type":"integer","title":"Trip Id","description":"Внутренний числовой идентификатор рейса в логистической системе. Может использоваться для интеграций с внешними системами и отладкой.","examples":[1024]},"delivery_date":{"type":"string","format":"date","title":"Delivery Date","description":"Дата рейса / планируемой доставки заказов в рамках этого рейса. Временная зона зависит от бизнес-настроек, обычно локальное время региона.","examples":["2025-06-15"]}},"type":"object","required":["id","trip_id","delivery_date"],"title":"Delivery Response Schema","description":"Схема ответа с информацией о рейсе доставки.\n\nИспользуется, например, в составе `OrderResponseSchema.trip`\nдля отображения краткой информации о рейсе, к которому привязан заказ.\n\nСтруктура полей:\n  * Идентификаторы:\n      - `id` — основной UUID рейса (используется как ключ в API);\n      - `trip_id` — числовой внутренний идентификатор рейса.\n  * Даты:\n      - `delivery_date` — дата рейса / планируемой доставки."},"ErrorDetailSchema":{"properties":{"code":{"type":"integer","title":"Code"},"message":{"type":"string","title":"Message"},"key":{"type":"string","title":"Key"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"}},"type":"object","required":["code","message","key"],"title":"ErrorDetailSchema"},"ErrorResponseSchema":{"properties":{"detail":{"$ref":"#/components/schemas/ErrorDetailSchema"}},"type":"object","required":["detail"],"title":"ErrorResponseSchema"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InitiatorEnum":{"type":"string","enum":["outlet","agent"],"title":"InitiatorEnum"},"MessageResponseSchema":{"properties":{"message":{"type":"string","title":"Message","description":"Текст ответа, кратко описывающий результат операции.","examples":["Password updated successfully","Operation completed"]},"detail":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail","description":"Дополнительная информация для клиента или отладки. Может содержать произвольный набор полей.","examples":[{"affected_rows":1},{"reason":"token expired"}]}},"type":"object","required":["message"],"title":"MessageResponseSchema","description":"Универсальная схема текстового ответа API.\n\nПредназначена для возврата простого человекочитаемого сообщения (`message`)\nи, при необходимости, дополнительной диагностической информации (`detail`).\n\nИспользуется:\n  * для информирования об успешном выполнении операции;\n  * для возврата простых статусов;\n  * как fallback-ответ в ряде эндпоинтов без сложной структуры данных."},"MissionItemSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"UID миссии."},"name":{"type":"string","title":"Name","description":"Название миссии."},"type":{"$ref":"#/components/schemas/MissionTypeEnum","description":"Тип миссии: plan_sales / merchandising / ..."},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Дата начала."},"finish_date":{"type":"string","format":"date","title":"Finish Date","description":"Дата окончания."},"sales_plan":{"anyOf":[{"$ref":"#/components/schemas/MissionSalesPlanSchema"},{"type":"null"}],"description":"План продаж (есть, если миссия типа plan_sales)."},"merchandising":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Merchandising","description":"Мерчендайзинг (пока не реализовано)."}},"type":"object","required":["uid","name","type","start_date","finish_date"],"title":"MissionItemSchema","description":"Один элемент списка заданий.\n\nСейчас реализуем только sales_plan, merchandising оставляем заглушкой."},"MissionListResponseSchema":{"properties":{"page_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page Number","description":"Номер страницы для completed-выдачи. Для active может быть null (пагинация не применяется).","examples":[1,2]},"page_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page Size","description":"Размер страницы для completed-выдачи. Для active может быть null.","examples":[30]},"has_next":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Next","description":"Есть ли следующая страница для completed-выдачи. Для active может быть null.","examples":[true,false]},"items":{"items":{"$ref":"#/components/schemas/MissionItemSchema"},"type":"array","title":"Items"}},"type":"object","title":"MissionListResponseSchema","description":"Ответ эндпойнта /missions.\n\nВАЖНО:\n  - status=active:\n      возвращаем активные миссии, кроме полностью выполненных (100%)\n  - status=completed:\n      возвращаем миссии с участием ТТ (progress > 0) за последние 35 дней,\n      а также active-миссии с прогрессом 100%"},"MissionListStatusEnum":{"type":"string","enum":["active","completed"],"title":"MissionListStatusEnum"},"MissionProductSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"UID продукта."},"name":{"type":"string","title":"Name","description":"Название продукта."},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image","description":"Картинка (берём первый элемент из Product.image[])."},"organization_id":{"type":"string","format":"uuid4","title":"Organization Id","description":"Дочерняя организация продукта (для корзины)."},"parent_organization_id":{"type":"string","format":"uuid4","title":"Parent Organization Id","description":"Родительская организация дочерки (для корзины)."},"portfolio_id":{"type":"string","format":"uuid4","title":"Portfolio Id","description":"Портфель продукта (для корзины)."},"progress":{"anyOf":[{"$ref":"#/components/schemas/MissionProgressSchema"},{"type":"null"}],"description":"Прогресс продукта (ТОЛЬКО если by_brand=false)."},"need_add_to_cart":{"type":"boolean","title":"Need Add To Cart","description":"Нужно ли показывать кнопку 'добавить в корзину'. True только если status=active и план ещё не выполнен полностью.","default":false}},"type":"object","required":["uid","name","organization_id","parent_organization_id","portfolio_id"],"title":"MissionProductSchema","description":"Продукт в рамках sales plan.\n\norganization_id / parent_organization_id / portfolio_id:\n  - обязательны для добавления в корзину\n  - organization_id: ДОЧЕРНЯЯ организация миссии (child_org_id)\n  - parent_organization_id: РОДИТЕЛЬСКАЯ организация для child_org_id\n  - portfolio_id: выбирается из product.portfolios по organization_id (портфели не повторяются по организации)\n\nneed_add_to_cart:\n  - True только при запросе status=active и если план ещё не закрыт полностью."},"MissionProgressSchema":{"properties":{"current_value":{"type":"number","title":"Current Value","description":"Текущее значение выполнения. Считается как delivered-продажи минус подтверждённые возвраты (refund status = returned).","examples":[3200.0]},"plan_type":{"$ref":"#/components/schemas/PlanTypeEnum","description":"Тип плана: quantity или amount."},"agent_percentage":{"type":"number","title":"Agent Percentage","description":"Процент, который получает торговый агент.","examples":[5.0]},"outlet_percentage":{"type":"number","title":"Outlet Percentage","description":"Процент, который получает торговая точка.","examples":[95.0]},"steps":{"items":{"$ref":"#/components/schemas/MissionProgressStepSchema"},"type":"array","title":"Steps","description":"Список ступеней (1..3)."},"percentage":{"type":"number","title":"Percentage","description":"Процент выполнения 0..100.","examples":[32.0]},"next_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Reward","description":"Следующая награда из админки, если есть непройденная ступень.","examples":[100.0]},"next_outlet_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Outlet Reward","description":"Сколько реально получит ТТ на следующей ступени.","examples":[95.0]},"total_reward":{"type":"number","title":"Total Reward","description":"Достигнутая награда из админки по последней достигнутой ступени.","default":0.0,"examples":[50.0]},"total_outlet_reward":{"type":"number","title":"Total Outlet Reward","description":"Сколько реально получила ТТ по последней достигнутой ступени.","default":0.0,"examples":[47.5]}},"type":"object","required":["current_value","plan_type","agent_percentage","outlet_percentage","steps","percentage"],"title":"MissionProgressSchema","description":"Прогресс выполнения плана.\n\ncurrent_value:\n  - текущее накопленное значение (количество или сумма)\nagent_percentage:\n  - процент агента\noutlet_percentage:\n  - процент торговой точки\nnext_reward:\n  - следующая награда из админки / coins* без применения процента ТТ\nnext_outlet_reward:\n  - сколько реально получит ТТ на следующей ступени\ntotal_reward:\n  - достигнутая награда из админки / coins* без применения процента ТТ\ntotal_outlet_reward:\n  - сколько реально получила ТТ по последней достигнутой ступени"},"MissionProgressStepSchema":{"properties":{"value":{"type":"number","title":"Value","description":"Целевое значение ступени.","examples":[10000.0]},"reward":{"type":"number","title":"Reward","description":"Награда из плана продаж / админки без применения процента ТТ.","examples":[50.0]},"outlet_reward":{"type":"number","title":"Outlet Reward","description":"Сколько реально получит ТТ на этой ступени.","examples":[47.5]}},"type":"object","required":["value","reward","outlet_reward"],"title":"MissionProgressStepSchema","description":"Одна ступень прогресса (1/2/3).\n\nvalue:\n  - целевое значение (кол-во или сумма), которое нужно достичь\nreward:\n  - сырая награда из админки / coins* без применения процента ТТ\noutlet_reward:\n  - сколько реально получит торговая точка на этой ступени\n    с учётом outlet_percentage"},"MissionSalesPlanSchema":{"properties":{"brand_id":{"type":"string","format":"uuid4","title":"Brand Id","description":"UID бренда."},"brand_name":{"type":"string","title":"Brand Name","description":"Название бренда."},"brand_logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Logo","description":"Лого бренда."},"brand_products":{"items":{"$ref":"#/components/schemas/MissionProductSchema"},"type":"array","title":"Brand Products","description":"Продукты бренда."},"progress":{"anyOf":[{"$ref":"#/components/schemas/MissionProgressSchema"},{"type":"null"}],"description":"Прогресс бренда (ТОЛЬКО если by_brand=true)."}},"type":"object","required":["brand_id","brand_name","brand_products"],"title":"MissionSalesPlanSchema","description":"Sales plan, сгруппированный по бренду.\n\nprogress:\n  - есть ТОЛЬКО если by_brand=true (прогресс на уровне бренда)\nbrand_products:\n  - список продуктов бренда\n  - progress у продуктов ТОЛЬКО если by_brand=false"},"MissionTypeEnum":{"type":"string","enum":["plan_sales","merchandising"],"title":"MissionTypeEnum"},"NotificationAction":{"type":"string","enum":["none","open_order_details","open_prostore","open_catalog","open_product_details","open_product_collection","open_task"],"title":"NotificationAction"},"NotificationAppType":{"type":"string","enum":["PRO","OUTLET"],"title":"NotificationAppType"},"NotificationContentType":{"type":"string","enum":["system","order","task","banner","carousel","product","product_collection","promotion","bonus"],"title":"NotificationContentType"},"NotificationItemSchema":{"properties":{"id":{"type":"string","minLength":1,"title":"Id","description":"Идентификатор уведомления из notification-service.","examples":["notif-123","8a43f988-9405-4c72-b7a0-d2a5cf4ef6d8"]},"schema_version":{"type":"integer","title":"Schema Version"},"app_type":{"$ref":"#/components/schemas/NotificationAppType"},"recipient_type":{"$ref":"#/components/schemas/NotificationRecipientType"},"notification_type":{"$ref":"#/components/schemas/NotificationType"},"content_type":{"$ref":"#/components/schemas/NotificationContentType"},"title":{"type":"string","title":"Title"},"body":{"type":"string","title":"Body"},"images":{"items":{"type":"string"},"type":"array","title":"Images"},"action":{"$ref":"#/components/schemas/NotificationAction"},"data":{"additionalProperties":true,"type":"object","title":"Data"},"is_read":{"type":"boolean","title":"Is Read"},"sent_at":{"type":"string","format":"date-time","title":"Sent At"}},"type":"object","required":["id","schema_version","app_type","recipient_type","notification_type","content_type","title","body","images","action","data","is_read","sent_at"],"title":"NotificationItemSchema"},"NotificationListResponseSchema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NotificationItemSchema"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"NotificationListResponseSchema"},"NotificationMarkAllReadResponseSchema":{"properties":{"updated_count":{"type":"integer","minimum":0.0,"title":"Updated Count","description":"Количество уведомлений, обновленных в read-состояние."}},"type":"object","required":["updated_count"],"title":"NotificationMarkAllReadResponseSchema"},"NotificationMarkReadResponseSchema":{"properties":{"success":{"type":"boolean","title":"Success"}},"type":"object","required":["success"],"title":"NotificationMarkReadResponseSchema"},"NotificationPlatform":{"type":"string","enum":["android","ios"],"title":"NotificationPlatform"},"NotificationRecipientType":{"type":"string","enum":["representative","outlet","shipping"],"title":"NotificationRecipientType"},"NotificationRegisterDeviceRequestSchema":{"properties":{"platform":{"$ref":"#/components/schemas/NotificationPlatform","description":"Платформа устройства: `android` или `ios`."},"fcm_token":{"type":"string","minLength":10,"title":"Fcm Token","description":"FCM registration token устройства.","examples":["fcm_token_xxxxxxxxxxxxxxxxxxxx"]},"device_id":{"type":"string","maxLength":255,"minLength":1,"title":"Device Id","description":"Уникальный идентификатор устройства в приложении.","examples":["8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76"]},"app_version":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"App Version","description":"Версия мобильного приложения.","examples":["2.14.0"]},"timezone":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Timezone","description":"Таймзона устройства (IANA), например `Asia/Qyzylorda`.","examples":["Asia/Qyzylorda"]}},"additionalProperties":false,"type":"object","required":["platform","fcm_token","device_id"],"title":"NotificationRegisterDeviceRequestSchema"},"NotificationRegisterDeviceResponseSchema":{"properties":{"device_token_uid":{"type":"string","format":"uuid4","title":"Device Token Uid","description":"UID зарегистрированной записи токена устройства."}},"type":"object","required":["device_token_uid"],"title":"NotificationRegisterDeviceResponseSchema"},"NotificationSettingsResponseSchema":{"properties":{"device_id":{"type":"string","maxLength":255,"minLength":1,"title":"Device Id","description":"Идентификатор устройства, для которого возвращены настройки."},"push_enabled":{"type":"boolean","title":"Push Enabled"},"order_status_enabled":{"type":"boolean","title":"Order Status Enabled"},"task_enabled":{"type":"boolean","title":"Task Enabled"},"marketing_enabled":{"type":"boolean","title":"Marketing Enabled"},"bonus_enabled":{"type":"boolean","title":"Bonus Enabled"},"moderation_enabled":{"type":"boolean","title":"Moderation Enabled"},"system_enabled":{"type":"boolean","title":"System Enabled"}},"type":"object","required":["device_id","push_enabled","order_status_enabled","task_enabled","marketing_enabled","bonus_enabled","moderation_enabled","system_enabled"],"title":"NotificationSettingsResponseSchema"},"NotificationType":{"type":"string","enum":["order_status_changed","prostore_bonus","outlet_moderation_status_changed","task_created","promotion_created","product_created","product_carousel","marketing_banner","system"],"title":"NotificationType"},"NotificationUnreadCountResponseSchema":{"properties":{"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Количество непрочитанных уведомлений."}},"type":"object","required":["count"],"title":"NotificationUnreadCountResponseSchema"},"NotificationUnregisterDeviceRequestSchema":{"properties":{"device_id":{"type":"string","maxLength":255,"minLength":1,"title":"Device Id","description":"Обязательный идентификатор устройства.","examples":["8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76"]},"fcm_token":{"anyOf":[{"type":"string","minLength":10},{"type":"null"}],"title":"Fcm Token","description":"FCM token устройства (опционально, как дополнительный селектор).","examples":["fcm_token_xxxxxxxxxxxxxxxxxxxx"]}},"additionalProperties":false,"type":"object","required":["device_id"],"title":"NotificationUnregisterDeviceRequestSchema"},"NotificationUnregisterDeviceResponseSchema":{"properties":{"success":{"type":"boolean","title":"Success","description":"Признак успешной деактивации токена."}},"type":"object","required":["success"],"title":"NotificationUnregisterDeviceResponseSchema"},"NotificationUpdateSettingsRequestSchema":{"properties":{"device_id":{"type":"string","maxLength":255,"minLength":1,"title":"Device Id","description":"Обязательный идентификатор устройства.","examples":["8d04b9fb-3a0d-4b09-8f6c-c8bf59e2fb76"]},"push_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Push Enabled","description":"Глобальный флаг push."},"order_status_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Order Status Enabled","description":"Уведомления по заказам."},"task_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Task Enabled","description":"Уведомления по заданиям."},"marketing_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Enabled","description":"Маркетинговые уведомления."},"bonus_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Bonus Enabled","description":"Уведомления по бонусам."},"moderation_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Moderation Enabled","description":"Уведомления по модерации."},"system_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"System Enabled","description":"Системные уведомления."}},"additionalProperties":false,"type":"object","required":["device_id"],"title":"NotificationUpdateSettingsRequestSchema"},"OrderContractProductsSchema":{"properties":{"number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Number","description":"Номер договора для этой группы позиций.","examples":["CN-2025-00123",null]},"products":{"items":{"$ref":"#/components/schemas/ProductCheckoutSchema"},"type":"array","title":"Products","description":"Товарные позиции, относящиеся к этому договору."}},"type":"object","title":"Order Contract Products Schema","description":"Группа товарных позиций заказа, объединённых по номеру договора.\n\nВажно:\n- `contract_number` может быть None, если транзакции по договору не привязаны\n  (или логика конкретного платежного метода это допускает).\n- `products` — список позиций (как и раньше), но только внутри одной группы."},"OrderDetailResponseV1Schema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор заказа (UUID).","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"internal_order_id":{"type":"integer","title":"Internal Order Id","description":"Числовой идентификатор заказа (внутренний порядковый номер).","examples":[12345]},"organization":{"anyOf":[{"$ref":"#/components/schemas/OrganizationResponseSchema"},{"type":"null"}],"description":"Информация об организации, от имени которой оформлен заказ. Может быть `null`, если организация не найдена (что считается аномалией)."},"outlet":{"anyOf":[{"$ref":"#/components/schemas/OutletResponseSchema"},{"type":"null"}],"description":"Информация о торговой точке (Outlet), к которой относится заказ. Может быть `null`, если точка не подгружалась или не найдена."},"status":{"$ref":"#/components/schemas/OrderStatusEnum","description":"Текущий статус заказа."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Дополнительное текстовое пояснение к статусу заказа, если есть.","examples":["Отменён клиентом",null]},"payment_method":{"$ref":"#/components/schemas/PaymentMethodEnum","description":"Способ оплаты заказа."},"payment_status":{"$ref":"#/components/schemas/PaymentStatusEnum","description":"Текущий статус оплаты заказа."},"total_sum":{"type":"number","title":"Total Sum","description":"Итоговая сумма заказа с учётом всех скидок (`final_price`).","examples":[12500.5]},"creation_date":{"type":"string","format":"date-time","title":"Creation Date","description":"Дата и время создания заказа (в UTC)."},"trip":{"anyOf":[{"$ref":"#/components/schemas/DeliveryResponseSchema"},{"type":"null"}],"description":"Информация о доставке / рейсе, к которому привязан заказ. Может быть `null`, если заказ ещё не распределён по рейсам."},"contracts":{"items":{"$ref":"#/components/schemas/OrderContractProductsSchema"},"type":"array","title":"Contracts","description":"Список групп товарных позиций по договорам. Если договоров несколько — будет несколько групп. Если договор не задан — группа будет с `contract_number = null`.","examples":[[{"contract_number":"CN-2025-00123","products":[]},{"contract_number":"CN-2025-00999","products":[]}]]},"gifts":{"anyOf":[{"items":{"$ref":"#/components/schemas/OrderGiftResponseSchema"},"type":"array"},{"type":"null"}],"title":"Gifts","description":"Список подарков, начисленных по промоакциям в рамках заказа."}},"type":"object","required":["id","internal_order_id","status","payment_method","payment_status","total_sum","creation_date","contracts"],"title":"Order Detail Response V1 Schema","description":"Расширенная схема детализации заказа.\n\nВ отличие от прежней версии:\n- `contract_number` больше не одно значение на заказ;\n- позиции заказа группируются по номеру договора в `contracts`.\n\nПоля:\n  * `contracts` — список групп товаров по договорам;\n  * `gifts` — список подарков по промоакциям."},"OrderGiftRequestSchema":{"properties":{"promotion_id":{"type":"string","format":"uuid4","title":"Promotion Id","description":"Идентификатор промоакции, из которой выбирается подарок.","examples":["25356657-88c9-4e85-b5dc-d856e67bef32"]},"product_id":{"type":"string","format":"uuid4","title":"Product Id","description":"Идентификатор продукта-подарка, доступного в рамках промоакции.","examples":["304e7c03-69d5-4032-ac44-e56951e06f89"]},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Количество выбранных подарков данного товара по промоакции.","examples":[1,2,5]}},"type":"object","required":["promotion_id","product_id","quantity"],"title":"Order Gift Request Schema","description":"Элемент списка выбранных подарков по промоакциям при создании заказа.\n\nОписывает:\n  * какую промоакцию пользователь использует;\n  * какой продукт-подарок выбрал;\n  * в каком количестве он выбирает этот подарок."},"OrderGiftResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор товара (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара."},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"Список URL-адресов изображений товара."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке."},"secondary_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Name","description":"Дополнительное/альтернативное наименование товара (например, на другом языке или сокращённый вариант).","examples":["Coca-Cola 1L"]},"brand_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Brand Id","description":"Идентификатор бренда подарочного товара.","examples":["c81d3c93-faa4-4632-b9ad-21d9d8e1463e",null]},"id_product":{"type":"integer","title":"Id Product","description":"Внутренний числовой ID товара.","examples":[103030]},"quantity":{"type":"integer","title":"Quantity","description":"Количество подарочного товара в этом заказе.","examples":[1,2,5]},"promotion_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Promotion Id","description":"Идентификатор промоакции, по которой был начислен подарок.","examples":["ea48a435-941f-4bf2-a6db-d3df8217ff96",null]},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация товара (артикул/код/и т.п.). Должна совпадать с тем, что отдаёт каталог. Обычно выбирается по organization_id (из портфеля/заказа)."}},"type":"object","required":["id","id_product","quantity"],"title":"Order Gift Response Schema","description":"Схема товара-подарка внутри оформленного заказа.\n\nНаследуется от `ProductBaseSchema`, поэтому включает базовую товарную\nинформацию, а также дополнительные поля, относящиеся к подарку в контексте заказа:\n\n  * `brand_id` — бренд товара;\n  * `id_product` — внутренний числовой ID товара;\n  * `quantity` — количество подарочных единиц в заказе;\n  * `promotion_id` — промоакция, по которой начислен подарок."},"OrderListResponseSchema":{"properties":{"page_number":{"type":"integer","exclusiveMinimum":0.0,"title":"Page Number","description":"Номер страницы (начинается с 1)","default":1,"examples":[1,2,5]},"page_size":{"type":"integer","maximum":500.0,"exclusiveMinimum":0.0,"title":"Page Size","description":"Количество элементов на одной странице (1–500)","default":20,"examples":[20,50,100]},"total_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Pages","description":"Общее количество страниц","examples":[12]},"total_items":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Items","description":"Общее количество элементов","examples":[234]},"results":{"items":{"$ref":"#/components/schemas/OrderResponseSchema"},"type":"array","title":"Results","description":"Список заказов на текущей странице."}},"type":"object","required":["results"],"title":"OrderListResponseSchema","description":"Ответ пагинации по заказам.\n\nПоля:\n  * page_number / page_size — входные параметры пагинации\n  * total_items — общее число заказов, подходящих под фильтры (без учёта limit/offset)\n  * total_pages — число страниц = ceil(total_items / page_size)\n  * results — список OrderResponseSchema на текущей странице"},"OrderPortfolioContractSchema":{"properties":{"portfolio_id":{"type":"string","format":"uuid4","title":"Portfolio Id","description":"Идентификатор портфеля, к которому относится договор.","examples":["4e301256-1808-4212-8abc-2b2aedac3e7f"]},"contract_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contract Number","description":"Номер договора для данного портфеля. Может быть не указан, если политика оплаты это допускает.","examples":["CN-2025-00123",null]}},"type":"object","required":["portfolio_id"],"title":"Order Base Schema","description":"Связка портфеля и договора, по которому проводится заказ."},"OrderResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор заказа (UUID).","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"internal_order_id":{"type":"integer","title":"Internal Order Id","description":"Числовой идентификатор заказа (внутренний порядковый номер).","examples":[12345]},"organization":{"anyOf":[{"$ref":"#/components/schemas/OrganizationResponseSchema"},{"type":"null"}],"description":"Информация об организации, от имени которой оформлен заказ. Может быть `null`, если организация не найдена (что считается аномалией)."},"outlet":{"anyOf":[{"$ref":"#/components/schemas/OutletResponseSchema"},{"type":"null"}],"description":"Информация о торговой точке (Outlet), к которой относится заказ. Может быть `null`, если точка не подгружалась или не найдена."},"status":{"$ref":"#/components/schemas/OrderStatusEnum","description":"Текущий статус заказа."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Дополнительное текстовое пояснение к статусу заказа, если есть.","examples":["Отменён клиентом",null]},"payment_method":{"$ref":"#/components/schemas/PaymentMethodEnum","description":"Способ оплаты заказа."},"payment_status":{"$ref":"#/components/schemas/PaymentStatusEnum","description":"Текущий статус оплаты заказа."},"total_sum":{"type":"number","title":"Total Sum","description":"Итоговая сумма заказа с учётом всех скидок (`final_price`).","examples":[12500.5]},"creation_date":{"type":"string","format":"date-time","title":"Creation Date","description":"Дата и время создания заказа (в UTC)."},"trip":{"anyOf":[{"$ref":"#/components/schemas/DeliveryResponseSchema"},{"type":"null"}],"description":"Информация о доставке / рейсе, к которому привязан заказ. Может быть `null`, если заказ ещё не распределён по рейсам."}},"type":"object","required":["id","internal_order_id","status","payment_method","payment_status","total_sum","creation_date"],"title":"Order Response Schema","description":"Схема элемента списка заказов.\n\nИспользуется в ответе эндпоинта получения списка заказов.\n\nСтруктура полей:\n  * Идентификаторы и связи:\n      - `id` (uid),\n      - `order_id`,\n      - `organization`.\n      - `outlet`.\n  * Статусы и оплата:\n      - `status`,\n      - `status_reason`,\n      - `payment_method`,\n      - `payment_status`.\n  * Суммы:\n      - `sum`,\n  * Временные метки и логистика:\n      - `creation_date`,\n      - `delivery_date`,\n      - `trip`."},"OrderStatusEnum":{"type":"string","enum":["pending","approved","confirmed","processing","assembled","rejected","shipped","delivered","completed","cancelled","failed"],"title":"OrderStatusEnum"},"OrganizationExtendedResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор организации.","examples":["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Наименование организации.","examples":["ТОО Example Company"]},"parent_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Parent Id","description":"UID родительской организации (если есть).","examples":["6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"]},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Описание организации (опционально).","examples":["Дистрибьютор FMCG продукции."]},"legal_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Address","description":"Юридический адрес.","examples":["г. Алматы, ул. Абая 10"]},"physical_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Physical Address","description":"Фактический адрес.","examples":["г. Алматы, ул. Сатпаева 15"]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Email организации.","examples":["info@example.kz"]},"contacts":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contacts","description":"Контактные данные/телефоны/ответственные (свободный формат).","examples":["+7 777 000 00 00; Иван Иванов"]},"iin_bin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iin Bin","description":"ИИН/БИН организации (если применимо).","examples":["123456789012"]},"bic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bic","description":"БИК банка.","examples":["KCJBKZKX"]},"iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iban","description":"IBAN счёта.","examples":["KZ123456789012345678"]},"kbe":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kbe","description":"КБЕ.","examples":["17"]},"logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo","description":"Ссылка/путь к логотипу организации.","examples":["https://cdn.example.kz/logos/org.png"]},"is_active":{"type":"boolean","title":"Is Active","description":"Активна ли организация.","examples":[true]},"group_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Group Id","description":"Идентификатор группы организаций (если используется группировка).","examples":["3f5b4bb0-7c20-4f7e-9d42-2d99f78b3cbe"]}},"type":"object","required":["id","is_active"],"title":"Organization Extended Response Schema","description":"Расширенная схема организации для выдачи в списках и админских формах.\n\nНаследуется от `OrganizationResponseSchema`, поэтому уже содержит:\n- `id` (alias для uid)\n- `name`\n- `parent_id`\n\nНиже добавлены ВСЕ поля из модели `Organization`, чтобы тебе было проще\n«выкинуть лишнее», не вспоминая названия.\n\nВажно по типам:\n- Поля, которые `nullable=True` в модели — здесь сделаны `| None`.\n- `limit` — float, потому что в модели FLOAT.\n- `integration` — enum `IntegrationTypeEnum`.\n- `created_at/updated_at/deleted_at` добавлены как типичные поля TimestampMixin.\n  Если у твоего миксина другое название — просто поправишь/удалишь.","examples":[{"bic":"KCJBKZKX","contacts":"+7 777 000 00 00; Иван Иванов","description":"Дистрибьютор FMCG продукции.","email":"info@example.kz","group_id":"3f5b4bb0-7c20-4f7e-9d42-2d99f78b3cbe","iban":"KZ123456789012345678","id":"8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","iin_bin":"123456789012","is_active":true,"kbe":"17","legal_address":"г. Алматы, ул. Абая 10","logo":"https://cdn.example.kz/logos/org.png","name":"ТОО Example Company","physical_address":"г. Алматы, ул. Сатпаева 15"}]},"OrganizationListResponseSchema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/OrganizationExtendedResponseSchema"},"type":"array","title":"Items","description":"Список организаций по заданным фильтрам."}},"type":"object","title":"Organization List Response Schema","description":"Ответ со списком организаций.\n\nИспользование:\n- Возвращаем массив `items`, где каждый элемент — `OrganizationExtendedResponseSchema`.\n- Можно расширить пагинацией позже (`total`, `page`, `size`), не ломая базовый контракт.","examples":[{"items":[{"id":"8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10","is_active":true,"name":"ТОО Example Company"}]}]},"OrganizationResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор организации.","examples":["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Наименование организации.","examples":["ТОО Example Company"]},"parent_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Parent Id","description":"UID родительской организации (если есть).","examples":["6b0dc2f0-9a61-4cba-8b56-6b8a3c8f39f2"]}},"type":"object","required":["id"],"title":"Organization Response Schema","description":"Короткая схема организации — обычно используется в выпадающих списках/связках.\n\nСодержит минимум:\n- id (uid) организации\n- name\n- parent_id\n\nПримечание:\n- `uid` отдаём как `id` через alias для удобства фронта."},"OutletBankDetailsSchema":{"properties":{"name":{"type":"string","title":"Name","description":"Наименование юридического лица / торговой точки для банковских реквизитов.","examples":["ТОО «Ромашка»"]},"bin":{"type":"string","title":"Bin","description":"БИН/ИНН организации.","examples":["990740001234"]},"bic":{"type":"string","title":"Bic","description":"БИК банка.","examples":["ALMNKZKA"]},"iban":{"type":"string","title":"Iban","description":"Номер банковского счёта (IBAN).","examples":["KZ779999999999999999"]},"kbe":{"type":"string","title":"Kbe","description":"Код назначения платежа (КБе).","examples":["17"]},"bank_name":{"type":"string","title":"Bank Name","description":"Наименование банка, в котором открыт счёт.","examples":["АО «Казкоммерцбанк»"]},"contact":{"type":"string","title":"Contact","description":"Контактная информация по реквизитам (ФИО, телефон и т.п.).","examples":["Иванов Иван, +7 777 000 00 00"]}},"type":"object","title":"Outlet Bank Details Schema","description":"Банковские реквизиты торговой точки.\n\nВсе строковые поля по умолчанию возвращаются как пустая строка,\nдаже если в базе они были NULL. Это упрощает работу фронта:\nможно не проверять поле на None."},"OutletOwnerInfoSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"UUID владельца торговой точки."},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Имя владельца.","examples":["Gaylord"]},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Фамилия владельца.","examples":["Steambath"]},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name","description":"Отчество владельца.","examples":[]}},"type":"object","required":["uid"],"title":"Outlet Owner Info Schema","description":"Данные владельца торговой точки.\n\nВладелец определяется по связи `m2m_user_outlets.is_owner = true`.\nЕсли владелец не найден — поле `owner` в OutletProfileInfo будет None."},"OutletProfileInfoSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"UUID торговой точки."},"name":{"type":"string","title":"Name","description":"Название торговой точки."},"code":{"type":"integer","title":"Code","description":"Код ProTarget (tbl_outlets.code).","examples":[123456789]},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street","description":"Улица.","examples":["Otchopar"]},"office":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Office","description":"Офис/квартира.","examples":["34"]},"entrance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entrance","description":"Подъезд.","examples":["2"]},"floor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Floor","description":"Этаж.","examples":["3"]},"is_owner":{"type":"boolean","title":"Is Owner","description":"Является ли текущий пользователь владельцем этой ТТ.","default":false},"owner":{"anyOf":[{"$ref":"#/components/schemas/OutletOwnerInfoSchema"},{"type":"null"}],"description":"Владелец торговой точки (если определён)."}},"type":"object","required":["uid","name","code"],"title":"Outlet Profile Info Schema","description":"Профильная информация о торговой точке (ТТ) для отображения в меню/профиле.\n\nПоля берутся из `tbl_outlets`:\n- `code` — это “код ProTarget” (как в DDL: tbl_outlets.code).\n- адрес собирается из `street/office/entrance/floor`.\n\nДополнительно:\n- `is_owner` — флаг для текущего пользователя (из m2m_user_outlets.is_owner).\n- `owner` — данные владельца ТТ (если удалось найти)."},"OutletResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор торговой точки.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"name":{"type":"string","title":"Name","description":"Отображаемое название торговой точки.","examples":["Magnum Small №123"]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Контактный email торговой точки. Может быть пустой строкой.","default":"","examples":["outlet@example.com",""]},"status":{"$ref":"#/components/schemas/OutletStatusEnum","description":"Текущий статус торговой точки (активна, заблокирована и т.п.).","examples":["active","blocked"]},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street","description":"Адрес торговой точки (улица, дом).","default":"","examples":["ул. Абая, 10",""]},"location_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Location Id","description":"Идентификатор локации/города, к которой привязана торговая точка.","examples":["8d8c0cf5-94b2-4f32-8aab-9d4b9c9c3c10",null]},"latitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latitude","description":"Широта торговой точки (в формате WGS84).","examples":[43.238949,null]},"longitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Longitude","description":"Долгота торговой точки (в формате WGS84).","examples":[76.889709,null]},"bank_details":{"items":{"$ref":"#/components/schemas/OutletBankDetailsSchema"},"type":"array","title":"Bank Details","description":"Список банковских реквизитов торговой точки. Может быть пустым, если реквизиты не заполнены.","examples":[[{"bank_name":"АО «Казкоммерцбанк»","bic":"ALMNKZKA","bin":"990740001234","contact":"Иванов Иван, +7 777 000 00 00","iban":"KZ779999999999999999","kbe":"17","name":"ТОО «Ромашка»"}],[]]}},"type":"object","required":["id","name","status"],"title":"Outlet Response Schema","description":"Основная схема ответа для торговой точки.\n\nИспользуется, например, при:\n  - получении списка торговых точек пользователя;\n  - детализации информации по конкретной точке;\n  - привязке точки к заказу/маршруту и т.п."},"OutletSettingResponseSchema":{"properties":{"outlet_type_id":{"type":"string","format":"uuid4","title":"Outlet Type Id","description":"UUID типа торговой точки. Определяет бизнес-правила, опции оплаты, маршрутизацию и другие характеристики точки.","examples":["7e0f0c1b-d2ff-44ab-bf33-5b9fd58e52cb"]},"max_debts_limit":{"type":"number","title":"Max Debts Limit","description":"Максимально допустимая задолженность торговой точки. Если текущий долг превышает это значение, заказ может быть запрещён.","examples":[500000.0]},"expire_days":{"type":"integer","title":"Expire Days","description":"Количество дней, через которое истекает действие заказа или связанных данных.","examples":[7,14]},"min_order_limit":{"type":"number","title":"Min Order Limit","description":"Минимальная сумма заказа, ниже которой оформление невозможно.","examples":[10000.0]}},"type":"object","required":["outlet_type_id","max_debts_limit","expire_days","min_order_limit"],"title":"Outlet Setting Response Schema","description":"Настройки торговой точки, описывающие лимиты, тип точки\nи правила, влияющие на создание заказов."},"OutletStatusEnum":{"type":"string","enum":["active","notregistered","inactive","on_moderation"],"title":"OutletStatusEnum"},"PaymentMethodEnum":{"type":"string","enum":["cash","pos_terminal","credit","bank","free","cash_credit","pre_cash"],"title":"PaymentMethodEnum"},"PaymentStatusEnum":{"type":"string","enum":["pending","paid","failed"],"title":"PaymentStatusEnum"},"PlanTypeEnum":{"type":"string","enum":["quantity","amount"],"title":"PlanTypeEnum"},"PortfolioResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор портфеля (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Человекочитаемое название портфеля (например, 'Основной', 'Акционный', 'Ключевые клиенты')."},"store_type":{"anyOf":[{"$ref":"#/components/schemas/StoreTypeEnum"},{"type":"null"}],"description":"Тип магазина/канала, к которому относится портфель (значение перечисления StoreTypeEnum, сериализуется в строку)."},"organization":{"anyOf":[{"$ref":"#/components/schemas/OrganizationResponseSchema"},{"type":"null"}],"description":"Организация-владелец портфеля."}},"type":"object","required":["id"],"title":"Portfolio Response Schema","description":"Основная схема ответа для портфеля.\n\nИспользуется, например, при выдаче списка портфелей,\nпри выборе портфеля для корзины/заказа и т.п."},"ProductCheckoutSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор товара (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара."},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"Список URL-адресов изображений товара."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке."},"total_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Price","description":"Цена всей позиции без скидки (quantity × unit_price).","examples":[1500.0]},"unit_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Unit Price","description":"Цена за одну единицу товара без скидок.","examples":[150.0]},"base_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price","description":"Базовая цена товара по складу (до применения промо-скидок).","examples":[150.0]},"quantity":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Quantity","description":"Количество товара в корзине.","examples":[1,2,5.5]},"stock_remaining":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stock Remaining","description":"Доступный остаток товара на складах.","examples":[23,120,0]},"promotion":{"anyOf":[{"$ref":"#/components/schemas/PromotionCheckoutResponseSchema"},{"type":"null"}],"description":"Результат применения промо к позиции. Если промо не применено — null."},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация товара (артикул/код/и т.п.). Должна совпадать с тем, что отдаёт каталог. Обычно выбирается по organization_id (из портфеля/заказа)."},"added_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Added At","description":"Дата и время добавления товара в корзину.","examples":["2025-01-23T11:42:01Z"]}},"type":"object","required":["id"],"title":"Product Response Cart Schema","description":"Схема товара в корзине или заказе.\n\nВажно:\n  - базовые поля товара приходят из ProductBaseSchema;\n  - количество, остатки и \"обычные\" цены — производные значения;\n  - всё, что относится к применённой промоакции и итоговым ценам \"после промо\"\n    вынесено в объект `promotion`;\n  - reference — справочник товара в разрезе организации (как отдаёт каталог)."},"ProductGiftResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор товара (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара."},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"Список URL-адресов изображений товара."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке."},"internal_product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Internal Product Id","description":"Внутренний числовой идентификатор товара (например, ID из старой системы/1С).","examples":[12345]},"secondary_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Name","description":"Дополнительное/альтернативное наименование товара (например, на другом языке или сокращённый вариант).","examples":["Coca-Cola 1L"]},"secondary_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Description","description":"Дополнительное текстовое описание товара.","examples":["Описание на другом языке"]},"store_type":{"$ref":"#/components/schemas/StoreTypeEnum","description":"Тип магазина/канала, для которого доступен товар.","examples":["mixed"]},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Флаг активности товара. Если False — товар скрыт.","examples":[true]},"sizes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Sizes","description":"Размеры / объёмы товара (если есть варианты).","examples":[[500,1000]]},"weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Weight","description":"Вес товара.","examples":[1.0]},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining","description":"Доступный остаток товара по выбранным складам.","examples":[120]},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Текущая цена товара без учёта промо.","examples":[1500.0]},"discount_price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Discount Price","description":"Цена товара с учётом скидки (если применимо).","examples":[1350.0]},"portfolio":{"anyOf":[{"$ref":"#/components/schemas/PortfolioResponseSchema"},{"type":"null"}],"description":"Портфель, к которому относится товар (если определён). Возвращается в единственном экземпляре. Внутри портфеля заполняется `organization`.","examples":[{"id":"9f9d6a71-aaaa-bbbb-cccc-111122223333","name":"Основной портфель","organization":{"id":"3a2b1c0d-1111-2222-3333-444455556666","name":"ТОО Ромашка","parent":{"id":"11111111-2222-3333-4444-555555555555","name":"Холдинг Ромашка"}},"store_type":"retail"}]},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация товара (артикул/код/и т.п.). Должна совпадать с тем, что отдаёт каталог. Обычно выбирается по organization_id из портфеля.","examples":[{"article":"CC-1L","code":"ABC-123"}]}},"type":"object","required":["id","store_type"],"title":"Product Gift Response Schema","description":"Схема товара для использования в `gifts` у промо-ответов.\n\nНазначение:\n  - дать данные товара в формате, совместимом со стандартной выдачей;\n  - не тянуть зависимости промо-схем, чтобы избежать цикличных импортов.","examples":[{"description":"Газированный напиток","images":["https://cdn.example.com/img1.png"],"internal_product_id":12345,"is_active":true,"name":"Подарок: Coca-Cola","price":1500.0,"remaining":10,"secondary_name":"Coca-Cola 1L","sizes":[500,1000],"store_type":"mixed","uid":"b3fd0c7f-6d52-4c2e-9c8d-8c6ce3a1eaf1","weight":1.0}]},"ProductListResponseSchema":{"properties":{"page_number":{"type":"integer","exclusiveMinimum":0.0,"title":"Page Number","description":"Номер страницы (начинается с 1)","default":1,"examples":[1,2,5]},"page_size":{"type":"integer","maximum":500.0,"exclusiveMinimum":0.0,"title":"Page Size","description":"Количество элементов на одной странице (1–500)","default":20,"examples":[20,50,100]},"total_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Pages","description":"Общее количество страниц","examples":[12]},"total_items":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Items","description":"Общее количество элементов","examples":[234]},"results":{"items":{"$ref":"#/components/schemas/ProductResponseSchema"},"type":"array","title":"Results","description":"Список товаров на текущей странице результата пагинации. Каждый элемент представлен схемой ProductResponseSchema, включающей базовые данные товара, ссылки на справочники, остатки и промоинформацию."}},"type":"object","required":["results"],"title":"ProductListResponseSchema","description":"Ответ пагинации по продуктам.\n\nНаследует page_number / page_size из PaginationSchema\nи добавляет общее число элементов и страниц."},"ProductReferenceSchema":{"properties":{"organization_id":{"type":"string","format":"uuid4","title":"Organization Id","description":"Идентификатор организации, для которой актуальна данная запись справочника товара."},"code":{"type":"string","title":"Code","description":"Внутренний код товара в учётной системе организации."},"article":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Article","description":"Артикул товара (если используется в учётной системе)."},"display_with_zero":{"type":"boolean","title":"Display With Zero","description":"Флаг, отображать ли товар в витрине/каталоге при нулевом остатке.","default":false},"status":{"type":"boolean","title":"Status","description":"Флаг активности товара для данной организации. Если False — товар считается отключённым.","default":true},"publish_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Publish Status","description":"Флаг публикации товара во внешнем каталоге/витрине.","default":true},"icpu_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icpu Code","description":"Код ICPU или аналогичный внутренний классификатор товара (если используется)."},"product_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Group","description":"Группа/категория товара в системе организации."},"gtin_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gtin Code","description":"GTIN / штрих-код товара (EAN/UPC и т.п.)."},"manufacturer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manufacturer","description":"Наименование производителя товара."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Внешний идентификатор товара (например, ID в 1С/SAP/другой системе)."}},"type":"object","required":["organization_id","code"],"title":"Product Reference Schema","description":"Справочная информация о товаре в разрезе организации.\n\nИсточник данных: модель ProductReference.\n\nОдна запись описывает, как конкретный товар отображается и учитывается\nв рамках одной организации (свой код, артикул, статусы и т.п.)."},"ProductResponseSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"Уникальный идентификатор товара (UID в БД). Во внешнем API отдаётся под ключом 'id'."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара."},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"Список URL-адресов изображений товара."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке."},"internal_product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Internal Product Id","description":"Внутренний числовой идентификатор товара (например, ID из старой системы/1С).","examples":[12345]},"secondary_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Name","description":"Дополнительное/альтернативное наименование товара (например, на другом языке или сокращённый вариант).","examples":["Coca-Cola 1L"]},"secondary_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Description","description":"Дополнительное текстовое описание товара.","examples":["Описание на другом языке"]},"store_type":{"$ref":"#/components/schemas/StoreTypeEnum","description":"Тип магазина/канала, для которого доступен товар (значение StoreTypeEnum, сериализуется в строку).","examples":["mixed"]},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Флаг активности товара. Если False — товар скрыт из каталога.","examples":[true]},"sizes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Sizes","description":"Размеры / объёмы товара (список, если несколько вариантов).","examples":[[500,1000]]},"weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Weight","description":"Вес товара (как правило, в килограммах или граммах).","examples":[1.0]},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining","description":"Общий доступный остаток товара по выбранным складам.","examples":[120]},"routes":{"anyOf":[{"items":{"$ref":"#/components/schemas/ScheduledRouteResponseSchema"},"type":"array"},{"type":"null"}],"title":"Routes","description":"Список запланированных маршрутов, связанных с товаром (используется в сценариях доставки / обхода торговых точек). Отсутствует (null), если маршруты не подгружались или не заданы."},"promotion":{"anyOf":[{"$ref":"#/components/schemas/PromotionOverheadSchema"},{"$ref":"#/components/schemas/PromotionCartSchema"},{"$ref":"#/components/schemas/PromotionAssortmentSchema"},{"$ref":"#/components/schemas/PromotionMSLSchema"},{"$ref":"#/components/schemas/PromotionSKUSchema"},{"$ref":"#/components/schemas/PromotionCatalogSchema"},{"type":"null"}],"title":"Promotion","description":"Информация о применённой к товару промоакции. Конкретная структура зависит от типа промо (overhead, cart, catalog, assortment, msl, discount_sku_percentage)."},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price","description":"Текущая цена товара (без учёта скидок по промо).","examples":[1500.0]},"discount_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Price","description":"Цена товара с учётом применённых промо-скидок.","examples":[1350.0]},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация по товару. Содержит идентификаторы и атрибуты, используемые во внешних справочниках/интеграциях (например: код, артикул, GTIN, производитель, базовая цена и т.п.). Может отсутствовать (null), если справочная запись не определена для данного контекста."},"portfolio":{"anyOf":[{"$ref":"#/components/schemas/PortfolioResponseSchema"},{"type":"null"}],"description":"Портфель (ассортиментный контекст), к которому относится товар. Используется для группировки товаров и ограничения доступного ассортимента в рамках сценария выдачи. Может отсутствовать (null), если портфельный контекст не применяется или не определён."},"sales_plans":{"items":{"$ref":"#/components/schemas/ProductSalesPlanCardSchema"},"type":"array","title":"Sales Plans","description":"Активные SKU-планы продаж для карточки товара."}},"type":"object","required":["id","store_type"],"title":"Product Response Schema","description":"ProductResponseSchema — схема представления товара в ответах API.\n\nИспользуется в выдаче каталога/поиска, в корзине, при формировании заказа,\nа также в детальной карточке товара (в зависимости от конкретного endpoint).\n\nСхема объединяет:\n  - базовые атрибуты товара (идентификация, названия, описания, характеристики);\n  - вычисляемые значения, сформированные бизнес-логикой (остаток, цены, промо);\n  - связанные доменные объекты в упрощённом виде (портфель, справочная запись, маршруты).\n\nОсновные принципы:\n  - Поля `price`, `remaining`, `promotion`, `discount_price` могут заполняться\n    только на уровне сервиса/репозитория и не обязаны присутствовать во всех сценариях.\n  - Связанные сущности (`reference`, `portfolio`, `routes`) возвращаются в компактном виде\n    и могут отсутствовать (null), если не применимо для текущего контекста запроса.\n  - Схема предназначена для чтения (response), а не для создания/редактирования сущностей.\n\nКогда какие поля могут быть null:\n  - `remaining`: если остатки не рассчитывались или недоступны.\n  - `price`: если цена не определена для текущего контекста.\n  - `discount_price`: если скидка не применялась или нет промо-скидки.\n  - `promotion`: если промо-условия не применялись или не загружались.\n  - `reference`: если отсутствуют справочные данные в данном контексте.\n  - `portfolio`: если товар не привязан к портфелю в данном контексте.\n  - `routes`: если маршруты не запрашивались или недоступны.\n\nПоля:\n  - `id_product`: внутренний числовой идентификатор (legacy/внешний реестр).\n  - `name`, `name_2`: основное и дополнительное наименование.\n  - `description`, `description_2`: основное и дополнительное описание.\n  - `store_type`: канал/тип магазина, для которого актуален товар.\n  - `is_active`: флаг активности товара.\n  - `size`, `weight`: характеристики товара.\n  - `remaining`: доступный остаток.\n  - `price`: базовая цена.\n  - `discount_price`: цена с учётом скидок.\n  - `promotion`: сведения о применённой промо-акции (если есть).\n  - `reference`: справочная запись (код/артикул/прайс и т.п., если есть).\n  - `portfolio`: портфель/ассортиментный контекст (если есть).\n  - `routes`: маршруты/план доставки или обслуживания (если применимо)."},"ProductSalesPlanCardSchema":{"properties":{"mission_id":{"type":"string","format":"uuid4","title":"Mission Id","description":"UID миссии."},"name":{"type":"string","title":"Name","description":"Название миссии."},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Дата начала плана."},"finish_date":{"type":"string","format":"date","title":"Finish Date","description":"Дата окончания плана."},"plan_type":{"$ref":"#/components/schemas/PlanTypeEnum","description":"Исходный тип плана из админки."},"agent_percentage":{"type":"number","title":"Agent Percentage","description":"Процент агента.","default":0.0},"outlet_percentage":{"type":"number","title":"Outlet Percentage","description":"Процент торговой точки.","default":100.0},"current_quantity":{"type":"integer","title":"Current Quantity","description":"Сколько штук учтено по SKU в рамках периода плана: delivered минус returned.","default":0},"current_amount":{"type":"number","title":"Current Amount","description":"Сумма по SKU в рамках периода плана: сумма delivered минус доля суммы по returned-количеству.","default":0.0},"percentage":{"type":"number","title":"Percentage","description":"Прогресс шкалы 0..100 относительно последней ступени в штуках.","default":0.0},"current_level":{"type":"integer","title":"Current Level","description":"Последний достигнутый уровень: 0/1/2/3.","default":0},"next_quantity_value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Quantity Value","description":"Следующая целевая ступень в штуках."},"next_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Reward","description":"Бонус следующей ступени как в админке."},"next_outlet_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Outlet Reward","description":"Сколько реально заработает ТТ на следующей ступени."},"total_reward":{"type":"number","title":"Total Reward","description":"Бонус последней достигнутой ступени как в админке.","default":0.0},"total_outlet_reward":{"type":"number","title":"Total Outlet Reward","description":"Сколько реально заработала ТТ по последней достигнутой ступени.","default":0.0},"steps":{"items":{"$ref":"#/components/schemas/ProductSalesPlanStepSchema"},"type":"array","title":"Steps","description":"Ступени шкалы для карточки товара."}},"type":"object","required":["mission_id","name","start_date","finish_date","plan_type"],"title":"ProductSalesPlanCardSchema"},"ProductSalesPlanStepSchema":{"properties":{"level":{"type":"integer","title":"Level","description":"Номер уровня: 1/2/3."},"amount_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount Value","description":"Сырая сумма уровня из админки. Для quantity-плана может быть None."},"quantity_value":{"type":"integer","title":"Quantity Value","description":"Целевое количество товара для уровня, рассчитанное для карточки товара."},"reward":{"type":"number","title":"Reward","description":"Бонус уровня как в админке."},"outlet_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Outlet Reward","description":"Сколько реально заработает ТТ на этом уровне."},"is_reached":{"type":"boolean","title":"Is Reached","description":"Достигнут ли уровень по текущему количеству доставленного товара.","default":false}},"type":"object","required":["level","quantity_value","reward"],"title":"ProductSalesPlanStepSchema"},"PromotionAssortmentSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"reward_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Value","description":"Размер вознаграждения по промо (зависит от типа вознаграждения)."},"unique_qty":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unique Qty","description":"Минимальное количество уникальных товаров (SKU) для применения промо."},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Минимальное суммарное количество товаров для применения промо."},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Максимальное количество товаров, на которое распространяется промо (если ограничено)."},"min_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Amount","description":"Минимальная сумма по ассортименту для применения промо."},"max_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Amount","description":"Максимальная сумма, на которую действует промо (если ограничено)."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по промо (если reward_type — скидка)."},"promotion_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Type","description":"Тип промоакции. Для данной схемы ожидается значение 'assortment'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции."}},"type":"object","title":"Promotion Assortment Schema","description":"Промоакция типа «assortment» — условия на набор/ассортимент товаров:\n  - минимальное количество уникальных SKU,\n  - ограничение по количеству и сумме.\n\nИспользуется, когда важно разнообразие ассортимента, а не только объём по одному SKU."},"PromotionBrandItemSchema":{"properties":{"brand_id":{"type":"string","format":"uuid4","title":"Brand Id","description":"UUID бренда.","examples":["2f3a9b7c-5d44-4b6a-8f0e-1e2c3b4a5d6f"]},"brand_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name","description":"Название бренда.","examples":["Coca-Cola"]},"brand_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Image","description":"URL логотипа/картинки бренда.","examples":["https://cdn.example.com/brand.png"]},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Минимальное количество по бренду.","examples":[5,null]},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Максимальное количество по бренду.","examples":[10,null]},"min_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Amount","description":"Минимальная сумма по бренду.","examples":[5000.0,null]},"max_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Amount","description":"Максимальная сумма по бренду.","examples":[10000.0,null]},"products":{"items":{"$ref":"#/components/schemas/PromotionProductSchema"},"type":"array","title":"Products","description":"Список товаров бренда, участвующих в промо."}},"type":"object","required":["brand_id"],"title":"Promo Base Schema","description":"Блок бренда внутри промо типа `by_brand`.\n\nСодержит:\n  - идентификатор бренда и метаданные (название/логотип),\n  - условия по бренду (min/max quantity/amount),\n  - список товаров этого бренда (с ценой/остатком/скидкой).\n\nПример:\n```json\n{\n  \"brand_id\": \"2f3a9b7c-5d44-4b6a-8f0e-1e2c3b4a5d6f\",\n  \"brand_name\": \"Coca-Cola\",\n  \"brand_image\": \"https://cdn.example.com/brand.png\",\n  \"min_quantity\": 5,\n  \"max_quantity\": null,\n  \"min_amount\": null,\n  \"max_amount\": null,\n  \"products\": [ ... ]\n}\n```"},"PromotionCartSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"reward_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Value","description":"Размер вознаграждения по промо (зависит от reward_type)."},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Минимальное количество товара в корзине для применения промо."},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Максимальное количество товара, на которое распространяется промо (если ограничено)."},"min_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Amount","description":"Минимальная сумма по товарам для применения промо."},"max_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Amount","description":"Максимальная сумма, на которую действует промо (если ограничено)."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки, если тип вознаграждения — скидка."},"promotion_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Type","description":"Тип промоакции. Для данной схемы ожидается значение 'cart'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции."}},"type":"object","title":"Promotion Cart Schema","description":"Промоакция типа «cart» — условия применяются к корзине/позиции в корзине:\n  - по количеству товара,\n  - по сумме по товарам.\n\nМожет задавать минимальные/максимальные количество и сумму,\nа также размер вознаграждения (скидки и т.п.)."},"PromotionCatalogSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"buy_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Buy Quantity","description":"Минимальное количество товара для применения промо."},"reward_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Value","description":"Размер вознаграждения при выполнении условия по количеству."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по промо (если reward_type — скидка)."},"promotion_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Type","description":"Тип промоакции. Для данной схемы ожидается значение 'catalog'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции."}},"type":"object","title":"Promotion Catalog Schema","description":"Промоакция типа «catalog» — скидка/вознаграждение при покупке\nопределённого количества товара из каталога.\n\nЧасто используется для простого механизма:\n  - купи N единиц товара — получи скидку/бонус."},"PromotionCheckoutResponseSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"UID промоакции, применённой к товару.","examples":["ea48a435-941f-4bf2-a6db-d3df8217ff96",null]},"type":{"anyOf":[{"$ref":"#/components/schemas/PromotionTypeEnum"},{"type":"null"}],"description":"Тип применённой промоакции (cart, assortment, by_brand, ...).","examples":["cart","assortment",null]},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип награды промо для позиции. discount — скидка, bonus_product — бонусный товар.","examples":["discount","bonus_product",null]},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по активной промоакции (например 15.0 = 15%).","examples":[null,5.0,15.0]},"discount_limit_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Discount Limit Quantity","description":"Максимальное количество товара, на которое распространяется скидка по промо. Если None — лимит отсутствует или не применим к данному типу промо.","examples":[null,3]},"bonus_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bonus Quantity","description":"Количество бонусных товаров, полученных по промо для этой позиции.","examples":[null,1,2]},"unit_discount_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Unit Discount Price","description":"Цена за одну единицу товара с учётом промо.","examples":[120.0]},"final_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Final Price","description":"Итоговая цена всей позиции (quantity × unit_discount_price) с учётом промо.","examples":[1240.5]}},"type":"object","title":"Promotion Checkout Response Schema","description":"Информация о применённой промоакции для конкретной позиции (товара) в корзине/заказе.\n\nЭто \"результат применения промо\" на уровне позиции.\n\nСодержит:\n  - идентификацию промо (type/id),\n  - тип награды (discount/bonus),\n  - параметры скидки/бонуса,\n  - пересчитанные цены, которые получились из-за промо:\n      * unit_discount_price — цена за единицу с учётом промо\n      * final_price — итоговая цена всей позиции с учётом промо\n\nПравила заполнения:\n  - Если промо не применено — promotion=None на верхнем уровне.\n  - При reward_type=discount обычно заполняются discount_percent и цены.\n  - При reward_type=bonus_product обычно заполняется bonus_quantity,\n    а цены могут совпадать с обычными (или быть None — как у вас принято)."},"PromotionGiftsSchema":{"properties":{"promotion_id":{"type":"string","format":"uuid4","title":"Promotion Id","description":"UUID промоакции, предоставляющей подарки.","examples":["ea48a435-941f-4bf2-a6db-d3df8217ff96"]},"promotion_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Name","description":"Название промоакции.","examples":["Купи 5 — получи подарок"]},"gift_products":{"anyOf":[{"items":{"$ref":"#/components/schemas/CartGiftResponseSchema"},"type":"array"},{"type":"null"}],"title":"Gift Products","description":"Список подарков, доступных пользователю в рамках промо."},"gift_amount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Gift Amount","description":"Количество подарков, начисленных пользователю по данной промоакции.","examples":[1,2,5]}},"type":"object","required":["promotion_id"],"title":"Promotion Gifts Schema","description":"Схема ответа для промоакции типа \"подарки за покупку\",\nсодержащая список доступных подарков и ограничение по их количеству.\n\nИспользуется для:\n  - отображения подарков, доступных пользователю,\n  - выбора подарков в корзине,\n  - вычисления ограничений промо."},"PromotionListItemSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"UUID промоакции (UID в БД). Во внешнем API отдаётся под ключом `id`.","examples":["ea48a435-941f-4bf2-a6db-d3df8217ff96"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Название промоакции.","examples":["Скидка 15% на товар"]},"type":{"anyOf":[{"$ref":"#/components/schemas/PromotionTypeEnum"},{"type":"null"}],"description":"Тип промоакции (cart, assortment, by_brand, msl, discount_sku_percentage, catalog)."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции.","examples":["2025-01-01"]},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции.","examples":["2025-02-01"]},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип награды промоакции (discount/bonus_product и т.п.).","examples":["discount"]},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности: может ли промо применяться кратно при увеличении объёма закупки.","examples":[true,false]},"reward_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Value","description":"Величина награды промо. Для скидок обычно хранится в базисных пунктах (1500 = 15%). Для других типов награды может иметь иной смысл.","examples":[1500,500,null]},"unique_qty":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unique Qty","description":"Минимальное число уникальных SKU (assortment).","examples":[3,null]},"buy_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Buy Quantity","description":"Количество для условия покупки (catalog).","examples":[5,null]},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Минимальное количество для применения промо.","examples":[1,3,null]},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Максимальное количество, на которое распространяется промо.","examples":[10,null]},"min_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Amount","description":"Минимальная сумма для применения промо.","examples":[5000.0,null]},"max_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Amount","description":"Максимальная сумма для промо (если задана).","examples":[10000.0,null]},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки (если reward_type=discount и reward_value задан). Например 15.0 означает 15%.","examples":[15.0,null]},"products":{"items":{"$ref":"#/components/schemas/PromotionProductSchema"},"type":"array","title":"Products","description":"Список товаров промо."},"gifts":{"items":{"$ref":"#/components/schemas/ProductGiftResponseSchema"},"type":"array","title":"Gifts","description":"Список подарков промо (если есть)."},"brands":{"items":{"$ref":"#/components/schemas/PromotionBrandItemSchema"},"type":"array","title":"Brands","description":"Список брендов (только для by_brand)."},"msl_bundle":{"anyOf":[{"$ref":"#/components/schemas/PromotionMSLBundleSummarySchema"},{"type":"null"}],"description":"Если type=msl: агрегированная информация по набору (1 кнопка 'В корзину', суммы, состав, подарки)."}},"type":"object","required":["id"],"title":"Promo Base Schema","description":"Промоакция с составом для выдачи в `/promotions`.\n\nВозвращает:\n  - базовые метаданные промо (id, name, promo_type, даты),\n  - тип награды (reward_type) и величину награды (reward_value),\n  - условия промо (минимальные/максимальные значения),\n  - состав (products / gifts / brands).\n\nПримечания:\n  - `discount_percent` рассчитывается из reward_value, если reward_type=discount\n    и reward_value хранится в базисных пунктах (1500 = 15%).\n  - `products` заполняется для cart/assortment/catalog/msl/sku.\n  - `brands` заполняется только для by_brand.\n  - `gifts` заполняется только для промо с подарками (cart/assortment/msl/by_brand).\n\nПример (cart):\n```json\n{\n  \"id\": \"ea48a435-941f-4bf2-a6db-d3df8217ff96\",\n  \"name\": \"Скидка 15% на товар\",\n  \"promo_type\": \"cart\",\n  \"start_date\": \"2025-01-01\",\n  \"end_date\": \"2025-02-01\",\n  \"reward_type\": \"discount\",\n  \"multiplicity\": true,\n  \"reward_value\": 1500,\n  \"min_quantity\": 3,\n  \"max_quantity\": null,\n  \"min_amount\": null,\n  \"max_amount\": null,\n  \"discount_percent\": 15.0,\n  \"products\": [ ... ],\n  \"gifts\": [],\n  \"brands\": []\n}\n```"},"PromotionMSLBundleSummarySchema":{"properties":{"total_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Price","description":"Сумма без скидки за 1 набор."},"total_discount_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Discount Price","description":"Сумма со скидкой за 1 набор (если reward_type=discount)."},"total_discount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Discount","description":"Экономия за 1 набор (total_price - total_discount_price)."},"max_bundles":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Bundles","description":"Оценка максимального количества наборов по остаткам/лимитам SKU."},"products":{"items":{"$ref":"#/components/schemas/PromotionProductInBundleSchema"},"type":"array","title":"Products","description":"Состав набора (SKU + required_quantity)."},"gifts":{"items":{"$ref":"#/components/schemas/ProductGiftResponseSchema"},"type":"array","title":"Gifts","description":"Подарки промо (если есть). UI покажет блок 'Товары в подарок'."}},"type":"object","title":"Promo Base Schema","description":"Сводка по MSL-набору для UI:\n  - total_price: сумма цен за 1 набор (без скидки)\n  - total_discount_price: сумма цен со скидкой за 1 набор (если reward_type=discount)\n  - total_discount: абсолютная экономия за 1 набор (если reward_type=discount)\n  - max_bundles: максимально возможное число наборов к покупке (оценка по остаткам и max_quantity)"},"PromotionMSLSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"reward_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Value","description":"Размер вознаграждения по промо."},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Минимальное количество товара для применения MSL-промо."},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Максимальное количество товара, на которое распространяется MSL-промо."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по промо (если reward_type — скидка)."},"promotion_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Type","description":"Тип промоакции. Для данной схемы ожидается значение 'msl'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции."}},"type":"object","title":"Promotion MSL Schema","description":"Промоакция типа «msl» — условия для позиций минимального обязательного ассортимента (MSL).\n\nЧаще всего применяется к товарам, которые должны постоянно присутствовать на полке,\nс вознаграждением за выполнение плана по количеству."},"PromotionOverheadSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"buy_quantity1":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Buy Quantity1","description":"Минимальное количество товара для первой ступени промо."},"reward_value1":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reward Value1","description":"Величина вознаграждения для первой ступени (зависит от типа вознаграждения)."},"buy_quantity2":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Buy Quantity2","description":"Минимальное количество товара для второй ступени промо."},"reward_value2":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reward Value2","description":"Величина вознаграждения для второй ступени."},"buy_quantity3":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Buy Quantity3","description":"Минимальное количество товара для третьей ступени промо."},"reward_value3":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reward Value3","description":"Величина вознаграждения для третьей ступени."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по промоакции, если тип вознаграждения — скидка."},"promotion_type":{"anyOf":[{"$ref":"#/components/schemas/PromotionTypeEnum"},{"type":"null"}],"description":"Тип промоакции. Для данной схемы ожидается значение 'overhead'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции (скидка, подарок и т.п.)."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции. Если True — условия промо могут применяться несколько раз при увеличении объёма закупки."}},"type":"object","title":"Promotion Overhead Schema","description":"Промоакция типа «overhead» — надбавочная/ступенчатая акция.\n\nПример логики:\n  - при покупке buy_quantity1 единиц товара — применяется reward_value1;\n  - при покупке buy_quantity2 — применяется reward_value2;\n  - при покупке buy_quantity3 — применяется reward_value3.\n\nТип вознаграждения (reward_type) определяет, что именно значит reward_value:\n  - скидка в процентах,\n  - скидка в фиксированной сумме,\n  - подарок и т.п."},"PromotionProductInBundleSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"UUID товара (UID в БД). Во внешнем API отдаётся под ключом `id`.","examples":["b3fd0c7f-6d52-4c2e-9c8d-8c6ce3a1eaf1"]},"internal_product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Internal Product Id","description":"Внутренний числовой идентификатор товара (ID из старой системы/1С).","examples":[12345]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара.","examples":["Coca-Cola 1L","Молоко 2.5%"]},"secondary_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Name","description":"Дополнительное/альтернативное наименование товара.","examples":["Coca-Cola 1L (RU)"]},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"secondary_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Description","description":"Дополнительное описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке.","examples":[6,12]},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"URL(ы) изображений товара. Может быть строкой или списком строк — как хранится у вас в модели.","examples":[["https://cdn.example.com/img1.png"],"https://cdn.example.com/img1.png"]},"sizes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Sizes","description":"Размеры / объёмы товара (если несколько вариантов).","examples":[[500,1000]]},"weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Weight","description":"Вес товара (в вашей системе).","examples":[1.0]},"store_type":{"anyOf":[{"$ref":"#/components/schemas/StoreTypeEnum"},{"type":"null"}],"description":"Тип магазина/канала, для которого доступен товар.","examples":["mixed"]},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация товара (артикул/код/и т.п.). Должна совпадать с тем, что отдаёт каталог. Обычно выбирается по organization_id (из портфеля)."},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Текущая цена товара без учёта скидки по промо.","examples":[1500.0]},"discount_price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Discount Price","description":"Цена товара с учётом промо-скидки. Заполняется только если reward_type=discount.","examples":[1275.0,null]},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по позиции товара. Например 15.0 означает 15%.","examples":[15.0,null]},"remaining":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Remaining","description":"Суммарный доступный остаток товара по складам торговой точки.","examples":[0,12,120]},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Ограничение по минимальному количеству товара (например, MSL/brand-item).","examples":[1,5,null]},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Ограничение по максимальному количеству товара (если применимо).","examples":[10,null]},"min_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Min Amount","description":"Ограничение по минимальной сумме (если применимо).","examples":[5000.0,null]},"max_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Max Amount","description":"Ограничение по максимальной сумме (если применимо).","examples":[10000.0,null]},"portfolio":{"anyOf":[{"$ref":"#/components/schemas/PortfolioResponseSchema"},{"type":"null"}],"description":"Портфель, к которому относится товар (если определён). Возвращается в единственном экземпляре. Внутри портфеля заполняется `organization`."},"required_quantity":{"type":"integer","minimum":1.0,"title":"Required Quantity","description":"Количество SKU, требуемое на 1 набор (1 bundle).","default":1}},"type":"object","required":["id"],"title":"Promotion Product Schema","description":"Товар внутри набора (bundle).\n\nrequired_quantity — сколько штук этого SKU нужно купить для 1 набора.\nДля MSL берём из PromotionMSLBundleProduct.min_quantity.\nЕсли min_quantity не задан — считаем 1."},"PromotionProductSchema":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id","description":"UUID товара (UID в БД). Во внешнем API отдаётся под ключом `id`.","examples":["b3fd0c7f-6d52-4c2e-9c8d-8c6ce3a1eaf1"]},"internal_product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Internal Product Id","description":"Внутренний числовой идентификатор товара (ID из старой системы/1С).","examples":[12345]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Основное наименование товара.","examples":["Coca-Cola 1L","Молоко 2.5%"]},"secondary_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Name","description":"Дополнительное/альтернативное наименование товара.","examples":["Coca-Cola 1L (RU)"]},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Основное текстовое описание товара."},"secondary_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secondary Description","description":"Дополнительное описание товара."},"pieces_per_package":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Per Package","description":"Количество единиц товара в одной упаковке.","examples":[6,12]},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Images","description":"URL(ы) изображений товара. Может быть строкой или списком строк — как хранится у вас в модели.","examples":[["https://cdn.example.com/img1.png"],"https://cdn.example.com/img1.png"]},"sizes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Sizes","description":"Размеры / объёмы товара (если несколько вариантов).","examples":[[500,1000]]},"weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Weight","description":"Вес товара (в вашей системе).","examples":[1.0]},"store_type":{"anyOf":[{"$ref":"#/components/schemas/StoreTypeEnum"},{"type":"null"}],"description":"Тип магазина/канала, для которого доступен товар.","examples":["mixed"]},"reference":{"anyOf":[{"$ref":"#/components/schemas/ProductReferenceSchema"},{"type":"null"}],"description":"Справочная информация товара (артикул/код/и т.п.). Должна совпадать с тем, что отдаёт каталог. Обычно выбирается по organization_id (из портфеля)."},"price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Price","description":"Текущая цена товара без учёта скидки по промо.","examples":[1500.0]},"discount_price":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Discount Price","description":"Цена товара с учётом промо-скидки. Заполняется только если reward_type=discount.","examples":[1275.0,null]},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по позиции товара. Например 15.0 означает 15%.","examples":[15.0,null]},"remaining":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Remaining","description":"Суммарный доступный остаток товара по складам торговой точки.","examples":[0,12,120]},"min_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Quantity","description":"Ограничение по минимальному количеству товара (например, MSL/brand-item).","examples":[1,5,null]},"max_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Quantity","description":"Ограничение по максимальному количеству товара (если применимо).","examples":[10,null]},"min_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Min Amount","description":"Ограничение по минимальной сумме (если применимо).","examples":[5000.0,null]},"max_amount":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Max Amount","description":"Ограничение по максимальной сумме (если применимо).","examples":[10000.0,null]},"portfolio":{"anyOf":[{"$ref":"#/components/schemas/PortfolioResponseSchema"},{"type":"null"}],"description":"Портфель, к которому относится товар (если определён). Возвращается в единственном экземпляре. Внутри портфеля заполняется `organization`."}},"type":"object","required":["id"],"title":"Promotion Product Schema","description":"Товар внутри промоакции.\n\nИспользуется в `/promotions` для отображения состава промо:\n  - список товаров, к которым применима промоакция;\n  - рассчитанная обычная цена (`price`);\n  - рассчитанная цена со скидкой (`discount_price`) — если reward_type=discount;\n  - остаток (`remaining`) по складам торговой точки;\n  - справочник товара (`reference`) — артикул/код/прочие поля.\n\nПочему тут дублирование, а не наследование от ProductResponseSchema:\n  - чтобы не ловить цикличные импорты между schemas/products и schemas/promotions;\n  - чтобы контракт `/promotions` был стабильный и независимый.\n\nВажные правила:\n  - `price` и `remaining` возвращаются только если товар активен и не удалён (логика в репо/сервисе);\n  - `discount_price` заполняется только если промо реально является скидочным;\n  - `reference` должен быть одинаковым с каталогом (обычно reference зависит от organization/portfolio).\n\nПример:\n```json\n{\n  \"id\": \"b3fd0c7f-6d52-4c2e-9c8d-8c6ce3a1eaf1\",\n  \"name\": \"Coca-Cola 1L\",\n  \"images\": [\"https://cdn.example.com/coke.png\"],\n  \"reference\": {\n    \"code\": \"ABC-123\",\n    \"article\": \"CC-1L\"\n  },\n  \"price\": 1500.0,\n  \"discount_price\": 1275.0,\n  \"remaining\": 42,\n  \"min_quantity\": null,\n  \"max_quantity\": null,\n  \"min_amount\": null,\n  \"max_amount\": null,\n  \"discount_percent\": 15.0,\n  \"portfolio\": {\n    \"id\": \"....\",\n    \"organization\": { \"...\": \"...\" }\n  }\n}\n```"},"PromotionSKUSchema":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Id","description":"Идентификатор промоакции."},"min_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Amount","description":"Минимальная сумма по товару для применения промо."},"max_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Amount","description":"Максимальная сумма, на которую распространяется промо (если ограничено)."},"discount_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Discount Percent","description":"Процент скидки по промо."},"promotion_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotion Type","description":"Тип промоакции. Для данной схемы обычно используется 'discount_sku_percentage'."},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Дата начала действия промоакции."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Дата окончания действия промоакции."},"reward_type":{"anyOf":[{"$ref":"#/components/schemas/RewardTypeEnum"},{"type":"null"}],"description":"Тип вознаграждения по промоакции."},"multiplicity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiplicity","description":"Флаг мультипликативности промоакции."}},"type":"object","title":"Promotion SKU Schema","description":"Промоакция на уровень SKU — как правило, процентная скидка\nпри выполнении условий по сумме (discount_sku_percentage).\n\nПрименяется к конкретной позиции товара."},"PromotionTypeDiscountPromoEnum":{"type":"string","enum":["cart","assortment","msl","discount_sku_percentage","by_brand"],"title":"PromotionTypeDiscountPromoEnum","description":"Тип промо-акций для эндпойнта /promotions (скидочные/механики).\n\nДоступны:\n- cart\n- assortment\n- msl\n- discount_sku_percentage\n- by_brand\n\nВажно: overhead и catalog НЕ включены — это витринные/справочные промо,\nони фильтруются в /products через promo_type."},"PromotionTypeEnum":{"type":"string","enum":["fixed","overhead","cart","catalog","assortment","msl","discount_sku_percentage","by_brand"],"title":"PromotionTypeEnum"},"PromotionTypeProductFilterEnum":{"type":"string","enum":["overhead","catalog"],"title":"PromotionTypeProductFilterEnum","description":"Тип промо для фильтра в списке товаров (/products).\n\nДоступны только:\n- overhead\n- catalog\n\nВажно: cart/assortment/msl/discount_sku_percentage/by_brand сюда НЕ входят,\nпотому что такие скидки/механики нужно получать через отдельные эндпойнты промо."},"RemoveProductFromCartRequestSchema":{"properties":{"parent_organization_id":{"type":"string","format":"uuid4","title":"Parent Organization Id","description":"UUID родительской организации, передаётся под именем `parent_organization_id`.","examples":["3f45ac49-9b4a-4b5e-9035-4a8267a1f0de"]},"outlet_id":{"type":"string","format":"uuid4","title":"Outlet Id","description":"UUID торговой точки, для которой выполняется операция с корзиной.","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"portfolio_id":{"type":"string","format":"uuid4","title":"Portfolio Id","description":"UUID портфеля, в рамках которого удаляется товар из корзины.","examples":["d2a1126f-5a0f-48ea-b1fc-8a04f0cd5671"]},"product_id":{"type":"string","format":"uuid4","title":"Product Id","description":"UUID товара, который требуется удалить из корзины в указанном портфеле.","examples":["6c3c1d04-9862-4e09-bf0d-6ad8a9fbef24"]}},"type":"object","required":["parent_organization_id","outlet_id","portfolio_id","product_id"],"title":"Delete Product From Cart Request Schema","description":"Удаление ТОВАРА из корзины В РАМКАХ ПОРТФЕЛЯ.\nТребования:\n  - portfolio_id обязателен\n  - product_id обязателен\n\nПоле `organization_id` принимает значение из ключа `parent_organization_id`,\nчто позволяет корректно идентифицировать головную организацию пользователя."},"RewardTypeEnum":{"type":"string","enum":["discount","bonus_product"],"title":"RewardTypeEnum"},"RouteInfoSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"Уникальный идентификатор маршрута.","examples":["1e5af48a-246c-4ead-9b33-b38bc672bb75"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Название маршрута.","examples":["Маршрут №3 (Центр → Алтай)"]},"index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Index","description":"Порядковый номер маршрута в списке.","examples":[1,2,3]},"organization_id":{"type":"string","format":"uuid4","title":"Organization Id","description":"UUID организации, которой принадлежит маршрут.","examples":["51aaf244-bd43-49a9-8ec1-11a1fe60fa51"]},"type_of_transfer":{"type":"string","title":"Type Of Transfer","description":"Тип доставки / передачи (например, авто, пеший курьер).","examples":["car","foot"]},"default_courier_pinfl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Courier Pinfl","description":"PINFL курьера, закреплённого за маршрутом по умолчанию.","examples":["12345678901234"]},"default_car_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Car Number","description":"Номер автомобиля, используемого по умолчанию на маршруте.","examples":["123ABC01"]}},"type":"object","required":["uid","organization_id","type_of_transfer"],"title":"Route Info Schema","description":"Информация о маршруте (маршрут как сущность без привязки к дате).\n\nИспользуется для отображения списка маршрутов,\nвыбора маршрута в интерфейсе навигации и привязки к расписанию."},"ScheduledRouteResponseSchema":{"properties":{"route":{"$ref":"#/components/schemas/RouteInfoSchema","description":"Информация о маршруте (основная сущность маршрута)."},"weekday":{"$ref":"#/components/schemas/WeekdayEnum","description":"День недели, для которого назначен маршрут.","examples":["monday","wednesday"]},"real_date":{"type":"string","format":"date","title":"Real Date","description":"Конкретная дата, на которую запланирован маршрут.","examples":["2025-02-14"]}},"type":"object","required":["route","weekday","real_date"],"title":"Scheduled Route Response Schema","description":"Схема запланированного маршрута.\n\nВключает информацию о маршруте (RouteInfoSchema),\nдень недели и конкретную дату, на которую маршрут запланирован."},"StoreTypeEnum":{"type":"string","enum":["food","nonfood","ref","mixed"],"title":"StoreTypeEnum"},"UpdateOrderRequestSchema":{"properties":{"product_id":{"type":"string","format":"uuid4","title":"Product Id","description":"Товар, количество которого меняется."},"quantity":{"type":"integer","minimum":0.0,"title":"Quantity","description":"Новое количество товара (в упаковках)."},"selected_gifts":{"anyOf":[{"items":{"$ref":"#/components/schemas/OrderGiftRequestSchema"},"type":"array"},{"type":"null"}],"title":"Selected Gifts","description":"Выбранные подарки. Если null — сервис переиспользует прошлый выбор из заказа."}},"type":"object","required":["product_id","quantity"],"title":"Order Base Schema","description":"Запрос на обновление заказа: изменить количество одного товара и (опционально) выбрать подарки."},"UserMeResponseSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"Уникальный идентификатор пользователя (UUID4).","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Имя пользователя.","examples":["Иван"]},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Фамилия пользователя.","examples":["Иванов"]},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name","description":"Отчество пользователя, если есть.","examples":["Иванович",null]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Адрес электронной почты пользователя.","examples":["user@example.com",null]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Номер телефона пользователя в цифровом формате.","examples":["77015557788",null]},"iin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iin","description":"Индивидуальный идентификационный номер (ИИН).","examples":["990101300123"]},"role_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Role Id","description":"Идентификатор роли пользователя, если назначена.","examples":["2a0f01f4-9b7b-4f2e-8a21-7e40ba2cbf0b",null]},"is_active":{"type":"boolean","title":"Is Active","description":"Флаг активности учётной записи пользователя.","examples":[true]},"selfie_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selfie Url","description":"Публичный URL селфи пользователя (если загружено).","examples":["http://116.203.127.141:9090/face-photos/images/2026-02-02/....jpg",null]},"outlets":{"items":{"$ref":"#/components/schemas/OutletProfileInfoSchema"},"type":"array","title":"Outlets","description":"Список торговых точек, доступных пользователю."}},"type":"object","required":["uid","is_active"],"title":"User Me Response Schema","description":"Расширенный профиль пользователя с торговыми точками.\n\nРасширяет базовый профиль (`UserBaseSchema`) полем `outlets` — списком торговых точек,\nк которым пользователь имеет доступ. Для каждой точки может быть указан владелец,\nесли он определён в данных привязки.","examples":[{"email":"user@example.com","first_name":"Иван","is_active":true,"last_name":"Иванов","middle_name":"Иванович","outlets":[{"code":123456789,"floor":"3","is_owner":true,"name":"PRO TARGET SALES","office":"34","owner":{"first_name":"Абылай","uid":"0b7d1a8d-3b62-4c1c-b15e-6a4b3f43fb3c"},"street":"Otchopar","uid":"b2f5c3d1-2b8f-4c0b-9c01-8a5ce0ad2d10"}],"phone":"77001234567","role_id":"2a0f01f4-9b7b-4f2e-8a21-7e40ba2cbf0b","selfie_url":"http://116.203.127.141:9090/face-photos/images/2026-02-02/....jpg","uid":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}]},"UserResponseSchema":{"properties":{"uid":{"type":"string","format":"uuid4","title":"Uid","description":"Уникальный идентификатор пользователя (UUID4).","examples":["5af8c223-d882-4351-a33b-b0dc50eedd53"]},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Имя пользователя.","examples":["Иван"]},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Фамилия пользователя.","examples":["Иванов"]},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name","description":"Отчество пользователя, если есть.","examples":["Иванович",null]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Адрес электронной почты пользователя.","examples":["user@example.com",null]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Номер телефона пользователя в цифровом формате.","examples":["77015557788",null]},"iin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iin","description":"Индивидуальный идентификационный номер (ИИН).","examples":["990101300123"]},"role_id":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Role Id","description":"Идентификатор роли пользователя, если назначена.","examples":["2a0f01f4-9b7b-4f2e-8a21-7e40ba2cbf0b",null]},"is_active":{"type":"boolean","title":"Is Active","description":"Флаг активности учётной записи пользователя.","examples":[true]},"selfie_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selfie Url","description":"Публичный URL селфи пользователя (если загружено).","examples":["http://116.203.127.141:9090/face-photos/images/2026-02-02/....jpg",null]}},"type":"object","required":["uid","is_active"],"title":"User Response Schema","description":"DTO профиля пользователя (базовая версия, без связанных сущностей).\n\nНазначение\n----------\nВозвращает **только собственные поля пользователя** (uid, имя, контакты, роль, активность),\nбез “тяжёлых” связей и агрегаций (например, без outlets/owner и т.п.).\n\nГде используется\n---------------\nРекомендуемые сценарии:\n  - ответы эндпоинтов, где нужно подтвердить обновление профиля:\n    `PATCH /users/me` → возвращаем актуальный профиль;\n  - любые ручки/сервисы, где нужны базовые данные пользователя без joins.\n\nГде НЕ использовать\n-------------------\n- `GET /users/me`, если там требуется список торговых точек (`outlets`).\n  Для этого используйте отдельный DTO (например `UserMeResponseSchema`) с полем `outlets`.\n\nГарантии (контракт)\n-------------------\n- `uid` и `is_active` присутствуют всегда.\n- Остальные поля опциональны и могут быть `null`, если данных нет.\n- Модель поддерживает `from_attributes=True`, поэтому допускается валидация\n  напрямую из ORM-объекта (`UserResponseSchema.model_validate(user)`).\n\nСовместимость\n-------------\nЭтот DTO стабилен для фронта: отсутствие “лишних” полей (таких как `outlets`)\n— намеренное решение, чтобы:\n  - не выполнять лишние запросы/джоины,\n  - не раздувать ответы там, где это не нужно,\n  - не создавать неявные зависимости UI от вторичных данных.\n\nБезопасность\n------------\nDTO не содержит секретов (пароль/хэши/токены). Контакты (email/phone/iin) возвращаются\nтолько если это допустимо бизнес-логикой и политикой доступа (обычно только для “self”).","examples":[{"email":"user@example.com","first_name":"Иван","is_active":true,"last_name":"Иванов","middle_name":"Иванович","phone":"77001234567","role_id":"2a0f01f4-9b7b-4f2e-8a21-7e40ba2cbf0b","selfie_url":"http://116.203.127.141:9090/face-photos/images/2026-02-02/....jpg","uid":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}]},"UserUpdateRequestSchema":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Новое имя пользователя.","examples":["Иван"]},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Новая фамилия пользователя.","examples":["Иванов"]},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name","description":"Новое отчество пользователя.","examples":["Иванович",null]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Новый номер телефона пользователя.","examples":["77015557788",null]},"iin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iin","description":"Новый ИИН пользователя.","examples":["990101300123",null]}},"additionalProperties":false,"type":"object","title":"User Update Request Schema","description":"Тело запроса на обновление профиля пользователя.\n\nПозволяет изменить только «безопасные» поля профиля:\n  * имя, фамилию, отчество;\n  * телефон;\n  * ИИН."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WeekdayEnum":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"title":"WeekdayEnum"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}},"tags":[{"name":"Users","description":"Пользовательские операции (v1).\n\nТиповые сценарии:\n- получение профиля\n- обновление данных пользователя (если разрешено)\n- служебные методы для клиентских приложений\n\nℹ️ В v2 схема пользователя может быть обновлена/уточнена, поэтому новые поля желательно добавлять через v2."},{"name":"Organizations","description":"Организации (v1).\n\nСправочник и привязки, используемые бизнес-логикой:\n- данные организации\n- связь организации с пользователями/торговыми точками\n\n⚠️ Если меняется доменная модель — делаем это в v2, а v1 сохраняем совместимым."},{"name":"Outlets","description":"Торговые точки (v1).\n\nСодержит операции по доступным торговым точкам:\n- список доступных outlets\n- детали торговой точки\n- связанные справочники/настройки (если есть)\n\n🧭 Важно: права доступа к торговым точкам обычно завязаны на пользователя/организацию."},{"name":"Brands","description":"Бренды (v1).\n\nСправочник брендов каталога. Обычно используется для фильтрации и отображения в UI:\n- список брендов\n- детализация (если предусмотрена)\n\n📌 Справочник может кешироваться на клиенте."},{"name":"Categories","description":"Категории (v1).\n\nСправочник категорий каталога:\n- дерево/список категорий\n- получение подкатегорий\n\n🌿 Если у категорий есть иерархия — в ответах могут быть parent/children."},{"name":"Products","description":"Товары и каталог (v1).\n\nОсновные операции:\n- поиск и фильтрация\n- карточка товара\n- доступность/остатки (если предусмотрено)\n\n⚠️ Новые поля (например, новые правила ценообразования/наличия) лучше добавлять в v2."},{"name":"Promotions","description":"Промоакции (v1).\n\nОперации, связанные с промо/скидками/подарками:\n- список активных промо для торговой точки\n- состав промо (товары/бренды/подарки)\n- рассчитанные значения (цены, скидки, остатки)\n\n🧮 Промо-логика чувствительна к изменениям, поэтому миграция в v2 обычно делается аккуратно и поэтапно."},{"name":"Carts","description":"Корзина (v1, legacy).\n\nОперации с корзиной в старом формате:\n- добавление/удаление/изменение позиций\n- пересчёт корзины\n- получение текущего состояния\n\n⚠️ Некоторые ручки могут быть помечены `deprecated=True`.\nЕсли ручка возвращает `410 Gone`, это означает, что она оставлена как указатель — используйте эквивалент в v2."},{"name":"Orders","description":"Заказы (v1, частично legacy).\n\nОперации управления заказами в совместимом формате:\n- создание/подтверждение\n- получение списка и деталей заказа\n- статусы и история\n\n🚚 v2 может менять схему заказа и правила расчёта; v1 сохраняем ради совместимости."},{"name":"Missions","description":"Задания: планы продаж / мерчендайзинг (v1).\n\nОперации для получения заданий торговой точки:\n- `/missions?outlet_id=...&status=active` — активные задания\n- `/missions?outlet_id=...&status=completed` — завершённые (за последние 35 дней) с участием ТТ\n\n📈 План продаж может быть:\n- by_brand=true  — план на бренд (агрегация по товарам бренда)\n- by_brand=false — план на конкретный товар\n\n🎯 Прогресс считается по `plan_type`:\n- amount    — сумма заказов\n- quantity  — количество\n\n🪙 Награда (coins, NBL) для торговой точки учитывает `agent_percentage` (часть награды агента вычитается)."},{"name":"Notifications","description":"Центр уведомлений OUTLET-приложения (v1).\n\nСценарии:\n- регистрация/деактивация FCM-токена устройства;\n- лента уведомлений с курсорной пагинацией и фильтрами;\n- получение unread-count для бейджа в меню;\n- mark one / mark all as read.\n\nЛента возвращает карточки строго в контракте `NotificationItem`: `id`, `schema_version`, `app_type`, `recipient_type`, `notification_type`, `content_type`, `title`, `body`, `images`, `action`, `data`, `is_read`, `sent_at`."}]}