Od zera do extension – cz. 5 Pierwsze Extension

Dzisiaj pokaże Ci, już ostatni krok na drodze do Twojego pierwszego Extension. Po małym teoretycznym wstępie odpalimy Visual Studio Code i utworzymy pierwszy mikro dodatek do systemu Dynamics 365 Business Central.

Czym tak właściwie są Extensions?

Extensions (czyli rozszerzenia) to nowe podejście do programowania w Business Central (wcześniej NAV). Już jakiś czas temu Microsoft zaprezentował Extensions 1.0 – niestety nie było to udane podejście do tematu, a cała technologia wykorzystana w wersji 1.0 odeszła do lamusa zanim na dobre się pojawiła. Niemniej na dzień dzisiejszy mamy do czynienia z Extensions 2.0 – czymś zgoła innym. Niestety nie sposób było wprowadzić nowej koncepcji programowania ograniczając się tylko do zmian kosmetycznych. Dlatego dzisiaj mamy nowy język, środowisko programowania, ale także całkowicie nowe podejście do dostosowywania sytemu. Główną koncepcją jest rezygnacja z modyfikacji kodu standardowego. Słysząc te herezje po raz pierwszy każdy łapie się za głowę (będzie jeszcze jeden szok w dalszej części wpisu). Niemniej aby program uczynić łatwym w zarządzaniu, aktualizacjach, a przede wszystkim dostosować go do pracy w modelu wynajmu oprogramowania, podjęcie takich kroków było niezbędne.

Idea jest prosta, klient dostaje bazowy system i za pośrednictwem „sklepu” może rozszerzać go o dodatki tworzone przez partnerów. Dodatkowo system ten jest zawsze aktualny, bo w jego aktualizacji nie przeszkadzają zmiany dokonywane w kodzie standardowym.

Oczywiście możliwość modyfikacji kodu sytemu pozostanie (w wersji OnPremis) – pytanie tylko na jak długo? I czy faktycznie warto z tej opcji dalej korzystać?

Zdarzenia

Teraz pewnie zastanawiasz się jak mam coś napisać, skoro nie mogę zmieniać obiektów standardowych? Jak chociażby dodać nowe pole do zaksięgowanej faktury sprzedaży, które będzie wyliczało się podczas jej księgowania?! No cóż – żaden problem. Od niedawna mamy w systemie coś takiego jak zdarzenia – odpowiednik tych znanych z wielu języków programowania. Jeżeli chcesz oprogramować proces księgowania faktury wystarczy, że skorzystasz z jednego z wielu dostępnych zdarzeń. Przykładowo możesz stworzyć własną funkcję podpinając ją pod zdarzenie OnAfterPostSalesDoc() z Codeunitu 80-tego. Teraz naprawdę nie ma potrzeby modyfikować do tego celu Codeunitu 80 🙂

Tutaj jednak mała uwaga – na chwilę obecną możemy wiele rzeczy do systemu dodać – rozszerzyć standardowe funkcje – niemniej modyfikacja standardu nie jest już taka prosta a czasami wręcz niemożliwa.

Język AL w telegraficznym skrócie

Tutaj przyszedł czas na kolejne zaskoczenie. Jeżeli jesteś programistą to zapewne kojarzysz obiekty w plikach tekstowych? Spójrz teraz na porównanie obiektu w pliku TXT i pliku źródłowym AL:

Ale spokojnie – to faktycznie wygląda podobnie i faktem jest, że teraz będziemy pisali obiekty w postaci tekstowej. Niemniej twoje pierwsze wrażenie, że jest to krok wstecz jest błędne. Przekonasz się o tym jak tylko zaczniesz korzystać z nowego języka.

Kilka faktów o języku AL:

Warto poświęcić trochę czasu i przeczytać materiały na stronie Microsoft dotyczące programowania w języku AL. Jest tam wszystko czego nie dam rady opisać.

Jak zacząć ?

Zróbmy pierwsze Extension 2.0 na podstawie prostego przykładu. Cel jest prosty:

  • stworzyć nową tabelę w której przechowywane będą wszystkie zmiany limitu kredytowego nabywcy
  • po zmianie limitu kredytowego dla nabywcy zmiana zostanie zapisana w nowej tabeli
  • na kartotece klienta wyświetlona będzie ilość zmian zapisanych w naszej tabeli

Przed rozpoczęciem upewnij się, że masz poprawnie zainstalowane VS Sutdio Code, oraz twój kontener z systemem Business Central działa.

Instalacja dodatku AL do Visual Studio Code

Aby móc programować w języku AL, trzeba zainstalować wersję dodatku do Visual Studio Code zgodną z wersją systemu Business Central, na której mamy zamiar programować. Jeżeli chodzi o kontenery to dodatek znajdziesz zawsze uruchamiając stronę hostowaną na twoim kontenerze. W naszym przypadku będzie to http://businesscentral:8080/ (http://[nazwa_kontenera]:8080).

