Перейти к основному содержанию
`Ctrl`+`K`

# Работа механизма DataInstall

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

- регистрации состояний и связей между ними;

- создания дефолтных записей;

- добавления типов объектов;

- заполнения справочников;

- создания служебных данных, необходимых для корректной работы модуля.

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

- через инструмент разработки с поддержкой генерации метаданных;

- или через команду `upgrade` по SSH.

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

## Ключевые особенности

- Автоматическое выполнение при генерации базы данных — запуск происходит через генератор таблиц (в среде разработки или через команду `upgrade`), как часть жизненного цикла развертывания.

- Управление версиями скриптов — система выполняет скрипт только при первом появлении или изменении версии, сравнивая её с записями в таблице `Btk_DataInstallScript`.

- Поддержка зависимостей между скриптами — перед выполнением проверяется наличие всех зависимостей; отсутствие требуемого скрипта — ошибка.

- Отложенное выполнение (`delayed`) — скрипты с флагом `delayed` не выполняются в момент генерации, а регистрируются для запуска фоновой задачей в нерабочее время.

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

## Общая схема процесса

Механизм DataInstall выполняется как часть обновления системы и проходит через семь последовательных этапов, обеспечивающих корректную и предсказуемую установку начальных данных.

1. Загрузка информации о ранее выполненных скриптах
   Система загружает данные о всех скриптах, которые уже были запущены ранее, включая их текущие версии и статус (обычный или отложенный). Это позволяет избежать повторного выполнения одних и тех же скриптов.

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

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

4. Проверка зависимостей между скриптами
   Проверяется, что все зависимости каждого скрипта будут выполнены до него. Также контролируется, что обычный скрипт не зависит от отложенного — такая связь считается некорректной.

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

6. Выполнение скриптов по очереди
   Скрипты запускаются в установленном порядке. Если скрипт обычный — он выполняется немедленно. Если отложенный — он регистрируется для фонового запуска. Ошибки одного скрипта не останавливают выполнение остальных.

7. Фиксация результатов в базе данных
   После завершения обновления система фиксирует информацию о всех успешно выполненных скри

## Хранение истории выполнения скриптов

Информация о выполненных DataInstall-скриптах хранится в таблице `btk_datainstallscript`.
Она используется системой для определения, какие скрипты уже были запущены, и требуется ли их повторное выполнение при обновлении.

Таблица содержит следующие ключевые поля:

- Имя — системное имя скрипта;

- Версия — текущая версия скрипта;

- Дата прогона — дата и время последнего успешного выполнения скрипта;

- Пользователь — учётная запись, под которой было установлено подключение к базе данных при запуске скрипта.

Примечание

Подключение происходит только на уровне БД, пользователь не входит в саму ERP-систему.

- Если указано `sys` — скрипт запускался через SSH с правами системного пользователя.

- Если указан IP-адрес — подключение было выполнено напрямую с локального компьютера.

## Создание DataInstall-скриптов

DataInstall полностью определяется в коде, а именно:

- в `odm.xml` для классов;

- в `pkg.xml` для пакетов.

Разработчик сам пишет скрипт прямо в XML-метаданных, где указывает:

- имя скрипта;

- его версию;

- зависимости;

- флаг отложенного выполнения (если требуется);

- текст JEXL.

Разработчик вручную добавляет в `odm.xml` или `pkg.xml` секцию `<dbData>` и прописывает в ней скрипт:

    <dbData>
      <script name="dataInstall" version="1">
          <install>SomeApi.dataInstall();</install>
      </script>
    </dbData>

Скрипт может содержать:

- прямой JEXL-код;

- вызов API-классов (частый вариант),

После этого JEXL-вызов будет выполнен генератором данных при обновлении версии скрипта.

## Использование

Ниже приведен минимальный пример «от и до»: начиная с объявления класса и заканчивая Scala-API, который вызывается DataInstall-скриптом.

