Урок 2. Сессии, Модель данных
Contents
Урок 2. Сессии, Модель данных#
Цель курса#
Целью курса является познакомить слушателей с возможностями Global Framework. В рамках этого курса будет создано приложение по управлению библиотекой.
Сессия#
Сессия приложения создается на поток прикладной бизнес логики и предоставляет доступ к сессии базы данных, 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 нотацией в реляционной СУБД. Контейнер не имеет жесткой, заранее определенной схемы и основан на множестве пар «ключ‑значение». Это позволяет использовать его как динамическое расширение объекта. Для добавления новых данных в контейнер не требуется перекомпиляция кода или изменение структуры СУБД.
Для создания нового класса в модуле необходимо:
Cоздать файл спецификации класса odm. Данный файл удобнее всего создать по шаблону в Install IntelliJ IDEA (как добавить такой шаблон в ide показано в 1вом уроке).
Внимание
Шаблон по умолчанию содержит коллекцию без имени и включенную группировку. Если в группировке нет необходимости её нужно удалить. Так же необходимо определить или удалить шаблоны для коллекций, в противном случае при генирации кода могут возникнуть ошибки.
Определить атрибуты будущего класса и подключить коллекции если они имеются.
Сгенерировать код по файлу спецификации
Собрать проект
Сгенерировать таблицы
Добавить 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)
}
Кэширование объектных запросов#
Кэширование объектных запросов возможно по требованию в случаи если класс настроен для сохранения в разделяемом кэше.
Чтобы включить кэширования запроса:
Добавьте в запрос опцию
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_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 наименование модуля
Создание выборки основного меню приложения#
Создайте создайте основную выборку приложения Библиотека
Создайте файл
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>
Создайте файл
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 = {} } //--------- }
Создайте файл
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>