SAP B1 Stok Hareketleri SQL: OINM ile Stok Föyü ve Devir
OINM ile SAP Business One stok föyü: TransType kodları, depo bazında yürüyen bakiye, dönem başı devir ve dönem giriş çıkış özeti SQL sorguları.
Erp Asistanı
Editör ekibi
10 dk okuma
İçindekiler
SAP Business One'da stok hareketlerinin tek adresi OINM'dir (Warehouse Journal). Stok kartındaki OnHand yalnız bugünkü miktarı verir; geçen ay sonunda depoda kaç adet vardı, dönem içinde ne girdi ne çıktı, bu hareket hangi belgeden doğdu sorularının cevabı OINM satırlarındadır. Bu yazıda OINM'i stok föyü olarak okumanın kalıbını, TransType kodlarını, depo bazında yürüyen bakiyeyi ve dönem başı devir hesabını SQL ile kuruyoruz. Sorguların tamamı yalnız okuma yapar; veritabanına yazan tek satır yoktur. Tablo adlarının mantığını bilmiyorsanız önce SAP Business One tablo yapısı rehberine bakın.
OINM'i tanımak: altı kolon yeter
OINM her stok girişi ve çıkışı için bir satır tutar. Miktar iki kolona bölünmüştür: satırın birinde InQty, diğerinde OutQty doludur, ikisi birden asla dolu olmaz. Net miktar her zaman SUM(InQty - OutQty) ile bulunur.
| Kolon | Anlam |
|---|---|
ItemCode |
stok kodu (OITM.ItemCode) |
Warehouse |
depo kodu, metindir: '01', 1 değil |
DocDate |
hareket tarihi (datetime) |
InQty / OutQty |
giren / çıkan miktar, stok birimiyle |
TransType / BASE_REF |
hareketi doğuran belge türü / o belgenin numarası (metin) |
TransNum |
hareket sıra numarası; aynı gün içindeki sıralama anahtarı |
SAP Business One 10.0'da OINM bir uyumluluk görünümüdür, asıl veri OIVL ve OILM tarafında durur. Okumak için OINM kullanın; bu görünüm üzerinden yazma yapılmaz. Yoğun sorguda tarih ve ürün filtresini en başa koyun.
TransType: hareket hangi belgeden doğdu
Stok föyünü okurken en çok karıştırılan nokta budur: hareketi yaratan belge türü TransType, belgenin numarası BASE_REF içindedir. BASE_REF metin kolonudur, JOIN'de kullanılmaz; eşleştirme gerekiyorsa belge tarafında DocEntry kullanın.
| TransType | Hareket | Yön |
|---|---|---|
| 15 | Satış irsaliyesi | Çıkış |
| 16 | Satış iade irsaliyesi | Giriş |
| 13 / 14 | Satış faturası / iade faturası | Yalnız irsaliyesiz kesildiyse |
| 20 | Mal girişi (satınalma) | Giriş |
| 21 | Satınalma iadesi | Çıkış |
| 18 / 19 | Alış faturası / iade | Yalnız mal girişsiz kesildiyse |
| 59 / 60 | Stok girişi / stok çıkışı | Giriş / Çıkış |
| 67 | Depo transferi | Çıkış + giriş (iki satır) |
| 202 | Üretim emri | Emre göre |
| 162 | Stok değerleme | Miktar değişmez |
| 310000001 | Stok açılış bakiyesi | Giriş |
Satış faturası irsaliyeden kopyalandıysa stok irsaliyede düşer, fatura stok hareketi yazmaz. Bu yüzden satış föylerinde 13 yerine 15 görürsünüz; bu bir eksiklik değil, doğru davranıştır. Ciro ve satılan miktar raporu için OINM değil INV1 / RIN1 kullanılır; OINM stok bakışıdır. Hazır ciro sorguları için SAP Business One hazır SQL sorguları yazısına bakabilirsiniz.
Tek ürünün stok föyü: yürüyen bakiye
Depo bazında yürüyen miktarı pencere fonksiyonu ile kuruyoruz. PARTITION BY Warehouse deposuz toplam ile depo bazlı bakiyeyi ayırır; ROWS UNBOUNDED PRECEDING çalışan toplamı verir.
SELECT m.DocDate, m.Warehouse, m.TransType, m.BASE_REF AS BelgeNo,
m.CardCode, m.InQty AS Giren, m.OutQty AS Cikan,
SUM(m.InQty - m.OutQty) OVER (PARTITION BY m.Warehouse
ORDER BY m.DocDate, m.TransNum
ROWS UNBOUNDED PRECEDING) AS Bakiye
FROM OINM m
WHERE m.ItemCode = N'P10001'
ORDER BY m.Warehouse, m.DocDate, m.TransNum;
Bu raporu SQL yazmadan, Türkçe sorarak alın: SAP Business One raporlama
Sıralamada DocDate tek başına yetmez: aynı gün birden fazla hareket varsa bakiye satırlar arasında zıplar. TransNum ikinci sıralama anahtarıdır, tek başına kullanıldığında ise depo geçişlerinde bakiye karışır.
Dönemsel giriş çıkış özeti
Dönem raporunda tarih aralığını yarı açık kurun: başlangıç dahil, bitiş hariç. Böylece ay sonu ile sonraki ayın ilk günü çakışmaz.
SELECT m.ItemCode, i.ItemName, m.Warehouse, w.WhsName,
SUM(m.InQty) AS Giren, SUM(m.OutQty) AS Cikan,
SUM(m.InQty - m.OutQty) AS Net
FROM OINM m
JOIN OITM i ON i.ItemCode = m.ItemCode
LEFT JOIN OWHS w ON w.WhsCode = m.Warehouse
WHERE m.DocDate >= '2025-01-01' AND m.DocDate < '2026-01-01'
GROUP BY m.ItemCode, i.ItemName, m.Warehouse, w.WhsName
HAVING SUM(m.InQty) <> 0 OR SUM(m.OutQty) <> 0
ORDER BY m.ItemCode, m.Warehouse;
HAVING satırı yalnız değerleme (162) gibi miktarsız hareketlerden doğan boş satırları eler. Depo adı OWHS üzerinden gelir; Warehouse kolonunda depo adı değil kod vardır.
Dönem başı devir ayrı hesaplanır
En sık yapılan hata, dönem raporuna tarih filtresi koyup bakiye sanmaktır. Filtre hareketleri kısar, geçmişi silmez: devir, başlangıç tarihinden önceki tüm hareketlerin toplamıdır. Aşağıdaki tek sorgu devir, dönem ve dönem sonunu birlikte verir.
SELECT d.ItemCode, d.Warehouse,
SUM(CASE WHEN d.DocDate < '2025-01-01' THEN d.InQty - d.OutQty ELSE 0 END) AS Devir,
SUM(CASE WHEN d.DocDate >= '2025-01-01' AND d.DocDate < '2026-01-01' THEN d.InQty ELSE 0 END) AS DonemGiris,
SUM(CASE WHEN d.DocDate >= '2025-01-01' AND d.DocDate < '2026-01-01' THEN d.OutQty ELSE 0 END) AS DonemCikis,
SUM(CASE WHEN d.DocDate < '2026-01-01' THEN d.InQty - d.OutQty ELSE 0 END) AS DonemSonu
FROM OINM d
GROUP BY d.ItemCode, d.Warehouse
HAVING SUM(CASE WHEN d.DocDate < '2026-01-01' THEN d.InQty - d.OutQty ELSE 0 END) <> 0
ORDER BY d.ItemCode, d.Warehouse;
Kontrolü kolaydır: Devir + DonemGiris - DonemCikis = DonemSonu. Tutmuyorsa tarih aralığına bir sınır eksik yazılmıştır.
Hareket türü kırılımı
Föyün neden arttığını ya da azaldığını görmek için hareketi doğuran belge türüne göre gruplayın.
SELECT m.TransType,
CASE m.TransType
WHEN 15 THEN N'Satış irsaliyesi'
WHEN 16 THEN N'Satış iade irsaliyesi'
WHEN 20 THEN N'Mal girişi'
WHEN 21 THEN N'Satınalma iadesi'
WHEN 59 THEN N'Stok girişi'
WHEN 60 THEN N'Stok çıkışı'
WHEN 67 THEN N'Depo transferi'
ELSE N'Diğer'
END AS Hareket,
COUNT(*) AS SatirSayisi,
SUM(m.InQty) AS Giren, SUM(m.OutQty) AS Cikan
FROM OINM m
WHERE m.DocDate >= '2025-01-01' AND m.DocDate < '2026-01-01'
GROUP BY m.TransType
ORDER BY SatirSayisi DESC;
Türkçe etiketleri N'...' ile yazıyoruz: veritabanının collation'ı Türkçe olmadığında öneksiz literal ş, ğ, İ karakterlerini sessizce bozar.
Belirli bir tarihteki stok
OITM.OnHand ve depo kırılımı yalnız bugünü tutar. Geçmişe dönük miktar için OINM'i kesme tarihine kadar toplamak gerekir.
SELECT m.ItemCode, m.Warehouse, SUM(m.InQty - m.OutQty) AS Miktar
FROM OINM m
WHERE m.DocDate < DATEADD(DAY, 1, CAST(GETDATE() AS date))
GROUP BY m.ItemCode, m.Warehouse
HAVING SUM(m.InQty - m.OutQty) <> 0
ORDER BY m.ItemCode, m.Warehouse;
Kesme tarihini geriye almak için DATEADD satırını '2025-09-30' gibi sabit bir tarihle değiştirin. HAVING sıfır bakiye satırlarını listeden çıkarır.
Föy ile kart stoğu mutabakatı
Föy toplamı ile stok kartındaki miktar tutmuyorsa sorun genelde dönem filtresi ya da açılış bakiyesidir. Aşağıdaki sorgu farkı olan kalemleri doğrudan listeler.
SELECT i.ItemCode, i.ItemName, i.OnHand AS KartStok,
ISNULL(h.HareketStok, 0) AS FoyStok,
i.OnHand - ISNULL(h.HareketStok, 0) AS Fark
FROM OITM i
LEFT JOIN (SELECT m.ItemCode, SUM(m.InQty - m.OutQty) AS HareketStok
FROM OINM m
GROUP BY m.ItemCode) h ON h.ItemCode = i.ItemCode
WHERE i.InvntItem = 'Y'
AND i.OnHand <> ISNULL(h.HareketStok, 0)
ORDER BY Fark DESC;
Fark tek bir üründe ve sürekli aynı miktardaysa açılış bakiyesi (310000001) ya da sayım düzeltmesi (59/60) gözden kaçmıştır. Fark çok üründe ve gün gün büyüyorsa entegrasyon tarafına bakın: Stok senkronizasyonu neden bozulur yazısı bu üç senaryoyu örneklerle anlatır.
Depo transferi kontrolü
Depo transferi (67) her zaman iki satır üretir: çıkan depoda OutQty, giren depoda InQty. Şirket toplamında net etkisi sıfırdır, depo bazında değil. Aşağıdaki sorgu belge içinde girişi çıkışına eşit olmayan transferleri gösterir; sağlıklı bir veritabanında boş döner.
SELECT m.DocDate, m.BASE_REF AS TransferNo, m.ItemCode,
SUM(m.InQty) AS TransferGiren, SUM(m.OutQty) AS TransferCikan
FROM OINM m
WHERE m.TransType = 67
AND m.DocDate >= '2025-01-01'
GROUP BY m.DocDate, m.BASE_REF, m.ItemCode
HAVING SUM(m.InQty) <> SUM(m.OutQty)
ORDER BY m.DocDate DESC;
Sık yapılan hatalar
- OINM'i ciro raporu sanmak: satılan miktar
INV1/RIN1tarafındadır, OINM stok bakışıdır. - Dönem raporunda devir satırını atlamak: filtre hareketleri kısar, geçmiş bakiyeyi silmez. Devir her zaman ayrı toplanır.
- Yürüyen bakiyeyi yalnız
DocDateile sıralamak: aynı gün içinde bakiye zıplar,TransNumşart. - Faturada (13) stok hareketi aramak: irsaliyeden kopyalanan fatura stok yazmaz, hareket 15'te durur.
- Depo kodunu sayı sanmak:
Warehousemetindir, '01' ile 1 aynı şey değildir. - Geçmiş tarihli stoğu
OITW.OnHandüzerinden okumak: o kolonlar yalnız bugünü tutar. BASE_REFile belgeye JOIN kurmak: kolon metindir, eşleştirmeDocEntryüzerinden yapılır.
Sık sorulanlar
OINM toplamı neden OITW.OnHand ile tutmuyor? OITW bugünkü miktarı tutar, sizin sorgunuz ise tarih filtresiyle geçmişi topluyor olabilir. Tarih filtresini kaldırıp mutabakat sorgusunu çalıştırın; fark hala varsa açılış bakiyesi veya sayım düzeltmesi eksik okunuyordur.
Stok föyünde neden satış faturası (13) hiç yok? Fatura irsaliyeden kopyalandığında stok hareketi irsaliyede oluşur ve TransType = 15 yazar. Yalnız irsaliyesiz kesilen faturalarda fatura türü stok hareketi üretir.
Aynı gün içinde yürüyen bakiye neden düzensiz? Sıralama anahtarınız eksik. ORDER BY DocDate, TransNum kullanın; TransNum hareketin oluşma sırasını taşır.
Bu sorgular SAP HANA'da çalışır mı? Mantık aynıdır ama sözdizimi farklıdır: tarih fonksiyonları ve tablo adlarının çift tırnakla yazımı değişir. Buradaki T-SQL sorguları SQL Server sürümü içindir.
Stok föyünü her ay elle uyarlamak yerine sorunuzu Türkçe yazabilirsiniz: Erp Asistanı SAP Business One şemanıza uygun, doğrulanmış SQL'i üretir. Nasıl çalıştığını SQL Üretme rehberinde görebilirsiniz.
Bu raporu SQL yazmadan, Türkçe sorarak 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.




