Технические особенности и утилиты 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 оптимизирует рекурсивные запросы, поэтому они могут быть достаточно эффективными, особенно для относительно небольших деревьев.