Агрегация предложений

POST /v1/quotes/aggregate

Подсчёт количества, сумма, среднее, минимум и максимум по коммерческим предложениям с фильтрацией и группировкой.

Стандартные поля:

  • amount — сумма предложения (числовое агрегирование имеет смысл)
  • stageId — стадия (для groupBy)
  • assignedById — ответственный (для groupBy)

Пользовательские поля (UF): UF-поля типов integer, double, money — для числовых функций; UF любого типа — для groupBy. Полный список UF-полей конкретного портала приходит в тексте ошибки INVALID_PARAMS, если передать несуществующее имя.

Поля запроса (body)

Параметр Тип Обяз. Описание
aggregate array нет Массив агрегаций. Каждый элемент: { "field": "amount", "function": "sum" }. Функции: count, sum, avg, min, max. Для count поле — "*". Без массива — только count
filter object нет Фильтрация по полям GET /v1/quotes/fields. Синтаксис фильтрации
groupBy string | string[] нет Поле или массив полей для группировки (максимум 5). Допустимые значения — из списка выше

Примеры

curl — личный ключ

Terminal
curl -X POST "https://vibecode.bitrix24.tech/v1/quotes/aggregate" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "aggregate": [
      { "field": "amount", "function": "sum" },
      { "field": "amount", "function": "avg" }
    ],
    "filter": { "assignedById": 1 },
    "groupBy": "stageId"
  }'

curl — OAuth-приложение

Terminal
curl -X POST "https://vibecode.bitrix24.tech/v1/quotes/aggregate" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "aggregate": [
      { "field": "amount", "function": "sum" },
      { "field": "amount", "function": "avg" }
    ],
    "filter": { "assignedById": 1 },
    "groupBy": "stageId"
  }'

JavaScript — личный ключ

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/quotes/aggregate', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    aggregate: [
      { field: 'amount', function: 'sum' },
      { field: 'amount', function: 'avg' },
    ],
    filter: { assignedById: 1 },
    groupBy: 'stageId',
  }),
})

const { success, data } = await res.json()
console.log('Всего предложений:', data.count)
console.log('По стадиям:', data.groups)

JavaScript — OAuth-приложение

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/quotes/aggregate', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    aggregate: [
      { field: 'amount', function: 'sum' },
      { field: 'amount', function: 'avg' },
    ],
    filter: { assignedById: 1 },
    groupBy: 'stageId',
  }),
})

const { success, data } = await res.json()

Для группировки по нескольким полям передайте массив: "groupBy": ["stageId", "assignedById"] (максимум 5).

Другие сценарии

Подсчёт записей — count с полем "*", самый быстрый запрос без выгрузки записей. Без массива aggregate результат тот же:

JSON
{ "aggregate": [{ "field": "*", "function": "count" }] }

Работа с пользовательскими полями (UF) — sum по UF + группировка по другому UF:

JSON
{
  "aggregate": [{ "field": "UF_CRM_DISCOUNT", "function": "sum" }],
  "groupBy": "UF_CRM_PRIORITY"
}

Поля ответа

Поле Тип Описание
success boolean Всегда true при успехе
data.count number Общее количество записей под фильтр
data.aggregates object Результаты агрегаций: { "amount": { "sum": 90000, "avg": 2500 } }
data.groups array Группы (только при groupBy). Каждый элемент: поля группировки + count + aggregates
data.meta.totalRecords number Общее количество записей под фильтр
data.meta.recordsProcessed number Сколько записей обработано для числовых агрегаций (максимум 5000)
data.meta.truncated boolean true, если под фильтр попало больше 5000 записей

Пример ответа

Ответ на основной запрос (агрегации + groupBy: "stageId"):

JSON
{
  "success": true,
  "data": {
    "count": 36,
    "aggregates": {
      "amount": { "sum": 90000, "avg": 2500 }
    },
    "groups": [
      {
        "stageId": "DRAFT",
        "count": 20,
        "aggregates": { "amount": { "sum": 50000 } }
      },
      {
        "stageId": "APPROVED",
        "count": 16,
        "aggregates": { "amount": { "sum": 40000 } }
      }
    ],
    "meta": {
      "totalRecords": 36,
      "recordsProcessed": 36,
      "truncated": false
    }
  }
}

Без groupBy поле data.groups в ответе отсутствует.

Пример ответа при ошибке

400 — неверное имя функции, несуществующее поле или groupBy по неаггрегируемому полю:

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "Field 'foo' not found. Available numeric fields: amount, stageId, assignedById"
  }
}

Ошибки

HTTP Код Описание
400 INVALID_PARAMS Невалидное имя функции, несуществующее поле, нечисловое поле в sum/avg/min/max, groupBy по неаггрегируемому полю или больше 5 полей в groupBy
401 TOKEN_MISSING API-ключ не имеет настроенных токенов
403 SCOPE_DENIED API-ключ не имеет скоупа crm

Полный список общих ошибок API — Ошибки.

Известные особенности

count vs числовые функции. count считается одним вызовом в Битрикс24 на любом объёме данных. Функции sum/avg/min/max подгружают записи постранично (максимум 5000) и считают на стороне Вайбкод — если под фильтр попадает больше 5000 записей, meta.truncated будет true, агрегация выполнится по первым 5000. Для точных счётчиков на больших выборках используйте count или сужайте фильтр.

Money-поля. UF-поля типа money хранятся в формате "сумма|валюта" ("1500|RUB") — агрегат извлекает числовую часть автоматически, складывать можно без парсинга.

Фильтрация по UF работает. В filter можно передавать любые поля — стандартные и пользовательские, любого типа. Например, { "filter": { "ufCrm_1234": "value" } } вернёт количество предложений с этим значением UF.

Смотрите также