Технические особенности и утилиты Avi#
Динамическое изменение свойств avm через setMetaProp#
Для этого необходимо использовать следующий метод:
ru.bitec.app.gtk.gl.Rep#setMetaProp
Пример использования:
setMetaProp(attr.sSystemName.get //для какого поля меняется свойство
//какое свойство
//в данном случае свойство задает системное имя атрибута в выборке,
//которое хранит настройки типа редактора
, s"View.Representation.Attributes.Attribute.Editor.editorTypeAttr"меняется
, thisPkg.getSEditorAttrName(attr) //значения свойства
)
Работа с датами в onRefreshExt#
Ограничение NDate#
В onRefreshExt нет приведения /*NDate*/, нужно писать через /*NString*/.
Использование NString#
В onRefreshExt в конструкции with объявляются поля, которые нужно получить с выборки (из кэша). Иногда требуется указать тип данных. Тип данных указывается в /*...*/, но парсер на знает NDate, поэтому необходимо указать, как NString и далее использовать в запросе cast(... as timestamp) или as date.
Приведение типов#
override protected def onRefreshExt: String = {
s"""with t as ( select
:id as id
,:idState as idState
,:gidSrc/*@NString*/ as gidSrc
,:idObjectType as idObjectType
,:idDepOwner as idDepOwner
,:idDischargePlace as idDischargePlace
,:idSegregationGroup as idSegregationGroup
,:idService as idService
,:idFlyOverWay as idFlyOverWay
,:idStateMC as idStateMC
,:dPlanEnd /*@NString*/ as dPlanEnd
,:dPlanBegin /*@NString*/ as dPlanBegin
)
SELECT
t.id
,to_char(cast(t.dPlanEnd as timestamp) - cast(t.dPlanBegin as timestamp), 'dd д. hh24 ч.') as nPlanBusy
,t1.sHeadLine_dz as idStateHL
,t2.sHeadLine_dz as idObjectTypeHL
,t3.sHeadLine_dz as idDepOwnerHL
,t4.sHeadLine_dz as idDischargePlaceHL
,t5.sMnemoCode_dz as idSegregationGroupHL
,t6.sHeadLine_dz as idServiceHL
,t7.sHeadLine as gidSrcHL
,t8.sHeadLine_dz as idFlyOverWayHL
FROM t
LEFT JOIN Btk_ClassState t1 on t.idState = t1.id
LEFT JOIN Btk_ObjectType t2 on t.idObjectType = t2.id
LEFT JOIN Bs_DepOwner t3 on t.idDepOwner = t3.id
LEFT JOIN Bs_Placement t4 on t.idDischargePlace = t4.id
LEFT JOIN Oil_SegregationGroup t5 on t.idSegregationGroup = t5.id
LEFT JOIN Gds_Service t6 on t.idService = t6.id
LEFT JOIN Btk_Object t7 on t.gidSrc = t7.gidRef
LEFT JOIN Oil_FlyOverWay t8 on t.idFlyOverWay = t8.id
"""
}
Поиск отображения на выборке#
selection.form.findSelection(...)
Полезный метод, который позволяет найти отображение по системному имени среди видимых в сессии.
Пример использования:
val sel = selection.form.findSelection(Bdg_ForecastAvi.card())
Пользовательская блокировка#
Пользовательская блокировка включается при взаимодействии пользователя с интерфейсом в методе Dvi#beforeEdit, который является результатом кодогенератора.
Принудительно можно включать блокировку с помощью вызова метода:
Btk_FormSessionApi().lockObject(gid)Btk_FormSessionApi().lockObjectMulti(gida)
Больше про пользовательскую блокировку можно узнать здесь.
Объект класса в процессе создания и другие состояния объекта rop#
Иногда, есть необходимость в Avi знать, что запись находится в процессе создания, т.е. не имеет реализации, как запись в таблице БД.
Это можно узнать по объекту записи rop:
используйте метод Avi
thisRop()чтобы получить объектrop, чья карточка открыта или на котором стоит фокус, если отображениеlist.если
rop.ropMode == InsertRopMode, то объект находится в процессе создания, т.е. есть в кэше приложения, но не имеет реализации в таблице БД.
Где поле объекта rop.ropMode хранит в себе информацию состояния объекта. Есть и иные состояния объекта:
ReadRopMode.
UpdateRopMode.
DeleteRopMode.
InsertRopMode.
Пример кода из проекта:
//Документ должен быть закоммичен, чтобы были пройдены все соответсвующие проверки,
// т.к. по закрытию card_ReadFromFile будет flush()
if (rop.ropMode == InsertRopMode) {
throw AppException("Для вызова операции необходимо, чтобы документ был заполенен и сохранён.")
}
Определение главной выборки формы#
В системе есть 3 типа форм:
главная;
модальная;
MDI (отображается в качестве закладки на главной форме) .
У каждой формы есть главная выборка. Флаг selection.isMainOnForm и указывает, что выборка - главная на форме.
Условие application.mainSelection == selection определит, является ли выборка главным меню.
Фильтрация в деревовидном списке (отображение Tree классов-деревьев)#
Как выводить ветку дерева до объекта, который удовлетворяет условию фильтрации#
Решение такой задачи в каждом случае может быть индивидуальным.
Примеры решений:
Использование денормализации#
Такое решение будет иметь те же преимущества, что денормализированная таблица, которая обеспечивает более быстрый доступ к данным по иерархичным связям, чем рекурсивные запросы.
Что такое Денормализация классов-деревьев и как её создать можно узнать здесь.
Пример условия:
t.id in (
select denorm.idParent
from Btk_TreeExampleDenormalization denorm
join Btk_TreeExample ex on ex.id = denorm.idChild
where (upper(ex.sCode) like upper('%'||:filter$flt_sCode||'%'))
)
Использование recursive запроса#
Строить запрос с нижних объектов, которые необходимо отфильтровать, дальше строить ветки вверх.
--рекурсия
with recursive RecursiveTreeExample as (
--нижнии объекты дерева, которые фильтруются
select id, idParent, sCode
from Btk_TreeExample
--условие
where upper(sCode) like upper('%' || :filter$flt_sCode || '%')
--union на случай, если в одной ветке несколько объектов подходят под фильтр
union
--постройка ветки вверх - присоединение родителей idParent
select e.id, e.idParent, e.sCode
from Btk_TreeExample e
--присоединение родителей idParent
join RecursiveTreeExample rc on e.id = rc.idParent
)
select id, idParent, sCode
from RecursiveTreeExample;
Примечание
PostgreSQL оптимизирует рекурсивные запросы, поэтому они могут быть достаточно эффективными, особенно для относительно небольших деревьев.