---
title: "Mikro ERP Son Alış Fiyatını ve Son Giriş Tarihini Getiren SQL Sorgusu"
description: "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..."
date: 2026-04-14
category: mikro-erp
tags: ["mikro-erp", "sql-server", "stok", "alis-fiyati", "maliyet", "tedarik", "astaflow"]
url: https://mikroerp.dev/blog/mikro-erp-son-alis-fiyati-sql/
---

## İş 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:

```sql
-- 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:
1. **İ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 = 0` filtresi hayat kurtarır.
2. **KDV Tutarsa Dahil Edilmez:** Veritabanındaki `sth_tutar` sadece ü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.
3. **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çindeki `sth_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ı](/case-study/)
*   **Mikro ERP DB API:** [STOK_HAREKETLERI (Cins, Tür, Tip Ayrımları)](https://apidocs.mikro.com.tr/tablo-alan-adlari/stok_hareketleri)
*   **İlgili Çözüm:** [Ürün Bazına Brüt Kâr Çıkarma Formülleri](/blog/mikro-erp-alis-satis-kar-analizi-sql/)