Page Application Worksheet (Журнал Применения). Редизайн с использованием элементов Part в качестве SubPage

В данной статье хотелось бы привести простейший, но от этого не менее полезный пример того, как можно применить Add-in в NAV 2009 SP1. Что это такое Add-in’ы и как они помогают расширить функциональность RTC-клиента, говорилось в 9-м выпуске Журнала.

Немного предыстории…

В NAV 5.0 появился достаточно интересный инструмент, называемый «Application Worksheet» - «Журнал Применения».

В Application Worksheet

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

Казалось бы, все просто и красиво: форма, сабформа со списком примененных операций и сабформа со списком операций, доступных для применения. Основные кнопки: Применение, Удалить Применение.

А теперь взглянем на то, как реализована форма Журнала Применения в версии NAV 2009 SP1:

Журнал применения в Microsoft Dynamics NAV 2009 SP1

Как видим, отличие кардинальное: во-первых, нет сабформы с примененными операциями, во-вторых, нет сабформы с операциями, доступными для применения. Обе эти сабформы были заменены формами, вызываемыми через View -> Applied Entries (F11) и View -> Select Additional Entries to Apply (Ctrl+F11), соответственно.

Расприменение операций происходит по нажатию на кнопку Remove Application. Применение доступной операции произойдет по нажатию кнопки ОК:

Remove Application

К слову, как сабформы в NAV 5.0, так и формы в NAV 2009 SP1 представляют собой один объект: Form 522 «Applied Item Entries Temp» и Form 522 «View Applied Entries», соответственно.

Дизайн «Журнала Применения» в RTC полностью соответствует дизайну формы классического 2009 клиента:

Журнал применения в ролеориентированном клиенте

Как мне представляется, вариант версии 2009 SP1 смотрится хуже, нежели его аналог в предшествующей 5-й версии: пользователь явно должен делать больше действий, открывая/закрывая и без того достаточно медлительные (будем надеяться, пока) Pages.

Попробуем разобраться, в чем же кроется причина подобной реализации «Журнала Применения» в NAV 2009 SP1…

  • Р’Рѕ-первых, начиная СЃ 2009-Р№ версии, появился такой тип объектов как Page.
  • Р’Рѕ-вторых, Microsoft придерживается принципа, что дизайн объектов Form Рё Page должен быть идентичным.
  • Р’-третьих, объект типа Page РЅРµ имеет аналога триггера OnAfterGetCurrRecord() объектов типа Form. Причины этого немного затронуты здесь, хотя Рё РЅРµ слишком убедительно.

Собственно, требования сходства дизайна и техническая реализация Pages на уровне ядра системы, привели к тому, что, казалось бы, удачная в техническом плане реализация «Журнала Применения», претерпела кардинальные изменения в 2009 SP1 версии.

Посмотрим, как реализована основная логика работы «Журнала Применения» в NAV 5.0:

OnAfterGetCurrRecord

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

РЇ был Р±С‹ рад показать Вам АНАЛОГР?ЧНУЮ логику РІ версии 2009 SP1, РЅРѕ ее там просто нет… РћРЅР°, конечно, присутствует, РЅРѕ РЅРµ РЅР° триггере, Р° РІ Action’е. (СЃРј. СЂРёСЃСѓРЅРѕРє выше).

Сделаем так, чтобы дизайн пейджа «Журнал Применения» был идентичен дизайну журнала в версии 5.0!

Для начала создадим Page, который будет аналогом сабформы 522 «Applied Item Entries Temp» в NAV 5.0. Ниже Вы можете увидеть структуру объекта и его свойства:

Page Designer

Далее. Сохраним Page 521 в виде нового объекта 50005 «Application Worksheet 2» (дабы не «портить» стандарт). Отличие структуры нового объекта будет заключаться в том, что мы добавим в него ряд новых элементов. Они выделены светло-зеленым цветом:

Новые элементы в странице

Элемент “Entry No” я пока что оставлю без комментариев, приведя лишь основные его свойства:

Свойства элемента Entry No

Метки Label1 и Label2 содержат текстовые константы Text001 и Text002 соответственно, позаимствованные из версии 5.0. Свойства элементов типа Part (Subpage1 и Subpage2) представлены ниже:

Subpage

