# Müşteri İşlemleri (Customer Transactions) - POS Manager

## Genel Bakış

Bu doküman, **posmanager.apper** projesindeki müşteri işlemleri sisteminin mimarisini açıklar. Sistem, **poscloud.apper** ana projesindeki Account-based double-entry bookkeeping mimarisini referans alır ve POS-specific implementasyonunu dokümante eder.

### Mimari Yaklaşım

POS Manager, ana PosCloud projesindeki finansal ilişki modelini POS senaryolarına uyarlar:

- **PosCustomer**: Remote database'deki müşteri modeli
- **PosCustomerAccount**: Müşteri-POS arasındaki finansal ilişkiyi temsil eden hesap
- **PosCustomerTransactions**: Hesap üzerinden gerçekleşen işlemler
- **Livewire Components**: UI katmanında reaktif component'ler

> **Not:** Bu doküman hem referans mimariyi (PosCloud) hem de POS Manager implementasyonunu kapsar.

---

## 1. Veri Akışı Mimarisi

İşlem sistemi sofistike bir **Hesap-tabanlı** mimari kullanır:

```
Müşteri ↔ Hesap (Account) ↔ İşlem (Transaction)
   ↓              ↓                ↓
şirket()     (bakiye)          (tutarlar)
```

### Temel İlişkiler

- **Customer**: Müşteri modeli, hesaplanabilir varlık olarak davranır
- **Account**: İki taraf arasındaki finansal ilişkiyi temsil eder (örn: Müşteri ↔ Şirket)
- **Transaction**: Hesap üzerinden gerçekleşen finansal hareketleri kaydeder

---

## 2. Ana Bileşenler

### 2.1 Customer Model (`app/Models/Customer.php`)

**Kullanılan Trait'ler:**
- `SoftDeletes`: Silinen kayıtları soft delete ile saklar
- `AccountableTrait`: Finansal ilişki yeteneklerini ekler

**Önemli Metodlar:**

#### `companyAccount()` (Satır 66-71)
```php
public function companyAccount($company = false)
{
    $company = $company ?: company();
    return $this->accountableRelationAccount($company);
}
```
- Müşteri ile mevcut şirket arasındaki Account ilişkisini döndürür
- Varsayılan olarak aktif şirketi kullanır

#### `companyTransactions()` (Satır 73-78)
```php
public function companyTransactions($company = false)
{
    $company = $company ?: company();
    return $this->transactions($company)->order();
}
```
- Müşterinin şirketle olan tüm işlemlerini tarihe göre sıralı olarak döndürür
- `AccountableTrait` içindeki `transactions()` metodunu kullanır

#### `getCompanyAccountBalanceAttribute` (Satır 232-239)
```php
public function getCompanyAccountBalanceAttribute()
{
    if ($this->companyAccount) {
        return $this->companyAccount->balance;
    } else {
        return 0;
    }
}
```
- Laravel accessor (erişimci) - `$customer->companyAccountBalance` şeklinde çağrılır
- Müşterinin cari bakiyesini döndürür
- Hesap yoksa 0 döner

**Diğer Önemli Özellikler:**
- `creditTransactions()`: Kredi işlemleri için ayrı bir sistem (CreditTransaction modeli)
- `creditBalance()`: Müşterinin kredi bakiyesini hesaplar
- `useCreditBalance()`: Kredi bakiyesini satışta kullanır
- `refundCreditBalance()`: Kredi bakiyesini iade eder

---

### 2.2 Transaction Model (`app/Models/Transaction.php`)

**Kullanılan Trait'ler:**
- `SoftDeletes`: Silinen işlemleri saklar
- `HasAccountablePartiesTrait`: Taraflar arası ilişki yönetimi

**Veritabanı Alanları (Fillable):**
- `amount`: İşlem tutarı
- `discount`: İskonto
- `financial_day_id`: Mali gün referansı
- `payment_type`: Ödeme tipi (nakit, kredi kartı, vb.)
- `action_type`: Polimorfik işlem tipi (Sale, Order, vb.)
- `action_id`: Polimorfik işlem ID'si
- `paid_at`: Ödeme tarihi
- `description`: Açıklama

**Model Lifecycle Events (Olaylar):**

