W tym tutorialu dowiesz się czym są funkcje dodatkowe w Optimie i jak możesz w prosty sposób wykorzystać je do integracji z JustLabel, aby wydruki mogły być uruchamiane bezpośrednio w systemie Comarch ERP Optima. Możesz także pobrać przykładowy plik z funkcją dodatkową służącą do wydruku etykiety towaru.
Informacje o Funkcjach Dodatkowych
Przedstawione w tym tutorialu informacje obejmują zakres wiedzy niezbędnej do integracji JustLabel z Comarch Optima przez funkcje dodatkowe. Nie skupiamy się tutaj na szczegółowym poznawaniu funkcji dodatkowych. Jeżeli zakres wiedzy przedstawiony tutaj nie będzie dla Ciebie wystarczjący, możesz odnieść się do strony pomocy Comarch.
Funkcje dodatkowe pozwalają na rozbudowę standardowej funkcjonalności systemu Comarch ERP Optima. Oznacza to, że są w stanie wykonywać dodatkowe zadania, które nie zostały przewidziane podczas projektowania systemu. W naszym przypadku, wykorzystujemy mechanizm funkcji dodatkowych do uruchamiania wydruków bezpośrednio z systemu, poprzez wysłanie żądania drukowania do endpointu REST API programu JustLabel.
Jak wskazuje dokumentacja Comarch Funkcje dodatkowe zostały zbudowane w oparciu o ten sam mechanizm, co wydruki (…) Funkcje dodatkowe są kontekstowo dostępne na wszystkich oknach, na których jest dostępny mechanizm drukowania co idealnie wpasowuje się w charakter naszej integracji.
Na wstążce programu Optima znajduje się przycisk Funkcje dodatkowe, który umożliwia rozwinięcie listy dostępnych funkcji dodatkowych podzielonych na kategorie (jeżeli tak zostały zorganizowane). Dla każdego okna lub zakładki, lista funkcji dodatkowych może się różnić w zależności od konfiguracji. Oznacza to, że możliwe jest przypisanie odpowiedniej funkcji lub listy funkcji dodatkowych (uruchamiających odpowiednie wydruki) pasujących kontekstowo do aktualnie otwartego okna systemu. Szukaj następującej ikony na wstążce:
Dodawanie oraz konfiguracja funkcji dodatkowych jest możliwa po wciśnięciu przycisku funkcji dodatkowych wraz z klawiszem Ctrl lub poprzez wybranie opcji Konfiguracja funkcji dodatkowych w rozwijanym menu dostępnym pod wspomnianym w poprzedniej sekcji przyciskiem.
W oknie konfiguracji, w formie drzewka kategorii dostępne są;
Funkcja dodatkowa to fragment kodu wykorzystujący sekcje oddzielone nagłówkami ujętymi w kwadratowe nawiasy. Możemy wykorzystać takie sekcje jak:
Na końcu tego samouczka znajduje się gotowy plik z pełnym kodem funkcji dodatkowej, potrafiący wyświetlać błędy oraz przygotowujący dane do wysyłki pod względem znaków specjalnych. Jednak dla lepszego zrozumienia przedstawimy najpierw kluczowe fragmenty kodu w wersji podstawowej.
Tworzymy definicję funkcji
Aby utworzyć nową funkcję dodatkową, otwórz w systemie Comarch ERP Optima okno Cennik
, następnie otwórz wspomniane wcześniej okno Konfiguracja funkcji dodatkowych i utwórz funkcję o nazwie Drukuj etykietę towaru
Dane etykiety JustLabel
W celu zbudowania przykładu, musimy najpierw zdefiniować pewne dane dotyczące konfiguracji etykiety, jaką chcemy wydrukować.
Mock
, a kolejka drukowania to pq_Mock
. Musisz zamienić tę wartość nazwą kolejki drukowania, którą chcesz wykorzystać w swoim środowisku.towar
code
– kod produktuPrzygotuj testową etykietę
Stwórz nowy, testowy szablon etykiety. Nazwij go
towar
. Na szablonie umieść pole tekstowe, którego wartością będzie placeholder{code}
. Możesz także zbudować bardziej rozbudowane wyrażenie, łącząc statyczny tekst i placeholder, np.„
Kod wybranego produktu: [(${code})]
„.
Sekcja SQL
Sekcja SQL naszej funkcji dodatkowej rozpoczyna się od deklaracji jej początku, czyli od [SQL]
. W następnej linii widzimy deklarację parametru, którego wartością jest aktualnie aktywny obiekt z przeglądarki okna Comarch Optima. Funkcja z założenia będzie wykonywana z okna Towarów, stąd zapytanie kierowane jest do tabeli Towary. Ostatnim elementem sekcji jest definicja zapytania SQL, wykorzystującego zdefiniowany wcześniej parametr.
[SQL]
@PAR ?@HX|Zaznaczenia|Zaznaczenia:1@? PAR@
Select Twr_TwrId From cdn.Towary where {Zaznaczenia(Twr_TwrId, 1)}
Klikając przycisk wykonania funkcji dodatkowej, aplikacja wykona powyższe zapytanie, a jego wyniki będą dostępne w sekcji JavaScript w obiekcie Recordset. Jak da się zauważyć, celem powyższego zapytania jest uzyskanie wartości id
zaznaczonego towaru.
Sekcja JS
Sekcja JavaScript może być napisana w bardzo różny sposób, zależny od programisty. Poniżej przedstawiamy tylko przykładowy, uproszczony sposób implementacji, który powinien dac Ci wyobrażenie jak zrealizować podstawowe zadanie. Poniższy przykład został rozbudowany i uzupełniony o dodatkowe funkcje w ostatniej sekcji tego samouczka.
Rozpoczynamy sekcję JavaScript poprzez znacznik [JS]
.
Musimy mieć na uwadze, że celem tej sekcji jest wykorzystanie danych z obiektu Recordset do budowy żądania REST przesyłanego do JustLabel. Utworzyliśmy sobie zmienną pomocniczą productPresent
która poinformuje nast w dalszej części programu, czy odnaleziono id
produktu, oraz zmienną jsondata
w której zapiszemy ciało obiektu JSON, przesyłanego do JustLabel w żądaniu drukowania.
[JS]
var jsondata = '';
var productPresent = false;
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+ rTowar.Kod + '"}}]}}';
Recordset.moveNext();
\}
Pętla while(!Recordset.EOF)
nie wykona się ani razu, jeżeli żaden produkt nie został odnaleziony. W przeciwnym wypadku, oznaczamy productPresent
jako true
i pobieramy za pomocą wbudowanych funkcji Optimy obiekt Towar
.
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId);
Do obiektu jsondata
zapisywane są wartości z obiektu rTowar
, przyporządkowane do nazw placeholderów zdefinowanych w szablonie etykiety towar
. W bardziej czytelnej formie, JSON jaki próbujemy uzyskać odpowiada schematowi przedstawionemu poniżej. Zwróć uwagę na poszczególne wartości, które pokrywają się z założeniami tego samouczka, zdefiniowanymi w sekcji Dane etykiety JustLabel
towar
code
W aktualnym przykładzie wykorzystujemy jeden placeholder. W pełnym przykładzie jest ich więcej i są wymienione jako kolejne pola obiektu placeholderValues
.
{
"template": {
"name": "towar",
"options": [
{
"numberOfCopies": 1,
"placeholderValues": {
"code": "rTowar.Kod"
}
}
]
}
}
Kolejnym etapem będzie wysłanie żądania na określony endpoint. W tym celu należy zbudować odpowiedni adres URL mikroserwisu. Zakładając, że aplikacja JustLabel uruchomiona jest na tej samej maszynie co aplikacja Comarch ERP Optima, adresem hosta będzie tutaj localhost
, a standardowym portem, na którym aplikacja nasłuchuje jest 5987
. W następnej kolejności należy podać adres entpointu print
, a po nim nazwę kolejki drukowania, która w naszym przykładzie nosi nazwę pq_Mock
. Ostateczny adres URL przedstawiono we fragmencie kodu:
if(productPresent) \{
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
\}
Powyższy kod przesyła żądanie POST na zdefiniowany adres URL, przekazując jako wartość JSON zbudowany w poprzednim kroku.
Opis funkcji dodatkowej
Na początku warto określić czemu służy dana funkcja. Optima udostępnia składnię, która to umożliwia. Dodajmy opis funkcji na początku skryptu.
/*<COM_DOK>
<OPIS>
Funkcja wysyłająca request drukowania etykiety z towarem
</OPIS>
</COM_DOK>
*/
Przedstawiliśmy podstawową wersję skryptu. Przed wysłaniem żądania należy zadbać o poprawność danych oraz zaprojektować mechanizm informowania użytkownika o powodzeniu polecenia lub o ewentualnych błędach. Zajmiemy się tym w sekcji opisującej nasz przykładowy skrypt w rozbudowanej wersji. Ostateczna postać podstawowego skryptu funkcji dodatkowej wygląda następująco:
/*<COM_DOK>
<OPIS>
Funkcja wysyłająca request drukowania etykiety z towarem
</OPIS>
</COM_DOK>
*/
[SQL]
@PAR ?@HX|Zaznaczenia|Zaznaczenia:1@? PAR@
Select Twr_TwrId From cdn.Towary where {Zaznaczenia(Twr_TwrId, 1)}
[JS]
var jsondata = '';
var productPresent = false;
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+ rTowar.Kod + '"}}]}}';
Recordset.moveNext();
\}
/* SEND PRINT REQUEST */
if(productPresent) \{
/* replace `pq_Mock` with the name of your printer queue*/
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
\}
Wynikiem takiego skryptu powinien być wydruk ze strony JustLabel. Nie zamienić nazwy kolejki pq_Mock
na realną kolejkę, której chcesz użyć do wydruku.
Kolejki wydruku
Kolejka wydruku tworzona jest automatycznie po dodaniu drukarki. Jej nazwa pokrywa się z nazwą drukarki poprzedzoną prefiksem pq_
. W związku z tym, jeżeli np. Twoja drukarka zdefiniowana jest jako Office
, standardowa nazwa jej kolejki to pq_Office
.
Program umożliwia edycję nazw kolejek drukowania oraz przypisanych do nich drukarek.
Mało prawdopodobne, aby etykieta zawierająca sam kod produktu była przydatna. W związku z tym dodamy kolejne placeholdery opisujące nasz towar. Upewnij się, że przed wykonaniem tego kroku, Twój szablon etykiety towar
w JustLabel ma zdefiniowane następujące placeholdery:
code
– kod produktudescription
– opis produktuname
– nazwaunit
– jednostka logistycznaid
– identyfikatorAby wartości mogły być wykorzystane przy budowie wydruku, muszą zostać pobrane z obiektu rTowar
i przekazane w żądaniu REST. Zmodyfikuj kod skryptu opisany w poprzedniej sekcji w następujący sposób:
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+rTowar.Kod+'","description":"'+safeRestValue(rTowar.Opis)+'","name":"'
+safeRestValue(rTowar.Nazwa)+'","id":"'+rTowar.Id+'","unit":"'
+safeRestValue(rTowar.JM)+'"}}]}}';
Recordset.moveNext();
\}
Jak widzisz, zmienił się sposób budowy obiektu jsondata
. Dołączone zostały kolejne pola obiektu rTowar
odpowiadające poszczególnym placeholderom.
W tym momencie, obiekt jsondata
powinien reprezentować obiekt JSON w nieco bardziej rozbudowanej postaci niż dotychczas. JSON jaki powinien zostać zbudowany prezentuje się następująco:
{
"template": {
"name": "towar",
"options": [
{
"numberOfCopies": 1,
"placeholderValues": {
"code": "rTowar.Kod",
"description": "rTowar.Opis",
"name": "rTowar.Nazwa",
"id": "rTowar.Id",
"unit": "rTowar.JM"
}
}
]
}
}
Powyższa zmiana, poza dodaniem nowych zmiennych, modyfikuje przekazywane wartości, zanim zostaną przekazane do REST API. Są one przygotowywane do wysyłki przez funkcję safeRestValue
.
Dodawanie funkcji w skrypcie
Kolejne funkcje przedstawione poniżej dodawaj po prostu po sobie w bloku
[JS]
. Proponujemy dodawać je na końcu kodu, po fragmencie odpowiadającym za wysłanie żądania drukowania.
Dodaj do skryptu następującą funkcję:
function safeRestValue(value) \{
value = value.replace(/[\\"\']/g, '')
.replace(/[\r\n\x0B\x0C\u0085\u2028\u2029]+/g," ");
return value;
\}
Funkcja safeRestValue
przyjmuje jeden argument tekstowy value
. Będzie to wartość, jaką chcemy przekazać do wydruku. Pola takie jak opis lub nazwa mogą zawierać znaki niedozwolone w żądaniu. Funkcja usuwa wszelkie znaki, które mogą spowodować błąd składni podczas przesyłania żądania wydruku.
Funkcja dodatkowa w aktualnej wersji jest w stanie pobrać zestaw danych z systemu Optima oraz przekazać je do aplikacji JustLabel w celu wydruku. Dane są przygotowane do przesłania i nie spowodują błędów przesyłu. Gotowa funkcja dodatkowa w wersji podstawowej wygląda następująco:
/*<COM_DOK>
<OPIS>
Funkcja wysyłająca request drukowania etykiety z towarem
</OPIS>
</COM_DOK>
*/
[SQL]
@PAR ?@HX|Zaznaczenia|Zaznaczenia:1@? PAR@
Select Twr_TwrId From cdn.Towary where {Zaznaczenia(Twr_TwrId, 1)}
[JS]
var jsondata = '';
var productPresent = false;
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+rTowar.Kod+'","description":"'+safeRestValue(rTowar.Opis)+'","name":"'
+safeRestValue(rTowar.Nazwa)+'","id":"'+rTowar.Id+'","unit":"'
+safeRestValue(rTowar.JM)+'"}}]}}';
Recordset.moveNext();
\}
/* SEND PRINT REQUEST */
if(productPresent) \{
/* replace `pq_Mock` with the name of your printer queue*/
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
\}
function safeRestValue(value) \{
value = value.replace(/[\\"\']/g, '')
.replace(/[\r\n\x0B\x0C\u0085\u2028\u2029]+/g," ");
return value;
\}
Zachęcamy do dalszej lektury, w celu poznania kolejnych elementów, jakie możesz wykorzystać tworząc funkcje dodatkowe w systemie Optima.
Choć funkcja dodatkowa w podstawowej postaci powinna działać poprawnie, uzupełnienie jej o niektóre funkcjonalności jest przydatne, a w pewnych miejscach niezbędne.
Wyświetlanie komunikatów
Dobrze, kiedy istnieje możliwość wyświetlenia komunikatu o błędzie lub powodzeniu akcji, aby użytkowik mógł dowiedzieć się jaki jest wynik jego działania. W tym celu, na początku sekcji [JS]
zadeklarujmy zmienną IE
, która będzie się odnosiła do okna Internet Explorer
, w którym będziemy wyświetlali komunikaty, a także stwórzmy obiekt shell
, który umożliwi wyświetlanie zarówno okna IE jak i prostych okien popup.
var shell = new ActiveXObject("WScript.Shell");
var IE;
Następnie utworzymy funkcję, która będzie przygotowywała obiekt okna Internet Explorer. Wywołamy ją przed każdym wyświetleniem komunikatu.
Poniższa funkcja tworzy obiekt ActiveX wykorzystując InternetExplorer i przypisuje go do zadeklarowanej wcześniej zmiennej IE
.
function setUpIE()\{
IE = new ActiveXObject ("InternetExplorer.Application");
IE.AddressBar = false;
IE.MenuBar = false;
IE.StatusBar = false;
IE.ToolBar = false;
IE.Resizable = false;
IE.Left = 50;
IE.Top = 50;
IE.Height = 500;
IE.Width = 800;
IE.Visible = true;
IE.Navigate('about:blank');
shell.AppActivate(IE);
\}
Ostatecznie, funkcja odpowiadająca za wyświetlenie wartości. Jak widzisz, funkcja przyjmuje tylko jeden argument message
, czyli wartość tekstową do wyświetlenia. Zauważ, że funkcja korzysta z tworzonego w poprzedniej funkcji obiektu przypisanego do zminnej IE
.
function showInIE(message)\{
var content = '<html>';
content += '<head>';
content += '<title>Wydruk JustLabel</title>';
content += '</head>';
content += '<body>';
content += message;
content += '</body></html>';
IE.Document.body.innerHTML = content;
IE.Document.focus;
shell.AppActivate(IE);
\}
Ostatnia funkcja będzie funkcją podstawową, decydującą o tym jaki komunikat pokazać w danej sytuacji. Będzie przyjmowała trzy argumenty:
boolean
, decydująca czy wyświetlić komunikat w przypadku błęduboolean
, decydująca czy wyświetlić komunikat w przypadku powodzeniaW zależności od statusu odpowiedzi obiektu xmlhttp
program będzie wyświetlał odpowiedni komunikat. Jeżeli status odpowiedzi będzie równy 202
, a wartość showSuccess
równa true
, program wyświetli komunikat informujący o poprawnym przyjęciu żądania drukowania. Endpoint REST API JustLabel w przypadku sukcesu zwraca UUID wydruku. W codziennym użytkowniu aplikacji nie jest to przydatna informacja, jednak w naszym przykładzie flagę showSuccess
ustawimy na true
w celach demonstracyjnych. Analogicznie, dla statusu różnego od 202
i flagi showError
równej true
, funkcja wyświetli komunikat o błędzie zwrócony z endpointu JustLabel.
function showResponse(xmlhttp, showError, showSuccess)\{
// error
if (xmlhttp.status != 202 && showError == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
// success
if (xmlhttp.status == 202 && showSuccess == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
\}
Pora dodać komunikaty w odpowiednich miejscach. W tym celu edytuj sekcję odpowiadającą za pobieranie danych w następujący sposób:
/* SEND PRINT REQUEST */
if(productPresent) \{
/* replace `pq_Mock` with the name of your printer queue*/
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
showResponse(xmlhttp, true, true); /* SHOW RESPONSE MESSAGE */
\}
else \{ /* POPUP IN CASE NO ITEM SELECTED */
shell.Popup("Nie wybrano towaru");
\}
Dodana została linia oznaczona komentarzem /* SHOW RESPONSE MESSAGE */
oraz sekcja else
oznaczona komentarzem /* POPUP IN CASE NO ITEM SELECTED */
. Pierwsza modyfikacja to wywołanie funkcji pokazującej odpowiedź serwera zarówno w przypadku powodzenia jak i błędu żądania (obie flagi ustawione jako true
). Druga modyfikacja to wyświetlenie prostego kompuniaktu popup
w przypadku braku zaznaczonego towaru do wydruku.
W tym momencie powinieneś móc uruchomić swój skrypt i zobaczyć odpowiedź z serwera w przypadku powodzenia oraz w przypadku błędu. Spróbujmy wywołać funkcję dodatkową.
Otrzymany wynik to identyrikator UUID wydruku, który został wykonany przez program JustLabel. W produkcyjnej wersji warto wyłączyć wyświetlanie tego komunikatu w przypadku sukcesu, ustawiając flagę showSuccess
na false
.
Spróbujmy wywołać błędne żądanie, aby zobaczyć wynik prezentujący błąd. W tym celu zmodyfikujmy chwilowo nazwę kolejki wydruku w skrypcie. Zamist poprawnego w naszym przypadku pq_Mock
, wykorzystamy nazwę pq_Mock_Missing
. Po wykonaniu skryptu z błądną nazwą, komunikat pokaże błąd w oknie IE. Jeżeli chcesz wyłączyć takie komunikaty, ustaw flagę showError
na false
.
Jak widzimy w treści komunikatu message
: Cannot find printer queue by its name [pq_Mock_Missing], kolejka nie została rozpoznana. Poprawmy nasz skrypt, aby ponownie wykorzystywał kolejkę pq_Mock
.
Ostatecznie, skrypt funkcji dodatkowej rozszerzony o funkcjonalność wyświetlania komunikatów wygląda następująco:
/*<COM_DOK>
<OPIS>
Funkcja wysyłająca request drukowania etykiety z towarem
</OPIS>
</COM_DOK>
*/
[SQL]
@PAR ?@HX|Zaznaczenia|Zaznaczenia:1@? PAR@
Select Twr_TwrId From cdn.Towary where {Zaznaczenia(Twr_TwrId, 1)}
[JS]
var shell = new ActiveXObject("WScript.Shell");
var IE;
var jsondata = '';
var productPresent = false;
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+rTowar.Kod+'","description":"'+safeRestValue(rTowar.Opis)+'","name":"'
+safeRestValue(rTowar.Nazwa)+'","id":"'+rTowar.Id+'","unit":"'
+safeRestValue(rTowar.JM)+'"}}]}}';
Recordset.moveNext();
\}
/* SEND PRINT REQUEST */
if(productPresent) \{
/* replace `pq_Mock` with the name of your printer queue*/
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
showResponse(xmlhttp, true, true); /* SHOW RESPONSE MESSAGE */
\}
else \{ /* POPUP IN CASE NO ITEM SELECTED */
shell.Popup("Nie wybrano towaru");
\}
function safeRestValue(value) \{
value = value.replace(/[\\"\']/g, '')
.replace(/[\r\n\x0B\x0C\u0085\u2028\u2029]+/g," ");
return value;
\}
function setUpIE()\{
IE = new ActiveXObject ("InternetExplorer.Application");
IE.AddressBar = false;
IE.MenuBar = false;
IE.StatusBar = false;
IE.ToolBar = false;
IE.Resizable = false;
IE.Left = 50;
IE.Top = 50;
IE.Height = 500;
IE.Width = 800;
IE.Visible = true;
IE.Navigate('about:blank');
shell.AppActivate(IE);
\}
function showInIE(message)\{
var content = '<html>';
content += '<head>';
content += '<title>Wydruk JustLabel</title>';
content += '</head>';
content += '<body>';
content += message;
content += '</body></html>';
IE.Document.body.innerHTML = content;
IE.Document.focus;
shell.AppActivate(IE);
\}
function showResponse(xmlhttp, showError, showSuccess)\{
// error
if (xmlhttp.status != 202 && showError == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
// success
if (xmlhttp.status == 202 && showSuccess == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
\}
W takiej postaci funkcja dodatkowa nadaje się do wykorzystania w celach testowych oraz wykorzystania w środowisku produkcyjnym. Nie jest to jednak ostateczna wersja funkcji, jaką przygotowaliśmy. W kolejnej sekcji dodamy funkcjonalność pozwalającą na wykorzystanie atrybutów systemu Optima.
Do tej pory w szablonie wykorzystywaliśmy tylko podstawowe pola bazy danych, jakie udostępnia system Optima. Były to standardowe informacje, jak kod produktu, opis czy identyfikator albo jednostka. W rzeczywistości może zdarzyć się, że będziesz chciał umieszczać na szablonie etykiety dodatkowe informacje, które będą przechowywane w dodatkowych polach systemu, nazywanych Atrybutami
.
Atrybuty towaru w Comarch Optima
Zakładamy, że wiesz czym są Atrybuty
systemu Comarch Optima. Jeżeli chcesz dowiedzieć się więcej na ich temat, odwiedź stronę pomocy firmy Comarch.
Oczywiście mógłbyś dodać kolejne pola w obiekcie JSON, definiując nazwy atrybutów na stałe w kodzie programu, jednak nie polecamy takiego podejścia. W przyszłości, jeżeli postanowisz dodać do szablonu etykiety kolejne wartości atrybutów, musiałbyś edytować funkcję dodatkową. Podobnie wyglądałaby sytuacja w przypadku zmiany nazwy atrybutu.
Bardziej elastycznym rozwiązaniem będzie automatyczne dołączenie wszystkich atrybutów do obiektu JSON. Dzięki takiemu podejściu, jeżeli dodasz nowy atrybut lub zmienisz jego nazwę, wystarczy, że dostosujesz szablon etykiety w programie JustLabel, bez konieczności modyfikacji funkcji dodatkowej w systemie Optima.
Aby wdrożyć to rozwiązanie, postępuj zgodnie z krokami przedstawionymi poniżej.
Dodaj atrybuty do JSON
Zmodyfikuj fragment skryptu, który buduje obiekt JSON w następujący sposób:
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+rTowar.Kod+'","description":"'+safeRestValue(rTowar.Opis)+'","name":"'
+safeRestValue(rTowar.Nazwa)+'","id":"'+rTowar.Id+'","unit":"'
+safeRestValue(rTowar.JM)+'"'+jsonizeAttributes(rTowar)+'}}]}}';
Recordset.moveNext();
\}
Jedyną zmianą było tutaj dodanie wywołania funkcji jsonizeAttributes(rTowar)
, którą zdefiniujemy w kolejnym kroku.
Funkcja „jsonizeAttributes”
Dodaj funkcję odpowiedzialną za odczyt wszystkich atrybutów wybranego towaru oraz przekształcenie ich w ciąg tekstowy dla JSON, zbudowany z par nazwa_atrybutu
: wartość
. Jak się domyślasz, nazwa_atrybutu
będzie odpowiadała nazwie placeholdera w szablonie etykiety, a wartość
będzie wartością wstawianą w miejsce placeholdera podczas wydruku.
Lista atrybutów pobierana jest z obiektu biblioteki Optima towar
, przekazanego do funkcji jako argument.
function jsonizeAttributes(towar)\{
var attrList = [];
var atrybuty = rTowar.Atrybuty;
var atrybut;
var json = '';
for(var i = 0; i < atrybuty.Count; i++) \{
atrybut = atrybuty.Item(i);
if(!listContains(attrList, atrybut.nazwa)) \{
json = json + ', "'+ safeAttributeName(atrybut.nazwa)+'":"'+safeRestValue(atrybut.Wartosc)+'"';
attrList.push(atrybut.nazwa);
\}
\}
return json;
\}
Jak widzisz, funkcja przygotowuje w odpowiedni sposób dane. Dla wartości atrybutów wykorzystuje funkcję safeRestValue
, którą utworzyliśmy wcześniej. Nazwa atrybutu zostaje natomiast przygotowana przez funkcję safeAttributeName
, która jeszcze nie istnieje. Jej budową zajmiemy się w kolenjej sekcji.
Poza tym, wykorzystano także funkcję pomocniczą listContains
. Również zostanie omówiona w kolejnych sekcjach.
Funkcja „safeAttributeName”
Zadaniem tej funkcji jest pobranie jako argument name
oraz przygotowanie i ustandaryzowanie nazw atrybutów, aby można było je wykorzystać w programie JustLabel. Dodaj następującą funkcję:
function safeAttributeName(name) \{
//test string test = ' `~!@#$%^&*()-=+[]{}|\:;"\',<.>/?';
name = name.toUpperCase();
name = name.replace(/[ `~!@#$%^&*()\-=+\[\]{}|\/\\:;"\',<.>?]/g, '_')
.replace(/[Ą]/g, 'A')
.replace(/[Ć]/g, 'C')
.replace(/[Ę]/g, 'E')
.replace(/[Ł]/g, 'L')
.replace(/[Ń]/g, 'N')
.replace(/[Ó]/g, 'O')
.replace(/[Ś]/g, 'S')
.replace(/[ŻŹ]/g, 'Z');
return name;
\}
Jedna z operacji to usunięcie polskich znaków diakrytycznych, możliwych do wykorzystania w nazwach atrybutów Comarch Optima, jednak niedozwolonych w nazwach placeholderów JustLabel. Z tego samego powodu, niedozwolone w nazwach placeholderów w JustLabel znaki specjalne zostaną zamienione na znak podkreślenia „_
„. Dodatkowo, cała nazwa zamieniana jest na tekst pisany wielkimi literami.
Tworząc szablony etykiet warto zdefiniować i znać zasady, wg których nazwy atrybutów są konwertowane na nazwy placeholderów w JSON. Pomoże to w poprawnym budowaniu elastycznych szablonów etykiet. Pomimo skomplikowanego wyglądu, powyższa funkcja jest prosta i wykonuje trzy zadania:
_
„Przykład
Jeżeli atrybut towaru w Comarch optima będzie nosił nazwę
@gęstość&ciśnienie
, nazwą placeholdera odpowiadającego temu atrybutowi powinna być_GESTOSC_CISNIENIE
.
Funkcja „listContains”
Jak wspomniano wcześniej, listContains
jest funkcją pomocniczą wykorzystaną w jsonizeAttributes
. Dodaj ją do skryptu:
function listContains(list, val) \{
for(var i = 0; i < list.length; i++) \{
if(list[i]==val) \{
return true;
\}
\}
return false;
\}
Zadaniem tej funkcji jest sprawdzenie, czy dany atrybut znajduje się już na liście. Inaczej mówiąc, warunek if(!listContains(attrList, atrybut.nazwa))
zapewnia bezpieczeństwo w sytuacji, kiedy występuje wiecej niż jeden atrybut w systemie Optima o tej samej nazwie. Comarch Optima pozwala na taką sytuację, jednak w JustLabel, przesłanie dwóch wartości z identycznym kluczem prowadziłoby do niejasności i błędu.
Poniżej przedstawiamy ostateczną wersję skryptu funkcji dodatkowej, służącej do wydruku etykiety towaru.
W kolejnych sekcjach tego samouczka funkcja zmieniała się i rozrastała w następujących etapach:
Aby móc wykorzystać poniższą funkcję, pamiętaj o zmianie nazwy kolejki pq_Mock
na rzeczywistą nazwę kolejki, którą chcesz wykorzystać.
/*<COM_DOK>
<OPIS>
Funkcja wysyłająca request drukowania etykiety z towarem
</OPIS>
</COM_DOK>
*/
[SQL]
@PAR ?@HX|Zaznaczenia|Zaznaczenia:1@? PAR@
Select Twr_TwrId From cdn.Towary where {Zaznaczenia(Twr_TwrId, 1)}
[JS]
var shell = new ActiveXObject("WScript.Shell");
var IE;
var jsondata = '';
var productPresent = false;
/* BUILD JSON OBJECT */
while(!Recordset.EOF) \{
productPresent = true;
var towarId = Recordset.Fields(0).Value;
var rTowar = Session.CreateObject("CDN.Towary").Item("Twr_TwrID=" + towarId );
jsondata = '{"template":{"name":"towar","options":[{"numberOfCopies":1,"placeholderValues":{"code":"'
+rTowar.Kod+'","description":"'+safeRestValue(rTowar.Opis)+'","name":"'
+safeRestValue(rTowar.Nazwa)+'","id":"'+rTowar.Id+'","unit":"'
+safeRestValue(rTowar.JM)+'"'+jsonizeAttributes(rTowar)+'}}]}}';
Recordset.moveNext();
\}
/* SEND PRINT REQUEST */
if(productPresent) \{
/* replace `pq_Mock` with the name of your printer queue*/
var url = 'http://localhost:5987/print/pq_Mock';
var xmlhttp = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xmlhttp.send(jsondata);
showResponse(xmlhttp, true, true); /* SHOW RESPONSE MESSAGE */
\}
else \{ /* POPUP IN CASE NO ITEM SELECTED */
shell.Popup("Nie wybrano towaru");
\}
function jsonizeAttributes(towar)\{
var attrList = [];
var atrybuty = rTowar.Atrybuty;
var atrybut;
var json = '';
for(var i = 0; i < atrybuty.Count; i++) \{
atrybut = atrybuty.Item(i);
if(!listContains(attrList, atrybut.nazwa)) \{
json = json + ', "'+ safeAttributeName(atrybut.nazwa)+'":"'+safeRestValue(atrybut.Wartosc)+'"';
attrList.push(atrybut.nazwa);
\}
\}
return json;
\}
function safeRestValue(value) \{
value = value.replace(/[\\"\']/g, '')
.replace(/[\r\n\x0B\x0C\u0085\u2028\u2029]+/g," ");
return value;
\}
function safeAttributeName(name) \{
//test string test = ' `~!@#$%^&*()-=+[]{}|\:;"\',<.>/?';
name = name.toUpperCase();
name = name.replace(/[ `~!@#$%^&*()\-=+\[\]{}|\/\\:;"\',<.>?]/g, '_')
.replace(/[Ą]/g, 'A')
.replace(/[Ć]/g, 'C')
.replace(/[Ę]/g, 'E')
.replace(/[Ł]/g, 'L')
.replace(/[Ń]/g, 'N')
.replace(/[Ó]/g, 'O')
.replace(/[Ś]/g, 'S')
.replace(/[ŻŹ]/g, 'Z');
return name;
\}
function listContains(list, val) \{
for(var i = 0; i < list.length; i++) \{
if(list[i]==val) \{
return true;
\}
\}
return false;
\}
function setUpIE()\{
IE = new ActiveXObject ("InternetExplorer.Application");
IE.AddressBar = false;
IE.MenuBar = false;
IE.StatusBar = false;
IE.ToolBar = false;
IE.Resizable = false;
IE.Left = 50;
IE.Top = 50;
IE.Height = 500;
IE.Width = 800;
IE.Visible = true;
IE.Navigate('about:blank');
shell.AppActivate(IE);
\}
function showInIE(message)\{
var content = '<html>';
content += '<head>';
content += '<title>Wydruk JustLabel</title>';
content += '</head>';
content += '<body>';
content += message;
content += '</body></html>';
IE.Document.body.innerHTML = content;
IE.Document.focus;
shell.AppActivate(IE);
\}
function showResponse(xmlhttp, showError, showSuccess)\{
// error
if (xmlhttp.status != 202 && showError == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
// success
if (xmlhttp.status == 202 && showSuccess == true) \{
setUpIE();
showInIE(xmlhttp.responseText);
\}
\}
Tak przygotowany skrypt jest ostateczną wersją jaką przygotowaliśmy. Możesz utworzyć go postępując zgodnie z krokami przedstawionymi w tym samouczku, do czego gorąco zachęcamy, lub pobrać gotowy plik w sekcji Pliki do pobrania
W testowym środowisku, dla danego towaru utworzyliśmy dwa atrybuty (możesz wykorzystać atrybuty, które już istnieją w Twoim środowisku Comarch Optima).
kolor
data ważności
Jak pamiętasz z sekcji dotyczącej konwersji nazwy atrybutów Optima na nazwy placeholderów JustLabel, w powyższym przykładzie nazwa data ważności
zostanie zmodyfikowana. W szablonie etykiety dodaliśmy zatem dwa placeholdery odpowiadające atrybutom, stosując się do zasad konwersji:
KOLOR
dla atrybutu kolor
DATA_WAZNOSCI
dla atrybutu data ważności
Wskazówka
Nasz skrypt dla każdego towaru tworzy listę wszystkich atrybutów i przesyła ją do program JustLabel. Dzięki temu, JustLabel zawsze ma do dyspozycji wszystkie atrybuty dostępne w Comarch Optima dla danego towaru.
Zadbaj o to, aby wszystkie towary, których etykiety chcesz drukować, posiadały atrybuty wymagane przez dany szablon etykiety. Jeżeli wartość danego placeholdera nie zostanie dostarczona przez brak atrybutu w systemie Optima, miejsce na ten atrybut pozostanie puste na wydruku. Odwrotna sytuacja, a więc dodatkowe wartości atrybutów przesłane do JustLabel, które nie są wykorzystywane w szablonie wydruku, są po prostu ignorowane.
Podsumowując, nasz szablon etykiety o nazwie towar
w swojej strukturze posiada placeholdery:
code
– kod produktudescription
– opis produktuname
– nazwaunit
– jednostka logistycznaid
– identyfikatorKOLOR
– placeholder odpowiadający atrybutowi kolor
DATA_WAZNOSCI
– placeholder odpowiadający atrybutowi data ważności
Przykładowy obiekt JSON, przesyłany do JustLabel, może wyglądać następująco:
{
"template": {
"name": "towar",
"options": [
{
"numberOfCopies": 1,
"placeholderValues": {
"code": "KODTOWARU",
"description": "Opis towaru, dla którego drukujemy etykietę",
"name": "Nazwa towaru",
"id": "23",
"unit": "SZT",
"KOLOR": "fioletowy",
"DATA_WAZNOSCI": "81247"
}
}
]
}
}
Wskazówka
Format daty widoczny w przykładowym JSON wynika z wykorzystania przez system Optima formatu daty Clarion, czyli liczby dni, które upłyneły od daty 1800-12-28.
Kolejne wersje naszej funkcji dodatkowej pojawiały się w sekcjach przedstawiających ich pełną postać. Udostępniamy pliki odpowiadające kolejnym wersjom w kolejności ich występowania. Każdy z plików powinien być możliwy do uruchomienia w środowisku Comarch Optima przy spełnieniu podstawowych wymogów. Najważniejszym jest oczywiście uruchomiona instancja JustLabel, nasłuchująca pod odpowiednim adresem i numerem portu, jak opisano w jednej z początkowych sekcji tego samouczka. Drugim warunkiem powinno być w każdym przypadku ustawienie odpowiedniej nazwy kolejki drukowania w skrypcie.
Pliki w kolejności występowania: