# Veritabanı Şeması (ERD)

## Diyagram

```mermaid
erDiagram
    USERS ||--o| DEALERS : "sahiptir (user_id)"
    DEALERS ||--o{ USERS : "personeli (dealer_id)"
    DEALERS ||--o{ DEALERS : "alt bayi (parent_dealer_id)"
    DEALERS ||--o{ DEALER_COMMISSIONS : "komisyon geçmişi"
    DEALERS ||--o{ SUBSCRIPTIONS : "oluşturur"
    DEALERS ||--o{ EARNINGS : "hakediş"
    DEALERS ||--o{ PAYMENTS : "ödeme alır"
    DEALERS ||--o{ APPLICATIONS : "atanır"

    COMPANIES ||--o{ PACKAGES : "sunar"
    PACKAGES ||--o{ SUBSCRIPTIONS : "seçilir"
    PACKAGES ||--o{ APPLICATIONS : "seçilir"
    COMPANIES ||--o{ SUBSCRIPTIONS : "firma"

    SUBSCRIPTIONS ||--o| EARNINGS : "tamamlanınca hakediş üretir"
    SUBSCRIPTIONS ||--o| APPLICATIONS : "başvurudan dönüşür"
    DEALER_COMMISSIONS ||--o{ EARNINGS : "hesaplamada kullanılır"

    USERS ||--o{ SUBSCRIPTIONS : "created_by"
    USERS ||--o{ PAYMENTS : "created_by (Master Admin)"
    USERS ||--o{ POSTS : "yazar"
    CATEGORIES ||--o{ POSTS : "kategori"
    CATEGORIES ||--o{ CATEGORIES : "alt kategori"

    USERS ||--o{ MODEL_HAS_ROLES : "rol ataması"
    ROLES ||--o{ MODEL_HAS_ROLES : ""
    ROLES ||--o{ ROLE_HAS_PERMISSIONS : ""
    PERMISSIONS ||--o{ ROLE_HAS_PERMISSIONS : ""

    USERS {
        bigint id PK
        bigint dealer_id FK
        string name
        string email UK
        string password
        enum status
        text two_factor_secret
        timestamp last_login_at
    }

    DEALERS {
        bigint id PK
        string code UK
        bigint user_id FK "UK - hesap sahibi"
        bigint parent_dealer_id FK "self-ref"
        string company_name
        enum type "sales|content|sales_content"
        enum status "active|passive"
        bigint created_by FK
    }

    DEALER_COMMISSIONS {
        bigint id PK
        bigint dealer_id FK
        enum type "percentage|fixed|hybrid"
        decimal percentage
        decimal fixed_amount
        boolean is_active
        date effective_date
        date end_date
    }

    COMPANIES {
        bigint id PK
        string name UK
        string slug UK
        enum status
        int sort_order
    }

    PACKAGES {
        bigint id PK
        bigint company_id FK
        string name
        enum service_type
        decimal setup_fee
        decimal monthly_fee
        enum status
    }

    SUBSCRIPTIONS {
        bigint id PK
        string reference_no UK
        bigint dealer_id FK
        bigint company_id FK
        bigint package_id FK
        string customer_name
        string tc_no
        enum service_type
        enum status "draft|pending|reviewing|approved|completed|cancelled"
        timestamp completed_at
        bigint created_by FK
    }

    EARNINGS {
        bigint id PK
        bigint dealer_id FK
        bigint subscription_id FK "UK - 1:1"
        bigint dealer_commission_id FK
        enum commission_type
        decimal package_price
        decimal percentage_amount
        decimal fixed_amount
        decimal total_amount
        enum status "pending|partially_paid|paid"
    }

    PAYMENTS {
        bigint id PK
        bigint dealer_id FK
        decimal amount
        date payment_date
        enum payment_type "havale|eft|nakit|diger"
        bigint created_by FK
    }

    CATEGORIES {
        bigint id PK
        bigint parent_id FK "self-ref"
        string name
        string slug UK
    }

    POSTS {
        bigint id PK
        bigint category_id FK
        bigint user_id FK
        string title
        enum type "blog|campaign|announcement|faq|page"
        enum status "draft|published"
    }

    APPLICATIONS {
        bigint id PK
        string name
        bigint package_id FK
        bigint assigned_dealer_id FK
        bigint subscription_id FK
        enum status "new|assigned|contacted|converted|rejected"
    }

    SETTINGS {
        bigint id PK
        string key UK
        text value
        string group
    }

    ACTIVITY_LOGS {
        bigint id PK
        string log_name
        string subject_type
        bigint subject_id
        string causer_type
        bigint causer_id
        json properties
    }

    NOTIFICATIONS {
        uuid id PK
        string type
        string notifiable_type
        bigint notifiable_id
        json data
        timestamp read_at
    }

    ROLES {
        bigint id PK
        string name UK
        string guard_name
    }

    PERMISSIONS {
        bigint id PK
        string name UK
        string guard_name
    }
```

