Netsis Hareketsiz Stok Raporu: Satmayan Ürünleri SQL ile Bulun
Eldeki stoğun sağlıklı yönetimi, hangi ürünlerin "ölü" durumda olduğunu bilmekle başlar. Netsis'te son N aydır herhangi bir hareket (giriş/çıkış, fatura, irsaliye, transfer vb.) görmemiş stokları tespit etmek için TBLSTHAR tablosu üzerinde çalışan, tekrar kullanılabilir bir SQL raporuna ihtiyaç duyarsınız. Bu yazıda, tam olarak bu raporu kuracak sorguyu adım adım paylaşıyor, sık yapılan hataları ve sık sorulan soruları ele alıyoruz. Önce Netsis tablo yapısı ve stok hareketleri temellerine göz atabilirsiniz.
Hareketsiz stok raporunun mantığı
Temel yaklaşım basittir: TBLSTSABIT stok kartlarını, TBLSTHAR stok hareketleriyle ilişkilendirip belirlediğiniz son N ay içinde hareket kaydı olmayanları listelemek. TBLSTHAR, Netsis'teki tüm stok hareketlerini (alış, satış, transfer, açılış, üretim vb.) tutar. NOT EXISTS veya LEFT JOIN ... IS NULL ile son N ayda tarih filtresine takılan hareketi bulunmayan stok kodlarını yakalarsınız.
STHAR_FTIRSIP filtresi koymazsanız sorgu, satış olsun alış olsun fark etmeksizin hiç hareket görmemiş ürünleri getirir. Eğer yalnız satış hareketlerini baz almak isterseniz STHAR_FTIRSIP IN ('1','3') gibi bir ek filtre eklemeniz gerekir. Aşağıdaki ana sorguda genel hareketsizlik yaklaşımını kullanıyor, varsayılan olarak tüm hareket türlerini değerlendiriyoruz.
Ana sorgu: son N ayda hareketsiz ürünler, güncel bakiye ve son hareket tarihi
Aşağıdaki sorgu, @Months parametresiyle kaç ay geriye bakılacağını belirler. Çıktıda stok kodu, stok adı, güncel eldeki miktar (tüm hareketlerin G-C toplamı), son hareket tarihi ve hareketsiz kalınan gün sayısı yer alır.
| Parametre | Açıklama |
|---|---|
@Months |
Geriye dönük ay sayısı (ör: 6) |
@SubeKodu |
Çok şubede filtre için şube kodu (genelde 0) |
DECLARE @Months INT = 6; -- son 6 ay
DECLARE @SubeKodu SMALLINT = 0; -- tek şube varsayılan
SELECT
s.STOK_KODU,
s.STOK_ADI,
ISNULL(sn.bakiye, 0) AS [Güncel Stok Bakiyesi],
sn.son_hareket_tarihi AS [Son Hareket Tarihi],
DATEDIFF(DAY, sn.son_hareket_tarihi, GETDATE()) AS [Hareketsiz Gün]
FROM TBLSTSABIT s
OUTER APPLY (
SELECT
SUM(CASE WHEN h.STHAR_GCKOD = 'G' THEN h.STHAR_GCMIK
WHEN h.STHAR_GCKOD = 'C' THEN -h.STHAR_GCMIK
ELSE 0 END) AS bakiye,
MAX(h.STHAR_TARIH) AS son_hareket_tarihi
FROM TBLSTHAR h
WHERE h.STOK_KODU = s.STOK_KODU
AND h.SUBE_KODU = @SubeKodu
) sn
WHERE NOT EXISTS (
SELECT 1 FROM TBLSTHAR h2
WHERE h2.STOK_KODU = s.STOK_KODU
AND h2.STHAR_TARIH >= DATEADD(MONTH, -@Months, GETDATE())
AND h2.SUBE_KODU = @SubeKodu
)
ORDER BY sn.bakiye DESC;
OUTER APPLY içindeki toplama, stoğun tüm geçmiş hareketlerini hesaba katarak güncel bakiyeyi ve son hareket tarihini getirir. NOT EXISTS ise son N ay içinde herhangi bir hareketi olan stokları eler. Sonuç, hareket görmeyen stokları, elde kalan miktarı ve en son ne zaman hareket ettiğini gösterir.
Önemli: SUBE_KODU filtresi, çok şubeli ortamlarda her şube için ayrı hareketsizlik raporu alabilmenizi sağlar. Tek şubede genellikle 0 veya 1'dir; veritabanınızda SELECT DISTINCT SUBE_KODU FROM TBLSTHAR ile kontrol edebilirsiniz.
Hareketsiz stokları daha detaylı analiz etme
Yalnızca satış hareketlerine göre hareketsizlik
Eğer "sadece satış yapılmayan, ama alım yapılan ürünler" gibi özel bir kırılım istiyorsanız, NOT EXISTS alt sorgusuna STHAR_FTIRSIP filtresi ekleyebilirsiniz:
-- Sadece satış/irsaliye tipinde son 6 ay hareketi olmayanlar
DECLARE @Months INT = 6, @SubeKodu SMALLINT = 0;
SELECT
s.STOK_KODU,
s.STOK_ADI,
sn.bakiye,
sn.son_hareket_tarihi
FROM TBLSTSABIT s
OUTER APPLY (
SELECT
SUM(CASE WHEN h.STHAR_GCKOD = 'G' THEN h.STHAR_GCMIK ELSE -h.STHAR_GCMIK END) AS bakiye,
MAX(h.STHAR_TARIH) AS son_hareket_tarihi
FROM TBLSTHAR h
WHERE h.STOK_KODU = s.STOK_KODU AND h.SUBE_KODU = @SubeKodu
) sn
WHERE NOT EXISTS (
SELECT 1 FROM TBLSTHAR h2
WHERE h2.STOK_KODU = s.STOK_KODU
AND h2.STHAR_FTIRSIP IN ('1','3') -- satış faturası ve satış irsaliyesi
AND h2.STHAR_TARIH >= DATEADD(MONTH, -@Months, GETDATE())
AND h2.SUBE_KODU = @SubeKodu
)
ORDER BY sn.bakiye DESC;
Depo bazında hareketsizlik
Deponun TBLSTHAR içindeki DEPO_KODU alanından yararlanabilirsiniz. OUTER APPLY içindeki toplamda h.DEPO_KODU = @DepoKodu filtresi ekleyerek yalnızca o depodaki bakiyeyi görür, NOT EXISTS alt sorgusunda da aynı depo için hareket kontrolü yaparsınız.
-- Belirli bir depoda son 6 ay hareket görmeyenler
DECLARE @Months INT = 6, @SubeKodu SMALLINT = 0, @DepoKodu SMALLINT = 1;
SELECT
s.STOK_KODU,
s.STOK_ADI,
ISNULL(depo.bakiye, 0) AS [Depo Bakiyesi],
depo.son_hareket_tarihi
FROM TBLSTSABIT s
OUTER APPLY (
SELECT
SUM(CASE WHEN h.STHAR_GCKOD = 'G' THEN h.STHAR_GCMIK ELSE -h.STHAR_GCMIK END) AS bakiye,
MAX(h.STHAR_TARIH) AS son_hareket_tarihi
FROM TBLSTHAR h
WHERE h.STOK_KODU = s.STOK_KODU
AND h.SUBE_KODU = @SubeKodu
AND h.DEPO_KODU = @DepoKodu
) depo
WHERE NOT EXISTS (
SELECT 1 FROM TBLSTHAR h2
WHERE h2.STOK_KODU = s.STOK_KODU
AND h2.DEPO_KODU = @DepoKodu
AND h2.STHAR_TARIH >= DATEADD(MONTH, -@Months, GETDATE())
AND h2.SUBE_KODU = @SubeKodu
)
ORDER BY depo.bakiye DESC;
Sık yapılan hatalar
SUBE_KODUfiltresini unutmak: Çok şubeli veritabanında yanlış bakiye ve hareket sayısı alırsınız. Sorguların başındaSET @SubeKoduile değişkeni tanımlayıp tüm JOIN/WHERE bloklarında kullanın.STHAR_GCKODyönünü ters kullanmak:'G'giriş,'C'çıkıştır; net miktar = girişler - çıkışlar. SadeceSUM(STHAR_GCMIK)yazılırsa giriş ve çıkış aynı yönde toplanır, bakiye yanlış olur.- Son hareket tarihini alırken
MAXkullanırken sadece son N aylık veriye bakmak: Oysa biz tüm tarih aralığını istiyoruz; bu yüzdenOUTER APPLYiçinde tarih filtresi yok. NOT EXISTSkısmında tarih filtresini>=yerine>yapmak:>= DATEADD(MONTH, -@Months, GETDATE())son N aylık sınırı doğru kapsar;>bir günü kaybetmenize yol açabilir.- Güncel bakiye ile hareketsizlik dönemini karıştırmak: Stoğun bakiyesi pozitif olmasına rağmen uzun süre hareketsiz olabilir. Hareketsizlik raporu sırf bakiyeye bakarak yapılamaz; hareket tarihleri esastır.
Sık sorulanlar
Sorgu neden bazı stokları hareketsiz göstermiyor?
Son N ay içinde iptal edilen bir hareket veya transfer gibi gözden kaçan bir kayıt, NOT EXISTS koşulunu bozar. Netsis'te TBLSTHAR tablosunda doğrudan bir iptal sütunu yoktur; ancak negatif giriş/çıkışla iptal simüle edilebilir. Böyle durumlarda stok hareketsiz görünmeyebilir; dilerseniz STHAR_HTUR gibi ek filtrelerle denetim yapın.
Sadece satış hareketsizliğini nasıl raporlarım?
Yukarıdaki ikinci örnekte olduğu gibi NOT EXISTS alt sorgusuna STHAR_FTIRSIP IN ('1','3') ekleyerek yalnızca satış faturası ve satış irsaliyesi tipindeki hareketleri baz alabilirsiniz. Bu durumda alım veya transfer hareketleri hareketsizlik sayılmaz.
Çok şubede çalışıyorum, tek sorguda tüm şubelerin hareketsiz stoklarını nasıl alırım?
Her şube için ayrı geçiş yaparak sorguyu çalıştırabilir ya da OUTER APPLY ve NOT EXISTS içine SUBE_KODU = s.SUBE_KODU gibi bir eşleme koyarak stok kartının bağlı olduğu şubeyi referans alabilirsiniz. Ancak stok kartı tablosu TBLSTSABIT'te SUBE_KODU şube stoğunu değil, kartın ait olduğu şubeyi gösterir (genelde tüm şubeler ortak kullanıyorsa 0); bu yüzden en sağlıklısı raporu şube bazında ayrı ayrı almaktır.
Güncel bakiyeyi neden TBLSTHAR'dan hesaplıyoruz, stok kartında hazır bakiye yok mu?
Netsis stok kartı (TBLSTSABIT) tanım tablosudur; anlık stok miktarını tutmaz. Stok miktarı yalnızca TBLSTHAR'daki giriş/çıkışların toplamından elde edilir. Bu nedenle bakiyeyi mutlaka hareket tablosu üzerinden hesaplamak gerekir.
Bu sorgularla hareketsiz stoklarınızı düzenli olarak izleyebilirsiniz. Daha karmaşık ihtiyaçlarınızda (örneğin stok grubu, marka bazında raporlama) Erp Asistanı'nda doğal dille sorgu yazarak anında SQL alabilirsiniz.
Netsis'te SQL ve raporu konuşarak alın
Türkçe sorunuzu yazın, şemanıza uygun çalışan SQL'i ya da hazır uygulamanızı saniyeler içinde alın. Kredi kartı gerekmez.