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

# Проектные расширения

## Точка расширения

Аббревиатуры:

- `Ept` - точка расширения;

- `Ext` - расширения.

Точка расширения (ExtensionPoint) – специфический пакет, методы которого объявлены таким образом, что к ним можно подключить дополнительный исполняемый код из других пакетов (расширений/Extension). Код расширений будет выполнен при вызове метода точки расширения.

    class Btk_ExampleEpt extends ExtensionPoint {
      val doSomeThing = declFunc("doSomeThing") {
        (sf: SuperFunc[Int], a: Int, b: Int) =>
          val superVal = sf().getOrElse(0)
          a + b + superVal
      }
    }
    object Btk_ExampleEpt extends PkgFactory[Btk_ExampleEpt]

Пример представляет объявление «Точки расширения» с методом `doSomeThing`

Правила наименования для точек расширения, которые используются для дополнения функционала сеттеров, вставки. удаления – `Xxx_Ept`, где `Xxx` – имя модуля, для которого точка расширения создается (например `Bs_Ept`, `Btk_Ept`).

Расширение (Extension) – специфический пакет, методы которого подключаются к методам точки расширения, и выполняются при вызове соответствующего метода точки расширения.

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

При первом обращении к классу точки расширения, производится поиск и сканирование файлов `META-INF/extensions.xml`. Расширения, у которых свойство `targetEpt` совпадает с именем точки расширения, подключаются к точке расширения.

    class Gs3_Btk_ExampleExt extends Extension {

    subFunc("doSomeThing") {
      (sf: SuperFunc[Int], a: Int, b: Int) =>
      val superVal = sf(a - 2, b).getOrElse(0)
      superVal + 2
    }
    }

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

Внимание

Если не вызвать `sf()`, методы расширений, находящиеся ниже в очереди не будут вызваны.

Правила наименования для расширений, которые используются для дополнения функционала других модулей для сеттеров, вставки. удаления – `Xxx_YyyExt`, где `Xxx` – имя модуля расширения, `Yyy` – имя модуля, в котором находится точка расширения (например `Mct_BsExt`).

### Конфигурирование расширений

Конфигурация расширений находится в файле `META-INF/extensions.xml`.

Структура фала:

    <?xml version="1.0" encoding="UTF-8"?>
    <exts>
    	<ext class="ru.bitec.app.btk.ext.Btk_ExceptionHandlerExt"
    	     targetEpt="ExceptionHandlerEpt"/>
    </exts>

### Порядок вызовов

По умолчанию, метод точки расширения получает порядковый номер «0», а все методы расширений – номер «10». В результате, в первую очередь буду вызваны все методы расширений, в произвольном порядке, в конце будет вызван код точки расширения.

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

    subFunc("doSomeThing", 1) {
      (sf: SuperFunc[Int], a: Int, b: Int) =>

    }

## Проектное перекрытие кода Api, Avi, Lib, Pkg

Для перекрытия кода Api, Avi, Lib, Pkg необходимо:

1. Создать свой класс-наследник от перекрываемого класса.

2. Зарегистрировать перекрытие класса в файле `META-INF/overrides.xml`.

### Пример перекрытия Avi-класса

#### Код исходного Avi-класса `Gs3_RootTestAvi.scala`

    object Gs3_RootTestAvi extends Gs3_RootTestAvi

    @AvmFile(name = "Gs3_RootTest.avm.xml")
    class Gs3_RootTestAvi extends Gs3_RootTestDvi {
      override def list(): List = {
        new List {
        override def meta = this
        }
      }
    }

#### Код перекрытия Avi-класса `Gs3_RootTestOverrideAvi.scala`

    object Gs3_RootTestOverrideAvi extends Gs3_RootTestOverrideAvi

    @AvmFile(name = "Gs3_RootTestOverride.avm.xml")
    class Gs3_RootTestOverrideAvi extends Gs3_RootTestAvi {
      override def list(): List = {
        new List {
        override def meta = this
        }
      }
      trait List extends super.List {

      }
    }

Аннотация `@AvmFile` может быть не указана, в этом случае будет использоваться аннотация из класса-предка.

#### Текст `META-INF/overrides.xml`

    <?xml version="1.0" encoding="UTF-8"?>
    <overrides>
      <replace-with
          class="ru.bitec.app.gs3.test.Gs3_RootTestOverrideAvi">
        <when-type-is class="ru.bitec.app.gs3.test.Gs3_RootTestAvi"/>
      </replace-with>
    </overrides>