#### `creating` Event (Satır 36-44)
```php
static::creating(function (Transaction $transaction) {
    $transaction->first_party_type  = $transaction->account->first_party_type;
    $transaction->first_party_id    = $transaction->account->first_party_id;
    $transaction->second_party_type = $transaction->account->second_party_type;
    $transaction->second_party_id   = $transaction->account->second_party_id;
});
```
- Yeni işlem oluşturulurken, Account modelinden taraf bilgilerini denormalize eder
- Bu bilgiler sadece oluşturma sırasında set edilir, sonra güncellenmez
- Neden? Hesap modellerindeki taraflar değişmez (immutable) olarak tasarlandı

#### `saving` Event (Satır 46-54)
```php
static::saving(function (Transaction $transaction) {
    $diff = $transaction->amount - $transaction->getOriginal('amount');
    
    if ($diff != 0) {
        $transaction->account->increment('balance', $diff);
    }
    
    static::crud($transaction);
});
```
- İşlem kaydedilirken, tutar değişikliği varsa hesabın bakiyesini otomatik günceller
- Fark pozitifse bakiye artar, negatifse azalır
- Platform işlemleri için cache temizleme yapar

#### `deleted` Event (Satır 56-62)
```php
static::deleted(function (Transaction $transaction) {
    $transaction->account->balance -= $transaction->amount;
    $transaction->account()->decrement('balance', $transaction->amount);
    
    static::crud($transaction);
});
```
- İşlem silindiğinde (soft delete), tutarı hesap bakiyesinden çıkarır

#### `restored` Event (Satır 64-69)
```php
static::restored(function (Transaction $transaction) {
    $transaction->account()->increment('balance', $transaction->amount);
    
    static::crud($transaction);
});
```
- Silinen işlem geri yüklendiğinde, tutarı hesap bakiyesine geri ekler

**İlişkiler:**

- `account()`: Transaction'ın ait olduğu Account
- `financialDay()`: İşlemin yapıldığı mali gün
- `action()`: Polimorfik ilişki - Sale, Order gibi modellere bağlanabilir
- `subscriptionPeriods()`: Abonelik dönemleri (fatura işlemleri için)

**Scope'lar:**
- `scopeOrder()`: `paid_at` ve `id`'ye göre azalan sırada sıralar

**Yardımcı Metodlar:**
- `isForPlatform()`: İşlemin platform ile ilgili olup olmadığını kontrol eder

---

### 2.3 Account Model (`app/Models/Account.php`)

**Amaç:**
İki taraf arasındaki finansal ilişkiyi ve bakiyeyi temsil eder.

**Veritabanı Alanları (Fillable):**
- `first_party_type`: Birinci taraf tipi (örn: 'App\Models\Customer')
- `first_party_id`: Birinci taraf ID'si
- `second_party_type`: İkinci taraf tipi (örn: 'App\Models\Company')
- `second_party_id`: İkinci taraf ID'si
- `balance`: Cari bakiye (otomatik hesaplanır)

**İlişkiler:**
- `transactions()`: Bu hesaba ait tüm işlemler (sıralı)

**Yardımcı Metodlar:**
- `recalculate($persist = false)`: Tüm işlemlerin toplamından bakiyeyi yeniden hesaplar

---

### 2.4 CreditTransaction Model (`app/Models/CreditTransaction.php`)

**ÖNEMLİ:** Bu model, regular `Transaction` modelinden **tamamen bağımsızdır**.

**Amaç:**
Müşterilerin ön ödemeli kredi bakiyelerini takip eder.

**Sabitler:**
```php
const TYPE_CREDIT_LOAD   = 'credit_load';    // Kredi yükleme
const TYPE_CREDIT_USE    = 'credit_use';     // Kredi kullanımı
const TYPE_CREDIT_REFUND = 'credit_refund';  // Kredi iadesi
```

**Alanlar:**
- `customer_id`: Müşteri referansı
- `company_id`: Şirket referansı
- `sale_id`: İlgili satış (opsiyonel)
- `amount`: Tutar (pozitif: yükleme, negatif: kullanım/iade)
- `balance_after`: İşlem sonrası bakiye
- `transaction_type`: İşlem tipi (yukarıdaki sabitler)
- `payment_type`: Ödeme tipi
- `description`: Açıklama
- `created_by`: İşlemi yapan kullanıcı

**Not:** CreditTransaction işlemleri, transactions.blade.php view'ında gösterilmez. Ayrı bir sistemdir.

---

### 2.5 Company Model (`app/Models/Company.php`)

