---
title: "Mikro ERP Vadesinde Ödeme Yüzdesi Analizi: Cari Ödeme Disiplini SQL Raporu"
description: "Müşterileriniz faturalarını vadesinde mi ödüyor? Mikro ERP'de vadesinde ödeme yüzdesi, ağırlıklı vade gecikme günü ve aylık trend analizi SQL sorgusu. Parça bazlı FIFO eşleştirme ile gerçek performans ölçümü."
date: 2026-06-27
category: mikro-erp
tags: ["mikro-erp", "sql-server", "cari-hesap", "vadesinde-odeme", "odeme-disiplini", "vade-analizi", "risk-analizi", "astaflow"]
url: https://mikroerp.dev/blog/mikro-erp-vadesinde-odeme-yuzdesi-sql/
---

## Vadesinde Ödeme Yüzdesi Neden Bu Kadar Önemli?

"Bu müşteriye vadeli satış yapmalı mıyız?" sorusunun cevabını veren en güçlü metriklerden biri **vadesinde ödeme yüzdesi**dir.

| Vadesinde Ödeme | Yorum | Aksiyon |
|---|---|---|
| **%80 ve üzeri** | İyi ödeme disiplini | Vade süresi uzatılabilir |
| **%60 — %80** | Takip gerektiren | Standart vade, yakın izleme |
| **%40 — %60** | Riskli | Vade kısaltılmalı veya teminat istenmeli |
| **%40 altı** | Yüksek risk | Peşin satış veya satış durdurma |

Bankalar kredi derecelendirmesinde, sigorta şirketleri alacak sigortasında bu metriği kullanır. ERP'nizden çekeceğiniz bu veri, **kendi iç kredi derecelendirme sisteminizin** temel taşıdır.

## Basit Yaklaşım: Fatura Bazlı Kontrol

İlk adım olarak, vadeli faturalarınızı ve ödeme durumlarını görelim:

```sql
DECLARE @CariKod VARCHAR(50) = '120.001'; -- Kendi cari kodunuzu yazin

SELECT
    ch.cha_Guid,
    CAST(ch.cha_tarihi AS date) AS [Fatura Tarihi],
    ch.cha_meblag AS [Tutar],
    ch.cha_evrakno_seri + '-' + CAST(ch.cha_evrakno_sira AS VARCHAR) AS [Evrak No],
    -- Vade tarihi cozumleme
    CASE
        WHEN ch.cha_vade BETWEEN 19000101 AND 20991231
        THEN TRY_CONVERT(date, CONVERT(char(8), ch.cha_vade), 112)
        WHEN ch.cha_vade BETWEEN -36500 AND -1
        THEN DATEADD(DAY, ABS(ch.cha_vade), CAST(ch.cha_tarihi AS date))
        ELSE NULL
    END AS [Vade Tarihi],
    -- Vadesinin dolup dolmadigi
    CASE
        WHEN CASE
            WHEN ch.cha_vade BETWEEN 19000101 AND 20991231
            THEN TRY_CONVERT(date, CONVERT(char(8), ch.cha_vade), 112)
            WHEN ch.cha_vade BETWEEN -36500 AND -1
            THEN DATEADD(DAY, ABS(ch.cha_vade), CAST(ch.cha_tarihi AS date))
            ELSE NULL
        END < GETDATE() THEN 'Vadesi Doldu'
        ELSE 'Vadesi Gelmedi'
    END AS [Vade Durumu]
FROM CARI_HESAP_HAREKETLERI ch WITH (NOLOCK)
WHERE ch.cha_kod = @CariKod
  AND ch.cha_tip = 0          -- Borc (fatura)
  AND ch.cha_iptal = 0
  AND ch.cha_meblag > 0
ORDER BY ch.cha_tarihi ASC;
```

Ancak bu basit sorgu sadece faturaları listeler — "vadesinde ödendi mi?" sorusuna cevap veremez çünkü hangi tahsilatın hangi faturayı kapattığını bilmez.

## Profesyonel Yaklaşım: FIFO Parça Bazlı Analiz

Vadesinde ödeme yüzdesini doğru hesaplamak için her tahsilatın hangi faturanın ne kadarını kapattığını bulmamız gerekir. FIFO kesişim algoritmasını kullanıyoruz:

