Отчёты: 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_idsSALESКатегории товаров
menu_idsSALESПапки меню из GET /reports/menu-folders, вместе с вложенными. Папка не этого бренда — 404
product_idsSALESТовары
payment_type_idsPAYMENTSТипы оплат из 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.

Группировки

ПолеКолонки в строкеОписание
datedateБизнес-день, YYYY-MM-DD
weekweekНеделя — дата её понедельника
monthmonthМесяц, YYYY-MM
weekdayweekdayДень недели: 1 понедельник … 7 воскресенье
hourhourЧас закрытия чека, 0–23. У возврата — час возврата
pointpoint_id, point_nameТочка, point_id — UUID
order_typeorder_type_id, order_type_nameЗал, с собой, доставка
order_type_suborder_type_sub_id, order_type_sub_nameПодтип заказа — канал продаж заведения
sourcesource_id, source_name0 — свои заказы, иначе агрегатор
waiterwaiter_id, waiter_nameОфициант чека
cashiercashier_id, cashier_nameКассир чека
clientclient_id, client_name, client_phoneКлиент из базы клиентов, привязанный к чеку
organizationorganization_id, organization_nameОрганизация, на которую чек оплачен в долг: юрлицо, агрегатор (Uzum, Яндекс)
regionregion_id, region_nameРегион организации чека — из её карточки
districtdistrict_id, district_nameРайон организации чека — из её карточки
productproduct_id, product_name, unit_nameТовар и его единица измерения (кг, шт, л) — в ней считается quantity
categorycategory_id, category_nameКатегория товара
menumenu_id, menu_name, menu_pathПапка меню товара — раздел вроде «Напитки» или «Мангал». menu_path — путь от верхней папки: «Блюда / Мангал»
payment_typepayment_type_id, payment_type_nameТип оплаты
payment_categorypayment_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 }
  ]
}
ПолеОписание
idId папки — для 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 с объяснением. Сократите период, уберите группировку или разбейте запрос по точкам. Длинную историю загружайте помесячно.