FirsatGO POS Entegrasyonu — YEMEK (food) Sektörü Geliştirici Dokümanı
engine:
food· kapsanan sektörler:restaurant,cafe,fast_food,bakeryBu doküman yalnız yemek sektörü sipariş/katalog farklarını anlatır. HMAC imza, idempotency, retry, para birimi (*_minor), tenant-izolasyon gibi ortak sözleşme kuralları için ana entegrasyon spesifikasyonuna (docs/POSYOU_INTEGRATION_SPEC.md) bakınız.
Genel Bakış
Yemek sektörü; restoran, kafe, fast-food ve fırın işletmelerinin sipariş akışını kapsar. FirsatGO talep ağıdır (Getir / Trendyol-GO konumu); siparişi POS sistemine FG → POS REST POST {posBase}/orders ile iletir ve durum güncellemelerini POS → FG integration/orders/:id/status ile geri alır. Yemek siparişinin ayırt edici yanı kalem-bazlı malzeme modifikasyonudur (eklenen / çıkarılan malzeme, ekstra ücret) ve siparişin mutfak hazırlık durumundan geçen state-machine'idir. Yemekte teslimatı restoran/işletme yönetir; bu yüzden sektöre-özel fulfillment{} bloğu gönderilmez.
Sipariş Yaşam-Döngüsü
Yemek state-machine'i hazırlık (mutfak) adımını içerir:
pending → confirmed → preparing → ready → on_the_way → delivered → completed
(+ payment_pending, cancelled)
| Durum | Anlam |
|---|---|
pending | Sipariş oluşturuldu, işletme henüz onaylamadı. |
confirmed | İşletme siparişi kabul etti. |
preparing | Mutfak hazırlığı başladı (yemek hazırlanıyor). |
ready | Sipariş hazır (kurye/teslim-alma bekliyor). |
on_the_way | Teslimata çıktı (yalnız delivery). |
delivered | Müşteriye teslim edildi. |
completed | Sipariş tamamlandı (kapandı). |
payment_pending | Ödeme bekleniyor (tahsilat tamamlanmamış). |
cancelled | İptal edildi; cancellation_reason dolu olur (order.cancelled olayı). |
POS → FG durum geri-bildirimi confirmed/preparing/ready/on_the_way/delivered/completed/cancelled değerlerini, siparişi order_id veya external_order_id ile eşleyerek gönderir.
Sektöre-Özel Sipariş Alanları
fulfillment{} — GÖNDERİLMEZ
Yemek sektöründe teslimat sorumluluğu restorandadır; fulfillment bloğu boş/null gelir. (Çiçek recipient/sender/card_note, tekel age_verified, market substitution gibi sektör-özel fulfillment alanları yemekte yoktur.) Yemekte sektör-özel bilgi tamamen items[] içindedir.
items[] — kalem + malzeme modifikasyonu
Her kalem temel alanlara ek olarak yemek-özel malzeme kırılımı ve satır finansalı taşır:
| Alan | Tip | Anlam |
|---|---|---|
reference_id | string | Ürün/kampanya referansı. |
name | string | Ürün adı. |
type | product | campaign | Kalem türü. |
quantity | number | Adet. |
original_unit_price | number (+_minor) | Orijinal/menü birim fiyatı. |
price | number (+_minor) | Orijinal birim fiyat alias'ı (satırlar ara-toplama tutarlı toplanır). |
discounted_price | number (+_minor) | Ürün-indirimli (tahsil edilen) birim fiyat. |
extra_price | number (+_minor) | Eklenen malzeme birim ücreti (per-unit; *quantity sayılır). |
line_total | number (+_minor) | original_unit_price × quantity + extra_price × quantity. |
line_discount | number (+_minor) | Ürün/menü indirimi: (original − discounted) × quantity. |
notes | string | null | Kalem notu (müşteri ürün-notu — fiş "Ürün Notları"). |
selected_ingredients[] | {name, group, extra_price} | Siparişte seçili tüm malzemeler. |
added_ingredients[] | {name, group, extra_price} | EKLENEN malzemeler (varsayılan-dışı seçilen). |
removed_ingredients[] | {name, group} | ÇIKARILAN malzemeler (varsayılan ama seçilmemiş + müşterinin removable_ingredients'tan çıkardıkları). |
Malzeme türetme kuralı (kaynak: ingredient_groups[].ingredients[] üzerindeki is_selected / is_default bayrakları):
- EKLENEN =
is_selected = trueveis_default = false(müşteri ekledi). - ÇIKARILAN =
is_default = trueveis_selected = false(müşteri çıkardı). Ayrıca düzremoved_ingredients(string adlar) listesi bununla birleştirilir (farklı kaynak → çift-sayım yok). - SEÇİLİ =
is_selected = trueolan tüm malzemeler.
table_number
type = dine_in (masada) siparişlerde masa numarası taşınır. Diğer tiplerde null.
Örnek (sektöre-özel alanlar)
{
"type": "delivery",
"table_number": null, // dine_in dışında null
"fulfillment": null, // YEMEK: gönderilmez
"items": [
{
"reference_id": "665f0a...",
"name": "Acılı Cheeseburger Menü",
"type": "product",
"quantity": 2,
"original_unit_price": 180.00,
"discounted_price": 162.00, // ürün-indirimli birim fiyat
"extra_price": 15.00, // eklenen malzeme birim ücreti (per-unit)
"line_total": 390.00, // 180×2 + 15×2
"line_discount": 36.00, // (180−162)×2
"notes": "Az pişmiş olsun", // kalem notu
"selected_ingredients": [
{ "name": "Cheddar", "group": "Peynir", "extra_price": 0 },
{ "name": "Ekstra Bacon", "group": "Ekstralar", "extra_price": 15.00 }
],
"added_ingredients": [
{ "name": "Ekstra Bacon", "group": "Ekstralar", "extra_price": 15.00 }
],
"removed_ingredients": [
{ "name": "Soğan", "group": "Sebzeler" }
]
}
]
}
Katalog / Ürün
Yemek ürünü katalog şekli (item_kind = "food_product"). FG → POS GET /catalog/products ile çekilir veya POS POST /catalog/products ile upsert eder. Yemeğe-özel/önemli alanlar:
| Alan | Tip | Anlam |
|---|---|---|
name | string | Ürün adı. |
price | number | Menü fiyatı. |
discounted_price | number | İndirim sonrası fiyat. |
discount_percentage | number (0–100) | İndirim yüzdesi. |
category | ref | Kategori referansı. |
images[] | string[] | Görsel URL'leri. |
ingredients[] | string[] | İçindekiler. |
allergens[] | string[] | Alerjenler. |
removable_ingredients[] | string[] | Çıkarılabilir malzemeler (müşteri "Çıkarılacak Malzeme Tercihleri"). |
is_vegetarian / is_vegan / is_gluten_free / is_spicy | boolean | Diyet/özellik bayrakları. |
calories | number | Porsiyon başı kalori. |
nutrition{} | {protein,carbs,fat,fiber,sugar,sodium} | Besin değeri. |
preparation_time | number (dk) | Hazırlık süresi. |
min_order_quantity / max_order_quantity | number | Min/maks sipariş adedi (0 = sınırsız). |
available_quantity / remaining_quantity | number | Stok (0 = sınırsız). |
variants[] | {name, options[]{name, price_modifier, is_default}} | Varyant/seçenekler (ör. boy). |
ingredient_groups[] | {group_name, selection_type(radio|checkbox|dropdown), is_required, min_selections, max_selections} | Malzeme grupları — seçim kuralları. |
ingredient_group_configurations[] | {exclude_ingredients[], extra_ingredients[]{name, extra_price}} | Grup-bazlı hariç tutulanlar + ekstra (ücretli) malzemeler. |
is_featured | boolean | Öne çıkan ürün. |
sector_attributes(barcode/sku/lot/expiry vb.) yemek için boş kalır — bunlar market/petshop gibi envanter-tabanlı sektörlere aittir.
Akış Notları
fulfillmentyok: Yemek payload'unda sektör-özelfulfillmentbekleme. Teslimatı restoran/işletme yönetir.extra_priceper-unit: Eklenen malzeme ücreti birim başınadır veline_total'da× quantityile çarpılır.qty > 1ekstralı kalemlerde toplamı per-unit ölçekte hesapla; aksi halde ara-toplam eksik çıkar.- Malzeme iki kaynaktan birleşir:
ingredient_groupstürevli çıkarılanlar + düzremoved_ingredientsstring listesi → tekremoved_ingredients[](çift-sayım yok). preparing= mutfak: Bu sektördepreparingdurumu mutfakta hazırlanmayı ifade eder;readyhazır anlamına gelir. POS bu iki ayrı durumu doğru raporlamalı.table_number: Yalnızdine_insiparişlerde dolu;delivery/pickup'tanull.- Finansal tutarlılık (kasa-fişi değişmezi):
original_subtotal − discount_amount + (delivery_fee + service_fee + tip_amount + tax) = total, veitem_discount + additional_discount = discount_amount.original_subtotalorijinal fiyat toplamıdır;subtotal_chargedürün-indirimli sepet toplamıdır. - İptal:
order.cancelledolayındacancellation_reasondolu gelir (top-level alandan veya soncancelledstatus_historygirdisinden türetilir).
Örnek (gerçekçi sipariş payload — POST {posBase}/orders)
{
"order_id": "665fa1b2c3d4e5f600112233",
"order_number": "FG-2026-00482915",
"status": "confirmed",
"type": "delivery",
"cancellation_reason": null,
// ===== FİNANSAL KIRILIM (TRY; major + *_minor) =====
"original_subtotal": 405.00, "original_subtotal_minor": 40500,
"subtotal": 405.00,
"subtotal_charged": 369.00, "subtotal_charged_minor": 36900,
"item_discount": 36.00, "item_discount_minor": 3600,
"additional_discount": 20.00, "additional_discount_minor": 2000,
"discount_amount": 56.00, "discount_amount_minor": 5600,
"coupon_code": "ACILSEPET20",
"tax": 0, "tax_minor": 0,
"delivery_fee": 25.00, "delivery_fee_minor": 2500,
"service_fee": 0, "service_fee_minor": 0,
"tip_amount": 0, "tip_amount_minor": 0,
"total": 374.00, "total_minor": 37400,
"currency": "TRY", "currency_minor_unit": "kurus",
// ===== MÜŞTERİ =====
"customer": { "user_id": "660aabbccddeeff011223344", "name": "Burak E.", "phone": "+90555..." },
// ===== ADRES =====
"delivery_address": {
"street": "Bağdat Cad. No:12 D:5", "city": "İstanbul", "district": "Kadıköy",
"directions": "Kırmızı kapı, 2. kat, interkom 5", "name": "Burak E.", "phone": "+90555...",
"coordinates": { "latitude": 40.9876, "longitude": 29.0567 }
},
"delivery_directions": "Kırmızı kapı, 2. kat, interkom 5",
"table_number": null,
// ===== ÖDEME =====
"payment_method": "cash",
"payment_method_label": "Nakit",
"payment_status": "pending",
// ===== NOT + İLERİ TARİH =====
"notes": "Zile basmayın bebek uyuyor",
"scheduled_for": null,
"estimated_time": 35,
// ===== YEMEK: fulfillment gönderilmez =====
"fulfillment": null,
// ===== KALEMLER + MALZEME =====
"items": [
{
"reference_id": "665f0a11bb22cc33dd44ee55",
"name": "Acılı Cheeseburger Menü",
"type": "product",
"quantity": 2,
"original_unit_price": 180.00, "original_unit_price_minor": 18000,
"price": 180.00, "price_minor": 18000,
"discounted_price": 162.00, "discounted_price_minor": 16200,
"extra_price": 15.00, "extra_price_minor": 1500,
"line_total": 390.00, "line_total_minor": 39000,
"line_discount": 36.00, "line_discount_minor": 3600,
"notes": "Az pişmiş olsun",
"selected_ingredients": [
{ "name": "Cheddar", "group": "Peynir", "extra_price": 0 },
{ "name": "Ekstra Bacon", "group": "Ekstralar", "extra_price": 15.00 }
],
"added_ingredients": [
{ "name": "Ekstra Bacon", "group": "Ekstralar", "extra_price": 15.00 }
],
"removed_ingredients": [
{ "name": "Soğan", "group": "Sebzeler" }
]
},
{
"reference_id": "665f0a11bb22cc33dd44ee99",
"name": "Ayran",
"type": "product",
"quantity": 1,
"original_unit_price": 45.00, "original_unit_price_minor": 4500,
"price": 45.00, "price_minor": 4500,
"discounted_price": 45.00, "discounted_price_minor": 4500,
"extra_price": 0, "extra_price_minor": 0,
"line_total": 45.00, "line_total_minor": 4500,
"line_discount": 0, "line_discount_minor": 0,
"notes": null,
"selected_ingredients": [],
"added_ingredients": [],
"removed_ingredients": []
}
],
"created_at": "2026-06-28T18:42:11.000Z",
"updated_at": "2026-06-28T18:43:02.000Z"
}