...

Кейс миграции данных в ELMA365 стандартным импортом из XLS/CSV

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем lunin, 6 июн 2025 в 09:18.

  1. lunin

    lunin Новичок

    В этой статье опишу опыт миграции данных на одном из проектов импортозамещения и подводные камни, найденные на этом пути. Источником данных был MS Dynamics, а миграция выполнялась с помощью стандартного для ELMA365 механизма импорта данных.
    Стартовые условия:

    • система редакции Enterprise, 7 изолированных решений, 5 кастомных интеграций;
    • кастомное решение с использованием элементов системных решений CRM и Проекты, большая часть которого разработана под требования заказчика;
    • около 50 приложений, данные которых нужно мигрировать;
    • количество записей: от нескольких элементов — в справочниках и до 1,5 миллионов — в приложениях.
    Ошибки, которые возникали, привожу текстом, чтобы их решение было удобно найти через поиск в статье.

    1. Выбор способа миграции
    Из названия статьи уже понятно, какой способ был выбран, но изначально рассматривалось несколько вариантов. Опишу их плюсы и минусы. Возможно, это кому‑то поможет сделать выбор в аналогичной ситуации.
    Как правило, любая миграция состоит из трёх шагов:

    • Выгрузка данных из источника.
    • Преобразование выгруженных данных в нужный формат целевой системы.
    • Загрузка данных в целевую систему.
    (общепринятый термин ETL — от англ. Extract, Transform, Load)
    Соответственно, при оценке вариантов миграции нужно понимать, как будет выполняться каждый из этих шагов.

    Миграция из БД в БД с помощью ETL-процесса
    Классический вариант переноса данных между базами, который был сразу отброшен из-за сложной структуры хранения данных в БД ELMA365. Вендор крайне не рекомендует прямые операции в БД. Затраты на разработку и последствия такой миграции невозможно было адекватно оценить.
    Миграция вручную посредством MS Excel
    Такой вариант изначально рассматривался как приоритетный. Предполагалось, что заказчик выгрузит данные из MS Dynamics в XLS-файлы, затем мы преобразуем их средствами Excel и загрузим в ELMA365 стандартным импортом. Вариант на первый взгляд казался самым простым и быстрым, но от него отказались по следующим причинам:

    1. Множество ручной работы, и как следствие — человеческие ошибки.
    2. MS Excel в зависимости от версии и настроек на разных ПК может искажать форматы данных (даты, преобразование чисел в строки и т. д.), что добавит ещё больше ручной работы.
    3. По информации от опытных коллег, есть риск, что файлы при импорте придётся разделять на части по несколько тысяч записей, чтобы избежать ошибок. А это снова ручная работа.
    4. Самое главное — с учётом масштаба проекта и высоких требований заказчика к точности миграции стало понятно, что потребуется много итераций. Придётся многократно проверять качество загрузки, находить и исправлять ошибки, прежде чем загрузить данные даже в тестовый контур, не говоря уже об итоговой миграции на продуктив. На каждой итерации пришлось бы повторять ручную обработку данных в Excel. Можно конечно вспомнить, что такое макросы и VBA, но это как-то совсем не современно :)
    Миграция с помощью сервиса и/или бизнес-процессов в ELMA365
    Такой подход, казалось бы, решал все проблемы предыдущих двух вариантов. Выгрузка данных выполняется запросом в БД Dynamics с помощью специального микросервиса, далее — преобразование скриптами в бизнес-процессе, и тот же процесс через стандартный блок создания элемента безопасно записывает данные в ELMA365 без прямых операций с БД. Никаких ручных операций и запускать можно сколько угодно раз.
    Но от этого варианта отказались из-за его высокой стоимости и долгой разработки. Несмотря на наличие готового микросервиса, нам пришлось бы проводить аналитику, писать запросы в БД Dynamics, скрипты преобразования, бизнес-процессы. Оценка и сроки не устраивали заказчика. Кроме того, никакую часть работ при таком варианте он не мог взять на себя.
    Миграция посредством SQL и стандартного импорта из CSV
    В результате совместного творчества с заказчиком родился следующий вариант:
    1. Мы анализируем структуру данных Dynamics и составляем маппинг — таблички с сопоставлением полей Dynamics и ELMA365, и описанием логики преобразования данных на человеческом языке.
    2. Заказчик на основе маппинга пишет SQL-запросы, которые выдают данные сразу в структуре, нужной для ELMA365. То есть одновременно решается задача выгрузки и преобразования.
    3. Полученные данные сохраняются в CSV-файлы и загружаются стандартным импортом в ELMA365.
    Это вариант одновременно решал проблему ручной работы, сокращал стоимость и позволял разделить задачи между нами и заказчиком. На нём и остановились.

    2. Отладка миграции: возможные ошибки и их решение
    На составление маппинга и написание SQL-запросов ушло примерно 2 месяца. Далее началась отладка — полученные CSV-файлы стали загружать на тестовый стенд и разбирать ошибки.
    Наивно ожидать, что все ошибки вскроются сразу. Они открываются последовательно — пока не исправишь одну, не узнаешь о наличии следующей. Соответственно, процесс отладки получается итерационным и трудно прогнозируемым. Это нужно учитывать при планировании: фиксированные сроки и стоимость для миграции крайне не надёжны.
    Ошибки в исходных данных
    Найденные ошибки в данных — это индивидуальные особенности конкретной системы-источника, и исправляются они на уровне базы данных или скриптов. Просто примите как факт при планировании миграции, что в любой системе, прожившей несколько лет, будут нестыковки: пустые значения в обязательных полях, несовпадение типов данных, несоответствие данных бизнес-логике и другие сюрпризы.
    Ошибки в синтаксисе (найди лишнюю запятую)
    Следующий тип ошибок связан с тем, что для импорта используется формат CSV с разделителями в виде запятой. Соответственно, если запятая встретится где-либо в данных, при импорте она будет воспринята как разделитель. Такая проблема была очевидна заранее, поэтому на уровне скриптов все значения экранировались кавычками, но этого оказалось недостаточно:
    • во-первых, оказалось, что запятые могут присутствовать и в названиях атрибутов, например Сумма, руб. Поэтому заголовки тоже нужно экранировать или вообще убирать отображаемые имена, оставляя только наименования атрибутов;
    • во-вторых, в значениях могут встречаться кавычки, что ломает логику с экранированием. В таких случаях необходимо двойное экранирование;
    • в-третьих, в текстовых полях встретились переносы строк, которые в CSV-файле воспринимаются как перенос всей строки — начало следующего элемента. Волевым решением от переносов просто избавились.
    Пример 1
    Ниже приведён пример ошибки, которую вызвала лишняя запятая в заголовке: «record on line 2: wrong number of fields». Как видно, текст ошибки далеко не всегда помогает найти причину.
    Строка 1 содержит заголовки таблицы (наименования атрибутов), строка 2 — первый элемент. Очень долго искали ошибку в строке 2, а на самом деле ошибка была из-за лишней запятой в наименовании атрибута в строке 1: было определено неверное количество столбцов таблицы и потому в первом же элементе (строка 2) не совпало количество столбцов, их было на один меньше.
    upload_2025-6-6_12-7-49.png
    Пример 2
    Еще один пример — ошибка, вызванная недостающей кавычкой: «extraneous or missing “ in quoted-field». Ошибка была действительно в 3-й строке, но что означает 299 в тексте ошибки, так и осталось непонятным: не совпало ни с номером символа, ни с номером колонки, в которой фактически была ошибка.
    upload_2025-6-6_12-8-7.png
    Несоответствие структуры приложения и файла (лишние атрибуты)
    При составлении маппинга мы делали экспорт данных с dev-стенда ELMA365, чтобы получить основу для таблицы, т.е. все атрибуты приложения. При этом не все атрибуты требовалось заполнять при миграции. В результате в CSV-файлах содержалось множество пустых атрибутов. Так лучше не делать, по нескольким причинам:
    • лишние колонки — это лишнее время обработки, и когда у вас таблица 100х1000000, то это чувствительно;
    • сложнее искать ошибки, т. е. те самые лишние запятые;
    • при перезаписи существующего элемента наличие колонки с пустым значением воспринимается как «стереть существующее значение» (это может проявить себя при «дозагрузке» данных);
    • устаревшие атрибуты с dev-стенда могут отсутствовать на целевом стенде, что будет вызывать ошибку импорта.
    Совет: выгружать в CSV-файл только колонки с нужными значениями, пустые — удалять.
    Ошибки при наличии связанных приложений
    В ELMA365 связь приложений реализуется через атрибуты-ссылки типа Один или Несколько. А еще связи могут быть «двусторонними», если указан параметр Поле для связи. В справке по платформе описано использование этого параметра для виджета Связанные элементы.
    upload_2025-6-6_12-10-9.png
    Рассмотрим пример. У приложения Служебные записки на закупку есть атрибут-ссылка на приложение Заявки на закупку, который так и называется — Заявка на закупку. В этом атрибуте указано поле для связи Служебная записка, который содержит ссылку на приложение Служебные записки на закупку.
    Таким образом, если пользователь в Служебной записке выбирает элемент приложения Заявка на закупку, то автоматически в этом элементе заполняется атрибут-ссылка на Служебную записку. Тоже самое происходит при заполнении связей при импорте.
    При подготовке к импорту для сохранения всех связей было решено переносить данные с «родными» идентификаторами из MS Dynamics (к счастью, их формат подходил), чтобы не пришлось после загрузки отдельно заниматься связыванием элементов. Причём, ELMA365 позволяет загрузить в атрибут-ссылку id элемента, которого ещё не существует, а после его появления связь корректно «стыкуется» и работает. Поэтому мы предположили, что последовательность загрузки не важна. На рисунке ниже показано, как выглядит ссылка на несуществующий элемент в таблице (на форме атрибут выглядит как незаполненный), до загрузки соответствующего приложения.
    upload_2025-6-6_12-10-40.png
    Однако, мы не учли поведение системы при «двусторонних» связях и во всех CSV-файлах были заполнены все атрибуты-ссылки с обеих сторон связи. Сначала выгрузка шла хорошо и элементы загружались, в связях отображалось ожидаемое «еще элементов…». Но дальше начали возникать подобные ошибки: «duplicate key value violates unique constraint»:
    upload_2025-6-6_12-10-55.png
    С помощью перевода и экспериментов мы расшифровали ошибку так:
    1. Загрузился элемент приложения А со связью на приложение Б.
    2. В элементе приложения Б автоматически записалась ссылка на элемент приложения А.
    3. При загрузке элементов приложения Б обнаруживается дубликат, т. е. загружаем связь, которая уже есть
    Проделав несколько неудачных экспериментов, мы выработали итоговый подход, который привёл к успешной загрузке связанных приложений:
    1. «Двусторонние» связи следует заполнять только с одной стороны, т. е. только в одном из связанных приложений. При этом желательно выбирать для заполнения ту сторону, на которой элементов меньше. Например, если связь ОдинНесколько, заполнять связи лучше в приложении с опцией Один. Причины такого решения две:
      • меньше вероятность ошибки;
      • длина строки в CSV-файле не бесконечная — слишком много идентификаторов в одну строку просто не влезет.
    2. Во втором приложении связь не просто остается пустой, а колонка полностью удаляется из CSV-файла, иначе при повторной загрузке связь очистится.
    3. Первым загружается приложение без связи, затем — приложение с заполненной связью. Таким образом, выстраивается последовательность загрузки всех приложений с учётом взаимосвязей.

    3. Отладка миграции: обработчики событий и бизнес-процессы
    В ELMA365 есть несколько вариантов автоматического запуска скриптов и процессов при создании или изменении элемента приложения. При миграции данных важно оценить, какие из этих скриптов необходимо применить к импортированным данным, а какие — наоборот, нужно исключить.
    Формирование названия элемента по шаблону
    Это стандартная настройка приложения. При импорте она не срабатывает, но в дальнейшем при работе пользователей с загруженными элементами настройка будет применяться при каждом сохранении элемента.
    Таким образом, важно убедиться, что:
    • название присутствует в файле импорта. Тогда все элементы получат название сразу после импорта;
    • при дальнейшем применении шаблона название элемента не «сломается» из-за того, что в шаблоне используется параметр, который не заполняется при импорте.
    upload_2025-6-6_12-13-22.png
    Скрипты на формах создания/редактирования элементов
    Логика аналогична предыдущему пункту — при импорте никакие формы не будут открываться и скрипты не будут выполняться. Но в дальнейшем при редактировании элемента пользователем скрипт запустится. Поэтому важно убедиться, что при миграции все необходимые для скриптов данные были загружены либо в скриптах присутствуют проверки на их отсутствие.
    Обработчики событий
    На форме импорта данных присутствует чек-бокс Игнорировать обработчики событий. Он влияет на запуск обработчиков событий создания элементов приложения, настроенных в разделе Администрирование > Модули.
    Такие обработчики, если они есть в конкретном решении, написаны не для миграции, а для ежедневной работы в системе. Кроме того, что их запуск при импорте создаёт дополнительную нагрузку, обработчики содержат алгоритмы, которые могут быть либо полезны либо, наоборот, вредны при импорте. При подготовке к миграции проанализируйте, какие обработчики настроены на каждое приложение, как они повлияют на результат импорта и нужно ли игнорировать каждый из них.
    В нашем случаем нашлись как приложения, по которым обработчики нужно обязательно запускать, так и те, которые нужно игнорировать. Отдельные обработчики пришлось доработать, чтобы их скрипты при импорте выполнялись частично.
    Рассмотрим пример.
    Обработчик, связанный с приложением, в «обычной жизни» создаёт папку для файлов, запускает бизнес-процесс и выполняет ряд изменений в данных. Папка нам при импорте нужна, процессы — только для определённых записей в зависимости от их статуса, а изменения в данных не нужны для всех записей. Для решения задачи мы добавили в обработчик параметр Режим работы с тремя значениями:
    • загрузка записей в финальном статусе — обработчик только создаёт папку;
    • загрузка записей в промежуточном статусе — обработчик создаёт папку и запускает бизнес-процесс;
    • штатный режим — выполняется вся первоначальная логика обработчика.
    Файл с данными для импорта этого приложения делился на две части (по статусам) и между загрузками этих частей нужно было переключить режим обработчика.
    Здесь нашёлся еще один подводный камень. Обработчик событий выполняется асинхронно, для каждого загруженного элемента алгоритм запускается заново. В зависимости от количества элементов и сложности алгоритма обработка всех загруженных записей может занимать длительное время. Если переключить режим обработчика, не дождавшись завершения обработки всех элементов, очередной элемент будет обрабатываться уже с учётом переключённого режима. В нашем случае это привело к запуску несколько тысяч лишних бизнес-процессов по элементам с финальным статусом.
    Чтобы избежать такой ситуации, нужно отслеживать завершение обработки всех загруженных записей. Сделать это можно без усложнения самого обработчика — по наличию видимых результатов. В нашем случае завершение обработки подтверждали тем, что не оставалось записей без созданных папок.
    upload_2025-6-6_12-15-4.png
    Бизнес-процессы, связанные с приложением
    В ELMA365 есть возможность связать бизнес-процесс с приложением. Такой процесс будет автоматически запускаться при создании элемента приложения, но… только при создании пользователем. При импорте элементов такие процессы не запускаются. Хорошо это или плохо, зависит от конкретной реализации в решении. Если есть необходимость запустить процесс при импорте, решить это можно, например созданием обработчика событий.
    upload_2025-6-6_12-15-40.png
    Встроенные алгоритмы типовых решений
    Типовые решения могут содержать дополнительные алгоритмы, срабатывающие при создании элемента приложения, которые не видны на уровне low-code.
    Например, в системном решении ELMA365 Проекты при создании элемента приложения Проекты создаётся папка проекта, которая далее используется на вкладке Файлы типовой формы проекта.
    Этот алгоритм срабатывает как при создании элемента пользователем, так и при импорте. Никакие чекбоксы на его запуск не влияют.

    4. Миграция данных и бизнес-процессы
    Данный пункт относится уже не столько к самой миграции, столько к тому, как работать пользователям после неё. Чтобы эта работа не доставляла лишней боли, подготовиться нужно заранее.
    Как правило, при проектировании решения закладывается логичный порядок работы: сначала создаётся элемент приложения, потом по нему запускается процесс, в ходе выполнения процесса могут заполняться атрибуты и меняться статусы.
    Маловероятно, что учитываются кейсы, когда, например, на шаге 2 элемент имеет статус, который он никак не может получить раньше 10-го шага. При миграции такое возможно, т .к. данные загружаются в том «текущем» состоянии, до которого они дошли в исходной системе, а процесс в новой системе запускается заново.
    На практике это может привести:
    • к неудобствам, когда пользователям придётся «прощелкать» лишние этапы процесса, чтобы дойти до актуального;
    • к невозможности работы, когда процесс падает с ошибкой или не даёт пользователю пройти какой-либо шаг.
    Универсального решения таких проблем нет. Нужно анализировать каждый процесс, тестировать его поведение с разными загруженным записями и определять решения: инструктировать пользователей или дорабатывать процесс, добавляя дополнительные переходы или проверки.
    Решение может быть организационным: договориться с пользователями, что все процессы завершаются в старой системе. Только после этого элементы загружаются в новую систему с финальными статусами и без запуска процессов. И в новой системе процессы запускаются только по новым записям и выполняются с начала. Возможность такого подхода определяется особенностями конкретного бизнеса, длительностью и количеством реальных процессов.

    Буду рад, если эта статья поможет кому-то не повторить аналогичные ошибки или быстрее с ними разобраться. В нашем проекте на момент публикации статьи завершается тестовая миграция, предстоит миграция на продуктив. И надеюсь, дополнять статью не придётся…