image1

© Syntellect 2019


В этом руководстве описывается процесс создания новой карточки, учитывая всё необходимое для работы с карточкой в системе.

Будут рассмотрены следующие возможности платформы:

  1. Формирование схемы данных с указанием физического и логического устройства таблиц. Мы создадим таблицу-перечисление и таблицу, включаемую в тип карточки. Все созданные нами объекты схемы данных мы сохраним в отдельной (новой) библиотеке.

  2. Создание типа карточки с заданием включаемых в него полей и настройкой внешнего вида карточки. Мы покажем, как сделать карточку доступной для пользователя и настроить её контролы и валидаторы.

  3. Создание представлений. Мы создадим представление-источник данных для автокомплита, а также представление с постраничным отображением для нашей карточки. Мы добавим рабочее место, в которое включим представления, и настроим для них права доступа.

  4. Включение типа карточки в типовое решение. Мы рассмотрим такие преимущества типового решения, как нумерация, регистрация, использование типового процесса согласования и резолюций, а также задание прав на работу с карточкой посредством правил доступа.

1. Схема данных

Платформа Tessa разделяет описание схемы данных и типов карточек, т.е. физической структуры таблиц и их логической взаимосвязи. Тип карточки объединяет несколько указанных полей из различных таблиц, а также определяет пользовательский интерфейс карточки, т.е. то, какие поля и каким образом редактируются, и где они визуально расположены.

Ниже приведено руководство по созданию простого типа карточки Документ с указанием следующих полей:

  • Тема документа – строка, длина не ограничена.

  • Номер – число, в которое записывается порядковый номер документа.

  • Тип документа – значение из перечисления:

    • Incoming (входящий)

    • Outgoing (исходящий)

    • Internal (внутренний)

  • Контрагент – ссылка на стандартный справочник контрагентов.

В типовом решении уже существуют карточки для работы с различными видами документов, но в рамках руководства мы создадим простую карточку со своей структурой, которую можно будет модифицировать любым образом (например, добавлять в неё произвольные поля и писать расширения, реализующие любую бизнес-логику).

Для упрощения процесса обновления на новые версии платформы крайне желательно все объекты схемы данных (такие как таблицы, колонки и т.п.), добавляемые и изменяемые в рамках проектного решения, сохранять в отдельной библиотеке.

1.1. Редактор схемы данных

Схема данных настраивается на вкладке Схема на панели навигации в левой части окна TessaAdmin. В дереве под узлом Таблицы расположены группы таблиц, а внутри – собственно таблицы с их колонками.

image2

Для каждого проекта, разрабатываемого на платформе Tessa, рекомендуется придумать простой префикс, которым снабжать все объекты системы для удобства их поиска, а также для отличия от объектов, которые могут появиться в новых версиях платформы или типового решения. Для типового решения это префиксы Kr и Wf. Для объектов, созданных в рамках этого руководства, будем использовать префикс Ab.

Первым делом создадим новую библиотеку, в которую будем добавлять новые таблицы. На узле Библиотеки с помощью контекстного меню добавим новую библиотеку.

image2 1

Добавится новая библиотека со стандартным именем Partition, выберем её и укажем новое имя - Ab Partition:

image2 2

Новая библиотека создана, в нее мы будем добавлять все новые объекты схемы данных.

Создадим справочник типов документов со значениями Incoming, Outgoing и Internal. Справочники можно создавать двумя способами:

  1. Как обычную таблицу, содержащую значения, которые явно заданы в схеме данных (т.н. Enumeration). Потребуются две колонки: идентификатор и отображаемое в UI название.

  2. Как множество карточек, в данном случае карточек Тип документа, с дополнительным полем Название типа (уникальный идентификатор есть у любой карточки).

Для такого простого справочника, как Тип документа, больше подойдёт вариант с Enumeration. Если же каждое значение справочника определяется множеством полей и должно иметь пользовательский интерфейс для редактирования, то для его описания следует использовать карточки. Примером такого справочника является справочник контрагентов Partners.

1.2. Таблица с типом Перечисление

Создание таблицы

Добавим таблицу типа Перечисление со значениями справочника типов документов. Для этого в контекстном меню узла Таблицы выберем пункт Добавить таблицу. Таблица будет создана под узлом (без группы), который потребуется развернуть и выделить узел с таблицей. Укажем для таблицы Название, Группу, а также Библиотеку, как показано на изображении ниже.

image3

Обратите внимание, что в свойстве Используется для типа указано Не указан, т.е. эта таблица не используется в типах карточек. Свойство Тип секции равно Перечисление, т.е. это таблица-перечисление (и это единственный корректный тип таблицы, которая не используется в типах карточек).

Флажок Виртуальная определяет, что эта таблица должна физически отсутствовать в базе данных, но присутствовать в схеме. Виртуальные таблицы не нужны для Перечисление, но могут быть полезны для таблиц, включённых в типы карточек, чтобы связывать контролы со схемой данных, причём данные могут устанавливаться расширениями произвольным образом.

Например, это позволяет вывести в карточке сотрудника список департаментов, в которые входит сотрудник, хотя физически в БД отсутствует таблица Департаменты, в которые входит сотрудник, но присутствует таблица Сотрудники, которые включены в департамент.

Т.е. таблица Департаменты, в которые входит сотрудник присутствует в схеме данных и поэтому может быть связана с соответствующим контролом, но она отмечена как виртуальная, т.к. она должна не располагаться в БД, а заполняться расширениями при загрузке карточки сотрудника (расширение выполняет SQL-запрос, и заполняет строки виртуальной секции по данным из таблиц).

Поле Описание может содержать произвольное многострочное описание таблицы. Аналогичное поле есть и у других элементов схемы данных, т.е. описание можно добавить также для колонок, ограничений, хранимых процедур, функций и миграций.

Добавление колонок
  1. Добавим в таблицу AbDocumentTypes колонку ID. Она будет содержать числовой идентификатор для этого типа. Для этого раскроем узел AbDocumentTypes, выделим узел Колонки и в контекстном меню выберем пункт Добавить физическую колонку. Разворачиваем узел с колонками и выбираем созданную колонку. Обратите внимание, что колонка автоматически создалась в нашей библиотеке - Ab Partition. Для созданной колонки нужно указать Название и Тип. Для колонки ID в качестве типа подойдёт Int16 Not Null, т.е. 16-битное целое число, которое не должно быть равно Null.

    image4
  2. Аналогично добавим строковую колонку Name с типом String(64) Not Null, т.е. в колонке может храниться Unicode-строка максимальной длиной в 64 символа.

    image5

1.3. Первичный ключ

Для упрощения работы со ссылками на эту таблицу, а также для гарантирования уникальности значений в колонке с идентификатором, создадим первичный ключ, выбрав узел Ограничения и в контекстном меню нажав Добавить первичный ключ. Новый ключ будет добавлен и для него автоматически будет указана библиотека - Ab Partition. Отметим для созданного ключа флажок Кластерный, чтобы созданный индекс был кластерным. В секции "Индексируемые колонки" из выпадающего списка выберем колонку ID и далее нажмем на кнопку "+" для добавления колонки:

image6

В узле Индексы был автоматически создан кластерный уникальный индекс. Сохраним схему в базу данных, нажав кнопку Сохранить всё в верхней панели (над деревом) или нажав сочетание клавиш <Ctrl+S>. При этом будет показан диалог сохранения, в котором надо нажать Сохранить ещё раз, дождаться, пока таблица будет добавлена, при этом цвет строки станет зелёным.

image7

2. Строки в таблице типа Перечисление

Закроем диалог сохранения, и перейдём на узел таблицы AbDocumentTypes - Записи, чтобы добавить строки Incoming, Outgoing и Internal. В колонке Name будет имя типа документа, а в колонке ID – числовой код, начиная с единицы. В колонке Библиотека автоматически будет указана наша библиотека - Ab Partition.

Дальше следует выделить ячейку под колонкой ID. Теперь начинаем вводить значения, перемещаясь между ячейками по горизонтали клавишей <Tab> и по вертикали клавишей <Enter>.

image9

Нажимаем <Ctrl+S>, чтобы сохранить значения. Теперь эти значения присутствуют не только в схеме данных, но и в таблице AbDocumentTypes в базе данных.

2.1. Разновидности таблиц

Следующим шагом будет добавление таблицы AbDocuments. Эту таблицу потребуется включить в типы карточек, поэтому рассмотрим значения перечисления в поле Используется для типа, которое и определяет то, каким образом таблица будет использоваться:

  1. Не указан – это обыкновенная таблица, которую можно было добавить и при помощи SQL, если бы добавление через редактор схемы было бы неудобным. Она не может быть включена в карточки, файлы и задания. Но на неё могут быть ссылки из других таких же таблиц, а также из карточек, файлов и заданий (в отличие от таблиц, добавленных вручную через SQL). Ниже мы рассмотрим, насколько удобен механизм ссылок в схеме данных. Также для такой таблицы могут быть заданы строки, которые будут автоматически добавлены при импорте схемы данных, как мы это сделали для AbDocumentTypes. Учитывая всё это, абсолютно все таблицы, используемые в проекте на платформе Tessa, рекомендуется добавлять через схему данных и указывать их как Не указан - Перечисление, если они не должны использоваться в составе карточек, файлов или заданий.

  2. Карточки – это таблица, которую можно включить в состав карточек, чтобы хранить данные этих карточек. Такая таблица имеет особую структуру, которую мы рассмотрим ниже. Одна и та же таблица может использоваться в различных типах карточек, например, таблица DocumentCommonInfo используется во всех карточках документов в типовом решении (в т.ч. в карточках входящих и исходящих документов, договоров и др.). Эту же таблицу рекомендуется использовать для карточек документов, добавленных в проекте, что мы покажем ниже.

  3. Файлы – аналогично для файлов.

  4. Задания – аналогично для заданий. С точки зрения схемы данных и UI для редактирования, файлы и задания устроены таким же образом, как и карточки, за исключением специфичных системных полей, за которыми "следит" платформа.

Для Используется для типа, отличного от Не указан, в поле Тип секции уже нельзя использовать Перечисление. Вместо этого задают один из трёх вариантов:

  1. Строковая – это строковая секция. На каждый экземпляр карточки, файла или задания (в зависимости от Используется для типа), будет приходиться ровно одна строка в этой таблице (конечно, если таблица включена в схему карточки). Например, это таблица Partners в карточке контрагента. Использование этого типа секций автоматически добавляет колонку:

    • ID – уникальный идентификатор карточки. По этому полю создаётся первичный ключ и кластерный индекс. Этот идентификатор автоматически заполняется платформой при создании карточки.

  2. Коллекционная – это коллекционная секция. На каждый экземпляр карточки (и др.) будет приходиться ни одной, одна или несколько строк этой таблицы. Например, это таблица RoleUsers с перечислением пользователей, входящих в департамент. Использование этой секции добавляет колонки:

    • ID – уникальный идентификатор карточки. По нему автоматически создаётся кластерный индекс, но не первичный ключ, т.к. в таблице может быть несколько строк с одним и тем же ID (для одной и той же карточки).

    • RowID – это уникальный идентификатор строки для этой таблицы, который заполняется платформой, когда пользователь добавляет строки. По этому идентификатору автоматически создаётся первичный ключ.

  3. Иерархическая – это иерархическая секция. Она аналогична коллекционной Collections, но содержит связь между строками "родительский-дочерний" в пределах одного экземпляра карточки посредством колонки ParentRowID. Например, это таблица TaskHistory, содержащая древовидную историю заданий в карточке. Существующие контролы не поддерживают иерархические секции (точнее работают с ними так же, как и с коллекционными), поэтому, скорее всего, при использовании иерархической секции также потребуется написать контрол или обрабатывать иерархическую связь в расширениях. Добавляемые в таблицу колонки:

    • ID – уникальный идентификатор карточки, аналогично секции Collections.

    • RowID – уникальный идентификатор строки, аналогично секции Collections.

    • ParentRowID – ссылка на RowID родительской строки или Null, если текущая строка является корневой.

