Mikro ERP Kur Farkı Neden Oluşur? Dövizli Hareket Kontrolü SQL Sorgusu

Dövizli işlemler neden TL bakiyesi ile eşleşmiyor? Mikro ERP'de kur farkı oluşumunun anatomisi, OUTER APPLY ile anlık kur eşitleme testleri ve SQL ile...

İş Problemi: “Döviz Borcumuz Bitti Ama TL Bakiyemiz Hala Açık!”

Ekonomi departmanlarının en panik yaşadığı tablo: Alman tedarikçiye olan 5.000 EUR borcumuzu aylar önce ödedik, bankadan çıktı gitti. Avrupa hesabımız sıfır (0). Ancak Mikro’dan “TL Mutabakat Ekstresi” çektiğimizde aynı adama 88.000 TL borçlu görünüyoruz.

Neden Sürekli Kur Farkı Oluşur? Mikro’nun mimarisinde (ve Türkiye Muhasebe Standartları’nın doğasında) ana yerel para birimi TL’dir.

  • 1 Ocak’ta 5.000 EUR lık mal aldınız. Kur 35. TL bazında borç = 175.000 TL.
  • 28 Şubat’ta 5.000 EUR havale yaptınız (döviz borcunuz kapandı!). Ancak kur o gün 38’di. TL bazında ödediğiniz para = 190.000 TL.
  • Finans: Döviz bakiyesi: 0. TL Bakiyesi: Alacaklı (-15.000 TL).

Eğer siz bu iki fatura arasındaki “Kur Farkı Virman” (cha_evrak_tip = 59) fişini muhasebeleştirmeyi unutursanız/erteleseniz, bütün yılı yanlış bütçe bilançolarıyla kapatırsınız.

Somut Hata Tespiti ve Çözüm

Aşağıdaki SQL sorgusu, TL bakiye ile açık kalmış Kur Farklarını milisaniyeler içinde ortaya döken “denetim” sorgusudur. Firmaların ay sonlarında bu sorguyu çalıştırıp kapanmamış farkları onarması gerekir.

SELECT 
    H.cha_kod                                      AS [Cari Kodu],
    C.cari_unvan1                                  AS [Firma Unvani],
    CASE H.cha_d_cins 
        WHEN 1 THEN 'USD' WHEN 2 THEN 'EUR' WHEN 3 THEN 'GBP' 
        ELSE 'Bilinmeyen Doviz'
    END                                            AS [Doviz Cinsi],
    
    -- Sistemde Kayitli (Gecikmis Kurlarla Olusan) TL Bakiye
    SUM(CASE WHEN H.cha_tip = 0 THEN H.cha_meblag 
             ELSE -H.cha_meblag END)               AS [Sistem Ici TL Bakiye],
             
    -- Gercek / Net Asil Doviz Bakiye
    SUM(CASE WHEN H.cha_tip = 0 
             THEN H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
             ELSE -H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
        END)                                       AS [Gercek Doviz Bakiye],
        
    -- Bugune Gore (Anlik Kurdan) TL Karsiligi Olmasi Gereken
    SUM(CASE WHEN H.cha_tip = 0 
             THEN H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
             ELSE -H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
        END) * ISNULL(DK.GuncelKur, 0)             AS [Bugunku Kurdan Asil TL Bakiye],
        
    -- FINANSAL SAPMA (KUR FARKI ZARARI VEYA KARI)
    (SUM(CASE WHEN H.cha_tip = 0 
             THEN H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
             ELSE -H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
        END) * ISNULL(DK.GuncelKur, 0)) - 
    SUM(CASE WHEN H.cha_tip = 0 THEN H.cha_meblag ELSE -H.cha_meblag END)   
                                                   AS [Kesilmesi Gereken Kur Farki TL]
                                                   
FROM CARI_HESAP_HAREKETLERI H WITH (NOLOCK)
INNER JOIN CARI_HESAPLAR C WITH (NOLOCK) ON H.cha_kod = C.cari_kod

-- OPTIMIZASYON: OUTER APPLY ile guncel kur tablolardan aninda yakalanir.
OUTER APPLY (
    SELECT TOP 1 dov_fiyat1 AS GuncelKur
    FROM DOVIZ_KURLARI WITH (NOLOCK) 
    WHERE dov_no = H.cha_d_cins 
      AND dov_fiyat1 > 0 -- Hatali 0 kur girislerini pas gec
    ORDER BY dov_tarih DESC
) DK

WHERE H.cha_d_cins > 0                             -- Sadece dovizli islemlere odaklan
  AND H.cha_iptal = 0
GROUP BY H.cha_kod, C.cari_unvan1, H.cha_d_cins, DK.GuncelKur
-- Dovizde gercekten islem gormus bir hesap olsun (-1 / +1 TL marj toleransi)
HAVING ABS(SUM(CASE WHEN H.cha_tip = 0 THEN H.cha_meblag ELSE -H.cha_meblag END)) > 1
-- Kur farkinin en bariz oldugu hesaplari en tepeye cikar
ORDER BY ABS(
    (SUM(CASE WHEN H.cha_tip = 0 
             THEN H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
             ELSE -H.cha_meblag / NULLIF(H.cha_d_kur, 0) 
        END) * ISNULL(DK.GuncelKur, 0)) - 
    SUM(CASE WHEN H.cha_tip = 0 THEN H.cha_meblag ELSE -H.cha_meblag END)
) DESC;

Performans / Ölçekleme Testi

Bu sorgudaki en kritik mekanizma OUTER APPLY blokudur. Normal bir LEFT JOIN’e oranla, tablodaki son kuru bulmada inanılmaz derecede hızlıdır çünkü SQL’in satır-satır top 1 eşleşme taramasına zorlar (Nested Loops). Birinci Edge Case, eğer DOVIZ_KURLARI tablosuna tatil veya pazar günü için kur işlenmemişse dov_fiyat1 sıfır veya NULL gelecektir. Bu bir “Divide by Zero” patlamasına neden olur. Çözüm, kodda gördüğümüz dov_fiyat1 > 0 şartı ve ISNULL(DK.GuncelKur, 0) ile sarmalanmasıdır.

Edge Cases (İstisnai Durumlar) ve Püf Noktaları

  1. Kur Farkı Faturasında KDV Sorunsalı: Kur farkı kesilirken hesaplanması gereken KDV’yi sadece SQL’den çıkartamazsınız! Faturadaki malzemenin %1, %10 veya %20 olmasına göre kur faturasındaki KDV matrahı değişmelidir. Yukarıdaki sorgu size Net (Toplam) parasal farkı teşhis ettirir.
  2. Alternatif Döviz Kullanımı (Altd_Kur): Mikro’da cari ana cinsi 0 (TL) olup da fiş girilirken alttan döviz 1 (USD) seçilirse cha_altd_kur alanı devree girer. Eğer bu şekilde fiş girilmişse, cha_d_kur yerine cha_altd_kur alanını kullanmanız gerekir.
  3. Evrak Kuru ile TCMB Kuru Kopukluğu: Siz mal alırken anlaştığınız için kuru 33.00 olarak fişe elle yazabilirsiniz; o günkü TCMB (Merkez Bankası - DOVIZ_KURLARI tablosu) 34.00 olabilir. Cari analizinde her zaman evrak içine girilen cha_d_kur baz alınır, genel kura bakmak hata yaptırır.

Bu Bilgiyi Nereden Biliyoruz? (Kaynaklar)

📚 İlgili Yazılar