Przykłady zastosowań gniazd rozszerzeń

Jesteś tutaj:
< Powrót

Przykłady zastosowań gniazd rozszerzeń

Przykład 1

Chcielibyśmy  kontrolować czy w dokumencie handlowym są ustawione rabat dla pozycji i całego dokumentu. Jeśli rabaty są ustawione dla jakiejkolwiek pozycji – dokument nie może być  zatwierdzony i musi pojawić się stosowne ostrzeżenie.
Aby oprogramować ww. zadanie należy  dodać przed zatwierdzaniem dokumentu w gnieździe o adresie Dokumenty handlowe | Okno danych dokumentu handlowego | Dodawanie lub poprawianie dokumentu | Zatwierdzenie danych dokumentu | Przed funkcję typu „Procedura SQL (3.1)” jak pokazano na Rys. 14
Przekazujemy IdObiektuNadrzednego, które w tym miejscu programu przyjmie wartość  id_dokumentu handlowego. Z listy dostępnych zmiennych uruchomionej klawiszem F4 („Wybierz zmienne aplikacji”) wybieramy IdObiektuNadrzednego i przesuwamy do listy „Wybrane parametry” (Rys. 15) a następnie zatwierdzamy tak przygotowaną listę parametrów.

Rys. 14  Dodawanie funkcji typu Procedura SQL w gnieździe

Rys. 15  Wybór parametrów dla funkcji

Następnie przyciskiem F3 (Rys. 16) uruchamiamy edytor schematu procedury SQL i w proponowanym standardowo schemacie zmieniamy kod SQL tak aby doprowadzić do poniższej postaci (kursywą zaznaczono fragment, który trzeba dopisać do schematu):

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Sprawdzanie_rabatow_dokumentu_handlowego’ and type = ‘P’)

drop procedure MAGSRC_Sprawdzanie_rabatow_dokumentu_handlowego

go

create procedure MAGSRC_Sprawdzanie_rabatow_dokumentu_handlowego

@IdObiektuNadrzednego numeric

as

declare @errmsg varchar(255)

begin

set xact_abort on

set transaction isolation level REPEATABLE READ

begin transaction

if exists (select 1 from pozycja_dokumentu_magazynowego where id_dok_handlowego = @IdObiektuNadrzednego

and rabat2<>0 and rabat<>0)

begin

select @errmsg = ‘Nie mogą być ustawione rabaty na pozycjach i na całym dokumencie!’

goto Error

end

if @@trancount>0 commit transaction

goto Koniec

Error:

raiserror (@errmsg,16,1)

if @@trancount>0 rollback tran

goto Koniec

Koniec:

set transaction isolation level READ COMMITTED

return

end

go

Rys. 16  Schemat procedury SQL proponowany automatycznie po zmianach z przykładu 1

Aby umieścić kod procedury SQL w postaci takiej jak na Rys. 16 należy wykonać skrypt SQL (Ctrl+W) lub skopiować kod z edytora do schowka (Ctrl+C) a następnie np. za pomocą SQL Management Studio uruchomić wykonanie wklejonego ze schowka skryptu.
Na zakończenie pozostaje zatwierdzić formularz dodawania funkcji (Rys. 14) przyciskiem F10 i zamknąć edytor gniazd rozszerzeń. Następnie można od razu (bez konieczności przelogowania się) sprawdzić czy kontrola rabatów działa poprzez wystawienie dokumentu, który nie spełnia przyjętych założeń tzn. zawiera rabat zarówno na pozycji jak i na całym dokumencie. Ostrzeżenie przy zatwierdzaniu dokumentu „Nie mogą być ustawione rabaty na pozycjach i na całym dokumencie!” to informacja, że zadanie zrealizowaliśmy poprawnie.
Istotą całego rozwiązania jest fragment kodu:

Procedura SQL Kopiuj kod

if exists (select 1 from pozycja_dokumentu_magazynowego where id_dok_handlowego = @IdObiektuNadrzednego

and rabat2<>0 and rabat<>0)

begin

select @errmsg = ‘Nie mogą być ustawione rabaty na pozycjach i na całym dokumencie!’

goto Error

end

w którym następuje sprawdzenie czy istnieją pozycje z wypełnionymi rabatami dla pozycji (kolumna rabat) i nagłówka (kolumna rabat2) w tabeli pozycja_dokumentu_magazynowego.