Для экземпляров карточек платформа автоматически добавляет строку в таблице Instances, которая содержит системную информацию по карточке (такую, как дата последнего изменения или ссылка на сотрудника, который создал карточку). Поэтому колонка ID для Используется для типа = Карточки ссылается на таблицу Instances и содержит ограничение по внешнему ключу (foreign key), чтобы никакие идентификаторы, кроме идентификаторов карточек, не были записаны в колонку ID. Аналогично для файлов существует таблица Files, а для заданий – Tasks. Файлы и задания в Tessa всегда существуют в рамках какой-либо карточки, поэтому Files и Tasks являются коллекционными секциями карточек (Cards + Collections), которые автоматически используются платформой для всех карточек, для которых есть файлы и задания.

2.2. Комплексные (ссылочные) колонки

Для таблицы AbDocuments подойдёт Используется для типа = Карточки, т.к. мы включим её в тип карточки, и Тип секции = Строковая, т.к. на каждый экземпляр документа будет приходиться ровно одна строка этой таблицы. Не забудьте для новой таблицы также указать нашу библиотеку - Ab Partition.

Добавим колонки:
  • Физическую колонку Subject, Тип String(Max) Not Null – тема документа, строка Unicode неограниченного размера, должно быть указано для каждого документа (Not Null).

  • Физическую колонку Number, Тип Int64 Not Null – номер документа, 64-битное целое число.

  • Комплексную колонку Type, Тип Reference (Typified) Not Null – ссылка на тип документа.

image10

Комплексная колонка:

  1. Ссылается на строку таблицы, заданной в свойстве Ссылка на таблицу/Referenced table. При этом автоматически создаётся внешний ключ на первичный ключ этой таблицы, если явно не был снят флажок С внешним ключом/With foreign key.

  2. Содержит колонки со значениями внешнего ключа, необходимые для поддержания ссылочной целостности.

  3. Содержит референсные колонки, дублирующие данные из таблицы, на которую ссылается комплексная колонка. Обычно такой колонкой является строка, отображаемая в представлениях (отчётах) и контролах карточки.

  4. Содержит произвольные колонки, которые логически "включаются" в комплексную колонку. Они могут иметь произвольный тип, но должны начинаться с префикса-имени колонки (здесь это Type). Все колонки, включённые в комплексную колонку, добавляются в тип карточки только совместно, поэтому это могут быть колонки, неразрывно связанные со ссылкой и заполняемые в некоторых (или во всех) случаях, когда заполняется ссылка. Например, ссылка на контрагента 1С должна иметь идентификатор карточки Tessa (внешний ключ) и отображаемое имя контрагента (референсная колонка), но она также может иметь идентификатор, загруженный из 1С, если этот контрагент был добавлен из 1С (для добавленных в Tessa контрагентов это поле будет равно Null).

Флажок Ссылка на родителя/Is reference to owner используется для связи между вложенными коллекционными секциями, в такой колонке строка дочерней коллекционной секции ссылается на строку родительской секции. Это позволяет организовать в карточках иерархию с ограниченным уровнем вложенности, причём на каждом уровне могут храниться различные данные. Например, в родительской коллекционной секции могут содержаться этапы согласования с указанием имени этапа и списка ролей, который организуется как дочерняя коллекционная секция, которая содержит ссылку на роль и ссылку на колонку RowID этапа согласования.

Далее:
  1. Укажем таблицу AbDocumentTypes в поле Ссылка на таблицу, т.е. укажем, что колонка Type содержит ссылку на таблицу AbDocumentTypes. Первичным ключом этой таблицы является колонка ID, поэтому в узел колонки "Type" была автоматически добавлена колонка "TypeID". Физически в БД для таблицы будет создана именно колонка TypeID, в то время как колонка Type определена в метаинформации схемы данных для использования в платформе. Например, комплексную колонку использует ссылочный контрол Reference для заполнения значений.

  2. После того, как мы указали тип ссылки, ниже можно флажками выбрать любые поля из целевой таблицы, которые будут входить в ссылку. Поставьте флажок у колонки Name. Обратите внимание, что автоматически добавилась колонка TypeName, дублирующая колонку Name в таблице AbDocumentTypes.

image11
Аналогично добавим последнюю колонку:
  1. Partner, Тип Reference (Typified) Null – ссылка на контрагента. Допустим, что контрагент может быть не задан, поэтому указываем тип данных, как допускающий Null.

  2. Далее аналогично добавляем в неё референсную колонку PartnerName с именем контрагента. Обратите внимание, что все добавленные колонки PartnerID и PartnerName автоматически указываются как допускающие Null.

image12

Сохраним схему <Ctrl+S>. Все созданные нами объекты в схеме данных сохранены в отдельной библиотеке - Ab Partition. Боле подробно об использовании библиотек, экспорте и импорте схемы данных/библиотек можно прочитать в Руководстве Администратора.

Теперь можно приступить к созданию типа карточки на вкладке Cards.

3. Типы карточек

Тип карточки описывает сущность, такую как Документ, Контрагент или Настройки уведомлений. Для типа определяется:

  • способ хранения, а именно то, какие поля для каких таблиц в схеме данных используются в этом типе;

  • пользовательский интерфейс карточки, связанный с этими полями;

  • валидаторы, определяющие необходимые для заполнения поля и другие условия.

Помимо типов карточек, редактор типов позволяет создавать типы файлов и заданий, которые не рассматриваются в этом руководстве. Однако, их структура очень похожа на структуру типов карточек, поэтому не составит труда изучить их самостоятельно или обратиться к соответствующему руководству.

3.1. Свойства карточки

Выберем вкладку Карточки в панели навигации слева, создадим тип карточки, выбрав пункт Добавить тип карточки в контекстном меню узла Карточки. Тип был создан в узле Карточки внутри группы (без группы). Типы карточек группируются таким же образом, как и таблицы в схеме данных. Выберем созданный узел Card и укажем его свойства:

  • Имя – уникальное имя типа карточки. Оно же определяет имя узла в редакторе, который соответствует типу. Имя может быть использовано в расширениях, а также при настройке решения, но обычный пользователь его не будет видеть.

  • Заголовок – заголовок типа карточки, т.е. его отображаемое для пользователей название. Значение может быть неуникальным, оно фигурирует при создании карточки, в заголовке вкладки для открытой карточки и в различных представлениях (например, в представлении со списком шаблонов, доступных пользователю).

  • Группа – название группы типов карточек. Оно требуется не только для группировки в редакторе, но и для группировки плиток при создании карточки, когда пользователю доступно для создания очень много карточек различных типов, и эти типы группируются в соответствии с этим полем. Причём пользователь по умолчанию видит именно название группы, но при разработке можно написать простое расширение, которое для заданного имени группы подставит другую локализованную строку. Именно таким образом локализуются все группы для стандартных типов карточек.

  • Формат дайджеста - определяет строку форматирования для дайджеста карточки, т.е. для названия, используемого в заголовке вкладки, истории действий и т.д. Для использования полей карточки доступна стандартная система плейсхолдеров (см. Руководство администратора). Наведя курсор на поле можно посмотреть всплывающую подсказку с примером. Если поле не заполнено - вычисление дайджеста выполняется стандартным образом (например, это номер карточки, если она включена в типовое решение и в ней присутствует секция с номером)

  • Идентификатор типа - идентификатор типа карточки. С помощью кнопок, расположенных справа от поля можно скопировать идентификатор типа или скопировать идентификатор типа и имя для использования в расширениях

  • Флажок Административный указывает, что тип карточки предназначен только для использования администратором. Например, флажок ставится для карточек ролей, правил доступа и настроек. Система гарантирует, что пользователь, не являющийся администратором, не сможет создать карточку, изменить её или удалить, но сможет открыть в режиме только для чтения. Для нашей карточки документа флажок ставить не требуется, т.к. с ней могут работать не только администраторы.

  • Флажок Скрытый позволяет скрыть тип карточки из списка на создание, т.е. ни один пользователь или администратор не сможет явно создать карточку стандартным образом. Флажок обычно ставится для вспомогательных карточек (карточек-сателлитов), которые создаются расширениями (например, это карточка-сателлит типового процесса согласования с информацией по этапам согласования). В нашем случае установка этого флажка не требуется.

  • Флажок Разрешить задания разрешает использование заданий для карточек этого типа. Включение этой настройки не добавляет автоматически команды для создания заданий в интерфейсе, но оно разблокирует системную вкладку История заданий и позволяет добавить такие команды, как начало процесса согласования или постановка задачи, в настройках типового решения (что будет рассмотрено ниже). Для таких типов карточек, как карточки ролей, карточки настроек и карточки-сателлиты, задания не требуются, поэтому эту настройку отключают. Для нашей карточки документа настройка должна быть включена, т.к. пользователи должны будут иметь возможности по согласованию документа.

  • Флажок Фиксировать действия активирует ведение логов аудита при любом действии с карточкой (например, при создании, открытии, изменении, удалении, импорте, экспорте и др.). Включим эту возможность для создаваемой карточки документа.

  • Флажок Удалять в корзину активирует возможность по удалению карточки в корзину с возможностью восстановления. Если пользователь случайно удалит карточку, то в течение нескольких дней администратор сможет восстановить карточку (по умолчанию 30 дней, срок настраивается). Удалённая карточка физически удаляется из всех таблиц, поэтому она также исчезает из всех отчётов. Включим настройку для карточки документа.

  • Флажок Единственный экземпляр определяет, что только один экземпляр карточки может быть создан. Обычно эта опция активируется для всех карточек настроек. Такую карточку можно открыть, не зная её идентификатора, но зная имя типа карточки или идентификатор типа. Также для таких карточек активируется кэширование на сервере и на клиенте, это значительно ускоряет доступ к данным карточки, что также актуально для карточек настроек. Для нашей карточки документа эта опция должна быть отключена.

  • Флажок Загружать при инициализации - признак того, что карточка, существующая в единственном экземпляре, будет загружена и добавлена в кэш на клиенте в процессе загрузки приложения Tessa Client.

Ниже на изображении показано, как должны быть заполнены все свойства созданного типа.

image13

3.2. Секции карточки

Сохраним тип карточки (кнопка Сохранить всё сохраняет все изменённые типы карточек). Раскроем узел, в него вложены следующие узлы:

  • Секции – здесь указываются секции карточки, т.е. таблицы, которые включены в эту карточку, и поля, т.е. колонки, которые используются в карточке из выбранных таблиц. В качестве колонок можно выбрать либо обычные физические колонки (строки, числа и т.д.), либо комплексные колонки типа Reference для организации ссылок на другие таблицы (и соответственно на другие карточки). Если таблица включена в секции карточки не целиком (т.е. некоторые колонки не включены), то колонки, не включенные в тип, нельзя использовать в типе карточки, а в БД они будут заполнены значениями по умолчанию (либо из default constraint, либо Null).

  • Вкладки – здесь редактируется пользовательский интерфейс карточки, а именно вкладки, блоки и контролы.

  • Валидаторы – здесь указываются валидаторы, которые связываются с полями карточки и добавляют ограничения на эти поля. Например, определяют, что поле AbDocuments.Type (т.е. ссылка на тип документа) всегда должно быть задано.

Выберем узел Секции, раскроем группу таблиц Ab и выберем все колонки внутри таблицы AbDocuments. Список выбранных колонок отображается как в дереве (посередине окна), так и в виде списка в правой области окна.

image14

3.3. Пользовательский интерфейс карточки

Перейдём к узлу Вкладки:
  • В верхней области отображает переключатель между редактируемыми вкладками. По умолчанию создана одна вкладка с названием в виде локализованной строки "Карточка" (настраивается в поле Заголовок).

  • Посредством кнопки + может быть добавлена новая вкладка, для которой отдельно настраивается UI.

  • Система справа от всех вкладок добавляет вкладку История заданий с деревом отправленных и завершённых заданий для карточки, если в свойствах типа был установлен флажок Разрешить задания.

В нашем случае мы используем единственную вкладку по умолчанию Карточка. Внутри вкладок друг под другом размещаются блоки.

Блок – это множество контролов с заголовком. Платформа предоставляет один тип блоков `Column block`, выводящий свои контролы в одну или несколько колонок.