```sql
DECLARE @CariKod VARCHAR(50) = '120.001';

;WITH Borclar AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY cha_tarihi ASC, cha_create_date ASC) AS SiraNo,
        CAST(cha_tarihi AS date) AS FaturaTarihi,
        cha_meblag AS Tutar,
        cha_evrakno_seri + '-' + CAST(cha_evrakno_sira AS VARCHAR) AS EvrakNo,
        CASE
            WHEN cha_vade BETWEEN 19000101 AND 20991231
            THEN TRY_CONVERT(date, CONVERT(char(8), cha_vade), 112)
            WHEN cha_vade BETWEEN -36500 AND -1
            THEN DATEADD(DAY, ABS(cha_vade), CAST(cha_tarihi AS date))
            ELSE NULL
        END AS VadeTarihi
    FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
    WHERE cha_kod = @CariKod
      AND cha_tip = 0 AND cha_iptal = 0 AND cha_meblag > 0
),
Alacaklar AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY cha_tarihi ASC, cha_create_date ASC) AS SiraNo,
        CAST(cha_tarihi AS date) AS OdemeTarihi,
        cha_meblag AS Tutar
    FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
    WHERE cha_kod = @CariKod
      AND cha_tip = 1 AND cha_iptal = 0 AND cha_meblag > 0
),
BorcCum AS (
    SELECT *, SUM(Tutar) OVER (ORDER BY SiraNo
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CumTutar
    FROM Borclar
),
AlacakCum AS (
    SELECT *, SUM(Tutar) OVER (ORDER BY SiraNo
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CumTutar
    FROM Alacaklar
),
Kesisim AS (
    SELECT
        b.SiraNo AS FaturaSiraNo,
        b.FaturaTarihi,
        b.VadeTarihi,
        b.EvrakNo,
        a.OdemeTarihi,
        -- Kesisim tutari (FIFO parca)
        CASE
            WHEN (CASE WHEN b.CumTutar < a.CumTutar
                       THEN b.CumTutar ELSE a.CumTutar END)
               - (CASE WHEN b.CumTutar - b.Tutar > a.CumTutar - a.Tutar
                       THEN b.CumTutar - b.Tutar
                       ELSE a.CumTutar - a.Tutar END) > 0
            THEN (CASE WHEN b.CumTutar < a.CumTutar
                       THEN b.CumTutar ELSE a.CumTutar END)
               - (CASE WHEN b.CumTutar - b.Tutar > a.CumTutar - a.Tutar
                       THEN b.CumTutar - b.Tutar
                       ELSE a.CumTutar - a.Tutar END)
            ELSE 0
        END AS KapananParca
    FROM BorcCum b
    INNER JOIN AlacakCum a
        ON a.CumTutar > b.CumTutar - b.Tutar
        AND a.CumTutar - a.Tutar < b.CumTutar
)

-- SONUC: Vadesinde odeme analizi
SELECT
    @CariKod AS [Cari Kodu],

    -- Toplam vadeli kapanan tutar
    SUM(CASE WHEN VadeTarihi IS NOT NULL
             THEN KapananParca ELSE 0 END)
        AS [Vadeli Kapanan Tutar],

    -- Vadesinde odenen parcalar
    SUM(CASE WHEN VadeTarihi IS NOT NULL
              AND OdemeTarihi <= VadeTarihi
             THEN KapananParca ELSE 0 END)
        AS [Vadesinde Odenen Tutar],

    -- VADESINDE ODEME YUZDESI
    CONVERT(decimal(5,1),
    100.0 * SUM(CASE WHEN VadeTarihi IS NOT NULL
                      AND OdemeTarihi <= VadeTarihi
                     THEN KapananParca ELSE 0 END)
          / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                            THEN KapananParca ELSE 0 END), 0))
        AS [Vadesinde Odeme Yuzdesi],

    -- Geciken parcalarin ortalama gecikme gunu
    CONVERT(decimal(10,1),
    SUM(CASE WHEN VadeTarihi IS NOT NULL
              AND OdemeTarihi > VadeTarihi
             THEN KapananParca * DATEDIFF(DAY, VadeTarihi, OdemeTarihi)
             ELSE 0 END)
    / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                       AND OdemeTarihi > VadeTarihi
                      THEN KapananParca ELSE 0 END), 0))
        AS [Ort Vade Gecikme Gun]

FROM Kesisim
WHERE KapananParca > 0;
```

