Урок 2. Сессии, Модель данных#

Цель курса#

Целью курса является познакомить слушателей с возможностями Global Framework. В рамках этого курса будет создано приложение по управлению библиотекой.

Library

Сессия#

Сессия приложения создается на поток прикладной бизнес логики и предоставляет доступ к сессии базы данных, EclipseLink кэшу, серверу приложения. А так же содержит необходимые инъекции зависимости для работы прикладной бизнес логики.

Сессия приложения создается:

  • на каждую mdi форму открытую в приложении

  • на rest запрос к серверу приложения

    Примечание

    Для ускорение rest запросов возможна настройка пула сессий

Для изучения смотри: Руководство разработчика: Сессия-приложения

Бизнес объект#

Бизнес-объект (БО) - объединение нескольких классов и их коллекций в группу для более удобного манипулирования ими при работе с кэшем и конфигурировании вспомогательных сервисов.

Бизнес объект позволяет:

  • Массово загружать данные в транзакционный кэш
    Для бизнес объекта можно указать стратегию загрузки данных существенно уменьшающую количество запросов в базу данных. Так как запросы пойдут не по каждому объекту а по каждому классу бизнес объекта.

  • Настраивать права доступа
    По бизнес объекту создается административный объект на котором можно массово выдать привилегии для всех классов бизнес объекта

  • Управлять электронной подписью
    Можно настроить правила подписи всего бизнес объекта включая не только шапку но и все вложенные коллекции.

  • Настраивать интеграцию и репликацию

Для изучения смотри: Руководство разработчика: Бизнес-объект

Общие сведения о классах#

Определяет правила хранения и обработки таблицы базы данных.

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

Перечень генерируемых элементов:

  • Доменная автономная бизнес логика(Dpi)
    Содержит код для автономной бизнес логики

  • Каркас прикладной автономной логики(Api)
    scala класс с окончанием Api, в котором пишется автономная бизнес логика для работы с классом. Наследуется от Dpi

  • Доменная интерактивная бизнес логика(Dvi)

  • Каркас прикладной интерактивной логики(Avi)
    scala класс с окончанием Avi, в котором пишется интерактивная бизнес логика. Наследуется от Dvi

  • Доменная разметка выборки(dvm.xml)
    Содержит сгенерированную по умолчанию разметку выборки.

  • Каркас прикладной декларации пользовательского интерфейса(Avm)
    xml файл с расширением avm.xml, в котором пишется разметка выборки

  • Интеграция с Orm

    • Pojo объект для хранения данных в кэше

    • Aro объект интеграции pojo в фреймворк

Типы данных#

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

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

  • Целое число

  • Дробное число

  • Строка

  • Дата

  • Ссылка (на объект заданного класса)

  • Ссылка на класс

  • Переменная ссылка (на объект произвольного класса)

  • Глобальный идентификатор gid

  • Json контейнер

Простые типы#

Простыми типами являются: Число, строка, дата

Тип

postgresql

odm

Рекомендация по использованию

Строка фиксированной длинны

varchar

Varchar

Текст до 4000 символов

Строка переменной длинны

text

Text

Текст до 15 мегабайт

Число

number

Number

Целое или дробное число

Дата

date

Date

Дата, дата и время

Ссылочные типы#

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

Ссылка на класс#

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

Глобальный идентификатор gid#

Gid является уникальным идентификатором в рамках системы. Переменная ссылка на gid является альтернативой системе переменной ссылочности из двух атрибутов (ссылка на класс + переменная ссылка на объект). Для организации переменной ссылки через gid используется один атрибут.

Json контейнер#

Json контейнер – это расширение объекта класса NoSQL нотацией в реляционной СУБД. Контейнер не имеет жесткой, заранее определенной схемы и основан на множестве пар «ключ‑значение». Это позволяет использовать его как динамическое расширение объекта. Для добавления новых данных в контейнер не требуется перекомпиляция кода или изменение структуры СУБД.

