LIFO MALİYET

USE tempdb
SET NOCOUNT ON
/**********************************************************************************************************************
Hesaplama Türü : Lıfo Maliyet
Sorguyu Yazan : VEDAT ÖZER

Tablo İsimleri

Stok Hareketler Tablosu   : STOK_DETAYLI
Stok Giriş Çıkış Eşlemesi : LIFOESLEME
Envanter Tablosu          : LIFOENVANTER
Negatif Bilgiler          : STOKNEGATIF

Stok Negatife düştüğünde maliyet hesabı yapılmıyacak. Eksiğe düştüğü tarihten itibaren aşağıdaki tabloya
yazacaz. 
**********************************************************************************************************************/
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'STOKNEGATIF') AND OBJECTPROPERTY(id,'IsUserTable')=1)
DROP TABLE [dbo].STOKNEGATIF
CREATE TABLE STOKNEGATIF ([KODU] varchar(150) NOT NULL,[ADI] varchar(250) NOT NULL,[TARIH] datetime NOT NULL,[AMBAR] varchar(50) NOT NULL,
[GIRISCIKIS] VARCHAR(10),[FISTURU] VARCHAR(50),[MIKTAR] FLOAT NOT NULL,KALAN DECIMAL(38,2))
CREATE CLUSTERED INDEX TANIMLAMA ON STOKNEGATIF(KODU,AMBAR,TARIH)
CREATE INDEX DENEME ON STOKNEGATIF(KODU)

/**********************************************************************************************************************
Maliyet hesaplamaların doğru bir şekilde yapmamız için Giriş ve Çıkış hareketlerin ilişkilendirmemiz lazım.
Aşağıdaki tabloya giriş ve çıkış bilgilerini yazacaz.
**********************************************************************************************************************/
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'LIFOESLEME') AND OBJECTPROPERTY(id,'IsUserTable')=1) BEGIN
DROP TABLE [dbo].LIFOESLEME
END
CREATE TABLE [dbo].LIFOESLEME ([KODU] varchar(150),[AMBAR] varchar(50),GIRISID INT,CIKISID INT,MIKTAR FLOAT,GIRISFIYAT FLOAT,
CIKISFIYAT FLOAT,ALIMTARIHI DATE,SATISTARIHI DATE)
CREATE CLUSTERED INDEX ITANIMLAMADX11 ON LIFOESLEME([KODU],AMBAR,ALIMTARIHI,GIRISID)
CREATE INDEX KART ON LIFOESLEME([KODU])

/**********************************************************************************************************************
Ambarlara göre elimizdeki stokğun bilgilerini yazacağımız tabloyu oluşturuyoruz.
**********************************************************************************************************************/
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'LIFOENVANTER') AND OBJECTPROPERTY(id,'IsUserTable')=1) BEGIN
DROP TABLE [dbo].LIFOENVANTER
END
CREATE TABLE [dbo].LIFOENVANTER ([KODU] varchar(150),[AMBAR] varchar(50),GIRISID INT,MIKTAR FLOAT,GIRISFIYAT FLOAT,ALIMTARIHI DATE)
CREATE CLUSTERED INDEX IDX11 ON LIFOENVANTER([KODU],AMBAR,ALIMTARIHI,GIRISID)
CREATE INDEX DENEME ON LIFOENVANTER([KODU])

/**********************************************************************************************************************
Aşağıdaki alanlarda maliyet hesabını yapacağımız satırları ekliyoruz.
**********************************************************************************************************************/

IF EXISTS (SELECT * FROM sys.tables WHERE name= 'STOK_DETAYLI') BEGIN
DROP TABLE STOK_DETAYLI
END
;
CREATE TABLE
[dbo].[STOK_DETAYLI](
[ID] [int] IDENTITY(1,1) NOT NULL,
[KODU] varchar(150) NOT NULL,
[ADI] varchar(250) NOT NULL,
[TARIH] datetime NOT NULL,
[AMBAR] varchar(50) NOT NULL,
[GIRISCIKIS] VARCHAR(10),
[FISTURU] VARCHAR(60),
[MIKTAR] FLOAT NOT NULL,
[BIRIMFIYAT] FLOAT ,
[TUTAR] FLOAT,
[MALIYET] FLOAT)
CREATE CLUSTERED INDEX STOK ON [STOK_DETAYLI](KODU,TARIH,AMBAR)
CREATE INDEX SIRA ON [STOK_DETAYLI](KODU)