Выберем из выпадающего списка тип блока Колоночный блок, добавим его кнопкой + справа от списка.

image15

Выберем добавленный блок в списке ниже, чтобы установить его свойства.

  • Заголовок – это строка с заголовком блока, которую пользователь увидит вместе с горизонтальным разделителем. Также эта строка определяет название блока в списке редактора. Назовём добавленный блок Main information.

  • Алиас – это уникальное имя блока в пределах вкладки, посредством которого к блоку можно обращаться из расширений. Значение по умолчанию Block1 можно не изменять.

  • Количество колонок – количество колонок с контролами, которые содержит блок. Контролы автоматически выстраиваются сначала по колонкам слева направо, а затем по строкам сверху вниз. Укажем значение 2 для нашего блока, чтобы выводить контролы в 2 колонки.

  • Вертикальный интервал между контролами – вертикальный отступ между строками, в которых размещаются контролы. Оставляем значение по умолчанию 5.

  • Горизонтальный интервал между контролами – горизонтальный отступ между колонками, в которых размещаются контролы. Оставляем значение по умолчанию 5.

  • Не оптимизировать колонки – опция, отключающая оптимизацию при отображении блока в одну колонку, что позволяет динамически увеличить количество колонок в коде расширений. В нашем блоке уже 2 колонки, и нам не требуется изменять количество колонок в коде расширений, поэтому флажок можно не отмечать.

  • Скрывать блок – скрывает блок из UI карточки. Скрытый блок можно будет сделать видимым в коде расширений. Не устанавливаем флажок для нашего блока.

  • Скрывать заголовок – скрывает заголовок блока. Если заголовок скрыт, то горизонтальный разделитель с текстом из свойства Caption не будет отображаться. Показать заголовок можно динамически посредством расширений. Рекомендуется скрывать заголовок блока, если он выглядит как часть соседнего блока, но посредством расширений такой блок со всеми контролами должен быть скрыт, или же если заголовок не должен отображаться по эстетическим соображениям. Не будем устанавливать флажок для создаваемого блока.

Под списком блоков есть область с настройками вкладки:

  • Вертикальный интервал между блоками – вертикальный отступ между блоками. Значение по умолчанию подходит для большинства случаев.

  • Предпросмотр – кнопки, отображающие UI карточки в режиме предпросмотра. Это позволяет увидеть, как выглядит карточка внешне, не запуская TessaClient и даже не сохраняя тип карточки.

    • Показать без расширений – отображает карточку без расширений, т.е. в строгом соответствии с установленными в редакторе типов свойствами.

    • Показать с расширениями – отображает карточку с выполнением расширений на метаинформацию ICardMetadataExtension, которые могут по каким-то критериям динамически изменить структуру карточки (её секции и поля) и пользовательский интерфейс. Например, как мы увидим далее, включив процесс согласования для этого типа карточки будет отображаться дополнительная вкладка "Процесс согласования" с настройками этапов процесса, а в схему карточки будут добавлены виртуальные секции для функционирования UI, причём заполнение и реакцию на изменение этих секций отрабатывают расширения типового решения. При разработке важно учесть, что для предпросмотра не работают расширения ICardUIExtension, которые могут скрывать контролы и выполнять другие динамические изменения UI для нужд решения. Для проверки этих расширений требуется создать или открыть карточку в TessaClient.

image16

3.4. Контролы и их связь с секциями

В области редактирования блока помимо свойств блока указывается список контролов, включённых в этот блок. Контролы добавляются так: выбирается тип контрола из выпадающего списка, и контрол этого типа добавляется в блок через кнопку +. Добавим для редактирования номера документа контрол с типом Целое число (это контрол, редактирующий целые числа).

image17

