Миграция с одной ERP-системы на другую

в 7:14, , рубрики: ERP-системы, lsFusion, Анализ и проектирование систем, Блог компании lsFusion, миграция, розничная торговля, Управление продуктом, управление проектами
image

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

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

Трудности

День, когда Байсон почтил твою деревню своим присутствием, стал для тебя самым главным в жизни. Но для меня это был вторник.
— Байсон, «Уличный боец»

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

Когда любой человек меняет один продукт на другой, то первым делом он замечает то, что у него забрали и пока не осознает того, что получил. Поэтому часто первой реакцией является: “что за ерунду вы мне тут подсунули — сделайте так, как было”. У нас на практике была ситуация, когда один пользователь жаловался, что ему неудобно новое действие, которое в старой системе делалось в пять переходов между формами. Но оно у него было отточено до такого автоматизма, что человеку действительно в первое время было быстрее его сделать, чем делать два новых, но гораздо более простых действия.

Еще одна сложность заключается в том, что при переходе заказчики любят изменять бизнес-процессы под свое видение. К этому нужно относиться с большой осторожностью, так как текущие процессы “as is” гарантированно работают. Процессы, заложенные в продукт, как правило, тоже работают, так как используются у других клиентов. А вот новые изобретения могут в теории выглядеть красиво, но на практике упереться в какую-то мелочь, из-за чего весь процесс может застопориться.

Сценарии

На практике существует два основных сценария по переходу с одной системы на другую:

Одномоментный

Определяется дата перехода на новую систему. Разрабатываются все функции по выгрузке данных из старой системы и загрузки их в новую. В день X ночью запускается миграция, и на утро все пользователи со всеми бизнес-процессами начинают работать в новой системе по новому.

На бумаге все красиво. Но на практике возникают как минимум четыре категории проблем:

  1. Обучение. В момент перехода, как правило, многие обученные пользователи забывают напрочь то, чему их учили. Они начинают тыкать все подряд, и в итоге останавливают свою работу со словами “ничего не работает”. Решить эту проблему можно при помощи сотрудников первой линии поддержки, которые могут помочь в этот момент. Но таких запросов в первый день перевода будет столько, что выполнить их имеющимся количеством сотрудников будет невозможно. Или надо иметь способ каким-то образом быстро расширять их количество на время перевода, что также достаточно тяжело сделать.
  2. Доработки и ошибки. Как правило, новая ERP-система несет с собой новые бизнес-процессы (иначе зачем ее менять). Понятно, что непосредственно перед внедрением их каким-то образом прогоняли, но в момент реальной “проверки боем” обычно вылезает огромное количество нюансов, из-за которых зачастую процессы не могут выполняться. Соответственно, опять количество таких доработок с немедленным приоритетом растет как снежный ком, и возникает резкая потребность в ресурсах программистов, которые физически не смогут закрывать такое количество запросов.
  3. Данные. Обычно разработчики старой системы не очень сильно горят желанием помогать с выгрузкой данных (для них это вообще достаточно печальное событие потери клиента), а если и помогают, то качество выгрузки оставляет желать лучшего. И нестыковки, как правило, обнаруживаются в первые дни эксплуатации новой системы. В результате требуется исправлять выгрузку, и каким-то образом перезагружать данные с учетом уже изменившихся во время эксплуатации.
  4. Производительность. В процессе тестирования не так легко воспроизвести полностью “реальную” нагрузку на систему. Если базовый функционал продукта обычно уже оттестирован и показывает нормальные параметры производительности, то “личные” доработки клиенту могут при определенных обстоятельствах “подсаживать” сервер. Это, в свою очередь, также требует ресурсов программистов для оптимизации.

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

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

Постепенный

При таком подходе происходит горизонтальное (по процессам) или вертикальное (по пользователям) разделение системы на части, которые внедряются по очереди друг за другом. Это позволяет разнести все вышеописанные проблемы во времени и решать их по мере поступления меньшим количеством сил.

Дальше я буду описывать процесс миграции ERP-системы на базе открытой и бесплатной платформы lsFusion для розничной сети магазинов, так как мы специализируемся именно на этой индустрии. Но такой же принцип мы применяли и у клиентов из других областей.

Считывание данных

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