INSERT INTO [STOK_DETAYLI]
SELECT 'STOK.001','Deneme Stok','2018-01-01 09:34:00.000','Merkez','GIRIS','Devir','15','1.25','18.75','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-02 09:35:00.000','Merkez','GIRIS','SatınAlma','15','2','30','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-02 09:36:00.000','Merkez','CIKIS','Satış','22','3','66','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-02 09:38:00.000','Merkez','GIRIS','SatınAlma','20','1.75','35','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-03 10:35:00.000','Antalya','GIRIS','SatınAlma','15','2.50','37.50','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-03 10:36:00.000','Antalya','CIKIS','Satış','12','3','36','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-04 11:35:00.000','Merkez','GIRIS','SatınAlma','20','1.75','35','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-04 11:40:00.000','Merkez','CIKIS','Satış','15','3','45','0' UNION ALL
SELECT 'STOK.001','Deneme Stok','2018-01-05 11:41:00.000','Merkez','GIRIS','SatınAlma','8','2.50','20','0'  UNION ALL
SELECT 'STOK.002','Deneme','2018-02-01 09:34:00.000','Merkez','GIRIS','Devir','15','1.25','18.75','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-02 09:35:00.000','Merkez','GIRIS','SatınAlma','15','2','30','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-02 09:36:00.000','Merkez','CIKIS','Satış','22','3','66','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-02 09:38:00.000','Merkez','GIRIS','SatınAlma','20','1.75','35','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-03 10:35:00.000','Antalya','GIRIS','SatınAlma','15','2.50','37.50','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-03 10:36:00.000','Antalya','CIKIS','Satış','12','3','36','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-04 11:35:00.000','Merkez','GIRIS','SatınAlma','20','1.75','35','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-04 11:40:00.000','Merkez','CIKIS','Satış','15','3','45','0' UNION ALL
SELECT 'STOK.002','Deneme','2018-02-05 11:41:00.000','Merkez','GIRIS','SatınAlma','8','2.50','20','0'




/**********************************************************************************************************************
Artık Gerekli maliyet hesaplamalarına başlıyacağız. Numaralı şekilde gideceğiz
**********************************************************************************************************************/

/**********************************************************************************************************************
1) Gerçek tabloda olan bütün verileri Geçiçi tabloya aktarıyoruz.
********************************************************************************************************************/
IF(OBJECT_ID('tempdb..##VERIDATA') IS NOT NULL) BEGIN DROP TABLE ##VERIDATA END
SELECT *,CAST(0 AS DECIMAL(38,2)) AS KALAN INTO ##VERIDATA
FROM [STOK_DETAYLI] WITH(NOLOCK, INDEX(STOK))
CREATE INDEX STOK ON ##VERIDATA(KODU)

IF(OBJECT_ID('tempdb..##LIFOESLEME') IS NOT NULL) BEGIN DROP TABLE ##LIFOESLEME END
CREATE TABLE ##LIFOESLEME ([KODU] varchar(150),[AMBAR] varchar(50),GIRISID INT,CIKISID INT,MIKTAR FLOAT,GIRISFIYAT FLOAT,
CIKISFIYAT FLOAT,ALIMTARIHI DATE,SATISTARIHI DATE)
CREATE CLUSTERED INDEX ITANIMLAMADX11 ON ##LIFOESLEME([KODU],AMBAR,ALIMTARIHI,GIRISID)
CREATE INDEX KART ON ##LIFOESLEME(GIRISID)

/**********************************************************************************************************************
2) Geçiçi tabloda Yürüyen bakiye işlemini yapıyoruz. Eksi değer veren varmı diye
Normal şartlarda 2012 versiyonda Partıtıon By fonksiyonu ile gidilebilir.
**********************************************************************************************************************/
DECLARE @STOKKODU VARCHAR(150),@AMBAR VARCHAR(50),@KALAN DECIMAL(38,2)
SET @STOKKODU = ''
SET @AMBAR = ''
SET @KALAN =0
UPDATE a
SET @KALAN = KALAN = CASE WHEN GIRISCIKIS ='GIRIS' THEN MIKTAR ELSE -MIKTAR END  + CASE WHEN KODU = @STOKKODU AND  AMBAR = @AMBAR THEN
@KALAN ELSE 0 END,
@STOKKODU = KODU,
@AMBAR =AMBAR
FROM ##VERIDATA  a WITH (TABLOCKX,INDEX(STOK))
OPTION (MAXDOP 1);

