# Checkout Sayfası - Livewire Bileşenleri Tasarım Kılavuzu

Bu doküman, checkout sayfasının ve tüm işlevlerinin Livewire bileşenleri olarak tasarlanması için kapsamlı bir kılavuz sunar. Veritabanı işlemleri detaylıca açıklanmıştır.

## İçindekiler

1. [Genel Mimari](#genel-mimari)
2. [Veritabanı Yapısı](#veritabanı-yapısı)
3. [Ana Livewire Bileşenleri](#ana-livewire-bileşenleri)
4. [Veritabanı İşlemleri](#veritabanı-i̇şlemleri)
5. [İlişkili Modeller](#i̇lişkili-modeller)
6. [Repository Pattern Kullanımı](#repository-pattern-kullanımı)
7. [Örnek Implementasyonlar](#örnek-implementasyonlar)

---

## Genel Mimari

Checkout sistemi aşağıdaki ana bileşenlerden oluşur:

- **CheckoutLivewire**: Ana checkout sayfası bileşeni
- **CartComponent**: Sepet yönetimi
- **PaymentComponent**: Ödeme işlemleri
- **CustomerComponent**: Müşteri seçimi ve yönetimi
- **DiscountComponent**: İndirim uygulamaları
- **OrderSummaryComponent**: Sipariş özeti

### Bileşen İletişimi

```
CheckoutLivewire (Parent)
├── CartComponent
├── CustomerComponent  
├── DiscountComponent
├── PaymentComponent
└── OrderSummaryComponent
```

Bileşenler arası iletişim:
- **Events**: `dispatch()`, `dispatchTo()`
- **Public Properties**: Parent-child veri akışı
- **Listeners**: Alt bileşenlerden gelen event'leri dinleme

---

## Veritabanı Yapısı

### Temel Tablolar

#### 1. orders (Siparişler)
```php
Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->foreignId('customer_id')->nullable()->constrained()->onDelete('set null');
    $table->foreignId('table_id')->nullable()->constrained()->onDelete('set null');
    $table->string('order_number')->unique(); // SIP-20260529-0001
    $table->enum('status', ['pending', 'confirmed', 'preparing', 'ready', 'completed', 'cancelled']);
    $table->enum('type', ['dine-in', 'takeaway', 'delivery']);
    $table->decimal('subtotal', 10, 2)->default(0);
    $table->decimal('discount_amount', 10, 2)->default(0);
    $table->decimal('tax_amount', 10, 2)->default(0);
    $table->decimal('total', 10, 2)->default(0);
    $table->text('notes')->nullable();
    $table->timestamp('ordered_at')->nullable();
    $table->timestamp('completed_at')->nullable();
    $table->softDeletes();
    $table->timestamps();
});
```

**Önemli Notlar:**
- `softDeletes()` kullanımı: Silinen siparişler veritabanında kalır, raporlama için gereklidir
- `order_number`: Benzersiz sipariş numarası (format: SIP-YYYYMMDD-XXXX)
- Foreign key'lerde `onDelete` stratejileri dikkatli seçilmeli

#### 2. order_items (Sipariş Kalemleri)
```php
Schema::create('order_items', function (Blueprint $table) {
    $table->id();
    $table->foreignId('order_id')->constrained()->onDelete('cascade');
    $table->foreignId('product_id')->constrained()->onDelete('restrict');
    $table->string('product_name'); // Snapshot için
    $table->integer('quantity')->default(1);
    $table->decimal('unit_price', 10, 2);
    $table->decimal('total_price', 10, 2);
    $table->json('options')->nullable(); // Ürün seçenekleri (boyut, ekstra vb.)
    $table->text('notes')->nullable();
    $table->integer('sort_order')->default(0); // Sıralama için
    $table->timestamps();
});
```

**Önemli Notlar:**
- `product_name` snapshot: Ürün silinse bile sipariş geçmişinde görünebilir
- `options` JSON field: Dinamik ürün varyasyonları için
- `sort_order`: Kullanıcı tarafından düzenlenen sıralama

#### 3. sales (Satışlar)
```php
Schema::create('sales', function (Blueprint $table) {
    $table->id();
    $table->foreignId('order_id')->constrained()->onDelete('cascade');
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->foreignId('user_id')->constrained()->onDelete('cascade'); // İşlemi yapan kullanıcı
    $table->string('sale_number')->unique(); // SAT-20260529-0001
    $table->enum('payment_method', ['cash', 'credit_card', 'debit_card', 'multinet', 'ticket', 'mixed']);
    $table->decimal('amount', 10, 2);
    $table->decimal('paid_amount', 10, 2)->default(0);
    $table->decimal('change_amount', 10, 2)->default(0);
    $table->enum('status', ['pending', 'completed', 'refunded', 'voided']);
    $table->text('notes')->nullable();
    $table->softDeletes();
    $table->timestamps();
});
```

**Önemli Notlar:**
- Bir order birden fazla sale içerebilir (parçalı ödeme)
- `softDeletes()` ile iptal edilen satışlar saklanır
- `sale_number`: Finansal takip için benzersiz numara

#### 4. sale_items (Satış Kalemleri)
```php
Schema::create('sale_items', function (Blueprint $table) {
    $table->id();
    $table->foreignId('sale_id')->constrained()->onDelete('cascade');
    $table->foreignId('order_item_id')->constrained()->onDelete('cascade');
    $table->integer('quantity');
    $table->decimal('unit_price', 10, 2);
    $table->decimal('total_price', 10, 2);
    $table->timestamps();
});
```

#### 5. discounts (İndirimler)
```php
Schema::create('discounts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->string('name');
    $table->enum('type', ['percentage', 'fixed']);
    $table->decimal('value', 10, 2); // Percentage ise %, fixed ise TL
    $table->enum('scope', ['order', 'product']);
    $table->json('conditions')->nullable(); // Minimum tut, belirli ürünler vb.
    $table->boolean('is_active')->default(true);
    $table->timestamp('valid_from')->nullable();
    $table->timestamp('valid_until')->nullable();
    $table->timestamps();
});
```

#### 6. applied_discounts (Uygulanan İndirimler)
```php
Schema::create('applied_discounts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('order_id')->constrained()->onDelete('cascade');
    $table->foreignId('discount_id')->constrained()->onDelete('cascade');
    $table->decimal('amount', 10, 2); // Uygulanan indirim tutarı
    $table->json('metadata')->nullable(); // Hangi ürünlere uygulandı vb.
    $table->timestamps();
});
```

#### 7. customers (Müşteriler)
```php
Schema::create('customers', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->string('first_name');
    $table->string('last_name');
    $table->string('email')->nullable();
    $table->string('phone')->nullable();
    $table->text('address')->nullable();
    $table->decimal('balance', 10, 2)->default(0); // Bakiye (pozitif/negatif)
    $table->json('preferences')->nullable();
    $table->softDeletes();
    $table->timestamps();
});
```

#### 8. transactions (Finansal İşlemler)
```php
Schema::create('transactions', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->foreignId('customer_id')->nullable()->constrained()->onDelete('set null');
    $table->foreignId('sale_id')->nullable()->constrained()->onDelete('set null');
    $table->enum('type', ['income', 'expense', 'refund', 'adjustment']);
    $table->decimal('amount', 10, 2);
    $table->string('description')->nullable();
    $table->json('metadata')->nullable();
    $table->timestamp('transaction_date');
    $table->timestamps();
});
```

**Önemli Notlar:**
- Tüm finansal hareketler bu tabloda kayıt edilir
- Double-entry bookkeeping prensibi uygulanabilir
- Raporlama ve denetim için kritik

---

## Ana Livewire Bileşenleri

### 1. CheckoutLivewire (Ana Bileşen)

```php
namespace App\Http\Livewire\Checkout;

use Livewire\Component;
use App\Models\Order;
use App\Repositories\CheckoutRepository;

class CheckoutLivewire extends Component
{
    public $orderId;
    public $order;
    public $cartItems = [];
    public $customerId;
    public $tableId;
    public $orderType = 'dine-in';
    public $notes;
    
    protected $listeners = [
        'cartUpdated' => 'refreshOrder',
        'customerSelected' => 'updateCustomer',
        'discountApplied' => 'refreshOrder',
        'paymentCompleted' => 'handlePaymentCompleted',
    ];

    public function mount($orderId = null)
    {
        if ($orderId) {
            $this->orderId = $orderId;
            $this->loadOrder();
        } else {
            $this->createNewOrder();
        }
    }

    public function createNewOrder()
    {
        $this->order = CheckoutRepository::createOrder([
            'company_id' => auth()->user()->company_id,
            'status' => 'pending',
            'type' => $this->orderType,
            'user_id' => auth()->id(),
        ]);
        
        $this->orderId = $this->order->id;
    }

    public function loadOrder()
    {
        // withTrashed() kullanarak soft-deleted ilişkileri de yükle
        $this->order = Order::withTrashed()
            ->with(['items.product', 'customer', 'appliedDiscounts.discount'])
            ->findOrFail($this->orderId);
            
        $this->cartItems = $this->order->items->toArray();
        $this->customerId = $this->order->customer_id;
        $this->tableId = $this->order->table_id;
        $this->notes = $this->order->notes;
    }

    public function refreshOrder()
    {
        $this->loadOrder();
    }

    public function updateCustomer($customerId)
    {
        $this->customerId = $customerId;
        $this->order->update(['customer_id' => $customerId]);
        $this->dispatch('customerUpdated', $customerId);
    }

    public function completeOrder()
    {
        $this->validate([
            'cartItems' => 'required|array|min:1',
        ]);

        $this->order->update([
            'status' => 'confirmed',
            'ordered_at' => now(),
        ]);

        session()->flash('message', 'Sipariş başarıyla oluşturuldu!');
        return redirect()->route('orders.show', $this->orderId);
    }

    public function render()
    {
        return view('livewire.checkout.checkout-livewire', [
            'order' => $this->order,
            'cartItems' => $this->cartItems,
        ]);
    }
}
```

### 2. CartComponent (Sepet Yönetimi)

```php
namespace App\Http\Livewire\Checkout;

use Livewire\Component;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use Illuminate\Support\Facades\DB;

class CartComponent extends Component
{
    public $orderId;
    public $items = [];
    public $showAddProductModal = false;
    public $selectedProductId;
    public $quantity = 1;
    public $itemNotes;

    protected $rules = [
        'selectedProductId' => 'required|exists:products,id',
        'quantity' => 'required|integer|min:1|max:999',
    ];

    public function mount($orderId)
    {
        $this->orderId = $orderId;
        $this->loadCartItems();
    }

    public function loadCartItems()
    {
        $this->items = OrderItem::where('order_id', $this->orderId)
            ->with('product')
            ->orderBy('sort_order')
            ->get()
            ->toArray();
    }

    public function addProduct()
    {
        $this->validate();

        $product = Product::findOrFail($this->selectedProductId);

        DB::transaction(function () use ($product) {
            OrderItem::create([
                'order_id' => $this->orderId,
                'product_id' => $product->id,
                'product_name' => $product->name, // Snapshot
                'quantity' => $this->quantity,
                'unit_price' => $product->price,
                'total_price' => $product->price * $this->quantity,
                'notes' => $this->itemNotes,
                'sort_order' => OrderItem::where('order_id', $this->orderId)->max('sort_order') + 1,
            ]);

            // Order toplamını güncelle
            $this->updateOrderTotals();
        });

        $this->reset(['selectedProductId', 'quantity', 'itemNotes']);
        $this->showAddProductModal = false;
        
        $this->loadCartItems();
        $this->dispatch('cartUpdated');
    }

    public function updateQuantity($itemId, $quantity)
    {
        $item = OrderItem::findOrFail($itemId);
        
        DB::transaction(function () use ($item, $quantity) {
            $item->update([
                'quantity' => $quantity,
                'total_price' => $item->unit_price * $quantity,
            ]);

            $this->updateOrderTotals();
        });

        $this->loadCartItems();
        $this->dispatch('cartUpdated');
    }

    public function removeItem($itemId)
    {
        $item = OrderItem::findOrFail($itemId);
        
        DB::transaction(function () use ($item) {
            $item->delete();
            $this->updateOrderTotals();
        });

        $this->loadCartItems();
        $this->dispatch('cartUpdated');
    }

    public function updateSortOrder($sortedItems)
    {
        DB::transaction(function () use ($sortedItems) {
            foreach ($sortedItems as $index => $itemData) {
                OrderItem::where('id', $itemData['id'])
                    ->update(['sort_order' => $index]);
            }
        });

        $this->loadCartItems();
    }

    protected function updateOrderTotals()
    {
        $order = Order::findOrFail($this->orderId);
        
        $subtotal = OrderItem::where('order_id', $this->orderId)
            ->sum('total_price');

        // İndirimleri hesapla
        $discountAmount = $this->calculateDiscounts($subtotal);
        
        // KDV hesapla
        $taxAmount = $this->calculateTax($subtotal - $discountAmount);
        
        $total = $subtotal - $discountAmount + $taxAmount;

        $order->update([
            'subtotal' => $subtotal,
            'discount_amount' => $discountAmount,
            'tax_amount' => $taxAmount,
            'total' => $total,
        ]);
    }

    protected function calculateDiscounts($subtotal)
    {
        // Applied discounts'tan hesapla
        return AppliedDiscount::where('order_id', $this->orderId)
            ->sum('amount');
    }

    protected function calculateTax($amount)
    {
        $taxRate = config('settings.tax_rate', 18);
        return $amount * ($taxRate / 100);
    }

    public function render()
    {
        return view('livewire.checkout.cart-component', [
            'items' => $this->items,
        ]);
    }
}
```

### 3. PaymentComponent (Ödeme İşlemleri)

```php
namespace App\Http\Livewire\Checkout;

use Livewire\Component;
use App\Models\Order;
use App\Models\Sale;
use App\Models\Transaction;
use Illuminate\Support\Facades\DB;

class PaymentComponent extends Component
{
    public $orderId;
    public $paymentMethod = 'cash';
    public $amountPaid = 0;
    public $showPaymentModal = false;
    public $orderTotal = 0;

    protected $rules = [
        'paymentMethod' => 'required|in:cash,credit_card,debit_card,multinet,ticket,mixed',
        'amountPaid' => 'required|numeric|min:0',
    ];

    public function mount($orderId)
    {
        $this->orderId = $orderId;
        $order = Order::findOrFail($orderId);
        $this->orderTotal = $order->total;
        $this->amountPaid = $order->total;
    }

    public function processPayment()
    {
        $this->validate();

        $order = Order::findOrFail($this->orderId);

        if ($this->amountPaid < $order->total) {
            session()->flash('error', 'Ödenen tutar sipariş toplamından az olamaz!');
            return;
        }

        DB::transaction(function () use ($order) {
            // Sale oluştur
            $sale = Sale::create([
                'order_id' => $order->id,
                'company_id' => $order->company_id,
                'user_id' => auth()->id(),
                'sale_number' => $this->generateSaleNumber(),
                'payment_method' => $this->paymentMethod,
                'amount' => $order->total,
                'paid_amount' => $this->amountPaid,
                'change_amount' => $this->amountPaid - $order->total,
                'status' => 'completed',
            ]);

            // Transaction kaydı oluştur
            Transaction::create([
                'company_id' => $order->company_id,
                'customer_id' => $order->customer_id,
                'sale_id' => $sale->id,
                'type' => 'income',
                'amount' => $order->total,
                'description' => "Satış #{$sale->sale_number}",
                'transaction_date' => now(),
            ]);

            // Order status güncelle
            $order->update([
                'status' => 'completed',
                'completed_at' => now(),
            ]);
        });

        $this->showPaymentModal = false;
        $this->dispatch('paymentCompleted', $order->id);
        
        session()->flash('message', 'Ödeme başarıyla tamamlandı!');
    }

    protected function generateSaleNumber()
    {
        $prefix = 'SAT';
        $date = now()->format('Ymd');
        $lastSale = Sale::whereDate('created_at', today())
            ->orderBy('id', 'desc')
            ->first();
        
        $sequence = $lastSale ? (intval(substr($lastSale->sale_number, -4)) + 1) : 1;
        
        return "{$prefix}-{$date}-" . str_pad($sequence, 4, '0', STR_PAD_LEFT);
    }

    public function render()
    {
        return view('livewire.checkout.payment-component', [
            'orderTotal' => $this->orderTotal,
            'changeAmount' => $this->amountPaid - $this->orderTotal,
        ]);
    }
}
```

### 4. DiscountComponent (İndirim Yönetimi)

```php
namespace App\Http\Livewire\Checkout;

use Livewire\Component;
use App\Models\Order;
use App\Models\Discount;
use App\Models\AppliedDiscount;
use Illuminate\Support\Facades\DB;

class DiscountComponent extends Component
{
    public $orderId;
    public $availableDiscounts = [];
    public $selectedDiscountId;
    public $customDiscountAmount = 0;
    public $customDiscountType = 'fixed'; // fixed veya percentage
    public $showCustomDiscountForm = false;

    public function mount($orderId)
    {
        $this->orderId = $orderId;
        $this->loadAvailableDiscounts();
    }

    public function loadAvailableDiscounts()
    {
        $this->availableDiscounts = Discount::where('company_id', auth()->user()->company_id)
            ->where('is_active', true)
            ->where(function ($query) {
                $query->whereNull('valid_from')
                    ->orWhere('valid_from', '<=', now());
            })
            ->where(function ($query) {
                $query->whereNull('valid_until')
                    ->orWhere('valid_until', '>=', now());
            })
            ->get();
    }

    public function applyDiscount($discountId)
    {
        $discount = Discount::findOrFail($discountId);
        $order = Order::findOrFail($this->orderId);

        DB::transaction(function () use ($discount, $order) {
            // Mevcut indirimleri temizle (isteğe bağlı)
            AppliedDiscount::where('order_id', $order->id)->delete();

            // İndirim tutarını hesapla
            $discountAmount = $this->calculateDiscountAmount($discount, $order->subtotal);

            // Uygulanan indirimi kaydet
            AppliedDiscount::create([
                'order_id' => $order->id,
                'discount_id' => $discount->id,
                'amount' => $discountAmount,
                'metadata' => [
                    'type' => $discount->type,
                    'value' => $discount->value,
                ],
            ]);

            // Order toplamını güncelle
            $this->updateOrderTotals($order);
        });

        session()->flash('message', 'İndirim başarıyla uygulandı!');
        $this->dispatch('discountApplied');
    }

    public function applyCustomDiscount()
    {
        $order = Order::findOrFail($this->orderId);

        $this->validate([
            'customDiscountAmount' => 'required|numeric|min:0',
            'customDiscountType' => 'required|in:fixed,percentage',
        ]);

        if ($this->customDiscountType === 'percentage' && $this->customDiscountAmount > 100) {
            session()->flash('error', 'Yüzde indirim 100\'den büyük olamaz!');
            return;
        }

        DB::transaction(function () use ($order) {
            AppliedDiscount::where('order_id', $order->id)->delete();

            $discountAmount = $this->customDiscountType === 'percentage'
                ? ($order->subtotal * $this->customDiscountAmount / 100)
                : $this->customDiscountAmount;

            AppliedDiscount::create([
                'order_id' => $order->id,
                'discount_id' => null,
                'amount' => $discountAmount,
                'metadata' => [
                    'type' => $this->customDiscountType,
                    'value' => $this->customDiscountAmount,
                    'custom' => true,
                ],
            ]);

            $this->updateOrderTotals($order);
        });

        $this->showCustomDiscountForm = false;
        $this->reset(['customDiscountAmount', 'customDiscountType']);
        
        session()->flash('message', 'Özel indirim uygulandı!');
        $this->dispatch('discountApplied');
    }

    public function removeDiscount()
    {
        DB::transaction(function () {
            AppliedDiscount::where('order_id', $this->orderId)->delete();
            
            $order = Order::findOrFail($this->orderId);
            $this->updateOrderTotals($order);
        });

        session()->flash('message', 'İndirim kaldırıldı!');
        $this->dispatch('discountApplied');
    }

    protected function calculateDiscountAmount($discount, $subtotal)
    {
        if ($discount->type === 'percentage') {
            return $subtotal * ($discount->value / 100);
        }

        return min($discount->value, $subtotal); // İndirim subtotal'dan fazla olamaz
    }

    protected function updateOrderTotals($order)
    {
        $discountAmount = AppliedDiscount::where('order_id', $order->id)->sum('amount');
        
        $taxableAmount = max(0, $order->subtotal - $discountAmount);
        $taxRate = config('settings.tax_rate', 18);
        $taxAmount = $taxableAmount * ($taxRate / 100);
        
        $total = $order->subtotal - $discountAmount + $taxAmount;

        $order->update([
            'discount_amount' => $discountAmount,
            'tax_amount' => $taxAmount,
            'total' => $total,
        ]);
    }

    public function render()
    {
        $appliedDiscount = AppliedDiscount::where('order_id', $this->orderId)->first();
        
        return view('livewire.checkout.discount-component', [
            'appliedDiscount' => $appliedDiscount,
        ]);
    }
}
```

---

## Veritabanı İşlemleri

### Transaction Yönetimi

Tüm kritik veritabanı işlemleri `DB::transaction()` içinde yapılmalıdır:

```php
DB::transaction(function () {
    // Birden fazla tabloya yazma işlemi
    // Hata durumunda hepsi rollback olur
});
```

**Kullanım Senaryoları:**
1. Sipariş oluşturma + ürün ekleme
2. Ödeme alma + sale oluşturma + transaction kaydı
3. İndirim uygulama + order totals güncelleme
4. Stok düşme + satış kaydı

### Soft Deletes Kullanımı

```php
// Normal sorgu (silinmemiş kayıtlar)
Order::findOrFail($id);

// Silinmiş kayıtları da dahil et
Order::withTrashed()->findOrFail($id);

// Sadece silinmiş kayıtlar
Order::onlyTrashed()->get();

// Soft delete
$order->delete();

// Geri yükle
$order->restore();

// Kalıcı silme
$order->forceDelete();
```

**Önemli Noktalar:**
- Raporlama için soft delete kullanılmalı
- `withTrashed()` unutulmamalı (ilişkilerde)
- Foreign key constraint'lerde `onDelete('cascade')` dikkatli kullanılmalı

### Eager Loading Optimizasyonu

```php
// ❌ Kötü: N+1 query problemi
$orders = Order::all();
foreach ($orders as $order) {
    echo $order->customer->name; // Her iterasyonda query
}

// ✅ İyi: Eager loading
$orders = Order::with('customer', 'items.product')->get();
foreach ($orders as $order) {
    echo $order->customer->name; // Tek query
}

// ✅ Conditional eager loading
$orders = Order::with(['items' => function ($query) {
    $query->where('quantity', '>', 0);
}])->get();
```

### Query Scopes

Model'lerde reusable query scopes tanımlayın:

```php
// Order Model
public function scopePending($query)
{
    return $query->where('status', 'pending');
}

public function scopeToday($query)
{
    return $query->whereDate('created_at', today());
}

public function scopeWithTotals($query)
{
    return $query->withSum('items', 'total_price');
}

// Kullanım
$todayPendingOrders = Order::pending()->today()->get();
```

---

## İlişkili Modeller

### Order Model İlişkileri

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Order extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'company_id',
        'customer_id',
        'table_id',
        'order_number',
        'status',
        'type',
        'subtotal',
        'discount_amount',
        'tax_amount',
        'total',
        'notes',
        'ordered_at',
        'completed_at',
    ];

    protected $casts = [
        'ordered_at' => 'datetime',
        'completed_at' => 'datetime',
        'subtotal' => 'decimal:2',
        'discount_amount' => 'decimal:2',
        'tax_amount' => 'decimal:2',
        'total' => 'decimal:2',
    ];

    // Relationships
    public function company()
    {
        return $this->belongsTo(Company::class);
    }

    public function customer()
    {
        return $this->belongsTo(Customer::class)->withTrashed();
    }

    public function table()
    {
        return $this->belongsTo(Table::class);
    }

    public function items()
    {
        return $this->hasMany(OrderItem::class)->orderBy('sort_order');
    }

    public function sales()
    {
        return $this->hasMany(Sale::class);
    }

    public function appliedDiscounts()
    {
        return $this->hasMany(AppliedDiscount::class);
    }

    // Accessors
    public function getFormattedTotalAttribute()
    {
        return number_format($this->total, 2, ',', '.') . ' ₺';
    }

    // Mutators
    public function setOrderNumberAttribute($value)
    {
        $this->attributes['order_number'] = strtoupper($value);
    }
}
```

### Repository Pattern

```php
namespace App\Repositories;

use App\Models\Order;
use App\Models\OrderItem;
use Illuminate\Support\Facades\DB;

class CheckoutRepository
{
    public static function createOrder(array $data): Order
    {
        return DB::transaction(function () use ($data) {
            $order = Order::create([
                ...$data,
                'order_number' => self::generateOrderNumber(),
            ]);

            return $order;
        });
    }

    public static function addItemToOrder(int $orderId, array $itemData): OrderItem
    {
        return DB::transaction(function () use ($orderId, $itemData) {
            $order = Order::findOrFail($orderId);
            
            $item = OrderItem::create([
                'order_id' => $orderId,
                ...$itemData,
            ]);

            self::recalculateOrderTotals($order);

            return $item;
        });
    }

    public static function completeOrder(int $orderId): Order
    {
        return DB::transaction(function () use ($orderId) {
            $order = Order::findOrFail($orderId);
            
            $order->update([
                'status' => 'completed',
                'completed_at' => now(),
            ]);

            return $order;
        });
    }

    protected static function generateOrderNumber(): string
    {
        $prefix = 'SIP';
        $date = now()->format('Ymd');
        $lastOrder = Order::whereDate('created_at', today())
            ->orderBy('id', 'desc')
            ->first();
        
        $sequence = $lastOrder ? (intval(substr($lastOrder->order_number, -4)) + 1) : 1;
        
        return "{$prefix}-{$date}-" . str_pad($sequence, 4, '0', STR_PAD_LEFT);
    }

    protected static function recalculateOrderTotals(Order $order): void
    {
        $subtotal = $order->items()->sum('total_price');
        $discountAmount = $order->appliedDiscounts()->sum('amount');
        
        $taxableAmount = max(0, $subtotal - $discountAmount);
        $taxRate = config('settings.tax_rate', 18);
        $taxAmount = $taxableAmount * ($taxRate / 100);
        
        $total = $subtotal - $discountAmount + $taxAmount;

        $order->update([
            'subtotal' => $subtotal,
            'discount_amount' => $discountAmount,
            'tax_amount' => $taxAmount,
            'total' => $total,
        ]);
    }
}
```

---

## Örnek Implementasyonlar

### Senaryo 1: Yeni Sipariş Oluşturma

```php
// CheckoutLivewire'de
public function createNewOrder()
{
    $this->order = CheckoutRepository::createOrder([
        'company_id' => auth()->user()->company_id,
        'status' => 'pending',
        'type' => $this->orderType,
        'user_id' => auth()->id(),
        'table_id' => $this->tableId,
    ]);
    
    $this->orderId = $this->order->id;
    
    session()->flash('message', 'Yeni sipariş oluşturuldu!');
}
```

### Senaryo 2: Sepete Ürün Ekleme

```php
// CartComponent'te
public function addProduct()
{
    $this->validate();

    $product = Product::findOrFail($this->selectedProductId);

    DB::transaction(function () use ($product) {
        OrderItem::create([
            'order_id' => $this->orderId,
            'product_id' => $product->id,
            'product_name' => $product->name,
            'quantity' => $this->quantity,
            'unit_price' => $product->price,
            'total_price' => $product->price * $this->quantity,
            'notes' => $this->itemNotes,
            'sort_order' => $this->getNextSortOrder(),
        ]);

        $this->updateOrderTotals();
    });

    $this->dispatch('cartUpdated');
}
```

### Senaryo 3: Ödeme Alma

```php
// PaymentComponent'te
public function processPayment()
{
    $this->validate();

    $order = Order::findOrFail($this->orderId);

    DB::transaction(function () use ($order) {
        // 1. Sale oluştur
        $sale = Sale::create([...]);

        // 2. Transaction kaydı
        Transaction::create([...]);

        // 3. Order status güncelle
        $order->update(['status' => 'completed', 'completed_at' => now()]);

        // 4. Müşteri bakiyesini güncelle (varsa)
        if ($order->customer) {
            $order->customer->increment('balance', $order->total);
        }
    });

    $this->dispatch('paymentCompleted', $order->id);
}
```

### Senaryo 4: İndirim Uygulama

```php
// DiscountComponent'te
public function applyDiscount($discountId)
{
    $discount = Discount::findOrFail($discountId);
    $order = Order::findOrFail($this->orderId);

    DB::transaction(function () use ($discount, $order) {
        // Eski indirimleri temizle
        AppliedDiscount::where('order_id', $order->id)->delete();

        // Yeni indirim hesapla ve kaydet
        $amount = $this->calculateDiscountAmount($discount, $order->subtotal);
        
        AppliedDiscount::create([
            'order_id' => $order->id,
            'discount_id' => $discount->id,
            'amount' => $amount,
        ]);

        // Toplamıları güncelle
        $this->updateOrderTotals($order);
    });

    $this->dispatch('discountApplied');
}
```

---

## Best Practices

### 1. Validasyon

```php
protected $rules = [
    'orderId' => 'required|exists:orders,id',
    'quantity' => 'required|integer|min:1|max:999',
    'paymentMethod' => 'required|in:cash,credit_card,debit_card',
];

public function updated($propertyName)
{
    $this->validateOnly($propertyName);
}
```

### 2. Error Handling

```php
try {
    DB::transaction(function () {
        // İşlemler
    });
    session()->flash('message', 'İşlem başarılı!');
} catch (\Exception $e) {
    Log::error('Checkout error: ' . $e->getMessage());
    session()->flash('error', 'Bir hata oluştu: ' . $e->getMessage());
}
```

### 3. Authorization

```php
public function mount($orderId)
{
    $order = Order::findOrFail($orderId);
    
    // Authorization check
    abort_unless($order->company_id === auth()->user()->company_id, 403);
}
```

### 4. Caching

```php
// Sık erişilen verileri cache'le
public function getAvailableDiscountsProperty()
{
    return Cache::remember("discounts.{$this->orderId}", 300, function () {
        return Discount::where('is_active', true)->get();
    });
}
```

### 5. Logging

```php
Log::info('Order created', [
    'order_id' => $order->id,
    'user_id' => auth()->id(),
    'total' => $order->total,
]);
```

---

## Performans Optimizasyonları

1. **Eager Loading**: N+1 query problemini önle
2. **Database Indexing**: Sık sorgulanan kolonlara index ekle
3. **Query Caching**: Tekrarlayan sorguları cache'le
4. **Lazy Loading**: Gereksiz veri yüklemesinden kaçın
5. **Batch Operations**: Çoklu insert/update işlemlerinde batch kullan

```php
// Index örnekleri
Schema::table('orders', function (Blueprint $table) {
    $table->index('company_id');
    $table->index('status');
    $table->index('created_at');
    $table->index(['company_id', 'status']); // Composite index
});
```

---

## Güvenlik Önlemleri

1. **Mass Assignment Protection**: `$fillable` kullan
2. **Authorization**: Her işlemde yetki kontrolü
3. **Input Validation**: Tüm inputları validate et
4. **SQL Injection Prevention**: Eloquent ORM kullan
5. **XSS Protection**: Blade template'lerinde {{ }} kullan

---

## Test Stratejisi

```php
// Feature Test Örneği
public function test_checkout_process()
{
    $order = Order::factory()->create(['status' => 'pending']);
    
    Livewire::test(CheckoutLivewire::class, ['orderId' => $order->id])
        ->set('cartItems', [...])
        ->call('completeOrder')
        ->assertSessionHas('message')
        ->assertRedirect();
}
```

---

## Sonuç

Bu kılavuz, checkout sayfasının Livewire bileşenleri olarak tasarlanması için gerekli tüm veritabanı işlemlerini ve best practice'leri içermektedir. Her bileşen sorumluluklarına göre ayrıştırılmış, transaction yönetimi ve error handling mekanizmaları detaylandırılmıştır.

Geliştirme sırasında:
- Repository pattern kullanarak business logic'i ayırın
- DB::transaction() ile atomicity sağlayın
- Soft deletes ile veri kaybını önleyin
- Eager loading ile performansı optimize edin
- Comprehensive validation ve authorization uygulayın