Аналогично свойствам блока, выбрав контрол в списке можно редактировать свойства контрола. Свойства контролов различных типов похожи, поэтому рассмотрим для типа Целое число все свойства подробнее.

  • Заголовок – заголовок контрола, который отображается пользователю рядом с контролом. Назовём контрол "Number".

  • Алиас – имя контрола, которое может использоваться для обращения к нему из расширений. Задавать его необязательно, в отличие от свойства Alias для блока.

  • Поля карточки – это поля карточки, с которыми связан контрол. Здесь можно указать только колонки, включённые в тип карточки в редакторе Sections, причём колонки должны иметь совместимый тип. В данном случае подойдут любые целочисленные колонки, которые не расположены внутри комплексных колонок.

  • Формат поля – поле, позволяющее задать строку формата для отображения значения в контроле. Например, если поле не редактируемое, то число можно дополнить нулями слева до длины в 6 символов, используя строку формата {0:######}, где в поле {0} передаётся первая выбранная колонка в Card fields. Для описания различных способов форматирования вы можете обратиться к ресурсу MSDN к описанию метода String.Format для .NET Framework. Для нашего контрола с номером это свойство остаётся пустым, в этом случае в поле выводится числовое значение обычным образом.

  • Всплывающая подсказка – строка с всплывающей подсказкой, которая позволяет сообщить пользователю о назначении этого контрола, если он наведёт на него мышь и задержит её на пару секунд. Всплывающая подсказка особенно полезна для описания назначения контрола типа "флажок" (и "всплывает" она быстрее). Оставим это свойство пустым.

  • Поле не заполнено – текстовое сообщение, которое выводится, если поле в контроле было не заполнено, причём поле является обязательным для заполнения. Обычно для указания того, что поле должно быть заполнено, используются валидаторы (см. ниже), но это также можно определить для конкретного контрола посредством настроек Mark as required и Field is required.

  • Минимальное значение – минимальное число, которое может быть введено в контроле. Можно указать 1, чтобы мог быть указан номер, начиная с 1, или оставить значение по умолчанию min, если ограничений быть не должно.

  • Максимальное значение – по аналогии, это максимальное число, которое может быть введено в контроле.

  • Цвет текста - цвет отображаемого текста.

  • Стиль текста - стиль шрифта отображаемого текста.

  • Отметить как обязательное - отметить поле как обязательное для заполнения.

  • Скрывать поле - скрыть поле.

  • Только для чтения – признак того, что контрол всегда доступен только для чтения, и пользователь не сможет ввести в него значение. Также контрол может быть доступен только для чтения, если у пользователя недостаточно прав на редактирование карточки или связанного поля (это определяется расширениями, например, расширениями типового решения для процесса согласования).

  • Скрывать заголовок – скрывает заголовок контрола. Если флажок стоит, то пользователь не увидит текста, заданного в свойстве заголовок.

  • Растягивать по ширине – указывает блоку, что для размещения этого контрола надо выделять целую строку со всеми колонками, а не только одну колонку в этой строке.

Укажем для контрола Заголовок и Поля карточки, выбрав колонку Number через кнопку с троеточием. Свойства контрола должны быть заполнены как на изображении ниже:

image18

Добавим контрол Type для ввода типа документа. Это ссылка на одно из значений в таблице AbDocumentTypes, которое мы определяли ранее: Incoming, Outgoing, Internal. Для ввода значения из справочника в Tessa используют контрол типа ссылка, который позволяет вводить значения, используя такие средства, как: автодополнение при вводе, выбор одного из значений из выпадающего списка или выбор значений из всех доступных справочников по кнопке с троеточием.

Для контрола также укажем заголовок и Поля карточки – комплексную колонку Type, в которой будет содержаться как идентификатор типа документа (колонка TypeID), так и отображаемый текст для выбранного типа (колонка TypeName).

image19

Предпросмотр типа карточки, выбранный одной из кнопок Показать без расширений или Показать с расширениями, покажет следующую проблему:

image20

Несмотря на то, что контрол Type отобразился, и является связанным с полем AbDocuments.Type в карточке, ввести в него значение невозможно, т.к. для контрола Ссылка надо указать источник данных для автодополнения. Таким источником в Tessa выступают представления.

Подходящего представления для нашей таблицы AbDocumentTypes нет, поэтому его надо создать. Сохраните тип карточки (Сохранить всё на верхней панели) и переключите вкладку Представления для редактирования представлений.

4. Представления

Представления в Tessa являются источником табличных данных, которые могут использоваться для автодополнения, для выпадающих списков, для справочников с поиском, группировкой и постраничным отображением, для отчётов с выгрузкой в html или csv и др. Редактирование, отладка, предпросмотр и настройка прав для представлений выполняются на вкладке Представления.

image21

4.1. Свойства представления

Добавим представление, используя кнопку "+" → Представление на верхней панели (справа от кнопки обновления).

Для созданного представления укажем:
  • Алиас - уникальный алиас представления, посредством которого к представлению возможно обращение как из расширений, так и для связи с ссылочными контролами, такими как Reference. Назовём представление AbDocumentTypes.

  • Имя - отображаемое имя представления по умолчанию, используется как имя узла по умолчанию в дереве рабочего места (отображаемого в TessaClient), а также при импорте/экспорте представлений. Непосредственное значение этого свойства не выводится пользователю, но бывает полезно указать поясняющее название для удобства работы с представлением. Например, "Document types".

  • Группа - это группа, используемая для группировки представлений в редакторе. Не выводится пользователю, поэтому может быть любой. Укажем Ab, чтобы группировка была такой же, как в схеме данных и в типах карточек.

Нажмём кнопку Сохранить всё, чтобы сохранить представление.

image22

Представление – источник табличных данных, и обычно оно задаётся шаблонизируемым SQL-запросом к БД. Поэтому представление может возвращать любые данные или справочники (причём не только с карточками, но и с таблицей-перечислением, как в нашем случае), а также отчёты (такие как отчёты КИД по завершённым заданиям, которые объединяют множества таблиц и используют агрегатные функции для определения количества заданий, сгруппированного по каждому сотруднику или департаменту).

Рассмотрим другие свойства представления, которые задаются в многострочных полях ввода:
  • Метаданные – метаинформация с указанием параметров пейджинга (постраничного отображения), названий выводимых колонок, сортировок, параметров фильтрации, сабсетов (группировок) и ссылок (т.е. описаний, на какие сущности ссылается каждая строка таблицы; например, строка может ссылаться как на документ, так и на контрагента, указанного в этом документе). Подробное описание синтаксиса есть в руководстве разработчика и руководстве администратора.

  • Запрос – шаблонизируемый SQL с запросом SELECT. Шаблонизируемость позволяет системе изменять запрос в зависимости от того, какие параметры, группировки и сортировки выбраны. Например, добавлять в выражение ORDER BY имя колонки, для которой выполняется сортировка.

  • Описание – это произвольное текстовое описание представления, т.е. комментарий. Система никак не использует значение этого поля.

4.2. Метаинформация представления и шаблонизируемый запрос

Укажем следующие значения в полях Метаданные и Запрос:

image23
#view(DefaultSortColumn: DocTypeName, DefaultSortDirection: asc, Paging: no)
#column(Alias: DocTypeID, Hidden: true)
#column(Alias: DocTypeName, Caption: Name, Hidden: false, SortBy: t.Name)
#param(Alias: Name, Caption: Name, Hidden: false, Type: nvarchar, Multiple: true)
#reference(ColPrefix: DocType, RefSection: AbDocumentTypes, DisplayValueColumn: DocTypeName, IsCard: false, OpenOnDoubleClick: false)
select
    t.ID as DocTypeID,
    t.Name as DocTypeName
from AbDocumentTypes t with(nolock)
where 1=1
    #param_expr(Name, t.Name)
order by #order_by
Рассмотрим поле Метаданные:
  • #view указывает общие настройки для представления, такие как:

    • DefaultSortColumn – алиас колонки, сортировка по которой выполняется по умолчанию (т.е. при открытии узла с представлением или при выборе из выпадающего списка);

    • DefaultSortDirection – направление сортировки для этой колонки ("asc" – по возрастанию, "desc" – по убыванию);

    • Paging – пейджинг, т.е. режим постраничного отображения, когда представление возвращает тысячи строк, а одновременно на экране должно отображаться, скажем, 20 строк (значение "no" – пейджинг отсутствует).

  • #column описывает колонку, возвращаемую в запросе SELECT.

    • Alias – это алиас колонки, т.е. то имя колонки, которое возвращает SELECT в SQL-запросе.

    • Для колонки DocTypeID мы указываем Hidden: true, чтобы эта колонка не отображалась в таблице, которая выводится пользователю.

    • Caption определяет имя колонки, выводимое пользователю в заголовке. Для колонки DocTypeName с названием типа документа у колонки устанавливается заголовок Name.

    • SortBy – это выражение, которое подставляется в запрос на месте плейсхолдера #order_by при сортировке по этому представлению (причём направление сортировки asc или desc добавляется системой автоматически).

  • #param описывает параметр поиска (фильтрации) представления.

    • Alias – алиас параметра, который используется для шаблонизации запроса, а также для указания, каким образом будет выполняться фильтрация при вводе текста в ссылочный контрол Reference с автодополнением.

    • Caption – название параметра, выводимое пользователю в диалоге выбора параметров фильтрации.

    • Hidden позволяет скрыть параметр для выбора пользователем (если указано значение "true"). Это позволяет по-прежнему использовать параметр для фильтрации из контрола, или для связи представлений master-detail, или для вызова представлений из расширений и др.

    • Multiple: true указывает, что в параметр можно добавить несколько значений, связанных через "или". Если параметр отображается пользователю (т.е. не скрыт через Hidden), то пользователь может задать несколько значений, например, `Name равен Incoming ИЛИ Name равен Outgoing`".

  • #reference позволяет указать системе, что в каждой строке представления содержится ссылка на некоторую сущность системы. В нашей случае, это ссылка на тип документа, что указано в свойстве RefSection. В одной строке может быть несколько ссылок, так, в строке в представлении "ActionHistory" для логов аудита предоставляются ссылки как на карточку, с которой произведено действие (открытие, удаление и т.п.), так и на запись в логе аудита, которую можно открыть по двойному клику.

    • ColPrefix – префикс алиасов колонок, которые входят в ссылку. Система считает, что все колонки, алиасы которых начинаются на эту строку, входят в ссылку, а значит, например, могут записываться в карточку при выборе в контроле Reference. Для нашего представления – это колонка DocTypeID с числовым идентификатором типа документа и колонка DocTypeName с именем типа.

    • RefSection – разделённый пробелами список таблиц, на которые ссылается эта ссылка. Это свойство используется при выборе значений через кнопку с троеточием в ссылочных контролах Reference.

      Например, ссылочная колонка "AbDocuments.Type" в схеме данных в свойстве "Referenced table" ссылается на таблицу AbDocumentTypes, поэтому контрол Reference, связанный с этой колонкой, по кнопке с троеточием откроет диалог с выбором всех представлений, у которых в RefSection присутствует эта таблица AbDocumentTypes. Ниже мы увидим пример использования такой возможности.

    • DisplayValueColumn – алиас колонки, которая считается отображаемым значением для ссылки. Обычно это свойство используется только для определения названий карточек, открываемых по ссылке (название отображается в заголовке вкладки), если для карточки явно не была определена функция Digest, формирующая название по другим полям карточки.

    • IsCard – признак того, что ссылка ссылается на карточку. В рассматриваемом случае это не карточка, а строка таблицы-перечисления, поэтому указываем "false".

    • OpenOnDoubleClick – признак того, что карточка будет открыта при двойном клике по строке представления. Поскольку тип документа – это не карточка, то указываем значение "false".

4.3. Отладка представления

В поле Запрос мы записали SQL-запрос с плейсхолдерами #param_expr для подстановки параметра фильтрации Name и #order_by для сортировки по колонке DocTypeName. Посмотрим, каким будет сгенерирован запрос на вкладке Отладка, переключиться на которую можно в правом верхнем углу окна.

image24

Кнопка на верхней панели (Сформировать текст запроса) генерирует текст для запроса, используя шаблонизацию текста в поле Запрос, Кнопка Выполнить запрос и отобразить данные выполняет запрос и выводит его результаты в таблице ниже Результат выполнения запроса. Справа указываются параметры фильтрации, передаваемые в запрос, причём некоторые из них являются системными:

  • Name – это наш параметр фильтрации по имени типа. Именно этот параметр должен использоваться при вводе данных в контроле Reference.

  • Текущий сотрудник – это системный параметр с идентификатором текущего пользователя. Используя его, можно, например, в представлении "My tasks" (мои задания) выводить только задания текущего пользователя.

  • Locale – идентификатор культуры (языка) текущего пользователя. Число "25" соответствует русскому языку. С помощью этого параметра можно локализовать значения в результате запроса Запрос.

Ниже в группе Сортировка указываются названия колонок, для которых возможна сортировка (для нас это DocTypeName с именем типа документа) и направление сортировки (по возрастанию, по убыванию, или отсутствует для колонки). Т.к. по умолчанию в метаинформации #view была указана колонка DocTypeName с направлением сортировки по возрастанию, то именно эти значения были выбраны.

В сгенерированном SQL-запросе видно, что плейсхолдер #order_by был заменён на "t.Name asc", где "t.Name" - значение свойства SortBy в метаинформации #column для колонки DocTypeName. Таким образом, в SQL сортировка будет выполняться для поля AbDocumentTypes.Name по возрастанию.

Укажем слева в параметре Name значение In для оператора сравнения начинается с кликнем по имени параметра. Ещё раз нажмём на кнопку генерации и выполнения запроса.

image25

Результаты такого же запроса, но с оператором содержит, будут выведены в выпадающем списке контрола с автодополнением, когда пользователь начнёт вводить in для ввода типа документа (ввод выполняется без учёта регистра символов, т.е. для значений параметра in или IN результат будет одинаковым). Будут выведены все строки, которые содержат значение "in":

image26

Параметр #param_expr(Name, t.Name) указывает, что при вводе параметра с алиасом Name (алиас задаётся в метаинформации #param, в нашем случае алиас и отображаемое имя одинаковы) значение этого параметра будет сравниваться с полем t.Name. Для выбранного значения параметра плейсхолдер #param_expr был заменён на AND t.Name like ('%' + @NAME_1+'%'), и, если подставить значение параметра, то это будет AND t.Name like '%in%', т.е. поле AbDocumentTypes.Name должно содержать подстроку in.

4.4. Предпросмотр представления

Теперь переключимся на режим "Просмотр" (справа от "Отладка"). Этот режим позволяет увидеть, каким образом представление будет отображаться для пользователя в дереве рабочего места.

image27

Кнопка фильтра (слева от кнопки обновления) открывает диалог фильтрации, где пользователь может выбрать фильтрацию по отображаемым параметрам (для которых не указано Hidden: true в метаинформации #param).

image28

После выбора значения текущие параметры фильтрации будут отображаться в области над представлением.

image29

Именно так пользователь будет видеть представление, если он решит заполнить поле со ссылкой на тип документа через кнопку троеточия.

4.5. Права доступа на представление

Для представления осталось настроить последнюю важную деталь: список ролей, для которых представление доступно. Если представление недоступно для пользователя, то он не может получить его данные любым из способов (при вводе в контроле поле будет отображаться с красной рамкой, кнопка выпадающего списка работать не будет, а через троеточие или в дереве рабочего места в главном окне узел с представлением не будет отображён). Т.е. недоступное по правам представление для приложения TessaClient идентично отсутствующему представлению.

По умолчанию список ролей пуст, и представление доступно только для администраторов, т.к. администраторам доступны абсолютно все представления в системе. Чтобы представление с типами документов было доступно всем сотрудникам, добавим роль Все сотрудники (в которую входят все сотрудники), начав вводить это поле, а потом нажав два раза <Enter> для его добавления в список.

image30

4.6. Связь представления со ссылочным контролом

Теперь сохраним представление и вернёмся в редактор типа карточки AbDocument.

Свяжем представление с контролом для ввода типа документа, указав его свойства:
  • Алиас представления – алиас представления, из которого будут получены данные при вводе в текстовое поле. У нас это "AbDocumentTypes". Если значение не указано, то в поле нельзя будет ввести данные посимвольно с автодополнением, но можно будет выбрать представление другим способом (из выпадающего списка или по кнопке с троеточием).

  • Алиас параметра – алиас параметра, в который будет передана строка, которую пользователь начал вводить в поле для ввода. Причём строка передаётся в параметр с оператором содержит. У нас это параметр Name. Если пользователь ввёл букву "o", то в параметр с алиасом Name в представлении с алиасом AbDocumentTypes будет передано значение "содержит 'o'". После этого SQL представления выполняется, возвращает две строки "Incoming" и "Outgoing", которые и выводятся в выпадающем списке.

    image31

  • Алиас представления выпадающего списка – алиас представления, используемого для отображения данных по кнопке выпадающего списка со стрелкой "вниз". При этом в представление не передаются параметры (если это не указано явно в настройках маппинга представления, о чём подробнее можно узнать в руководстве администратора). Выпадающий список обычно имеет смысл только для небольших справочников, таких как наш справочник типов документов. Поэтому запишем значение "AbDocumentTypes" в это поле.

  • Выпадающий список – флажок, который включает режим выпадающего списка. С отключённым флажком кнопка со стрелкой вниз отображаться не будет независимо от значения свойства Алиас представления выпадающего списка. Обычно и включают этот флажок, и задают алиас представления.

    image32

  • Разрешить открывать ссылки – флажок, разрешающий открытие карточки по двойному клику по значению в поле (или через контекстное меню на введённом значении). Карточка открывается по идентификатору, указанному во внешнем ключе колонки, с которой связан контрол. Поскольку тип документа – это не карточка, то снимем этот флажок.

Свойства контрола будут выглядеть следующим образом:

image33

Если в форме нажать на кнопку троеточия, то будет выведено такое предупреждение:

image34

Это означает, что или нет ни одного представления, доступного по правам доступа (по списку ролей), или в системе нет подходящих представлений, размещённых в рабочих местах. Можно скрыть кнопку троеточия, поставив флажок Скрывать кнопку выбора, т.к. для такого небольшого по размерам справочника нет смысла организовывать сложный выбор из различных представлений с фильтрацией.

Но мы рассмотрим, как добавить представление в рабочее место, чтобы оно отображалось при нажатии кнопки с троеточием.

5. Рабочие места

Рабочее место – это дерево с папками и узлами, в которых отображаются представления, как одиночные, так и связанные как master-detail (например, см. отчёты в папке "Отчеты", рабочее место "Пользователь"). Рабочее место группирует доступные пользователю представления и отображается в виде вкладки при открытии окна TessaClient.

Например, вот окно TessaClient для пользователя, которому доступны рабочие места "Администратор" (с административными представлениями) и "Пользователь" (с представлениями и отчётами для пользователей), причём выбрана вкладка "Пользователь".

image35

5.1. Редактор рабочих мест

Узлы в отображаемом слева дереве настраиваются на вкладке Рабочие места в TessaAdmin.

image36

В этом редакторе слева добавляются узлы с представлениями или поисковыми запросами, а также папки (группирующие другие узлы). В области справа указываются роли, пользователям в которых доступно рабочее место, по аналогии с правами на представление. Администраторам по умолчанию доступны все рабочие места, а права для обычных пользователей настраиваются индивидуально для каждого рабочего места.

Рабочие места "Администратор" и "Пользователь", доступные в типовом решении, можно изменять, но при переходе на новую версию платформы вам необходимо будет объединять вашу версию рабочего места и версию в обновлении платформы. Кроме того, часто требуется раздельная настройка прав для рабочего места "Пользователь", доступного всем пользователям, и для рабочих мест, созданных в рамках проекта. Так, может быть отдельное рабочее место для регистратора документов и отдельное для архивариуса.

Создадим рабочее место "Documents" с представлением AbDocumentTypes. Для этого добавим рабочее место через кнопку "+" → Рабочее место в панели инструментов сверху (кнопка справа от кнопки обновления). Имя представления можно задать в области "Свойства" в правой части окна.

5.2. Свойства рабочего места и права доступа

image37

Укажем роль "Все сотрудники", пользователям в которой будет доступно рабочее место (помимо администраторов). Сохраним созданное рабочее место кнопкой "Сохранить всё".

image38

5.3. Узел с представлением

Теперь в рабочее место надо добавить узел с представлением AbDocumentTypes для выбора типов документов. Это можно сделать, выбрав узел с рабочим местом, а затем нажав кнопку Создать → Представление.

image39

В правой части окна расположена область со свойствами. Сначала в свойстве "Представление" выберем представление "AbDocumentTypes" из выпадающего списка.

image40

Большинство необходимых полей заполнилось автоматически:

  • Id – это уникальный идентификатор, который создаёт и использует система. Изменить это значение нельзя, но можно скопировать и использовать в расширениях.

  • Заголовок – имя узла, которое будет отображаться пользователю. Оно заполнилось из свойства Заголовок представления, которое уже имеет подходящее значение "Document types".

  • Значок – иконка слева от узла, когда он отображается в дереве. Иконка задаётся кодом, который можно узнать в файле Manuals\Tessa Icons.png в папке со сборкой. По умолчанию была указана стандартная для представлений иконка. Для узла-папки указывается другая иконка, или можно задать любую иконку из списка доступных.

  • Развернутый значок – иконка, отображаемая, когда узел развёрнут. Если узел является папкой, то иконка используется, когда папка развёрнута и узлы внутри видимы. Для узлов представлений разворачиванием узла является раскрытие подузлов с сабсетами (группировками), например, это узлы "По состоянию", "По типу задания" и др. для узла "Мои задания" в рабочем месте "Пользователь". Также узел представления может использоваться, как и папка, для размещения других представлений, которые никак не связаны с этим представлением.

  • Представление – это алиас представления, которое отображается при выборе узла. Именно здесь нужно в первую очередь выбрать представление.

  • Режим отображения – режим отображения узла. Выберем здесь "Режим отбора", чтобы узел отображался, только когда производится выбор через троеточие. В рабочем месте, которое отображается в главном окне TessaClient, узел будет скрыт. Это позволяет настраивать различные состояния рабочего места для выбора из справочников и для обычной работы (просмотра отчётов, поиска карточек и т.п.).
    "Режим просмотра" работает обратным образом, т.е. скрывает узел при выборе через троеточие, но показывает в главном окне TessaClient.
    "Режим всегда" показывает узел в обоих случаях.

  • Режим выделения -

    • Строка - в представлении при клике на строку выделяется вся строка.

    • Ячейка - в представлении при клике на строку выделяется только одна, выбранная в строке, ячейка.

  • Отображать узел – задаёт, отображается ли узел "Развёрнутым" по умолчанию или "Свёрнутым". Если у узла нет дочерних узлов, то свойство игнорируется.

  • Отображение счетчика строк – позволяет не отображать счётчик строк в представлении. Для представления без пейджинга будет отображено то количество строк, которое вернул запрос, а для представления с пейджингом – в соответствии со специальным сабсетом RowCountSubset, который вычисляет общее количество доступных строк (с учётом параметров фильтрации), не возвращая эти строки (что позволяет ему отрабатывать быстро даже для сложных запросов). Значение "Как задано в представлении" обычно отображает количество строк, если обратное явно не указано в метаинформации представления, а значения "Скрыть" и "Отображать" соответственно скрывают или отображают счётчик количества строк. Для представления с типами документов строк будет 3, и никаких дополнительных настроек указывать не требуется.

  • Где отображать узел:

    • Везде - представление будет отображаться как в Tessa Client, так и в тонком клиенте.

    • Tessa Client - представление будет отображаться только в Tessa Client.

    • Тонкий клиент - представление будет отображаться только в тонком клиенте.

  • Область Расширения позволяет добавить специальное программируемое расширение, которое можно создать, воспользовавшись руководством разработчика. Расширение может изменить внешний вид отображаемых данных или логику взаимодействия с ними.

  • Область Параметры позволяет задать значения параметров фильтрации, которые всегда будут переданы в представление, открытое из этого узла (и пользователь не сможет удалить эти значения). Это настройка, аналогичная административным поисковым запросам, и позволяет выводить различные отчёты на основе одних и тех же представлений, которым передаются различные значения параметров.

Следует иметь в виду, что даже если пользователю в соответствии с правами доступа было доступно рабочее место "Documents", но недоступно представление AbDocumentTypes, то вкладка с рабочим местом будет показана, но узел "Document types" будет скрыт. Или другой пример: хотя рабочее место "Пользователь" доступно всем пользователям, но узлы с отчётами "Отчеты" доступны только пользователям, которые могут видеть представления с отчётами, что настраивается администратором в правах типового решения.

5.4. Предпросмотр рабочего места

Сохраним изменения кнопкой "Сохранить всё" и перейдём в область предпросмотра по кнопке Просмотр в правом верхнем углу окна. Эта область показывает, как выглядит рабочее место для пользователя.

image41

Как видим, узел "Document types" доступен и выбран, но справа от дерева не отображается таблица с данными. Это распространённая ошибка при настройке, связанная с тем, что таблица с представлением не размещена в области узла.

5.5. Размещение представлений в узле

Чтобы настроить такую связь, перейдём обратно в область редактирования по кнопке Редактор, затем выберем узел с представлением "Document types" в списке справа:

image42

И перетащим как drag&drop в область размещения слева:

image43

В результате должно получиться следующее:

image44

Область размещения позволяет настроить сложные представления, такие как master-detail, и даже вкладки с несколькими представлениями для одного узла. Пример такой настройки можно посмотреть в рабочем месте "Пользователь" для узла с контрагентами "Справочники/Контрагенты", где сверху отображается таблица с контрагентом, а снизу для выбранного контрагента отображаются документы, ссылающиеся на этого контрагента и сгруппированные во вкладках по типам.

Перейдём в режим "Просмотр" и увидим, что таблица с типами документов теперь действительно отображается для узла.

image45

Теперь при нажатии кнопки с троеточием в контроле Ссылка со ссылкой на тип документа мы увидим следующее окно, в котором можно искать по всем представлениям, которые в метаинформации предоставляют ссылку #reference на секцию RefSection: AbDocumentTypes.

image46

Если по-прежнему отображается сообщение о недоступных представлениях, то кэш представлений не был обновлён для запущенного приложения TessaAdmin. В этом случае достаточно перезапустить TessaAdmin.

6. Представления с карточками

В карточке документа AbDocument уже есть несколько полей, связанных со схемой данных, поэтому карточку можно начать использовать. Но есть одно "но": созданные карточки нигде не будут отображаться, т.к. в системе не определён источник данных для этих карточек – т.е. не создано представление, которое должно быть размещено в рабочем месте.

6.1. Метаинформация представления

Перейдём на вкладку "Представления" и создадим представление "AbDocuments" следующего вида:

image47
#view(DefaultSortColumn: DocNumber, DefaultSortDirection: desc, Paging: always, RowCountSubset: Count)
#column(Alias: DocID, Hidden: true)
#column(Alias: DocNumber, Caption: Number, SortBy: t.Number)
#column(Alias: DocSubject, Caption: Subject)
#column(Alias: TypeID, Hidden: true)
#column(Alias: TypeName, Caption: Type, SortBy: t.TypeName)
#column(Alias: PartnerID, Hidden: true)
#column(Alias: PartnerName, Caption: Partner)
#param(Alias: Number, Caption: Number, Hidden: false, Type: bigint, Multiple: true)
#param(Alias: Subject, Caption: Subject, Hidden: false, Type: nvarchar, Multiple: true)
#param(Alias: Type, Caption: Type, Hidden: false, Type: int, Multiple: true, RefSection: AbDocumentTypes)
{
    #autocomplete(View: AbDocumentTypes, Param: Name, RefPrefix: Type, PopupColumns: 1)
    #dropdown(View: AbDocumentTypes, PopupColumns: 1)
}
#param(Alias: Partner, Caption: Partner, Hidden: false, Type: uniqueidentifier, Multiple: true, RefSection: Partners)
{
    #autocomplete(View: Partners, Param: Name, RefPrefix: Partner, PopupColumns: 1)
}
#param(Alias: PartnerName, Caption: Partner name, Hidden: false, Type: nvarchar, Multiple: true)
#reference(ColPrefix: Doc, RefSection: AbDocuments, DisplayValueColumn: DocNumber, IsCard: true, OpenOnDoubleClick: true)
#reference(ColPrefix: Type, RefSection: AbDocumentTypes, DisplayValueColumn: TypeName, IsCard: false, OpenOnDoubleClick: false)
#reference(ColPrefix: Partner, RefSection: Partners, DisplayValueColumn: PartnerName, IsCard: true, OpenOnDoubleClick: false)
#subset(Alias: Types, Caption: By type, CaptionColumn: TypeName, RefColumn: TypeID, RefParam: Type)
#subset(Alias: Count)

Рассмотрим, что указывается в метаинформации:

  • Через #view представление сортируется по умолчанию по колонке DocNumber (номер документа) по убыванию номеров.

  • Указываем, что представление выполняется с пейджингом, который пользователь не может отключить Paging: always, т.к. документов может быть зарегистрировано несколько тысяч.

  • Для пейджинга указываем название сабсета для подсчёта строк RowCountSubset: Count. Это означает, что в представлении задан #subset с алиасом Count. Такой сабсет выполняет представление как запрос SQL, который был шаблонизирован особым образом и который возвращает единственное значение – общее количество строк в представлении (т.е. сколько всего документов в системе). Это позволяет системе показать в панели пейджинга не только номер текущей страницы, но и общее количество страниц 1 / 1.

    image48
    Если указан сабсет RowCountSubset, то он будет выполнен каждый раз при запросе данных представления системой. Т.е. если пользователь обновит представление или перейдёт на другую страницу, то сначала будет выполнен запрос для получения данных отображаемой страницы, а затем запрос на получение общего количества строк в представлении. Поэтому в целях оптимизации работы представления с миллионами строк сабсет RowCountSubset можно не указывать, тогда общее количество страниц не будет рассчитываться. Таким образом, например, оптимизировано представление "Available documents" со списком всех доступных документов.
  • Для каждой отображаемой или скрываемой колонки добавляем запись #column. Если её не добавить, и колонка будет возвращаться в запросе SELECT, то она отображается с возвращаемым в запросе именем. Например, колонка "Partner" была бы отображена как "PartnerName", а колонка "PartnerID" не была бы скрыта. Также в #column мы задаём сортировки SortBy для тех колонок, для которых сортировки актуальны (т.к. они требуют наличия индекса в БД для быстрого отображения при наличии тысяч строк в таблице AbDocuments).

  • #param позволяет задать как числовые параметры фильтрации Type: bigint, так и строковые Type: nvarchar. В значении Type указывается тип SQL без размерности, т.е. nvarchar, а не nvarchar(128). Соответственно можно использовать параметры для указания даты Type: date или даты и времени Type: datetime, а также других типов SQL.

  • #param позволяет добавлять параметры фильтрации в виде полей с автодополнением по аналогии со ссылочными контролами в карточке. Это сделано для параметра с алиасом Type, который можно как вводить с клавиатуры с автодополнением #autocomplete, так и выбирать через выпадающий список по кнопке со стрелкой вниз #dropdown. Поскольку такой параметр выбирает идентификатор типа документа, то в свойстве Type указывается int.

    • В #autocomplete указывается:

      • Алиас представления с типами документов View: AbDocumentTypes, и ещё параметр этого представления, в который передаётся текст, который начал вводить пользователь и который надо дополнить Param: Name.

      • Далее нужно задать колонку в результате запроса представления RefColumn: TypeID, идентификатор из которой будет передан в параметр с алиасом Type нашего представления AbDocuments.

      • Свойство PopupColumns перечисляет разделённые пробелами индексы колонок из представления AbDocumentTypes, которые должны отображаться в выпадающем списке при автодополнении. Представление возвращает колонки TypeID и TypeName, а вывести надо колонку TypeName с именем типа документа, поэтому указываем колонку с индексом "1" (индекс отсчитывается от нуля).

      • Свойство RefSection определяет имя секции (или имена секций, разделённые пробелами), на которую ссылается вводимое значение. Это свойство требуется для работы кнопки с троеточием (которая работает так же, как и в ссылочном контроле карточки).

    • #dropdown нужен только для небольших справочников, для которых действительно надо вывести все значения через выпадающий список. В свойстве View также указывается алиас представления, которое будет выполнено для формирования выпадающего списка (никаких параметров в него не передаётся), а в свойстве PopupColumns перечисляются индексы колонок представления AbDocumentTypes, и это снова колонка TypeName(индекс "1").

  • #param с алиасом Partner обращается к стандартному справочнику контрагентов из типового решения, данные которого предоставляет представление "Partners" (его можно, конечно же, открыть в редакторе представлений, и изучить его параметры). Здесь не используется #dropdown, т.к. представление может возвращать тысячи контрагентов, которые не получится вывести через выпадающий список.

  • #param с алиасом PartnerName позволяет искать не по конкретному контрагенту, выбранному из справочника (т.е. не по идентификатору), а по имени контрагента. Таким образом, например, можно найти все документы, которые ссылаются на контрагента с именем, содержащим какую-то подстроку.

  • #reference с префиксом Doc (колонки DocID, DocNumber, DocSubject) предоставляет ссылку на документ, которую можно использовать, например, чтобы исходящий документ ссылался на входящий документ (для этого в карточке документа потребуется сделать ссылку на документ, т.е. на ту же секцию AbDocuments).

  • #reference с префиксом Type (TypeID и TypeName) предоставляет ссылку из документа на тип документа, который в нём указан.

    image49
  • #reference с префиксом Partner, ссылается на того контрагента, на которого ссылается документ (поскольку каждая строка представления описывает один документ, то каждая строка может ссылаться на контрагента, если он указан в документе). Когда мы добавим представление AbDocuments в рабочее место, то любой пользователь, у которого есть доступ к представлению AbDocuments и к рабочему месту Documents, сможет выбрать контрагента по кнопке с троеточием не только из стандартного справочника с контрагентами (в рабочем месте "User"), но и из AbDocuments (если это не работает из TessaAdmin, то вы недавно внесли изменения и надо перезапустить TessaAdmin).

    image50

6.2. Сабсеты

Сабсеты определяют режим выборки представления, т.е. SQL-запрос представления выполняется либо для выборки данных, отображаемых в таблице (без сабсета), либо для определения общего количества строк при постраничном выводе (сабсет, указанный в RowCountSubset), либо при группировке, вызванной на узле, чтобы отобразить значения как подузлы:

image51

Сабсет по типу документа "Types" выполняется с двумя значениями "Incoming" и "Outgoing" (т.к. ни одного документа с типом "Internal" не создано). Эти значения добавляются в узел с представлением.

image52

Результаты выполнения для представления в режиме сабсета "Types" выглядят так:

image53

При выборе значения для представления AbDocuments указывается параметр Type, заданный в свойстве RefParam сабсета #subset. В параметр передаётся значение из колонки RefColumn: TypeID из результатов выполнения представления в режиме сабсета Types (а там возвращается две колонки: TypeID с идентификатором и TypeName с названием). Отображаемое пользователю значение (в дереве и при выборе в сообщении "равен 'Outgoing'") определяется по колонке CaptionColumn: TypeName.

6.3. Шаблонизируемый запрос представления

Прежде чем подробнее описать сабсеты #subset, добавим код запроса в поле "Запрос".

select
    #if(Normal) {             (1)
    t.ID as DocID,
    t.Number as DocNumber,
    t.Subject as DocSubject,
    t.TypeID,
    t.TypeName,
    t.PartnerID,
    t.PartnerName
    } {                       (2)
    t2.*
    }
from
(
    select
        #if(Normal) {         (3)
        t.ID,
        row_number() over (order by #order_by) as rn
        }
        #if(Types) {
        distinct t.TypeID, t.TypeName
        }
        #if(Count) {          (4)
        count(*) as cnt
        }
    from AbDocuments t with(nolock)
    where 1 = 1
        #param_expr(Number, t.Number)
        #param_expr(Subject, t.Subject)
        #param_expr(Type, t.TypeID)
        #param_expr(Partner, t.PartnerID)
        #param_expr(PartnerName, t.PartnerName)
) t2
#if(Normal) {
inner join AbDocuments t with(nolock) on t.ID = t2.ID
}
#if(PageOffset) {             (5)
where t2.rn >= #param_expr(PageOffset) and t2.rn < (#param_expr(PageOffset) + #param_expr(PageLimit))
}
#if(Normal) {
order by t2.rn
}
#if(Types) {
order by t2.TypeName
}
1 Мы видим использование оператора #if, который добавляет своё содержимое в фигурных скобках {…​}, если выражение внутри круглых скобок #if(…​) выполняется.
2 Вторые фигурные скобки (самый первый #if в запросе) аналогичны блоку "иначе", т.е. они добавляются, если условие в круглых скобках не выполняется.
3 #if(Normal) выполняется (добавляет содержимое первых фигурных скобок), когда запрос выполняется в обычном режиме (без сабсетов).
4 #if(SubsetAlias) выполняется, если задан сабсет с указанным алиасом. Например, #if(Count) выполняется, когда представление выполняется в режиме сабсета Count.
5 #if(PageOffset) выполняется, если задан специальный параметр PageOffset с количеством строк, которые надо пропустить для определения первой отображаемой строки на текущей выводимой странице. Параметр задаётся системой, когда представление выполняется в режиме без сабсета #if(Normal) и когда постраничное отображение включено: Paging: always или Paging: optional (когда пользователь может сам переключить с постраничного отображения на полное). В общем случае #if(ParamAlias) выполняется, если задан параметр с указанным алиасом (а алиасы параметров и сабсетов не могут совпадать). Например, #if(Number) выполнится, если задано хотя бы одно значение для параметра "Number" с номером документа.

Посмотреть, как выглядит запрос с нужным сабсетом, можно на вкладке "Отладка" в свойстве "Выбранное подмножество".

image54

Например, так выглядит запрос для сабсета Count. Он возвращает общее количество строк в таблице с указанными параметрами фильтрации. Зададим параметр Number (номер документа) как "меньше чем 5".

DECLARE @NUMBER_1 bigint;
SET @NUMBER_1=5;
DECLARE @CURRENTUSERID_1 uniqueidentifier;
SET @CURRENTUSERID_1='3db19fa0-228a-497f-873a-0250bf0a4ccb';
DECLARE @LOCALE_1 int;
SET @LOCALE_1=9;

select
    t2.*
from
(
    select
        count(*) as cnt    from AbDocuments t with(nolock)
    where 1 = 1
         AND ((t.Number < @NUMBER_1))
) t2

В результате выводится количество:

image55

А при выполнении представления без сабсета получаем такой запрос:

DECLARE @NUMBER_1 bigint;
SET @NUMBER_1=5;
DECLARE @PAGEOFFSET_1 int;
SET @PAGEOFFSET_1=1;
DECLARE @PAGELIMIT_1 int;
SET @PAGELIMIT_1=20;
DECLARE @CURRENTUSERID_1 uniqueidentifier;
SET @CURRENTUSERID_1='3db19fa0-228a-497f-873a-0250bf0a4ccb';
DECLARE @LOCALE_1 int;
SET @LOCALE_1=9;

select    t.ID as DocID,
    t.Number as DocNumber,
    t.Subject as DocSubject,
    t.TypeID,
    t.TypeName,
    t.PartnerID,
    t.PartnerName
from
(
    select        t.ID,
        row_number() over (order by t.Number desc ) as rn    from AbDocuments t with(nolock)
    where 1 = 1
         AND ((t.Number < @NUMBER_1))
) t2
inner join AbDocuments t with(nolock) on t.ID = t2.ID
where t2.rn >= @PAGEOFFSET_1 and t2.rn < (@PAGEOFFSET_1 + @PAGELIMIT_1)
order by t2.rn

В запрос система автоматически передаёт параметры пейджинга PageOffset (номер записи, начиная с которой отображается текущая страница), PageLimit (количество строк в одной странице), идентификатор текущего пользователя CurrentUser и код языка локализации для пользователя Locale. Параметр Number передаётся, т.к. мы его указали (как если бы его указал пользователь).

Результат запроса выглядит так:

image56

Система скрывает из этого результата колонки с идентификаторами (у которых задано Hidden: true), и представляет результат пользователю в виде таблицы.

image57

Теперь не забудем указать роль "Все сотрудники", нажав кнопку "Роли" в правом верхнем углу окна, для назначения доступа к представлению, чтобы все пользователи получили к нему доступ.

image58

6.4. Настройка узла рабочего места с сабсетом

Сохраним представление и добавим его в рабочее место, выбрав вкладку "Рабочие места" в правой панели навигации, а затем выбрав узел "Documents" и нажав кнопку "Создать - Представление" в панели инструментов сверху. В области "Свойства" выберем значение свойства Представление как "AbDocuments". Оставим Режим отображения: Всегда, чтобы узел был доступен как в главном окне TessaClient, так и при выборе ссылок через кнопку с троеточием.

image59

После этого не забудем перетащить drag&drop представление Documents из списка справа сверху в область размещения узла слева. Проверить результат можно в режиме предпросмотра, нажав кнопку "Просмотр" в правой верхней части окна.

image60

Для удобства использования давайте укажем настройки для сабсета по типу документа. Для этого нажмём кнопку сабсета в узле дерева и кликнем на сабсет "By type".

image61

Кликнем по добавленному узлу.

image62

В области справа "Свойства" указываются свойства отображения узла с сабсетом в дереве.

image63
  • Узел можно переименовать, указав ему другой Заголовок.

  • Узел можно скрыть Режим отображения: Скрыть от пользователя, т.е. для конкретного узла "Documents" в рабочем месте "Documents" сабсет "By type" будет недоступен. Не будем этого делать для нашего узла.

  • Узел можно добавлять сразу в раскрытом состоянии Отображать узел: Развернутым. Эта настройка полезна для удобства использования.

Посмотрим, как настройка Отображать узел: Развернутым выглядит в действии. Пусть пользователь добавляет сабсет.

image64

И он сразу добавляется в развёрнутом виде:

image65

В то время, как без этой настройки узел сабсета свёрнут. Добавлять сабсет как свёрнутый узел полезно, если представление в режиме этого сабсета может выполняться длительное время, и пользователь может случайно кликнуть не по тому сабсету и попасть на экран загрузки. Если узел сабсета добавляется свёрнутым, то сабсет выполняется при первом раскрытии этого узла (или при обновлении представления кнопкой <F5> или плиткой на левой боковой панели, когда выбран узел сабсета или один из его подузлов).

image66

Итак, мы настроили представление, из которого можно открывать и искать документы с группировкой по типам документов. Представление добавлено в рабочее место, из которого пользователь сможет увидеть его в TessaClient.

image67

7. Завершение настройки карточки

Теперь перейдём на вкладку "Карточки" и закончим разработку типа карточки AbDocument.

7.1. Дополнительные контролы

Добавим контрол с заголовком "Partner" типа Ссылка для ссылки на контрагента из стандартного справочника контрагентов. Связываем его с полем карточки AbDocuments.Partner, указываем Алиас представления: Partners, Алиас параметра: Name (т.е. стандартное представление со списком контрагентов). Остальные свойства будут иметь значения по умолчанию.

image68

И добавим строковый контрол Subject типа Строка для ввода темы документа. Связываем его с колонкой AbDocuments: Subject. В этот контрол может быть введён большой объём текста (тип колонки String(Max), т.е. может быть больше 4000 символов), поэтому он должен растягиваться на ширину всех колонок (флажок Растянуть по ширине). Также укажем минимальное и максимальное количество строк: Минимум строк: 3 и Максимум строк: 6.

image69

Следующим шагом будет указание обязательности для заполнения полей. Так, строка является заполненной, когда она содержит хотя бы один символ, отличный от пробелов, а любое другое значение заполнено, когда оно не пустое (например, когда ссылка задана). Как упоминалось выше, обязательные поля можно указать для конкретных контролов, поставив флажок Отметить как обязательное. Но лучшим способом являются валидаторы.

7.2. Валидаторы для обязательных полей

Валидатор определяет, что конкретное поле карточки является обязательным (или подходит под другое условие, определяемое типом валидатора, например, числовое поле является положительным). Преимущество задания ограничений через валидаторы в том, что все контролы, связанные с этим полем через свойство Card fields, будут оповещать пользователя о необходимости заполнения этого поля. Кроме того, даже при отсутствии таких контролов проверка условий по полям с валидаторами всё равно будет выполняться при сохранении карточки. Таким образом, значение поля может вычисляться в расширениях и отображаться пользователю в другой форме, а проверка при сохранении будет выполняться именно для этого поля.

Создание валидатора выполняется через контекстное меню на узле "Валидаторы", который вложен в узел с типом карточки.

image70

В этой версии платформы существует три типа валидаторов:

  • Непустая секция определяет, что коллекционная или древовидная секция содержит хотя бы одно значение.

  • Непустое поле определяет, что выбранное поле (физическая или комплексная колонка) содержит непустое значение.

  • Уникальное поле определяет, что выбранное поле (физическая или комплексная колонка) содержит уникальное значение.

Добавим валидатор на поле, раскроем узел "Валидаторы" и выберем добавленный валидатор. Укажем для него колонку AbDocuments.Subject в свойстве Проверяемое поле, т.е. этот валидатор будет проверять, что в теме документа присутствуют отличные от пробелов символы. В свойстве Сообщение об ошибке зададим сообщение, которое увидит пользователь, когда попытается сохранить карточку с незаполненным полем. Если сообщение не задать, то пользователь увидит системное сообщение, которое полезно для отладки, но для рядового пользователя будет непонятным.

image71

Аналогично добавим валидаторы на поля Number и Type, т.е. номер и тип документа являются обязательными для заполнения. Поле Partner с контрагентом может быть не заполнено, т.к., например, для внутренних документов не указывается контрагент.

image72
image73

Если мы сейчас откроем окно предпросмотра, то увидим, что рядом с заголовками полей, с которыми связаны валидаторы, отображается звёздочка. Она индицирует для пользователя тот факт, что поле нельзя оставлять пустым.

image74

7.3. Создание экземпляра карточки

Теперь, наконец, настройка типа карточки завершена, и в системе есть всё необходимое, чтобы создать экземпляр карточки.

Откроем правую боковую панель в TessaClient, выберем плитку "Create card". Как видим, у администратора есть множество карточек для создания.

image75

Если посмотреть на содержимое плитки "Создать карточку" для пользователя, не являющегося администратором, то ему будут доступны только те карточки, которые он должен создавать в соответствии с правами доступа, а это наша карточка "Document", а также, возможно, другие типы карточек.

image76

Нажав на плитку "Document", карточка создаётся. При этом она создана только в памяти клиентского приложения, т.е. в БД нет записей, соответствующих карточке. Внешний вид карточки похож на тот, что мы видели в окне предпросмотра в редакторе типов карточек.

image77

Если карточку сразу же сохранить (плитка "Сохранить новую" на левой боковой панели или <Ctrl+S>), то пользователю будет показано окно с ошибкой, где перечисляются сообщения всех сработавших валидаторов (текст сообщений задавался в свойстве валидатора Error message).

image78

Закрыв окно, пользователь увидит карточку, в которой незаполненные поля были подсвечены красным. Так валидаторы "сообщили" контролам, что поля не заполнены, и те показали рамки, которые исчезают после заполнения.

image79

Заполним поля и сохраним карточку.

image80

Заголовок вкладки с карточкой изменился с "Новая карточка" на "Карточка", что показывает, что карточка сохранена. При сохранении карточка записывает свои данные в БД, а затем загружается из БД. При этом в БД была создана строка (запрос INSERT) в таблице Instances (системная таблица, описывающая системные свойства карточки), а также добавлена строка в строковую секцию AbDocuments. Т.к. это единственная секция карточки, то других строк добавлено не было, но сложные карточки могут содержать десятки строковых и коллекционных секций. При последующих сохранениях карточки изменённые строки будут обновляться запросами UPDATE.

Если в заголовке вкладки должно быть выведено другое название карточки, вычисляемое по полям этой карточки (например, из поля Number, дополненное нулями слева), то для этого необходимо написать расширение на функцию Digest. Подробнее об этом можно узнать в руководстве разработчика.

7.4. Открытие карточки из представления

Закроем карточку и обновим представление "Documents" (плитка "Обновить" на левой боковой панели или <F5>). Представление показывает одну строку с только что созданной карточкой.

image81

Её можно открыть двойным кликом по строке представления.

image82

Обратите внимание, что заголовок вкладки теперь "1", т.е. значение поля "Number", хотя мы не писали расширение Digest. Это штатная возможность платформы, когда представление или контрол со ссылкой предоставляют текст с названием карточки.

Название карточки из ссылки

Взглянем ещё раз на #reference, ссылающийся на документ в метаинформации представления AbDocuments.

#reference(ColPrefix: Doc, RefSection: AbDocuments, DisplayValueColumn: DocNumber, IsCard: true, OpenOnDoubleClick: true)

Была открыта карточка именно по этой ссылке #reference, т.к. в ней указаны свойства OpenOnDoubleClick: true и IsCard: true. Идентификатор карточки был получен из колонки-идентификатора DocID, т.к. в свойстве ColPrefix: Doc определяется, что в #reference входят все колонки, алиас которых начинается с Doc, и DocID является первой подходящей колонкой с типом данных uniqueidentifier (Guid в схеме данных).

А заголовок вкладки с карточкой предоставила колонка DisplayValueColumn: DocNumber для строки, по которой дважды кликнули.

7.5. История действий

Теперь откроем историю действий с карточкой, выбрав плитку "История действий" в левой боковой панели.

image83

История действий – это и есть логи аудита, которые фиксируют каждое действие с карточкой. Они записываются системой для тех типов карточек, у которых в настройках включен флажок Фиксировать действия. Сейчас для карточки доступно 3 записи (сверху выполненные позже): создание карточки, затем открытие (которое автоматически произошло после сохранения) и потом ещё одно открытие (которое выполнялось из представления по двойному клику).

image84

У карточки имя "< имя неизвестно >", т.к. для неё не была определена функция Digest в расширениях, и это ещё одна причина её определить. Любую запись в истории можно открыть, и прочитать подробную информацию обо всём, происходящем с карточкой. Запись по созданию карточки выглядит следующим образом:

image85

Здесь для строковой секции AbDocuments записаны значения полей, которые заполнил пользователь.

7.6. Структура карточки

Структура карточки показывает в JSON-подобном формате сериализованные данные карточки, которые были отправлены на сервер. Такие данные называются структурой карточки.

image86

По этим данным также видно, какая информация указана в секции AbDocuments. Просмотреть её до того, как карточка будет сохранена, можно, выбрав плитку "Card structure" на левой боковой панели в карточке документа.

image87

Если в поле Partner указать некоторого контрагента "Syntellect" (если его нет, то можно создать карточку контрагента с таким именем), и, не сохраняя карточку, открыть её структуру, то она будет выглядеть следующим образом.

image88

Здесь отображается не полная структура, а та, что будет направлена на сервер при сохранении, она содержит только системные поля и изменённые поля в секциях. В секции AbDocuments ссылочный контрол заполнил два поля: ParnterID с идентификатором контрагента и PartnerName с его именем. В разделе выше, в котором описывалась работа представлений, было рассказано, как поля из представления Partners заполняются в полях карточки.

Отображение колонок представления на поля карточки

  1. У полей комплексной колонки Partner "отбрасывается" префикс, т.е. вместо PartnerID получаем суффикс ID, а вместо PartnerNameName.

  2. В представлении Partners выполняется поиск подходящей ссылки #reference, из которой также удаляется префикс, заданный в ColPrefix. Это оставляет те же суффиксы ID и Name.

  3. Для колонок с одинаковыми суффиксами переносятся данные, т.е. из ID запроса ("PartnerID") в ID колонки (тоже "PartnerID", т.к. префиксы совпали).

Чтобы посмотреть все поля в структуре карточки (которые визуализируются контролами), нужно снять флажок Карточка перед сохранением.

7.7. Удаление карточки и её восстановление

Теперь посмотрим, как происходит удаление карточки. Поскольку в типе карточки был поставлен флажок Удалять в корзину, то карточка удаляется "в корзину", из которой она может быть восстановлена администратором. Однако, при удалении карточки все строки из её секций фактически удаляются, а в "корзине" размещается сериализованная структура карточки. Поэтому из нашей таблицы AbDocuments (и из одноимённого представления) строка с карточкой будет удалена.

Удалим карточку:

image89

История действий по карточке с отметкой удаления будет доступна не из карточки (которая удалена), а из представления "История действий" в рабочем месте "Администратор".

image90

Собственно, удалённая карточка есть в представлении "Удаленные карточки".

image91

Открыв удалённую карточку по двойному клику, можно будет выполнить следующие действия: "Просмотр", "Восстановить" или окончательно "Удалить" (кнопки на левой боковой панели). Для удалённой карточки используется имя "<имя неизвестно>", т.к. для неё не определена функция Digest.

Система автоматически удаляет карточки спустя несколько дней после их удаления (по умолчанию через 30 дней), это настраивается в файле Plugins\Tessa\configuration\RemoveDeletedCards.xml, который расположен внутри основной папки сервиса Chronos.

image92

8. Типовое решение

Теперь, когда карточка настроена, можно адаптировать её для задействования в типовом решении. Это добавляет такие преимущества, как регулирование прав доступа на карточку, расширенные средства нумерации, использование процесса согласования и резолюций, регистрация карточки и сквозной поиск по доступным документам, стандартные отчёты и полноценное использование типов документов с различными настройками нумераций, прав доступа и др. В отличие от упрощённого указания типа документа как поля в карточке, хотя никто не мешает совместить оба подхода.

Подробную информацию по настройке и расширению типового решения можно получить в руководстве по работе с типовым решением и в руководстве разработчика. Здесь же приводится краткое руководство по добавлению некоторых возможностей типового решения в созданный нами тип карточки AbDocument.

8.1. Секция DocumentCommonInfo

В типовом решении есть специальная секция DocumentCommonInfo, которая содержит часто используемые поля для документов, которая также используется для организации нумерации, типов документов и сквозных представлений, таких как "Мои документы". Рекомендуется включать такую секцию во все типы карточек, которые являются каким-либо видом документа, а также в которых есть некоторые из определённых в секции полей.

В секции AbDocuments мы определяли колонку Subject для темы документа, а также комплексную колонку Partner для ссылки на контрагента. Мы использовали колонку Number для хранения номера, хотя поле для ручного ввода номера неудобно, т.к. чаще всего порядковый номер документа должен быть выдан системой в соответствии с годом, департаментом и прочими полями карточки. Если же номер вводится вручную, то это внешний номер документа, т.е. номер документа "на бумаге", для которого в DocumentCommonInfo предусмотрена отдельная колонка OutgoingNumber. Кроме того, тип AbDocument является видом документа, который будет использоваться в системе, а значит, можно добавить строковую секцию DocumentCommonInfo в тип карточки.

  1. Откроем схему данных (вкладка "Схема" в TessaAdmin), найдём таблицу с документами Ab/AbDocuments и удалим колонки Subject, Number, Partner, оставив только Type. Удалить колонку можно, выбрав пункт "Удалить" в контекстном меню на колонке. После чего сохраним схему кнопкой "Save".

    image93
  2. Теперь перейдём на вкладку "Карточки", выберем тип документа Ab/AbDocument и нажмём кнопку "Проверить всё". Система выдаст окно с указанием ошибок, которое достаточно закрыть.

    image94
  3. Выберем узел "Секции", развернём узел группы таблиц "Common", в нём развернём таблицу DocumentCommonInfo и отметим флажками колонки:

    • CardType – ссылка на тип карточки, это обязательная колонка для функционирования сквозных отчётов по таблице DocumentCommonInfo, например, отчёта "Available documents" (список документов, доступных текущему пользователю). Если колонка добавлена в тип карточки, то она автоматически заполняется системными расширениями, поэтому её достаточно отметить флажком.

    • Partner – ссылка на контрагента.

    • Subject – строковое поле с темой документа.

    • Number, FullNumber, Sequence, SecondaryNumber, SecondaryFullNumber, SecondarySequence – всё это поля, необходимые для функционирования системы нумерации в типовом решении. Следует либо отметить их все (что мы и сделаем), либо не отмечать ни одно из них, если автоматическая нумерация документов не требуется.

      image96

      А также снимем флажки с колонок Partner, Subject и Number в узле Ab/AbDocuments

  4. Перейдём в узел "Вкладки". Для контрола "Partner" укажем поле DocumentCommonInfo.Partner.

    image97
  5. Для контрола "Subject" укажем поле DocumentCommonInfo.Subject.

    image98
  6. Контрол "Number" удалим, вместо него добавим контрол типа "Numerator", который позволяет работать с автоматически генерируемыми номерами. В поле Card section укажем на секцию DocumentCommonInfo, после чего другие поля заполнятся автоматически. Разместим контрол над другими контролами в блоке с помощью кнопки со стрелкой "вверх" справа от списка контролов.

    image99
  7. Далее требуется изменить валидатор для поля Subject - указать его из секции DocumentCommonInfo. За полем номера теперь следит контрол типа "Нумератор", другая валидация для него не требуется, поэтому данный валидатор удалим.

    image100

Теперь можно убедиться в предпросмотре, что тип карточки успешно отображается и выглядит, как и раньше, и сохранить тип кнопкой "Сохранить всё".

image101

8.2. Адаптация представления под секцию DocumentCommonInfo

Однако, открыв представление "Documents", мы увидим, что SQL представления некорректен и ссылается на колонки в таблице AbDocuments, которые теперь отсутствуют.

image102

Поэтому откроем представление Ab/AbDocuments во вкладке "Представления" в TessaAdmin. Изменим SQL запрос в поле "Запрос" так, чтобы он использовал данные из таблицы DocumentCommonInfo. Следует иметь в виду, что отображаемый номер хранится не в поле Number, а в поле FullNumber(Number – числовой номер, FullNumber – его текстовое представление, которое может быть дополнено буквами, нулями слева и др.). Фильтрацию же и сортировку лучше всего выполнять для числового поля Number, для которого уже есть индекс в таблице DocumentCommonInfo. При этом имена колонок, возвращаемые запросом, нужно оставить теми же, чтобы не изменять "интерфейс" представления для всех, кто его уже может использовать.

select
    #if(Normal) {
    t.ID as DocID,
    d.FullNumber as DocNumber,
    d.Subject as DocSubject,
    t.TypeID,
    t.TypeName,
    d.PartnerID,
    d.PartnerName
    } {
    t2.*
    }