Jeżeli korzystasz z wersji zainstalowanej standardowo to musisz pamiętać o doinstalowaniu składnika „Modern Development…”. Po jego zainstalowanie plik z rozszerzeniem znajdziesz np. w katalogu: C:\Program Files (x86)\Microsoft Dynamics NAV\130\Modern Development Environment\ALLanguage.vsix

Jeżeli masz już odpowiedni plik (*.vsix) to aby go zainstalować uruchom Visual Studio Code, a następnie paletę komend (Shift+Ctrl+P) i wpisz polecenie: „Extensions: Install from VSIX…”, następnie wybierz plik z rozszerzeniem. Po przeładowaniu VS Code będzie gotowe do pracy.

Tworzymy projekt

Skoro dodatek masz już zainstalowany. Utwórz pierwszy projekt:

  • W Visual Studio Code uruchom komendę: AL:GO
  • Wybierz katalog w którym ma zostać utworzony projekt
  • Wybierz opcję ze środowiskiem lokalnym
  • Po utworzeniu projektu otworzy się plik konfiguracyjny w którym trzeba podać aktualne dane lokalnego serwera Business Central – w naszym przypadku powinno być wymagana tylko zmiana nazwy instancji na „businessventral”. Tak zmodyfikowany plik należy zapisać
  • Następnym krokiem jest pobranie symboli z naszego serwera, w tym celu uruchom komendę „AL:Download Symbols”. Symbole to nic innego jak definicja obiektów zapisanych w bazie danych (table, codunity, etc.)
  • Podaj nazwę użytkownika i hasło (to samo jakim logujesz się do Business Central)
  • Po pobraniu symboli projekt jest gotowy do uruchomienia

Pliki konfiguracyjne

W utworzonym projekcie znajdują się dwa pliki konfiguracyjne:

  • launch.json – to tutaj znajdują się parametry do uruchomienia skompilowanego rozszerzenia (nazwa serwera, itp.)
  • app.json – tutaj zaś są dane naszego rozszerzenia (nazwa naszego extension itp)

Nie chcę tutaj zagłębiać się w opis tych plików, zapoznaj się z nim u źródła: https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-json-files

Snippets

Zanim zaczniemy tworzyć obiekty chciałbym Ci tylko powiedzieć, że należy korzystać z przygotowanych snippetsów. Jeżeli nie wiesz co to takiego to cofnij się do wpisu o Visual Studio Code. Wszystkie snippetsy przygotowane dla języka AL rozpoczynają się od przedrostka „t”, czyli aby utworzyć tabelę należy zacząć po prostu pisać „ttable”, a następnie za pomocą tabulatora przechodzić do kolejnych pól.

Tworzymy nową tabelę

Czas zacząć tworzyć nasze pierwsze extension o zbudowania pierwszej tabeli. A jak to zrobić? Najlepiej w praktyce. Stwórz i zapisz plik o nazwie: „Tab50100.Credit Limit History.al”. Będzie to nasza tabela przechowująca historię zmian limitu kredytowego nabywcy.

W stworzonym pliku korzystając ze snippetsów (ttable, tfield..) stwórz strukturę nowej tabeli:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
table 50100 "Credit Limit Hist."
{
    DataClassification = ToBeClassified;
    LookupPageId = "Credit Limit History"; #stronę stworzymy w następnym kroku
    DrillDownPageId = "Credit Limit History";

    fields
    {
        field(1; "Customer No."; Code[20])
        {
            TableRelation = Customer."No.";
        }
        field(2; "Modification date time"; Datetime)
        {
        }
        field(3; "Credit Limit Amout"; Decimal)
        {
        }
        field(4; "User ID"; Text[50])
        {
            TableRelation = User."User Name";
        }
    }

    keys
    {
        key(PK; "Customer No.", "Modification date time")
        {
            Clustered = true;
        }
    }
}

Tworzenie tabeli z użyciem snippetsów wygląda mniej więcej tak:

(jak nagrywałem to nie zauważyłem przypadkowej zmiany właściwości DataClasification – powinna zostać standardowa)

Jak widzisz tabela wygląda prawie tak samo jakbyś wyeksportował ją do tekstu prosto z Development Environment.

Tworzymy nowy page

Aby można było wyświetlić zawartość nowej tabeli potrzebny będzie page typu lista. Nowy page zapiszmy w pliku o nazwie: „Pag50100.Credit Limit History.al”. Nowy plik można stworzyć przy pomocy snippetsu tpage. Struktura nowego pliku powinna wyglądać mniej więcej tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
page 50100 "Credit Limit History"
{
    PageType = List;
    SourceTable = "Credit Limit Hist.";

    layout
    {
        area(content)
        {
            repeater(Group)
            {
                field("Modification date time";"Modification date time")
                {
                }
                field("Credit Limit Amout";"Credit Limit Amout")
                {
                }
                field("User ID";"User ID")
                {
                }
            }
        }
        area(factboxes)
        {
        }
    }

    actions
    {
        area(processing)
        {

        }
    }
}

Specjalnie pozostawiłem sekcje area(factboxes) i actions – aby było widać jak stworzyć podstawowe elementy strony.

Dodanie pola do tabeli Customer

