Универсальный фильтр#
Создание дополнительной группы фильтрации по классу#
На операции
lazyInitFilterинициализируем дополнительную группу.override def lazyInitFilter(): Unit = { if (!fltManager.isPopulatedRootGroup) { Btk_FltPkg().createRootGroupByClass(fltManager, "Bs_Goods", "ТМЦ") } super.lazyInitFilter() }
На
onApplyFilerполучаем текст фильтрации по группе.override def onApplyFilter(): Unit = { super.onApplyFilter() //проверяем, что группы инициализированы и фильтр активен в выборке if (fltManager.isPopulatedRootGroup && fltManager.isActive) { val alias = "tt" val macros = Btk_FltPkg().generateMacroByGroup(fltManager, "Bs_Goods", alias) selection.setMacro("GdsMacro", macros.where) } }
В операции
onRefreshиспользуем установленный вonApplyFilerмакрос:Через метод
prepareSelectStatement.override protected def onRefresh: Recs = { prepareSelectStatement("&GdsMacro") }
В тексте sql-запроса.
override protected def onRefresh: Recs = { """ select ..... from ..... where .... and &GdsMacro """ }
Создание дополнительной группы фильтрации с произвольными атрибутами#
На операции
lazyInitFilterинициализируем дополнительную группу.override def lazyInitFilter(): Unit = { if (!fltManager.isPopulatedRootGroup) { Btk_FltPkg().createCustomRootGroup(fltManager, "Some_CustomGroup", "Произвольная группа", (builder) => { builder.addRefObjectAttr("idGds", "Тмц", "Bs_Goods") builder.addBasicAttr("sCode", "Код", AttributeTypes.Varchar, false) }) } super.lazyInitFilter() }
На
onApplyFilerполучаем текст фильтрации по группе.override def onApplyFilter(): Unit = { super.onApplyFilter() //проверяем, что группы инициализированы и фильтр активен в выборке if (fltManager.isPopulatedRootGroup && fltManager.isActive) { val macros = Btk_FltPkg().generateMacroByGroup(fltManager, "Some_CustomGroup", "tt") selection.setMacro("SomeMacro", macros.where) } }
В операции
onRefreshиспользуем установленный вonApplyFilerмакрос:Через метод
prepareSelectStatement.override protected def onRefresh: Recs = { prepareSelectStatement("&SomeMacro") }
В тексте sql-запроса.
override protected def onRefresh: Recs = { """ select ..... from ..... where .... and &SomeMacro """ }
Добавление JSON-атрибуту признака раскрываемости в дереве атрибутов и формирование атрибутов, отображающихся при его раскрытии#
def afterBuildFltEntityMeta(fltMetaEntity: FltMetaEntity, fltManager: FltManager): Unit = {
//проверка, что переданный класс - класс, для которого нужно добавить логику
if (fltMetaEntity.name == "Bs_Goods") {
//определяем, что json-атрибут доступен по умолчанию.
val typeSizeAttrOpt = fltMetaEntity.attrMap.get("jTypeSizeAttrs".toLowerCase)
if (typeSizeAttrOpt.isDefined) {
val typeSizeAttr = typeSizeAttrOpt.get
//ставим признак, что он может быть раскрыт в дереве
Btk_FltMetaAttributePkg().setCanExpand(typeSizeAttr, true)
//формируем атрибуты-потомки
for (rvx <- new OQuery(Gds_TypeSizeCharacteristicAta.Type) {
}) {
//создаем значимые атрибуты, которые хранятся в json-контейнере
val fltAttr = Btk_FltMetaAttributePkg().buildBasicJObjectAttr(
systemName = rvx.get(_.sCode),
caption = rvx.get(_.sCaption),
dbType = AttributeTypes.Number,
isBoolean = false,
jsonColumnName = "jTypeSizeAttrs"
)
//устанавливаем созданному атрибуту потомка.
Btk_FltMetaAttributePkg().setParentTree(fltAttr, typeSizeAttr)
//добавляем атрибут в общий перечень атрибутов.
fltMetaEntity.attrMap(fltAttr.systemName.toLowerCase) = fltAttr
}
}
}
}
Примечание
Каждый из таких атрибутов формирует условие, которое получает значение из json-контейнера.
Добавление значимому атрибуту признака ссылочности и признака раскрываемости в дереве атрибутов#
def afterBuildFltEntityMeta(fltMetaEntity: FltMetaEntity, fltManager: FltManager): Unit = {
// Проверка, что переданный класс - класс, для которого нужно добавить логику.
if(fltMetaEntity.name == "Bs_Goods") {
// Вызываем из пакета `Btk_FltMetaAttributePkg` метод для создания значимого атрибута `buildBasicAttr`.
val fltAttr = Btk_FltMetaAttributePkg().buildBasicAttr(
systemName = "screateuser_dz", // systemName — системное имя атрибута. Уникально в пределах одного класса,
caption = "Создавший пользователь", // caption — отображаемое наименование,
dbType = AttributeTypes.Varchar, // dbType — тип данных,
isBoolean = false) // isBoolean — булевый атрибут.
// Вызываем из пакета `Btk_FltMetaAttributePkg` метод для установки параметров преобразования атрибута в ссылочный `setExpandableParams`.
Btk_FltMetaAttributePkg().setExpandableParams(
fltAttr = fltAttr, // fltAttr — атрибут фильтра, который преобразуется в ссылочный,
selfAttrName = "screateuser_dz", // selfAttrName — имя поля в текущем объекте (self), используемое в выражении selfJoinExpression,
selfJoinExpression = "lower(${column})", // selfJoinExpression — выражение, которое формирует правую часть условия JOIN, используя поле текущего объекта,
childAttrName = "susername", // childAttrName — имя поля в дочернем объекте (child), используемое в выражении childJoinExpression,
childJoinExpression = "lower(${column})", // childJoinExpression — выражение, которое формирует левую часть условия JOIN, используя поле дочернего объекта,
refClass = "Btk_User") // refClass — имя класса (таблицы), к которой делается JOIN.
// Здесь формируется join по атрибутам с приведением значений к нижнему регистру (через lower).
// ${column} — подставляет алиас таблицы и имя поля, которое указываем в `selfAttrName` или `childAttrName`
// в зависимости от выражения `selfJoinExpression` подставляется `selfAttrName`, а к `childJoinExpression` подставляется `childAttrName`.
// В результате получается запрос вида:
// join Btk_User t94 on lower(t94.susername) = lower(t93.screateuser_dz)
// Ставим признак, что атрибут может быть раскрыт в дереве.
Btk_FltMetaAttributePkg().setCanExpand(fltAttr, true)
// Добавляем атрибут в общий перечень атрибутов.
fltMetaEntity.attrMap(fltAttr.systemName.toLowerCase) = fltAttr
}
}
Скрытие группы «Отбор»#
Для скрытия группы «Отбор» необходимо в операции инициализации фильтра lazyInitFilter установить флаг ru.bitec.app.btk.flt.FltAviManager#isAvailableMainGroup в значение False.
Пример кода:
override def lazyInitFilter(): Unit = {
if (!fltManager.isPopulatedRootGroup) {
fltManager.isAvailableMainGroup = false
}
super.lazyInitFilter()
}
Как переопределить параметры события#
Для передачи параметров в выборку сеттера ссылочного атрибута необходимо переопределить обработку событий в прикладной выборке, а именно функцию EntityAviDefault#handleFltEvents. Параметры, которые могут быть переопределены, представлены изменяемыми коллекциями для удобства работы:
setterParams: mutable.Map[String, Any]: Список параметров, которые передаются в выборку клиентского сеттера без изменений.availableValues: mutable.ArrayBuffer[Any]: Список допустимых id или gid, определяющий возможные значения атрибута фильтра. Ограничивает список, открывающийся «по трём точкам».
override def handleFltEvents(event: FltEvent): Unit = {
event match {
// событие перед установкой значения ссылочного атрибута из интерфейса
case event: FltBeforeClientSetRefObjectEvent =>
event.setterParams.update("FLT_IDDEPOWNER", 47606.nl)
// добавление к полученному от атрибута списку разрешённых идентификаторов дополнительных значений
event.availableValues += 132021.nl
// очистка и составление НОВОГО списка идентификаторов
event.availableValues.clear()
event.availableValues ++= Array(1.nl, 2.nl)
case _ =>
}
}
Настройка поля выбора в прикладной выборке с типом сравнения «Как в универсальном фильтре»#

В описанном примере будет создан выбор из справочника ТМЦ, и настройки фильтрации будут хранится в поле jData.
Настройка выборки#
Создайте в классе поле с типом
jsonbдля хранения настроек.В
Aviобъявите структуру, которая будет дополнять запрос вonRefreshдополнительными полями./** * Класс с дополнительными полями * @param sCompareKindName Имя типа сравнения. Невидимый * @param sCompareKindCaption Наименование типа сравнения * @param sFilterValue Значение фильтра * @param sFilterValueEditor Тип редактора для значения фильтра. Невидимый. * @param bFilterValueRo Признак, что значение фильтра не доступно для редактирования. Невидимый. */ case class Row( var sCompareKindName: NString, var sCompareKindCaption: NString, var sFilterValue: NString, var sFilterValueEditor: NString, var bFilterValueRo: Boolean )
В
Aviв операцииonRefreshдобавьте вывод дополнительных полей, необходимых для работы фильтра. Воспользуйтесь методомru.bitec.app.btk.flt.selected.attribute.standalone.Btk_FltStandaloneLib#getDisplaySettings.override def onRefresh: Recs = { val arrBuffer: ArrayBuffer[(Btktst_FltStandaloneApi#ApiRop, Row)] = ArrayBuffer() //делаем запрос объектов из БД for (rop <- new OQuery(Btktst_FltStandaloneAta.Type) { }) { //создаем атрибут, ссылочный на ТМЦ, и загружаем в него текущие настройки из БД val standaloneAttribute = FltStandaloneAttribute.refAttr( sRefClass = "Bs_Goods", sData = rop.get(_.jData) ) //получаем настройки отображения атрибута в выборке val displaySettings = Btk_FltStandaloneLib().getDisplaySettings(standaloneAttribute) //добавляем дополнительные поля в результат onRefresh arrBuffer += (( rop, Row( sCompareKindName = displaySettings.sCompareKindName, sCompareKindCaption = displaySettings.sCompareKindCaption, sFilterValue = displaySettings.sFilterValue, sFilterValueEditor = displaySettings.sFilterValueEditor, bFilterValueRo = displaySettings.bFilterValueRo ) )) } arrBuffer.toVector }
В
Aviв операцииonRefreshItemтакже добавьте вывод дополнительных полей.override def onRefreshItem: Recs = { val rop = thisRop() //создаем атрибут, ссылочный на ТМЦ, и загружаем в него текущие настройки из БД val standaloneAttribute = FltStandaloneAttribute.refAttr( sRefClass = "Bs_Goods", sData = rop.get(_.jData) ) //получаем настройки отображения атрибута в выборке val displaySettings = Btk_FltStandaloneLib().getDisplaySettings(standaloneAttribute) ( rop, Row( sCompareKindName = displaySettings.sCompareKindName, sCompareKindCaption = displaySettings.sCompareKindCaption, sFilterValue = displaySettings.sFilterValue, sFilterValueEditor = displaySettings.sFilterValueEditor, bFilterValueRo = displaySettings.bFilterValueRo ) ) }
В
Aviв операцииcheckWorkabilityдобавьте блокировку поля ввода значения.override def checkWorkability(): Unit = { super.checkWorkability() selection.attrs("sFilterValue").isReadOnly = selection.getSelfVar("bFilterValueRo").asBoolean() }
Добавьте сеттеры. Важно добавить их с признаками вызова
refreshAfterиcwaAfter.@Setter(refreshAfter = true, cwaAfter = true) def setsCompareKindName(event: SetterEvent): Unit = { val res = Btk_FltStandaloneLib().setCompareKindName( FltStandaloneAttribute.refAttr("Bs_Goods", thisRop().get(_.jData)), event ) //если были внесены изменения в настройку, то сохраняем ее в БД if (res.hasChange) { thisApi().setjData(thisRop(), res.attributeValue.buildJson().toNString) } } @Setter(refreshAfter = true, cwaAfter = true) def setsCompareKindCaption(event: SetterEvent): Unit = { } @Setter(refreshAfter = true, cwaAfter = true) def setsFilterValue(event: SetterEvent): Unit = { val res = Btk_FltStandaloneLib().setFilterValue( FltStandaloneAttribute.refAttr("Bs_Goods", thisRop().get(_.jData)), event ) //если были внесены изменения в настройку, то сохраняем ее в БД if (res.hasChange) { thisApi().setjData(thisRop(), res.attributeValue.buildJson().toNString) } }
В
avm.xmlдобавьте настройку атрибутов.<attributes> <attr name="sCompareKindName" isVisible="false"/> <attr name="sCompareKindCaption" isVisible="true" order="100" caption="Вид сравнения"> <editor> <lookup changeableAttr="sCompareKindName" isLookupLazyLoad="true" lookupKeyAttr="sName" lookupListAttr="sCaption" lookupQuery="gtk-Btk_FltAttributeCompareKindAvi#Lookup_ByParams" isResetButtonVisible="true"> <params> <param name="names" value="Equal;InEqual;InValueSet;InList;InterValMnemoCode" dataType="String"/> </params> </lookup> </editor> </attr> <attr name="sFilterValue" isVisible="true" order="110" caption="Значение"> <editor editorTypeAttr="sFilterValueEditor"/> </attr> <attr name="sFilterValueEditor" isVisible="false"/> <attr name="bFilterValueRo" isVisible="false"/> </attributes>
Примечание
Доступные типы сравнения указываются в теге
<params>через символ;. Перечень системных имен указан вru.bitec.app.btk.flt.selected.attribute.FltAttributeCompareKind.
Использование в бизнес-логике#
Пример получения
where-условияпо сохраненной настройке:val rop = thisRop() //создаем атрибут, ссылочный на ТМЦ, и загружаем в него текущие настройки из БД val standaloneAttribute = FltStandaloneAttribute.refAttr( sRefClass = "Bs_Goods", sData = rop.get(_.jData) ) //сформировать макрос, где все условия будут накладываться на колоноку t.id val macros = Btk_FltStandalonePkg().generateMacro(standaloneAttribute, "t.id") if (macros.hasFilter) { dialogs.showMessage(macros.where) } else { dialogs.showMessage("Условий не наложено") }
Пример получения перечня значений по сохраненной настройке:
val rop = thisRop() //создаем атрибут, ссылочный на ТМЦ, и загружаем в него текущие настройки из БД val standaloneAttribute = FltStandaloneAttribute.refAttr( sRefClass = "Bs_Goods", sData = rop.get(_.jData) ) //получить перечень Gid-ов, удовлетворяющих условию val valuesOpt = Btk_FltStandalonePkg().getRefAttrValues(standaloneAttribute) if (valuesOpt.isDefined) { dialogs.showMessage( s"""Кол-во: ${valuesOpt.get.size} |Первые 20 значений: ${valuesOpt.get.take(20).mkString(";")} |""".stripMargin ) } else { dialogs.showMessage("Условий не наложено") }