> **SQL Server 2022 kullananlar için:** `CASE WHEN b.CumTutar < a.CumTutar THEN b.CumTutar ELSE a.CumTutar END` yerine `LEAST(b.CumTutar, a.CumTutar)`, benzer şekilde `GREATEST()` kullanabilirsiniz.

### Beklenen Output

| Cari Kodu | Vadeli Kapanan | Vadesinde Ödenen | Vadesinde % | Ort Gecikme |
|---|---|---|---|---|
| 120.001 | 485.000 | 342.000 | 70,5% | 18,3 gün |

Bu müşteri faturalarının **%70,5'ini** vadesinde ödemiş, geciken faturalarda ortalama **18,3 gün** gecikme yaşanmış.

## Aylık Trend Raporu: Ödeme Disiplini Zaman Serisi

Müşterinin ödeme disiplini iyileşiyor mu yoksa kötüleşiyor mu? Aylık trend raporu ile takip edin:

```sql
-- Aylik trend: vadesinde odeme yuzdesi zaman serisi
-- (Yukaridaki CTE'leri aynen kullanin, sadece final SELECT degisir)

SELECT
    FORMAT(FaturaTarihi, 'yyyy-MM') AS [Ay],
    COUNT(DISTINCT FaturaSiraNo) AS [Fatura Sayisi],
    CONVERT(decimal(18,0), SUM(KapananParca)) AS [Kapanan Tutar],

    -- Vadesinde odeme yuzdesi (o ayin faturalari icin)
    CONVERT(decimal(5,1),
    100.0 * SUM(CASE WHEN VadeTarihi IS NOT NULL
                      AND OdemeTarihi <= VadeTarihi
                     THEN KapananParca ELSE 0 END)
          / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                            THEN KapananParca ELSE 0 END), 0))
        AS [Vadesinde Odeme %],

    -- O ayin ortalama gecikme gunu
    CONVERT(decimal(10,1),
    SUM(CASE WHEN VadeTarihi IS NOT NULL
              AND OdemeTarihi > VadeTarihi
             THEN KapananParca * DATEDIFF(DAY, VadeTarihi, OdemeTarihi)
             ELSE 0 END)
    / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                       AND OdemeTarihi > VadeTarihi
                      THEN KapananParca ELSE 0 END), 0))
        AS [Ort Gecikme Gun]

FROM Kesisim
WHERE KapananParca > 0
GROUP BY FORMAT(FaturaTarihi, 'yyyy-MM')
ORDER BY [Ay];
```

### Beklenen Trend Output

| Ay | Fatura Sayısı | Kapanan Tutar | Vadesinde % | Ort Gecikme |
|---|---|---|---|---|
| 2025-07 | 5 | 120.000 | 85,0% | 8 gün |
| 2025-08 | 4 | 95.000 | 78,2% | 12 gün |
| 2025-09 | 6 | 140.000 | 72,1% | 15 gün |
| 2025-10 | 5 | 110.000 | 65,0% | 22 gün |
| 2025-11 | 4 | 88.000 | 55,3% | 28 gün |
| 2025-12 | 7 | 165.000 | 48,0% | 35 gün |

🔴 **Erken uyarı sinyali!** Vadesinde ödeme yüzdesi 85%'ten 48%'e düşmüş, gecikme günü 8'den 35'e çıkmış. Bu müşterinin kredi limiti gözden geçirilmeli.

## Sonuçları İş Kararlarına Çevirme

Vadesinde ödeme yüzdesi tek başına bir sayıdır. Anlamlı hale getirmek için:

### Karar Matrisi

| Vadesinde % | Trend | Karar |
|---|---|---|
| >80% | Sabit/Yükselen | ✅ Güvenli — vade uzatılabilir |
| >80% | Düşen | ⚠️ İzle — henüz iyi ama bozuluyor |
| 60-80% | Sabit | 🟡 Standart vade, teminat öner |
| 60-80% | Düşen | 🔴 Vade kısalt, teminat iste |
| <60% | Herhangi | 🔴 Peşin satış veya satış durdur |