Теперь приведу ряд картинок, на которых можно увидеть незначительные изменения кода (комментарии ///MyArticle3), сделанные в Page 50005 «Application Worksheet 2». Все эти изменения напрямую позаимствованы из формы 521 версии NAV 5.0. Поэтому все функции и определение переменных Вы можете увидеть прямо там, останавливаться подробно на них я не буду.

Р?так, триггер OnOpenPage():

onOpenPage

Триггер OnFindRecord:

onFindRecord

Процедура UpdateFilterFields():

UpdateFilterFields

Процедура InitSubpages():

InitSubpages

Процедура ShowMenu:

ShowMenu

Теперь настало время объяснить, для чего же мы сделали integer-переменную EntryNo:

  1. для того чтобы «запоминать» номер текущей операции и, посредством этого, написать аналог OnAfterGetCurrRecord()-триггера формы 521 NAV 5.0;
  2. аналогом OnAfterGetCurrRecord()-триггера будет являться управление триггером EntryNo - OnControlAddIn(Index : Integer;Data : Text[1024]).

Р?так, как уже было замечено, РІ процедуре UpdateFilterFields() РїСЂРѕРёСЃС…РѕРґРёС‚ инициализация/изменение значения переменной EntryNo. Процедура UpdateFilterFields(), РІ СЃРІРѕСЋ очередь, запускается РёР· ПРОЦЕДУРЫ OnAfterGetCurrRecord(). Процедура OnAfterGetCurrRecord() запускается РёР· триггера OnAfterGetRecord().В (так СѓР¶ реализовано РІ стандарте, РјС‹ просто позаимствовали РїРѕРґРѕР±РЅСѓСЋ реализацию).

Р?так, напишем простейший Add-in:

[ControlAddInExport("ApplicationWorksheetControl")] 
public class ApplicationWorksheetControl : StringControlAddInBase 
{ 
В  protected override Control CreateControl() 
  { 
  В  TextBox myControl = new TextBox(); 
  В  myControl.TextChanged += new EventHandler(myControl_TextChanged); 
    return myControl; 
В  } 
   void myControl_TextChanged(object sender, EventArgs e) 
В  { 
  В  this.RaiseControlAddInEvent(1, "OnAfterGetCurrRecord"); 
В  } 
   public override string Value 
В  { 
  В  get 
  В  { 
    В  return base.Value; 
  В  } 
  В  set 
  В  { 
    В  base.Value = value; 
  В  } 
В  } 
}

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

Напишем обработку контрола в NAV:

onControlAddin

Стоит сказать, что подобный РєРѕРґ (без РґРІСѓС… CASE’ов, естественно) РІ базовой ФУНКЦР?Р? OnAfterGetRecord данного Page’а приведет Рє вполне предсказуемой некорректной работе. Поэтому без написания Рё обработки Add-in’a здесь никак РЅРµ обойтись…

Для полноты вынесем возможности применения/расприменения в Actions:

Action Designer

Функции обработки Action’ов были скопированы из Form 521 «Application Worksheet» NAV 5.0 и приведены ниже:

picture-17.png

Теперь посмотрим, что получилось в итоге:

Application Worksheet обновленная версия

Вроде бы все хорошо: есть главная форма с отображением товарных операций, есть операции применения на Subpage1, есть доступные к применению операции на Subpage2. Но, как оказалось, работать Actions данного Page не будут…

Дело в том, что:

  1. конструкция типа CurrPage.SETSELECTIONFILTER(Record) не работает с субпейджами;
  2. невозможно программно позиционироваться и определить текущую активную запись Subpage из главного Page.

К чему я это все. Взглянем на код функции GetRecords, вызываемой в Subpage по нажатию Action «Remove Application»:

GetRecords

РџСЂРё выборе 2 Рё более записей РЅР° Subpage, система РІСЃРµ равно вернет ПЕРВУЮ запись данного Subpage. Таким образом, данный РєРѕРґ работать РЅРµ будет (РїРѕ крайней мере, так, как РјС‹ РѕС‚ него ожидаем). Р?, как Р’С‹ догадались, даже РїСЂРё случае выбора только второй записи, система РІСЃРµ равно вернет первую, РЅР° которой РјС‹ даже РЅРµ позиционируемся.

РќРѕ решение есть. Р? заключается РѕРЅРѕ РІ том, что Actions необходимо выносить РЅРµ РІ Page, Р° РІ Subpage, записи которого РјС‹ хотим обработать! Р’ классическом клиенте, как Р’С‹ знаете, подобная логика работы РЅР° формах невозможна: РІСЃРµ контролы типа Button, Menu Button, Рє примеру, находятся РЅР° главной форме.

РќРѕ есть РѕРґРЅРѕ «Но», которое возникает после переноса Actions РЅР° сабпейдж: после того, как РјС‹ перенесем обработку Actions непосредственно РЅР° сабпейджы, необходимо будет сделать так, чтобы сабпейджы физически были разными объектами системы. Р?наче РјС‹ РЅРµ сможем отличить операции расприменения/применения. Поскольку первая операция должна выполняться РЅР° Subpage1, Р° вторая – РЅР° Subpage2.

Таким образом, сделаем копию пейджа «Applied Item Entries Temp», создав новый пейдж 50007 «Applied Item Entries Temp 2». Ну и не забудем заодно заменить свойство контрола Subpage2 журнала применения: PagePartID – «Applied Item Entries Temp 2».

Р?так, начнем обрабатывать Subpage1… Дабы запоминать номер «родительской операции», Рє которой применена операция сабпейджа, изменим функцию SetMyView следующим образом (Рє слову, скопируем эту функцию РІ новый пейдж 50007 В«Applied Item Entries Temp 2В»):

SetMyView

Теперь скопируем недавно созданный нами Action Remove Application с главного пейджа в Page «Applied Item Entries Temp». Вот его обработчик:

picture-21.png

Скопируем созданный нами Action Apply с главного пейджа в Page «Applied Item Entries Temp 2»:

ApplyRec

А вот, что в итоге получилось:

Журнал применения для ролеориентированного клиента - последняя версия

В Subpage1 «Applied Entries» вынесено расприменение, в Subpage2 «Entries Open for Application» вынесено применение (видно на предыдущем screenshot).

Большой недостаток представленного решения: невозможно скрыть поле «Entry No.» в главном Page. Если установим его свойство Visible = FALSE, то Add-in не будет работать (содержимое самого текстового поля меняться в этом случае не будет – событие не сработает).

Предлагайте Ваши улучшения, пожелания Рё критику Рє представленному функционалу. Р?скренне Р±СѓРґСѓ рад прочесть.

Метки: ,



Комментариев: 4

  1. Айрат пишет:

    Вчера столкнулся с подобной задачей. Большое спасибо, ваша статья очень помогла.
    “Большой недостаток представленного решения: невозможно скрыть поле В«Entry No.В» РІ главном Page. ”
    Предлагаю свои улучшения:
    1) Сделать аналогичную сборку-болванку, которая не изменяет внешний вид контролов. Можно взять полностью код из данной статьи.
    2) Нужно привязать ControlAddIn к любому уже из имеющихся фильтров (например, Item Filter).
    3) Р’ триггере OnAfterGetRecord() напишем бесполезный РєРѕРґ (!!! Р’РќР?РњРђРќР?Р•, выполнение данного пункта обязательно, иначе Navision РЅРµ будет вызывать событие OnAfterGetRecord РїСЂРё выборе пользователем записи) РІСЂРѕРґРµ “Item Filter”:=”Item Filter” (этот бесполезный РєРѕРґ должен описывать присвоение именно той переменной, РЅР° которую повешен наш ControlAddIn).
    4) (Выполнение данного пункта тоже обязательно, хотя тут РІСЃРµ пункты обязательны) Р’ триггере “Item Filter - OnControlAddIn(Index : Integer;Data : Text[1024])” должен обязательно стоять непустой РєРѕРґ, тут же достаточно написать комментарий. Например:

    Item Filter - OnControlAddIn(Index : Integer;Data : Text[1024])
    //some text

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

  2. Айрат пишет:

    Р?Р·РІРёРЅСЏСЋСЃСЊ, деинформировал.
    Р’СЃРµ-таки, надо именно изменять значение переменной РІ триггере OnAfterGetRecord(). Р?наче РЅРµ будет работать. Поэтому, действительно, без В«Entry No.В» здесь РЅРµ обойтись.

  3. Orwell пишет:

    Спасибо за комментарий, Айрат.
    Не было возможности проверить в NAV 2013. Но, предположу, что подобный технический недостаток там так же не обойти…

  4. Айрат пишет:

    Но, насколько мне известно, в NAV 2013 уже появился триггер OnAfterGetCurrRecord() на страницах. Поэтому можно обойтись им.
    Вернусь опять к NAV 2009. Если задача простая, и необоходимо только фильтровать данные связанной таблицы на субформе определенным образом, то можно вообще обойтись свойством субформы SubFormLink.
    РЈ меня же задача стояла так, что данные РЅР° субформе либо фильтруются РІ зависимости РѕС‚ позиции РЅР° главной форме либо РЅРµ фильтруются (фильтруются или нет зависит РѕС‚ галочки РІ области фильтров РѕСЃРЅРѕРІРЅРѕР№ формы). Поэтому, обойтись свойством SubFormLink РЅРµ вышло. РњРЅРµ РЅР° странице нужен был именно аналог триггера OnAfterGetCurrRecord() формы. Рассматривался ещё вариант СЃ видимостью форм/страниц, РЅРѕ страницы нельзя динамически отображать/скрывать. Р?, соответственно, остался только вариант СЃ ControlAddIn.

Оставьте свой отзыв!