## Tasarım Kararları

**users / dealers ayrımı.** `dealers` tablosu ticari bayi varlığını temsil eder; `users` ise giriş yapan hesaplardır. Bir bayinin sahibi `dealers.user_id` (UNIQUE) ile eşlenir, bayi bünyesindeki ek personel `users.dealer_id` ile aynı bayiye bağlanır. Master Admin kullanıcılarında `dealer_id` NULL'dur. Bu ayrım, bir bayi altında birden fazla operatör hesabı açılmasına izin verir.

**Alt bayi hiyerarşisi.** `dealers.parent_dealer_id` self-referencing FK ile üst bayi/alt bayi yapısı desteklenir (ileride bölge bayiliği gibi ihtiyaçlar için).

**Komisyon geçmişi.** `dealer_commissions` bir bayi için tek satır değil, tarihli geçmiş tutar: `is_active` + `effective_date` + `end_date`. Bu sayede geçmiş hakedişler hesaplandıkları andaki komisyon oranıyla izlenebilir kalır (`earnings.dealer_commission_id`).

**Komisyon hesaplama tetikleyicisi.** `subscriptions.status` alanı `completed` değerine geçtiğinde `SubscriptionObserver` devreye girer, `CommissionService` bayinin o anki aktif komisyonuna göre `earnings` kaydı oluşturur (yüzdelik/sabit/karma). `earnings.subscription_id` UNIQUE olduğu için aynı abonelik için ikinci kez hakediş oluşmaz (idempotent).

**Cari hesap (bakiye).** Ayrı bir "cari hesap" tablosu tutulmaz; bakiye türetilmiş bir değerdir: `SUM(earnings.total_amount) - SUM(payments.amount)`. Bu, `Dealer` modelindeki `total_earnings`, `total_paid`, `remaining_balance` accessor'ları ile hesaplanır — tek kaynaktan gerçek zamanlı doğru veri sağlar.

**RBAC (Spatie Permission).** `roles`, `permissions`, `model_has_roles`, `model_has_permissions`, `role_has_permissions` standart pivot yapısı kullanılır. Üç rol tanımlıdır: `master-admin` (tüm izinler), `dealer-sales` (abonelik + kendi finansı), `dealer-content` (yalnızca `content.*` izinleri — finans ve raporlama izinleri asla verilmez, böylece içerik bayisi finans modülünü ve diğer bayileri göremez).

**Audit / Activity Log.** `activity_logs` tablosu Spatie Activitylog paketiyle entegre çalışacak şekilde tasarlanmıştır (`subject`, `causer`, `properties` polimorfik alanları). Kullanıcı girişi, bayi oluşturma, ödeme girişi, komisyon değişikliği, abonelik ekleme gibi tüm kritik işlemler modellerdeki `LogsActivity` trait'i üzerinden otomatik kaydedilir.

**Referansal bütünlük.** Finansal ve operasyonel geçmişin bozulmaması için: `subscriptions.company_id/package_id` → `restrictOnDelete` (kullanılan firma/paket silinemez), `earnings`/`subscriptions`/`payments` üzerinde `softDeletes` (kalıcı silme yerine arşivleme), `dealer_commissions`/`applications` üzerinde `nullOnDelete` (opsiyonel ilişkiler). Tüm parasal alanlar `decimal(12,2)` ile tutulur (float yuvarlama hatalarını önlemek için).

**Güvenlik.** Şifreler `hashed` cast ile bcrypt/argon; 2FA alanları (`two_factor_secret`, `two_factor_recovery_codes`) `users` tablosunda şifreli JSON olarak saklanır; `sessions` tablosu IP/user-agent takibi yapar (login activity log altyapısı); tüm foreign key'ler `constrained()` ile index'li ve `onDelete` davranışı açıkça tanımlıdır (sessiz veri bütünlüğü hatalarını önler).