Jeśli ww. warunek jest spełniony ustawiany jest stosowny komunikat i sygnalizowany błąd.
Warto podkreślić, że dzięki specyfice obsługi błędów w gniazdach rozszerzeń raportowanych z procedur SQL (patrz Rozdział 4) nie musimy (chociaż możemy) odbierać błędów i oprogramowywać ich wyświetlania. Gniazdo robi to automatycznie tzn. po odebraniu sygnalizacji błędu wyświetla komunikat o błędzie (zwracany z SQL funkcją raiserror ) i przerywa dalsze wykonanie kodu zarówno umieszonego w gnieździe rozszerzeń jak i standardowego kodu programu, którego wykonanie nastąpiłoby normalnie po zakończeniu kodu gniazda.
W niektórych przypadkach powyższe zachowanie w obsłudze błędów może być jednak pewną przeszkodą np. w sytuacji ustawiania podwójnych rabatów program mógłby wyświetlać stosowny komunikat jednocześnie pozwalając zdecydować czy zatwierdzić dokument czy też nie. Jak zrealizować takie zachowanie programu pokazuje Przykład 2.

Przykład 2
Tym razem  kontrolę czy w dokumencie handlowym są ustawione rabaty dla pozycji i całego dokumentu chcemy nieco rozbudować w stosunku do przykładu z pkt. 6.1. Jeśli rabaty są ustawione dla jakiejkolwiek pozycji program powinien wyświetlić ostrzeżenie o tym fakcie i zadać pytanie czy zatwierdzić dokument.
Chcemy przechwycić komunikat generowany z procedury SQL i wyświetlić pytanie czy zatwierdzić dokument. Aby ominąć standardową obsługę błędu najprościej wykorzystać zmienne @BylBlad oraz @TekstBledu.  Zmienna @BylBlad ustawiana jest na wartość 1 gdy  wystąpi błąd zaś komunikat błędu zapisywany jest w zmiennej @TekstBledu. Jeśli następna funkcja w kolejności do wykonania po funkcji, która wygenerowała błąd (w naszym przypadku jest to procedura SQL) użyje jako parametrów jednej z ww. zmiennych (np. w funkcji IF @BylBlad>0 THEN ELSE lub Komunikat wyświetlający @TekstBledu) wówczas program przyjmuje, że obsługę błędu przejmuje programista gniazda i wykonanie dalszego kodu gniazda nie jest przerywane a także nie jest wyświetlany standardowy komunikat błędu.
W pierwszej kolejności musimy sprawdzić w warunku funkcji IF THEN ELSE czy zmienna błąd @BylBlad > 0 (lub równa 1) co oznacza, że pojawił się błąd.  W tym celu ustawiamy kursor w edytorze gniazd rozszerzeń na funkcji „Sprawdzenie rabatów dokumentu handlowego” i naciskamy przycisk ”Dodaj” przechodząc do formularza edycji funkcji. Wybieramy typ funkcji IF (warunek) THEN ELSE (Rys. 17).

Rys. 17  Definiowanie funkcji IF (warunek) THEN ELSE

Z listy dostępnych zmiennych (klawisz F4) wybieramy zmienną @BylBlad i zatwierdzamy wybór. W polu „Warunek” program automatycznie zaproponuje „@BylBlad>0”. Pozostawiamy taki warunek nie zmieniony (albo możemy wpisać @BylBlad=1). W polu Nazwa podajemy nazwę funkcji np. „Czy są podwójne rabaty?” i zatwierdzamy (klawiszem F10) formularz edycji funkcji w gnieździe rozszerzeń.
W efekcie zawartość gniazda o adresie Dokumenty handlowe | Okno danych dokumentu handlowego | Dodawanie lub poprawianie dokumentu | Zatwierdzenie danych dokumentu | Przed powinna wyglądać jak na Rys. 18.

Rys. 18  Stan gniazda po dodaniu instrukcji IF (warunek) THEN ELSE (Przykład 2)

Teraz musimy dodać pytanie, które powinno wyświetlić się jeśli @BylBlad>0. Dodajemy więc kolejną funkcję i z listy typów funkcji wybieramy „Pytanie” podając parametry jak na Rys. 19.

Rys. 19  Definiowanie funkcji typu Pytanie (Przykład 2)