### Kredi Derecelendirme Entegrasyonu

Bu metrik, cari derecelendirme skorunun temel bileşenlerinden biridir. Detaylı skorlama modeli için: [Cari Kredi Derecelendirme ve Skorlama](/blog/mikro-erp-cari-kredi-derecelendirme-skorlama-sql/)

## Nihai Tam Sorgu: Kopyala — Yapıştır — Çalıştır

Vadesinde ödeme yüzdesi + aylık trend tek sorguda:

```sql
-- =============================================
-- VADESINDE ODEME YUZDESI + AYLIK TREND
-- Kullanim: @CariKod degistirin, F5 basin
-- Uyumluluk: SQL Server 2016+
-- =============================================
DECLARE @CariKod VARCHAR(50) = '120.001'; -- << BURAYA KENDI CARI KODUNUZU YAZIN

IF OBJECT_ID('tempdb..#Kesisim') IS NOT NULL DROP TABLE #Kesisim;

;WITH Borclar AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY cha_tarihi ASC, cha_create_date ASC) AS SiraNo,
        CAST(cha_tarihi AS date) AS FaturaTarihi,
        cha_meblag AS Tutar,
        CASE
            WHEN cha_vade BETWEEN 19000101 AND 20991231
            THEN TRY_CONVERT(date, CONVERT(char(8), cha_vade), 112)
            WHEN cha_vade BETWEEN -36500 AND -1
            THEN DATEADD(DAY, ABS(cha_vade), CAST(cha_tarihi AS date))
            ELSE NULL
        END AS VadeTarihi
    FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
    WHERE cha_kod = @CariKod
      AND cha_tip = 0 AND cha_iptal = 0 AND cha_meblag > 0
),
Alacaklar AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY cha_tarihi ASC, cha_create_date ASC) AS SiraNo,
        CAST(cha_tarihi AS date) AS OdemeTarihi,
        cha_meblag AS Tutar
    FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
    WHERE cha_kod = @CariKod
      AND cha_tip = 1 AND cha_iptal = 0 AND cha_meblag > 0
),
BorcCum AS (
    SELECT *, SUM(Tutar) OVER (ORDER BY SiraNo
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CumTutar
    FROM Borclar
),
AlacakCum AS (
    SELECT *, SUM(Tutar) OVER (ORDER BY SiraNo
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CumTutar
    FROM Alacaklar
)

SELECT
    b.SiraNo AS FaturaSiraNo, b.FaturaTarihi, b.VadeTarihi,
    a.OdemeTarihi,
    CASE
        WHEN (CASE WHEN b.CumTutar < a.CumTutar
                   THEN b.CumTutar ELSE a.CumTutar END)
           - (CASE WHEN b.CumTutar - b.Tutar > a.CumTutar - a.Tutar
                   THEN b.CumTutar - b.Tutar
                   ELSE a.CumTutar - a.Tutar END) > 0
        THEN (CASE WHEN b.CumTutar < a.CumTutar
                   THEN b.CumTutar ELSE a.CumTutar END)
           - (CASE WHEN b.CumTutar - b.Tutar > a.CumTutar - a.Tutar
                   THEN b.CumTutar - b.Tutar
                   ELSE a.CumTutar - a.Tutar END)
        ELSE 0
    END AS KapananParca
INTO #Kesisim
FROM BorcCum b
INNER JOIN AlacakCum a
    ON a.CumTutar > b.CumTutar - b.Tutar
    AND a.CumTutar - a.Tutar < b.CumTutar;

-- SONUC 1: Genel ozet
SELECT
    @CariKod AS [Cari Kodu],
    CONVERT(decimal(18,0),
        SUM(CASE WHEN VadeTarihi IS NOT NULL THEN KapananParca ELSE 0 END))
        AS [Vadeli Kapanan Tutar],
    CONVERT(decimal(5,1),
        100.0 * SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi <= VadeTarihi
                         THEN KapananParca ELSE 0 END)
              / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                                THEN KapananParca ELSE 0 END), 0))
        AS [Vadesinde Odeme %],
    CONVERT(decimal(10,1),
        SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi > VadeTarihi
                 THEN KapananParca * DATEDIFF(DAY, VadeTarihi, OdemeTarihi) ELSE 0 END)
        / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi > VadeTarihi
                          THEN KapananParca ELSE 0 END), 0))
        AS [Ort Gecikme Gun]
FROM #Kesisim WHERE KapananParca > 0;

-- SONUC 2: Aylik trend
SELECT
    FORMAT(FaturaTarihi, 'yyyy-MM') AS [Ay],
    COUNT(DISTINCT FaturaSiraNo) AS [Fatura Sayisi],
    CONVERT(decimal(5,1),
        100.0 * SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi <= VadeTarihi
                         THEN KapananParca ELSE 0 END)
              / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL
                                THEN KapananParca ELSE 0 END), 0))
        AS [Vadesinde %],
    CONVERT(decimal(10,1),
        SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi > VadeTarihi
                 THEN KapananParca * DATEDIFF(DAY, VadeTarihi, OdemeTarihi) ELSE 0 END)
        / NULLIF(SUM(CASE WHEN VadeTarihi IS NOT NULL AND OdemeTarihi > VadeTarihi
                          THEN KapananParca ELSE 0 END), 0))
        AS [Ort Gecikme Gun]
FROM #Kesisim WHERE KapananParca > 0
GROUP BY FORMAT(FaturaTarihi, 'yyyy-MM')
ORDER BY [Ay];

DROP TABLE #Kesisim;
```

