惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
腾讯CDC
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
B
Blog
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
T
Threatpost
博客园 - 聂微东
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
C
Check Point Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
爱范儿
爱范儿
IT之家
IT之家
S
Secure Thoughts
M
MIT News - Artificial intelligence
NISL@THU
NISL@THU
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
有赞技术团队
有赞技术团队
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
Schneier on Security
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
雷峰网
雷峰网
Project Zero
Project Zero
博客园 - Franky
H
Heimdal Security Blog
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Kaiten → коробочный Bitrix24: как мы переносили не задачи, а память команды
Вячеслав Лихобабин · 2026-05-10 · via Все публикации подряд на Хабре

На первый взгляд миграция из Kaiten в Bitrix24 выглядит как обычная интеграционная задача: прочитать данные из одного REST API и записать в другой REST API.

Но это впечатление быстро проходит, когда начинаешь переносить не демо-доску, а живую проектную систему.

В Kaiten уже накоплены пользователи, пространства, карточки, комментарии, файлы, ссылки внутри описаний, пользовательские поля, стадии, архивные задачи, связи между карточками и исторический контекст работы команды. Если перенести только названия карточек, формально миграция состоится. Но для бизнеса это будет потеря памяти.

В нашем случае нужно было перенести данные из облачного Kaiten в коробочный Bitrix24 так, чтобы команда смогла продолжить работу уже в новом контуре: с группами, задачами, файлами, комментариями, правами доступа и понятной структурой.

В этой статье расскажу, как мы построили мигратор на Python, где помог асинхронный подход, почему маппинг ID оказался центральной частью архитектуры, какие ограничения обнаружились в коробочном Bitrix24 и почему часть задач пришлось решать не только через REST API, но и через отдельные серверные скрипты.

Репозиторий с кодом: https://github.com/vlikhobabin/kaiten-to-bitrix

Контекст: нужно было перенести не доску, а рабочую историю

В нашей компании большая часть операционной работы велась в Kaiten. Там были пространства, карточки, файлы, обсуждения, пользовательские поля, связи между задачами и привычная структура доступа.

Однако через год активной работы в Kaiten мы решили переходить на коробочный Bitrix24. Причины решений, я полагаю, будут похожими у разных компаний: единый корпоративный портал, требования к размещению данных, интеграция с внутренними процессами, контроль над инфраструктурой, желание собрать коммуникации и задачи в одном контуре и т.д.

На уровне бизнеса задача звучала просто:

перенести все проектные данные из Kaiten в Bitrix24.

На уровне реализации это быстро превратилось в другой список:

  • перенести пользователей и сопоставить их по email;

  • перенести пространства Kaiten в группы Bitrix24;

  • сохранить участников и права доступа;

  • перенести карточки в задачи;

  • разложить задачи по стадиям;

  • перенести комментарии;

  • сохранить исторические даты комментариев;

  • скачать файлы из Kaiten;

  • загрузить файлы в Bitrix24;

  • заменить ссылки в описаниях карточек;

  • обработать пользовательские поля;

  • учесть архивные и завершённые карточки;

  • перенести связи Parent/Child и связанные карточки;

  • сделать так, чтобы миграцию можно было повторять, тестировать и докатывать частями.

В этот момент становится понятно: это не «экспорт-импорт». Это восстановление рабочей памяти команды в другой системе.

Что именно переносили

В итоговой версии мигратор поддерживал несколько основных типов объектов.

Kaiten                         Bitrix24
------                         --------
Пользователи              →    Пользователи
Пространства              →    Группы / проекты
Карточки                  →    Задачи
Комментарии               →    Комментарии / сообщения задачи
Файлы                     →    Файлы Bitrix24.Диск
Пользовательские поля     →    UF-поля и/или блок в описании
Parent/Child связи        →    Подзадачи / зависимости / связанные задачи
Группы доступа Kaiten     →    Участники групп Bitrix24

Сразу оговорюсь: это не универсальный коробочный продукт «нажмите кнопку и мигрируйте любой Kaiten в любой Bitrix24». Это миграционный инструмент, написанный под реальный переход, с учётом конкретной структуры данных, конкретного Bitrix24 и конкретных компромиссов.

Но именно поэтому в нём много практических деталей, которые редко видны в красивых интеграционных схемах.

Архитектура мигратора

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