Zatwierdzamy formularz funkcji. Umieszczenie w treści pytania zmiennej @TekstBledu spowoduje, że wyświetli się w oknie dialogowym odebrany tekst błędu oraz ciąg znaków „Czy zatwierdzić dokument?”.
Z uwagi na fakt, że domyślnie funkcje dodawane są kolejno musimy przesunąć myszą funkcję „Zadaj pytanie czy zatwierdzić dokument?” aby znalazła się ona w części, która wykonuje się po instrukcji warunkowej IF THEN tzn. jeśli warunek jest spełniony (Rys. 20).

Rys. 20  Przeniesienie Pytania przed ELSE (Przykład 2)

W efekcie uzyskamy stan w edytorze gniazd rozszerzeń jak na Rys. 21:

Rys. 21  Stan gniazda po przeniesieniu Pytania (Przykład 2)

Następnie trzeba sprawdzić jak użytkownik odpowiedział na pytanie. W tym celu musimy dodać kolejną instrukcją warunkowego wykonania kodu IF THEN ELSE tym razem sprawdzając warunek @OdpNaPytanie > 0  czyli czy użytkownik  chce zatwierdzić dokument (wcisnął TAK na oknie dialogowym pytania) (Rys. 22).

Rys. 22  Dodawanie funkcji IF (warunek) THEN ELSE (Przykład 2)
Widok w edytorze gniazd rozszerzeń jaki musimy uzyskać pokazuje Rys. 23

Rys. 23  Obsługa odpowiedzi na pytanie (Przykład 2)

Jeśli użytkownik zdecyduje, że nie chce zatwierdzać dokumentu wówczas należy przerwać standardowe zatwierdzanie dokumentu w programie. Możemy to uzyskać korzystając z funkcji Koniec z opcją Przerwij. Funkcję Koniec należy dodać w sekcji „ELSE – Czy zatwierdzić dokument?” ponieważ chcemy aby wykonała się ona gdy użytkownik odpowie, że nie chce zatwierdzać dokumentu (Rys. 24).

Rys. 24  Definiowanie funkcji Koniec z opcją Przerwij (Przykład 2)

Ostatecznie definicja gniazda powinna wyglądać jak na Rys. 25.

Rys. 25  Ostateczna postać definicji (Przykład 2)

Po zamknięciu edytora gniazd rozszerzeń możemy na dowolnym dokumencie handlowym przetestować poprawność działania. Ustawiamy rabat do faktury i dla pozycji po czym zatwierdzamy dokument. Powinniśmy otrzymać komunikat:

Rys. 26  Uruchomienie wykonania gniazda (Przykład 2)

Gdy wybierzemy przycisk Nie program powróci do edycji formularza dokumentu handlowego (faktury). Dokument nie zostanie zatwierdzony dopóki nie usuniemy rabatu na dokumencie lub na wszystkich pozycjach albo nie udzielimy twierdzącej odpowiedzi na powyższe pytanie (Rys. 26).

Przykład 3
W tym przykładzie sprawdzimy czy dla określonego artykułu został przypisany rabat. Dla tego artykułu udzielony rabat nie powinien być  większy niż 5% . Pokażemy w jaki sposób możemy ustawić w takim przypadku rabat na formularzu pozycji dokumentu handlowego (magazynowego) aby automatycznie skorygować wartość rabatu w przypadku przekroczenia progu -5%.
Na początek musimy sprawdzić czy to jest właściwy artykuł (przyjmijmy, że jego ID_ARTYKULU = 7). Sprawdzenia dokonamy przed zatwierdzeniem formularza pozycji dokumentu czyli w gnieździe o adresie Dokumenty handlowe | Okno danych pozycji dokumentu handlowego | Dodawanie lub poprawianie pozycji | Zatwierdzenie danych pozycji | Przed. Umieszczamy w ww. gnieździe instrukcję warunkowego wykonania IF THEN ELSE, w której określamy warunek $IdArtykulu=7 AND $Rabat1<-5 korzystając oczywiście z listy dostępnych zmiennych. Rabat dla pozycji przechowywany jest w bazie danych w polu Rabat1 tabeli POZYCJA_DOKUMENTU_MAGAZYNOWEGO. Pole to przechowuje rabaty ze znakiem minus a narzuty ze znakiem plus – stąd w przykładzie warunek $Rabat1<-5. Na liście zmiennych znajdziemy jego odpowiednik w postaci zmiennej $Rabat1.