### Пример перекрытия Api

#### Код перекрытия

    class Gs3_RootTestOverrideApi extends Gs3_RootTestApi {

    }

    object Gs3_RootTestOverrideApi extends ApiFactory[
      java.lang.Long,
      Gs3_RootTestAro,
      Gs3_RootTestOverrideApi
    ]

#### Текст `META-INF/overrides.xml`

    <replace-with
        class="ru.bitec.app.gs3.test.collections.Gs3_RootTestOverrideApi">
      <when-type-is
      class="ru.bitec.app.gs3.test.collections.Gs3_RootTestApi"/>
    </replace-with>

### Пример перекрытия Pkg

#### Код перекрытия

    class Btk_OverridePkg extends Btk_Pkg {

    }

    object Btk_OverridePkg extends PkgFactory[Btk_OverridePkg]

#### Текст `META-INF/overrides.xml`

    <replace-with class="ru.bitec.app.gs3.Btk_OverridePkg">
      <when-type-is class="ru.bitec.app.btk.Btk_Pkg"/>
    </replace-with>

### Пример перекрытия Lib

#### Код перекрытия

    class Btk_OverrideLib extends Btk_Lib {

    }
    object Btk_OverrideLib extends AviLibFactory[Btk_Lib]

#### Текст `META-INF/overrides.xml`

    <replace-with class="ru.bitec.app.gs3.Btk_OverrideLib">
      <when-type-is class="ru.bitec.app.btk.Btk_Lib"/>
    </replace-with>

### Запрет перекрытия

Для запрета проектного перекрытия кода Avi-класса, необходимо отметить класс аннотацией @Final.

    @Final
    class Gs3_RootTestAvi extends Gs3_RootTestDvi {}

Для запрета проектного перекрытия кода Api,Lib,Pkg классов, необходимо отметить класс аннотацией `@Final` или ключевым словом `final`:

    @Final
    class Gs3_RootTestApi extends Gs3_RootTestDpi[
      Gs3_RootTestAro,
      Gs3_RootTestApi,
      Gs3_RootTestAta
    ] {}

или

    final class Gs3_RootTestApi extends
    Gs3_RootTestDpi[
      Gs3_RootTestAro,
      Gs3_RootTestApi,
      Gs3_RootTestAta
    ] {}

## Jexl расширения методов

С помощью расширений методов имеется возможность доработки и модификации стандартных серверных методов и операций пользовательских интерфейсов из приложения `Настройки системы`. Расширение представляет из себя jexl-скрипт, вызываемый перед или после выполнения стандартного метода/операции.

### Расширения Api-методов

Добавление точек расширения в `Api` предусмотрено только в следующие методы:

- `Сеттеры атрибутов` – `Dpi-`сеттеры всех атрибутов выбранного класса;

- `AfterEdit` – `Dpi-`метод, вызываемый на окончание редактирования объекта класса, например при редактировании строки и перемещении курсора на другую строку;

- `BeforeEdit` – `Dpi-`метод, вызываемый в начале редактирования объекта класса;

- `FlushObject` – метод записи (сохранения) объекта в базу данных;

- `Delete` – `Dpi-`метод удаления объекта класса из базы данных;

- `Insert` – `Dpi-`метод создания нового объекта класса.

Примечание

Все расширения вызываются из „Dpi“. Т.е. срабатывание перед сеттером - это срабатывание перед вызовом `Dpi-`сеттера.

Примечание

Для методов «FlushObject» и «BeforeEdit» можно добавлять только точки расширения «До».

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

Для подключения точек расширения Api-методов перейдите: `Сущности` - `Обозреватель проекта`. Выбрав Api нужного класса, перейдите на закладку `Точки расширения`. На закладке `Расширения до` или `Расширения после` создайти запись нового расширения и пропишите ему исполняемый скрипт.

В скрипт передаются следующие параметры:

- `rop` (роп объекта) - во всех случаях, кроме точек расширения «До» метода `Insert` и «После» метода `Delete`.

- `idDeleted` (id удаленного объекта) - для точки расширения «После» метода `Delete`.

- `value` (новое устанавливаемое значение) - для сеттеров.

- `oldValue` (старое устанавливаемое значение) - для сеттеров.

Пример скрипта с использованием вызова процедур из Bts:

    Bts_ProcedureApi.execByMnemoCode(
      "ProcedureCode",
      {"argName1": value1, "argName2": value2, ...}
    )