/**********************************************************************************************************************
3) Döngü ile verileri yazacağımız geçiçi tabloyu oluşturuyoruz.
**********************************************************************************************************************/
IF(OBJECT_ID('tempdb..#GECICI') IS NOT NULL) BEGIN DROP TABLE #GECICI END
CREATE TABLE #GECICI (ID INT,TARIH DATETIME,[KODU] varchar(150),[AMBAR] varchar(50),GIRISCIKIS VARCHAR(15),BIRIMFIYAT FLOAT,MIKTAR DECIMAL(38,2))
CREATE CLUSTERED INDEX SIRALAMA ON #GECICI([KODU],AMBAR,TARIH)
CREATE INDEX VEDAT ON #GECICI([KODU],ID)

/**********************************************************************************************************************
4) Negatif olan stokları toplu olarak tabloya aktarıyoruz.
**********************************************************************************************************************/
TRUNCATE TABLE STOKNEGATIF
INSERT INTO STOKNEGATIF
SELECT KODU,ADI,TARIH,AMBAR,GIRISCIKIS,FISTURU,MIKTAR,KALAN FROM ##VERIDATA
WHERE KALAN<0

/**********************************************************************************************************************
5) Malıyet Hesabını yapıyoruz.
**********************************************************************************************************************/

DECLARE @KODU VARCHAR(150) , @AMBARR VARCHAR(50)
DECLARE STOK CURSOR FOR
SELECT DISTINCT KODU,AMBAR FROM ##VERIDATA
ORDER BY KODU,AMBAR

OPEN STOK
FETCH NEXT FROM STOK
INTO @KODU,@AMBAR
WHILE @@FETCH_STATUS=0
BEGIN
 
DELETE #GECICI      WITH(TABLOCK)   WHERE KODU=@KODU AND AMBAR=@AMBAR

IF EXISTS (SELECT COUNT(*) FROM STOKNEGATIF WITH(NOLOCK,INDEX(DENEME)) WHERE KODU=@KODU AND AMBAR=@AMBAR AND KALAN<0) BEGIN

DECLARE @ID INT,@TARIH DATETIME,@GIRISCIKIS VARCHAR(10),@FISTUR VARCHAR(60),@MIKTAR DECIMAL(38,2),@BIRIMFIYAT DECIMAL(38,2)
DECLARE @GIRENID INT,@GIRENTARIH DATETIME,@GIRENFISTUR VARCHAR(60),@GIRENMIKTAR DECIMAL(38,2),@GIRENBIRIMFIYAT DECIMAL(38,2)
DECLARE LIFOMALIYET CURSOR FOR
SELECT ID,TARIH,GIRISCIKIS,FISTURU,MIKTAR,BIRIMFIYAT FROM ##VERIDATA
WHERE KODU=@KODU AND AMBAR=@AMBAR
OPEN LIFOMALIYET
FETCH NEXT FROM LIFOMALIYET INTO @ID,@TARIH,@GIRISCIKIS,@FISTUR,@MIKTAR,@BIRIMFIYAT
WHILE @@FETCH_STATUS=0 BEGIN

IF @GIRISCIKIS = 'GIRIS' BEGIN
INSERT INTO #GECICI
VALUES(@ID,@TARIH,@KODU,@AMBAR,@GIRISCIKIS,@BIRIMFIYAT,@MIKTAR)
END

  IF @GIRISCIKIS='CIKIS'  
    BEGIN

     WHILE (1=1)

  BEGIN

  SELECT TOP 1 @GIRENID=ID,@GIRENTARIH=TARIH,@GIRENMIKTAR=MIKTAR,@GIRENBIRIMFIYAT=BIRIMFIYAT  FROM #GECICI
  WHERE KODU=@KODU AND AMBAR=@AMBAR AND GIRISCIKIS='GIRIS' AND MIKTAR>0
  ORDER BY TARIH DESC
  IF @GIRENMIKTAR>=@MIKTAR
      BEGIN
  INSERT INTO ##LIFOESLEME (KODU,AMBAR,GIRISID,CIKISID,MIKTAR,GIRISFIYAT,CIKISFIYAT,ALIMTARIHI,SATISTARIHI)
  VALUES(@KODU,@AMBAR,@GIRENID,@ID,@MIKTAR,@GIRENBIRIMFIYAT,@BIRIMFIYAT,@GIRENTARIH,@TARIH)
  UPDATE #GECICI WITH(TABLOCK)  SET MIKTAR = COALESCE(MIKTAR,0) - COALESCE(@MIKTAR,0) WHERE ID=@GIRENID
  BREAK
      END
  ELSE
      BEGIN
  INSERT INTO ##LIFOESLEME (KODU,AMBAR,GIRISID,CIKISID,MIKTAR,GIRISFIYAT,CIKISFIYAT,ALIMTARIHI,SATISTARIHI)
  VALUES(@KODU,@AMBAR,@GIRENID,@ID,@GIRENMIKTAR,@GIRENBIRIMFIYAT,@BIRIMFIYAT,@GIRENTARIH,@TARIH)
      UPDATE #GECICI WITH(TABLOCK) SET MIKTAR = COALESCE(MIKTAR,0) - COALESCE(@GIRENMIKTAR,0) WHERE ID=@GIRENID
  SET @MIKTAR = @MIKTAR - @GIRENMIKTAR
  END
 
  END

               END
 
