В своем совете Андрей Панько рассказал о том, как реализованы матричные формы в Microsoft Dynamics NAV 2009 и как их можно улучшить.

В Dynamics NAV 2009 пользователи получили новые возможности по управлению интерфейсом. Теперь они могут гибко настраивать свое рабочее окружение, которое включает меню, ролевые центры, а также индивидуальную настройку списков и карточек. К сожалению, за все нужно платить. Элемент управления MatrixBox в такую концепцию не вписывался, в результате от данного элемента пришлось отказаться.

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

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

Посмотрим, как решили данную проблему непосредственно в Microsoft.

Начнем с матричных форм, предназначенных исключительно для анализа. Обратимся к форме 491 Items by Location (Товары По Складам). В данной матричной форме в строках отображаются товары, а в столбцах склады (в зависимости от установленного флажка в заголовке формы это могут быть склады-хранения или транзитные склады). На пересечении товара и склада отображается наличие товара. Если в ячейке щелкнуть кнопку Детализировать (F6), то откроется список товарных операций, отфильтрованный по соответствующему товару и складу.

Так выглядит матричная форма в классическом клиенте Так выглядит матричная форма в классическом клиенте

Помним, что Microsoft рекомендует вначале создавать форму для классического клиента, а затем трансформировать ее в страницу для ролеориентированного клиента. Беда в том, что напрямую преобразовать форму 491 в страницу нельзя – у элемента MatrixBox нет аналога для объекта Страница. Поэтому разработчики пошли на хитрость.

Была создана форма 9230 _Items by Location. В этой форме на закладке Options указываются параметры: отображать склады-хранения или транзитные склады, использовать в качестве заголовка столбца поле Name. А на закладке Matrix Options указываются склады, которые будут выступать в качестве столбцов (выбранные склады отображаются в виде интервала: А..В). Кроме того на форме есть кнопка Show Matrix. Больше на форме ничего нет.

Однако есть другая форма – 9231 Items by Location Matrix, она основана на таблице 27 Item (Товары). В данной форме содержится элемент управления TableBox со столбцами «No.», «Description», а также 32 (именно так, Microsoft рекомендует 32) поля для имитации матричной части. Названия столбцов заполняются при нажатии на кнопку Show Matrix в форме 9230 _Items by Location. Собственно форма 9231 Items by Location Matrix и открывается при нажатии на эту кнопку. Затем для каждой записи таблицы Item выполняется заполнение матричной части.

Вспомогательные объектыВспомогательные объекты

Трансформация

При трансформации форма 491 игнорируется. Форма 9230 становится страницей 491. Форма 9231 становится страницей 9231. Страницы с идентификатором 9230 не существует.

Form 491 Items by Location --X

Form 9230 _Items by Location --> Page 491 Items by Location

Form 9231 Items by Location Matrix --> Page 9231 Items by Location Matrix

Таким образом, формы 9231 и 9230 являются вспомогательными. Тем не менее, обе формы работают и ими можно пользоваться (правда не ясно зачем, если есть форма 491 с MatrixBox).

Общая концепция

Рассмотрим, как это работает. В целом задача разбивается на два этапа. Сначала формируется набор записей, которые будут выступать в качестве столбцов, а затем формируется сама псевдоматричная форма на базе таблицы из 32 столбцов.

Реализация матричной формы в ролеориентированном клиентеРеализация матричной формы в ролеориентированном клиенте

Теперь рассмотрим всю процедуру по шагам.

При открытии страницы 491 Items by Location срабатывает триггер onOpenPage, в нем происходит вызов функции SetColumns с параметром MATRIX_SetWanted::Initial.

SetColumns – основная функция, в которой выполняется формирования набора записей, выступающих в роли столбцов матрицы. В функции SetColumns, в зависимости от значения, установленного в поле « Show Items in Transit» (при первом запуске оно равно FALSE), выполняется фильтрация переменной MatrixRecord (переменная запись, ссылающаяся на таблицу 14 Location).