Şirket modeli de `AccountableTrait` kullanır, bu sayede:
- Platform ile arasında hesap oluşturabilir
- Müşterilerle finansal ilişki kurabilir
- `platformAccount()`: Şirket-platform arasındaki hesabı döndürür
- `getLivePlatformBalance()`: Platforma olan borcu hesaplar (cache'lenmiş)

---

## 3. AccountableTrait Detayları (`app/Models/AccountableTrait.php`)

Bu trait, Customer ve Company gibi modellere hesaplanabilir yetenekler ekler.

### Temel Metodlar:

#### `accounts()`
Bir modelin tüm hesaplarını döndürür (polimorfik sorgu).

#### `transactions($relation = false)`
```php
// Tüm işlemler
$customer->transactions();

// Belirli bir taraf ile olan işlemler
$customer->transactions($company);
```

#### `getCreateAccount($relation)`
Belirtilen taraf ile hesap oluşturur veya varsa getirir.

#### `createTransaction($relation, $attributes)`
Belirtilen taraf ile yeni bir işlem oluşturur.

#### `accountableRelationAccount($relation)`
Belirli bir taraf ile olan Account ilişkisini döndürür.

### Taraf Sıralama Mantığı (`accountableIsFirstParty`)

Account modelinde iki eşit taraf vardır. Hangisinin `first_party`, hangisinin `second_party` olacağı alfabetik sıralama ile belirlenir:

```php
strcmp($this->accountableGetMorphKey($this), $this->accountableGetMorphKey($relation))
```

Örneğin:
- `App\Models\Company` vs `App\Models\Customer`
- "Company" < "Customer" → Company her zaman first_party olur

Bu tutarlılık sağlar ve aynı iki taraf için birden fazla hesap oluşmasını engeller.

---

## 4. Livewire Component Mimarisi (POS Manager Implementation)

POS Manager, geleneksel controller-based view yerine **Livewire 3.x** component mimarisi kullanır.

### 4.1 Component Hiyerarşisi

```
customers/show.blade.php (Layout)
├── livewire:customer-header (:customerId)
├── livewire:customer-info (:customerId)
└── livewire:customer-transactions (:customerId)
```

### 4.2 CustomerHeader Component

**Dosya:** `app/Livewire/CustomerHeader.php`  
**View:** `resources/views/livewire/customer-header.blade.php`

**Sorumluluklar:**
- Müşteri adını ve başlık bilgisini gösterir
- `mount()` metodunda PosCustomer verisini yükler
- Session'dan `selected_pos_service_id` kullanarak filtreleme yapar

**Örnek Kullanım:**
```blade
<livewire:customer-header :customer-id="$id" />
```

### 4.3 CustomerInfo Component

**Dosya:** `app/Livewire/CustomerInfo.php`  
**View:** `resources/views/livewire/customer-info.blade.php`

**Sorumluluklar:**
- Müşteri detaylarını (ad, telefon, adres) gösterir
- Grid layout ile responsive tasarım
- Null-safe rendering (`?? '-'`)

**Görüntülenen Alanlar:**
- Ad Soyad
- Telefon
- Adres

### 4.4 CustomerTransactions Component

**Dosya:** `app/Livewire/CustomerTransactions.php`  
**View:** `resources/views/livewire/customer-transactions.blade.php`

**Sorumluluklar:**
- Müşteri işlemlerinin listesini gösterir
- Bakiye hesaplaması ve görüntüleme
- Yeni transaction ekleme (modal)
- Vadeli satış tahsilatı (collect sale modal)
- Timeline görünümü (birleşik borç/tahsilat listesi)

**Önemli Property'ler:**
```php
public function getBalanceProperty()
{
    // Database'den Account.balance üzerinden gelir
    return (float) $this->customer->current_balance;
}

public function getTotalDebtProperty()
{
    // Vadeli satışların toplamı
    return $this->deferredSales->sum(...);
}

public function getTotalPaidProperty()
{
    // Transaction'ların toplamı
    return $this->transactions->sum(...);
}

public function getTimelineProperty()
{
    // Deferred sales + transactions birleşik timeline
    // Tarih bazlı sıralı (desc)
}
```

**Modal İşlemleri:**
1. **addTransaction()**: Yeni manuel transaction ekleme
2. **collectSale()**: Vadeli satış tahsilatı
3. **openCollectModal($saleId)**: Tahsilat modal'ını açma

### 4.5 show.blade.php Layout

**Dosya:** `resources/views/customers/show.blade.php`

Sadece Livewire component'lerini çağırır:

```blade
@extends('layouts.app')

@section('content')
<div class="px-4 pb-60">
    {{-- Header --}}
    <div class="sm:flex sm:items-center gap-x-2">
        <livewire:customer-header :customer-id="$id" />
    </div>

    {{-- Customer Info Card --}}
    <div class="max-w-6xl mt-4">
        <livewire:customer-info :customer-id="$id" />
    </div>

    {{-- Transactions Livewire Component --}}
    <div class="max-w-6xl mt-4 pb-60">
        <livewire:customer-transactions :customer-id="$id" />
    </div>
</div>
@endsection
```

---

## 5. Eski View Implementasyonu (Referans - PosCloud)

> **Not:** Aşağıdaki bölüm poscloud.apper ana projesindeki eski controller-based implementasyondur. POS Manager'da Livewire'a geçilmiştir.

### Controller'dan Gelen Veriler (PosCloud)

```php
// CustomerController.php - transactions() metodu
$customer = Customer::with('companyAccount')->forCompany()->find($id);
$transactions = $customer->companyTransactions()->paginate(100);

return view('admin.sales.customers.transactions')
    ->with('customer', $customer)
    ->with('transactions', $transactions);
```

### Eski View Yapısı

#### 1. Bakiye Kutusu
```blade
<div class="info-box">
    <span class="info-box-text">@transUc('models.generic.balance')</span>
    <span class="info-box-number">@currencySymbol {{ formatCurrency($customer->companyAccountBalance) }}</span>
</div>
```

#### 2. İşlem Tablosu (DÜZELTİLMİŞ - Livewire'da)
```blade
{{-- Livewire view'da doğru kullanım --}}
<span class="inline-flex px-2 py-0.5 text-xs rounded-full bg-green-100 text-green-600">
    {{ \App\Constants\PaymentType::getLabel($item['payment_type']) }}
</span>
```

✅ **Livewire implementasyonunda ödeme tipi tekrarı düzeltilmiştir.**

---

## 5. İşlem Oluşturma Akışı

### Adım Adım Süreç:

1. **Hesap Bul/Oluştur**
   ```php
   $account = $customer->getCreateAccount($company);
   ```
   - Customer-Company arasında Account yoksa oluşturur
   - Varsa mevcut olanı döndürür
   - `first_party` ve `second_party` alanlarını alfabetik sıraya göre set eder

2. **İşlem Oluştur**
   ```php
   $transaction = $account->transactions()->create([
       'amount' => 100.50,
       'payment_type' => 1,
       'paid_at' => now(),
       'description' => 'Açıklama',
       // ... diğer alanlar
   ]);
   ```

3. **Creating Event Tetiklenir**
   - Transaction modelindeki `creating` callback çalışır
   - Account'tan taraf bilgilerini kopyalar:
     - `first_party_type`, `first_party_id`
     - `second_party_type`, `second_party_id`

4. **Saving Event Tetiklenir**
   - Tutar farkı hesaplanır (yeni kayıtta tüm tutar)
   - Account bakiyesi güncellenir: `$account->increment('balance', $diff)`
   - Platform işlemleri için cache temizlenir

5. **Sonuç**
   - Transaction veritabanına kaydedilir
   - Account bakiyesi otomatik güncellenir
   - `$customer->companyAccountBalance` artık yeni bakiyeyi yansıtır

---

## 6. İlişki Özeti

```
Customer
  ├─ companyAccount() → Account (Müşteri ↔ Şirket ilişkisi)
  │    └─ transactions() → Transaction koleksiyonu
  ├─ companyTransactions() → Doğrudan transaction sorgusu
  ├─ creditTransactions() → CreditTransaction koleksiyonu (AYRI SİSTEM)
  ├─ companies() → BelongsToMany (çoklu şirket ilişkisi)
  ├─ addresses() → HasMany (adresler)
  └─ firstAddress() → HasOne (ilk adres)

Transaction
  ├─ account() → Account (ait olduğu hesap)
  ├─ action() → MorphTo (Sale, Order, vb. - polimorfik)
  ├─ financialDay() → FinancialDay (mali gün)
  └─ subscriptionPeriods() → HasMany (abonelik dönemleri)

Account
  ├─ first_party → MorphTo (Customer, Company, Platform, vb.)
  ├─ second_party → MorphTo (Customer, Company, Platform, vb.)
  └─ transactions() → HasMany (bu hesaptaki işlemler)

CreditTransaction (BAĞIMSIZ)
  ├─ customer() → BelongsTo
  ├─ company() → BelongsTo
  └─ sale() → BelongsTo (opsiyonel)
```

---

## 7. POS Manager Implementasyon Durumu

### ✅ Tamamlanan İyileştirmeler

#### 1. **Account Model Implementasyonu** ✅
- **Dosya:** `app/Models/PosCustomerAccount.php` (YENİ)
- **Özellikler:**
  - `first_party` / `second_party` polimorfik ilişkiler
  - `balance` alanı (otomatik güncellenen)
  - `recalculate()` metodu (tutarlılık kontrolü için)
  - `scopeBetweenParties()` scope'u

#### 2. **Transaction Lifecycle Events** ✅
- **Dosya:** `app/Models/PosCustomerTransactions.php`
- **Eklenen Event'ler:**
  - `creating`: Account'tan taraf bilgilerini kopyalar
  - `saved`: Tutar değişikliğinde bakiyeyi otomatik günceller
  - `deleted`: Silinen transaction'ı bakiyeden düşer
  - `restored`: Geri yüklenen transaction'ı bakiyeye ekler

#### 3. **PosCustomer Model Güncellemeleri** ✅
- **Dosya:** `app/Models/PosCustomer.php`
- **Yeni Metodlar:**
  - `companyAccount($companyId)`: Account ilişkisini döndürür
  - `getOrCreateCompanyAccount($companyId)`: Account yoksa oluşturur
  - `companyTransactions($companyId)`: Şirket bazlı transaction sorgusu
  - `getCurrentBalanceAttribute()`: Database'den bakiye accessor'ü
  - `createTransaction($attributes, $companyId)`: Account-based transaction oluşturur

#### 4. **Livewire Component Refactoring** ✅
- **Dosya:** `app/Livewire/CustomerTransactions.php`
- **Değişiklikler:**
  - `getBalanceProperty()`: Artık client-side hesaplama yerine database'den geliyor
  - `addTransaction()`: Account-based pattern ile transaction oluşturuyor
  - `collectSale()`: Vadeli satış tahsilatı Account üzerinden kaydediliyor
  - PosCloud API senkronizasyonu non-blocking (API hatası local transaction'ı etkilemiyor)

#### 5. **Payment Type Display Fix** ✅
- **Dosya:** `resources/views/livewire/customer-transactions.blade.php`
- **Satır:** 258
- **Çözüm:** `\App\Constants\PaymentType::getLabel()` kullanılarak tek tip gösterim sağlandı

---

### 🟡 Devam Eden / Gelecek Çalışmalar

#### 6. **CreditTransaction Sistemi** 📋 Planlandı
- **Durum:** Henüz implement edilmedi
- **Öneri:** POS Manager için ön ödemeli kredi sistemi eklenebilir
- **Referans:** CUSTOMERS.md Bölüm 2.4 (CreditTransaction model yapısı)

#### 7. **Transaction Filtreleme** 💡 Öneri
- **Mevcut:** Timeline'da tüm işlemler gösteriliyor
- **Öneri:** Tarih, ödeme tipi, tutar aralığı filtreleri eklenebilir
- **Not:** Livewire component'ine filter property'leri eklenebilir

#### 8. **Error Handling Standardization** 🔄 Geliştirme Aşamasında
- **Mevcut:** Flash messages ile hata/başarı bildirimleri var
- **Öneri:** Unified error handling pattern'i uygulanabilir
- **Referans:** Project specification memory - "Unified Error Handling for Missing Resources"

---

## 8. API Entegrasyonu

API endpoint'leri (`routes/api.php`):

```php
Route::get('customer-balance', [ApiController::class, 'customerBalance']);
Route::get('customer-transactions', [ApiController::class, 'customerTransactions']);
Route::post('customer-transactions', [ApiController::class, 'createCustomerTransaction']);
```

**`customerTransactions()` Metodu** (`ApiController.php` satır 377-405+):
- Tarih filtresi destekler (`date_from`, `date_to`)
- İki yönlü sorgu yapar:
  - Customer first_party ise
  - Customer second_party ise
- Pagination destekler

---

## 9. Örnek Kullanım Senaryoları

### Senaryo 1: Müşteri Nakit Ödeme Yapar

```php
// 100 TL nakit ödeme
$customer->createTransaction($company, [
    'amount' => -100,  // Negatif: müşteri ödüyor
    'payment_type' => PaymentTypes::CASH,
    'paid_at' => now(),
    'description' => 'Nakit ödeme',
]);

// Sonuç:
// - Account balance: -100 TL azalır
// - $customer->companyAccountBalance: güncellenir
```

### Senaryo 2: Müşteri Kredi Kartı ile Borçlanır

```php
// 250 TL kredi kartı ile harcama (borç)
$customer->createTransaction($company, [
    'amount' => 250,  // Pozitif: müşteri borçlanıyor
    'payment_type' => PaymentTypes::CREDIT_CARD,
    'paid_at' => now(),
    'description' => 'Kredi kartı harcama',
]);

// Sonuç:
// - Account balance: +250 TL artar
// - Müşterinin şirkete borcu: 250 TL
```

### Senaryo 3: Kredi Yükleme (CreditTransaction)

```php
// 500 TL kredi yükleme
CreditTransaction::create([
    'customer_id' => $customer->id,
    'company_id' => company()->id,
    'amount' => 500,
    'balance_after' => 500,
    'transaction_type' => CreditTransaction::TYPE_CREDIT_LOAD,
    'payment_type' => PaymentTypes::CASH,
    'description' => 'Kredi yükleme',
    'created_by' => user()->id,
]);

// Sonuç:
// - $customer->creditBalance(): 500 TL
// - Regular Transaction etkilenmez (AYRI SİSTEM)
```

### Senaryo 4: Kredi Kullanımı (Satış Sırasında)

```php
// Satış checkout sırasında
if ($customer->creditBalance() > 0) {
    $customer->useCreditBalance($sale, PaymentTypes::CREDIT_BALANCE);
}

// CreditTransaction oluşturulur:
// - amount: -500 (kullanılan kredi)
// - balance_after: 0
// - transaction_type: TYPE_CREDIT_USE
```

---

## 9b. POS Manager Örnek Kullanım Senaryoları

### Senaryo 1: Livewire Component ile Manuel Transaction Ekleme

```php
// CustomerTransactions.php - addTransaction() metodu
$account = $this->customer->getOrCreateCompanyAccount($selectedPosServiceId);

$transaction = $this->customer->createTransaction([
    'amount' => 150.00,
    'discount' => 0,
    'payment_type' => PaymentType::CASH,
    'paid_at' => now(),
    'description' => 'Nakit ödeme',
], $selectedPosServiceId);

// Sonuç:
// - PosCustomerAccount.balance: +150 TL (otomatik güncellendi via lifecycle event)
// - $customer->current_balance: 150.00
// - PosCloud API'ye sync edildi (non-blocking)
```

### Senaryo 2: Vadeli Satış Tahsilatı

```php
// CustomerTransactions.php - collectSale() metodu
$transaction = $this->customer->createTransaction([
    'amount' => 250.00,
    'discount' => 0,
    'payment_type' => PaymentType::CREDIT_CARD,
    'paid_at' => now(),
    'description' => 'VADELİ SATIŞ TAHSİLAT - Adisyon #123',
], $selectedPosServiceId);

// Lifecycle events otomatik çalışır:
// 1. creating: Account'tan first_party/second_party bilgilerini kopyalar
// 2. saved: Account.balance += 250
```

### Senaryo 3: Bakiye Sorgulama (Database-Driven)

```php
// PosCustomer modelinde
$customer = PosCustomer::find(1);

// Accessor üzerinden database bakiyesi
echo $customer->current_balance; // 450.00 (PosCustomerAccount.balance'dan gelir)

// Doğrudan account üzerinden
$account = $customer->companyAccount();
echo $account->balance; // 450.00
```

### Senaryo 4: Account Recalculation (Tutarlılık Kontrolü)

```php
// Bakiye tutarsızlığı durumunda manuel recalculation
$account = $customer->companyAccount();

// Tüm transaction'lardan bakiyeyi yeniden hesapla
$calculatedBalance = $account->recalculate($persist = true);

// $account->balance artık transaction toplamına eşit
```

---

## 10. Teknik Notlar

### Cache Stratejisi

Platform bakiyesi cache'lenir (`Company::getLivePlatformBalance`):
```php
Cache::remember(companyCacheKey('live-platform-balance', $this), 10800, function () {
    // 3 saat (10800 saniye) cache
});
```

Transaction lifecycle event'lerinde cache temizlenir:
```php
static::crud($transaction) {
    if ($transaction->isForPlatform()) {
        Cache::forget(companyCacheKey('live-platform-balance', $transaction->first_party_id));
    }
}
```

### Soft Deletes

Transaction modeli `SoftDeletes` kullanır:
- Silinen işlemler veritabanında kalır (`deleted_at` set edilir)
- Normal sorgular silinenleri göstermez
- `withTrashed()` ile silinenler de dahil edilebilir
- Geri yükleme (`restore()`) bakiyeyi otomatik düzeltir

### Polimorfik İlişkiler

Transaction'ın `action` ilişkisi polimorfiktir:
```php
public function action()
{
    return $this->morphTo('action', 'action_type', 'action_id')->withTrashed();
}
```

Bu sayede bir Transaction şunlara bağlanabilir:
- `Sale` modeli (satış)
- `Order` modeli (sipariş)
- `Subscription` modeli (abonelik)
- Diğer modeller

---

## 11. Veritabanı Şeması

### PosCloud Ana Proje (Referans)

#### `accounts` Tablosu
```sql
id                  BIGINT UNSIGNED PRIMARY KEY
first_party_type    VARCHAR(255)      -- 'App\Models\Customer', 'App\Models\Company', vb.
first_party_id      BIGINT UNSIGNED
second_party_type   VARCHAR(255)
second_party_id     BIGINT UNSIGNED
balance             DECIMAL(10, 2)    -- Cari bakiye
created_at          TIMESTAMP
updated_at          TIMESTAMP
```

#### `transactions` Tablosu
```sql
id                  BIGINT UNSIGNED PRIMARY KEY
account_id          BIGINT UNSIGNED   -- Foreign Key → accounts.id
amount              DECIMAL(10, 2)    -- İşlem tutarı
discount            DECIMAL(10, 2)    -- İskonto
financial_day_id    BIGINT UNSIGNED   -- Foreign Key → financial_days.id
payment_type        TINYINT           -- Ödeme tipi enum
action_type         VARCHAR(255)      -- Polimorfik: 'App\Models\Sale', vb.
action_id           BIGINT UNSIGNED   -- Polimorfik ID
paid_at             DATETIME          -- Ödeme tarihi
description         TEXT              -- Açıklama
first_party_type    VARCHAR(255)      -- Denormalize edilmiş
first_party_id      BIGINT UNSIGNED   -- Denormalize edilmiş
second_party_type   VARCHAR(255)      -- Denormalize edilmiş
second_party_id     BIGINT UNSIGNED   -- Denormalize edilmiş
deleted_at          TIMESTAMP NULL    -- Soft delete
created_at          TIMESTAMP
updated_at          TIMESTAMP
```

---

### POS Manager Implementation (Remote Database)

#### `customer_accounts` Tablosu
```sql
id                  BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
first_party_type    VARCHAR(255)      -- 'App\Models\PosCustomer' veya 'App\Models\PosCompany'
first_party_id      BIGINT UNSIGNED
second_party_type   VARCHAR(255)      -- Karşı taraf
second_party_id     BIGINT UNSIGNED
balance             DECIMAL(10, 2) DEFAULT 0.00  -- Cari bakiye (otomatik güncellenen)
created_at          TIMESTAMP NULL
updated_at          TIMESTAMP NULL

-- Indexes
INDEX idx_first_party (first_party_type, first_party_id),
INDEX idx_second_party (second_party_type, second_party_id),
UNIQUE KEY idx_unique_account (first_party_type, first_party_id, second_party_type, second_party_id)
```

**Not:** `customer_accounts` tablosu remote POS database'de bulunur (`mysql-remote` connection).

#### `transactions` Tablosu (POS-specific)
```sql
id                  BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
account_id          BIGINT UNSIGNED   -- Foreign Key → customer_accounts.id (POS company ID'si olarak da kullanılır)
financial_day_id    BIGINT UNSIGNED NULL
amount              DECIMAL(10, 2)    -- İşlem tutarı (pozitif: tahsilat, negatif: ödeme)
discount            DECIMAL(10, 2) DEFAULT 0.00
payment_type        TINYINT           -- Ödeme tipi (1: Nakit, 2: Kredi Kartı, vb.)
action_type         VARCHAR(255) NULL -- Polimorfik: 'App\Models\PosSale', vb.
action_id           BIGINT UNSIGNED NULL
first_party_type    VARCHAR(255)      -- Denormalize: Account'tan kopyalanır
first_party_id      BIGINT UNSIGNED   -- Denormalize
second_party_type   VARCHAR(255)      -- Denormalize
second_party_id     BIGINT UNSIGNED   -- Denormalize
paid_at             DATETIME NULL
description         TEXT NULL
deleted_at          TIMESTAMP NULL    -- Soft delete
created_at          TIMESTAMP NULL
updated_at          TIMESTAMP NULL

-- Indexes
INDEX idx_account_id (account_id),
INDEX idx_paid_at (paid_at),
INDEX idx_second_party (second_party_type, second_party_id),
INDEX idx_action (action_type, action_id)
```

**Önemli Notlar:**
1. `account_id` alanı hem `customer_accounts.id` referansı hem de `selected_pos_service_id` filtresi için kullanılır
2. Global scope (`selected_pos`) otomatik filtreleme uygular
3. Lifecycle event'ler ile `balance` otomatik güncellenir

---

## 12. Sonuç ve Özet

### POS Manager - Tamamlanan İyileştirmeler

✅ **Account Model Implementasyonu**: `PosCustomerAccount` modeli oluşturuldu  
✅ **Lifecycle Events**: Transaction event'leri ile otomatik bakiye yönetimi  
✅ **Database-Driven Balance**: Client-side hesaplama yerine database bakiyesi kullanılıyor  
✅ **Livewire Architecture**: Modern, reaktif component mimarisi  
✅ **PosCloud Sync**: API senkronizasyonu non-blocking şekilde implement edildi  
✅ **Documentation**: CUSTOMERS.md POS-specific implementasyonla güncellendi  

### Mimari Karşılaştırma

| Özellik | PosCloud (Referans) | POS Manager (Implementasyon) |
|---------|---------------------|-------------------------------|
| Account Model | `Account` | `PosCustomerAccount` ✅ |
| Transaction Model | `Transaction` | `PosCustomerTransactions` ✅ |
| Customer Model | `Customer` | `PosCustomer` ✅ |
| UI Framework | Controller + Blade | Livewire 3.x ✅ |
| Lifecycle Events | ✅ | ✅ Uygulandı |
| Balance Management | Database accessor | Database accessor ✅ |
| Polymorphic Relations | ✅ | ✅ Uygulandı |
| CreditTransaction | ✅ | 📋 Planlandı |
| Remote DB | Hayır | Evet (`mysql-remote`) |

### Dikkat Edilmesi Gerekenler

⚠️ **Remote Database Connection**: Tüm modeller `mysql-remote` connection kullanır  
⚠️ **Session Dependency**: `selected_pos_service_id` session'dan alınır  
⚠️ **API Sync Failure Handling**: PosCloud API hataları local transaction'ı etkilemez (non-blocking)  
⚠️ **Alfabetik Taraf Sıralaması**: Account oluştururken `strcmp()` ile tutarlılık sağlanır  

### Gelecek Geliştirmeler İçin Öneriler

🔧 **CreditTransaction Sistemi**: Ön ödemeli kredi yönetimi eklenebilir  
🔧 **Advanced Filtering**: Tarih, ödeme tipi, tutar filtreleri Livewire'a eklenebilir  
🔧 **Balance Reconciliation Admin Tool**: Tutarsızlık tespit ve düzeltme aracı  
🔧 **Export Functionality**: Excel/PDF raporlama  
🔧 **Real-time Updates**: WebSocket ile canlı bakiye güncellemeleri  
🔧 **Unit Tests**: Account ve Transaction lifecycle event'leri için test coverage  

---

## 13. Migration Notları

### customer_accounts Tablosu Oluşturma

Eğer `customer_accounts` tablosu remote database'de yoksa, aşağıdaki migration çalıştırılmalıdır:

```php
// database/migrations/XXXX_XX_XX_create_customer_accounts_table.php
public function up()
{
    Schema::connection('mysql-remote')->create('customer_accounts', function (Blueprint $table) {
        $table->id();
        $table->morphs('first_party');
        $table->morphs('second_party');
        $table->decimal('balance', 10, 2)->default(0);
        $table->timestamps();

        $table->unique(['first_party_type', 'first_party_id', 'second_party_type', 'second_party_id']);
    });
}
```

### Mevcut Transaction'lar için Account Oluşturma

Eski transaction kayıtları için account backfill scripti:

```php
// artisan command veya tinker ile
$transactions = PosCustomerTransactions::whereNull('account_id')->get();

foreach ($transactions as $transaction) {
    $customer = PosCustomer::find($transaction->second_party_id);
    if ($customer) {
        $account = $customer->getOrCreateCompanyAccount($transaction->account_id);
        $transaction->update(['account_id' => $account->id]);
    }
}
```

---

**Son Güncelleme:** 15 Mayıs 2026  
**Hazırlayan:** AI Code Assistant  
**Projeler:** poscloud.apper (Referans Mimari) & posmanager.apper (POS Implementation)  
**Değişiklikler:** Account-based architecture implementasyonu, Livewire refactoring, documentation sync

</file_content>