Belge üretimi (PDF/fatura) sunucu tarafında: yaklaşımlar ve tuzaklar
Sunucuda PDF üretmenin üç yolu, font ve para biçimlendirmesinde ilk günden yapılması gereken tercihler, belirlenebilir çıktı ve kuyruk kurulumu üzerine bir not.
Sunucu tarafında belge üretmek, ilk bakışta çözülmüş bir problem gibi görünüyor: bir şablon, bir veri kümesi, bir PDF. Üretime çıktıktan sonra ortaya çıkan sorunların hemen hepsi ise şablonla değil, PDF'in kendisiyle ilgili. Bu yazı tek bir soruya bakıyor: fatura, irsaliye, teklif gibi belgeleri sunucuda üretirken hangi yaklaşımı seçmeli ve hangi tuzaklara hazırlıklı olmalı.
Üç yaklaşım, üç farklı maliyet
HTML'i başsız tarayıcıya bastırmak. Şablonu HTML+CSS yazarsınız, Chromium printToPDF ile çıktı alır. Tasarım özgürlüğü en yüksek, giriş maliyeti en düşük yol. Bedeli çalışma zamanında ödenir: her belge için bir tarayıcı bağlamı, yüzlerce megabayt bellek ve soğuk başlangıç.
Doğrudan PDF çizmek. PDFKit, ReportLab, fpdf2 gibi kütüphanelerle metni ve çizgileri koordinatlara siz yerleştirirsiniz. Kaynak tüketimi düşük, çıktı belirlenebilir, ama tasarım değişikliği kod değişikliği demek. Sabit formlu belgelerde (irsaliye, çek, resmi form) genellikle doğru tercih.
Dizgi sistemine devretmek. LaTeX ya da Typst gibi araçlar, sayfa kırılımı ve tablo taşması işini sizden daha iyi çözüyor. Şablonu yazacak kişinin o sistemi bilmesi gerekiyor; ekipte tek kişi biliyorsa bu bir bakım riski.
Seçim kriteri basit: belge tasarımı pazarlama tarafından mı değişiyor, yoksa mevzuattan mı geliyor? Birincisinde HTML, ikincisinde doğrudan çizim veya dizgi daha az acı veriyor.
Font gömme, Türkçe ve ilk üretim sürprizi
Yerel makinede düzgün görünen belgenin sunucuda "ğ" ve "İ" harflerini kutu olarak basması, bu işin klasik ilk hatası. Sebep neredeyse her zaman aynı: konteynerde font yok, ve tarayıcı da kütüphane de sessizce bir yedeğe düşüyor.
İki önlem gerekiyor. Fontu imaja gömün ve çıktının içine yerleştirilmesini zorlayın:
COPY fonts/Inter-*.ttf /usr/share/fonts/truetype/app/
RUN fc-cache -f
Doğrudan çizim tarafında da yedeğe düşmeyi hata sayın:
doc.registerFont("gövde", "fonts/Inter-Regular.ttf");
doc.font("gövde"); // varsayılan Helvetica'ya düşerse Türkçe karakter bozulur
Font lisanslarını da bu aşamada okuyun; bir fontu web sayfasında kullanma hakkı, onu PDF'e gömme hakkını her zaman kapsamıyor.
Para, tarih ve yerel biçim
Belge üretimi, para hatalarının en görünür olduğu yer: müşteri toplamı elle topluyor ve bir kuruş tutmadığında sizi arıyor. İki kural yeterli. Parayı tam sayı olarak (kuruş cinsinden) saklayın, biçimlendirmeyi yalnız çıktı anında yapın. Yuvarlamayı satır bazında bir kez yapın ve toplamı yuvarlanmış satırlardan hesaplayın; önce toplayıp sonra yuvarlarsanız belgedeki satırların toplamı belge toplamını tutmaz.
const kurus = 129990; // 1.299,90
const metin = new Intl.NumberFormat("tr-TR", {
style: "currency", currency: "TRY",
}).format(kurus / 100);
Tarih tarafında da benzer bir tuzak var. Sunucu UTC çalışıyorsa ve belgeye tarihi olduğu gibi basıyorsanız, akşam 23:30'da kesilen bir belge ertesi güne yazılabiliyor. Belgeye basılacak tarihi işletmenin saat diliminde hesaplayın ve bunu şablonun değil, veri katmanının işi yapın.
Belirlenebilirlik ve tekrar üretim
Aynı veriden iki kez PDF ürettiğinizde çıktı bayt bayt aynı olmuyorsa, karşılaştırma ve önbellekleme yapamazsınız. Bunun iki sebebi var: PDF üstverisine yazılan üretim zamanı ve rastgele üretilen belge kimliği. Her ikisi de sabitlenebilir:
doc.info.CreationDate = new Date(kayit.olusturmaZamani);
Bunu yaptığınızda belgeyi içerik özetine göre önbelleğe alabilir, testlerde de çıktının hash'ini karşılaştırabilirsiniz. Görsel farkı yakalamak için sayfayı PNG'ye çevirip piksel karşılaştırması yapan bir test, şablon değişikliklerinde beklenmedik kaymaları erken gösteriyor.
Kaynak yönetimi: PDF üretimi istek döngüsünde durmamalı
Başsız tarayıcı yaklaşımında en sık görülen üretim arızası, eşzamanlı birkaç isteğin belleği tüketmesi. Üretimi HTTP isteğinden ayırın: iş kuyruğa girsin, işçi süreç üretsin, sonuç nesne deposuna yazılsın, kullanıcıya süreli bir bağlantı verilsin. Tarayıcı bağlamını her iş için yeniden açmak yerine havuzdan verin, ama işçi başına belge sayısını sınırlayıp süreci belirli aralıklarla yeniden başlatın; uzun ömürlü Chromium süreçleri bellek biriktiriyor.
Zaman aşımı da belgeye özel olmalı. Tek sayfalık teklif ile üç yüz satırlık bir icmal aynı sınıra sokulursa, ya kısa belgeler gereksiz bekliyor ya uzunlar yarıda kesiliyor.
Fatura özelinde: PDF belgenin kendisi değil
Türkiye'de e-Fatura ve e-Arşiv tarafında hukuken geçerli olan belge, UBL-TR biçimindeki XML. PDF, o belgenin görüntüsü. Bu ayrımı mimaride kurmazsanız, ilerleyen aşamada görüntüyü değiştirdiğinizde asıl belgeyi de değiştirdiğinizi sanma riski doğuyor.
Pratik karşılığı şu: XML üretimi ile görüntü üretimini iki ayrı modül olarak tutun, ikisini aynı veri modelinden besleyin ve gönderilmiş bir belgenin görüntüsünü yeniden üretmek zorunda kalmayın — üretildiği anda saklayın.
Fatura biçimi, alan zorunlulukları ve saklama süreleri tebliğlerle değişiyor. Uygulamaya geçmeden önce güncel biçim şartlarını GİB duyurularından ve mali müşavirinizden doğrulayın.
Nereden başlamalı
Elinizde belge üretimi olmayan bir sistem varsa sıralama şu: önce belge tasarımının kim tarafından ve ne sıklıkla değiştiğini yazın, yaklaşımı ona göre seçin. Sonra fontu ve para biçimlendirmesini ilk günden doğru kurun; bu ikisi sonradan düzeltilirken üretilmiş bütün belgeleri yeniden basmayı gerektiriyor. Kuyruğu ve saklamayı ise ilk yüz belgeden önce ekleyin, çünkü bu iki eksik üretimde aynı anda patlıyor.
- belge üretimi
- üretim ortamı
- mimari