## Edge Cases (Dikkat Edilmesi Gerekenler)

### 1. Vadesiz Faturalar (VadeTarihi = NULL)

Mikro'da bazı faturalarda vade tarihi girilmemiş olabilir (`cha_vade = 0` veya boş). Bu faturalar vadesinde ödeme analizine **dahil edilmez** — çünkü karşılaştırma yapılacak bir vade tarihi yoktur.

Eğer vadesiz faturaları da dahil etmek isterseniz, varsayılan bir vade günü atayabilirsiniz:

```sql
-- Vadesiz faturalara 30 gun varsayilan vade ata
ISNULL(VadeTarihi, DATEADD(DAY, 30, FaturaTarihi)) AS VadeTarihi
```

### 2. Çek/Senet ile Ödeme

Çek alındığı tarih (`cha_tarihi`) vade tarihinden önce olabilir ama gerçek tahsilat çekin vadesinde gerçekleşir. Bu durumda "vadesinde ödendi" kararı hangi tarihe göre verilecek?

Detaylı analiz için: [Çek ve Senet Gerçek Tahsilat Tarihi](/blog/mikro-erp-cek-senet-gercek-tahsilat-tarihi-sql/)

### 3. Kısmi Ödemeler

Bir faturanın bir kısmı vadesinden önce, bir kısmı vadesinden sonra ödenmiş olabilir. FIFO parça bazlı analiz bunu otomatik olarak ele alır — her parça ayrı ayrı değerlendirilir.

## İlgili Yazılar

- [Ortalama Tahsilat Süresi (DSO) Hesaplama](/blog/mikro-erp-ortalama-tahsilat-suresi-dso-sql/)
- [FIFO Fatura-Tahsilat Eşleştirme Algoritması](/blog/mikro-erp-fifo-fatura-tahsilat-eslestirme-sql/)
- [Çek ve Senet Gerçek Tahsilat Tarihi](/blog/mikro-erp-cek-senet-gercek-tahsilat-tarihi-sql/)
- [Cari Kredi Derecelendirme ve Skorlama](/blog/mikro-erp-cari-kredi-derecelendirme-skorlama-sql/)
- [Cari Risk Raporu — Bakiye, Sipariş, Teminat](/blog/mikro-erp-cari-risk-raporu-sql/)
- [Cari Yaşlandırma SQL Sorgusu — FIFO Bakiye Dağıtımı](/blog/mikro-erp-cari-yaslandirma-sql-sorgusu/)

---

*Bu yazı [AstaFlow Case Study](/case-study) serisinin bir parçasıdır — vadesinde ödeme yüzdesi, cari risk derecelendirme modülünün temel girdilerinden biridir.*