# Paket Servis Entegrasyonlarını Şubelerle (POS) Eşleştirme

|                    |                                                                                                                                                                                                                                                    |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Oluşturma**      | 2026-09-07                                                                                                                                                                                                                                         |
| **Durum**          | Uygulanıyor — Faz 1-5 + 5.1 + prod deploy + **Faz 5.2 tamam** (2026-09-11: GTS app-level entegrasyon, webhook teslimatı, canlı sipariş panelde, `respond-order` ACCEPT canlı kanıtlandı); kalan: Faz 6 (POS senkronu) + 2.5/5.1.1/7.x temizlikleri |
| **Kapsam**         | `delivery.apper` (kod) + `admin.apper` (şema)                                                                                                                                                                                                      |
| **İlgili doküman** | `docs/USER_POS_ACCESS.md` (posmanager'dan kopya, güncellenmeli)                                                                                                                                                                                    |

---

## 1. Amaç

Uber Eats, Yemeksepeti gibi paket servis entegrasyonlarını **Bulut Adisyon şubeleriyle**
eşleştirmek ve `navigation-bar.blade.php` üzerinden seçilen şubenin (`selected_pos_service_id`)
uygulamadaki tüm veriyi (entegrasyonlar, siparişler, raporlar, ayarlar) filtrelemesini sağlamak.

---

## 2. Kilitlenen Kararlar

| #   | Karar                                                                                                                      | Teknik sonucu                                                                                                                                                                                                |
| --- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1   | Bir şube, bir platformda **yalnızca tek** hesaba sahip olabilir                                                            | `account_id` bir ayrıştırıcı değil, **1:1 webhook çözüm anahtarı**dır. `data.stores[]` gibi JSON çözümlerine gerek yok. `listStores()` > 1 dönerse bu bir **hata durumu** olarak kullanıcıya raporlanır.     |
| 2   | **Getir yok** (Uber Eats satın aldı, hiç yokmuş gibi davranılacak)                                                         | Platform listesi: `uber_eats`, `yemeksepeti`. poscloud'un `companies.getir_api_key` / `getir_restaurant_id` kolonları **hiç okunmayacak** → projeler arası çift kaynak problemi ortadan kalktı.              |
| 3   | `selected_pos_service_id`'ye bağlı entegrasyonlar listelenir; siparişler, raporlar, ayarlar seçilen ID'ye göre filtrelenir | POS seçimi bir **güvenlik sınırıdır**. Üç kural zorunlu: (a) middleware ile her istekte garanti, (b) tüm scope'larda seçim yoksa **fail-closed**, (c) takım değişiminde sıfırlama.                           |
| 4   | Bulut Adisyon'a sipariş aktarımı **daha sonra API ile** yapılacak (API mevcut)                                             | `mysql-remote`'a INSERT yok → `database/README.md`'nin "sadece SELECT" kuralıyla çelişki kalmadı. `synced_to_pos` / `pos_sale_id` API çağrısının sonucu olarak doldurulacak. **Bu görev ertelendi (Faz 6).** |

### Doğrulanan teknik gerçekler

- **Jetstream `switchTeam()` hiçbir event dispatch etmiyor** (`vendor/laravel/jetstream/src/HasTeams.php:41-54` → sadece `forceFill + save`). Takım değişimi dinlenerek POS session'ı temizlenemez; istek bazlı karşılaştırma gerekir.
- **`integrations.relation_id` ve `integrations.account_id` bugüne kadar hiç kullanılmamış** (mysoft setup scripti ve Uber servisleri dahil). Alanlar temiz, backfill çakışması yok.
- **`selected_pos_service_id` projede hiçbir yerde okunmuyor** — sadece yazılıyor (`routes/web.php:21`, `ViewServiceProvider.php:57`). POS seçimi bugün purely dekoratif.

---

## 3. Nihai Veri Modeli

```
teams (ana hesap)
  └─ user_accesses   type='pos', service_id → uzak companies.id, note = şube adı
       └─ integrations   type='delivery', tool='uber_eats' | 'yemeksepeti'
              relation_id  = service_id           ← şube bağı
              account_id   = platform mağaza kimliği (Uber store_id)
              access_token / refresh_token / token_expires_at / api_key / data
            └─ uber_eats_orders   pos_service_id + integration_id
```

### Alan sözlüğü

| Kavram             | Tablo / Kolon                                                                                  |
| ------------------ | ---------------------------------------------------------------------------------------------- |
| Ana hesap          | `teams.id` → `integrations.current_team_id`, `user_accesses.current_team_id`                   |
| Şube / işletme     | `user_accesses` (`type='pos'`, `service_id`, `note`)                                           |
| Entegrasyon ailesi | `integrations.type = 'delivery'`                                                               |
| Platform           | `integrations.tool = 'uber_eats' \| 'yemeksepeti'`                                             |
| Şube bağı          | `integrations.relation_id = user_accesses.service_id`                                          |
| Platform hesabı    | `integrations.account_id` (Uber `store_id`)                                                    |
| Credential         | `access_token`, `refresh_token`, `token_expires_at`, `api_key`, `username`, `password`, `data` |

**Kardinalite:** 1 `Integration` = 1 (takım × platform × şube) = 1 platform hesabı.

### Benzersizlik kuralları (Faz 0 — admin.apper)

| Index                    | Alanlar                                      | Amaç                                                                           |
| ------------------------ | -------------------------------------------- | ------------------------------------------------------------------------------ |
| `uk_integration_branch`  | `(type, tool, current_team_id, relation_id)` | Karar 1: şube başına platform başına tek hesap                                 |
| `uk_integration_account` | `(tool, account_id)`                         | Webhook çözüm anahtarı; bir platform mağazası yalnızca bir şubeye bağlanabilir |

`uk_integration_account` webhook'ta **takım bağlamı olmadan** tek indexli sorguyla çözüm sağlar
(webhook'ta oturum/team zaten yoktur). `account_id` nullable olduğu için MySQL unique index
birden fazla NULL'a izin verir → "henüz mağaza keşfedilmemiş" kayıtlar çakışmaz.

### Platform kaydı config'ten yönetilecek

Yemeksepeti eklenince şema değil, sadece config + servis sınıfı değişmeli:

```php
// config/delivery.php
return [
    'platforms' => [
        'uber_eats' => [
            'label' => 'Uber Eats', 'color' => '#06C167',
            'credential_mode' => 'oauth',      // oauth | api_key
            'connect_route' => 'uber-eats.connect',
        ],
        'yemeksepeti' => [
            'label' => 'Yemeksepeti', 'color' => '#FA0050',
            'credential_mode' => 'api_key',
            'connect_route' => null,
        ],
    ],
];
```

---

## 4. Mevcut Kodda Tespit Edilen Sorunlar

### 🔴 Kritik — veri bozan

| #   | Sorun                                                                                                                                                                                                          | Konum                                                                                                            |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| K1  | `account_id` integer cast → Uber `store_id` (string) yazıldığında **0**'a dönüşür, webhook eşleştirmesi imkânsızlaşır                                                                                          | `app/Models/Integration.php:43`                                                                                  |
| K2  | Webhook çözümünde `orderBy('id')->first()` fallback → her sipariş en düşük ID'li entegrasyonun takımına yazılıyor (**cross-tenant sızıntı**)                                                                   | `app/Services/UberEats/UberEatsWebhookService.php:205-211`                                                       |
| K3  | `slug = 'uber_eats-{teamId}'` + kolon UNIQUE → aynı takımın **2. şubesi hiç bağlanamaz** (QueryException)                                                                                                      | `UberEatsAuthService.php:199`, `UberEatsTokenService.php:157`, `admin.apper/...create_integrations_table.php:25` |
| K4  | `relation_id` hiç yazılmıyor/okunmuyor; entegrasyon çözümü yalnızca `type + current_team_id` → şube başına tekilleştirme yok, 2. şube bağlanınca ilk şubenin token'ının üzerine yazılır                        | `UberEatsAuthService.php:189-192`, `UberEatsTokenService.php:147-150`                                            |
| K5  | `type='uber_eats'` → `type='delivery'` + `tool='uber_eats'` dönüşümü ve slug yeniden yazımı gerekiyor (bugüne kadar `type` ve `tool` hep aynı değerle doldurulmuş: `mysoft`/`mysoft`, `uber_eats`/`uber_eats`) | `UberEatsAuthService.php:35,196-197`                                                                             |

### 🟠 Yetki / güvenlik

| #   | Sorun                                                                                                                                                          | Konum                                                                  |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| G1  | `/set-pos-access/{service_id}` **auth grubunun dışında** → guest istekte `auth()->user()->userAccesses()` = **500**                                            | `routes/web.php:19-28`                                                 |
| G2  | Takım değişince `selected_pos_*` session anahtarları temizlenmiyor → başka ana hesabın şubesinin verilerine erişim                                             | Jetstream event dispatch etmiyor; middleware ile çözülmeli             |
| G3  | `selected_pos_note` ile `selected_pos_service_id` **bağımsız** defaultlanıyor → navbar A şubesini gösterirken sorgular B şubesine çalışabilir                  | `app/Providers/ViewServiceProvider.php:51-59`                          |
| G4  | OAuth callback'te şube bağlamı kaybolabilir: `beginAuthorization()` sadece `state` saklıyor; kullanıcı arada şube değiştirirse token **yanlış şubeye** yazılır | `UberEatsAuthService.php:45-46`                                        |
| G5  | `CheckUserAccess` `type='posmanager'` arıyor (posmanager'dan kopya; doğrusu `delivery`) ve alias kayıtlı olmasına rağmen **hiçbir route'a bağlı değil**        | `app/Http/Middleware/CheckUserAccess.php:19`, `app/Http/Kernel.php:68` |

### 🟡 Fonksiyonel eksikler

| #   | Sorun                                                                                                                                                                                   | Konum                                                         |
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| F1  | `uber_eats_orders`'da `pos_service_id` yok. `integration_id` üzerinden türetilebilir **ama** FK `nullOnDelete` → Integration silinirse şube bilgisi **geri dönülemez şekilde kaybolur** | `database/migrations/2026_09_07_000002_...php:23-24`          |
| F2  | `/orders` paneli sadece `currentTeamScope` kullanıyor → şube değişince liste değişmiyor                                                                                                 | `app/Http/Controllers/UberEatsOrderController.php:19`         |
| F3  | `relation()` morphTo tanımlı ama tabloda `relation_type` kolonu yok → **çalışamaz**; `relation_id` artık POS `service_id` olduğuna göre açık `belongsTo` ile değiştirilmeli             | `app/Models/Integration.php:65-68`                            |
| F4  | Şube geçişi **sadece dashboard'da** → "şube seç → o şubenin entegrasyonunu bağla" akışı çalışmaz                                                                                        | `resources/views/partials/navigation-bar.blade.php:42`, `:84` |
| F5  | Etiket karmaşası: POS dropdown başlığı da "Hesap Değiştir", takım dropdown'ı da. POS = **İşletme/Şube**, Team = **Hesap**                                                               | `navigation-bar.blade.php:68`, `:123`, `:143`                 |
| F6  | `service_id != 6` sabiti şube modelinde anlamsız/kırılgan → `is_active` / `is_archived` kullanılmalı                                                                                    | `app/Providers/ViewServiceProvider.php:43-46`                 |
| F7  | Tip uyumsuzluğu: `user_accesses.service_id` **integer**, `integrations.relation_id` **string** → karşılaştırmalarda cast şart, aksi halde `whereIn` implicit cast ile index'i öldürür   | `admin.apper/...user_accesses:20`, `...integrations:21`       |
| F8  | Session başlatma mantığı `ViewServiceProvider`'da (her view render'ında çalışıyor) → middleware'e taşınmalı, view composer sadece **okumalı**                                           | `app/Providers/ViewServiceProvider.php:48-59`                 |
| F9  | `store.provisioned` eventi sadece loglanıyor → eşleştirme tetikleyicisi olarak kullanılmalı                                                                                             | `UberEatsWebhookService.php:169-175`                          |

### ⚪ Temizlik

| #   | Sorun                                                                                                                                                                          | Konum                     |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- |
| T1  | Mükerrer migration: `2026_09_07_000001/000002` dosyaları hem delivery.apper hem admin.apper'de var → `database/README.md` kuralı gereği delivery.apper kopyaları **silinmeli** | `database/migrations/`    |
| T2  | Yetim testler: `StockItem`, `PosCategory`, `StockSupplier`, `StockTransaction` modelleri delivery.apper'de **yok** (posmanager'dan kopya)                                      | `tests/Unit/Models/`      |
| T3  | Ölü import: `IntegrationController` diye bir sınıf yok                                                                                                                         | `routes/web.php:3`        |
| T4  | `docs/USER_POS_ACCESS.md` posmanager'dan kopya; `PosCompany`/`StockItem` referansları delivery.apper'de geçersiz → yeni modele göre yeniden yazılmalı                          | `docs/USER_POS_ACCESS.md` |

---

## 5. Görev Listesi

### Faz 0 — Şema (**admin.apper'de** yapılacak)

> `database/README.md`: delivery.apper'de migration dosyası oluşturulmaz, `php artisan migrate` çalıştırılmaz.

- [x] **0.1** `integrations`'a `uk_integration_branch` UNIQUE `(type, tool, current_team_id, relation_id)` ekle
- [x] **0.2** `integrations`'a `uk_integration_account` UNIQUE `(tool, account_id)` ekle
- [x] **0.3** `integrations.relation_id` için index ekle
- [x] **0.4** `uber_eats_orders`'a `pos_service_id` (nullable, index) kolonu ekle _(F1)_
- [x] **0.5** Backfill: `type='uber_eats'` → `type='delivery'`, `tool='uber_eats'`; slug'ları `delivery-uber_eats-{teamId}-{serviceId}` biçimine çevir _(K3, K5)_
- [x] **0.6** delivery.apper'deki mükerrer migration kopyalarını sil _(T1)_

### Faz 1 — Model katmanı

- [x] **1.1** `Integration`: `TYPE_DELIVERY = 'delivery'` sabiti; `account_id` integer cast'ini **kaldır** _(K1)_
- [x] **1.2** `Integration`: `relation()` morphTo'yu kaldır → `posAccess(): BelongsTo(UserAccess, 'service_id', 'relation_id')` _(F3)_
- [x] **1.3** `Integration`: `scopeDelivery()`, `scopePlatform($tool)`, `scopeForSelectedPos()` — seçim yoksa `whereRaw('1 = 0')` **fail-closed** _(Karar 3)_
- [x] **1.4** `UserAccess`: `integrations()` ve `deliveryIntegrations()` ilişkileri
- [x] **1.5** `UberEatsOrder`: `pos_service_id` fillable/cast + `scopeForSelectedPos()` _(F1, F2)_
- [x] **1.6** `config/delivery.php` platform kaydı oluştur

### Faz 2 — POS seçim altyapısı

- [x] **2.1** `app/Http/Middleware/EnsureSelectedPos.php` oluştur:
    - `pos_team_id` değişmişse tüm `selected_pos_*` anahtarlarını sıfırla _(G2)_
    - seçim yoksa veya kullanıcının `type='pos'` erişimi olmayan bir `service_id` ise varsayılana düş
    - `selected_pos_note`'u **her zaman** `selected_pos_service_id`'den türet _(G3)_
    - `service_id != 6` sabitini kullanma; `is_active` / `is_archived` filtrele _(F6)_
- [x] **2.2** `Kernel.php`'ye `pos.selected` alias'ı ekle ve korunan route grubuna bağla _(web middleware grubuna bağlandı — Karar 3a "her istekte garanti" gereği Jetstream rotalarını da kapsar)_
- [x] **2.3** `/set-pos-access` route'unu auth middleware grubunun **içine** taşı _(G1)_
- [x] **2.4** `ViewServiceProvider`'ı sadeleştir: session'a **yazmasın**, sadece okusun _(F8, G3)_
- [ ] **2.5** `CheckUserAccess` → `type='delivery'` ve route grubuna bağla _(G5)_ — tip düzeltildi, JSON yerine HTML 403; bağlama, `user_accesses`'e `type='delivery'` kaydı eklenince yapılacak (paylaşılan DB'ye yazma yetkisi yok)
- [x] **2.6** `routes/web.php`'deki ölü `IntegrationController` importunu kaldır _(T3)_