kaiten-to-bitrix/
├── config/          # настройки и конфигурация
├── connectors/      # API-клиенты Kaiten и Bitrix24
├── migrators/       # миграторы по типам объектов
├── models/          # Pydantic-модели данных
├── transformers/    # преобразование форматов
├── utils/           # вспомогательные модули
├── scripts/         # сценарии запуска миграции
│   └── vps/         # серверные скрипты для коробочного Bitrix24
├── mappings/        # соответствия ID, создаются во время миграции
└── logs/            # журналы выполнения

Стек достаточно обычный:

  • Python;

  • httpx для HTTP-запросов;

  • asyncio для параллельной обработки;

  • pydantic / pydantic-settings для моделей и настроек;

  • .env для токенов и параметров подключения;

  • отдельные Python-скрипты для операций на сервере Bitrix24;

Ключевая идея была в разделении ответственности:

Connector     → знает, как говорить с API конкретной системы
Model         → описывает структуру данных
Transformer   → преобразует Kaiten-объект в Bitrix24-формат
Migrator      → управляет процессом переноса конкретного типа данных
Script        → даёт понятную точку запуска
Mapping       → хранит соответствие старых и новых ID

Такой подход оказался особенно важен из-за зависимостей между объектами. Нельзя нормально перенести карточку, пока не понятно, в какую группу Bitrix24 она должна попасть. Нельзя корректно перенести комментарий, пока не создана задача. Нельзя восстановить связи между карточками, пока не создан маппинг карточек.

Главный артефакт миграции — не задача, а mapping

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

В нашем случае это были JSON-маппинги:

mappings/user_mapping.json
mappings/space_mapping.json
mappings/card_mapping.json
mappings/custom_fields_mapping.json
mappings/custom_properties_cache.json
mappings/groups_cache.json

Без этих файлов миграция превращается в одноразовый скрипт, который страшно запускать повторно. С ними появляется возможность:

  • не создавать дубли;

  • докатывать миграцию частями;

  • повторно запускать обработку конкретного пространства;

  • восстанавливать связи между карточками после массового переноса;

  • переносить комментарии и файлы уже к существующим задачам;

  • анализировать ошибки после выполнения;

  • делать инкрементальную миграцию.

На практике маппинг - это не техническая мелочь, а позвоночник всего процесса.

Я не буду подробно описывать все этапы переноса, а остановлюсь лишь на интересных, с технической точки, моментах.

Пространства Kaiten → группы Bitrix24

В Kaiten рабочая область организована через пространства, доски, колонки, карточки и права доступа. В Bitrix24 ближайшим контейнером для проектной работы являются группы или проекты.

Мы приняли решение переносить не каждую доску, а именно пространства.

Логика была примерно такой:

  • конечные пространства без дочерних пространств превращались в группы Bitrix24;

  • пространства второго уровня тоже могли превращаться в группы;

  • технические или ненужные пространства исключались через конфигурацию;

  • участники пространства переносились в участники группы;

  • дополнительно учитывались пользователи через группы доступа Kaiten;

  • для новых групп включались нужные возможности: задачи, файлы, календарь, чат, база знаний, поиск.

Именно здесь обнаружился первый важный организационный момент: структура Kaiten и структура Bitrix24 не совпадают один к одному.

Можно было бы попытаться перенести всё максимально буквально: пространство в пространство, доску в доску, колонку в колонку. Но в реальности миграция между системами почти всегда требует не буквального копирования, а адаптации модели данных.

В нашем случае пространство Kaiten становилось группой Bitrix24, а карточки внутри пространства - задачами этой группы.

Карточки Kaiten → задачи Bitrix24

Карточки - самая объёмная и самая «грязная» часть миграции. Для карточек нужно было решить сразу несколько задач.

Во-первых, определить, в какую группу Bitrix24 попадёт задача. Для этого использовался space_mapping.json. Во-вторых, сопоставить стадии. В Kaiten у колонок есть типы. В нашем сценарии они маппились так:

type: 1              → стадия «Новые»
type: 2 и другие     → стадия «Выполняются»
type: 3              → по умолчанию пропускаем
архивные карточки    → по умолчанию пропускаем

Позже появилась опция --include-archived. С ней завершённые и архивные карточки можно было переносить в стадию «Сделаны» и выставлять статус завершённой задачи.

В-третьих, нужно было перенести не только заголовок и описание, но и всё окружение карточки:

  • автора;

  • ответственного;

  • участников;

  • сроки;

  • описание;

  • комментарии;

  • файлы;

  • пользовательские поля;

  • связи с другими карточками;

  • информацию об исходном пространстве.

