# Сервисные возможности для классов Возможности классов расширяются сервисным окружением, которое постоянно совершенствуется. ## Служебные атрибуты Для сервисных возможностей фреймворк добавляет в таблицы служебные атрибуты. Служебные атрибуты заканчиваются постфиксом `_dz`. Пример: - `nVersion_dz` - `screateuser_dz` - `dcreatedate_dz` ```{attention} Разработчикам запрещено создавать и изменять такие атрибуты. ``` ### Отображение состояния сессии в форме Состояние сессии можно определить по цвету системных операций `Сохранить` и `Откатить` на тулбаре формы. Все состояния формы перечислены в таблице: | Пользовательская блокировка | Данные для отправки в базу данных | Кнопка "Сохранить" | Кнопка "Откатить" | |-|-|-|-| | Нет | Нет | не активна | не активна | | Нет | Есть | активна | активна(крест) | | Есть | Нет | не активна | активна(круг) | | Есть | Есть | активна | активна(крест) | ## Автонумерация Автонумерация – процесс автоматической выдачи значения для атрибута класса. В основе лежит выдача порядкового номера (числа), которое может быть преобразовано в значение по маске. Автоматическая нумерация объектов системы позволяет устанавливать для созданных объектов уникальные коды. При этом создаваемые коды последовательно увеличиваются. Существует два режима работы автонумерации: - Без заполнения пропусков - С заполнением пропусков ### Определение разреза автонумерации Автонумерация в пределах значений одного или нескольких атрибутов класса называется автонумерацией с разрезом. Значение разреза может вычисляться по правилу, указанному в теге «expression», если выражение не указано, будет использоваться значение атрибута. Например, в течении одного дня выдаются номера последовательно, при наступлении следующего дня номера начинают выдаваться заново. Такой разрез называется разрез по дате. ### Настройка автонумерации без разреза Для автонумерации без разреза в odm файле класса указываем для атрибута тип `autonum`, далее в блоке `autonum` задаем имя разреза «dimension» ```xml lpad(counter,3,"0") ``` ### Настройка автонумерации с разрезом Для настройки разрезов `dimension` 1. Задайте уникальные имена 2. Укажите атрибуты разреза \ Разрезы указываются из атрибутов класса 3. При необходимости задайте порядковый номер \ Если его не указывать, то они будут обрабатываться в алфавитном порядке. Количество разрезов должно быть `>= 1`. Указание нескольких разрезов: ```xml lpad(counter,3,"0") truncDate(dimValue) ``` Если в настройках автонумерации указан разрез, то автоматически включено заполнение пропусков. Отключить заполнение пропусков можно через свойство `isHoleFill` ```xml ``` ### Общие настройки Возможно использовать маски для вывода значения счетчика. Значение, указанное в элементе mask, будет передано в обработчик Jexl-скриптов. Параметр, заменяемый на значение счетчика, называется `counter`. В Jexl добавлены методы: - `lpad(string, size, padChar)` Добавление строки символами слева - `rpad(string, size, padChar)` Добавление строки символами справа - `truncDate(date)` Получение даты на начало дня от переданной - `truncYear(date)` Получение даты на начало года от переданной В jexl скрипте можно обращаться к api и pkg объектам. К значениям атрибутов разреза возможно применение выражений, они указываются в элементе `expression` для `dimension`. На каждый разрез может быть не больше одного выражения. Значение, указанное в элементе `expression`, будет передано в обработчик Jexl-скриптов, параметр, заменяемый на значение атрибута разреза, называется `dimValue`. ```xml var sOTCode = Btk_ObjectTypeApi.load(self.idObjectType()).getByAttrName(\"sCode\"); if (sOTCode eq \"OperDecisionPrj\" || sOTCode eq \"ProductDecisionPrj\") { counter + \"-Г\" } else { counter + \"-Г.\" + Bs_PrjVerApi.load(self.idPrjVer()).getByAttrName(\"sCode\") } if (dimValue == null) { null } else { var sCode = Btk_ObjectTypeApi.load(dimValue).getByAttrName(\"sCode\"); if (sCode eq \"OperDecisionPrj\" || sCode eq \"OperDecisionWrk\" || sCode eq \"ProductDecisionPrj\" || sCode eq \"ProductDecisionWrk\" ) 0L else null; } ``` Для обращения к полям класса при вычислении маски используется переменная `self`, которая является объектом типа Aro, поэтому для получения значения атрибута необходимо добавлять в конце скобки (т.к. это метод). Например: ``` self.sCaption() ``` После генерации кода необходимо будет выполнить метод `*Dpi.autoNumCreateTable`. Если с момента последнего выполнения данного метода были изменены данные автонумерации (имена разрезов, типы данных разрезов, количество разрезов), то таблицы и индексы будут пересозданы. Так же генерация окружения автонумерации выполняется перед инициализацией первоначальных данных (init data), которая вызывается при обновлении проектного jar-кода или при выполнении генерации таблиц в Intellij Idea : `External Tolls > Generate Tables` Присвоение значения автонумерации происходит в момент сохранения транзакции. При ручной установке значения автонумерующегося атрибута (при работе из интерфейса) будет задан вопрос: "Значение присваивается автоматически в момент сохранения, ввод данных отключит автонумерацию. Продолжить?" Положительный ответ на вопрос установит значение и отключит автонумерацию объекта. Для повторного включения автонумерации нужно сбросить значение атрибута. ### Автонумерация в миксинах Возможно задание автонумерации в миксине. Атрибуты, которые объявлены как автонумерующиеся в миксине, но при этом как обычные в целевом классе, получат настройки автонумерации + сквозные таблицы хранения значений автонумерации для всех классов, использующих данный миксин. Если атрибут объявлен как автонумерующийся и в миксине, и в классе, то будет использована настройка класса. ### Создание окружения автонумерации Для создания окружения автонумерации по классу: 1. Зайдите в приложение "Настройка системы" 2. Откройте список классов 3. Перевидите фокус на целевой класс \ Для данного класса должен стоять признак "Есть автонумерация" 4. Выполните операцию «Создать структуры автонумерации» \ Данная операция находится под молоточками. \ При этом произойдет вызов метода `*Dpi.autoNumCreateTable` Для создания окружения автонумерации по атрибуту класса: 1. Откройте карточку класса 2. Переведите фокус на целевой атрибут 3. Выполните операцию «Создать структуру автонумерации» \ Данная операция вызывает метод `*Dpi.autoNumCreateTableByAttr` ### Проектное переопределение Для индивидуальной настройки работы автонумерации реализован механизм проектного переопределения. Вся настройка производится из пользовательского интерфейса без необходимости изменения исходного кода и перекомпиляции. Проектные настройки хранятся в БД и не сбрасываются при установке обновлений. Для выполнения проектного переопределения: 1. Зайдите в карточку класса 2. Выберете автонумерующийся атрибут 3. Откройте закладку «Настройки автонумерации»; 4. На панели открытой закладки нажмите кнопку «Переопределить» 5. Настройте разрезы и маску; 6. Выполните операцию создания окружения нумерации. #### Настройка счетчиков и данных разрезов. Если автонумерация без разрезов, то она и без заполнения пропусков. Счетчик такой автонумерации хранится в сиквенсе. Автонумерация с разрезами хранит свои данные в двух таблицах: - Таблица разрезов \ В этой таблице на каждый уникальный набор значений разрезов хранится значение счетчика, маска, признак, что есть пропуски - Таблица пропусков \ Для каждого разреза (ссылка на таблицу разрезов) хранит информацию о имеющихся пропусках. В виде пар значений: "значение от" и "значение до" Таким образом для изменения текущего значения счетчика для автонумерации без разрезов достаточно изменить значение сиквенса. Для автонумерации с разрезами чтобы изменить счетчик нужно установить значение в таблице разрезов. Для настройки: 1. Зайдите в карточку класса 2. Выберете автонумерующийся атрибут 3. Откройте закладку "Настройки автонумерации" 4. Откройте закладку "Данные автонумерации" 5. Настройте значение счетчика: 1. Если автонумерация без разрезов, то на детальной закладке будет карточка с одним атрибутом, который показывает текущее значение сиквенса. 2. Если автонумерация с разрезом, то на детальной закладке будут отображены данные из таблицы разрезов и таблицы пропусков. Для изменения счетчика в нужном разрезе указывается значение поле «Максимальное значение». Если разреза нет, то его можно создать. Если удалить разрез, то автонумерация начнется с 1 для этого разреза. ## Копирование объектов При создании класса, при необходимости генерируется операция копирования данный метод позволяет создать новый объект скопировав значение атрибутов и содержание его коллекций. При этом происходит глубокое копирование, то есть для коллекций создаются новые строчки. Так же возможно добавить в один объект данные из другого объекта. Копирование управляется свойствами в метаданных (все по умолчанию = true). Возможность копирования объекта можно настроить на уровне всего класса, и на каждый атрибут. Например, запретив копирование нескольким атрибутам в классе. На уровне класса копирование настраивается свойством `isCopyObjectEnabled` На уровне атрибута свойством `isCopyInCopyObject` В случае, если на классе включено копирование: В мастер-выборке `EntityAvi#Default` создана операция «Копировать», пустая и не активная по умолчанию. Для классов с включенным свойством `isCopyObject` операция будет содержать исполняемый код, в отображении `List` операция будет активна. В отображении `Card` создается операция `copyObjectCard`, которая создает новый объект и копирует в него данные по `CardRep.IdItemSharp`. Для параметра мастер-объекта используется переменная `idBOParent#`. Настройка копирования в Odm для класса: ```xml ``` Настройка копирования для атрибута класса: ```xml ``` При создании метода `copyObject` в мастер-классе в метод будут добавлены вызовы `copyObject` из коллекций если для этих классов включено копирование. Если не указано свойство `isCopyInCopyObject` атрибут будет копироваться в методе `copyObject`. По умолчанию не копируются системные атрибуты: - `id` - `gid` - `gidRef` - `sImpExpKey_dz` - `nImpExpFlag_dz` - `sMnemoCode_dz` - `sHeadLine_dz` - атрибуты связи с мастер-сущностью. ### Параметры метода copyObject - Для миксинов \ `(gidFrom: NGid, gidTo: NGid, gidParent: NGid)` - Для V-коллекций \ `(idFrom: NLong, idTo: NLong, gidParent: NGid)` - Для всех остальных \ `(idFrom: NLong, idTo: NLong, gidParent: NLong)` Метод `copyObject` создает объект (если `gidTo`/`idTo` не передан) с помощью dpi метода, установка значений происходит через Dpi-сеттеры. ## Группировка Группировка используется для систематизации хранения объектов и удобства восприятия пользователем, так же группировка позволяет массово управлять характеристиками и настройками объектов класса. Дерево групп представляет собой иерархическую структуру, потомки наследуют характеристики предков с возможностью переопределения. Для включения группировки класса 1. Откройте Odm для класса 2. Задайте `group.type` - тип группировки - single \ Объект может входить только в одну группу - multi \ Объект может входить в несколько групп 3. Задайте group.root - системное имя корня группировки класса ```xml ``` 4. Добавьте в набор коллекций класса развязку групп ```xml   ``` Для подключения группировки, у класса обязательно должен присутствовать глобальный идентификатор (атрибут `gid`). Для управления группами в карточке объекта можно добавить закладку. Для этого: 1. Откройте avm класса 2. Добавьте закладку ```xml ``` Связь объекта с группой осуществляется через класс Btk_ObjectGroup, денормализованное дерево групп объектов хранится в Btk_FlatObjectGroup. Эти таблицы могут использоваться в запросах для фильтрации объектов или организации прикладной бизнес-логики. ### Интеграция с выборкой При включении группировки в список объектов класса добавляются операции по работе с группами. Для того чтобы открыть панель группировки для класса: 1. Откройте список объектов 2. Выполните операцию "Открыть группы" \ При этом слева откроется панель с деревом групп. При включенной панели группировки в основной части выборки будут показываться объекты, находящиеся в текущей группе или ее потомках. Новые объекты будут создаваться в выбранной группе. ## Сервис прикрепленных файлов Сервис прикрепленных файлов позволяет прикреплять к объектам класса произвольные файлы, которые сохраняются в специальном хранилище на сервере и сопоставляются с конечным объектом-владельцем. ### Файловое хранилище Global3-Framework позволяет классифицировать места хранения файлов по функциональной принадлежности. Для этой цели используется справочник файловых хранилищ системы. Файловое хранилище может быть локальным или сетевым. Локальное хранилище располагается в локальной директории сервера приложений. Сетевое хранилище использует SMB протокол. Перед работой с файлами необходимо настроить файловые хранилища. Для открытия списка настроек: 1. Откройте приложение `Настройка системы` 2. Выполните пункт меню `Сущности > Файловые хранилища` Минимально в системе должно быть настроено два файловых хранилища: - `Default` – хранилище по умолчанию - `btkAttach` – хранилище прикрепленных файлов Для минимизации роста объема файлового хранилища используется специальный алгоритм хранения, позволяющий сопоставлять с одним физическим файлом несколько объектов системы: - Файл в хранилище загружается только один раз - копирование объектов системы, связанных с файлом, не создают - физическую копию файла, а создают символическую ссылку на файл (`btk_attachitem`). Существует два режима работы прикрепленных файлов: - Простой - Версионный ### Простое хранение файлов Позволяет работать с прикрепленными файлами без возможности хранения истории изменений файла. Этот режим является частным случаем версионного хранения с одной версией. Версия у файла всегда остается одна, любое изменение приводит к перезаписи файла версии. Для режима простого хранения файлов доступны операции: - Прикрепить файл \ В хранилище добавляется новый файл, загруженный пользователем, а в закладке отображается запись, соответствующая этому файлу, отмеченная как основная. - Удаление \ Удаляется запись из закладки. В случае, если запись отмечена как основная, произойдет удаление файла из хранилища. ```{attention} Операция удаления записи из хранилища необратима ``` - Скачать файл \ Из хранилища скачивается файл с именем и расширением, указанным в записи. - Сделать файл основным \ Для записи устанавливается флаг «Основной». Для записи, которая была основной до этого, флаг снимается. При копировании родительской записи, так же копируются записи прикрепленных файлов, без флага «Основной». ### Версионное хранение файлов Версионирование прикрепленных файлов позволяет сохранять историю изменений. Если включен режим версионирования, любое изменение файла приводит к созданию новой версии. В режиме версионного хранения доступны операции обычного хранения, а также ряд дополнительных: - Добавить новую версию \ К записи добавляется новая версия. При этом предыдущая версия остается в хранилище. - Удалить последнюю версию \ Из хранилища удаляется последняя версия файла. В случае, если версия только одна, будет предложено удалить запись. ```{attention} Операция удаления записи из хранилища необратима ``` - Отобразить историю изменений файла \ Открывается список с версиями для текущей записи. Из списка так же доступны операции удаление и скачивания для каждой версии. При копировании родительской записи, так же копируются записи прикрепленных файлов с версиями, без флага `Основной`. ### Настройка По умолчанию сервис прикрепленных файлов для класса отключен. Для подключения необходимо в odm файле в настройке класса указать тип хранения – обычный или версионный ```xml ``` При генерации avm файла в отображение `List` и `Card`, а также для классов с иерархией в отображение `tree` добавится закладка "Прикрепленные файлы" ## Поиск по шаблону Поиск по шаблону - серверная возможность поиска объектов класса по частичному или полному совпадению введенного текса со значениями полей объекта или его заголовка и мнемокода. Поиск регистронезависимый. ### Алгоритм работы поиска 1. Определяются поля, по которым требуется осуществлять поиск, и их приоритет. 2. Выполняются последовательно запросы пока не получен результат в виде одной записи 1. по всем полям в порядке приоритета ищутся значения по полному совпадению введенного текста (`=`) 2. по всем полям в порядке приоритета ищутся значения по частичному совпадению введенного текста (`like`) 3. Возвращается результат \ Результатом поиска является первый запрос который вернул одну запись, или первый запрос который вернул данные. По умолчанию для класса включен поиск по шаблону, и осуществляется по служебным полям `sheadline_dz` и `smnemocode_dz`. Сначала по заголовку, потом по мнемокоду. ### Настройка поиска для класса В odm-файле для класса добавить тег `patternSearch` ```xml ``` Описание настроек: - `isActive` \ Активность поиска по шаблону на классе. Если выключено, то функция поиска по шаблону будет возвращать 0 записей. Умолчательное значение = true - `headLineOrder` \ Порядок поиска по служебному полю `sheadline_dz`. Умолчательное значение = 1. Чем меньше значение, тем выше приоритет описка по этому полю. Поля с наименьшими значениями порядкового номера обрабатываются в первую очередь. - `mnemoCodeOrder` \ Порядок поиска по служебному полю `smnemocode_dz`. Умолчательное значение = 2. Логика обработки как в `headLineOrder` - `searchType` \ Тип поиска: - по началу слова(по умолчанию) \ like с правым % - по вхождению \ like с правым и левым % - `indexHlMc` \ Индексировать поля `sheadline_dz` и `smnemocode_dz`. При включенном свойстве будут созданы индексы по этим поля в верхнем регистре. ### Настройка поиска по атрибутам 1. Откройте odm файл 2. Добавьте тэг patternSearch для нужного атрибута \ Если тег добавлен, то атрибут участвует в поиске по шаблону. ```xml ``` Описание настроек: - `order` \ Порядковый номер поиска по полю этого атрибута. \ Нумерация сквозная по классу, т.е. можно настроить, чтобы атрибут обрабатывался раньше заголовка и мнемокода. - `searchType` \ Тип поиска: - по началу слова(по умолчанию) \ like с правым % - по вхождению \ like с правым и левым % ### Перекрытие метода поиска Метод реализован в одном из рутовых API: ```scala ru.bitec.app.gtk.eclipse.rdb.SEntityBaseApiImpl#findByPattern ``` В наследниках его можно перекрыть, и написать свою логику поиска по шаблону. ## Объектные характеристики Характеристика – это качественное или количественное свойство объекта. Характеристиками в системе являются специальные атрибуты, которые содержат дополнительную пользовательскую информацию. Система может хранить характеристики двумя способами: - Хранение в json контейнере объекта \ Этот способ является основным. Представляет собой NOSql расширение строки таблицы, что позволяет добавлять характеристики динамически, без пересборки проекта и обновления схемы базы данных. - Хранение в виде атрибута класса с признаком «объектная характеристика» \ В некоторых случаях характеристики используются в высоконагруженной бизнес-логике, что не позволяет использовать json контейнер. Для снижения накладных расходов доступа к данным характеристика может хранится в колонке таблицы класса. При добавлении характеристик, которые хранятся в атрибуте класса требуется синхронизация схемы БД. ### Интеграция с выборкой Для вывода характеристик используется универсальное отображение объектных характеристик. За необходимость генерации отображения отвечает настройка в Оdm `objectAttrCardType` Варианты настройки: - `None` \ Объектные характеристики не используются, отображение `Card_ObjectAttr` не формируется - `Simple` \ Объектные характеристики настраиваются в классе или на типе объекта. Если у класса есть тип объекта, то характеристики настраиваются на типах объекта этого класса. Если у класса нет типа объекта, то выводятся все объектные характеристики. - `Group` \ Объектные характеристики настраиваются для групп - `GroupAndObjectType` \ Объектные характеристики настраиваются для групп и дополняются характеристиками, настроенными на типе объекта. Отсутствие настройки равнозначно `objectAttrCardType="simple" ` В отображение объектных характеристик выводятся значения атрибутов, настроенных в качестве объектных характеристик класса. Характеристики хранятся в системном json атрибуте `jObjAttrs_dz`, а также в атрибутах класса с признаком: `isObjectAttr="true" ` Так же в этом отображении выводятся и `универсальные характеристики` (См. главу `Универсальные характеристики` в [Документация по модулю btk](http://helpcenter/btk_documentaion_v1/html/index.html)) ### Настройка характеристик на проекте Пользователь может добавить необходимые характеристики в карточке класса. Для этого: 1. Откройте приложение `Настройка системы` 2. Выполните пункт меню `Сущности > классы` 3. Перейдите на закладку атрибуты 4. При необходимости задайте значение по умолчанию для уже созданных атрибутов 5. Создайте атрибут объектной характеристики ```{note} Имена атрибутов должны соответствовать соглашению по именам атрибутов: - Строковые начинаются с `s`. Например, `sStringAttrName` - Числовые начинаются с `n`. Например, `nNumberAttrName` - Даты начинаются с `d`. Например, `dDateAttrName` - Логические начинаются с `b`. Например, `bBooleanAttrName` ``` 6. Сбросьте кэш выборки \ Для этого снимите галочку с в пункте меню `Сервис > Управление решением > Использовать кэш метаданных выборок` 7. Сбросьте кэш настройки \ Для на текущей закладке атрибутов выполните операцию `Сбросить кеш настройки` ### Значения по умолчанию Значения по умолчанию заполняются при создании объекта. Если класс с группировкой, то значения по умолчанию подставляются из настроек основной группы. Для базовых атрибутов класса значение по умолчанию указывается в Odm, и устанавливается в Dpi при вставке. Для таких атрибутов есть возможность переопределить значение, указанное в Odm. #### Проектное переопределение В карточке класса в списке атрибутов нужно установить значение в поле "Значение по умолчанию". Для сброса переопределения: - в списке атрибутов выполнить операцию "Удалить переопределение значения по умолчанию" #### Программное переопределение Программное переопределение возможно в методе ```scala ru.bitec.app.btk.Btk_AttributeApi#overrideDefaultValue ``` #### Правила указания значений 1. Для ссылочных полей указывается ID ссылочного объекта 2. Для атрибутов переменной ссылочности указывается GID ссылочного объекта 3. Для числовых разделителем целой части и дробной является точка. 4. Дата указывается в стандартном формате. dd.MM.yyyy HH:mm:ss. Если указать «sysdate», то значением по умолчанию будет текущая дата на момент вставки объекта. ### Програмная установка значений json-характеристик В бизнес-логике: ```scala val rop = Bs_GoodsApi.load(209851.nl); //установка ссылочной характеристики Bs_GoodsApi().setAttrValue(rop, "idGradeJson", 77016.nl); ``` В jexl-скрипте: ``` var rop = Bs_GoodsApi.load(209851L); //установка ссылочной характеристики Bs_GoodsApi.setAttrValue(rop, "idGradeJson", 77016L); ``` ## Аудит Аудит предназначен для фиксации различных событий при работе пользователей в системе: вставка, изменение, удаление, выполнение операций, и т.д. Аудит ведется в рамках бизнес-объектов и классов, входящих в БО. Для нужд аудита используется отдельная схема в БД, Для БО в схеме `Aud` формируется таблица с именем `<Имя класса-шапки БО>_dzAud` в табличном пространстве, указанном в настройке модуля btk `Табличное пространство аудита` (по умолчанию `pg_default`), в которой хранятся данные аудита. ```{attention} Если в классе включен аудит, а в классе-шапке БО, в который входит этот класс – не включен или у него не синхронизирована структура аудита, аудит не будет сохраняться. Класс-шапка БО для объекта класса определяется через системное поле `gidRoot_dz` ``` Настройка: 1. Зайдите в приложение `Настройка системы` 2. Откройте список классов \ Пункт меню `Сущности > Классы` 3. Выберите требуемый класс 4. В закладке `Характеристики` установите признак `Вести аудит` 5. Если класс не является шапкой БО, включите аудит для шапки БО 6. Для класса шапки БО выполните операцию `Создать оболочку аудита`\ При этом произойдет создание таблицы аудита в схеме аудита 7. Сбросьте `shared кэш` \ Для этого выполните пункт меню `Сервис > Управление решением > Очистить кэш ORM(shared кэш)` ```{attention} Для того чтобы аудит вступил в силу, пользователь должен переоткрыть форму ``` Просмотр данных аудита: - В карточке объекта по пункту меню: `Информация > Аудит объекта` - В списке объектов класса из контекстного меню `Информация > Аудит` - Общий список аудита `Настройка системы \ Аудит > Аудит` Методы работы с аудитом: - `Btk_AuditPkg().isEnabledAudit` \ Признак, что аудит ведется - `Btk_AuditPkg().isEnabledAudit()` \ Сеттер, для изменения признака ведения аудита в сессии - `Btk_AuditPkg().insertRow` \ создать запись в аудите. ### Перенос существующих таблиц аудита в указанное табличное пространство: Для обновления табличного пространства: 1. Зайдите в приложение `Настройка системы` 2. Откройте настройки модуля btk \ Пункт меню `Настройки и сервисы > Настройки модулей системы > Общие настройки модулей` 3. Выберите настройки модуля btk 4. В настройке `Табличное пространство аудита` укажите желаемое табличное пространство 5. По операции под молоточками `Скачать файл со скриптом переноса таблиц аудита` скачайте скрипт sql и запустите на выполнение ## Автоматическая генерация штрих-кодов объекта Сервисная возможность при вставке объекта класса формировать ему штрихкод. Для этого в Оdm в теге Class есть свойство `barCodeObjectTypeName` в качестве значения указывается системное имя объектов класса `Bs_BarCodeObjectType`. Для регистрации новой записи используется метод ```scala Bs_BarCodeObjectTypeApi().register ``` ### Описание работы маски В случае, если в маске указано: `001-[counter]` При этом: - Длина штрихкода составляет 10 символов. - Следующее значение счетчика: 12 Итоговый штрихкод: ``` 001-000012 ``` ## Подписи объектов для печати Сервисная возможность указания типа подписей объекта. Используется при печати отчетов. Позволяет формировать в печатной форме список лиц с местом для подписи. Список подписей настраивается в коллекции для печатной формы. Список может быть переопределен у объекта. Для хранения подписей печатных бланков используется json-контейнер `jSign_dz`. Приоритет отображения данных по типу подписи: 1. Данные по типу подписи, хранимые в атрибуте объекта класса `jSign_dz` (формируется для всех классов) 2. Данные из настроек подписей в отчете. При печати отчета, если удалось определить сохраненные подписи объекта, то будут переданы настройки подписей на самом отчете. Кроме этого, в базовых справочниках реализована возможность формирования комиссий, состав которых может быть передан в отчет. Подробная настройка комиссий будет рассмотрена в разделе «Прикладная типизация объекта класса» Данные подписей передаются в параметр `SIGNDATA_DZ`. Представляют собой json-объект с ключами: - `idComission` – идентификатор комиссии. \ Определяется через тип объекта и подразделение. - `data` – массив подписей объекта \ Каждый элемент представляет собой json-объект, на каждый тип подписи. Описание полей json-объекта подписи: - `idBlankSignType` – ИД типа подписи - `idBlankSignTypeMC` – системное имя типа подписи - `idBlankSignTypeHL` – наименование типа подписи - `sPosition` – должность - `sFIO` – Фамилия Имя Отчество - `sBasisDocument` – Документ-основание - `dDate` – дата подписи - `idDepartment` – ИД подразделения - `idDepartmentMC` – код подразделения - `idDepartmentHL` – наименование подразделения - `nOrder` – Порядковый номер подписи комиссии Для работы с атрибутом `jSign_dz` реализован API в пакете `Bs_ObjectSignPkg` В случае, если для класса задан `тип объекта` для работы с подписями можно воспользоваться стандартной закладкой `ru.bitec.app.bs.sign.Bs_ObjectSignAvi.List_Master` ## Полнотекстовый поиск Сервисная возможность класса осуществлять быстрый поиск по значению атрибутов класса. Данные классов хранятся в индексе полнотекстового поиска в схеме fts: - `Btk_FtsReg` – индекс объектов классов - `Btk_FtsWords` – индекс используемых слов. Форма поиска доступна в главном меню приложений, в меню «Сервис» ### Настройка Для включения полнотекстового поиска в Odm установить свойство класса `fts.Enabled` в значение `true` По умолчанию все строковые значимые/автонумерующиеся/вычисляемые, а также ссылочные на объект и атрибуты переменной ссылочности с указанным ссылочным классом участвуют в полнотекстовом поиске. Для переопределения поиска по атрибуту используется свойство `fts.Enabled` ### Обновление индекса После обновления кода на проекте и вызова ssh-команды `init Data` происходит анализ классов. Если настройки класса изменились (класс стал индексируемым или добавились новые индексируемые атрибуты, которые до этого не индексировались. Новые атрибуты не учитываются). Происходит планирование обновления индекса для всех объектов класса. При изменении объектов классов и сохранении в БД (session.flush()), которые индексируются, или на которые ссылаются объекты индексируемых классов, эти объекты добавляются в лог измененных объектов индекса. Значения ссылочных полей хранятся в строковом виде, и определяются как значение поля `sHeadLine_dz` ссылочного объекта. В системе зарегистрировано задание "Синхронизация полнотекстового индекса", которое обновляет индекс. Это задание анализирует таблицу измененных объектов и таблицу измененных классов, сохраняет эти данные в общую очередь на индексацию, вычисляет и обновляет данные индекса. ### Команды строки поиска Формат описания: - `Введенная строка` \ Логика обработки Команды: - `Цепь` \ Найдет текст, в котором присутствует слово цепь, цепи, цепью и тд. - `цепь звено` \ Надет текст, в котором присутствуют оба слова и их склонения - `Цепь И звено` \ Тоже самое, что и цепь звено. Пробел считается оператором «И» - `Цепь & звено` \ Тоже самое, что и цепь звено - `Цепь ИЛИ звено` \ Надет текст, в котором присутствует одно из или оба слова и их склонения - `Цепь | звено` \ Тоже самое, что и цепь ИЛИ звено - `Цепь И (Звено ИЛИ Кольцо)` \ Найдет текст, в котором есть слово цепь и одно из слов звено или кольцо - `Цепь#40` \ Надет текст, в котором есть похожие на цепь слова, например, цеп. Число после # показывает точность поиска, от 1 до 100. Где 100 – полное совпадение - `#Цепь` \ Тоже самое, что и Цепь#40 - `Цеп*` \ Найдет текст, слова в котором начинаются на цеп. Например, цепь, цепная и тд. ## Пообъектный доступ Сервисная возможность разграничения доступа на уровне объектов класса. Права выдаются ролям и пользователям. Пользователи, замещающие других, обладают правами замещаемых пользователей. Права на объекты выдаются в виде группы доступа (`Btk_RlsAccessGroup`), на каждый объект для пользователя или роли может быть только одна группа. ### Управление правами Для управления правами в интерфейсах выборок используется библиотека `Btk_RlsLib`, и ее методы: - `manageAccess` \ Операция для стандартных выборок, вызывает интерфейс настройки прав для всех выделенных строк. Определяет объекты через поля `gid` или `id`/`idClass` - `manageAccessByGid` \ Открывает интерфейс настройки прав по одному переданному объекту - `manageAccessByGidList` \ Открывает интерфейс настройки прав по перечню объектов Если в выборке администрируются одновременно несколько объектов, то изначально будут показаны пользователи и роли, обладающие правами на все объекты и одинаковыми группами доступа. Для выдачи прав в бизнес-логике используются методы пакета Btk_RlsPkg: - `grantToUsers` выдача прав пользователям - `revokeFromUsers` – отзыв прав у пользователей - `grantToRoles` – выдача прав ролям - `revokeFromRoles` – отзыв прав у ролей ### Удаление администрируемых объектов. При удалении объектов, для которых используется пообъектный доступ, необходимо выполнить очистку структур доступа. Для этого используются методы пакета Btk_RlsPkg: - `clearByObject` - `clearByObjectList` ### Проверка наличия прав на объекты Для проверки прав в запросах можно использовать таблицу `Btk_RlsUserRightsFlat`, при построении запроса необходимо использовать `exists` по `gidObj` и `idUser` Для проверки прав в бизнес-логике используются методы пакета Btk_RlsPkg: - `getAccessByObject` - `getAccessByObjectList` ## Сервис универсальных коллекций Для работы системы реализована возможность прикрепления произвольной коллекции к любому классу системы, при этом не создавая связи между модулями и не привлекая к настройке программиста. Для этого во всех Dpi методах удаления вызывается ```scala Btk_UniversalTabPkg().deleteManualCollections(rop.gid) ``` При удалении записи в которой есть коллекции доступно 2 варианта действий: - Каскадное удаление \ Удаляются все записи коллекций, ссылающиеся на удаляемую запись - Генерация ошибки \ При наличии записей в коллекции, ссылающихся на удаляемую, поднимается ошибка ### Подключение из приложения 1. Откройте приложение `Настройка системы` 2. Выполните пункт меню `Сущности > Классы` 3. Откройте карточку нужного класса 4. Перейдите на закладку `Коллекции` \ На данной закладке отображаются все коллекции класса, а также есть возможность добавить универсальную коллекцию 5. При необходимости добавьте универсальную коллекцию \ При добавлении требуется указать класс и атрибут ссылочности. ```{attention} Ссылочность возможна только для атрибутов с типом данных `Long` и типом `RefObject` с указанным классом для ссылочности или с типом данных `Varchar` и типом `RefAnyObject`. Ссылочность поддерживается в том числе и для проектных атрибутов и объектных характеристик, хранящихся в json контейнере `jObjAttrs_dz` ``` 6. При необходимости настройте «Действие при удалении» 7. При необходимости "Удалите коллекцию" \ Удаление из списка доступно только для коллекций, добавленных вручную 8. Сбросьте кэш Btk_ClassCollection является классом попадающим в кэш, после подключения новых коллекций необходим сброс объектного кэша ```{attention} При подключении для типа объекта универсальных закладок обязательно убедитесь, что для класса указана универсальная коллекция, если она необходима ``` ### Подключение из прикладного кода В некоторых случаях необходимо подключение универсальной коллекции из прикладного кода, для этого можно воспользоваться методом ```scala Btk_ClassCollectionApi().register( idpBtkClass: NLong, idpRefClass: NLong, idpRefAttr: NLong, spDelAction: NString ): ApiRop ``` Доступные действия содержатся в объекте ```scala Btk_ClassCollectionApi ``` ## Денормализация классов-деревьев Зачастую для древовидных классов требуется вспомогательный класс денормализации, который обеспечивает более быстрый доступ к данным по иерархичным связям, чем рекурсивные запросы. Для работы системы с денормализацией древовидных классов реализован трейт (`trait`) `TreeDenormApi` (`ru.bitec.app.btk.denormalization.TreeDenormApi`). Данный трейт предоставляет функциональность создания/обновления/удаления денормализации по объектам из класса-дерева. Пример: Пусть название класса-дерева будет `Btk_TreeExample`. Для подключения денормализации необходимо создать класс, хранящий денормализацию. Обычно в таком классе содержатся атрибуты: - `id` - `idClass` - `idParent` - `idChild` - `nParentLevel`. Для даного примера название такого класса `Btk_TreeExampleDenormalization`. После создания класса `Btk_TreeExampleDenormalization` необходимо подключить к его Api трейт `TreeDenormApi`. Затем имплементировать абстрактный метод `masterTableName`, который должен возвращать имя таблицы хранения класса-дерева `Btk_TreeExample`. Пример синтаксиса подключения: ```scala class Btk_TreeExampleDenormalizationApi extends Btk_TreeExampleDenormalizationDpi[ Btk_TreeExampleDenormalizationAro, Btk_TreeExampleDenormalizationApi, Btk_TreeExampleDenormalizationAta ] with TreeDenormApi[ EntityAbst,java.lang.Long, Btk_TreeExampleDenormalizationAro ] override def masterTableName: NString = "Btk_TreeExample" ```   Если названия полей в вашем классе хранения денормализации отличаются от обычных имён, то имеется возможность переопределения методов геттеров названий полей: ```scala def idParentFieldName = "idParent" def idChildFieldName = "idChild" def nParentLevelFieldName = "nParentLevel" ``` Далее нужно запланировать событие заполнения денормализации. Обычно это делают в Api основного класса (`Btk_TreeExample`) в методе установки ссылки на предка предположим `setidParent` и в методе `insert` на случай, когда родительская запись остается пустой. В таком методе нужно вызвать ```scala ru.bitec.app.btk.denormalization.TreeDenormApi#updateDenormAfterFlush ```   Метод `updateDenormAfterFlush` накапливает все записи, которые ему передавали что позволяте обновить денормализацию после сброса данных в БД. Также необходимо удалять денормализацию при удалении записи из основного класса. Обычно это делают в методе `delete`. В нём нужно вызвать метод ```scala ru.bitec.app.btk.denormalization.TreeDenormApi#deleteDenormBeforeFlush ``` `deleteDenormBeforeFlush` накапливает все записи, которые ему передавали что позволяет удалять по ним денормализацию перед сохранением в БД. Также возможно обновление/удаление денормализации непосредственно в момент редактирования, а не после сохранения, однако такое использование замедлит работу сервиса, и оно не рекомендовано. Методы прямого обновления/удаления: ```scala ru.bitec.app.btk.denormalization.TreeDenormApi#updateDenorm ru.bitec.app.btk.denormalization.TreeDenormApi#deleteByObject ``` ```{note} Реализацию можно посмотреть в `Bs_DepartmentTreeApi` в связке к `Bs_DepartmentApi`. ``` ## Настройки приложения Настройки приложения позволяют задовать глобальные константы Для получения значения настройки используйте функцию ```scala Btk_AppPropertiesTypeApi.GetbValue( sType //- Системное имя настройки idObjectType //Тип объекта ) //возвращает true или false ``` ### Btk_AppPropertiesType Таблица настройки приложения, содержит поля: - `sSysName` - Системное имя - `sCaption` - Наименование - `sDescription` - Описание - `idModule` - Модуль ### Btk_AppProperties Таблица значений настроек приложения, содержит поля: - `idType` - Настройка - `idObjectType` - Тип объекта - `bValue` - Значение boolean - `nValue` - Значение number - `sValue` - Значение string - `dValue` - Значение date ## Вставка изображений в прикреплённый файлы типов word и pdf Для вставки изображений в прикрепляемые файлы документа необходимо настроить нужные изображения в коллекции-расширении(Настройки вставки изображений) к типу объекта этого документа. В коллекции доступны следующие настройки: - Активность Данное поле отвечает будет ли вставленные изображения в прикрепляемый файл - Печатная форма В поле указывается печатная форма с типом jasper и форматом png, которой будет передан только один аргумент "IDDOC" - id документа. - Изображение Файл изображение в формате png ```{note} В случае указания печатной формы как источник изображения файл изображения будет удалён. Если сначала будет указана печатка то при загрузке файла изображения, удалена будет ссылка на печатную форму. ``` - Положение изображения по осям XY Поля отвечают за положение изображение внутри PDF документа, точкой отсчёта является нижний левый угол - Ширина и высота изображение Поля отвечают за размер изображения Как только в коллекции-расширении настройки изображений будет активное изображение для вставки, в отображении прикреплённых файлов к документу данного типа объекта станут активны операции для вставки данных изображений в один документ или вовсе сразу. ## Справочник параметров Для создания локальных коллекций с параметрами класса реализован трейт (`trait`) `Prm_ParamCalcApi` (`ru.bitec.app.prm.techparam.Prm_ParamCalcApi`) и абстрактное отображение `Prm_CalcTechOpParamAbsAvi` (`ru.bitec.app.prm.techparam.Prm_CalcTechOpParamAbsAvi`). ### Prm_ParamCalcApi В трейте реализована функциональность заполнения параметров и их значений. Обычно в коллекции содержатся атрибуты: - `idTechParameter` - Ссылка на параметр Prm_TechParameter - `idRefTable` - Ссылка на таблицу значений параметров Prm_RefTable - `nValue` - Числовое значение - `sValue` - Строковое значение - `dValue` - Значение даты - `bManualInput` - Параметр заполнен вручную Для использования функционала необходимо в Api нужного класса отнаследовать данный трейт и переопределить геттеры(сеттеры) параметров, Api родительского класса. Пример: ```scala class Mnf_MCOperParamApi extends Mnf_MCOperParamDpi[Mnf_MCOperParamAro, Mnf_MCOperParamApi, Mnf_MCOperParamAta] with Prm_ParamCalcApi[EntityAbst, java.lang.Long, Mnf_MCOperParamAro] { override protected def entityAta: Mnf_MCOperParamAta = Mnf_MCOperParamAta override lazy val apiParent = Mnf_MCOperApi().asInstanceOf[SApi[AnyRef, Aro[_ <: AnyRef]]] override def getsValue(rop: ApiRop): NString = rop.get(_.sValue) override def getnValue(rop: ApiRop): NNumber = rop.get(_.nValue) override def getdValue(rop: ApiRop): NDate = rop.get(_.dValue) override def getidValue(rop: ApiRop): NLong = rop.get(_.idValue) override def setidTechParam(rop: ApiRop, value: NLong): Unit = setidTechParameter(rop, value) override def getidTechParam(rop: ApiRop): NLong = rop.get(_.idTechParameter) override def getidRefTable(rop: ApiRop): NLong = rop.get(_.idRefTable) override def getidCalcFormulaByParent(ropParent: AnyRop): NLong = ropParent.asInstanceOf[Mnf_MCOperApi#ApiRop].get(_.idCalcFormula) override def isManualInput(rop:ApiRop): Boolean = rop.get(_.bManualInput).toBoolean } ``` Если в вашем классе не используются какие-либо из этих атрибутов, то их сеттеры и геттеры можно переопределить на Unit() и null-значение соответственно. Функция `isManualInput` используется для выбора параметров, значения которых будут рассчитаны в функции `refreshParamValByParent`. Дополнительные возможности: - `setParamValueRichAnyRef` - простановка значения параметра RichAnyRef(value) - `getParamValue` - получение значения параметра - `clearAllValues` - очищает значения параметра - `fillByParent` - заполнение параметров по формуле от родителя - `getsParamsCodeAndValueByParent` - возвращает строку со значениями параметров вида (код параметра: значение) ### Prm_CalcTechOpParamAbsAvi Абстрактная Avi содержит в себе два функциональных трейта: - `Default_Calc` - Для расчета по формуле, параметры не хранятся - `SavedParamsCalc` - Для коллекций с хранимыми значениями, можно использовать в паре с трейтом Prm_ParamCalcApi #### Default_Calc ```{note} Пример использования - `Mct_TechOpParamAvi.List_idOperCardOperationForCalculate` В интерфейсе: `Управление данными об изделии (Судостроение) -> Технология -> Справочник операционных карт (Mct_OperCardAvi.list) -> Закладка Операции -> Проверка формул по параметрам` ``` Для использования необходимо отнаследоваться от данного отображения и переопределить методы `getFormula` (получение формулы) и `getParams` (получение параметров). #### SavedParamsCalc Аналогично трейту Prm_ParamCalcApi переопределяются сеттеры значений, а также функции `onRefresh`, `onRefreshItem` и `getAdditionalInfo` ```{note} Примеры использования: `ru.bitec.app.mnf.manufCard.Mnf_MCOperTestParamAvi.List_idMcOper` `ru.bitec.app.mct.techproc.Mct_TechProcNormGroupParamAvi.List_idTechProcNorm` ``` Необязательная функция `refreshValues` - заполнение параметров (по умолчанию active = false) - для вызова функции заполнения параметров refreshParamValByParent трейта Prm_ParamCalcApi и/или своего необходимого функционала