Mikro ERP Son Alış Fiyatını ve Son Giriş Tarihini Getiren SQL Sorgusu
Satınalma süreçlerinin kalbi: Bir ürünün son alış fiyatı nasıl hatasız bulunur? MAX(tarih) tuzağı, ROW_NUMBER performansı ve fason ile bedelsiz...
İş Problemi: “Biz Bu Kalemi En Son Hangi Tedarikçiden Kaça Aldık?”
Satınalma departmanı ve satış onay mecrası sürekli “Sonuç/Maliyet” ekseninde bilgiye ihtiyaç duyar. Eğer Mikro ERP kullanan şirketinizde 15.000 stok kalemi varsa ve 10 temsilci aynı anda yeni fiyat kartı açmaya çalışıyorsa, en güncel alım verisini manuel olarak görmek korkunç bir eziyettir.
MAX() Fonksiyonu Tuzağı
Deneyimsiz SQL yazarları bu raporu çekerken MAX(sth_tarih) kullanıp bırakır. Peki aynı gün içinde bir ürünü sabah “Tedarikçi A’dan 100 TL’ye”, öğleden sonra “Acil Lazım Diye Tedarikçi B’den 120 TL’ye” alırsanız sistem hangisini getirecektir?
Cevap: İkisinin rastgele bir kombinasyonunu.
Buna ek olarak, “Son Alış” sorusundaki “Alış” kelimesi çok tehlikelidir. İade mi? Bedelsiz numune girişi mi? Bunların izole edilmesi şarttır.
Somut Hata: Bedelsiz Giriş Yüzünden Çöken Analiz
Eğer bedelsiz gelen eşantiyon (promosyon) faturalarını sistemden soyutlamazsanız şu başınıza gelir:
Rapor: “Acer Laptop – Son Alış Fiyatı: 0.00 TL!” Satış temsilcisi raporu görüp “Bu ürün bedava geldi herhalde” diyerek çok ucuz fiyat çeker ve şirket korkunç zarara uğrar.
Çözüm: Window Functions (ROW_NUMBER) ile Profesyonel Tarama
Aynı gün iki fatura kesilme krizini, saniye sırasını (sth_evrakno_sira) da devreye alarak %100 doğrulukla çözen SQL AstaFlow modeli:
-- 1. BÖLÜM: Tüm temiz girişleri tarihe ve evrak sırasına göre Numaralandırma Zarfı (Window)
;WITH SafAlimlar AS (
SELECT
sth_stok_kod,
sth_tarih,
sth_miktar,
sth_tutar,
sth_cari_kodu,
sth_evrakno_seri,
sth_evrakno_sira,
-- Window Function: Her ürün için tarihte en günceli ve evrakta son atılanı '1' olarak numaralar.
ROW_NUMBER() OVER (
PARTITION BY sth_stok_kod
ORDER BY sth_tarih DESC, sth_evrakno_sira DESC
) AS SiraNo
FROM STOK_HAREKETLERI WITH (NOLOCK)
-- BEDELSİZ ÇÖP TESTLERİNİ VE İADELERİ DIŞLAMA FİLTRESİ
WHERE sth_tip = 0 -- Kesinlikle bir giriş hareketi olmalı
AND sth_cins = 0 -- 0 = Normal Alış Faturası
AND sth_iptal = 0 -- İptal faturası değil
AND sth_normal_iade = 0 -- Satıştan bize iade edilenler ASLA "Alım" değildir!
AND sth_tutar > 0 -- BEDELSİZ promosyon girişlerini kesinlikle iptal et.
)
-- 2. BÖLÜM: Sadece O ürünün "1" Numaralı (En sonuncu) kaydını ekrana ver.
SELECT
SA.sth_stok_kod AS [Stok Kodu],
S.sto_isim AS [Malzeme Adı],
CONVERT(VARCHAR(10), SA.sth_tarih, 104) AS [Son Alım Tarihi],
-- Gün eskitme: 6 aydır alınmamış olanı uyar
CASE WHEN DATEDIFF(DAY, SA.sth_tarih, GETDATE()) > 180 THEN '🔴 Çok Eski Fiyat' ELSE '🟢 Güncel' END AS [Fiyat Güvenilirliği],
-- Tutar / Miktar Matematiği
SA.sth_miktar AS [Alınan Kg/Adet],
ROUND(SA.sth_tutar / NULLIF(SA.sth_miktar, 0), 4) AS [Net Birim Fiyat (KDV Hariç)],
-- Cari Bilgi
SA.sth_cari_kodu AS [Tedarikçi Kodu],
C.cari_unvan1 AS [Tedarikçi Firma],
SA.sth_evrakno_seri + '-' + CAST(SA.sth_evrakno_sira AS VARCHAR) AS [Alım Fatura No]
FROM SafAlimlar SA
INNER JOIN STOKLAR S WITH (NOLOCK) ON SA.sth_stok_kod = S.sto_kod
LEFT JOIN CARI_HESAPLAR C WITH (NOLOCK) ON SA.sth_cari_kodu = C.cari_kod
-- FİNAL KURAL: Zarfın içindeki ilk kağıdı al (SiraNo = 1)
WHERE SA.SiraNo = 1
ORDER BY SA.sth_tarih DESC;
Performans / Ölçekleme Testi
Bu sorgu bir “Window Function” (PARTITION BY) çalıştırdığı için belleğe yaslanır. Performansını kanıtlamak için şunu yaptık:
Eğer bu sorguyu 15 yıllık tüm faturalar üzerinde yaparsanız, SQL Server 10 saniye boyunca belleğe tabloyu yüklemeye çalışır (Memory Sort).
Büyük Şirket Tavsiyesi: Eğer sisteminiz yavaşsa, iç CTE satırına AND sth_tarih >= DATEADD(YEAR, -2, GETDATE()) filtresini ekleyin. Son 2 yılda bir kez bile alınmamış bir ürünün son alış fiyatı genellikle anlamsızlaşmış, veritabanını şişirmeyen bir hurdadan ibarettir.
Edge Cases (İstisnai Durumlar)
Daha ileri bir satın alma departmanı için bilmeniz gerekenler:
- İade Karışıklığı: Satış faturalarından gelen iade (mal geri geldi = tip 0) girişleri bir alış maliyeti değildir. Bunu engellemek için kodumuza koyduğumuz
sth_normal_iade = 0filtresi hayat kurtarır. - KDV Tutarsa Dahil Edilmez: Veritabanındaki
sth_tutarsadece ürünün saf bedelidir (İskonto düşülmüş halinin saf KDV hariç bedeli). Üzerine Vergiler binmemiştir. E-ticaret panellerinize yansıtırken maliyet çarpımınızda vergileri mutlaka gözlemlemelisiniz. - Depolar Arası Transfer Hatası: Transferler de (depodan depoya) bir giriş fişi yaratır (
sth_cins = 2). Tedarikçi firmayı ve kurumsal alış fiyatını ararken sırf aynı depoya fiş kestiğiniz için bu hareketi çekmemeye dikkat edin! Bunun koruması da SQL içindekisth_cins = 0(Sadece Normal Alış Faturası) korumasıdır.
Bu Bilgiyi Nereden Biliyoruz? (Kaynaklar)
- AstaFlow Case Study: Dashboard Üzerinde Hızlı Kâr Analizi İhtiyaçları
- Mikro ERP DB API: STOK_HAREKETLERI (Cins, Tür, Tip Ayrımları)
- İlgili Çözüm: Ürün Bazına Brüt Kâr Çıkarma Formülleri