Отчёты: OLAP продаж
Отчёты нужны, чтобы загружать данные заведения в свою аналитику: Power BI, Looker Studio, Metabase, Excel или собственный дашборд.
В разделе три вида методов:
| Что | Метод | Когда брать |
|---|---|---|
| OLAP продаж | POST /reports/olap | Выручка, блюда, оплаты, часы, официанты, каналы — в любых разрезах |
| Готовые отчёты | /reports/payroll, /shifts, /attendance, /products, /stop-list, /stock, /suppliers… | Зарплата, посещаемость, смены, товары с ценами и фудкостом, стоп-лист, склад, организации и поставщики с долгами, новые клиенты |
| Выгрузки | /export/cheques, /cheque-lines, /returns | Сырые чеки и позиции в своё хранилище |
Доступ
Отчёты содержат выручку, себестоимость и зарплаты, поэтому обычный ключ их не видит. Заведение включает доступ на конкретном ключе: Интеграции → API-ключи → ключ → «Доступ к отчётам».
Без галочки любой метод раздела вернёт 403:
{ "message": "У ключа нет доступа к отчётам. Включите его в кабинете: Интеграции → API-ключи" }
Отдельный ключ под аналитику
Не включайте отчёты на ключе мобильного приложения или сайта. Выпустите отдельный ключ для BI: его можно отозвать, не затронув приём заказов.
Как считаются цифры
Отчёты считаются по тем же правилам, что аналитика в кабинете Mison, поэтому ваши цифры совпадут с тем, что видит заведение.
- Бизнес-день. Чек относится к дню своего закрытия. Если у точки день начинается не в полночь (например, в 03:00), чек, закрытый 25.09 в 02:00, попадёт в 24.09.
- Только закрытые чеки. Открытые, отменённые и удалённые чеки в продажи не входят.
- Возврат — отдельный документ. Он ложится на дату возврата, а не на дату чека: продажи прошлого месяца после возврата не меняются. Поэтому в отчётах есть отдельные показатели
return_*иnet_*— выручка за вычетом возвратов. - Обслуживание и доставка входят в выручку чека (
ORDERS), но не в продажи товаров (SALES). - Сегодняшний день неполный. Его можно запрашивать, но цифры будут расти до закрытия дня. Для хранилища перезагружайте последние 2–3 дня каждую ночь: касса может досылать чеки с опозданием, а возврат может прийти через несколько дней.
- Деньги — в валюте заведения, числами с точностью до 2 знаков.
Поля отчёта
GET /reports/olap/columns
GET /reports/olap/columns?report_type=SALES
Список группировок и показателей. Без параметра — по всем видам отчёта сразу.
{
"report_type": "SALES",
"title": "Продажи товаров: строка чека",
"dimensions": [
{ "name": "date", "title": "Бизнес-день", "columns": ["date"] },
{ "name": "product", "title": "Товар (блюдо, модификатор)", "columns": ["product_id", "product_name"] }
],
"measures": [
{ "name": "quantity", "title": "Продано, в единицах товара" }
]
}
columns — какие колонки появятся в строке ответа, если группировать по этому полю.
OLAP-отчёт
POST /reports/olap
Вы выбираете вид отчёта, группировки и показатели — сервер возвращает таблицу. Метод ничего не меняет, повторять его безопасно.
{
"report_type": "SALES",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["date", "category"],
"measures": ["quantity", "net_amount", "cost", "return_amount", "net_revenue"],
"filters": {
"point_ids": ["f40d9ea6-bb66-4d8a-9185-219916b68224"],
"order_type_ids": [3]
},
"include_totals": true
}
{
"report_type": "SALES",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["date", "category"],
"measures": ["quantity", "net_amount", "cost", "return_amount", "net_revenue"],
"rows": [
{
"date": "2026-09-01",
"category_id": 117,
"category_name": "Донеры",
"quantity": 412,
"net_amount": 12360000,
"cost": 5190000,
"return_amount": 30000,
"net_revenue": 12330000
}
],
"totals": {
"quantity": 11840,
"net_amount": 355200000,
"cost": 149180000,
"return_amount": 420000,
"net_revenue": 354780000
},
"data_freshness": [
{
"point_id": "0e180d32-aa78-46b0-999e-4e10f4947d03",
"point_name": "Чиланзар",
"last_sync_at": "2026-10-05 21:26:04"
}
]
}
data_freshness приходит всегда, по каждой точке запроса: last_sync_at — когда касса последний раз отдала чек в облако (время сервера, Ташкент).
Отчёт за сегодня ещё не окончательный
Касса без связи копит чеки у себя и досылает позже. Пока она не досинхронизирована, отчёт за сегодня — а сразу после полуночи и за вчера — потом вырастет. Если last_sync_at у точки отстал от текущего времени, а точка ещё работает, предупредите об этом пользователя, а не выдавайте цифру за итог дня.
Параметры
| Поле | Обязательно | Описание |
|---|---|---|
report_type | да | SALES, ORDERS или PAYMENTS — см. ниже |
date_from, date_to | да | Период, YYYY-MM-DD, обе даты включительно. Не длиннее 92 дней |
group_by | нет | Группировки, до 7 полей. Пусто — одна строка итога за период |
measures | да | Показатели, хотя бы один |
filters | нет | Фильтры, см. ниже |
include_totals | нет | true — добавить totals, итог по всему отчёту. По умолчанию false |
order_by | нет | Порядок строк по показателю: {"measure": "gross_profit", "direction": "desc"} (asc или desc, по умолчанию desc). Показатель должен быть среди measures. Несовместим с grouping_sets |
limit | нет | Оставить первые N строк после сортировки: «топ-10 по прибыли», «5 худших» |
Без order_by строки отсортированы по полям группировки в порядке group_by.
Фильтры — только внутри `filters`
Точки и остальные фильтры передавайте в filters. Для point_ids, product_ids и других фильтров из таблицы ниже сервер принимает и верхний уровень запроса, но не полагайтесь на это: запрос без фильтра отдаёт цифры по всем точкам бренда.
Итоги не складывайте из строк
Число чеков и средние по строкам не суммируются: чек с двумя блюдами попадёт в строки обоих блюд. Нужен итог — просите include_totals: true, сервер посчитает его отдельно.
Фильтры
| Фильтр | Отчёты | Описание |
|---|---|---|
point_ids | все | UUID точек из GET /points. Пусто — все точки бренда |
order_type_ids | все | 1 зал, 2 с собой, 3 доставка |
order_type_sub_ids | все | Подтипы заказа из GET /order-type-subs |
source_ids | все | 0 — свои заказы заведения, иначе id интеграции-агрегатора |
category_ids | SALES | Категории товаров |
menu_ids | SALES | Папки меню из GET /reports/menu-folders, вместе с вложенными. Папка не этого бренда — 404 |
product_ids | SALES | Товары |
payment_type_ids | PAYMENTS | Типы оплат из GET /payment-types |
client_ids | все | Клиенты — client_id из группировки client |
organization_ids | все | Организации — organization_id из группировки organization |
region_ids | все | Регионы организаций — region_id из группировки region |
district_ids | все | Районы организаций — district_id из группировки district |
Все фильтры — массивы чисел (у point_ids — строк). Точка чужого бренда в point_ids вернёт 404, а не пустой отчёт.
Виды отчёта
Каждый вид считается по своей строке фактов. Разные виды не смешиваются в одном запросе — у каждого свои показатели.
SALES — продажи товаров
Строка — позиция чека: блюдо или модификатор. Для меню, ABC-анализа и фудкоста.
Группировки: date, week, month, weekday, hour, point, order_type, order_type_sub, source, waiter, cashier, client, organization, region, district, product, category, menu.
| Показатель | Описание |
|---|---|
quantity | Продано, в единицах товара |
amount | Сумма позиций по цене продажи, со скидками на саму позицию |
discount | Доля скидки на чек, пришедшаяся на позиции |
net_amount | Сумма после всех скидок: amount − discount |
cost | Себестоимость проданного на момент продажи |
gross_profit | Валовая прибыль: net_amount − cost |
cheques_count | Чеков, в которых есть позиция |
return_quantity | Возвращено, в единицах товара |
return_amount | Сумма возвратов |
return_cost | Себестоимость возвращённого |
net_revenue | Выручка за вычетом возвратов: net_amount − return_amount |
food_cost_percent | Фудкост, %: (cost − return_cost) / net_revenue × 100 |
margin_percent | Рентабельность, %: gross_profit / net_amount × 100. Ноль — товар продан по цене закупки |
avg_price | Средняя цена продажи единицы: amount / quantity — цена по чекам, со скидкой на саму позицию. Отвечает на «почём продаём» |
avg_cost | Средняя себестоимость единицы: cost / quantity |
clients_count | Уникальных клиентов в чеках с позицией |
organizations_count | Уникальных организаций в чеках с позицией |
Скидка на весь чек раскладывается на позиции пропорционально их сумме. Так net_amount по всем товарам сходится с суммой чеков после скидки.
Себестоимость берётся из чека — по средней цене ингредиентов на складе точки в момент продажи. Если ингредиент или заготовку списали в минус без прихода или приняли по нулевой цене, себестоимость блюда на этой точке занижена. Сравнивайте avg_cost в группировке point: сильный разброс между точками у одного блюда — повод проверить приходы.
ORDERS — чеки
Строка — чек целиком. Для выручки, среднего чека, гостей и сравнения каналов.
Группировки: date, week, month, weekday, hour, point, order_type, order_type_sub, source, waiter, cashier, client, organization, region, district.
| Показатель | Описание |
|---|---|
cheques_count | Закрытых чеков |
guests | Гостей |
revenue | Выручка: итог чеков к оплате с обслуживанием и доставкой |
goods_amount | Сумма позиций до скидки на чек |
discount | Скидки на чек |
delivery_amount | Стоимость доставки |
service_amount | Обслуживание: надбавка за обслуживание в чеках |
service_cheques_count | Чеков с обслуживанием |
service_percent | Процент обслуживания, %: service_amount / goods_amount × 100 по чекам с обслуживанием |
return_service_amount | Обслуживание, возвращённое гостям (по дате возврата) |
net_service_amount | Обслуживание за вычетом возвратов: service_amount − return_service_amount |
avg_cheque | Средний чек: revenue / cheques_count |
avg_guest | Выручка на гостя: revenue / guests |
returns_count | Документов возврата |
return_amount | Сумма возвратов |
net_revenue | Выручка за вычетом возвратов: revenue − return_amount |
clients_count | Уникальных клиентов, купивших за период |
organizations_count | Уникальных организаций, купивших за период |
SALES считается по позициям, ORDERS — по итогам чеков. Если у чека итог не совпадает с позициями (так бывает у единичных чеков, исправленных на кассе), суммы двух отчётов разойдутся на доли процента. Для выручки берите ORDERS, для товаров — SALES.
Обслуживание входит в revenue, а в SALES не попадает: это не товар. Сколько обслуживания собрал официант — service_amount с группировкой waiter. Это сумма надбавки в чеках, а не зарплата: какая её доля положена официанту, задаёт его мотивационная программа — см. GET /reports/payroll.
PAYMENTS — оплаты
Строка — оплата чека. Чек, оплаченный наличными и картой, даёт две строки.
Группировки: date, week, month, weekday, hour, point, order_type, order_type_sub, source, waiter, cashier, client, organization, region, district, payment_type, payment_category.
| Показатель | Описание |
|---|---|
amount | Оплачено. Сдача вычтена из наличных |
cheques_count | Чеков с этой оплатой |
return_amount | Возвращено деньгами этим типом оплаты |
net_amount | Оплачено за вычетом возвратов: amount − return_amount |
clients_count | Уникальных клиентов в чеках с этой оплатой |
organizations_count | Уникальных организаций в чеках с этой оплатой |
Сумма amount по всем типам оплат равна revenue из ORDERS за тот же период.
Почему оплаты — отдельный отчёт
В оплатах нет себестоимости и позиций намеренно. Если группировать продажи по типу оплаты, чек с двумя оплатами посчитает свою себестоимость дважды, и фудкост завысится. Нужна доля оплат в выручке — стройте её из PAYMENTS и ORDERS.
Группировки
| Поле | Колонки в строке | Описание |
|---|---|---|
date | date | Бизнес-день, YYYY-MM-DD |
week | week | Неделя — дата её понедельника |
month | month | Месяц, YYYY-MM |
weekday | weekday | День недели: 1 понедельник … 7 воскресенье |
hour | hour | Час закрытия чека, 0–23. У возврата — час возврата |
point | point_id, point_name | Точка, point_id — UUID |
order_type | order_type_id, order_type_name | Зал, с собой, доставка |
order_type_sub | order_type_sub_id, order_type_sub_name | Подтип заказа — канал продаж заведения |
source | source_id, source_name | 0 — свои заказы, иначе агрегатор |
waiter | waiter_id, waiter_name | Официант чека |
cashier | cashier_id, cashier_name | Кассир чека |
client | client_id, client_name, client_phone | Клиент из базы клиентов, привязанный к чеку |
organization | organization_id, organization_name | Организация, на которую чек оплачен в долг: юрлицо, агрегатор (Uzum, Яндекс) |
region | region_id, region_name | Регион организации чека — из её карточки |
district | district_id, district_name | Район организации чека — из её карточки |
product | product_id, product_name, unit_name | Товар и его единица измерения (кг, шт, л) — в ней считается quantity |
category | category_id, category_name | Категория товара |
menu | menu_id, menu_name, menu_path | Папка меню товара — раздел вроде «Напитки» или «Мангал». menu_path — путь от верхней папки: «Блюда / Мангал» |
payment_type | payment_type_id, payment_type_name | Тип оплаты |
payment_category | payment_category_id, payment_category_name | Категория оплаты |
Возврат наследует у исходного чека тип заказа, подтип, источник, официанта и кассира, а дату и час берёт свои.
Идентификатор может быть null, например у чека без официанта. У чека без клиента client_id — null, у чека, не оплаченного в долг организации, null — organization_id. Регион и район берутся из карточки организации, поэтому у чека без организации они тоже null.
Сколько клиентов и организаций купило
clients_count и organizations_count считают уникальных покупателей, возвраты в них не входят. Один клиент за неделю — это 1, сколько бы чеков у него ни было, поэтому по строкам эти показатели не складываются: итог за период берите из include_totals. Новые клиенты и организации (регистрации) — отдельный отчёт GET /reports/registrations.
Категория или папка меню
Категории заведения заводят не всегда, а папки меню есть у всех: в них товары разложены на кассе. Если category возвращает одну строку с category_id: null, категорий у бренда нет — группируйте по menu. Раздел, о котором спрашивает пользователь («сколько продано с мангала»), ищите по названию в GET /reports/menu-folders и передавайте его id в menu_ids.
Папки меню
GET /api/integrations/v1/reports/menu-folders
Authorization: Bearer <access_token>
Действующие папки меню бренда — id для фильтра menu_ids и группировки menu. Меню общее на бренд, поэтому без point_id.
{
"folders": [
{ "id": 43037, "name": "Блюда", "parent_id": null, "path": "Блюда", "products_count": 32 },
{ "id": 43580, "name": "MANGAL", "parent_id": 43037, "path": "Блюда / MANGAL", "products_count": 19 }
]
}
| Поле | Описание |
|---|---|
id | Id папки — для menu_ids |
name | Название папки |
parent_id | Родительская папка, null — верхний уровень |
path | Путь от верхней папки |
products_count | Действующих товаров прямо в папке, без вложенных |
Фильтр menu_ids берёт папку вместе со всеми вложенными. Товар, проданный из папки, которую потом удалили, в отчёте остаётся: группировка menu покажет и удалённую папку.
Примеры
Выручка по дням и каналам:
{
"report_type": "ORDERS",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["date", "order_type", "source"],
"measures": ["cheques_count", "revenue", "avg_cheque", "net_revenue"]
}
Тепловая карта по дням недели и часам:
{
"report_type": "ORDERS",
"date_from": "2026-07-01",
"date_to": "2026-09-30",
"group_by": ["weekday", "hour"],
"measures": ["cheques_count", "revenue"]
}
ABC-анализ блюд с фудкостом:
{
"report_type": "SALES",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["product"],
"measures": ["quantity", "net_revenue", "gross_profit", "food_cost_percent"],
"include_totals": true
}
Самые и наименее прибыльные товары (запрос с order_by и limit; для «наименее» — "direction": "asc"):
{
"report_type": "SALES",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["product"],
"measures": ["quantity", "gross_profit", "margin_percent"],
"order_by": { "measure": "gross_profit", "direction": "desc" },
"limit": 10
}
gross_profit — разница выручки и себестоимости. Себестоимость берётся из остатков склада: если у товара средняя цена закупки равна цене продажи, прибыль окажется нулевой — это данные склада, а не ошибка отчёта.
Кому в долг и сколько отпущено (организации-должники с товарами; строки без organization_id — обычные продажи, их отбросьте):
{
"report_type": "SALES",
"date_from": "2026-10-01",
"date_to": "2026-10-01",
"group_by": ["organization", "product"],
"measures": ["quantity", "net_amount"],
"filters": { "category_ids": [101] }
}
Продажи официантов:
{
"report_type": "ORDERS",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["point", "waiter"],
"measures": ["cheques_count", "guests", "revenue", "avg_cheque"],
"filters": { "order_type_ids": [1] }
}
Обслуживание по официантам за неделю:
{
"report_type": "ORDERS",
"date_from": "2026-09-28",
"date_to": "2026-10-04",
"group_by": ["point", "waiter"],
"measures": ["cheques_count", "revenue", "service_cheques_count", "service_amount", "service_percent"],
"order_by": { "measure": "service_amount", "direction": "desc" },
"include_totals": true
}
Сколько продано из раздела меню (id папки — из GET /reports/menu-folders):
{
"report_type": "SALES",
"date_from": "2026-10-01",
"date_to": "2026-10-04",
"group_by": ["product"],
"measures": ["quantity", "net_amount"],
"filters": { "menu_ids": [43580] },
"include_totals": true
}
Наличные и безнал по дням:
{
"report_type": "PAYMENTS",
"date_from": "2026-09-01",
"date_to": "2026-09-30",
"group_by": ["date", "payment_category"],
"measures": ["amount", "return_amount", "net_amount"]
}
Ограничения
| Ограничение | Значение |
|---|---|
| Период одного запроса | до 92 дней |
Полей в group_by | до 7 |
| Строк в ответе | до 50 000 |
| Время построения | до 60 секунд |
| Запросов в минуту на методы отчётов, на API-ключ | 60 |
Если запрос не укладывается, сервер вернёт 400 с объяснением. Сократите период, уберите группировку или разбейте запрос по точкам. Длинную историю загружайте помесячно.