И вот здесь обычная миграция превращается в серию маленьких компромиссов.

Грабля 1. Производительность API и контроль конкурентности

Если переносить карточки последовательно, миграция получается слишком медленной. Особенно когда у карточек есть файлы, комментарии и пользовательские поля. Но если просто запустить сотни запросов параллельно, можно получить другую проблему: таймауты, ошибки API, нестабильные ответы, превышение лимитов и сложную отладку. Поэтому мы использовали асинхронный подход с ограничением конкурентности.

Упрощённо логика выглядела так:
semaphore = asyncio.Semaphore(10)

async def process_card(card):
    async with semaphore:
        for attempt in range(3):
            try:
                return await migrate_single_card(card)
            except Exception:
                if attempt == 2:
                    raise
                await asyncio.sleep(1)

await asyncio.gather(*(process_card(card) for card in cards))

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

В нашем проекте ориентировочные показатели были такими:

Тип данных

Количество

Ориентировочное время

Пользователи

89

около 25 секунд

Пространства

34

около 3 минут

Карточки

около 1200

около 30 минут

Файлы

около 450

около 15 минут

Комментарии

около 3800

около 20 минут

Это не бенчмарк библиотеки. Это практический замер на конкретной миграции.

Грабля 2. Файлы — это не только вложения

Файлы в карточках оказались отдельной проблемой.

Наивная логика выглядит так:

  1. Получить список файлов карточки.

  2. Скачать файл из Kaiten.

  3. Загрузить файл в Bitrix24.

  4. Прикрепить к задаче.

Но в реальной карточке файлы могут быть не только во вложениях. Они могут быть ссылками внутри описания:

