## Агрегация свойств позиций корзины

`POST /v1/basket-item-properties/aggregate`

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

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

| Поле | Назначение |
|---|---|
| `basketId` | Идентификатор позиции корзины. Источник: [`GET /v1/basket-items`](/docs/entities/basket-items/list). Для `groupBy` и числовых функций |
| `code` | Символьный код свойства — для `groupBy` |
| `sort` | Порядок сортировки — для `sum`, `avg`, `min`, `max` |
| `id` | Идентификатор свойства. Источник: [`GET /v1/basket-item-properties`](/docs/entities/basket-item-properties/list). Числовое поле |

Поля для `groupBy` перечислены в `data.aggregatable` метода [`GET /v1/basket-item-properties/fields`](/docs/entities/basket-item-properties/fields). Числовые функции принимают поля с типом `number`: `id`, `basketId`, `sort`.

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

| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
| `aggregate` | array | нет | Массив выражений с полями `field` и `function`. Функции: `count`, `sum`, `avg`, `min`, `max`. Для `count` поле — `"*"`. Если параметр не передан, используется `[{ "field": "*", "function": "count" }]` |
| `filter` | object | нет | Фильтрация по полям [`GET /v1/basket-item-properties/fields`](/docs/entities/basket-item-properties/fields).<br>[Синтаксис фильтрации](/docs/filtering). Пример: `{ "basketId": 1001 }` |
| `groupBy` | string \| string[] | нет | Поле или массив полей группировки: `basketId`, `code` |
| `groupOrderBy` | array | нет | Сортировка групп объектами с полями `field` и `direction`. В `field` — `count`, поле из `groupBy` или `<field>:<function>` из числового выражения `aggregate`. Направления: `asc`, `desc`. По умолчанию `desc`. Требует `groupBy` |
| `groupLimit` | number | нет | Число возвращаемых групп. Требует `groupBy` |

## Примеры

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

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/basket-item-properties/aggregate" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"aggregate":[{"field":"*","function":"count"}],"groupBy":"code","groupOrderBy":[{"field":"count","direction":"desc"}],"groupLimit":3}'
```

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

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/basket-item-properties/aggregate" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"aggregate":[{"field":"*","function":"count"}],"groupBy":"code","groupOrderBy":[{"field":"count","direction":"desc"}],"groupLimit":3}'
```

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

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/basket-item-properties/aggregate', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "aggregate": [
      {
        "field": "*",
        "function": "count"
      }
    ],
    "groupBy": "code",
    "groupOrderBy": [
      {
        "field": "count",
        "direction": "desc"
      }
    ],
    "groupLimit": 3
  }),
})

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

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

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/basket-item-properties/aggregate', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "aggregate": [
      {
        "field": "*",
        "function": "count"
      }
    ],
    "groupBy": "code",
    "groupOrderBy": [
      {
        "field": "count",
        "direction": "desc"
      }
    ],
    "groupLimit": 3
  }),
})

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

Для нескольких полей группировки передайте `groupBy` массивом.

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

Блоки ниже — тела запросов.

Количество свойств одной позиции корзины:

```json
{
  "filter": {
    "basketId": 1001
  },
  "aggregate": [
    {
      "field": "*",
      "function": "count"
    }
  ]
}
```

Сумма порядка сортировки и максимальный идентификатор свойства:

```json
{
  "aggregate": [
    {
      "field": "sort",
      "function": "sum"
    },
    {
      "field": "id",
      "function": "max"
    }
  ]
}
```

## Поля ответа