Rys. 27   Warunek sprawdzający (Przykład 3)

Teraz pozostaje ustawić rabat na -5 aby nie przekraczać założonej wartości rabatu  i jednocześnie nie zmuszać użytkownika do ręcznego korygowania jego wartości na formularzu. Skorzystamy z procedury SQL, w której dokonamy zmiany rabatu.

Rys. 28  Definicja wywołania procedury SQL ustawiającej rabat na -5 (Przykład 3)

Schemat procedury modyfikujemy do poniższej postaci:

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Ustaw_rabat_na_minus_5’ and type = ‘P’)

drop procedure MAGSRC_Ustaw_rabat_na_minus_5

go

create procedure MAGSRC_Ustaw_rabat_na_minus_5

@Rabat1 decimal(14,4) OUTPUT

as

declare @errmsg varchar(255)

begin

set @Rabat1 = -5

return

end

go

a następnie wykonujemy skrypt SQL aby umieścić procedurę w bazie danych.
Warto zauważyć, że zmienna $Rabat1 jest typu OUTPUT co oznacza, że jej  zmiany dokonane w procedurze SQL zostaną automatycznie przekazane do aplikacji. Dzięki temu nie musimy wykonywać żadnych więcej czynności aby ustawienie rabatu  wpłynęło na dane na formularzu pozycji dokumentu. Wywołanie procedury umieszczamy w sekcji THEN instrukcji warunkowej jak pokazano na Rys. 29.

Rys. 29  Zawartość gniazda po dodaniu procedury SQL (Przykład 3)

Oczywiście wartości pozycji zostaną automatycznie przeliczone na formularzu tzn. zmiana rabatu w gnieździe spowoduje taki sam efekt jakby użytkownik zmienił jego wartość na formularzu. Przed zatwierdzeniem warto jeszcze poinformować użytkownika stosownym komunikatem, że rabat został ustawiony na -5%.

Rys. 30  Definicja komunikatu (Przykład 3)

Warto zauważyć, że w treści komunikatu wyświetlamy zawartość zmiennej $Rabat1 a ponieważ w procedurze SQL ustaliliśmy jej wartość na -5 taka też liczba pojawi się ww. komunikacie. Ostatecznie definicja gniazda powinna wyglądać następująco:

Rys. 31  Ostateczna postać definicji gniazda (Przykład 3)
W ten sposób zabezpieczyliśmy formularz pozycji dokumentu handlowego (magazynowego) przed możliwością zmiany dla wybranego artykułu rabatu poniżej pewnego progu niezależnie od ustawień całej polityki rabatowej w WAPRO Mag.

 

Przykład 4

W tym przykładzie zostanie zaprezentowany sposób obliczania wartości usługi tzn. kalkulacja ceny netto usługi na podstawie zadanych parametrów. Przyjmiemy, że cena usługi wynika z wyliczenia sumy iloczynów ilości roboczogodzin w poszczególnych stawkach i wartości stawek wg wzoru:

Cena sprzedaży netto usługi =     Ilość w stawce1 (rbh) * Stawka1(cena za rbh)
+Ilość w stawce2 (rbh) * Stawka2(cena za rbh)

                                                   +Ilość w stawce3 (rbh) * Stawka3(cena za rbh)

Ceny stawek za roboczogodzinę (rbh) będą wartościami konfiguracyjnymi przechowanymi w tabeli dodatkowej skojarzonej z firmą. Ilości roboczogodzin będą podawane każdorazowo przed dodaniem lub modyfikacją usługi wybieranej z kartoteki asortymentowej podczas rejestracji faktury sprzedaży. Przyjmiemy założenie, że w jednym dokumencie sprzedaży może być tylko jedna pozycja usługowa. Dodatkowo ilości roboczogodzin ujęte w kalkulacji ceny będą zapisane w opisie pozycji i drukowane na fakturze.
Na początek trzeba zdefiniować odpowiednie tabele dodatkowe. Ceny stawek za roboczogodzinę będą przechowywane w tabeli „Stawki za roboczogodzinę” (nazwa techniczna MAGGEN_Stawki_za_roboczogodzine) (Rys. 32) dodanej w grupie tabel Firma.  W okresie od 01-01-2009 do 31-12-2009 przyjmiemy, że będą obowiązywały  następujące wartości stawek:
• Stawka 1 – 20 zł za rbh;
• Stawka 2 – 35 zł za rbh;
• Stawka 3 – 45 zł za rbh.
W tym celu należy dodać zapis w tabeli dodatkowej „Stawki za roboczogodzinę” jak pokazano na Rys. 33.

 

