wtorek, 9 listopada 2010

Stan magazynu - wartość magazynowa

Dawno nie było żadnego wpisu, więc trzeba to nadrobić. Tradycyjne na forum poświęconym XL http://cdn.3lance.pl/viewtopic.php?f=13&t=1451 natrafiłem na ciekawy temat. Co więcej sam ostatnio się z nim zetknąłem i przygotowałem rozwiązanie. Zaznaczam, iż na pewno nie jest ono do końca optymalne jeśli chodzi o czas wykonania, aczkolwiek dla moich potrzeb działa bez zarzutu - dla 150 000 towarów wykonuje się w 1m 46 sekund - idzie przeżyć :D

Na początek procedura obliczająca wartość magazynu ze względu na ilości magazynowe:
CREATE PROCEDURE [CDN].[_Magazyn_Stan] (@Data INT, @magazyn INT )
AS
BEGIN

SELECT     MAX(twr_kod) AS Kod,MAX(twr_nazwa) AS Nazwa,MAX(twr_jm) AS JM,
ISNULL(CAST(SUM( CASE man_gidtyp when 1089 then CDN._Dostawy_na_dzien(dst_GIDnumer, dst_GIDtyp, @Data,1)
ELSE (-CDN._Dostawy_na_dzien(dst_GIDnumer, dst_GIDtyp, @Data,1)) END 
/ CASE CDN._Dostawy_na_dzien(dst_GIDnumer, dst_GIDtyp, @Data,2) WHEN 0 THEN mas_ilosc ELSE CDN._Dostawy_na_dzien(dst_GIDnumer, dst_GIDtyp, @Data,2)
END * mas_ilosc) AS DECIMAL(15,2)),0) AS Wartosc,
ISNULL(SUM( CASE man_gidtyp WHEN 1089 THEN mas_ilosc ELSE (mas_ilosc - 2*mas_ilosc) END ),0) AS Ilosc
FROM         CDN.MagNag INNER JOIN
                      CDN.MagElem ON CDN.MagNag.MaN_GIDNumer = CDN.MagElem.MaE_GIDNumer INNER JOIN
                      CDN.MagSElem ON CDN.MagElem.MaE_GIDLp = CDN.MagSElem.MaS_GIDLp AND
                      CDN.MagElem.MaE_GIDNumer = CDN.MagSElem.MaS_GIDNumer AND CDN.MagElem.MaE_GIDTyp = CDN.MagSElem.MaS_GIDTyp INNER JOIN
                      CDN.Dostawy ON CDN.MagSElem.MaS_DstNumer = CDN.Dostawy.Dst_GIDNumer AND
                      CDN.MagSElem.MaS_DstTyp = CDN.Dostawy.Dst_GIDTyp INNER JOIN
                      CDN.Magazyny ON CDN.MagNag.MaN_MagDTyp = CDN.Magazyny.MAG_GIDTyp AND
                      CDN.MagNag.MaN_MagDNumer = CDN.Magazyny.MAG_GIDNumer INNER JOIN
                      CDN.TwrKarty ON CDN.MagElem.MaE_TwrNumer = CDN.TwrKarty.Twr_GIDNumer AND CDN.MagElem.MaE_TwrTyp = CDN.TwrKarty.Twr_GIDTyp
WHERE twr_typ IN (1,2) AND man_magdnumer = @magazyn AND man_data3 <= @Data
GROUP BY twr_gidnumer
ORDER BY MAX(twr_kod)
END

Jak widać wykorzystuje w niej odwołania do funkcji. Dlaczego ? Ponieważ pola w tabeli dostaw podlegają edycji ze względu na korekty i często zdarza się, że obecnie dst_ilosc = 0, a jeszcze tydzień temu nie mieliśmy korekty i zamiast 0 mieliśmy 5. Chodzi więc o to, aby otrzymać rzeczywistą wartość magazynu na dany dzień - nie interesuje na korekta z marca, skoro sprawdzamy stan dla stycznia - wtedy tej korekty przecież nie było. Funkcja ma za zadanie obliczyć tak wartość dostawy na dany dzień, jak i ilość jaka z niej wynika. Poniżej kod funkcji:
CREATE FUNCTION [CDN].[_Dostawy_na_dzien] (@Dst_GIDNumer int, @Dst_GIDTyp int,  @Data int, @Opcja int)
RETURNS DECIMAL(15,4)
AS
BEGIN