1. Фрагмент `Rpltst_ForDeleteTest.odm.xml`

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <class xmlns="http://www.global-system.ru/xsd/global3-class-1.0"
           name="Rpltst_ForDeleteTest"
           caption="Тест удаления"
           supertype="reference"
           cardEditor.representation="Card"
           listEditor.representation="List"
           viewOptions.openCardType="mdi">

        <attributes>
            <attr name="id" attribute-type="Long" caption="Идентификатор" order="-1" type="basic" isVisible="false"/>
            <attr name="idClass" attribute-type="Long" caption="idClass" order="-2" type="basic" isVisible="false"/>
            <attr name="sSystemName" attribute-type="Varchar" caption="Системное имя" order="10" isMnemoCode="true"/>
            <attr name="sCaption" attribute-type="Varchar" caption="Наименование" order="20" isHeadLine="true"/>
            <attr name="sDescription" attribute-type="Varchar" caption="Описание" order="30"/>
            <attr name="idState" attribute-type="Long" caption="Состояние" order="70"
                  type="refObject" ref.class="Btk_ClassState"/>
        </attributes>

        <dbData>
            <script name="dataInstall" version="1">
                <install>Rpltst_ForDeleteTestApi.dataInstall();</install>
            </script>
        </dbData>
    </class>

Здесь определяется один скрипт с именем `dataInstall`, который вызывает метод `dataInstall()` в соответствующем API-классе.

2. Код API, вызываемый скриптом `Rpltst_ForDeleteTestApi.scala`

    class Rpltst_ForDeleteTestApi
      extends Rpltst_ForDeleteTestDpi[Rpltst_ForDeleteTestAro, Rpltst_ForDeleteTestApi, Rpltst_ForDeleteTestAta] {

      override protected def entityAta: Rpltst_ForDeleteTestAta = Rpltst_ForDeleteTestAta

      def dataInstall(): Unit = {

        session.commit()
        Btk_Pkg().setRWSharedUOWEditType()

        // регистрация состояния "Формируется"
        Btk_ClassStateApi().register(
          idpMasterClass = idClass,
          spSystemName = "Rpltst_ForDeleteTestForming",
          spCaption = "Формируется",
          bpStartState = 1.nn,
          npOrder = 100.nn)
        session.flush()

        // регистрация состояния "Сформирован"
        Btk_ClassStateApi().register(
          idpMasterClass = idClass,
          spSystemName = "Rpltst_ForDeleteTestFormed",
          spCaption = "Сформирован",
          bpStartState = 0.nn,
          npOrder = 200.nn)
        session.flush()
      }
    }

    object Rpltst_ForDeleteTestApi extends ApiFactory[Long, Rpltst_ForDeleteTestAro, Rpltst_ForDeleteTestApi]

    class Rpltst_ForDeleteTestAro extends Rpltst_ForDeleteTestDro
    class Rpltst_ForDeleteTestAta extends Rpltst_ForDeleteTestDta[Rpltst_ForDeleteTestAro, Rpltst_ForDeleteTestApi, Rpltst_ForDeleteTestAta]
    object Rpltst_ForDeleteTestAta extends Rpltst_ForDeleteTestAta

Этот метод создает два состояния класса. Именно такие операции и составляют типичные DataInstall-скрипты.

## Ограничения

- Нельзя понижать версию скрипта — система выполнит его повторно только при увеличении версии.

- Изменение имени скрипта (`name`) приводит к созданию нового скрипта. Система определяет уникальность по полному системному имени (`sSystemName`). Старый скрипт остаётся в истории, новый добавляется отдельно — это может вызвать дублирование данных.

- Отсутствие или ошибка в зависимом скрипте блокирует выполнение зависящего от него. При этом общий процесс не останавливается.

- Обычный (не отложенный) скрипт не может зависеть от отложенного — такая связь считается некорректной и фиксируется как ошибка.

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

- Не рекомендуется выполнять тяжёлые операции, внешние вызовы или сетевые запросы. Скрипты работают в контексте обновления схемы и не предназначены для длительных или блокирующих действий.

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