Rys. 32  Struktura tabeli „Stawki za roboczogodzinę” (Przykład 4)

Ilości roboczogodzin potrzebnych do kalkulacji ceny będą przechowywane w tabeli „Rozliczenie usługi” (nazwa techniczna MAGGEN_Rozliczenie_uslugi) zdefiniowanej w grupie tabel Dokument_handlowy.  Ta grupa tabel jest właściwa dla potrzeb przechowania danych potrzebnych do kalkulacji danych usługi albowiem rozliczenie usługi następować ma podczas fakturowania (wystawiania dokumentu handlowego).

Rys. 33  Definicja stawek za roboczogodzinę (Przykład 4)
Tabela „Rozliczenie usługi” przechowuje ilości roboczogodzin w stawkach (Rys. 34).

Rys. 34  Struktura tabeli „Rozliczenie usługi”  (Przykład 4)

W gnieździe rozszerzeń o adresie Dokumenty handlowe | Okno danych pozycji dokumentu handlowego | Dodawanie lub poprawianie pozycji | Otwarcie okna danych pozycji | Przed zdefiniujemy warunek, którego spełnienie będzie aktywowało rozliczenie usługi. Po pierwsze rozliczenie dotyczy konkretnej usługi (odpowiednie id_artykulu z kartoteki asortymentowej programu) a po drugie powinno wywołać się tylko przy fakturowaniu (odpowiedni typ dokumentu handlowego). W instrukcji warunkowej IF THEN ELSE określimy warunek jako $TypDokHandlowego =”FV” AND $IdArtykulu=8 (artykuł o przykładowym id_artykulu równym 8 to usługa podlegająca rozliczeniu). Zapis w gnieździe będzie wyglądał następująco:

Rys. 35  Sprawdzenie warunku aktywującego rozliczenie usługi (Przykład 4)

Teraz czas zdefiniować funkcję pobierającą dane do kalkulacji ceny usługi czyli wywołać formularz tabeli dodatkowej „Rozliczenie usługi” (Rys. 36). Z listy tabel wybieramy MAGGEN_Rozliczenie_uslugi. Program automatycznie oczekuje podania identyfikatora dokumentu handlowego stosownie do grupy tabel gdzie zdefiniowaliśmy „Rozliczenie usługi”. Wybieramy z listy dostępnych zmiennych @IdObiektuNadrzednego. Pojawia się jednak problem jaką wartość umieścić w polu „Id wiersza w tabeli dla potrzeb formularza”. W przypadku gdy dodajemy usługę wystarczy podać wartość 0 (zero). Jednakże w trybie poprawiania pozycji powinniśmy podać taką wartość identyfikatora wiersza jaka została nadana podczas dodawania pozycji. Identyfikator wiersza zawsze nadawany jest automatycznie po dodaniu wiersza do tabeli dodatkowej (jest to cecha konstrukcyjna tabel dodatkowych) wobec czego powinniśmy odczytać wartość identyfikatora wiersza i zapisać go w zmiennej (np. @Wynik2 ), którą podamy w polu  „Id wiersza w tabeli dla potrzeb formularza”. Odczytu wartości identyfikatora i jego zapisu w zmiennej @Wynik2 najlepiej dokonać za pomocą procedury SQL.

Rys. 36  Definicja wywołania formularza tabeli „Rozliczenie usługi” (Przykład 4)

Procedura SQL pobierze wartość kolumny IDENTYFIKATOR tabeli MAGGEN_Rozliczenie_uslugi dla wiersza skojarzonego z daną fakturą (dokumentem handlowym) i przepisze jego wartość do zmiennej @Wynik2. Jeśli taki wiersz nie istnieje (tzn. dodajemy dopiero zapis w tabeli dodatkowej) wówczas procedura wykona proste przypisanie @Wynik2 = 0.
Na początek musimy zdefiniować parametry wywołania procedury (Rys. 37) – będzie potrzebna zmienna @Wynik2 (bo w niej zapiszemy wartość szukanego identyfikatora) a także @IdObiektuNadrzednego (bo zapisy tabeli dodatkowej „Rozliczenie usługi” skojarzone są z tabelą DOKUMENT_HANDLOWY poprzez relację ID_NADRZEDNEGO = ID_DOKUMENTU_HANDLOWEGO (patrz – Struktura tabeli … – Rys. 34))