Chciałbym Ci także zademonstrować jak rozszerzać istniejące tabele, w tym celu do tabeli nabywcy dodamy pole zliczające ilość zapisanych zmian limitu kredytowego.

Aby dodać pole do istniejącej tabeli należy stworzyć obiekt typu Table Extension. Utwórz i zapisz nowy plik Tab18-Ext.Customer.al

Następnie utwórz wstępną strukturę zacznając od wpisania snipeetsu ttableext i korzystając z tabulatora uzupełnij wszystkie elementy tak aby struktura wyglądała następująco:

1
2
3
4
5
6
7
8
9
10
11
tableextension 50100 CustomerExt extends Customer
{
    fields
    {
        field(50100;"Credit Limit History Changes";Integer)
        {
            FieldClass=FlowField;
            CalcFormula=count("Credit Limit Hist." where("Customer No."=field("No.")));
        }
    }    
}

Dodanie nowego pola do kartoteki nabywcy

Aby zobaczyć w systemie nowo dodane pole należy je dodać (poza tabelą) do strony „Customer Page”. W tym celu utwórz obiekt Page Extension o nazwie: Pag21-Ext.Customer Card.al. I korzystając ze snippetu tpageext utwórz następujący plik:

1
2
3
4
5
6
7
8
9
10
11
12
pageextension 50100 CustomerCardExt extends "Customer Card"
{
    layout
    {
        addafter("Credit Limit (LCY)")
        {
            field("Credit Limit History Changes";"Credit Limit History Changes")
            {
            }
        }
    }
}

Tutaj widać elementy dość ciekawe jak np. addafter – czyli dodaj po polu. Zachęcam do zajrzenia w dokumentację wszystkich typów obiektów aby przekonać się o dostępnych możliwościach.

Nowy Codeunit

Teraz trzeba stworzyć Codeunit, który wykryje zdarzenie walidacji pola limitu kredytowego na kartotece nabywcy i zapisze tą zmianę w naszej tabeli. Także do dzieła – stwórz i zapisz plik „Cod50100.Credit Limit History Mgt.al”. Następnie nadaj mu treść mniej więcej taką jak poniżej:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
codeunit 50100 "Credit Limit History Mgt"
{
    trigger OnRun();
    begin
    end;

    [EventSubscriber(ObjectType::Table, Database::Customer, 'OnAfterValidateEvent', 'Credit Limit (LCY)', true,true)]
    procedure onValidateCustCreditLimit(Rec:Record Customer;xRec:Record Customer;CurrFieldNo:Integer)
    var
        CustLimitHist:Record "Credit Limit Hist.";
    begin
        CustLimitHist.Init();
        CustLimitHist."Customer No." := Rec."No.";
        CustLimitHist."Modification date time" := CurrentDateTime();
        CustLimitHist."Credit Limit Amout" := Rec."Credit Limit (LCY)";
        CustLimitHist."User ID" := UserId();
        CustLimitHist.Insert();    
    end;
}

Jak widać stworzona funkcja jest typu EventSubscriber – podpięty do zdarzenia „Credit Limit (LCY)”.onValidate() wywoływanego na tabli Customer. Oznacza to, ze nasza funkcja wywoła się po walidacji tegoż pola. Więcej o zdarzeniach możesz przeczytać np. tutaj.

Uruchomienie extension

Aby uruchomić extension wystarczy nacisnąć przycisk F5 – oczywiście jeżeli dobrze skonfigurowałeś(aś) plik launch.json. Po kompilacji powinno otworzyć się okno przeglądarki z systemem Business Central. Po otwarciu kartoteki nabywcy można przetestować to co stworzyliśmy:

Debugger

Oczywiście mamy także możliwość debuggowania kodu. Wystarczy przed uruchomieniem utworzyć breakpoint:

TXT2AL

Warto w tym miejscu wspomnieć także o narzędziu dostępnym z poziomu wiersza poleceń. Pozwala ono na konwertowanie obiektów wyeksportowanych w formacie .txt do nowego formatu .al. Narzędzie znajdziecie w katalogu: C:\Program Files (x86)\Microsoft Dynamics NAV\130\RoleTailored Client\Txt2Al.exe. Korzystanie z niego jest banalnie proste wystarczy podać dwa parametry:
txt2al.exe –source „c:\txt\” –target „c:\al\”.

Więcej informacji na stronie: https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-txt2al-tool

Podsumowanie

To tyle jeżeli chodzi o pierwsze extension. Wpis i tak jest już długi dlatego pominąłem chociażby temat raportów. Niemniej jeżeli będzie zainteresowanie tym tematem chętnie stworzę kilka kolejnych wpisów.

Na koniec archiwum z kodem źródłowym do pobrania tutaj.

Do poczytania

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-dev-overview

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-extension-advanced-example

Od zera do Extension – cz.1 Visual Studio Code

Od zera do Extension – cz2. PowerShell

Od zera do Extension – cz3. Docker

Od zera do Extension – cz4. Business Central Sandbox

Zostaw odpowiedź

Twój adres e-mail nie zostanie opublikowany Wymagane pola są zaznaczone *