Смотрите документ: [ТЗ.pdf](https://.../files/...)

Если просто загрузить файл в Bitrix24, ссылка в описании всё равно будет вести в Kaiten. После отключения Kaiten или ограничения доступа такая ссылка станет бесполезной.

Поэтому пришлось делать обработку описания:

  1. Найти Markdown-ссылки на файлы Kaiten.

  2. Скачать исходный файл.

  3. Загрузить его в Bitrix24.

  4. Получить новую ссылку.

  5. Заменить старую ссылку в описании задачи.

Упрощённый фрагмент:
file_pattern = r'\[([^\]]+)\]\((https?://[^)]+/files/[^)]+)\)'

for match in re.finditer(file_pattern, description):
    file_name = match.group(1)
    old_url = match.group(2)

    content = await kaiten.download_file(old_url)
    bitrix_file_id = await bitrix.upload_task_file(task_id, file_name, content)
    new_url = await bitrix.get_file_url(bitrix_file_id)

    description = description.replace(match.group(0), f'[{file_name}]({new_url})')

Именно такие детали отличают «перенесли данные» от «пользователи действительно могут продолжить работу».

Грабля 3. Пользовательские поля Kaiten богаче, чем кажется

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

В Kaiten набор типов достаточно широкий: строки, числа, даты, email, телефон, checkbox, select, multi-select, URL, formula, user, attachment, голосования, каталоги и другие варианты.

В Bitrix24 у задач тоже есть пользовательские поля, но модель другая. На момент актуализации статьи официальный REST Bitrix24 умеет создавать пользовательские поля задач через task.item.userfield.add, но поддерживаемые типы для задач ограничены: строка, число, дата/время и boolean.

То есть формально API для пользовательских полей есть. Но для полной миграции Kaiten-полей этого недостаточно.

Например, если в Kaiten есть select или multi_select с набором значений, цветами, сортировкой и исторически накопленными значениями, то простое создание строкового поля в Bitrix24 теряет часть смысла. А если поле вообще специфическое - вроде голосования, каталога или вложения - прямого соответствия может не быть.

В проекте использовались два подхода.

Подход 1. Создавать пользовательские поля в Bitrix24

Для части полей мы пытались создавать соответствующие UF-поля в Bitrix24. В коробочной версии для этого использовались серверные скрипты с прямым доступом к базе, так как у Битрикса для этого нет штатных API методов. В облачной версии все пользовательские поля придется создать заранее.

Логика двухэтапная:

  1. Локально получить поля Kaiten и их значения.

  2. На сервере Bitrix24 создать нужные записи и маппинги.

Это позволяло точнее контролировать создаваемую структуру, но добавляло риск: мы уже не просто используем публичный REST API, а вмешиваемся в внутреннюю структуру коробочного продукта.

Подход 2. Дублировать пользовательские поля в описании задачи

Для сохранения читаемого контекста часть пользовательских полей форматировалась прямо в описание задачи:

<hr>
<h3>Пользовательские поля</h3>
<table>
  <tr><th>Название</th><th>Значение</th></tr>
  <tr><td>Приоритет клиента</td><td>Высокий</td></tr>
  <tr><td>Плановая дата</td><td>15.04.2025</td></tr>
</table>

Это не всегда красиво как модель данных, зато надёжно как миграция смысла.

Пользователь открывает задачу в Bitrix24 и видит важные поля, даже если они не идеально легли в нативную модель Bitrix24.

Сейчас я бы рассматривал такую стратегию как нормальный компромисс:

  • критичные поля переносить в нативные UF-поля;

  • сложные или плохо сопоставимые поля сохранять в описании;

  • обязательно формировать отчёт о том, какие поля были перенесены нативно, а какие - как информационный блок.

Грабля 4. Комментарии и историческая хронология

Перенести комментарии - не значит просто добавить новые сообщения к задаче.

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

В старой логике Bitrix24 для комментариев использовались методы семейства task.commentitem.*. В новых версиях Bitrix24 комментарии в новой карточке задачи переехали в чат задачи, а старые методы для комментариев помечены как deprecated, хотя часть операций ещё может работать в целях совместимости.

Это важный момент для тех, кто будет повторять такую миграцию сейчас: обязательно проверьте версию модуля задач Bitrix24 и актуальную документацию REST перед реализацией.

В нашем реальном проекте потребовался отдельный серверный слой для сохранения исторических дат комментариев. После создания комментариев мы обновляли дату в базе Bitrix24 через серверный скрипт:

python3 /root/kaiten-vps-scripts/update_comment_dates.py '{"601": "2025-07-08 14:22:00"}'

Скрипт обновлял POST_DATE в таблице b_forum_message.

В облачном Битрикс24, я полагаю, сохранить исходную дату комментария нет никакой технической возможности.

В идеальном мире такие вещи должны делаться только через официальный API. Но миграции в коробочные системы часто живут не в идеальном мире. Там есть исторические данные, внутренние таблицы, отличия версий, ограничения методов и требование бизнеса: «после перехода пользователи должны видеть нормальную историю».

Поэтому главное правило здесь такое: если вы идёте в базу напрямую, это должно быть осознанное миграционное действие, а не постоянная архитектура интеграции.

Грабля 5. Дочерние пространства и связи между карточками

В первом приближении кажется, что можно мигрировать пространство за пространством.

Но в Kaiten структура может быть глубже:

  • есть дочерние пространства;

  • карточки могут жить в подчинённой структуре;

  • доступ может наследоваться или задаваться через группы;

  • карточки могут быть связаны друг с другом;

  • у карточек могут быть родительские и дочерние отношения.

В репозитории для этого появилась отдельная логика:

  • автоматический поиск карточек из дочерних пространств;

  • рекурсивный поиск родительского пространства, для которого уже есть группа Bitrix24;

  • обработка parent_entity_uid, если нет прямого parent_id;

  • сохранение информации об исходном пространстве в описании задачи;

  • перенос Parent/Child отношений;

  • перенос связанных карточек;

  • установка связей уже после создания карточек, когда card_mapping.json заполнен.

Почему связи лучше устанавливать в конце?

Потому что при переносе карточки A связанная карточка B может ещё не существовать в Bitrix24. Если пытаться установить связь сразу, часть связей неизбежно потеряется. Поэтому надёжнее сначала создать все задачи, сохранить mapping, а затем вторым проходом восстановить отношения.

Это хороший пример общей закономерности миграций: некоторые данные нельзя корректно перенести за один проход.

Результаты миграции

В результате удалось перенести рабочие данные из Kaiten в коробочный Bitrix24 с сохранением основной структуры и контекста.

Что получилось хорошо:

  • пользователи сопоставлены по email;

  • пространства перенесены в группы Bitrix24;

  • участники добавлены в группы;

  • карточки перенесены в задачи;

  • активные карточки попали в рабочие стадии;

  • архивные и завершённые можно было переносить отдельным режимом;

  • комментарии перенесены;

  • файлы скачаны из Kaiten и загружены в Bitrix24;

  • ссылки в описаниях заменены;

  • пользовательские поля сохранены как данные и/или как читаемый блок;

  • связи между карточками восстановлены отдельным проходом;

  • миграцию можно было тестировать и запускать частями.

Ориентировочная статистика проекта:

Объект

Объём

Пользователи

89

Пространства

34

Карточки

около 1200

Файлы

около 450

Комментарии

около 3800

Пользовательские поля

десятки полей и значений

Главный результат даже не в том, что объекты появились в Bitrix24. Главный результат - команда не потеряла рабочий контекст.

Что я бы сделал иначе сейчас

Этот проект был написан под конкретный переход. Если бы я делал такую миграцию заново, я бы усилил несколько вещей.

1. Сначала сделал бы reconciliation report

То есть отдельный отчёт сверки:

В Kaiten найдено пользователей: 100
Перенесено в Bitrix24: 89
Пропущено: 11
Причины: нет email, архивный пользователь, дубль

В Kaiten найдено карточек: 1400
Перенесено: 1200
Пропущено: 200
Причины: архивные, финальная колонка, нет маппинга пространства

Файлов найдено: 470
Файлов перенесено: 450
Ошибок скачивания: 20

Логи полезны разработчику, но бизнесу и проектной команде нужен понятный итоговый отчёт: что было, что стало, что не перенеслось и почему.

2. Жёстче разделил бы «данные» и «историю»

Перенести текущие карточки - одна задача.

Перенести историю обсуждений, даты, авторов, файлы, ссылки и связи - другая.

Для бизнеса часто критичны именно текущие активные задачи. Исторический слой можно переносить отдельным этапом, с другими требованиями к точности и срокам.

Я бы прямо разделил режимы:

current-only       # только активная рабочая структура
with-history       # комментарии, даты, файлы
archive-full       # полная историческая миграция

3. Перепроверил бы актуальные методы Bitrix24 REST

За время жизни проекта API меняется.

На момент актуализации статьи у Bitrix24 есть REST-методы для пользовательских полей задач, но они ограничены по типам. Также изменилась модель комментариев в новой карточке задач: обсуждения уезжают в чат задачи, а старые методы комментариев помечаются как устаревшие.

Поэтому старое утверждение «это невозможно через API» сегодня лучше заменить на более точное:

часть операций можно делать через официальный REST, но не вся семантика Kaiten-полей и исторических комментариев переносится один к одному. Для конкретной коробочной версии и конкретного набора данных нужно отдельно проверять, какие операции делать через REST, какие через D7/Bitrix Framework, а какие остаются миграционными SQL-операциями.

4. Добавил бы слой D7 вместо части прямого SQL

Для коробочного Bitrix24 прямой SQL работает, но это самый жёсткий вариант.

Более аккуратный путь - писать серверные скрипты не только на уровне SQL, а по возможности использовать внутренние API Bitrix Framework / D7. Это всё равно требует доступа к серверу, но меньше завязывает мигратор на конкретные таблицы.

Прямой SQL я бы оставил как последний уровень: когда REST и D7 не дают нужного результата.

5. Сделал бы мигратор более продуктовым

Сейчас это инженерный инструмент. Для повторного использования я бы добавил:

  • веб-интерфейс настройки миграции;

  • экран предварительного анализа;

  • отчёт расхождений;

  • прогресс по этапам;

  • очередь заданий;

  • безопасный restart;

  • режим rollback для созданных объектов;

  • настройку правил маппинга через UI;

  • экспорт итогового отчёта в PDF/Excel;

  • уведомления в Telegram или Bitrix24.

Но это уже превращает одноразовый мигратор в отдельный продукт.

Выводы

Миграция проектной системы - это не перенос таблицы задач.

Это перенос рабочей памяти команды.

В карточках живут не только заголовки и описания. Там есть обсуждения, решения, вложения, ссылки, пользовательские поля, связи, статусы, исторические даты и неявная логика работы.

REST API решает только часть задачи. Вторая часть - это проектирование соответствий, контроль целостности, повторяемость, проверка результата и честное принятие компромиссов.

В нашем случае Python-мигратор позволил перенести данные из Kaiten в коробочный Bitrix24 и сохранить основной рабочий контекст. Но главный урок был не в выборе httpx, asyncio или Pydantic.

Главный урок такой: если система целый год была местом, где команда думала, спорила, принимала решения и прикладывала файлы, то миграция такой системы должна относиться к этим данным не как к «записям в API», а как к памяти организации.

Именно её и нужно переносить.

Полезные ссылки