Пример выброса ошибки на `FlushObject`, если у объекта не заполнен атрибут `sDescription`:

    var aro = rop.copyAro();
    if (aro.sDescription() == null){
      raise("Не заполнено описание!")
    }

Пример выброса ошибки на `Сеттере состояния`, если у объекта не заполнен атрибуты:

    var fromState = oldValue
    var toState = value
    if (fromState == 100 && toState == 300){
      var aro = rop.copyAro();
      if (aro.sAttr1() == null || aro.nAttr2 == null){
        raise("Не заполнены все поля!")
      }
    }

### Расширения Avi-методов

Добавление точек расширения в `Avi` предусмотрено в сеттеры выборки, в ее операции или на события выборки, такие как beforeEdit, afterEdit и т.д.

Выполнение этих точек будет производиться в контексте выборки.

Для подключения точек расширения Avi-методов перейдите: `Настройки системы` - `Обозреватель проекта`. Выбрав нужную выборку, перейдите на закладку `Редактор операций`. На закладке `Расширения до` или `Расширения после` создайте запись нового расширения и пропишите ему исполняемый скрипт.

Пример подстановки даты детализации из мастера на операцию `insert`:

    var ropParent = ParentApi.load(getVar("super$id").asNLong());
    var ropDet = DetApi.load(getVar("id").asNLong());
    DetApi.setdDate(ropDet, ropParent.dDate);

Пример подстановки значения фильтра на событие `beforeFirstOpen`:

    setVar("flt_bNotActive", 1)

### Перекрытие `acObject` и `acObjectItem`

Для перекрытия `acObject` и `acObjectItem`:

1. Перейдите: `Настройки системы` - `Обозреватель проекта`.

2. Выберете нужную выборку.

3. Перейдите на закладку `Настройки отображений`.

4. Выберете нужное отображение и установите флаг переопределения `acObject`/`acObjectItem`.

5. В закладках напишите JEXL-скрипт для получения нового значения.
   В JEXL-скрипте можно получить стандартное значение `acObject`/`acObjectItem` вызвав функцию `default();`.

### Пользовательские события

Реализована возможность для `Api` добавлять собственные события, а так же для существующих стандартных событий добавлять новые типы срабатывания.

#### Добавление нового события

Новое событие будет отображено в списке событий `Api`. Оно создается без типов срабатываний, типы срабатывание привязываются к событию отдельным методом.

Пример добавления нового события:

    Btk_MethodExtensionApi().registerClassEvent(
      idvClass,
      "SomeUserEventName",
      "Новое пользовательское событие"
    )

Пример удаления пользовательского события:

    Btk_MethodExtensionApi().unregisterClassEvent(
      idvClass,
      "SomeUserEventName"
    )

#### Добавление типа срабатывания к событию

Стандартные события (сеттеры, вставка и тд.) имеют по умолчанию 2 типа срабатывания: `До` и `После`, а пользовательские события создаются без типа срабатывания. Реализована возможность добавления нового типа срабатывания к событию. Каждый тип срабатывания будет отображаться отдельной закладкой у события.

Создание нового типа срабатывания:

    Btk_MethodExtensionTypeApi().register("SomeUserEventType", "На пересчет денормализации")

Создание связи между событием и типом срабатывания:

    Btk_MethodExtensionApi().registerEventTypeToClassEvent(
      idvClass,
      "SomeUserEventName",
      Btk_MethodExtensionTypeApi().findByMnemoCode("SomeUserEventType")
    )

Удаление связи между событием и типом срабатывания:

    Btk_MethodExtensionApi().unregisterEventTypeFromClassEvent(
      idvClass,
      "SomeUserEventName",
      Btk_MethodExtensionTypeApi().findByMnemoCode("SomeUserEventType")
    )

#### Вызов пользовательского события или пользовательского типа срабатывания из кода приложения

Разработчик в коде сам размещает вызов срабатывания события в нужном месте.

Пример вызова:

    Btk_MethodExtensionApi().runMethodExtensionByClass(idvClass, "SomeUserEventType", "SomeUserEventName", "valueStr" -> "abc", "valueNum" -> 1.nn)

Переданные в метод значения параметров будут доступны в `Jexl-скриптах` этого события.

Пример обращения к переменной из `Jexl-скрипта`:

    println("Значение переменной valueStr: " + valueStr + " Значение переменной valueNum: " + valueNum.toString());