Для создания нового класса в модуле необходимо:

  1. Cоздать файл спецификации класса odm. Данный файл удобнее всего создать по шаблону в Install IntelliJ IDEA (как добавить такой шаблон в ide показано в 1вом уроке).

    Внимание

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

  2. Определить атрибуты будущего класса и подключить коллекции если они имеются.

  3. Сгенерировать код по файлу спецификации

  4. Собрать проект

  5. Сгенерировать таблицы

  6. Добавить orm класса в файл src/main/resources/orm/all.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--suppress JpaConfigDomFacetInspection --><persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="all">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <!--<mapping-file>ru/bitec/app/lbr/Lbr_Name.orm.xml</mapping-file>-->
    ...
    <mapping-file>ru/bitec/app/lbr/Lbr_ClassName.orm.xml</mapping-file>
    ...
  </persistence-unit>
</persistence>

Работа c провайдерами строк#

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

Метод получения rop:

thisApi().load(идентификатор.asNLong)

Примеры сеттеров в файле выборки:

val rop = thisRop
thisApi().setidContras(rop,getVar("super$id").asNLong)

Работа с rop в API:

for (ropGrade <- new OQuery(entityAta.Type){  
  where (t.idGdsGrade === idpGdsGrade)  
}) {  
  setidGdsGrade(ropGrade, None.nl)  
}

Для изучения смотри: Руководство разработчика: Класс

Общие сведения о выборках#

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

Выборка определяет:

  • Способ получения данных

  • Способ отображения данных пользователю

  • Бизнес логику обработки пользовательских действий

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

Пользовательский интерфейс приложения является совокупностью экземпляров отображений выборок.

Для изучения смотри: Руководство разработчика: Выборка

Взаимодействие с базой данных#

Объектные запросы#

Кроссплатформенные запросы, которые выполняются на уровне объектов класса.

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

Пример запроса

new OQuery(Bs_GoodsAta.Type) {  
  where(t.sSystemName === spMnemoCode)  
}

Методы#

  • where
    Условие запроса.

  • orderBy
    Выражение для сортировки резуьтата

  • batchAll
    Массовая загрузка объектов. Возвращает строки с прогруженными записями всех коллекций этого класса.

  • forUpdate
    Выполнение запроса с блокированием вернувшихся записей

  • forUpdateNoWait
    Выполнение запроса с блокирование вернувшихся записей, без ожидания разблокирования, если записи уже заблокированы другой сессией.

  • tryCacheQueryResults
    Попытаться закешировать результат запроса. Смотри пункт «Кеширование»

  • unique
    Говорит, что запрос возвращает одну уникальную запись. Позволяет использовать cache-index’ы, указанные в orm класса

Кеширование объектных запросов#

Кеширование запросов работает только для классов с разделяемым режимом кеширования(Shared).

Кэширование по полю#

Кеширование через cache-index’ы указанные в orm класса. Такой запрос должен возвращать одну строку и дополняется командой unique(). Например для атрибутов мнемокода класса в orm формируется запись:

<cache-index>  
  <column-name>SSYSTEMNAME</column-name>  
</cache-index>

Запрос выглядит следующим образом:

new OQuery(entityAta.Type) {  
  unique()  
  where(t.sSystemName*=== spMnemoCode)  
}
Кэширование объектных запросов#

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

Чтобы включить кэширования запроса:

  1. Добавьте в запрос опцию tryCacheQueryResults().
    Результат такого запроса будет кэширован, если транзакция не находится в режиме редактирования разделяемых объектов.

Пример запроса:

new OQuery(entityAta.Type) {  
  tryCacheQueryResults ()  
  where(t.sSystemName === spMnemoCode)  
}

Транзакционный индекс#

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

Пример объявления:

lazy val idxidParent = TxIndex(Btk_GroupAta.Type)(_.idParentGroup)

Методы

  • byKey
    Посетитель по ключу индекса.

  • refreshByKey
    Посетитель по ключу индекса c обновлением из базы данных.

  • queryKeys
    Кеширование ключей индекса.

  • forPartition
    Открывает секцию для массового обновления индекса. Используется для прозрачного массового обновления после очистки транзакционного кэша. Секции могут быть вложенными друг в друга, в таком случае ключи суммируются.

byParent#

Метод есть у классов коллекций, возвращает обходчик записей отфильтрованных по идентификатору предка

Реляционные запросы#

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

Для более удобного использования в контекст бизнес логики добавлены дополнительные функции.

ASQL#