Rys. 37  Definicja wywołania procedury SQL pobierającej identyfikator tabeli (Przykład 4)

Zmodyfikujemy zaproponowany dla procedury schemat zapisując następująco algorytm pobierania identyfikatora wiersza tabeli:

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Pobierz_wiersz_tabeli_Rozliczenia_uslugi’

and type = ‘P’)

drop procedure MAGSRC_Pobierz_wiersz_tabeli_Rozliczenia_uslugi

go

create procedure MAGSRC_Pobierz_wiersz_tabeli_Rozliczenia_uslugi

@IdObiektuNadrzednego numeric, @Wynik2 varchar(255) OUTPUT

as

begin

select top 1 @Wynik2 = IDENTYFIKATOR

from MAGGEN_Rozliczenie_uslugi

where ID_NADRZEDNEGO = @IdObiektuNadrzednego

if @@rowcount = 0

set @Wynik2 = 0

return

end

go

Ostatecznie funkcje umieszczone w gnieździe powinny wyglądać jak na Rys. 38.

Rys. 38  Poprawna definicja wywołania formularza tabeli „Rozliczenie usługi” (Przykład 4)

Warto zauważyć, że opisana sekwencja „procedura SQL pobierająca wiersz tabeli – formularz tabeli” gwarantuje nam, że dla danego dokumentu
(obiektu nadrzędnego) w tabeli dodatkowej będzie istniał tylko jeden zapis. Przepisanie do zmiennej @Wynik2 identyfikatora wiersza tabeli, który następnie podawany jest do funkcji wywołującej formularz powoduje uruchomienie  formularza w trybie poprawiania. Możemy więc zmienić zawartość pól w wierszu tabeli ale nie możemy dodać drugiego zapisu w „Rozliczeniu usługi” dla danego dokumentu handlowego za pomocą ww. funkcji. W efekcie, opisana technika realizuje implementację relacji „jeden do jeden” między wierszem tabeli DOKUMENT_HANDLOWY i wierszem tabeli MAGGEN_Rozliczenie_uslugi.
Mając dane potrzebne do ustalenia ceny usługi pozostaje dokonać stosownych przeliczeń, które wykonamy po zatwierdzeniu formularza tabeli „Rozliczenie usługi”. Ponownie posłużymy się procedurą SQL.
W procedurze będziemy potrzebowali @IdObiektuHandlowego (id_dokumentu_handlowego), @IdFirmy (aby pobrać wartości stawek z tabeli dodatkowej „Stawki za roboczogodzinę” przypisanej do tabeli Firma) oraz zmienną @CenaNettoPLN, którą musimy wyliczyć (Rys. 39).

Rys. 39  Definicja procedury obliczenia ceny usługi (Przykład 4)

Implementację algorytmu kalkulacji ceny usługi można sprowadzić do jednego zapytania SQL:

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Oblicz_cene_uslugi’ and type = ‘P’)

drop procedure MAGSRC_Oblicz_cene_uslugi

go

create procedure MAGSRC_Oblicz_cene_uslugi

@IdFirmy numeric, @IdObiektuNadrzednego numeric, @CenaNettoPLN decimal(14,4) OUTPUT

as

declare @errmsg varchar(255)

begin

select top 1 @CenaNettoPLN = r.Ilosc_rbh_Stawka_1 * s.Stawka_1

                             + r.Ilosc_rbh_Stawka_2 * s.Stawka_2

                             + r.Ilosc_rbh_Stawka_3 * s.Stawka_3

from MAGGEN_Rozliczenie_uslugi r

,MAGGEN_Stawki_za_roboczogodzine s

                             where r.ID_NADRZEDNEGO = @IdObiektuNadrzednego

                             and r.Data_sys between s.Data_od and s.Data_do

                             and s.ID_NADRZEDNEGO = @IdFirmy

order by s.Data_do,s.Data_od

return

end

go

 

