01.06.2016

Obsługa zdarzeń w Microsoft Dynamics NAV 2016

Obsługa zdarzeń
Zdarzenia (Events) są jedną z największych zmian z punktu widzenia budowy aplikacji od czasu wypuszczenia nowej wersji systemu Microsoft Dynamics NAV 2013. Pozwalają oddzielić dostosowaną funkcjonalność od logiki biznesowej aplikacji i pozostawić standardową funkcjonalność Dynamics NAV nienaruszoną. Stosowanie tej funkcji ułatwia proces aktualizowania programu Microsoft Dynamics NAV do nowszej wersji.
Zdarzenia przypominają trochę funkcjonalność kanałów RSS w Internecie, gdzie autor tekstu publikuje wiadomość w kanale RSS a czytelnicy danego kanału mogą odebrać komunikat o pojawieniu się nowego tekstu, następnie pobrać tekst i go przeczytać. Publikujący dany tekst w kanale RSS może być tylko jeden, natomiast czytelników opublikowanego tekstu może być wiele.
Zdarzenie w NAV jest swego rodzaju deklaracją nowego wystąpienia danej sytuacji lub zmianą już istniejącej sytuacji w NAV. Poprzez analogię do kanału rss w zdarzeniach NAV także mamy dwie strony, jedną stroną jest Publikujący zdarzenie (Publisher) i drugą stroną są subskrybenci zdarzenia (Subscribers). Publikującym zdarzenie w NAV może być dowolny obiekt tj. tabela (Table), strona (Page) czy jednostka kodu (Codeunit). Natomiast subskrybentem zdarzenia może być tylko funkcja umieszczona w jednostce kodu.
W systemie Dynamics NAV wyróżnia się następujące typy zdarzeń: biznesowe (Business Events), integracyjne (Integration Events), globalne (Global Events) i zdarzenia wyzwalane przez triggery (Trigger Events).
Biznesowe i integracyjne zdarzenia muszą być zadeklarowane i opublikowane poprzez zmianę w kodzie aplikacji. Między biznesowymi i integracyjnymi zdarzeniami jest w chwili obecnej tylko umowna granica, ponieważ funkcjonalnie tworzy się je i obsługuje tak samo. Zgodnie z założeniami Microsoftu zdarzenia zadeklarowane jako biznesowe nie mogą ulec zmianie w czasie, czyli raz zaimplementowane muszą pozostać niezmienione w kolejnej wersji aplikacji. Ten typ zdarzeń został zarezerwowany dla dostawców projektujących rozwiązania wertykalne. Podczas normalnych prac programistycznych należy używać zdarzeń integracyjnych.

Globalne zdarzenia są predefiniowanymi zdarzeniami systemowymi i znajdują się w jednostce kodu 1 ApplicationManagement. Jednostka kodu 1 zawiera wiele triggerów funkcji globalnych m.in. CompanyOpen, CompanyClose etc. Dla każdej z takich funkcji jednostka kodu 1 zawiera jedno lub dwa zdarzenia globalne Przed i Po. Na przykładzie zamknięcia firmy będą dwa zdarzenia globalne OnBeforeCompanyClose i OnAfterCompanyClose.

Zdarzenia wyzwalane przez triggery są zdarzeniami systemowymi i nie można ich zmienić. Znajdują się w tabelach i na stronach.
Zdarzenia w tabeli:

Zdarzenia na stronie:

W przykładzie do pola Opis 2 w wierszu faktury wstawimy Nr dokumentu zewnętrznego z Nagłówka wydania sprzedaży. Przypisanie ma nastąpić podczas tworzenia wierszy faktury na podstawie wierszy wydania.
W tabeli 111 Sales Shipment Line zostanie zadeklarowane zdarzenie integracyjne. Zdarzenie powinno być zainicjowane po wstawieniu wiersza faktury w funkcji InsertInvLineFromShptLine. Funkcja InsertInvLineFromShptLine uruchamiana jest podczas pobierania wierszy wydania do faktury sprzedaży.
Należy utworzyć nową funkcję i ustawić jej właściwość Events na Publisher.

