Цель
Прочитать CSV-сопоставление dealId,newStageId и применить смену стадии к каждой строке. Может быть сотни сделок или десятки тысяч — BatchByChunkV2.make() разбивает вызовы на блоки по 50 на батч на стороне сервера, без ручной пагинации.
Стек и предпосылки
- Node.js 20+, ESM
@bitrix24/b24jssdk@^1.0.0- Вебхук со scope
crm, заданный какB24_HOOK. - CSV-файл
migrations.csv:dealId,newStageId 1234,WON 1235,WON 1240,LOSE
pnpm add @bitrix24/b24jssdk
export B24_HOOK="https://your-portal.bitrix24.com/rest/1/xxxxxxxxxxxxxxxx/"
Полный пример
scripts/migrate-deals.mjs:
import { readFile } from 'node:fs/promises'
import {
B24Hook,
EnumCrmEntityTypeId,
ParamsFactory
} from '@bitrix24/b24jssdk'
const hookUrl = process.env.B24_HOOK
if (!hookUrl) {
console.error('B24_HOOK is not set')
process.exit(2)
}
const csv = await readFile(process.argv[2] ?? 'migrations.csv', 'utf8')
const rows = csv
.split(/\r?\n/)
.slice(1) // заголовок
.filter(Boolean)
.map((line) => {
const [dealId, newStageId] = line.split(',')
return { dealId: Number(dealId), newStageId }
})
console.log(`planned updates: ${rows.length}`)
const b24 = B24Hook.fromWebhookUrl(hookUrl, {
restrictionParams: ParamsFactory.getBatchProcessing()
})
const calls = rows.map(({ dealId, newStageId }) => [
'crm.item.update',
{
entityTypeId: EnumCrmEntityTypeId.deal,
id: dealId,
fields: { stageId: newStageId }
}
])
const response = await b24.actions.v2.batchByChunk.make({
calls,
options: {
isHaltOnError: false,
requestId: `deal-stage-migration-${Date.now()}`
}
})
if (!response.isSuccess) {
console.error('partial failure:')
for (const message of response.getErrorMessages()) {
console.error(` - ${message}`)
}
process.exit(1)
}
console.log(`updated: ${response.getData()?.length ?? 0} of ${rows.length}`)
Запуск
node scripts/migrate-deals.mjs migrations.csv
# planned updates: 3450
# updated: 3450 of 3450
Если несколько строк не пройдут (удалённая сделка, отсутствующий scope, неверная стадия), isHaltOnError: false позволяет остальной части батча завершиться, и скрипт выходит с кодом 1, перечислив сбои.
Как это работает
BatchByChunkV2.make({ calls })принимает произвольное число команд и нарезает их внутри на батчи по 50 — жёсткий лимит на запрос. Порядок вывода совпадает с порядком ввода.Result.isSuccessстановитсяfalse, если в любом чанке была ошибка.getErrorMessages()возвращает по одному сообщению на каждый неудавшийся вызов. Успешные вызовы всё равно вgetData()— частичный прогресс сохраняется.ParamsFactory.getBatchProcessing()повышает порог времени операций и снижает частоту запросов, что держит очередь портала отзывчивой, пока долгая миграция идёт рядом с живым UI-трафиком.- Именование запроса через
options.requestIdупрощает фильтрацию логов на стороне портала и дедупликацию случайных перезапусков.
Ограничения
- Требуется форма
callsиз кортежей-двоек:BatchByChunkV2намеренно отклоняет команды в виде именованных объектов, потому что границы чанков разорвали бы имена. - Каждая команда учитывается в дневном бюджете вызовов. Для десятков тысяч обновлений запускайте скрипт в непиковые часы и подумайте о разбиении по ответственному пользователю.
crm.item.updateBitrix24 валидируетstageIdотносительно категории сделки. Опечатка вnewStageIdвозвращает ошибку конкретного вызова — видна вgetErrorMessages()— но весь скрипт не прерывается благодаряisHaltOnError: false.- Атомарный откат не поддерживается. Если нужно транзакционное поведение, сначала выгрузите текущие значения в отдельный CSV и запустите вторую миграцию для восстановления.
См. также
BatchByChunkV2.make()— полный справочник метода.BatchV2.make()— когда у вас ≤ 50 вызовов и нужны именованные результаты.- Система ограничений — выбор пресета
restrictionParams.
Вебхук CLI
Node-скрипт на 30 строк, который аутентифицируется на портале Bitrix24 через входящий вебхук и печатает вызывающего пользователя — полезно как первое, что вы запускаете после создания вебхука.
Синхронизация ERP
Двусторонняя синхронизация контактов между Bitrix24 и внешней ERP. Сопоставление по ИНН (основное) или email (запасное). Cron каждый час.