from
(
    select
        #if(Normal) {
        t.ID,
        row_number() over (order by #order_by) as rn
        }
        #if(Types) {
        distinct t.TypeID, t.TypeName
        }
        #if(Count) {
        count(*) as cnt
        }
    from AbDocuments t with(nolock)
    inner join DocumentCommonInfo d with(nolock) on d.ID = t.ID
    where d.CardTypeID = '{67ad1ddb-978b-4de5-9c5d-ee398053bb07}'
        #param_expr(Number, d.Number)
        #param_expr(Subject, d.Subject)
        #param_expr(Type, t.TypeID)
        #param_expr(Partner, d.PartnerID)
        #param_expr(PartnerName, d.PartnerName)
) t2
#if(Normal) {
inner join AbDocuments t with(nolock) on t.ID = t2.ID
inner join DocumentCommonInfo d with(nolock) on d.ID = t2.ID
}
#if(PageOffset) {
where t2.rn >= #param_expr(PageOffset) and t2.rn < (#param_expr(PageOffset) + #param_expr(PageLimit))
}
#if(Normal) {
order by t2.rn
}
#if(Types) {
order by t2.TypeName
}

Для оптимизации запроса при наличии большого количества разнотипных документов в системе рекомендуется добавить фильтрацию по DocumentCommonInfo.CardTypeID, указав идентификатор типа карточки. Идентификатор можно получить в поле TypeID структуры карточки, которую можно отобразить, например, из окна предпросмотра типа карточки.