### Faz 3 — Uber servisleri POS-farkındalıklı

- [x] **3.1** `UberEatsAuthService::integrationFor($serviceId)` → `type='delivery'` + `tool='uber_eats'` + `current_team_id` + `relation_id` ile ara; `$serviceId` yoksa **throw** (fallback yok) _(K4)_
- [x] **3.2** slug'ı şube bazlı üret: `delivery-uber_eats-{teamId}-{serviceId}` _(K3)_
- [x] **3.3** `UberEatsTokenService::resolveIntegration()`'ı 3.1 ile aynı imzaya getir _(K4)_
- [x] **3.4** `beginAuthorization()` → `state` ile birlikte hedef `service_id`'yi de session'a yaz _(G4)_
- [x] **3.5** `callback()` → kullanıcının o `service_id` için `type='pos'` erişimini **doğrula**, yoksa reddet _(G4)_
- [x] **3.6** Bağlantı sonrası `listStores()` → `account_id` + `title` yaz; **>1 store dönerse hata** olarak yüzeye çıkar _(Karar 1)_
- [x] **3.7** `store.provisioned` webhook'unda eşleştirme kaydını oluştur/güncelle _(F9)_

### Faz 4 — Webhook fail-closed

- [x] **4.1** `resolveIntegration($storeId)` → `account_id` üzerinden tek indexli sorgu; **`orderBy('id')->first()` fallback'i kaldır** _(K2)_
- [x] **4.2** Eşleşme yoksa event'i `unmapped` olarak işaretle, **200 ack dönmeye devam et** (Uber 7 kez retry ediyor)
- [x] **4.3** Order upsert'e `pos_service_id = integration.relation_id` ekle _(F1)_
- [x] **4.4** `uber-eats:simulate-webhook` komutunu yeni çözüm yoluna göre güncelle