Warto zwrócić uwagę, że w tabeli stawek za roboczogodzinę określiliśmy daty obowiązywania stawek. W zapytaniu SQL wyliczającym cenę wykorzystujemy ten fakt wybierając te stawki, które spełniają kryterium daty (warunek r.Data_sys between s.Data_od and s.Data_do ). W przypadku gdyby okresy dat się pokrywały (lub zachodziły na siebie w przedziałach „od-do”) czyli zapytanie zwróciłoby kilka wierszy zawierających stawki, wówczas wybierzemy tylko jedną (select top 1) sortując wiersze wg s.Data_do,s.Data_od. Z kolei kolumna r.Data_sys wypełniana jest automatycznie dla dodanego wiersza tabeli dodatkowej i zawiera datę dodania wiersza. W naszym przykładzie oznacza to datę wprowadzenia ilości roboczogodzin do tabeli „Rozliczenie usługi”. Tym sposobem możemy bardzo łatwo powiązać stawki za roboczogodziny z ilością roboczogodzin potrzebnych do kalkulacji ceny usługi.
Ostatecznie w gnieździe powinniśmy uzyskać widok definicji funkcji jak na Rys. 40.

Rys. 40  Pobranie danych do kalkulacji ceny i jej obliczenie (Przykład 4)

Praktycznie na tym można by zakończyć implementację rozwiązania w naszym przykładzie ponieważ cenę usługi mamy obliczoną  i przekazywaną do formularza pozycji dokumentu handlowego. Pozostaje jednak oprogramować sytuacje, w których użytkownik może podjąć działania inne niż spodziewane np. :
a) użytkownik anuluje formularz tabeli „Rozliczenie usługi” zamiast zatwierdzić;
b) użytkownik podał zerowe ilości roboczogodzin – cena usługi wyniesie więc zero;
c) użytkownik pomimo obliczenia ceny usługi zmodyfikuje ją ręcznie przed zatwierdzeniem formularza pozycji dokumentu handlowego a do tego nie chcemy dopuścić;
Jeśli użytkownik anuluje formularz powinniśmy przynajmniej wywołać ostrzeżenie o konieczności określenia ilości i akceptacji roboczogodzin. Aby stwierdzić czy użytkownik anulował formularz wystarczy sprawdzić w warunku IF THEN ELSE czy zmienna @Status = 0. Potem w sekcji THEN warunku umieścimy komunikat o treści „Usługa może być dodana tylko jeśli zostanie zatwierdzony formularz roboczogodzin!” a następnie funkcję KONIEC z opcją Przerwij aby zatrzymać proces wystawiania pozycji dokumentu handlowego (podobnie jak w Przykład 2). W efekcie uzyskamy widok definicji funkcji w gnieździe jak na Rys. 41.

Rys. 41  Zawartość gniazda po dodaniu obsługi anulowania formularza (Przykład 4)

Następnie należy zabezpieczyć się przed sytuacją podania zerowej ilości roboczogodzin przez użytkownika. Najprościej wykorzystać do tego mechanizm obsługi błędów w gniazdach tzn. w przypadku kiedy wyliczona cena będzie równa 0 wygenerujemy błąd w procedurze SQL obliczającej ilość. Brak jawnej obsługi tego błędu w gnieździe spowoduje przechwycenie tego błędu przez standardowe mechanizmy obsługi błędów gniazd, wyświetlenie komunikatu i przerwanie dalszego dodawania pozycji dokumentu handlowego. Wystarczy wobec tego zmodyfikować kod procedury obliczenia ceny usługi dodając warunek na sprawdzenie ceny i sygnalizację błędu:

Procedura SQL Kopiuj kod

if isnull(@CenaNettoPLN,0.0) = 0.0

begin

set @errmsg = ‘Cena usługi wynosi 0! Nie można dodać usługi bez podania ceny wynikającej z rozliczenia roboczogodzin wykonania usługi!’

raiserror (@errmsg,16,1)

end

 

Procedura SQL będzie więc wyglądała następująco:

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Oblicz_cene_uslugi’ and type = ‘P’)

drop procedure MAGSRC_Oblicz_cene_uslugi

go

create procedure MAGSRC_Oblicz_cene_uslugi

@IdFirmy numeric, @IdObiektuNadrzednego numeric, @CenaNettoPLN decimal(14,4) OUTPUT

as

declare @errmsg varchar(255)

begin

select top 1 @CenaNettoPLN = r.Ilosc_rbh_Stawka_1 * s.Stawka_1

+ r.Ilosc_rbh_Stawka_2 * s.Stawka_2

