n8n ile Fatura ve Rapor Otomasyonu
Muhasebe departmanından her ay aynı mesajı alıyorsunuz: “Faturalar hazır mı? Raporu ne zaman göndereceksin?” Bu döngü, saat 17:00’de başlayıp gece yarısına kadar süren bir kabusa dönüşüyor. n8n ile bu kabusun nasıl bittiğini anlatacağım.
Fatura ve rapor otomasyonu, n8n’in en güçlü olduğu alanlardan biri. Görsel iş akışı yapısı sayesinde karmaşık iş mantığını kod yazmadan modelleyebilir, ya da ihtiyaç duyduğunuzda JavaScript ile derinlemesine özelleştirebilirsiniz. Bu yazıda gerçek bir senaryoyu baştan sona ele alacağız: Veritabanından fatura verisi çekme, PDF oluşturma, e-posta gönderme ve aylık raporları otomatik hazırlama.
Ortamı Hazırlamak
n8n’i Docker ile ayağa kaldırmak en temiz yol. Özellikle fatura gibi hassas verileri işliyorsanız, self-hosted tercih edin. Bulut sürümde verileriniz üçüncü taraf sunucularda işleniyor, bu durum bazı şirketlerde KVKK açısından sorun yaratabilir.
# Docker Compose ile n8n kurulumu
mkdir -p /opt/n8n && cd /opt/n8n
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=guclu_sifre_buraya
- N8N_HOST=n8n.sirketiniz.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.sirketiniz.com/
- GENERIC_TIMEZONE=Europe/Istanbul
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=n8n_db_sifresi
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
postgres:
image: postgres:15
container_name: n8n_postgres
restart: always
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=n8n_db_sifresi
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
EOF
docker compose up -d
Prodüksiyon ortamında n8n’i Nginx arkasında çalıştırmanızı öneririm. Özellikle webhook endpoint’leri dışarıya açık olacaksa SSL zorunlu.
Fatura Otomasyon Mimarisi
Tipik bir fatura iş akışı şu adımlardan oluşur:
- Tetikleyici: Zamanlama (cron) veya dış sistem webhook’u
- Veri Toplama: Veritabanı sorgusu veya API çağrısı
- Veri Dönüşümü: Fatura formatına uygun hale getirme
- Belge Oluşturma: PDF veya HTML fatura üretimi
- Dağıtım: E-posta, FTP, bulut depolama
- Kayıt: İşlem logları ve durum güncellemesi
Bu adımların her birini n8n node’ları ile karşılamak mümkün. Şimdi detaylara girelim.
Veritabanından Fatura Verisi Çekme
Çoğu şirketin ERP ya da muhasebe sistemi, fatura verilerini bir SQL veritabanında tutuyor. n8n’in PostgreSQL ve MySQL node’ları bu iş için biçilmiş kaftan.
-- Bekleyen faturaları çeken sorgu örneği
SELECT
f.fatura_no,
f.kesim_tarihi,
f.vade_tarihi,
f.toplam_tutar,
f.kdv_tutari,
f.genel_toplam,
m.musteri_adi,
m.musteri_email,
m.musteri_adres,
m.vergi_no,
f.durum
FROM faturalar f
INNER JOIN musteriler m ON f.musteri_id = m.id
WHERE f.durum = 'bekliyor'
AND f.kesim_tarihi = CURRENT_DATE
ORDER BY f.fatura_no ASC;
Bu sorguyu n8n’de PostgreSQL node’una yapıştırıyorsunuz, ardından her satır için ayrı bir iş akışı kolu çalıştırıyorsunuz. n8n’in “Split In Batches” node’u burada kritik: Eğer 500 fatura varsa hepsini aynı anda işlemeye çalışırsanız sistemi çökertirsiniz. 10’arlı gruplar halinde işlemek hem belleği korur hem de bir hata çıktığında geri dönmek kolaylaşır.
PDF Fatura Oluşturma
n8n’in yerleşik PDF oluşturma yeteneği yok, ancak bu eksikliği birkaç farklı yöntemle kapatabilirsiniz. En pratik yol, bir HTML şablonu oluşturup Puppeteer veya WeasyPrint ile PDF’e dönüştürmek.
Bunun için küçük bir yardımcı servis kuracağız:
# PDF oluşturma servisi için Node.js uygulaması
mkdir -p /opt/pdf-service && cd /opt/pdf-service
cat > package.json << 'EOF'
{
"name": "pdf-service",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.0",
"puppeteer": "^21.0.0",
"handlebars": "^4.7.0"
}
}
EOF
cat > server.js << 'EOF'
const express = require('express');
const puppeteer = require('puppeteer');
const Handlebars = require('handlebars');
const fs = require('fs');
const app = express();
app.use(express.json({ limit: '10mb' }));
app.post('/generate-pdf', async (req, res) => {
const { template, data } = req.body;
try {
const templateContent = fs.readFileSync(`/templates/${template}.hbs`, 'utf8');
const compiledTemplate = Handlebars.compile(templateContent);
const html = compiledTemplate(data);
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
printBackground: true
});
await browser.close();
res.setHeader('Content-Type', 'application/pdf');
res.send(pdf);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3001, () => console.log('PDF servisi 3001 portunda çalışıyor'));
EOF
Fatura HTML şablonu için Handlebars kullanıyoruz. Bu şablon dosyasını /templates/fatura.hbs olarak kaydediyorsunuz ve n8n’den HTTP Request node’u ile çağırıyorsunuz.
n8n İş Akışı: HTTP Request ile PDF Alma
n8n tarafında fatura verisi hazır olduğunda, PDF servisini çağırıp sonucu binary veri olarak almanız gerekiyor.
// n8n Function node - Fatura verisini PDF servis formatına hazırla
const faturaVerisi = items[0].json;
const pdfRequest = {
template: 'fatura',
data: {
faturaNo: faturaVerisi.fatura_no,
kesimTarihi: new Date(faturaVerisi.kesim_tarihi).toLocaleDateString('tr-TR'),
vadeTarihi: new Date(faturaVerisi.vade_tarihi).toLocaleDateString('tr-TR'),
musteriAdi: faturaVerisi.musteri_adi,
musteriAdres: faturaVerisi.musteri_adres,
vergiNo: faturaVerisi.vergi_no,
kalemler: faturaVerisi.kalemler || [],
araToplam: faturaVerisi.toplam_tutar.toFixed(2),
kdvTutari: faturaVerisi.kdv_tutari.toFixed(2),
genelToplam: faturaVerisi.genel_toplam.toFixed(2),
odenecekTutar: faturaVerisi.genel_toplam.toFixed(2)
}
};
return [{ json: pdfRequest }];
Bu node’dan çıkan veri, HTTP Request node’una beslenir. HTTP Request node ayarları:
- Method: POST
- URL:
http://pdf-service:3001/generate-pdf - Response Format: File
- Content-Type: application/json
E-posta Gönderimi ve Ek Yönetimi
PDF hazır olduğunda, e-posta göndermek için n8n’in Gmail veya SMTP node’larını kullanabilirsiniz. Kurumsal ortamlarda genellikle şirketin kendi SMTP sunucusu tercih edilir.
// n8n Function node - E-posta içeriğini hazırla
const fatura = $node["Fatura Verisi"].json;
const pdf = $binary.data; // HTTP Request'ten gelen binary
const emailIcerigi = {
konu: `Fatura No: ${fatura.fatura_no} - ${fatura.musteri_adi}`,
alici: fatura.musteri_email,
html: `
<p>Sayın ${fatura.musteri_adi},</p>
<p>
<strong>${fatura.fatura_no}</strong> numaralı faturanız ekte sunulmuştur.
</p>
<p>
<strong>Fatura Tarihi:</strong> ${fatura.kesim_tarihi}<br>
<strong>Vade Tarihi:</strong> ${fatura.vade_tarihi}<br>
<strong>Toplam Tutar:</strong> ${fatura.genel_toplam} TL
</p>
<p>
Herhangi bir sorunuz olursa lütfen [email protected]
adresinden iletişime geçin.
</p>
<p>Saygılarımızla</p>
`,
ekDosyaAdi: `${fatura.fatura_no}.pdf`
};
return [{ json: emailIcerigi, binary: { attachment: pdf } }];
Aylık Rapor Otomasyonu
Fatura gönderimi tek seferlik işlem, aylık rapor ise tekrarlayan ve daha karmaşık bir süreç. Çoğu muhasebe departmanı, ay sonunda şu raporları istiyor:
- Tahsilat durumu özeti
- Gecikmiş faturalar listesi
- Müşteri bazında ciro raporu
- KDV beyanname özeti
Bu raporları n8n ile otomatize etmek için Cron node’u ile her ayın son iş günü tetikleyici kuruyorsunuz.
// n8n Code node - Ay sonu rapor verilerini işle
const faturalar = items;
// Özet hesaplamaları
let tahsilEdilen = 0;
let bekleyen = 0;
let gecikmiş = 0;
const bugun = new Date();
const raporVerisi = {
raporTarihi: bugun.toLocaleDateString('tr-TR'),
donem: `${bugun.toLocaleString('tr-TR', { month: 'long' })} ${bugun.getFullYear()}`,
ozet: {},
musteriBazli: {},
gecikmisFaturalar: []
};
faturalar.forEach(({ json: fatura }) => {
const tutar = parseFloat(fatura.genel_toplam);
const vade = new Date(fatura.vade_tarihi);
if (fatura.durum === 'odendi') {
tahsilEdilen += tutar;
} else if (vade < bugun) {
gecikmiş += tutar;
raporVerisi.gecikmisFaturalar.push({
faturaNo: fatura.fatura_no,
musteriAdi: fatura.musteri_adi,
tutar: tutar.toFixed(2),
vadeTarihi: vade.toLocaleDateString('tr-TR'),
gecikmeGunu: Math.floor((bugun - vade) / (1000 * 60 * 60 * 24))
});
} else {
bekleyen += tutar;
}
// Müşteri bazlı gruplama
if (!raporVerisi.musteriBazli[fatura.musteri_adi]) {
raporVerisi.musteriBazli[fatura.musteri_adi] = {
toplamCiro: 0,
faturaAdedi: 0
};
}
raporVerisi.musteriBazli[fatura.musteri_adi].toplamCiro += tutar;
raporVerisi.musteriBazli[fatura.musteri_adi].faturaAdedi += 1;
});
raporVerisi.ozet = {
tahsilEdilen: tahsilEdilen.toFixed(2),
bekleyen: bekleyen.toFixed(2),
gecikmiş: gecikmiş.toFixed(2),
toplamCiro: (tahsilEdilen + bekleyen + gecikmiş).toFixed(2),
tahsilatOrani: ((tahsilEdilen / (tahsilEdilen + bekleyen + gecikmiş)) * 100).toFixed(1)
};
// Gecikmiş faturaları büyükten küçüğe sırala
raporVerisi.gecikmisFaturalar.sort((a, b) => b.gecikmeGunu - a.gecikmeGunu);
return [{ json: raporVerisi }];
Google Sheets Entegrasyonu ile Canlı Takip
Raporları PDF olarak göndermek yetmez, yönetimin canlı takip edebileceği bir panel de isteniyor. n8n’in Google Sheets node’u bu iş için mükemmel.
// n8n Code node - Sheets için veri hazırlama
const rapor = items[0].json;
const satirlar = [];
// Özet satırı ekle
satirlar.push({
tarih: rapor.raporTarihi,
donem: rapor.donem,
tur: 'ÖZET',
aciklama: 'Toplam Ciro',
tutar: rapor.ozet.toplamCiro,
durum: '-'
});
satirlar.push({
tarih: rapor.raporTarihi,
donem: rapor.donem,
tur: 'ÖZET',
aciklama: 'Tahsilat Oranı %',
tutar: rapor.ozet.tahsilatOrani,
durum: '-'
});
// Gecikmiş faturaları ekle
rapor.gecikmisFaturalar.forEach(fatura => {
satirlar.push({
tarih: rapor.raporTarihi,
donem: rapor.donem,
tur: 'GECİKMİŞ',
aciklama: `${fatura.faturaNo} - ${fatura.musteriAdi}`,
tutar: fatura.tutar,
durum: `${fatura.gecikmeGunu} gün gecikmiş`
});
});
return satirlar.map(satir => ({ json: satir }));
Google Sheets node’unu “Append Row” modunda kullanıyorsunuz. Her ay sonu otomatik olarak yeni satırlar ekleniyor, yönetim istediği zaman filtreleyip bakabiliyor.
Hata Yönetimi ve Bildirimler
Otomasyon sistemleri sessizce başarısız olduğunda gerçek sorunlar başlar. Sabah işe geliyorsunuz, 300 fatura gönderilmemiş ve kimse haberdar değil. Bu yüzden hata yönetimi, iş mantığı kadar önemli.
n8n’de her kritik node’dan sonra bir Error Trigger veya IF node koyuyoruz:
# n8n webhook ile Slack/Teams bildirimi için örnek curl komutu
# Bu komutu n8n'in Execute Command node'u ile çalıştırabilirsiniz
curl -X POST "${SLACK_WEBHOOK_URL}"
-H 'Content-type: application/json'
-d "{
"text": ":x: Fatura Otomasyonu Hatası",
"attachments": [{
"color": "#ff0000",
"fields": [
{"title": "Hata", "value": "${ERROR_MESSAGE}", "short": false},
{"title": "Fatura No", "value": "${FATURA_NO}", "short": true},
{"title": "Zaman", "value": "$(date '+%d.%m.%Y %H:%M')", "short": true}
]
}]
}"
n8n’de “Error Workflow” özelliğini aktif edin. Herhangi bir workflow hatayla bittiğinde, bu hata workflow’u otomatik devreye giriyor ve size bildirim gönderiyor. Settings > Error Workflow kısmından ayarlıyorsunuz.
Yeniden Deneme ve İdempotency
Fatura sistemlerinde bir faturanın iki kez gönderilmesi ciddi sorun yaratır. İdempotency sağlamak için işlenen faturaların takibini tutmanız şart.
-- İşlem takip tablosu
CREATE TABLE fatura_islem_log (
id SERIAL PRIMARY KEY,
fatura_no VARCHAR(50) NOT NULL,
islem_tipi VARCHAR(50) NOT NULL, -- 'email_gonderildi', 'pdf_olusturuldu'
islem_tarihi TIMESTAMP DEFAULT NOW(),
basarili BOOLEAN DEFAULT true,
hata_mesaji TEXT,
UNIQUE(fatura_no, islem_tipi)
);
-- İşlemden önce kontrol
SELECT COUNT(*) as islem_sayisi
FROM fatura_islem_log
WHERE fatura_no = '2024-001234'
AND islem_tipi = 'email_gonderildi'
AND basarili = true;
Bu sorguyu n8n’de her fatura işlemeden önce çalıştırıyorsunuz. Sonuç 0 ise devam, 1 ise atlayın. Bu basit kontrol, yeniden çalıştırma senaryolarında faturaların tekrar gönderilmesini engelliyor.
Performans ve Ölçeklendirme
Ayda 5.000’den fazla fatura işliyorsanız bazı optimizasyonlar şart:
- Paralel işleme: n8n’in “Split In Batches” node’unu 25-50 faturalık gruplarla kullanın
- Queue modu: N8N_EXECUTIONS_MODE=queue değişkeniyle çalışan node’ları ayırın
- Binary veri: PDF’leri mümkünse n8n memory’sinde değil, S3 veya NFS üzerinde tutun
- Veritabanı bağlantı havuzu: PostgreSQL için
max_connectionsayarını kontrol edin
# n8n'i queue modunda çalıştırmak için ek ortam değişkenleri
# docker-compose.yml environment bölümüne ekleyin
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- N8N_CONCURRENCY_PRODUCTION_LIMIT=10
Queue modu için Redis de eklemeniz gerekiyor. Bu yapıda n8n ana süreç iş kuyruğunu yönetirken, worker süreçleri asıl işlemleri yapıyor. Ağır yük altında worker sayısını artırarak ölçeklenebiliyorsunuz.
Gerçek Hayattan Dersler
Bir e-ticaret firmasında bu sistemi kurduğumda karşılaştığım en büyük sorun, müşteri e-posta adreslerindeki tutarsızlıklardı. Bazı kayıtlarda büyük harf, bazılarında boşluk, birinde Türkçe karakter vardı. E-posta göndermeden önce basit bir normalizasyon adımı eklemek zorunda kaldık.
İkinci büyük sorun, ERP sisteminin bazen yanlış veri döndürmesiydi. Fatura toplamı 0 TL olan kayıtlar, null dönen müşteri isimleri… n8n’de her veri çekme adımından sonra bir doğrulama node’u koymak iyi alışkanlık. IF node ile “genel_toplam > 0 ve musteri_email boş değil” kontrolü, sahte gönderimlerden kurtardı bizi.
Son olarak, muhasebe departmanının sisteme güvenmesi için şeffaflık çok önemli. Her sabah önceki günün özet raporunu otomatik e-posta ile gönderiyoruz: Kaç fatura gönderildi, kaçı başarısız oldu, toplamda ne kadar. Bu küçük detay, “otomasyon çalışıyor mu?” sorusunu gündemden kaldırdı.
Sonuç
n8n ile fatura ve rapor otomasyonu kurmak, bir haftalık yatırımla aylarca el emeği kurtarıyor. Kritik noktalara odaklanmak gerekiyor: Sağlam bir PDF servisi, idempotency kontrolü, kapsamlı hata bildirimleri ve muhasebe departmanıyla sürekli geri bildirim döngüsü.
Başlangıç için tek bir süreçle başlayın, örneğin sadece ay sonu raporu. Sisteme güven oluştuktan sonra fatura gönderimini ekleyin, ardından gecikmiş fatura hatırlatmalarını. Bir anda her şeyi otomatize etmeye çalışmak, ilk ciddi hatada tüm sisteme olan güveni sarsıyor. Küçük adımlarla ilerlemek, hem teknik olarak hem de organizasyonel olarak çok daha sağlıklı sonuç veriyor.