FETCH NEXT FROM LIFOMALIYET INTO @ID,@TARIH,@GIRISCIKIS,@FISTUR,@MIKTAR,@BIRIMFIYAT
 
  END

/**********************************************************************************************************************
5) Malıyet Hesabını bitti. Elimizde kalan malzemeleri envanter bölümüne aktarıyoruz.
**********************************************************************************************************************/
            DELETE LIFOENVANTER WITH(TABLOCK)   WHERE KODU=@KODU AND AMBAR=@AMBAR
INSERT INTO LIFOENVANTER(KODU,AMBAR,GIRISID ,MIKTAR ,GIRISFIYAT,ALIMTARIHI)
            SELECT KODU,AMBAR,ID ,MIKTAR ,BIRIMFIYAT,TARIH FROM #GECICI WHERE ISNULL(MIKTAR,0)>0 AND GIRISCIKIS='GIRIS'
            AND KODU=@KODU  AND AMBAR=@AMBAR

/**********************************************************************************************************************
6) Çıkışların maliyetini hesaplaması aşağıdaki şekilde yapılmaktadır. Bundan sonra bütün maliyetler gerçek tablodaki
MALIYET alanına UPDATE yapılmaktadır.

İlk İşlemde  ; (MIKTAR * GIRISFIYAT) TUTAR
Sonrasında   ; (TUTAR / MIKTAR)
**********************************************************************************************************************/
UPDATE V
SET V.MALIYET=CAST(F.MALIYET  AS decimal(38,8))
FROM STOK_DETAYLI V
LEFT JOIN(
SELECT CIKISID,AMBAR,KODU,ISNULL(SUM(TUTAR)/NULLIF(SUM(MIKTAR),0),0) MALIYET FROM (
SELECT CIKISID,AMBAR,KODU,CAST(MIKTAR*GIRISFIYAT AS decimal(38,8))TUTAR,(MIKTAR) AS MIKTAR
FROM ##LIFOESLEME WHERE KODU=@KODU  AND AMBAR=@AMBAR ) AS  YHP
GROUP BY CIKISID,KODU,AMBAR
) AS F ON F.CIKISID=V.ID
WHERE GIRISCIKIS='CIKIS' AND V.KODU=@KODU  AND V.AMBAR=@AMBAR

/**********************************************************************************************************************
7) Geçiçi tabloda olan Giriş ve Çıkış eşleşmelerini gerçek tabloya yazıyoruz. Normal şartlarda Direk bu tabloya
yazabilirdik fakat geçiçi tablo ile çalışmak her zaman için performans için iyidir.
**********************************************************************************************************************/
    DELETE LIFOESLEME   WITH(TABLOCK)   WHERE KODU=@KODU AND AMBAR=@AMBAR
INSERT INTO LIFOESLEME
SELECT * FROM ##LIFOESLEME WHERE KODU=@KODU AND AMBAR=@AMBAR
DELETE ##LIFOESLEME   WITH(TABLOCK)   WHERE KODU=@KODU AND AMBAR=@AMBAR

CLOSE LIFOMALIYET
DEALLOCATE LIFOMALIYET

END

FETCH NEXT FROM STOK INTO @KODU,@AMBAR
END

CLOSE STOK
DEALLOCATE STOK

SELECT * FROM STOK_DETAYLI

Yorumlar