+ r.Ilosc_rbh_Stawka_3 * s.Stawka_3

from MAGGEN_Rozliczenie_uslugi r

,MAGGEN_Stawki_za_roboczogodzine s

where r.ID_NADRZEDNEGO = @IdObiektuNadrzednego

and r.Data_sys between s.Data_od and s.Data_do

and s.ID_NADRZEDNEGO = @IdFirmy

order by s.Data_do,s.Data_od

if isnull(@CenaNettoPLN,0.0) = 0.0

begin

set @errmsg = ‘Cena usługi wynosi 0! Nie można dodać usługi bez podania ceny wynikającej z rozliczenia roboczogodzin wykonania usługi!’

raiserror (@errmsg,16,1)

end

return

end

go

 

Pozostało zabezpieczyć formularz pozycji dokumentu handlowego przed ręczną modyfikacją ceny przez użytkownika. W praktyce musimy przed zatwierdzeniem formularza pozycji ponownie obliczyć cenę i jeśli będzie inna niż wyliczona zablokować możliwość dodania lub poprawienia pozycji dokumentu. W tym celu w gnieździe Dokumenty handlowe | Okno danych pozycji dokumentu handlowego | Dodawanie lub poprawianie pozycji | Zatwierdzenie danych pozycji | Przed musimy umieścić wywołanie funkcji obliczania ceny usługi oczywiście po sprawdzeniu warunku analogicznego jak na Rys. 35. Mamy więc stan zapisów w gnieździe jak na Rys. 42:

Rys. 42  Przy zatwierdzaniu danych pozycji trzeba sprawdzić cenę (Przykład 4)

Wykorzystaliśmy ponownie wcześniej zdefiniowaną procedurę SQL do obliczenia ceny jednakże przydałoby się w przypadku kiedy cena wyliczona różni się od tej na formularzu pozycji dokumentu pojawił się stosowny komunikat. Aby sprawdzić czy ceny się różnią należy oczywiście zastosować warunek IF THEN ELSE. Pojawia się jednak problem – procedura obliczania ceny automatycznie podstawia wyliczoną wartość do zmiennej $CenaNettoPLN skojarzonej z wartością na formularzu wobec czego wartość ceny z formularza wprowadzona przez użytkownika zostaje nadpisana. Należy zatem przed uruchomieniem procedury obliczania ceny przepisać do zmiennej @Wynik1 (lub @Wynik2 lub @Wynik3) wartość $CenaNettoPLN a następnie po wyliczeniu ceny porównać czy @Wynik1 = $CenaNettoPLN. Przepisanie do zmiennej można zrealizować bardzo prostą procedurą SQL:

Procedura SQL Kopiuj kod

if exists (select 1 from sysobjects where name = ‘MAGSRC_Przepisanie_ceny_do_wynik1’ and type = ‘P’)

drop procedure MAGSRC_Przepisanie_ceny_do_wynik1

go

create procedure MAGSRC_Przepisanie_ceny_do_wynik1

@CenaNettoPLN decimal(14,4) OUTPUT, @Wynik1 varchar(255) OUTPUT

as

declare @errmsg varchar(255)

begin

set @Wynik1 = @CenaNettoPLN

end

go

której definicję wywołania obrazuje Rys. 43:

Rys. 43  Procedura przepisania ceny do zmiennej @Wynik1 (Przykład 4)

Teraz możemy dodać stosowny warunek i komunikat. Jeśli cena @Wynik1 = $CenaNettoPLN to kontynuujemy zatwierdzanie pozycji zaś w przeciwnym przypadku (czyli po ELSE) wyświetlimy komunikat o treści np. „Cena nie może być modyfikowana na formularzu ! Cena usługi wynosi $CenaNettoPLN”. Warto zauważyć, że nie musimy po komunikacie przerwać wykonania kodu bo w zmiennej $CenaNettoPLN jest już cena wyliczona wobec czego zostanie ona automatycznie przepisana na formularz i zapisana w pozycji dokumentu handlowego. Ostatecznie zawartość gniazda Dokumenty handlowe | Okno danych pozycji dokumentu handlowego | Dodawanie lub poprawianie pozycji | Zatwierdzenie danych pozycji | Przed dla naszego przykładu będzie wyglądać jak na Rys. 44.

Rys. 44  Obsługa zatwierdzania pozycji (Przykład 4)