image103

Именно такой идентификатор должен быть вставлен в тело запроса в фильтрацию по d.CardTypeID.

Не забудем также изменить сортировку в метаинформации для колонки DocNumber, т.к. она теперь выполняется для таблицы d (алиас для DocumentCommonInfo в запросе).

#column(Alias: DocNumber, Caption: Number, SortBy: d.Number)

Теперь представление корректно выполняется, в чём можно убедиться в режимах "Отладка" и "Просмотр".

8.3. Настройки типового решения

Следующим шагом является добавление типа карточки в настройки типового решения. Это позволит активировать систему нумерации, а также использование резолюций и прочих возможностей типового решения.

Откроем карточку настроек типового решения из TessaClient, выбрав плитку "Типовое решение" внутри плитки "Настройки" на правой боковой панели.

image104

Откроется карточка настроек типового решения.

image105

В ней кнопкой "Добавить" надо добавить в таблицу строку с указанием типа карточки и его настроек. Это будет наш тип карточки AbDocument.

image106
  1. Флажок Использовать типы документов позволяет использовать типы документов для дифференцирования нумерации и прав доступа к карточкам одного и того же типа. Оставим флажок снятым. Подробнее о типах документах можно прочитать в руководстве по настройке типового решения.

  2. Включим возможность использования типового процесса согласования, отметив флажок Использовать согласование.

  3. Выберем в поле Автоматическое выделение номера значение "При создании". Это укажет системе о том, что номер надо резервировать в момент создания карточки и выделять в момент первого сохранения (при закрытии вкладки с созданной, но несохранённой карточкой номер будет дерезервирован).

  4. Поле Выделять из последовательности можно оставить пустым. Это поле указывает имя последовательности, из которой выделяются номера. Это позволяет номерам не пересекаться для различных типов карточек, типов документов, годов регистрации документов, названий департаментов и др. По умолчанию название последовательности определяется как "CardTypeSequence", где "CardType" - это имя типа карточки. В нашем случае последовательность будет называться "AbDocumentSequence".

  5. В поле Формат полного номера укажем Ab{00000n}. Это строка форматирования, используемая для формирования строкового представления номера, отображаемого пользователю, по полученному из последовательности числовому представлению. Такая строка определяет, что сначала следуют буквы "Ab", а потом числовой номер, дополненный нулями слева до длины в 6 символов. Например, "Ab000002" для числового номера "2".

  6. Поставим флажок Использовать регистрацию, чтобы включить возможность по регистрации документов. При этом ниже появятся настройки для номера, генерируемого для регистрации. Для документа проектный (исходный) номер всегда хранится в колонках SecondaryNumber, SecondaryFullNumber, SecondarySequence, и этот же номер хранится в Number, FullNumber, Sequence, но только до регистрации. Сразу после регистрации последние 3 колонки заполняются регистрационным номером, а колонки Secondary…​ по-прежнему содержат проектный номер.

  7. Укажем Автоматическое выделение номера: Выделять, чтобы при регистрации система автоматически генерировала регистрационный номер из последовательности по заданным правилам.

  8. В Выделять из последовательности зададим имя последовательности, отличающегося от значения по умолчанию, чтобы не пересекаться с последовательностью для проектных номеров. Например, "AbDocumentsRegSequence".

  9. В Формат полного номера укажем другой формат номера, например, AbReg{00000n}.

  10. Поставим флажок Использовать типовой процесс отправки задач, чтобы разрешить отправку задач для этого типа карточки. Также отметим появившийся флажок Отключить проверку даты для подзадач, чтобы не ограничивать дату создания дочерней задачи относительно даты запланированного завершения родительской задачи.