Выполнение запроса на чтение Создаёт объект AnormSql для выполнения запроса к базе без модификации данных. Если в текущей сессии начата транзакция, SQL-выражение выполняется в ней. Если транзакция в сессии не начата, запрос выполнится в автономной sql-транзакции.

Пример:

val idTrigger = 1
ASQL"SELECT t.sCaption as sHeadline FROM Btk_JobSchedule t WHERE t.id = $idTrigger".as(nStr("sHeadline").*).headOption

ATSQL#

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

Пример:

ATSQL("alter table bs_barcode add id int8;").execute()

ASelect#

Выполнение запросов на чтение\запись с большим кол-вом колонок.

Пример:

for (rv <- new ASelect {
  val nParentLevel = asInt("nParentLevel")
  val gidParent = asString("gidParent")
  val gidChild = asString("gidChild")
  val idParent = asLong("idParent")
  SQL"""
  select nParentLevel,gidParent,gidChild,idParent,idChild from table
  """
}) {
  println(rv.nParentLevel())
  //запрос поля без его предварительного объявления
  println(rv.get("idChild").asNLong())
}

Практика#

Создание классов для модуля библиотека#

Задача: Создать классы:

  • Справочник: Lbr_Publisher

  • Справочник: Lbr_Author

  • Справочник: Lbr_Book

Справочник Lbr_Publisher - Издатель#

Системное имя

Наименование

Тип данных атрибута

Тип атрибута

Примечание

gid

gid

Varchar

Basic

sSystemName

Системное имя

Varchar

Basic

Мнемокод

sCaption

Наименование

Varchar

Basic

Заголовок

sDescription

Описание

Varchar

Basic

Совет

Создайте класс по адресу lbr/src/main/resources/ru/bitec/app/lbr/Lbr_Publisher.odm.xml где lbr наименование модуля

Справочник Lbr_Author - Автор#

В классе включить группировку

Системное имя

Наименование

Тип данных атрибута

Тип атрибута

Обязательный

Примечание

gid

gid

Varchar

Basic

sCode

Код

Varchar

Basic

V

Мнемокод

sLastName

Фамилия

Varchar

Basic

V

sFirstName

Имя

Varchar

Basic

V

sMiddleName

Отчество

Varchar

Basic

V

sFIO

ФИО

Varchar

Basic

Заголовок

Совет

Создайте класс по адресу lbr/src/main/resources/ru/bitec/app/lbr/Lbr_Author.odm.xml где lbr наименование модуля

Справочник Lbr_Book - Книга#

Системное имя

Наименование

Тип данных атрибута

Тип атрибута

Примечание

gid

gid

Varchar

Basic

sISBN

ISBN

Varchar

Basic

Мнемокод

sCaption

Наименование

Varchar

Basic

Заголовок

idPublisher

Издатель

Long

refObject

Ссылка на класс Lbr_Publisher

idAuthor

Автор

Long

refObject

Ссылка на класс Lbr_Author

nYear

Год издания

Number

basic

nColPage

Кол-во страниц

Number

basic

sDesc

Описание

Varchar

basic

Совет

Создайте класс по адресу lbr/src/main/resources/ru/bitec/app/lbr/Lbr_Book.odm.xml где lbr наименование модуля

Создание выборки основного меню приложения#

