amounts & amountExponent ✓ CanlıSatır tutarları bugüne kadar yalnız sonuç olarak yayınlanıyordu (lineTotalDecimal, ondalık); bileşenleri geri hesaplamak ve yuvarlamayı yapmak size kalıyordu. Artık bileşen kırılımı tamsayı minor unit olarak da geliyor: para birimi belgede bir kez, satırda çıplak tamsayı. Alanlar opsiyoneldir — gelmezlerse ondalık alanlar aynen sürer.
← API Uçları · Olay sırası (sequence)
lineTotalDecimal, total, paid, payments[].amount ondalık kalmaya devam ediyor.{ "currency": "TRY", "amountExponent": 2 } // belge KÖKÜNDE, satış düzeyinde| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
| currency | string? | – | Para birimi — ISO-4217 ("TRY"). |
| amountExponent | number? | – | amounts.* tamsayılarının ondalık basamak sayısı (TRY → 2, yani kuruş). Yalnız amounts.* için. |
{
"lineId": "masa-5-a1f2",
"title": "Menemen",
"quantity": 2,
"options": [{ "id": "o1", "title": "Ekstra peynir", "price": 15, "vatRate": 10 }],
"lineTotalDecimal": 129, // ONDALIK — değişmedi
"vatRate": 10,
"amounts": { // TAMSAYI minor unit (amountExponent: 2 → kuruş)
"base": 10000, "options": 3000, "extra": 0, "discount": 100, "timer": 0,
"gross": 12900, "net": 11727, "vat": 1173
}
}| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
| base | number | – | Adet × birim fiyat (seçenek, ekstra, indirim hariç). |
| options | number | – | Seçili seçeneklerin toplamı, adetle çarpılmış. |
| extra | number | – | Satır düzeyi ilave. |
| discount | number | – | Satır düzeyi indirim — pozitif değer, çıkarılır. |
| timer | number | – | Dakika-bazlı ürünlerde tahakkuk eden ücret (aşağıdaki tuzağa bak). |
| gross | number | – | Satır toplamı — tek kaynak (lineTotalDecimal'ın minor-unit karşılığı). |
| net · vat | number | – | Matrah ve vergi. |
| perVat[] | array? | – | Yalnız karışık KDV oranlı satırda gelir — { rate, gross, vat, net } kovaları (aşağıda). |
base + options + extra − discount + timer === grossnet + vat === grossEşitlikler bit-bit tutar: bileşenlerin ayrı yuvarlanmasından doğan artık, mutlak değerce en büyük bileşene emilir. Kendi tarafında yeniden yuvarlama yapmana gerek yok.
Satır düzeyinde aynı ad iki farklı para ölçeğinde yaşıyordu ve hangisinin hangi ölçekte olduğunu söyleyen hiçbir alan yoktu:
// ESKİ — aynı nesnede, aynı adlarla:
{ "discount": 12.6, "extra": 5, "lineTotal": 187.4,
"amounts": { "discount": 1260, "extra": 500, "gross": 18740 } }
// YENİ:
{ "discountDecimal": 12.6, "extraDecimal": 5, "lineTotalDecimal": 187.4,
"amounts": { "discount": 1260, "extra": 500, "gross": 18740 } }amountExponent: 2 beyanı bu ondalık alanlara uygulanmaz (kökteki total da ondalıktır). Üssü görüp satırdaki discount'ı 100'e bölen bir entegratör 12,60 ₺ yerine 0,126 ₺ yazardı — ve mali tüketici satırı imzaladığı için sapma geri alınamaz hâle gelirdi. Sonek bu tuzağı adın kendisinde görünür kılar.amounts minor unit'in tek otoritesidir.orders[], cancels[], lineChanges[].before/after ve customer.order_added satırlarının hepsi — okuma uçları (tables/get, packets/get) dahil.total/paid/totalDiscount ve payments[].amount ondalık kalıyor — onların minor unit karşılığı yok, dolayısıyla ad çakışması da yok.cart[]) değişmedi: istekte hâlâ discount gönderirsin.unit_amount / unit_amount_decimal), Square her tutarı Money{amount,currency} nesnesine sarar, Shopify MoneyV2 ondalık string kullanır — hiçbiri aynı adı iki ölçekte kullanmaz. (⏳ Sandbox'ta canlı, production'a dağıtılmadı.)lineTotalDecimal, total, paid, extraDecimal, discountDecimal, options[].price, payments[].amount ondalıktır ve değişmemiştir. Üssü onlara uygularsan 100 kat sapma alırsın.Özet uçlarında (tables/open · packets/open) minor-unit alan olmadığı için amountExponent hiç gönderilmez — yalnız currency gelir. Bu bilinçli bir asimetridir.
timer okuma anında hesaplanır: hiçbir durum değişikliği olmadan iki olay arasında gross artar. "Değer düştü → storno" mantığı kuran bir tüketici bu satırları ayrı ele almalıdır. timer tam da bunu ayırt edebilmen için ayrı yayınlanıyor.Satırdaki bir seçenek ürünün oranından farklı bir KDV oranı taşıyorsa (Almanya 2026: yemek %7 / içecek %19 → "burger + kola" menüsü) kova kırılımı eklenir.
"amounts": {
// …
"gross": 17000, "vat": 1583, "net": 15417,
"perVat": [
{ "rate": 19, "gross": 5000, "vat": 798, "net": 4202 },
{ "rate": 7, "gross": 12000, "vat": 785, "net": 11215 }
]
}Σ perVat.gross === amounts.gross ve her kovada net + vat === gross garanti.vat/net kırılımdan türetilir — aksi hâlde aynı satır için iki farklı KDV rakamı olurdu.fiscal.de ile imzalanan fişin matrah kırılımı (amountsPerVatRate) aynı dağıtımdan üretilir → yayınlanan veri ile imzalanan belge çelişmez.Her seçeneğin brütü kendi dondurulmuş oranına; ürün tabanı ve timer ürünün oranına; satır düzeyi extraDecimal ve discountDecimal kovalara ciro payıyla orantılı dağıtılır (Stripe discount_amounts[] / Shopify discount_allocations[] deseni).
Seçeneğin oranı yazım anında dondurulur — menü sonradan değişse bile geçmiş satış sabit kalır.
vatRate: null ne demek? Dondurulmamış demektir: o seçenek satırın vatRate'ini kullanır ve karışık oran iddia edilmez. Eski satırlarda ve entegrasyon kanallarının açtığı paketlerde null gelir — oran dondurma yalnız panel addOrder ve eklenti sepet uçlarında yapılır.Aynı payload şekli hem webhook'ta hem okuma uçlarında kullanılır — tek kaynak.
| Yüzey | currency | amountExponent | amounts |
|---|---|---|---|
table.* / packet.* webhook'ları | var | var | var |
| packets/get | var | var | var |
| tables/get | var | var | var |
| packets/open (özet) | var | yok | yok |
| tables/open (özet) | var | yok | yok |
Blocking hook includeData (masa + paket) | var | var | var |
customer.order_added | var | var | var |
sequence / sequenceScope ise zarf alanlarıdır — yalnız webhook teslimlerinde bulunur (olay sırası).
currency / amountExponent / amounts hiç konmaz — bugünkü ondalık alanlar aynen sürer. Uydurma üsle tutar yayınlamak 100 kat sapma demektir, bu yüzden sessizce düşürülür. Satırın verisi bozuksa yalnız o satırda amounts düşer; diğer satırlar etkilenmez.addOrder ve eklenti sepet uçlarında yapılır; entegrasyon kanallarında yapılmaz.import { minorToDecimal, sumLineAmounts, vatBreakdown } from '@restomenum/plugin-sdk';
const { currency, amountExponent, orders, total } = data; // "TRY", 2
const totals = sumLineAmounts(orders); // null → BİR satır bile amounts taşımıyor
if (totals && amountExponent !== undefined) {
minorToDecimal(totals.gross, amountExponent); // 129 ← gösterim için ondalığa çevir
minorToDecimal(totals.vat, amountExponent); // 11.73
} else {
// geri düşüş: ondalık alanlar (total / lineTotalDecimal) — 0 VARSAYMA
console.log(total);
}
// Mali kırılım: karışık oranlı satırda perVat kovaları, tek oranlı satırda satırın vatRate'i.
// null → bir satırın amounts'ı ya da oranı yok: oran TAHMİN EDİLMEZ.
vatBreakdown(orders); // [{ rate: 19, gross, vat, net }, { rate: 7, … }] — yüksek oran önceminorToDecimal(minor, exponent) — gösterim için ondalığa çevirir (hesabı tamsayıda yap).sumLineAmounts(lines) — brüt/matrah/vergi toplamı; bir satır bile amounts taşımıyorsa null.vatBreakdown(lines) — KDV kovaları (yüksek oran önce); oran bilinmiyorsa null.parseLineAmounts · lineAmountsBalanced — açık eşleyici + invaryant kontrolü (savunma katmanı).amounts yoksa ondalık alanlara düş, 0 varsayma. Kod yolunu ikiye ayırma — tek bir "tutar okuyucu" yaz.perVat'ı kullan ve üst düzey vat/net'i ondan türetilmiş kabul et.lineAmountsBalanced) ama kendi hesabını ona dayandırma — beklenmeyen gövdeyi logla, satışı durdurma.