| Поле | Тип | Описание |
|---|---|---|
| `success` | boolean | Всегда `true` при успехе |
| `data.count` | number | Общее количество свойств под фильтр |
| `data.aggregates` | object | Числовые результаты по полю и функции. При одном `count` — пустой объект |
| `data.aggregates.<field>.truncated` | boolean | Приходит со значением `true`, если числовые результаты посчитаны по неполной выборке |
| `data.groups` | array | Приходит при `groupBy`. Каждая группа содержит значение поля группировки, `count` и `aggregates` |
| `data.groups[].count` | number | Количество обработанных записей группы |
| `data.groups[].aggregates` | object | Числовые результаты внутри группы |
| `data.groups[].truncated` | boolean | Приходит со значением `true` на неполной выборке. Количество и вычисления группы относятся к обработанным записям |
| `data.meta.totalRecords` | number | Общее количество записей под фильтр |
| `data.meta.recordsProcessed` | number | Количество обработанных записей, до 5000. Без группировки и числовых функций — `0` |
| `data.meta.truncated` | boolean | `true`, если прочитано меньше записей, чем обещает общее количество, в том числе из-за потолка 5000, либо чтение оборвалось ошибкой подстраницы. На полном ответе — `false`. Без группировки и числовых функций всегда `false` |
| `data.meta.recordsShortfall` | number | Число непрочитанных записей: `totalRecords - recordsProcessed`. Приходит при недоборе, в том числе при потолке 5000 |
| `data.meta.pageErrorSample` | object | Пример ошибки подстраницы. Приходит, если чтение оборвалось на ошибке |
| `data.meta.warnings` | array | При неполной выборке содержит предупреждение с кодом `AGGREGATE_TRUNCATED` |
| `data.meta.groupTotal` | number | Приходит при `groupBy`: число групп до применения `groupLimit` |
| `data.meta.groupsTruncated` | boolean | Приходит при `groupBy`. `true`, если `groupLimit` ограничил число возвращённых групп |

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

```json
{
  "success": true,
  "data": {
    "count": 249,
    "aggregates": {},
    "groups": [
      {
        "code": "CATALOG.XML_ID",
        "count": 69,
        "aggregates": {}
      },
      {
        "code": "PRODUCT.XML_ID",
        "count": 69,
        "aggregates": {}
      },
      {
        "code": "Color",
        "count": 39,
        "aggregates": {}
      }
    ],
    "meta": {
      "totalRecords": 249,
      "recordsProcessed": 249,
      "truncated": false,
      "groupTotal": 7,
      "groupsTruncated": true
    }
  }
}
```

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

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

400 — поле `name` недоступно для группировки:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "groupBy field 'name' is not aggregatable on this entity. Available: basketId, code."
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|---|---|---|
| 400 | `INVALID_PARAMS` | Неизвестная функция агрегирования |
| 400 | `INVALID_PARAMS` | Поле для `groupBy` не входит в `basketId`, `code` |
| 400 | `INVALID_PARAMS` | Числовая функция передана для строкового поля, например `value` |
| 422 | `BITRIX_ERROR` | Битрикс24 отклонил запрос. Причина указана в `error.message` |
| 503 | `BITRIX_TIMEOUT` | Битрикс24 не ответил вовремя |
| 502 | `BITRIX_UNAVAILABLE` | Нет соединения с Битрикс24 |
| 403 | `SCOPE_DENIED` | У API-ключа нет скоупа `sale` |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `TOKEN_MISSING` | Нет настроенных токенов доступа к Битрикс24 |
| 429 | `RATE_LIMITED` | Превышен лимит запросов. Повторите после срока из заголовка `Retry-After` |

Полный список общих ошибок API — [Коды ошибок](/docs/errors).

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

**Подсчёт и вычисления.** Один `count` без `groupBy` не выгружает записи. Числовые функции и группировка требуют чтения записей.

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

- [Список свойств позиций корзины](/docs/entities/basket-item-properties/list)
- [Поиск свойств позиций корзины](/docs/entities/basket-item-properties/search)
- [Поля свойства позиции корзины](/docs/entities/basket-item-properties/fields)
- [Синтаксис фильтрации](/docs/filtering)
- [Лимиты и оптимизация](/docs/optimization)