DECLARE @Wynik DECIMAL(15,4);

IF @Opcja = 1 
BEGIN
SELECT    @Wynik = isnull(sum(TrS_KosztKsiegowy),0)
FROM         CDN.TraSElem INNER JOIN
                      CDN.TraElem ON CDN.TraSElem.TrS_GIDNumer = CDN.TraElem.TrE_GIDNumer AND CDN.TraSElem.TrS_GIDLp = CDN.TraElem.TrE_GIDLp INNER JOIN
                      CDN.TraNag ON CDN.TraElem.TrE_GIDNumer = CDN.TraNag.TrN_GIDNumer INNER JOIN
                      CDN.Dostawy ON CDN.TraSElem.TrS_DstTyp = CDN.Dostawy.Dst_GIDTyp AND CDN.TraSElem.TrS_DstNumer = CDN.Dostawy.Dst_GIDNumer
WHERE Dst_GIDNumer = @Dst_GIDNumer
                and Dst_GIDTyp = @Dst_GIDTyp
                and TrN_GIDTyp in (1489, 1617, 1521)


SELECT    @Wynik = @Wynik + isnull(sum(TrS_KosztKsiegowy),0)
FROM         CDN.TraSElem INNER JOIN
                      CDN.TraElem ON CDN.TraSElem.TrS_GIDNumer = CDN.TraElem.TrE_GIDNumer AND CDN.TraSElem.TrS_GIDLp = CDN.TraElem.TrE_GIDLp INNER JOIN
                      CDN.TraNag ON CDN.TraElem.TrE_GIDNumer = CDN.TraNag.TrN_GIDNumer INNER JOIN
                      CDN.Dostawy ON CDN.TraSElem.TrS_DstTyp = CDN.Dostawy.Dst_GIDTyp AND CDN.TraSElem.TrS_DstNumer = CDN.Dostawy.Dst_GIDNumer
WHERE Dst_GIDNumer = @Dst_GIDNumer AND Dst_GIDTyp = @Dst_GIDTyp AND TrN_GIDTyp IN (1497, 1625, 1529) AND TrN_Data3 <= @Data
END
-----------------------------------------------------------------------------------------------------------------------------
ELSE
BEGIN
SELECT    @Wynik = isnull(sum(TrS_Ilosc),0)
FROM         CDN.TraSElem INNER JOIN
                      CDN.TraElem ON CDN.TraSElem.TrS_GIDNumer = CDN.TraElem.TrE_GIDNumer AND CDN.TraSElem.TrS_GIDLp = CDN.TraElem.TrE_GIDLp INNER JOIN
                      CDN.TraNag ON CDN.TraElem.TrE_GIDNumer = CDN.TraNag.TrN_GIDNumer INNER JOIN
                      CDN.Dostawy ON CDN.TraSElem.TrS_DstTyp = CDN.Dostawy.Dst_GIDTyp AND CDN.TraSElem.TrS_DstNumer = CDN.Dostawy.Dst_GIDNumer
WHERE Dst_GIDNumer = @Dst_GIDNumer
                AND Dst_GIDTyp = @Dst_GIDTyp
                AND TrN_GIDTyp in (1489, 1617, 1521)


SELECT    @Wynik = @Wynik + isnull(sum(TrS_Ilosc),0)
FROM         CDN.TraSElem INNER JOIN
                      CDN.TraElem ON CDN.TraSElem.TrS_GIDNumer = CDN.TraElem.TrE_GIDNumer AND CDN.TraSElem.TrS_GIDLp = CDN.TraElem.TrE_GIDLp INNER JOIN
                      CDN.TraNag ON CDN.TraElem.TrE_GIDNumer = CDN.TraNag.TrN_GIDNumer INNER JOIN
                      CDN.Dostawy ON CDN.TraSElem.TrS_DstTyp = CDN.Dostawy.Dst_GIDTyp AND CDN.TraSElem.TrS_DstNumer = CDN.Dostawy.Dst_GIDNumer
WHERE Dst_GIDNumer = @Dst_GIDNumer AND Dst_GIDTyp = @Dst_GIDTyp AND TrN_GIDTyp IN (1497, 1625, 1529) AND TrN_Data3 <= @Data

END

RETURN (@Wynik)
END


Złożone razem daje poprawny stan MAGAZYNU na dzień - a nie stan handlowy. W wolnej chwili postaram się usprawnić to zapytanie tak, aby działało szybciej :)