### Faz 5 — UI

- [x] **5.1** Navbar: şube geçişini **tüm sayfalarda** etkinleştir, `routeIs('dashboard')` kısıtını kaldır _(F4)_
- [x] **5.2** Navbar: POS dropdown başlığını "İşletme Değiştir" yap, takım dropdown'ından ayrıştır _(F5)_
- [x] **5.3** `UberEatsOrderController` → POS scope uygula; görsele şube adı kolonu ekle _(F2)_
- [x] **5.4** Entegrasyon listesi ekranı: seçili şubenin bağlı/bağlı olmayan platformları (`config/delivery.php` üzerinden)
- [x] **5.5** Eşleşmemiş mağaza/event uyarı rozeti

### Faz 5.1 — Yerel pipeline testi _(Uber Eats kanalı — Uber bağımlılığı yok)_

> Amaç: Faz 4 çözümleme zincirini (`account_id` → `relation_id` → `pos_service_id`) ve Faz 5 UI'ını gerçek Uber trafiği olmadan uçtan uca kanıtlamak (Kabul Kriteri 2-3). Mağaza: `753a73d8-c81a-4610-975d-5983a21a73be` (Lakeside Restaurant — `docs/uber-eats/test_store_info.md`).

- [ ] **5.1.1** Temizlik (SQL — kullanıcı): id=4 silindi ✓; kalan: `account_id=''` → `NULL` normalizasyonu ve Faz 4 öncesi simülatör artığı 5 sahte siparişin silinmesi (`created_at < '2026-09-08'`)
- [x] **5.1.2** Store bağlaması **tamam** — OAuth reconnect token'ı sakladı; `bindStore` consent kimliği (authorization_code) token'ıyla listeleyecek şekilde düzeltildi (`discoverStores` → boşsa client_credentials fallback) → `account_id=753a73d8-…`, `title=Lakeside Restaurant`
- [x] **5.1.3** Dashboard pill'i **Eşleşti** gösterdi (LAKESIDE seçiliyken render doğrulaması)
- [x] **5.1.4** Simülatör: event `status=processed` + order `pos_service_id=1675`, `current_team_id=1` yazıldı _(Kabul Kriteri 2 canlı doğrulandı)_
- [x] **5.1.5** Sipariş dashboard "Siparişler" kartında ve `/orders` tahtasında İşletme=LAKESIDE ile göründü
- [x] **5.1.6** Get Order zenginleştirmesi sahte order_id için gracefully atlandı — beklendiği gibi

