Как работать с полем Привязка к разделам инфоблоков
Scope:
crm,lists,catalogКто может выполнять методы: права разных модулей складываются, нужны все перечисленные
- crm.deal.userfield.add — администратор CRM
- crm.deal.update и crm.deal.get — пользователь с правом «изменения» и «чтения» сделок
- lists.get и lists.section.get — пользователь с правом «Чтение» для нужного списка
- lists.section.add — пользователь с правом «Изменение» для нужного списка
- catalog.catalog.list и catalog.section.list — администратор
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Поле «Привязка к разделам инфоблоков» хранит идентификаторы разделов: папок списка или разделов товарного каталога. В ответе методов такое поле выглядит как число 237 или массив чисел [19, 33], названий разделов там нет.
Раздел — это узел дерева. У него есть родитель и уровень вложенности, а привязка хранит только сам узел: вложенные в него разделы в значение не попадают. Если нужны и дочерние разделы, соберите их сами по идентификатору родителя.
Поле привязывается к одному инфоблоку через настройку IBLOCK_ID, поэтому идентификатор инфоблока нужно получить заранее: для списков — методами группы lists.*, для каталога — методами группы catalog.*.
Разберем на примере сделок. Заведем два поля: одиночное со ссылкой на раздел списка и множественное со ссылкой на разделы каталога. Заполним их в конкретной сделке, прочитаем обратно и развернем идентификаторы в названия разделов.
Сценарий состоит из пяти шагов.
- Найдем инфоблок методами lists.get и catalog.catalog.list
- Создадим поля привязки методом crm.deal.userfield.add
- Получим идентификаторы разделов методами lists.section.get и catalog.section.list
- Запишем значения методом crm.deal.update
- Развернем значения в названия методами crm.deal.get, lists.section.get и catalog.section.list
В результате в сделке будут заполнены оба поля, а по сохраненным идентификаторам вы получите названия разделов.
Что нужно до начала
Подготовьте данные сценария:
- Инфоблок, к которому привязываем. Это список Битрикс24 или товарный каталог. Его идентификатор получим на первом шаге
- Разделы в этом инфоблоке. В списке их может не быть: тогда lists.section.get вернет пустой массив, а разделы придется создать
- Сделка, в которой заполним поля. Понадобится ее
id. Сами поля создаются сразу для всех сделок, а не для одной - Доступ к REST. Вебхук или приложение со scope
crm,listsиcatalog. Поля создает только администратор CRM, разделы каталога тоже доступны только администратору
Вебхук выполняет запросы с правами создавшего его пользователя. Если у этого пользователя нет доступа к списку или к каталогу, методы вернут ошибку доступа, хотя сами методы вызваны верно.
Дальше в примерах используем список с идентификатором 123, товарный каталог с идентификатором 25 и сделку 8419. В вашем Битрикс24 эти значения будут другими: идентификаторы инфоблоков возьмите из ответов первого шага, идентификатор сделки — из своей сделки.
Для серверных JS-примеров с B24Hook нужен Node.js 18, 20, 22 или новее, для новых проектов — 22 или новее. B24JsSDK — ES module: сохраните код в файле .mjs или добавьте "type": "module" в package.json. Для примеров с b24pysdk нужен Python 3.9 или новее.
Храните путь вебхука в переменной окружения и не публикуйте его в открытом коде.
Как использовать примеры в документации
1. Найдем инфоблок и его идентификатор
Метод lists.get возвращает списки одного типа. Передайте параметр:
IBLOCK_TYPE_ID— тип инфоблока.lists— обычные списки,bitrix_processes— списки процессов
Метод catalog.catalog.list возвращает торговые каталоги без параметров.
В ответах сохраните:
IDсписка — передадим в настройкуIBLOCK_IDодиночного поляiblockIdкаталога — передадим в настройкуIBLOCK_IDмножественного поля
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
// Типизированных оберток для методов списков и каталога в SDK нет, вызываем их через ядро SDK
async function callMethod(method, params, requestId) {
const response = await $b24.actions.v2.call.make({
method,
params,
requestId
})
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
return response.getData().result
}
const lists = await callMethod(
'lists.get',
{ IBLOCK_TYPE_ID: 'lists' },
'lists-get'
)
const catalogs = await callMethod(
'catalog.catalog.list',
{},
'catalog-catalog-list'
)
console.table(lists.map((list) => ({ ID: list.ID, NAME: list.NAME })))
console.table(catalogs.catalogs)
<?php
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcher;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), new NullLogger()))
->initFromWebhook(getenv('B24_HOOK'));
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
// Типизированных оберток для методов списков и каталога в SDK нет, вызываем их через ядро SDK
function callMethod($serviceBuilder, string $method, array $params = []): mixed
{
return $serviceBuilder
->core
->call($method, $params)
->getResponseData()
->getResult();
}
$lists = callMethod($serviceBuilder, 'lists.get', ['IBLOCK_TYPE_ID' => 'lists']);
$catalogs = callMethod($serviceBuilder, 'catalog.catalog.list');
print_r($lists);
print_r($catalogs['catalogs']);
import os
from b24pysdk import BitrixWebhook
bitrix_token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token=os.environ["B24_HOOK_TOKEN"],
)
# B24_HOOK_TOKEN = 'USER_ID/TOKEN'
def call_method(method, params=None):
# Типизированных оберток для методов списков и каталога в SDK нет, вызываем их напрямую
return bitrix_token.call_method(
api_method=method,
params=params or {},
)["result"]
lists = call_method("lists.get", {"IBLOCK_TYPE_ID": "lists"})
catalogs = call_method("catalog.catalog.list")
print(lists)
print(catalogs["catalogs"])
Сокращенный ответ lists.get:
{
"result": [
{
"ID": "123",
"IBLOCK_TYPE_ID": "lists",
"NAME": "Обновленный список задач",
"ACTIVE": "Y"
}
],
"total": 1
}
Сокращенный ответ catalog.catalog.list:
{
"result": {
"catalogs": [
{
"id": 25,
"iblockId": 25,
"iblockTypeId": "CRM_PRODUCT_CATALOG",
"name": "Товарный каталог CRM",
"productIblockId": null
},
{
"id": 27,
"iblockId": 27,
"iblockTypeId": "CRM_PRODUCT_CATALOG",
"name": "Товарный каталог CRM (предложения)",
"productIblockId": 25
}
]
},
"total": 2
}
Каталог с заполненным productIblockId — это инфоблок торговых предложений. Для сценария берем каталог с productIblockId: null, в примере это 25.
2. Создадим поля привязки
Метод crm.deal.userfield.add создает пользовательское поле для всех сделок. Передайте параметры:
FIELD_NAME— код поля. Параметр обязательный. Если код не начинается сUF_CRM_, префикс добавится автоматическиUSER_TYPE_ID— тип поля, для привязки к разделам этоiblock_sectionMULTIPLE—Yдля нескольких значений,Nдля одногоEDIT_FORM_LABEL— название поля в карточке, по языкамSETTINGS.IBLOCK_ID— идентификатор инфоблока с первого шага. Без него метод вернет ошибкуSETTINGS.DISPLAY— вид элемента управления в карточке:UI,DIALOG,LISTилиCHECKBOX
Полный список типов полей возвращает метод crm.userfield.types. Для привязки к отдельным элементам инфоблока есть парный тип iblock_element.
В ответе сохраните идентификаторы созданных полей: по ним можно прочитать настройки методом crm.deal.userfield.get.
const listFieldId = await callMethod(
'crm.deal.userfield.add',
{
fields: {
FIELD_NAME: 'UF_CRM_IBS_LIST',
USER_TYPE_ID: 'iblock_section',
MULTIPLE: 'N',
EDIT_FORM_LABEL: { ru: 'Раздел списка', en: 'List section' },
SETTINGS: { IBLOCK_ID: 123, DISPLAY: 'UI' }
}
},
'userfield-add-list-section'
)
const catalogFieldId = await callMethod(
'crm.deal.userfield.add',
{
fields: {
FIELD_NAME: 'UF_CRM_IBS_CAT',
USER_TYPE_ID: 'iblock_section',
MULTIPLE: 'Y',
EDIT_FORM_LABEL: { ru: 'Разделы каталога', en: 'Catalog sections' },
SETTINGS: { IBLOCK_ID: 25, DISPLAY: 'UI' }
}
},
'userfield-add-catalog-section'
)
console.log(listFieldId, catalogFieldId)
$listFieldId = callMethod($serviceBuilder, 'crm.deal.userfield.add', [
'fields' => [
'FIELD_NAME' => 'UF_CRM_IBS_LIST',
'USER_TYPE_ID' => 'iblock_section',
'MULTIPLE' => 'N',
'EDIT_FORM_LABEL' => ['ru' => 'Раздел списка', 'en' => 'List section'],
'SETTINGS' => ['IBLOCK_ID' => 123, 'DISPLAY' => 'UI'],
],
]);
$catalogFieldId = callMethod($serviceBuilder, 'crm.deal.userfield.add', [
'fields' => [
'FIELD_NAME' => 'UF_CRM_IBS_CAT',
'USER_TYPE_ID' => 'iblock_section',
'MULTIPLE' => 'Y',
'EDIT_FORM_LABEL' => ['ru' => 'Разделы каталога', 'en' => 'Catalog sections'],
'SETTINGS' => ['IBLOCK_ID' => 25, 'DISPLAY' => 'UI'],
],
]);
print_r([$listFieldId, $catalogFieldId]);
list_field_id = call_method(
"crm.deal.userfield.add",
{
"fields": {
"FIELD_NAME": "UF_CRM_IBS_LIST",
"USER_TYPE_ID": "iblock_section",
"MULTIPLE": "N",
"EDIT_FORM_LABEL": {"ru": "Раздел списка", "en": "List section"},
"SETTINGS": {"IBLOCK_ID": 123, "DISPLAY": "UI"},
},
},
)
catalog_field_id = call_method(
"crm.deal.userfield.add",
{
"fields": {
"FIELD_NAME": "UF_CRM_IBS_CAT",
"USER_TYPE_ID": "iblock_section",
"MULTIPLE": "Y",
"EDIT_FORM_LABEL": {"ru": "Разделы каталога", "en": "Catalog sections"},
"SETTINGS": {"IBLOCK_ID": 25, "DISPLAY": "UI"},
},
},
)
print(list_field_id, catalog_field_id)
Ответ содержит идентификатор поля:
{
"result": 6007773
}
Сокращенный ответ crm.deal.userfield.get с настройками поля:
{
"result": {
"ID": "6007773",
"ENTITY_ID": "CRM_DEAL",
"FIELD_NAME": "UF_CRM_IBS_LIST",
"USER_TYPE_ID": "iblock_section",
"MULTIPLE": "N",
"SETTINGS": {
"DISPLAY": "UI",
"LIST_HEIGHT": 1,
"IBLOCK_ID": 123,
"DEFAULT_VALUE": "",
"ACTIVE_FILTER": "N"
}
}
}
Сохраняются только пять настроек. Значение FIELD_NAME понадобится на четвертом шаге — под этим ключом записываются значения.
3. Получим идентификаторы разделов
Разделы списка возвращает метод lists.section.get. Передайте параметры:
IBLOCK_TYPE_ID— тип инфоблока, тот же, что на первом шагеIBLOCK_ID— идентификатор спискаFILTER— условия отбора, напримерIDконкретного раздела
Разделы каталога возвращает метод catalog.section.list. Передайте параметры:
filter.iblockId— идентификатор каталога, иначе в ответ попадут разделы всех каталоговselect— поля раздела, для сценария достаточноid,iblockId,nameиiblockSectionId
Оба метода возвращают плоский список разделов, а дерево строится по ссылке на родителя: IBLOCK_SECTION_ID у списка и iblockSectionId у каталога. У корневых разделов это поле пустое.
Если список только создан, разделов в нем нет и метод вернет пустой массив. Раздел заводит метод lists.section.add с обязательными параметрами IBLOCK_TYPE_ID, IBLOCK_ID, SECTION_CODE и FIELDS.NAME.
let listSections = await callMethod(
'lists.section.get',
{
IBLOCK_TYPE_ID: 'lists',
IBLOCK_ID: 123
},
'lists-section-get'
)
if (listSections.length === 0) {
await callMethod(
'lists.section.add',
{
IBLOCK_TYPE_ID: 'lists',
IBLOCK_ID: 123,
SECTION_CODE: 'first_section',
FIELDS: { NAME: 'Первый раздел' }
},
'lists-section-add'
)
listSections = await callMethod(
'lists.section.get',
{
IBLOCK_TYPE_ID: 'lists',
IBLOCK_ID: 123
},
'lists-section-get-again'
)
}
const catalogSections = await callMethod(
'catalog.section.list',
{
select: ['id', 'iblockId', 'name', 'iblockSectionId'],
filter: { iblockId: 25 }
},
'catalog-section-list'
)
const listSectionId = Number(listSections[0].ID)
const catalogSectionIds = catalogSections.sections.slice(0, 2).map((section) => section.id)
console.log(listSectionId, catalogSectionIds)
$listSections = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => 'lists',
'IBLOCK_ID' => 123,
]);
if ($listSections === []) {
callMethod($serviceBuilder, 'lists.section.add', [
'IBLOCK_TYPE_ID' => 'lists',
'IBLOCK_ID' => 123,
'SECTION_CODE' => 'first_section',
'FIELDS' => ['NAME' => 'Первый раздел'],
]);
$listSections = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => 'lists',
'IBLOCK_ID' => 123,
]);
}
$catalogSections = callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id', 'iblockId', 'name', 'iblockSectionId'],
'filter' => ['iblockId' => 25],
]);
$listSectionId = (int)$listSections[0]['ID'];
$catalogSectionIds = array_map(
static fn(array $section): int => (int)$section['id'],
array_slice($catalogSections['sections'], 0, 2)
);
print_r([$listSectionId, $catalogSectionIds]);
list_sections = call_method(
"lists.section.get",
{"IBLOCK_TYPE_ID": "lists", "IBLOCK_ID": 123},
)
if not list_sections:
call_method(
"lists.section.add",
{
"IBLOCK_TYPE_ID": "lists",
"IBLOCK_ID": 123,
"SECTION_CODE": "first_section",
"FIELDS": {"NAME": "Первый раздел"},
},
)
list_sections = call_method(
"lists.section.get",
{"IBLOCK_TYPE_ID": "lists", "IBLOCK_ID": 123},
)
catalog_sections = call_method(
"catalog.section.list",
{
"select": ["id", "iblockId", "name", "iblockSectionId"],
"filter": {"iblockId": 25},
},
)
list_section_id = int(list_sections[0]["ID"])
catalog_section_ids = [
section["id"] for section in catalog_sections["sections"][:2]
]
print(list_section_id, catalog_section_ids)
Сокращенный ответ lists.section.get:
{
"result": [
{
"ID": "237",
"IBLOCK_ID": "123",
"IBLOCK_SECTION_ID": null,
"NAME": "Раздел для проверки документации",
"DEPTH_LEVEL": "1",
"CODE": "doc_check_section_1"
}
],
"total": 1
}
Сокращенный ответ catalog.section.list:
{
"result": {
"sections": [
{ "id": 19, "iblockId": 25, "iblockSectionId": 31, "name": "Экскурсии" },
{ "id": 31, "iblockId": 25, "iblockSectionId": null, "name": "Одежда" },
{ "id": 33, "iblockId": 25, "iblockSectionId": 31, "name": "Обувь" }
]
},
"total": 3
}
В примере получили раздел списка 237 и разделы каталога 19 и 33. Оба раздела каталога вложены в раздел 31: если привязать сделку к разделу 31, разделы 19 и 33 в значение поля не попадут.
4. Запишем значения
Метод crm.deal.update записывает значения в поля сделки. Передайте параметры:
id— идентификатор сделкиfields— объект с кодами полей. В одиночное поле передайте число, в множественное — массив чисел
Битрикс24 не проверяет переданные идентификаторы: метод примет и несуществующий раздел, и раздел чужого инфоблока, и вернет true. Поэтому проверьте разделы перед записью. Раздел списка ищем методом lists.section.get с фильтром по ID: пустой массив означает, что раздела в этом списке нет. Разделы каталога проверяем методом catalog.section.list, передав в фильтр массив идентификаторов — так за один вызов видно, какие из них относятся к нужному каталогу.
async function isListSectionValid(iblockTypeId, iblockId, id) {
const found = await callMethod(
'lists.section.get',
{
IBLOCK_TYPE_ID: iblockTypeId,
IBLOCK_ID: iblockId,
FILTER: { ID: id }
},
`lists-section-check-${id}`
)
return found.length > 0
}
async function filterCatalogSections(iblockId, ids) {
if (ids.length === 0) {
return []
}
const found = await callMethod(
'catalog.section.list',
{
select: ['id'],
filter: { iblockId, id: ids }
},
'catalog-section-check'
)
return found.sections.map((section) => section.id)
}
if (!(await isListSectionValid('lists', 123, listSectionId))) {
throw new Error(`Раздела ${listSectionId} нет в списке 123`)
}
const validCatalogSectionIds = await filterCatalogSections(25, catalogSectionIds)
await callMethod(
'crm.deal.update',
{
id: 8419,
fields: {
UF_CRM_IBS_LIST: listSectionId,
UF_CRM_IBS_CAT: validCatalogSectionIds
}
},
'deal-update-section-bindings'
)
function isListSectionValid($serviceBuilder, string $iblockTypeId, int $iblockId, int $id): bool
{
$found = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => $iblockTypeId,
'IBLOCK_ID' => $iblockId,
'FILTER' => ['ID' => $id],
]);
return $found !== [];
}
function filterCatalogSections($serviceBuilder, int $iblockId, array $ids): array
{
if ($ids === []) {
return [];
}
$found = callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id'],
'filter' => ['iblockId' => $iblockId, 'id' => $ids],
]);
return array_map(
static fn(array $section): int => (int)$section['id'],
$found['sections']
);
}
if (!isListSectionValid($serviceBuilder, 'lists', 123, $listSectionId)) {
throw new RuntimeException('Раздела ' . $listSectionId . ' нет в списке 123');
}
$validCatalogSectionIds = filterCatalogSections($serviceBuilder, 25, $catalogSectionIds);
callMethod($serviceBuilder, 'crm.deal.update', [
'id' => 8419,
'fields' => [
'UF_CRM_IBS_LIST' => $listSectionId,
'UF_CRM_IBS_CAT' => $validCatalogSectionIds,
],
]);
def is_list_section_valid(iblock_type_id, iblock_id, section):
found = call_method(
"lists.section.get",
{
"IBLOCK_TYPE_ID": iblock_type_id,
"IBLOCK_ID": iblock_id,
"FILTER": {"ID": section},
},
)
return len(found) > 0
def filter_catalog_sections(iblock_id, ids):
if not ids:
return []
found = call_method(
"catalog.section.list",
{
"select": ["id"],
"filter": {"iblockId": iblock_id, "id": ids},
},
)
return [section["id"] for section in found["sections"]]
if not is_list_section_valid("lists", 123, list_section_id):
raise RuntimeError(f"Раздела {list_section_id} нет в списке 123")
valid_catalog_section_ids = filter_catalog_sections(25, catalog_section_ids)
call_method(
"crm.deal.update",
{
"id": 8419,
"fields": {
"UF_CRM_IBS_LIST": list_section_id,
"UF_CRM_IBS_CAT": valid_catalog_section_ids,
},
},
)
Сокращенный ответ:
{
"result": true
}
Значение true подтверждает, что сделка обновлена, но ничего не говорит о корректности привязок. Проверить сами значения можно только чтением на следующем шаге.
5. Развернем значения в названия
Метод crm.deal.get возвращает сделку со всеми пользовательскими полями. Одиночное поле приходит строкой, множественное — массивом чисел.
Названия разделов в сделке не хранятся. Раздел списка получите методом lists.section.get с фильтром по ID, разделы каталога — методом catalog.section.list с массивом идентификаторов в фильтре: все нужные разделы придут одним вызовом.
const deal = await callMethod('crm.deal.get', { id: 8419 }, 'deal-get')
const boundListSectionId = Number(deal.UF_CRM_IBS_LIST)
const boundCatalogSectionIds = deal.UF_CRM_IBS_CAT ?? []
const listSectionNames = boundListSectionId > 0
? await callMethod(
'lists.section.get',
{
IBLOCK_TYPE_ID: 'lists',
IBLOCK_ID: 123,
FILTER: { ID: boundListSectionId }
},
'lists-section-resolve'
)
: []
const catalogSectionNames = boundCatalogSectionIds.length > 0
? await callMethod(
'catalog.section.list',
{
select: ['id', 'name', 'iblockSectionId'],
filter: { iblockId: 25, id: boundCatalogSectionIds }
},
'catalog-section-resolve'
)
: { sections: [] }
console.log(listSectionNames[0]?.NAME)
console.table(catalogSectionNames.sections)
$deal = callMethod($serviceBuilder, 'crm.deal.get', ['id' => 8419]);
$boundListSectionId = (int)$deal['UF_CRM_IBS_LIST'];
$boundCatalogSectionIds = $deal['UF_CRM_IBS_CAT'] ?? [];
$listSectionNames = $boundListSectionId > 0
? callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => 'lists',
'IBLOCK_ID' => 123,
'FILTER' => ['ID' => $boundListSectionId],
])
: [];
$catalogSectionNames = $boundCatalogSectionIds !== []
? callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id', 'name', 'iblockSectionId'],
'filter' => ['iblockId' => 25, 'id' => $boundCatalogSectionIds],
])
: ['sections' => []];
print_r($listSectionNames[0]['NAME'] ?? null);
print_r($catalogSectionNames['sections']);
deal = call_method("crm.deal.get", {"id": 8419})
bound_list_section_id = int(deal["UF_CRM_IBS_LIST"] or 0)
bound_catalog_section_ids = deal.get("UF_CRM_IBS_CAT") or []
list_section_names = (
call_method(
"lists.section.get",
{
"IBLOCK_TYPE_ID": "lists",
"IBLOCK_ID": 123,
"FILTER": {"ID": bound_list_section_id},
},
)
if bound_list_section_id > 0
else []
)
catalog_section_names = (
call_method(
"catalog.section.list",
{
"select": ["id", "name", "iblockSectionId"],
"filter": {"iblockId": 25, "id": bound_catalog_section_ids},
},
)
if bound_catalog_section_ids
else {"sections": []}
)
print(list_section_names[0]["NAME"] if list_section_names else None)
print(catalog_section_names["sections"])
Сокращенный ответ crm.deal.get:
{
"result": {
"ID": "8419",
"TITLE": "Проверка привязки к разделам инфоблоков",
"UF_CRM_IBS_LIST": "237",
"UF_CRM_IBS_CAT": [19, 33]
}
}
Сокращенный ответ catalog.section.list с фильтром по идентификаторам:
{
"result": {
"sections": [
{ "id": 19, "iblockSectionId": 31, "name": "Экскурсии" },
{ "id": 33, "iblockSectionId": 31, "name": "Обувь" }
]
},
"total": 2
}
Если в ответе разделов меньше, чем идентификаторов в поле, часть привязок указывает на разделы, которых нет в этом каталоге.
Проверим результат
Сценарий выполнен, если после чтения сделки оба поля заполнены и по каждому идентификатору находится раздел.
Что проверить в ответах:
UF_CRM_IBS_LISTсодержит строку с идентификатором раздела, а не"0"и не пустую строкуUF_CRM_IBS_CATсодержит массив с идентификаторами разделов- lists.section.get с фильтром по этому
IDвернул один раздел, а не пустой массив - catalog.section.list вернул столько же разделов, сколько идентификаторов в поле
В интерфейсе откройте карточку сделки: в полях «Раздел списка» и «Разделы каталога» будут названия разделов. Пустое поле в карточке при непустом значении в ответе означает, что сохраненного раздела в привязанном инфоблоке нет.
Ошибки и диагностика
Если метод вернул ошибку, проверьте данные запроса.
|
Код или текст ошибки |
Причина и действие |
|
|
В crm.deal.userfield.add не передан код поля. Передайте |
|
|
В настройках поля нет |
|
|
lists.section.get вызван с типом инфоблока, который не относится к спискам. Для разделов каталога используйте catalog.section.list |
|
|
В lists.section.add не передан обязательный параметр. Его имя указано в тексте ошибки: |
|
|
catalog.section.get вызван с идентификатором несуществующего раздела или без прав администратора. Сообщение про доступ приходит в обоих случаях, поэтому проверяйте разделы методом catalog.section.list |
Если ошибки не было, а привязка не работает, проверьте сохраненное значение методом crm.deal.get.
- Значение
"0"означает, что в поле передали строку вместо числа. Нечисловое значение приводится к нулю, ошибку метод не возвращает - Значение
"1"в одиночном поле означает, что в него передали массив. Одиночное поле принимает только число, массив приводится к единице, а не к первому элементу - Значение есть, а в карточке пусто — сохранен идентификатор несуществующего раздела или раздела другого инфоблока. Проверьте раздел методом lists.section.get или catalog.section.list и запишите значение заново
Чтобы очистить привязку, передайте в поле пустую строку. Повторный запуск сценария перезаписывает значения, дубликаты не создаются.
Что важно учитывать
- Поле привязано к одному инфоблоку. Тип инфоблока в настройках не хранится, сохраняется только
IBLOCK_ID - Битрикс24 не проверяет, что раздел существует и относится к привязанному инфоблоку. Проверка идентификаторов — задача вашей интеграции
- Привязка хранит один узел дерева. Вложенные разделы не подставляются автоматически: чтобы получить ветку целиком, отберите разделы по родителю в
IBLOCK_SECTION_IDилиiblockSectionId - Одиночное поле возвращается строкой, множественное — массивом чисел
- Разделы каталога доступны только администратору, разделы списка — пользователю с правом чтения этого списка
- Для привязки к отдельным элементам инфоблока используйте парный тип поля
iblock_element, он разобран в туториале Как работать с полем Привязка к элементам инфоблоков - Для других объектов CRM поля создают одноименными методами, например crm.lead.userfield.add, а в смарт-процессе — методом userfieldconfig.add
Пример кода
Полный сценарий одним скриптом: находит инфоблоки, создает оба поля, получает и проверяет разделы, записывает значения и разворачивает их в названия.
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
const LIST_IBLOCK_TYPE = 'lists'
const DEAL_ID = 8419
// Типизированных оберток для методов списков и каталога в SDK нет, вызываем их через ядро SDK
async function callMethod(method, params, requestId) {
const response = await $b24.actions.v2.call.make({ method, params, requestId })
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
return response.getData().result
}
async function main() {
const lists = await callMethod('lists.get', { IBLOCK_TYPE_ID: LIST_IBLOCK_TYPE }, 'lists-get')
const catalogs = await callMethod('catalog.catalog.list', {}, 'catalog-catalog-list')
const listIblockId = Number(lists[0].ID)
const catalogIblockId = catalogs.catalogs.find((item) => item.productIblockId === null).iblockId
await callMethod('crm.deal.userfield.add', {
fields: {
FIELD_NAME: 'UF_CRM_IBS_LIST',
USER_TYPE_ID: 'iblock_section',
MULTIPLE: 'N',
EDIT_FORM_LABEL: { ru: 'Раздел списка', en: 'List section' },
SETTINGS: { IBLOCK_ID: listIblockId, DISPLAY: 'UI' }
}
}, 'userfield-add-list-section')
await callMethod('crm.deal.userfield.add', {
fields: {
FIELD_NAME: 'UF_CRM_IBS_CAT',
USER_TYPE_ID: 'iblock_section',
MULTIPLE: 'Y',
EDIT_FORM_LABEL: { ru: 'Разделы каталога', en: 'Catalog sections' },
SETTINGS: { IBLOCK_ID: catalogIblockId, DISPLAY: 'UI' }
}
}, 'userfield-add-catalog-section')
let listSections = await callMethod('lists.section.get', {
IBLOCK_TYPE_ID: LIST_IBLOCK_TYPE,
IBLOCK_ID: listIblockId
}, 'lists-section-get')
if (listSections.length === 0) {
await callMethod('lists.section.add', {
IBLOCK_TYPE_ID: LIST_IBLOCK_TYPE,
IBLOCK_ID: listIblockId,
SECTION_CODE: 'first_section',
FIELDS: { NAME: 'Первый раздел' }
}, 'lists-section-add')
listSections = await callMethod('lists.section.get', {
IBLOCK_TYPE_ID: LIST_IBLOCK_TYPE,
IBLOCK_ID: listIblockId
}, 'lists-section-get-again')
}
const catalogSections = await callMethod('catalog.section.list', {
select: ['id', 'iblockId', 'name', 'iblockSectionId'],
filter: { iblockId: catalogIblockId }
}, 'catalog-section-list')
const listSectionId = Number(listSections[0].ID)
const catalogSectionIds = catalogSections.sections.slice(0, 2).map((section) => section.id)
const checked = await callMethod('catalog.section.list', {
select: ['id'],
filter: { iblockId: catalogIblockId, id: catalogSectionIds }
}, 'catalog-section-check')
await callMethod('crm.deal.update', {
id: DEAL_ID,
fields: {
UF_CRM_IBS_LIST: listSectionId,
UF_CRM_IBS_CAT: checked.sections.map((section) => section.id)
}
}, 'deal-update-section-bindings')
const deal = await callMethod('crm.deal.get', { id: DEAL_ID }, 'deal-get')
const boundListSection = await callMethod('lists.section.get', {
IBLOCK_TYPE_ID: LIST_IBLOCK_TYPE,
IBLOCK_ID: listIblockId,
FILTER: { ID: Number(deal.UF_CRM_IBS_LIST) }
}, 'lists-section-resolve')
const boundCatalogSections = await callMethod('catalog.section.list', {
select: ['id', 'name', 'iblockSectionId'],
filter: { iblockId: catalogIblockId, id: deal.UF_CRM_IBS_CAT ?? [] }
}, 'catalog-section-resolve')
console.log(boundListSection[0]?.NAME)
console.table(boundCatalogSections.sections)
}
main().catch((error) => console.error(error.message))
<?php
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcher;
const LIST_IBLOCK_TYPE = 'lists';
const DEAL_ID = 8419;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), new NullLogger()))
->initFromWebhook(getenv('B24_HOOK'));
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
// Типизированных оберток для методов списков и каталога в SDK нет, вызываем их через ядро SDK
function callMethod($serviceBuilder, string $method, array $params = []): mixed
{
return $serviceBuilder
->core
->call($method, $params)
->getResponseData()
->getResult();
}
$lists = callMethod($serviceBuilder, 'lists.get', ['IBLOCK_TYPE_ID' => LIST_IBLOCK_TYPE]);
$catalogs = callMethod($serviceBuilder, 'catalog.catalog.list');
$listIblockId = (int)$lists[0]['ID'];
$catalogIblockId = 0;
foreach ($catalogs['catalogs'] as $catalog) {
if ($catalog['productIblockId'] === null) {
$catalogIblockId = (int)$catalog['iblockId'];
break;
}
}
callMethod($serviceBuilder, 'crm.deal.userfield.add', [
'fields' => [
'FIELD_NAME' => 'UF_CRM_IBS_LIST',
'USER_TYPE_ID' => 'iblock_section',
'MULTIPLE' => 'N',
'EDIT_FORM_LABEL' => ['ru' => 'Раздел списка', 'en' => 'List section'],
'SETTINGS' => ['IBLOCK_ID' => $listIblockId, 'DISPLAY' => 'UI'],
],
]);
callMethod($serviceBuilder, 'crm.deal.userfield.add', [
'fields' => [
'FIELD_NAME' => 'UF_CRM_IBS_CAT',
'USER_TYPE_ID' => 'iblock_section',
'MULTIPLE' => 'Y',
'EDIT_FORM_LABEL' => ['ru' => 'Разделы каталога', 'en' => 'Catalog sections'],
'SETTINGS' => ['IBLOCK_ID' => $catalogIblockId, 'DISPLAY' => 'UI'],
],
]);
$listSections = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => LIST_IBLOCK_TYPE,
'IBLOCK_ID' => $listIblockId,
]);
if ($listSections === []) {
callMethod($serviceBuilder, 'lists.section.add', [
'IBLOCK_TYPE_ID' => LIST_IBLOCK_TYPE,
'IBLOCK_ID' => $listIblockId,
'SECTION_CODE' => 'first_section',
'FIELDS' => ['NAME' => 'Первый раздел'],
]);
$listSections = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => LIST_IBLOCK_TYPE,
'IBLOCK_ID' => $listIblockId,
]);
}
$catalogSections = callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id', 'iblockId', 'name', 'iblockSectionId'],
'filter' => ['iblockId' => $catalogIblockId],
]);
$listSectionId = (int)$listSections[0]['ID'];
$catalogSectionIds = array_map(
static fn(array $section): int => (int)$section['id'],
array_slice($catalogSections['sections'], 0, 2)
);
$checked = callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id'],
'filter' => ['iblockId' => $catalogIblockId, 'id' => $catalogSectionIds],
]);
callMethod($serviceBuilder, 'crm.deal.update', [
'id' => DEAL_ID,
'fields' => [
'UF_CRM_IBS_LIST' => $listSectionId,
'UF_CRM_IBS_CAT' => array_map(
static fn(array $section): int => (int)$section['id'],
$checked['sections']
),
],
]);
$deal = callMethod($serviceBuilder, 'crm.deal.get', ['id' => DEAL_ID]);
$boundListSection = callMethod($serviceBuilder, 'lists.section.get', [
'IBLOCK_TYPE_ID' => LIST_IBLOCK_TYPE,
'IBLOCK_ID' => $listIblockId,
'FILTER' => ['ID' => (int)$deal['UF_CRM_IBS_LIST']],
]);
$boundCatalogSections = callMethod($serviceBuilder, 'catalog.section.list', [
'select' => ['id', 'name', 'iblockSectionId'],
'filter' => ['iblockId' => $catalogIblockId, 'id' => $deal['UF_CRM_IBS_CAT'] ?? []],
]);
print_r($boundListSection[0]['NAME'] ?? null);
print_r($boundCatalogSections['sections']);
import os
from b24pysdk import BitrixWebhook
LIST_IBLOCK_TYPE = "lists"
DEAL_ID = 8419
bitrix_token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token=os.environ["B24_HOOK_TOKEN"],
)
# B24_HOOK_TOKEN = 'USER_ID/TOKEN'
def call_method(method, params=None):
# Типизированных оберток для методов списков и каталога в SDK нет, вызываем их напрямую
return bitrix_token.call_method(
api_method=method,
params=params or {},
)["result"]
lists = call_method("lists.get", {"IBLOCK_TYPE_ID": LIST_IBLOCK_TYPE})
catalogs = call_method("catalog.catalog.list")
list_iblock_id = int(lists[0]["ID"])
catalog_iblock_id = next(
catalog["iblockId"]
for catalog in catalogs["catalogs"]
if catalog["productIblockId"] is None
)
call_method(
"crm.deal.userfield.add",
{
"fields": {
"FIELD_NAME": "UF_CRM_IBS_LIST",
"USER_TYPE_ID": "iblock_section",
"MULTIPLE": "N",
"EDIT_FORM_LABEL": {"ru": "Раздел списка", "en": "List section"},
"SETTINGS": {"IBLOCK_ID": list_iblock_id, "DISPLAY": "UI"},
},
},
)
call_method(
"crm.deal.userfield.add",
{
"fields": {
"FIELD_NAME": "UF_CRM_IBS_CAT",
"USER_TYPE_ID": "iblock_section",
"MULTIPLE": "Y",
"EDIT_FORM_LABEL": {"ru": "Разделы каталога", "en": "Catalog sections"},
"SETTINGS": {"IBLOCK_ID": catalog_iblock_id, "DISPLAY": "UI"},
},
},
)
list_sections = call_method(
"lists.section.get",
{"IBLOCK_TYPE_ID": LIST_IBLOCK_TYPE, "IBLOCK_ID": list_iblock_id},
)
if not list_sections:
call_method(
"lists.section.add",
{
"IBLOCK_TYPE_ID": LIST_IBLOCK_TYPE,
"IBLOCK_ID": list_iblock_id,
"SECTION_CODE": "first_section",
"FIELDS": {"NAME": "Первый раздел"},
},
)
list_sections = call_method(
"lists.section.get",
{"IBLOCK_TYPE_ID": LIST_IBLOCK_TYPE, "IBLOCK_ID": list_iblock_id},
)
catalog_sections = call_method(
"catalog.section.list",
{
"select": ["id", "iblockId", "name", "iblockSectionId"],
"filter": {"iblockId": catalog_iblock_id},
},
)
list_section_id = int(list_sections[0]["ID"])
catalog_section_ids = [section["id"] for section in catalog_sections["sections"][:2]]
checked = call_method(
"catalog.section.list",
{
"select": ["id"],
"filter": {"iblockId": catalog_iblock_id, "id": catalog_section_ids},
},
)
call_method(
"crm.deal.update",
{
"id": DEAL_ID,
"fields": {
"UF_CRM_IBS_LIST": list_section_id,
"UF_CRM_IBS_CAT": [section["id"] for section in checked["sections"]],
},
},
)
deal = call_method("crm.deal.get", {"id": DEAL_ID})
bound_list_section = call_method(
"lists.section.get",
{
"IBLOCK_TYPE_ID": LIST_IBLOCK_TYPE,
"IBLOCK_ID": list_iblock_id,
"FILTER": {"ID": int(deal["UF_CRM_IBS_LIST"] or 0)},
},
)
bound_catalog_sections = call_method(
"catalog.section.list",
{
"select": ["id", "name", "iblockSectionId"],
"filter": {
"iblockId": catalog_iblock_id,
"id": deal.get("UF_CRM_IBS_CAT") or [],
},
},
)
print(bound_list_section[0]["NAME"] if bound_list_section else None)
print(bound_catalog_sections["sections"])
Продолжите изучение
- Как работать с полем Привязка к элементам инфоблоков
- Получить список типов пользовательских полей crm.userfield.types
- Создать пользовательское поле для сделок crm.deal.userfield.add
- Получить параметры раздела или список разделов универсального списка lists.section.get
- Создать раздел универсального списка lists.section.add
- Получить список разделов торгового каталога catalog.section.list
- Получить список торговых каталогов catalog.catalog.list
- Изменить сделку crm.deal.update