Как перенести файл из поля CRM в задачу
Scope:
crm,disk,taskКто может выполнять методы: чтобы пройти сценарий целиком, нужны права на чтение элемента CRM, добавление файла в папку Диска и редактирование задачи
- crm.item.get — пользователь с правом «чтения» элементов объекта CRM
- disk.folder.uploadFile — пользователь с правом «Добавление» для папки Диска
- tasks.task.files.attach — постановщик задачи или пользователь с правом редактирования задачи и чтения файла
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Менеджер прикрепил договор к сделке, а работать с ним будет исполнитель в задаче. Скопировать файл между объектами одним вызовом нельзя: сделка и задача хранят файлы по-разному.
Файловое поле сделки — это поле типа «Файл»: в нем лежит идентификатор файла CRM. Задача устроена иначе. Вложения она хранит в поле UF_TASK_WEBDAV_FILES типа «Файл (диск)», а такое поле принимает только идентификатор объекта на Диске.
Это две независимые нумерации. Идентификатор из сделки для Диска ничего не значит: методы Диска вернут по нему ошибку или посторонний файл, у которого совпал номер.
Полей типа «Файл (диск)» в CRM нет, поэтому готовый идентификатор Диска в сделке взять негде. Файл переносят вручную: скачивают из сделки, загружают на Диск и уже оттуда прикрепляют к задаче.
Сценарий состоит из четырех шагов.
- Получим ссылку на файл методом crm.item.get
- Скачаем файл по этой ссылке обычным HTTP-запросом
- Загрузим файл на Диск методом disk.folder.uploadFile
- Прикрепим файл к задаче методом tasks.task.files.attach
Сценарий показан на одном файле.
Что нужно до начала
Подготовьте данные сценария:
- Элемент CRM с заполненным файловым полем. Понадобятся
entityTypeIdобъекта,idэлемента и имя поля видаufCrm_1688736288. В примерах используется сделка, у нееentityTypeIdравен2. Список полей вернет метод crm.item.fields, у файловых полей типfile - Задача, к которой прикрепим файл. Понадобится
IDзадачи, его вернет метод tasks.task.list - Папка на Диске для загрузки. Понадобится
IDпапки. Хранилища и их содержимое вернут методы disk.storage.getlist и disk.storage.getchildren - Доступ к REST. Вебхук или приложение с правами
crm,diskиtask
Как использовать примеры в документации
1. Получим ссылку на файл из элемента CRM
Прочитаем сделку методом crm.item.get с параметрами:
entityTypeId— укажем2, идентификатор типа «сделка»id— укажем6533, идентификатор сделки с файлом
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 response = await $b24.actions.v2.call.make({
method: 'crm.item.get',
params: {
entityTypeId: 2,
id: 6533
},
requestId: 'crm-item-get'
})
if (!response.isSuccess) {
throw new Error(response.getErrorMessages().join('; '))
}
const item = response.getData().result.item
const file = item.ufCrm_1688736288
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
# Вызов через ядро SDK: типизированная обертка не возвращает пользовательские поля
item = token.call_method("crm.item.get", {
"entityTypeId": 2,
"id": 6533,
})["result"]["item"]
file = item["ufCrm_1688736288"]
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
// Вызов через ядро SDK: типизированная обертка не возвращает пользовательские поля
$item = $serviceBuilder->core->call(
'crm.item.get',
['entityTypeId' => 2, 'id' => 6533]
)->getResponseData()->getResult()['item'];
$file = $item['ufCrm_1688736288'];
В файловом поле придут три значения:
id— идентификатор файла CRM. На Диске такого объекта нет, методам Диска это число передавать нельзяurl— ссылка для интерфейса Битрикс24. Открывается в браузере авторизованного пользователя, интеграции не подходитurlMachine— ссылка для интеграции. Содержит токен доступа и подпись файла, поэтому скачивается обычным HTTP-запросом. Ее и берем на следующем шаге
{
"result": {
"item": {
"id": 6533,
"title": "Поставка оборудования",
"ufCrm_1688736288": {
"id": 27807,
"url": "https://your-domain.bitrix24.com/bitrix/services/main/ajax.php?action=crm.controller.item.getFile&SITE_ID=s1&entityTypeId=2&id=6533&fieldName=UF_CRM_1688736288&fileId=27807",
"urlMachine": "https://your-domain.bitrix24.com/rest/1/xxxxxxxxxxxxxxxx/crm.controller.item.getFile/?token=xxxxxxxx"
}
}
}
}
Если поле множественное, вместо объекта придет массив таких объектов — тогда шаги 2, 3 и 4 повторяются для каждого файла.
2. Скачаем файл
На этом шаге метод не вызываем: файл забираем обычным HTTP-запросом по ссылке urlMachine.
Отдельная авторизация запросу не нужна — токен уже внутри ссылки. Передавайте ее целиком и не разбирайте на части: набор параметров может измениться. Обычно ссылка абсолютная, но если в ответе она начинается с /, добавьте к ней адрес своего Битрикс24.
Дальше файл нужно закодировать в Base64 — в этом виде его принимает Диск. Как устроено кодирование, разобрано в статье Как загрузить файлы.
const fileResponse = await fetch(file.urlMachine)
if (!fileResponse.ok) {
throw new Error('Не удалось скачать файл: ' + fileResponse.status)
}
const buffer = Buffer.from(await fileResponse.arrayBuffer())
const fileContent = buffer.toString('base64')
import base64
import requests
file_response = requests.get(file["urlMachine"], timeout=30)
file_response.raise_for_status()
file_content = base64.b64encode(file_response.content).decode()
$binary = file_get_contents($file['urlMachine']);
if ($binary === false) {
throw new RuntimeException('Не удалось скачать файл');
}
$fileContent = base64_encode($binary);
Исходное имя файла приходит в заголовке Content-Disposition. В примерах ниже имя задается вручную, поэтому на Диске и в задаче файл будет называться так, как указано в data.NAME, а не так, как он назывался в сделке. Чтобы сохранить исходное имя, возьмите его из заголовка и передайте на следующем шаге.
3. Загрузим файл на Диск
Загрузим файл методом disk.folder.uploadFile с параметрами:
id— укажем1739, идентификатор папки Дискаdata— передадим имяNAME, с ним файл сохранится на ДискеfileContent— передадим массив из имени файла и строки Base64 с прошлого шага
const uploadResponse = await $b24.actions.v2.call.make({
method: 'disk.folder.uploadFile',
params: {
id: 1739,
data: {
NAME: 'contract.pdf'
},
fileContent: [
'contract.pdf',
fileContent
]
},
requestId: 'disk-uploadfile'
})
if (!uploadResponse.isSuccess) {
throw new Error(uploadResponse.getErrorMessages().join('; '))
}
const diskFile = uploadResponse.getData().result
disk_file = client.disk.folder.uploadfile(
bitrix_id=1739,
data={
"NAME": "contract.pdf",
},
file_content=[
"contract.pdf",
file_content,
],
).response.result
$diskFile = $serviceBuilder->getDiskScope()->folder()->uploadFile(
1739,
['NAME' => 'contract.pdf'],
['contract.pdf', $fileContent]
)->getFile();
В ответе придут два разных идентификатора, их важно не перепутать:
ID— идентификатор объекта Диска. Именно его передаем в задачуFILE_ID— внутренний идентификатор файла. В поля типа «Файл (диск)» его передавать нельзя
{
"result": {
"ID": 6687,
"NAME": "contract.pdf",
"TYPE": "file",
"PARENT_ID": "1739",
"FILE_ID": 28073,
"SIZE": "405559"
}
}
4. Прикрепим файл к задаче
Прикрепим файл методом tasks.task.files.attach с параметрами:
taskId— укажем3709, идентификатор задачиfileId— укажем6687, значениеIDиз ответа предыдущего шага
const attachResponse = await $b24.actions.v2.call.make({
method: 'tasks.task.files.attach',
params: {
taskId: 3709,
fileId: diskFile.ID
},
requestId: 'task-files-attach'
})
if (!attachResponse.isSuccess) {
throw new Error(attachResponse.getErrorMessages().join('; '))
}
const attachment = attachResponse.getData().result
attachment = client.tasks.task.files.attach(
task_id=3709,
file_id=disk_file["ID"],
).response.result
$attachment = $serviceBuilder->core->call(
'tasks.task.files.attach',
[
'taskId' => 3709,
'fileId' => $diskFile['ID']
]
)->getResponseData()->getResult();
Метод вернет attachmentId — идентификатор связи между файлом Диска и задачей.
{
"result": {
"attachmentId": 423
}
}
Проверим результат
Файл должен появиться в задаче. Проверить это можно двумя способами.
В интерфейсе Битрикс24 откройте задачу — файл будет в блоке вложений.
Через REST передайте attachmentId в параметр id метода disk.attachedObject.get.
const checkResponse = await $b24.actions.v2.call.make({
method: 'disk.attachedObject.get',
params: {
id: attachment.attachmentId
},
requestId: 'disk-attached-object-get'
})
console.log(checkResponse.getData().result)
check = token.call_method(
"disk.attachedObject.get",
{
"id": attachment["attachmentId"],
},
)["result"]
print(check)
$check = $serviceBuilder->core->call(
'disk.attachedObject.get',
['id' => $attachment['attachmentId']]
)->getResponseData()->getResult();
print_r($check);
Сценарий выполнен успешно, если:
ENTITY_TYPEравенtasks_taskENTITY_IDравен идентификатору задачиOBJECT_IDравенIDфайла на Диске из третьего шагаNAMEсодержит имя, под которым файл сохранен на Диске
{
"result": {
"ID": "423",
"OBJECT_ID": "6687",
"MODULE_ID": "tasks",
"ENTITY_TYPE": "tasks_task",
"ENTITY_ID": "3709",
"NAME": "contract.pdf",
"SIZE": "405559"
}
}
Ошибки и диагностика
Если метод вернул ошибку, проверьте данные запроса.
|
Код |
Причина и действие |
|
|
Элемента с таким |
|
|
Неверный |
|
В файловом поле пришло |
У этого элемента поле не заполнено. Возьмите элемент с файлом |
|
Файлового поля нет в ответе |
Указано неверное имя поля. Проверьте имя методом crm.item.fields |
|
Вместо файла скачалась HTML-страница входа |
Использована ссылка |
|
|
Папка с указанным |
|
|
Не передано имя файла в |
|
|
В папке уже есть файл с таким именем. Задайте другое имя или передайте |
|
|
Файл не удалось сохранить. Проверьте свободное место на Диске и корректность Base64 |
|
|
В |
|
|
Передан не |
Если ошибки не было, а файла в задаче нет, проверьте:
taskIdуказывает на существующую задачу. При неверном идентификаторе метод tasks.task.files.attach все равно вернетattachmentId, но файл в задаче не появится- в
fileIdпереданIDобъекта Диска из ответа disk.folder.uploadFile, а неFILE_ID - файл загрузился в папку, к которой у пользователя есть доступ
Повторяйте сценарий с того шага, который вернул ошибку. Если файл уже загружен на Диск, не загружайте его заново: исправьте taskId или fileId и повторите только вызов tasks.task.files.attach.
Что важно учитывать
- Файл в поле CRM не является объектом Диска. Передать его в задачу напрямую или строкой Base64 нельзя: поле
UF_TASK_WEBDAV_FILESпринимает только идентификатор объекта Диска - Если прикрепляете файл не методом tasks.task.files.attach, а записью в поле
UF_TASK_WEBDAV_FILES— например при создании задачи методом tasks.task.add — передавайте значение в форматеn<идентификатор>, например["n6687"]. Число без префикса поле не примет - Ссылка
urlMachineсодержит токен доступа и подпись файла. Не записывайте ее в логи, не сохраняйте в базе и не передавайте третьим лицам - Сценарий работает не только со сделками. Для другого объекта CRM поменяйте
entityTypeId, а имя файлового поля возьмите из crm.item.fields. У смарт-процессов в имени поля появляется номер процесса, напримерufCrm_7_1739432938 - Файл копируется, а не перемещается. В сделке он остается, а на Диске появляется отдельный объект. Если исходный файл в сделке потом заменят, копия в задаче не обновится
Пример кода
Код проходит все четыре шага: читает элемент CRM, скачивает файл, загружает его на Диск и прикрепляет к задаче.
Замените вебхук, идентификаторы элемента, папки и задачи, а также имя файлового поля.
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
const entityTypeId = 2
const itemId = 6533
const fieldName = 'ufCrm_1688736288'
const folderId = 1739
const taskId = 3709
const fileName = 'contract.pdf'
async function call(method, params, requestId) {
const response = await $b24.actions.v2.call.make({ method, params, requestId })
if (!response.isSuccess) {
throw new Error(method + ': ' + response.getErrorMessages().join('; '))
}
return response.getData().result
}
// 1. Читаем элемент CRM и берем машинную ссылку
const { item } = await call('crm.item.get', { entityTypeId, id: itemId }, 'crm-item-get')
const file = item[fieldName]
if (!file || !file.urlMachine) {
throw new Error('В поле ' + fieldName + ' нет файла или ссылки urlMachine')
}
// 2. Скачиваем файл и кодируем в Base64
const fileResponse = await fetch(file.urlMachine)
if (!fileResponse.ok) {
throw new Error('Не удалось скачать файл: ' + fileResponse.status)
}
const fileContent = Buffer.from(await fileResponse.arrayBuffer()).toString('base64')
// 3. Загружаем файл на Диск
const diskFile = await call('disk.folder.uploadFile', {
id: folderId,
data: { NAME: fileName },
fileContent: [fileName, fileContent]
}, 'disk-uploadfile')
// 4. Прикрепляем файл Диска к задаче
const attachment = await call('tasks.task.files.attach', {
taskId: taskId,
fileId: diskFile.ID
}, 'task-files-attach')
console.log('Файл прикреплен, attachmentId:', attachment.attachmentId)
import base64
import requests
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
entity_type_id = 2
item_id = 6533
field_name = "ufCrm_1688736288"
folder_id = 1739
task_id = 3709
file_name = "contract.pdf"
# 1. Читаем элемент CRM и берем машинную ссылку
item = token.call_method("crm.item.get", {
"entityTypeId": entity_type_id,
"id": item_id,
})["result"]["item"]
file = item.get(field_name)
if not file or not file.get("urlMachine"):
raise ValueError(f"В поле {field_name} нет файла или ссылки urlMachine")
# 2. Скачиваем файл и кодируем в Base64
file_response = requests.get(file["urlMachine"], timeout=30)
file_response.raise_for_status()
file_content = base64.b64encode(file_response.content).decode()
# 3. Загружаем файл на Диск
disk_file = client.disk.folder.uploadfile(
bitrix_id=folder_id,
data={"NAME": file_name},
file_content=[file_name, file_content],
).response.result
# 4. Прикрепляем файл Диска к задаче
attachment = client.tasks.task.files.attach(
task_id=task_id,
file_id=disk_file["ID"],
).response.result
print("Файл прикреплен, attachmentId:", attachment["attachmentId"])
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
$serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $log))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
$entityTypeId = 2;
$itemId = 6533;
$fieldName = 'ufCrm_1688736288';
$folderId = 1739;
$taskId = 3709;
$fileName = 'contract.pdf';
// 1. Читаем элемент CRM и берем машинную ссылку
$item = $serviceBuilder->core->call(
'crm.item.get',
['entityTypeId' => $entityTypeId, 'id' => $itemId]
)->getResponseData()->getResult()['item'];
$file = $item[$fieldName] ?? null;
if (!$file || empty($file['urlMachine'])) {
throw new RuntimeException('В поле ' . $fieldName . ' нет файла или ссылки urlMachine');
}
// 2. Скачиваем файл и кодируем в Base64
$binary = file_get_contents($file['urlMachine']);
if ($binary === false) {
throw new RuntimeException('Не удалось скачать файл');
}
$fileContent = base64_encode($binary);
// 3. Загружаем файл на Диск
$diskFile = $serviceBuilder->getDiskScope()->folder()->uploadFile(
$folderId,
['NAME' => $fileName],
[$fileName, $fileContent]
)->getFile();
// 4. Прикрепляем файл Диска к задаче
$attachment = $serviceBuilder->core->call(
'tasks.task.files.attach',
['taskId' => $taskId, 'fileId' => $diskFile['ID']]
)->getResponseData()->getResult();
echo 'Файл прикреплен, attachmentId: ' . $attachment['attachmentId'];