Создайте создайте основную выборку приложения Библиотека

  1. Создайте файл src/main/resources/ru/bitec/app/lbr/Lbr_MainMenu.avm.xml

    <?xml version="1.0"?>
    <view xmlns="http://www.global-system.ru/xsd/global3-view-1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.global-system.ru/xsd/global3-view-1.0.xsd"
          name="Lbr_MainMenu">
    
        <acObject caption="Управление библиотекой"/>
    
        <representation name="Default" caption="Управление библиотекой"    editMode="edit" >
    
            <layout>
                <simpleComposer>
                    <frame caption="Управление библиотекой">
                        <grid/>
                    </frame>
                </simpleComposer>
            </layout>
    
            <attributes>
                <attr name="ID" caption="Идентификатор" isVisible="false"/>
                <attr name="IDACCESSGROUP#" caption="Группа доступа"    isVisible="false"/>
                <attr name="dGlobalCurrentDate" caption="Дата" editorType="datePick"    order="40"/>
                <attr name="idGlobalDepOwner" caption="Организация" isVisible="false"    order="40"/>
                <attr name="idGlobalDepOwnerHL" caption="Организация" order="40.2">
                    <editor>
                        <lookup lookupQuery="gtk-ru.bitec.app.bs.Bs_DepOwnerAvi#Lookup"
                                changeableAttr="idGlobalDepOwner"    isLookupLazyLoad="true"
                                lookupKeyAttr="id" lookupListAttr="sHeadLine"    isResetButtonVisible="true"/>
                    </editor>
                    <ref class="Bs_DepOwner"/>
                </attr>
                <attr name="dGlobalBeginDate" caption="Период с"    editorType="datePick" order="50" isLastInLine="true"/>
                <attr name="dGlobalEndDate" caption="по" editorType="datePick"    order="60" isLastInLine="true"/>
                <attr name="nChoosePeriodBtn" caption="Выбрать период"    editorType="button" order="65"/>
            </attributes>
    
            <operations>
    
            </operations>
        </representation>
    
    </view>
    
  2. Создайте файл src/main/scala/ru/bitec/app/lbr/Lbr_MainMenuAvi.scala

    package ru.bitec.app.lbr
    
    import ru.bitec.app.bs._
    import ru.bitec.app.btk._
    import ru.bitec.app.gtk.gl.avi.Visibilities
    import ru.bitec.gtk.core.AvmFile
    
    @AvmFile(name = "Lbr_MainMenu.avm.xml")
    object Lbr_MainMenuAvi extends Bs_ApplicationAvi {
    override def default(): Default = {
      new Default {
        override def meta = this
      }
    }
    
    trait Default extends super.Default {
      override def onLoadMeta(): Unit = {
        super.onLoadMeta()
      }
    
      //========== Справочники ======================
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible, imageCollection="Btk_Application", imageIndex=6,
        visibleOnMainMenu = Visibilities.Visible, caption = "Справочники", order = 10)
      def mm_ReferenceRoot(): Unit = {}
    
      //-------Подразделения-----
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Подразделения", order = 10, headOperation = "mm_ReferenceRoot")
      def mm_DepartmentRoot(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Подразделения", order = 5, headOperation = "mm_DepartmentRoot")
      def mm_Department(): Unit = {
        Bs_DepartmentAvi.tree().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Организации", order = 10, headOperation = "mm_DepartmentRoot")
      def mm_Bs_DepOwner(): Unit = {
        Bs_DepOwnerAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Виды подразделений", order = 20, headOperation = "mm_DepartmentRoot")
      def mm_Bs_DepartmentSort(): Unit = {
        Bs_DepartmentSortAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 20, headOperation = "mm_ReferenceRoot")
      def mm_Delimiter1(): Unit = {}
    
      // Контрагенты
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Контрагенты", order = 30, headOperation = "mm_ReferenceRoot")
      def mm_Bs_ContrasRoot(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Контрагенты", order = 10, headOperation = "mm_Bs_ContrasRoot")
      def mm_Bs_Contras(): Unit = {
        Bs_ContrasAvi.mainList().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Типы контрагентов", order = 20, headOperation = "mm_Bs_ContrasRoot")
      def mm_Bs_ContrasType(): Unit = {
        Btk_ObjectTypeAvi.list().newForm().params(Map(
          "FLT_SREFCLASSNAME" -> "Bs_Contras",
          "FLT_SREFCLASSNAME.readOnly" -> true
        )).open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 30, headOperation = "mm_ReferenceRoot")
      def mm_Delimiter11(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Физические лица", order = 50, headOperation = "mm_ReferenceRoot")
      def mm_Bs_Person(): Unit = {
        Bs_PersonAvi.list().newForm().open()
      }
    
      //----------------------------------
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 60, headOperation = "mm_ReferenceRoot")
      def mm_Delimiter12(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
       visibleOnMainMenu = Visibilities.Visible, caption = "Авторы", order = 80, headOperation = "mm_ReferenceRoot")
     def mm_Lbr_Autor(): Unit = {
       Lbr_AuthorAvi.list().newForm().open()
     }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
       visibleOnMainMenu = Visibilities.Visible, caption = "Издательства", order = 90, headOperation = "mm_ReferenceRoot")
     def mm_Lbr_Publisher(): Unit = {
       Lbr_PublisherAvi.list().newForm().open()
     }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
       visibleOnMainMenu = Visibilities.Visible, caption = "Книги", order = 110, headOperation = "mm_ReferenceRoot")
     def mm_Lbr_Book(): Unit = {
       Lbr_BookAvi.list().newForm().open()
     }
    
      
    
      
    
      
    
     
    
      //========== Документы ======================
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible, imageCollection="Btk_Application", imageIndex=7,
        visibleOnMainMenu = Visibilities.Visible, caption = "Документы", order = 20)
      def mm_DocumentRoot(): Unit = {}
    
      
    
     
    
      
    
      //========== Отчеты ======================
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible, imageCollection="Btk_Application", imageIndex=8,
        visibleOnMainMenu = Visibilities.Visible, caption = "Отчеты", order = 50)
      def mm_LbrReportRoot(): Unit = {}
    
     
    
      //========== Настройки ======================
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible, imageCollection="Btk_Application", imageIndex=9,
        visibleOnMainMenu = Visibilities.Visible, caption = "Настройки", order = 60)
      def mm_SettingRoot(): Unit = {}
    
      //========== Настройки документации ======================
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Настройки документации", order = 10, headOperation = "mm_SettingRoot")
      def mm_DocSetting(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Типы объектов", order = 10, headOperation = "mm_DocSetting")
      def mm_Btk_ObjectType(): Unit = {
        Btk_ObjectTypeAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Настройка детализации типов объектов", order = 15, headOperation = "mm_DocSetting")
      def mm_Btk_ObjectTypeDet(): Unit = {
        Btk_ObjectTypeDetAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Жизненные циклы", order = 40, headOperation = "mm_DocSetting")
      def mm_Btk_LifeCycle(): Unit = {
        Btk_LifeCycleAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Закладки", order = 60, headOperation = "mm_DocSetting")
      def mm_Btk_Tab(): Unit = {
        Btk_TabAvi.list().newForm().open()
      }
    
      //========== Печатные формы ==============================
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Печатные формы", order = 90, headOperation = "mm_SettingRoot")
      def mm_Rpt_Report(): Unit = {
        val vRpt_ReportAvi = Btk_Lib().getAviBySimpleName("Rpt_ReportAvi")
        if (vRpt_ReportAvi != null)
          vRpt_ReportAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 120, headOperation = "mm_SettingRoot")
      def mm_Delimiter60(): Unit = {}
    
      //============ Дополнительно =========================
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Дополнительно", order = 130, headOperation = "mm_SettingRoot")
      def mm_AdditionalSettings(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Групировка объектов", order = 130, headOperation = "mm_AdditionalSettings")
      def mm_Btk_Group(): Unit = {
        Btk_GroupAvi.list().newForm().open()
      }
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Состояния классов", order = 150, headOperation = "mm_AdditionalSettings")
      def mm_Btk_ClassList(): Unit = {
        Btk_ClassListAvi.list().newForm().open()
      }
    
      //========== Менеджер заданий ======================
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 140, headOperation = "mm_SettingRoot")
      def mm_Delimiter140(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Менеджер заданий", order = 150, headOperation = "mm_SettingRoot")
      def mm_Btk_Job(): Unit = {
        Btk_JobGroupAvi.tree().newForm().open()
        //Btk_JobAvi.tree().newForm().open()
      }
    
      //========== Тестирование и отладка ======================
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "-", order = 9998, headOperation = "mm_SettingRoot")
      def mm_Delimiter9998(): Unit = {}
    
      @Oper(visibleOnContextMenu = Visibilities.Invisible, visibleOnToolbar = Visibilities.Invisible,
        visibleOnMainMenu = Visibilities.Visible, caption = "Тесты и отладка", order = 9999, headOperation = "mm_SettingRoot")
      def mm_Testing(): Unit = {}
       } //---------
     }
    
  3. Создайте файл src/main/resources/META-INF/applications.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <apps>
        <!--<app mainSelection="Lbr_MainMenuAvi" caption="Наименование приложения"    imageIndex="1"/>-->
        <app mainSelection="Lbr_MainMenuAvi" caption="Управление библиотекой"    imageIndex="6"/>
    </apps>