image107

Закроем строку и сохраним карточку настроек.

8.4. Правила доступа к карточке

Остался последний шаг перед использованием всех возможностей типового решения – это создание правила доступа. Правила доступа – это специальные административные карточки, в которых указывается, для каких типов карточек (или типов документов) в каких состояниях какие разрешения для каких ролей доступны.

Представление "Правила доступа" с правилами доступа расположено в рабочем месте "Администратор" внутри папки "Типовое решение".

image108

По умолчанию в системе создано правило доступа с правами по умолчанию, которые разрешают всем пользователям любые действия с типами карточек из типового решения, находящимися в любых состояниях.

Откроем эту карточку двойным кликом и допишем в поле Типы наш тип карточки "Document", после чего не забудем сохранить правило доступа. Убедитесь, что отмечены все флажки с правами, в поле Состояния карточек указаны все состояния карточек, а в поле Список ролей есть роль "Все сотрудники", разрешающая эти действия всем сотрудникам. Конечно же, такие правила доступа указываются в целях тестирования, в реальной эксплуатации будет добавлено множество карточек со сложными правилами доступа.

image109

Наступил момент проверки наших изменений. Создадим карточку документа "Document" из группы "Ab" (правая панель, плитка "Create card"). Если создание типа карточки недоступно, то перезапустите приложение TessaClient.