Сперва нужно определить в каких СУБД хранится необходимая информация и как к ней обратиться. За последние несколько лет нам пришлось столкнуться со следующими СУБД, с которыми работали старые системы:

  • Access. Так как платформа написана на Java, то удалось откопать древнюю библиотеку, которая умела подключаться к mdb (правда только на чтение). Работала она весьма своеобразно, так как несмотря на фильтры считывала всю таблицу целиком, но тем не менее этого хватило. Нам повезло, что служба IT заказчика достаточно неплохо знала структуру базы данных и рассказала нам, что и где хранится.
  • MS SQL. Старой системой была Axapta 2000х годов. Нам настроили доступ к тестовой системе, где мы запустили GUI и SQL Profiler. Дальше заходили по очереди в справочники, и смотрели какие запросы формирует Axapta. По ним легко было понять, где конкретно хранится та или иная информация.
  • Visual Foxpro. Тут было все просто, так как был переход с наших же legacy систем и получить информацию не составило труда. Единственной сложностью было найти Java библиотеку, которая умеет работать не с DBASE таблицами, а именно с Visual Foxpro форматом.
  • Oracle. Переход с коробочной программы Супермаг. Работали по схеме аналогичной схеме Axapta: GUI и запросы в SQL Developer'е по последним выполненным запросам. Структура базы и интерфейсы там конечно были значительно попроще, чем в Axapta, поэтому больших проблем не возникло.
  • Progress/OpenEdge. Переход с коробочной программы Gestori. Учитывая, что СУБД, мягко говоря, совсем не популярная, то никаких профайлеров мы не нашли. Однако там был dump в текстовые файлы. Это позволило просто заходить в GUI тестового сервера и поиском по текстовым файлам искать в них данные, которые видны на экране. К счастью, на официальном сайте этой СУБД есть возможность скачать JDBC драйвер, и делать к базе SQL запросы. Дальше уже было дело техники.

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

Мастер-данные

Все начинается с импорта мастер-данных. В частности, для розничной торговли первыми реализуется импорт справочников групп товаров, товаров, контрагентов и магазинов. При постепенном переходе существует два подхода к импорту мастер-данных:

  • Единовременный импорт данных, а затем двойной ввод в старую и новую систему.
  • Постоянное инкрементное обновление данных из старой в новую систему.

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

В идеале, старая система могла бы накапливать изменения и каким-то образом сообщать, что требуется переимпортировать. Но обычно никто ничего в старой системе менять и дорабатывать не будет. Поэтому единственная рабочая схема, которую мы используем выглядит следующим образом:

  1. Идет запрос к справочнику старой системе, который считывает из него все данные.
  2. Считанные данные сравниваются с текущими в новой системе и обнаруживаются изменения.
  3. Изменения записываются в единой транзакции в новую систему.

Код на платформе для импорта справочника товаров будет выглядеть следующим образом:

importItems 'Импортировать товары' {
   NEWSESSION {
       LOCAL file = FILE ();
       // считываем данные из старой системы
       READ 'jdbc:sqlserver://localhost;databaseName=items;User=import;Password=password@SELECT id, name FROM items' TO file;
  
       LOCAL id = STRING[20] (INTEGER);
       LOCAL name = ISTRING[100] (INTEGER);
       // загружаем их в свойства
       IMPORT TABLE FROM file() TO id, name;
      
       //создаем новые товары
       FOR id(INTEGER i) AND NOT item(id(i)) NEW b = Item DO {
           id(b) <- id(i);
       }
      
       // меняем значения
       FOR id(Item b) = id(INTEGER i) DO {
           name(b) <- name(i);
       }
      
       // удаляем товары, которых нету в запросе
       DELETE Item b WHERE b IS Item AND NOT [ GROUP SUM 1 BY id(INTEGER i)](id(b));
      
       APPLY;
   }
}

Работать это будет по следующей схеме:

  • READ считает весь справочник с кодом и наименованием в память
  • IMPORT запишет все данные в локальные свойства (а именно во временные таблицы в базе данных)
  • Первый цикл создаст все товары, которых еще нету в новой базе данных (поиск по коду товара). Несмотря на то, что там написан FOR, он скомпилируется в один запрос к базе, который будет только работать с временными таблицами.
  • Второй цикл обновит поля для всех товаров (в том числе и вновь созданных). При этом во временные таблицы будут записаны только изменившиеся значения.
  • Третье действие DELETE обнаруживает все товары, которые есть в новой базе, но нет в считанных данных и удалит их.
  • APPLY начнет транзакцию и запишет все изменения в базу данных на основе сформированных предыдущими командами временных таблиц.

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