Środowisko deweloperskie domyślnie ustawi typ zdarzenia (EventType) na Business, więc należy właściwość zmienić na Integration. W tym miejscu należy wspomnieć, że funkcja publikująca zdarzenie koniecznie powinna być zadeklarowana jako lokalna (w wersji Microsoft Dynamics NAV 2016 środowisko deweloperskie automatycznie oznacza wszystkie nowe funkcje jako lokalne), żeby inny obiekt przez przypadek nie uruchomił zdarzenia. Zmiana właściwości IncludeSender na Tak (domyślnie Nie) spowoduje przekazanie do funkcji subskrybującej referencji do obiektu publikującego zdarzenie. W ten sposób będzie można odwołać się bezpośrednio do obiektu publikującego jak i wywołać jego funkcje globalne. Ustawienie właściwości GlobalVarAccess na Tak (domyślnie Nie) pozwoli funkcji subskrybującej dane zdarzenie uzyskać dostęp do wszystkich zmiennych globalnych obiektu publikującego zdarzenie. Z tą właściwością należy postępować bardzo ostrożnie, ponieważ w przypadku zmiany zmiennej globalnej (np. z Code na Text lub zmiany nazwy zmiennej globalnej), do której odwołuje się funkcja subskrybująca, nastąpi odłączenie funkcji subskrybującej od zdarzenia. Jednym słowem funkcja subskrybująca przestanie być informowana o wystąpieniu zdarzenia i nie wykona się kod aplikacji umieszczony w funkcji subskrybującej.
W funkcji publikującej zdarzenie nie mamy możliwości umieszczenia żadnego kodu poza komentarzem. Próba skompilowania obiektu z funkcją publikującą zawierającą kod zakończy się niepowodzeniem i komunikatem błędu.

Kolejną rzeczą jest nazewnictwo funkcji. Nazwa naszej funkcji publikującej to OnAfterInsertInvLineFromShptLine. Nazwa funkcji powinna jasno opisywać miejsce jej zakotwiczenia. W naszym przykładzie miejscem zakotwiczenia funkcji publikującej zdarzenie jest wstawienie wiersza faktury, dlatego nazwa funkcji publikującej zawiera wyrażenie InsertInvLineFromShptLine. Przedrostek OnAfter oznacza, że zakotwiczenie będzie miało miejsce po wstawieniu wiersza sprzedaży. Przed ustawieniem zakotwiczenia należy jeszcze w funkcji publikującej zdarzenie umieścić w parametrach zmienne, które chcemy następnie przekazać do funkcji subskrybującej. W obecnym przykładzie przekażemy tabelę SalesLine, która jest przekazywana jako parametr w funkcji InsertInvLineFromShptLine.

Zakotwiczenia dokonuje się poprzez wywołanie w kodzie funkcji publikującej zdarzenie.

Następnym krokiem jest utworzenie funkcji subskrybującej dane zdarzenie. Jak było wspomniane wcześniej funkcje subskrybujące mogą znajdować się tylko w jednostkach kodu. Do tego celu utworzymy nową jednostkę kodu w zakresie 50000 i roboczo nazwiemy ją EventSubscribers.

W nowej jednostce kodu tworzymy funkcję o nazwie AddExtDocNoToSalesInvLineOnGetSalesShipment.

We właściwościach funkcji ustawiamy Events jako Subscriber.

We właściwości EventPublisherObject wybieramy obiekt, który zawiera zdarzenie.

We właściwości EventFunction wybieramy funkcję zawierającą publikowane zdarzenie.

Cała deklaracja funkcji subskrybującej wygląda następująco:

Następnym elementem jest dodanie kodu w funkcji subskrybującej, który przypisze Nr dokumentu zewnętrznego do wiersza faktury.
Kawałek kodu będzie wyglądał następująco:

W funkcji zadeklarowana została tabela Nagłówek wydania sprzedaży jako zmienna lokalna. W pierwszym wierszu kodu następuje pobranie Nagłówka wydania sprzedaży na podstawie Nr dokumentu z Wiersza wydania sprzedaży. W drugim wierszu kodu mamy przypisanie Nr dokumentu zewnętrznego do pola Opis 2 z Wiersza faktury. W trzecim wierszu kodu potwierdzamy modyfikację wiersza faktury.
Pozostało tylko przetestować funkcjonalność.

  1. Utworzyć zamówienie sprzedaży na towary.
  2. Wpisać Nr dokumentu zewnętrznego w nagłówku zamówienia sprzedaży.

  1. Zaksięgować wydanie sprzedaży.
  2. Utworzyć fakturę sprzedaży.
  3. Pobrać wiersze zaksięgowanego wydania do faktury i sprawdzić czy przypisał się Nr dokumentu zewnętrznego w polu Opis 2 w każdym wierszu.