Как получить воронки сделок со стадиями и семантикой
Scope:
crmКто может выполнять методы: любой пользователь с доступом к CRM
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Воронки сделок помогают разделить разные процессы продаж: новые продажи, продление договора, работу с партнерами или отдельные направления бизнеса. У каждой воронки свой набор стадий. У каждой стадии есть семантика — состояние сделки: в работе, успешно завершена или неуспешно завершена.
Семантика нужна для отчетов, автоматизации и фильтрации сделок. Например, по ней можно отделить активные сделки от выигранных и проигранных, даже если названия стадий отличаются в разных воронках.
В результате вы получите таблицу для каждой воронки сделок. В строках таблицы будут стадии, их названия и семантика.
Чтобы получить воронки сделок со стадиями и семантикой, вызываем последовательно два метода:
- crm.category.list — получаем массив
categoriesс воронками сделок и берем из негоidиname - crm.status.list — для каждой воронки передаем код стадий в
filter.ENTITY_IDи получаем стадии
Данные переходят между методами так:
idворонки изcategoriesопределяет код стадий- для основной воронки с
id = 0используем кодDEAL_STAGE - для дополнительной воронки с
id > 0используем кодDEAL_STAGE_{id} - сформированный код передаем в
filter.ENTITY_IDметода crm.status.list
1. Получаем список воронок сделок
Вызываем метод crm.category.list с параметром entityTypeId: 2, где 2 — идентификатор типа объекта сделка. Идентификаторы типов объектов CRM можно получить методом crm.enum.ownertype.
Список воронок фильтруется по правам доступа пользователя. Если пользователь не может читать определенную воронку, метод не вернет ее в ответе.
Как использовать примеры в документации
const categoryResponse = await $b24.actions.v2.call.make({
method: 'crm.category.list',
params: {
entityTypeId: 2,
},
requestId: 'category-list',
})
if (!categoryResponse.isSuccess) {
throw new Error(categoryResponse.getErrorMessages().join('; '))
}
const arCategory = categoryResponse.getData().result.categories.reduce((acc, item) => {
acc[item.id] = item.name
return acc
}, {})
// crm.category.list не имеет типизированной обертки — вызываем через core
$result = $sb->core->call('crm.category.list', ['entityTypeId' => 2])
->getResponseData()
->getResult();
$arCategory = array_column($result['categories'] ?? [], 'name', 'id');
categories = client.crm.category.list(entity_type_id=2).response.result.get("categories", [])
category_map = {item["id"]: item["name"] for item in categories}
В ответе метод возвращает массив categories с доступными пользователю воронками сделок, включая основную. У каждой воронки есть id — идентификатор воронки, name — название, isDefault — признак основной воронки.
{
"result": {
"categories": [
{
"id": 0,
"name": "Общая",
"sort": 100,
"entityTypeId": 2,
"isDefault": "Y"
},
{
"id": 7,
"name": "Продление договора",
"sort": 200,
"entityTypeId": 2,
"isDefault": "N"
}
]
},
"total": 2
}
Поле total показывает общее количество найденных воронок. Метод возвращает одну страницу ответа — до 50 записей. Примеры выше обрабатывают полученные в ответе элементы.
2. Получаем стадии и семантику для каждой воронки
Метод crm.status.list получает стадии по фильтру ENTITY_ID. Для сделок код стадий зависит от воронки:
DEAL_STAGE— стадии основной воронкиDEAL_STAGE_{id}— стадии дополнительной воронки, где{id}— идентификатор воронки
Берем поле id из ответа crm.category.list, формируем ENTITY_ID и вызываем crm.status.list с сортировкой по SORT. Например, для воронки с идентификатором 7 нужно передать DEAL_STAGE_7.
В ответе используем поля:
STATUS_ID— идентификатор стадииNAME— название стадииEXTRA.SEMANTICS— семантика стадииEXTRA.COLOR— цвет стадии
Значение EXTRA.SEMANTICS показывает группу стадии:
process— сделка находится в работеsuccess— сделка успешно завершенаfailure— сделка завершена неуспешноapology— отдельная группа неуспешно завершенных стадий
В примерах ниже используются данные, полученные на предыдущем шаге.
for (const [id, name] of Object.entries(arCategory)) {
const entityId = Number(id) > 0 ? `DEAL_STAGE_${id}` : 'DEAL_STAGE'
const stageResponse = await $b24.actions.v2.call.make({
method: 'crm.status.list',
params: {
order: { SORT: 'ASC' },
filter: { ENTITY_ID: entityId },
},
requestId: `status-list-${id}`,
})
if (!stageResponse.isSuccess) {
console.error(stageResponse.getErrorMessages().join('; '))
continue
}
for (const item of stageResponse.getData().result) {
console.log(name, item.STATUS_ID, item.NAME, item.EXTRA?.SEMANTICS)
}
}
foreach ($arCategory as $id => $name)
{
$entityId = $id > 0 ? 'DEAL_STAGE_' . $id : 'DEAL_STAGE';
$stages = $sb->getCRMScope()->status()->list(
['SORT' => 'ASC'],
['ENTITY_ID' => $entityId]
)->getStatuses();
foreach ($stages as $item)
{
echo $name . ': ' . $item->STATUS_ID . ': ' . $item->NAME
. ' - ' . ($item->EXTRA['SEMANTICS'] ?? '') . PHP_EOL;
}
}
for category_id, category_name in category_map.items():
entity_id = f"DEAL_STAGE_{category_id}" if int(category_id) > 0 else "DEAL_STAGE"
result_deal = client.crm.status.list(
order={"SORT": "ASC"},
filter={"ENTITY_ID": entity_id},
).response.result
for item in result_deal:
print(
category_name,
item.get("STATUS_ID", ""),
item.get("NAME", ""),
(item.get("EXTRA") or {}).get("SEMANTICS", ""),
)
В ответе метод возвращает массив стадий для указанного ENTITY_ID.
{
"result": [
{
"ENTITY_ID": "DEAL_STAGE_7",
"STATUS_ID": "NEW",
"NAME": "Новая",
"EXTRA": {
"SEMANTICS": "process",
"COLOR": "#39A8EF"
}
},
{
"ENTITY_ID": "DEAL_STAGE_7",
"STATUS_ID": "WON",
"NAME": "Сделка успешна",
"EXTRA": {
"SEMANTICS": "success",
"COLOR": "#7BD500"
}
},
{
"ENTITY_ID": "DEAL_STAGE_7",
"STATUS_ID": "LOSE",
"NAME": "Сделка провалена",
"EXTRA": {
"SEMANTICS": "failure",
"COLOR": "#FF5752"
}
}
],
"total": 3
}
Поле total показывает общее количество найденных стадий для указанного ENTITY_ID. Метод возвращает одну страницу ответа — до 50 записей. Примеры выше обрабатывают полученные в ответе элементы.
Полный пример кода
Пример выводит таблицу для каждой воронки сделок. В таблице показаны идентификатор стадии, название стадии и семантика.
// npm install @bitrix24/b24jssdk
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl('https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/')
const categoryResponse = await $b24.actions.v2.call.make({
method: 'crm.category.list',
params: {
entityTypeId: 2,
},
requestId: 'category-list',
})
if (!categoryResponse.isSuccess) {
throw new Error(categoryResponse.getErrorMessages().join('; '))
}
const arCategory = categoryResponse.getData().result.categories.reduce((acc, item) => {
acc[item.id] = item.name
return acc
}, {})
for (const [id, name] of Object.entries(arCategory)) {
const entityId = Number(id) > 0 ? `DEAL_STAGE_${id}` : 'DEAL_STAGE'
const stageResponse = await $b24.actions.v2.call.make({
method: 'crm.status.list',
params: {
order: { SORT: 'ASC' },
filter: { ENTITY_ID: entityId },
},
requestId: `status-list-${id}`,
})
if (!stageResponse.isSuccess) {
console.error(stageResponse.getErrorMessages().join('; '))
continue
}
const table = document.createElement('table')
const caption = document.createElement('caption')
caption.textContent = name
table.appendChild(caption)
const thead = document.createElement('thead')
const trHead = document.createElement('tr')
for (const text of ['STATUS ID', 'NAME', 'SEMANTICS']) {
const th = document.createElement('th')
th.textContent = text
trHead.appendChild(th)
}
thead.appendChild(trHead)
table.appendChild(thead)
const tbody = document.createElement('tbody')
for (const item of stageResponse.getData().result) {
const tr = document.createElement('tr')
if (item.EXTRA?.COLOR) {
tr.style.color = item.EXTRA.COLOR
}
for (const value of [item.STATUS_ID, item.NAME, item.EXTRA?.SEMANTICS]) {
const td = document.createElement('td')
td.textContent = value ?? ''
tr.appendChild(td)
}
tbody.appendChild(tr)
}
table.appendChild(tbody)
document.body.appendChild(table)
}
$b24.destroy()
<?php
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('b24');
$log->pushHandler(new StreamHandler('php://stdout'));
$sb = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/');
// crm.category.list не имеет типизированной обертки — вызываем через core
$result = $sb->core->call('crm.category.list', ['entityTypeId' => 2])
->getResponseData()
->getResult();
$arCategory = array_column($result['categories'] ?? [], 'name', 'id');
foreach ($arCategory as $id => $name):
$entityId = $id > 0 ? 'DEAL_STAGE_' . $id : 'DEAL_STAGE';
$stages = $sb->getCRMScope()->status()->list(
['SORT' => 'ASC'],
['ENTITY_ID' => $entityId]
)->getStatuses();
if (!empty($stages)):
?>
<table>
<caption><?=htmlspecialchars((string)$name, ENT_QUOTES, 'UTF-8')?></caption>
<thead>
<tr>
<th>STATUS ID</th>
<th>NAME</th>
<th>SEMANTICS</th>
</tr>
</thead>
<tbody>
<?php foreach ($stages as $item): ?>
<?php
$statusId = htmlspecialchars((string)($item->STATUS_ID ?? ''), ENT_QUOTES, 'UTF-8');
$stageName = htmlspecialchars((string)($item->NAME ?? ''), ENT_QUOTES, 'UTF-8');
$semantics = htmlspecialchars((string)($item->EXTRA['SEMANTICS'] ?? ''), ENT_QUOTES, 'UTF-8');
$color = (string)($item->EXTRA['COLOR'] ?? '');
$colorStyle = preg_match('/^#[0-9A-Fa-f]{6}$/', $color) ? ' style="color:' . $color . '"' : '';
?>
<tr<?=$colorStyle?>>
<td><?=$statusId?></td>
<td><?=$stageName?></td>
<td><?=$semantics?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php endforeach; ?>
from b24pysdk import BitrixWebhook, Client
from b24pysdk.errors import BitrixAPIError
client = Client(
BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
)
try:
categories = client.crm.category.list(entity_type_id=2).response.result.get("categories", [])
category_map = {item["id"]: item["name"] for item in categories}
for category_id, category_name in category_map.items():
entity_id = f"DEAL_STAGE_{category_id}" if int(category_id) > 0 else "DEAL_STAGE"
result_deal = client.crm.status.list(
order={"SORT": "ASC"},
filter={"ENTITY_ID": entity_id},
).response.result
print(category_name)
print("STATUS ID\tNAME\tSEMANTICS")
for item in result_deal:
print(
"\t".join(
[
str(item.get("STATUS_ID", "")),
str(item.get("NAME", "")),
str((item.get("EXTRA") or {}).get("SEMANTICS", "")),
]
)
)
except BitrixAPIError as error:
print(error)
Если результат пустой или возникла ошибка
Если crm.category.list или crm.status.list вернул ошибку, проверьте авторизацию и права пользователя. Для работы сценария нужен доступ к CRM и scope crm.
Если crm.category.list вернул пустой массив categories, пользователь не видит доступные воронки сделок. Проверьте права пользователя на чтение CRM и повторите сценарий с первого шага.
Если crm.status.list вернул пустой массив стадий, проверьте значение ENTITY_ID:
- для основной воронки с
id = 0передавайтеDEAL_STAGE - для дополнительной воронки с
id > 0передавайтеDEAL_STAGE_{id} - не передавайте
DEAL_STAGE_0
После исправления ENTITY_ID повторите второй шаг для этой воронки.
Если в ответе есть поле total, но обработаны не все элементы, учтите ограничение одной страницы ответа. Примеры в туториале обрабатывают только элементы, полученные в текущем ответе.
Проверяем результат
После запуска примера на странице появятся таблицы по полученным воронкам сделок. Заголовок таблицы — название воронки. В строках таблицы отображаются стадии этой воронки:
STATUS ID— код стадии, который можно использовать в полях и фильтрах сделокNAME— название стадии в интерфейсе CRMSEMANTICS— группа стадии:process,success,failureилиapology
Если в Битрикс24 есть только основная воронка, пример выведет одну таблицу. Если созданы дополнительные воронки, для каждой из них будет своя таблица.
Основная воронка имеет id = 0. Для нее код стадий — DEAL_STAGE, без суффикса _0.