Так как все действия компилируются в SQL запросы без какого-либо итерирования, то выполняется это все достаточно быстро и безопасно. Обычно обмен справочником товаров размером в 100-200 тысяч записей занимал пару минут. При этом, так как PostgreSQL является версионником, то никакой блокировки работы пользователей в это время не происходит.
Таким же образом постоянно синхронизируются прайсы поставщиков, ассортиментные матрицы, графики заказов и прочая необходимая для работы информация. Тут однако может возникать проблема, что доменная логика в старой системе не совпадает с новой доменной логикой. Например, у нас в системе есть история версий матриц и понятие уровней товара внутри матрицы. Если в старой системе текущий ассортимент магазинов хранится в плоском виде как boolean для магазина и товара, то создается одна матрица для каждого магазина ровно с одним уровнем.

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

Процесс

В розничной торговле процесс перевода мы обычно делим вертикально по магазинам и одновременно горизонтально от процессов магазина к процессам офиса.

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

Пример исходного кода

importInventory 'Импортировать остатки' {
    NEWSESSION {
        LOCAL file = FILE ();
        // считываем данные из старой системы
        READ 'jdbc:sqlserver://localhost;databaseName=items;User=import;Password=password@SELECT idSku, quantity, idDoc, dateDoc, idDetail FROM inventory' TO file;
        
        LOCAL idSku = STRING[20] (INTEGER);
        LOCAL quantity = NUMERIC[16,3] (INTEGER);
        // Здесь idDoc, dateDoc и idDetail - это коды документов последнего поступления в старую систему по товару
        // Если получить их из старой системы не получается, то можно генерировать на основе кода товара
        LOCAL idDoc = STRING[20] (INTEGER);
        LOCAL dateDoc = DATE (INTEGER);
        LOCAL idDetail = STRING[20] (INTEGER);
        // загружаем их в свойства
        IMPORT TABLE FROM file() TO idSku, quantity, idDoc, dateDoc, idDetail;      
        
        // создаем новые документы поступлений
        FOR idDoc(INTEGER i) AND NOT receipt(idDoc(i)) NEW r = Receipt DO {
            id(r) <- idDoc(i);
        }
        
        // обновляем атрибуты документов поступлений
        FOR id(Receipt r) = idDoc(INTEGER i) DO {
            date(r) <- dateDoc(i);
        }
        
        // создаем строки, которых еще нету
        FOR idDetail(INTEGER i) AND NOT receiptDetail(idDetail(i)) NEW d = ReceiptDetail DO {
            id(d) <- idDetail(i);
        }
        

        // обновляем атрибуты документов поступлений
        FOR id(ReceiptDetail d) = idDetail(INTEGER i) DO {
            receipt(d) <- receipt(idDoc(i));
            quantity(d) <- quantity(i);
        }
        
        // удаляем документы и строки поступления, которых уже нету в старой системе
        DELETE Receipt r WHERE id(r) AND NOT [ GROUP SUM 1 BY idDoc(INTEGER i)](id(r));
        DELETE ReceiptDetail d WHERE id(d) AND NOT [ GROUP SUM 1 BY idDetail(INTEGER i)](id(d));
        
        // начинаем транзакцию и применяем изменения в базу данных 
        APPLY;
    }
}

За несколько недель до старта магазина подключается импорт реализации из POS систем. Это нужно для того, чтобы были исторические данные для формирования заказов в первые дни работы.

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

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

После того, как на первом магазине выявляются и решаются все проблемы, описанные выше, где-то через пару недель переводятся еще 2-3 магазина. Опять итерация выявления и исправления проблем. И дальше очень быстро переводятся все остальные, в зависимости от ресурсов службы поддержки пользователей клиента. Все это время мастер-данные продолжают вводится в старой системе для того, чтобы могли функционировать магазины на старой системе.

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

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

Заключение

Несмотря на кажущуюся сложность перехода с одной системы на другую, пройдя этот путь несколько десятков раз, понимаешь, что при отлаженной схеме все на самом деле не так страшно. Больше проблем возникает с тем, что большинство сотрудников клиента достаточно консервативны и при малейшей возможности требуют сделать “так, как было”. И тут очень важно наличие у заказчика человека с достаточными полномочиями, который сможет сравнить старые и новые процессы, определить какие из них оптимальнее и суметь “переломить” сотрудников. Или дорабатывать процесс по схеме “как было”, если окажется наоборот.

Автор: weissruss

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js