Далее выполняется очистка массивов: MATRIX_CaptionSet - массив заголовков столбцов и MatrixRecords – массив столбцов-записей (не путать с MatrixRecord).

Для упрощения преобразования матричных форм был разработан специальный программный модуль 9200 Matrix Management. В целях унификации этот программный модуль работает с переменными типа RecordRef. Поэтому в функции SetColumns используется следующий код: MatrixRecordRef.GETTABLE(MatrixRecord); где MatrixRecordRef это переменная типа RecordRef.

На основании значения, установленного в поле «Show Column Name», в переменной CaptionFieldNo указывается идентификатор поля, которое будет использоваться в качестве названия (Код склада или его Название).

Далее происходит вызов функции GenerateMatrixData программного модуля 9200 Matrix Management. В нее передаются следующие параметры:

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

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

Далее переменная MatrixRecord позиционируется на первую запись в наборе, а затем в цикле выполняется заполнение массива MatrixRecords. Количество шагов в цикле равно количеству отобранных записей. Таким образом, будет сформирован массив записей-столбцов.

Дальше пользователь запускает действие Show Matrix, при этом выполняется код в триггере onAction. В триггере в страницу 9231 Items by Location Matrix выполняется передача параметров: массив заголовков записей, массив записей-столбцов и переменная MatrixRecord (отфильтрованная по полю «Use As In-Transit»). Далее страница 9231 Items by Location Matrix запускается на выполнение.

Для каждой записи таблицы 27 Item, а страница 9231 Items by Location Matrix основывается на этой таблице, вызывается триггер onAfterGetRecord. В триггере onAfterGetRecord для каждого столбца вызывается функция MATRIX_OnAfterGetRecord, в которой выполнялся расчет наличия данного товара на соответствующем складе (банальное наложение фильтров и вызов функции CALCSUMS).

Как улучшить

Не ясно, зачем у страницы 491 Items by Location заполнено свойство SourceTable=Item. Связь с таблицей можно убрать.

В Странице 9231 Items by Location Matrix из функции MATRIX_ OnAfterGetRecord следует убрать вызов функции SetVisible, а вместо этого поместить его в триггер onOpenPage. Поясню – согласно данным от разработчиков свойство Visible для полей обрабатывается только в триггерах onInit и onOpenPage, поэтому изменение видимости из триггера onAfterGetRecord попросту не будет иметь никакого эффекта.

Почему при вызове страницы 9231 Items by Location Matrix из страницы 491 передается переменная MatrixRecord? Лучше передавать количество записей в наборе – переменную MATRIX_CurrSetLength (не путать с количеством записей в массиве – ARRAYLEN). В триггере onAfterGetRecord страницы 9231 Items by Location Matrix вместо присутствующих там изысков написать

FOR MATRIX_CurrentColumnOrdinal := 1 TO MATRIX_CurrSetLength DO
  MATRIX_OnAfterGetRecord(MATRIX_CurrentColumnOrdinal); 

Это позволит обойтись без лишних операций извлечения записей из базы данных. Но это еще не все. Попробуйте создать дополнительно 30 складов (с кодами 10-39) и постройте матрицу для второго набора записей (т.к. всего складов станет 36, то четырем из них не хватит места на первой странице). В результате можно будет заметить, что для четырех первых столбцов остатки отображаются правильно, а для остальных 28-ми будут выводиться остатки по пустому складу. Т.е. будет выполнено по 28 вызовов функции CALCSUMS для каждого товара. Чтобы это увидеть, нужно до изменения триггера onAfterGetRecord, закомментировать SetVisible в триггере onOpenPage.

После этого в странице 9231 Items by Location Matrix можно удалить переменную MatrixRecord и MATRIX_NoOfMatrixColumns (вместе с ее инициализацией в триггере onOpenPage) – они не нужны.

 

Андрей Панько >>
Dynamics NAV MVP
apanko@rabota-na-rezultat.ru