image110

Как видим, карточка автоматически получила номер "Ab00001", поля Partner и Subject корректно заполняются, а также появилась новая вкладка "Процесс согласования". Это вкладка настройки процесса согласования, которая была добавлена в UI карточки расширениями на метаинформацию, причём все контролы на этой вкладке связываются с виртуальными полями (которые не хранятся в БД). А, собственно, их сохранение производится в отдельной карточке-сателлите, которая создаётся автоматически расширениями типового решения.

image111

Заполним обязательные поля и сохраним карточку. Обращаем внимание на заголовок вкладки, который был автоматически заполнен номером документа. Это расширение на Digest для типового решения, к которому прикреплён наш тип карточки.

Откроем левую боковую панель. В ней появилось множество плиток, такие как "Начать согласование" для запуска типового процесса согласования, "Зарегистрировать документ" для регистрации документа и "Поставить задачу" для создания задачи. Зарегистрируем документ.

image112

Обратите внимание, что в поле Number теперь записан регистрационный номер (как и в заголовке вкладки).

image113

Нажмём на левой боковой панели "Отменить регистрацию" для отмены регистрации документа. После этого документ опять получит свой проектный номер в качестве основного.

Следует иметь в виду, что теперь практически все действия с карточкой контролируются правилами доступа, поэтому любой пользователь не сможет создать карточку, зарегистрировать её или создать задачу, если это ему не разрешено в правилах доступа.

8.5. Последовательности

Рассмотрим карточки последовательностей, которые были автоматически созданы при первом выделении из них номеров. Представление с последовательностями доступно в рабочем месте "Администратор", узел "Прочее/Последовательности".

image114

Откроем карточку двойным кликом.

image115

Здесь можно увидеть "Интервалы" свободных номеров. Сейчас левая граница "Начало" единственного диапазона смещена до "2", т.к. номер "1" был занят созданной нами карточкой "Ab00001". При текущих настройках для типа AbDocument при удалении этой карточки номер будет освобождён, а диапазон смещён до "1", но это поведение настраивается.

В области "Зарезервированные номера" отображаются зарезервированные номера, которые ещё формально не выделены из последовательности, но уже не могут быть заняты другими пользователями, которые одновременно создают карточки (или занимают номер другим способом). Эти номера администратор может удалить, если из-за неполадок в приложениях номер остался "висеть" и не освободился автоматически.

Чтобы понять, каким образом работает резервирование номеров, создадим ещё раз карточку документа AbDocument, но не будем её сохранять. Обновим карточку последовательности AbDocumentSequence.

image116

Появилась строка с зарезервированным номером, которую можно вручную удалить, тем самым вернув номер в диапазон свободных номеров. Сейчас диапазон смещён до "3", т.е. номер "2" не будет выделен из последовательности.

Закроем вкладку с созданным документом и обновим последовательность. Мы видим, что зарезервированный номер исчез, и номер "2" вернулся в диапазон свободных номеров, чтобы при повторном создании документа его могли опять занять.

image117

Это завершает руководство. Мы создали карточку, настроили для неё представления и источники данных, а также интегрировали её с типовым решением. Дальнейшую информацию для углублённого изучения платформы и типового решения можно получить в других руководствах.