### Faz 5.2 — Gerçek Uber sandbox uçtan uca test

> GTS yanıtı (2026-09-09, `docs/uber-eats/order_integration.md`): Uber sipariş tetiklemiyor — test siparişleri **self-serve** (resmî "Testing Orders" akışı: müşteri hesabıyla sipariş verilir, ardından `accept_pos_order`/`deny_pos_order` test edilir).
>
> GTS güncellemesi (2026-09-11): mağaza hiçbir test client ID'sine entegre değildi; GTS mağazayı `6T0ZpaX9l6tfxIHCSJnARAClTSIciuZt` client ID'sine bağladı — webhook teslimatı ancak bundan sonra başladı.

- [x] **5.2.1** Prod'da mağaza bağlandı (integration #59, `title=Lakeside Restaurant`, token'lar OAuth ile saklı). İlk bağlama eski Mudanya mağazası `753a73d8-…` ile manuel SQL yapılmıştı; 2026-09-11'de OAuth reconnect yeni mağazayı otomatik bağladı → `account_id=a92dda92-5636-4ba9-b7e5-585817f8a308`
- [x] **5.2.2** App seviyesi yetki **GTS tarafından çözüldü** (2026-09-11): "test store was not previously integrated with any test client ID" — GTS mağazayı client ID `6T0ZpaX9l6tfxIHCSJnARAClTSIciuZt`'e entegre etti. Kök neden: webhook'lar app-level tetiklenir; mağaza hiçbir uygulamaya bağlı değilken imza/URL doğru olsa da **sıfır POST** gelir (access log ile kanıtlandı). Düzeltme sonrası ilk gerçek `orders.notification` düştü. Not: `a92dda92-…` **Store ID**'dir; gerçek Org UUID `557f86db-9e7e-45ee-a5e6-85b4698fe5d3`
- [x] **5.2.3** Webhook aboneliği **canlı doğrulandı**: PRIMARY, BASIC_HMAC, `https://delivery.apper.com.tr/api/webhooks/uber-eats`; portal üretimi Signing Key prod `.env`'de → doğru imzalı ping **200** + log `webhook received (no handler): ping` (parity kanıtı). Abonelik GTS entegrasyonu sonrası bir kez silinip yeniden oluşturuldu. Ops notu: `optimize:clear` sonrası root sahipli `bootstrap/cache` manifest'leri web kullanıcısı tarafından yazılamadığı için geçici 500 — sahiplik web kullanıcısına verilmeli. Middleware `UBER_EATS_WEBHOOK_SIGNING_KEY` → `_SECONDARY` → `UBER_EATS_CLIENT_SECRET` önceliğiyle çalışır
- [x] **5.2.4** Mağaza hazırlığı + self-serve test siparişi **tamam** (2026-09-11): yeni test mağazası (Amsterdam/Paasbergpad — bkz. `docs/uber-eats/test_store_info.md`) müşteri hesabıyla sipariş verildi; `orders.notification` imzalı geldi (`webhook_meta.client_id=6T0Z…`), integration #59'a çözümlendi ve sipariş panelde (LAKESIDE) göründü. Bu adımda üç prod bug'ı yakalanıp düzeltildi: (1) `event_time` **milisaniye** → DATETIME overflow (`SQLSTATE 22007`, year 58665) → `receivedAt()` normalizasyonu; (2) gerçek `resource_href` **api.uber.com**'u işaret ediyordu → SSRF guard allowlist'e eklendi; (3) probe'larla netleşti: Testing app'in verisi sandbox host'ta yaşar (test-api `v2/eats/order` mevcut token'la **200**), prod OAuth realm'i kilitli (`application environment is mismatched`) ve v1 `/eats/orders` (çoğul) deprecated (400) → `url()` artık bilinen Uber host'larından gelen her href'i **path+query koruyarak** config base host'una (test-api) yazar
- [x] **5.2.5** Doğrulama: canlı sipariş yalnızca LAKESIDE (`pos_service_id=1675`) altında göründü _(Kabul Kriteri 2 prod'da)_. Bilinmeyen mağaza → `unmapped` + 200 ack davranışı lokalde kanıtlandı (2026-09-09, integrations boşken gelen imzalı event) _(Kabul Kriteri 3)_; prod'da istenirse `php artisan uber-eats:simulate-webhook --store=<bogus-uuid>` ile tekrarlanabilir
- [x] **5.2.6** **(yeni — resmî akış adım 5; 200 ack sonrası 11.5 dk içinde koşulmazsa sipariş auto-cancel)** Accept/Deny yeteneği: `UberEatsApiService::post()/patch()` + `acceptOrder()/denyOrder()` (aksiyon URL'i resmî **v1 çoğul** aileden order id ile türetilir; href yalnızca GET Order okumalarında) + `updateStoreStatus()`; `fetchOrderDetail()` `resource_href`'e saygı gösterir; test için `uber-eats:respond-order {order_id} [--deny]` artisan komutu — **CANLI KANIT (2026-09-11)**: `uber-eats:respond-order 4061fc51-09d0-4768-bf6e-e5222db9cbc6` → `Accept succeeded` (204 No Content). Nihai topoloji: `POST /v1/eats/orders/{id}/accept_pos_order` (v2 tekil accept her iki host'ta 404) + boş body **`{}`** (Laravel `Http::post($url, [])` → `[]` → 400 `bad_request` tuzağı) + host rewrite (api.uber.com → test-api). Red yolu aynı ailede kodda hazır; isteğe bağlı ikinci siparişle `--deny --reason="test"` doğrulanabilir

### Faz 5.3 — Sipariş detay + onay/red arayüzü _(2026-09-11)_

- [x] **5.3.1** `GET /orders/{order:order_id}` → `orders.show`: Get Order v2 şemasına göre sepet (ürün + modifiye grupları + özel notlar + tedarik sorunları), ödeme dökümü (`payment.charges`), müşteri/mağaza/kurye kartları, webhook olay zaman çizelgesi, ham payload (`<details>`)
- [x] **5.3.2** `POST /orders/{order:order_id}/respond` (`action=accept|deny` + `reason`): `acceptOrder()/denyOrder()` (resmî v1 çoğul aile), optimistik `status` güncellemesi (ACCEPTED/DENIED), flash başarı/hata; branch guard fail-closed (F2) + CurrentTeamScope (route binding)
- [x] **5.3.3** 11.5 dk (690 sn) onay penceresi göstergesi: son `orders.notification` `received_at` + 690 sn; pencere kapalıysa uyarı
- [x] **5.3.4** Liste sayfası: sipariş hücresi `display_id` + detay bağlantısı; durum rozetleri CREATED/PLACED (amber), ACCEPTED (yeşil), DENIED/CANCELED (kırmızı), FINISHED (mavi)
- [x] **5.3.5** `UberEatsWebhookService`: zenginleştirilmiş siparişlerde `status` artık `current_state`'ten yazılır (Get Order v2 alanı)

### Faz 6 — Bulut Adisyon aktarımı _(ertelendi — Karar 4)_

- [ ] **6.1** API üzerinden sipariş aktarımı; `synced_to_pos`, `pos_sale_id`, `synced_to_pos_at` güncelleme

### Temizlik

- [ ] **7.1** Yetim testleri kaldır veya delivery.apper modellerine uyarla _(T2)_
- [ ] **7.2** `docs/USER_POS_ACCESS.md`'yi yeni modele göre yeniden yaz _(T4)_

---

## 6. Kabul Kriterleri

1. Aynı takıma ait **iki farklı şube** ayrı ayrı Uber Eats'e bağlanabiliyor; her birinin token'ı kendi `Integration` satırında.
2. Webhook ile gelen sipariş, `account_id` → `relation_id` zinciriyle **doğru şubenin** `pos_service_id`'sine yazılıyor.
3. Eşleşmeyen bir `store_id` için sipariş **hiçbir şubeye** yazılmıyor (event `unmapped`, HTTP 200).
4. Navbar'dan şube değiştirildiğinde `/orders` listesi, entegrasyon listesi ve istatistikler **anında** o şubeye göre değişiyor.
5. Takım değiştirildiğinde eski takımın `service_id`'si session'da **kalmıyor**.
6. Giriş yapmamış bir istek `/set-pos-access/...` için 500 değil, login'e yönlendirme döndürüyor.
7. `selected_pos_service_id` boşken hiçbir sorgu **filtrelenmemiş** sonuç döndürmüyor (fail-closed).

---

## 7. İlgili Dosyalar

| Dosya                                               | Rol                                                            |
| --------------------------------------------------- | -------------------------------------------------------------- |
| `app/Models/Integration.php`                        | Eşleştirme kaydı (`relation_id`, `account_id`, `type`, `tool`) |
| `app/Models/UserAccess.php`                         | Şube listesi (`type='pos'`, `service_id`)                      |
| `app/Models/UberEatsOrder.php`                      | Sipariş (`pos_service_id` eklenecek)                           |
| `app/Providers/ViewServiceProvider.php`             | View composer / session init                                   |
| `app/Http/Middleware/CheckUserAccess.php`           | Araç yetkisi (`delivery` olacak)                               |
| `app/Http/Middleware/EnsureSelectedPos.php`         | **Yeni** — POS seçim garantisi                                 |
| `app/Http/Kernel.php`                               | Middleware alias'ları                                          |
| `app/Services/UberEats/UberEatsAuthService.php`     | OAuth authorization_code                                       |
| `app/Services/UberEats/UberEatsTokenService.php`    | OAuth client_credentials                                       |
| `app/Services/UberEats/UberEatsWebhookService.php`  | Webhook ingestion + eşleştirme                                 |
| `app/Http/Controllers/UberEatsController.php`       | connect / callback / status                                    |
| `app/Http/Controllers/UberEatsOrderController.php`  | Sipariş panosu                                                 |
| `routes/web.php`                                    | `set-pos-access`, entegrasyon ve sipariş rotaları              |
| `resources/views/partials/navigation-bar.blade.php` | Şube / hesap geçiş menüsü                                      |
| `resources/views/orders/index.blade.php`            | Sipariş panosu görünümü                                        |
| `config/delivery.php`                               | **Yeni** — platform kaydı                                      |
| `config/uber_eats.php`                              | Uber OAuth / API ayarları                                      |
| `admin.apper/database/migrations/`                  | Tüm şema değişiklikleri                                        |
