ESNEK ÇÖZÜM HER İKİ SENARYO 11 Ocak 2025 - v3

ESNEK KDV SİSTEMİ

Hem KDV Dahil, Hem KDV Hariç Fiyat Girişi - Müşteriye Göre Ayarlanabilir

📝 Esnek Sistem - Ne İstiyoruz?

İhtiyaç: Bazı müşteriler fiyatı KDV dahil, bazıları KDV hariç girmek istiyor.

Müşteri A

"Ben fiyatı KDV hariç gireceğim. 600 TL yazacağım, müşteri 600 + 120 = 720 TL ödeyecek."

Müşteri B

"Ben fiyatı KDV dahil gireceğim. 720 TL yazacağım, müşteri 720 TL ödeyecek. (içinde KDV var)"

Çözüm: Admin panelde her cycle için "Fiyat Türü" seçimi ekleyelim: KDV Dahil veya KDV Hariç. Sistem otomatik hesaplar!

⚙️ Esnek Sistem Tasarımı

1️⃣ Database - price_type Field Ekle

// billing_cycles JSON yapısı (GÜNCELLENMİŞ) { "monthly": { "price": 600, "price_type": "without_tax", // ← YENİ! "with_tax" veya "without_tax" "label": {"tr": "Aylık", "en": "Monthly"}, "duration_days": 30 }, "yearly": { "price": 7200, "price_type": "with_tax", // ← Bu cycle KDV dahil! "label": {"tr": "Yıllık", "en": "Yearly"}, "duration_days": 365, "compare_price": 8640 // İndirim gösterimi için } }

2️⃣ İki Senaryo Nasıl Çalışır?

Senaryo 1: KDV Hariç (without_tax)

Admin Girişi:
price: 600 TL
price_type: "without_tax"
Bridge İşlemi:
unit_price = 600 // Direkt tax_rate = 20% // Ayrıştırma YOK!
Cart Hesaplama:
Ara Toplam: 600 TL
KDV (20%): 120 TL
Toplam: 720 TL ✅
Frontend Gösterim:
600 TL + KDV
KDV dahil: 720 TL

Senaryo 2: KDV Dahil (with_tax)

Admin Girişi:
price: 720 TL
price_type: "with_tax"
Bridge İşlemi:
priceWithTax = 720 taxRate = 20% unit_price = 720 / 1.20 = 600 TL // KDV ayrıştırması yapılır!
Cart Hesaplama:
Ara Toplam: 600 TL
KDV (20%): 120 TL
Toplam: 720 TL ✅
Frontend Gösterim:
720 TL (KDV dahil)
veya: 600 TL + 120 TL KDV

Sonuç: Her iki senaryoda da müşteri 720 TL ödüyor! Fark sadece admin'in nasıl girdiği. Sistem otomatik hesaplıyor.

💻 Kod Değişiklikleri (Adım Adım)

1

Migration Oluştur

Komut: php artisan make:migration add_price_type_to_subscription_plans --path=Modules/Subscription/database/migrations/tenant
Schema::table('subscription_plans', function (Blueprint $table) { // billing_cycles zaten JSON, içeriğe price_type ekleneceği için // migration'a gerek YOK! Sadece döküman amaçlı. // VEYA güvenlik için default price_type ekle: $table->enum('default_price_type', ['with_tax', 'without_tax']) ->default('without_tax') ->after('tax_rate') ->comment('Yeni cycle oluştururken varsayılan fiyat türü'); });
Not: price_type her cycle için ayrı olduğundan, billing_cycles JSON içinde saklanır. Migration sadece default_price_type ekler (opsiyonel).
2

Model - SubscriptionPlan.php

Modules/Subscription/App/Models/SubscriptionPlan.php
/** * Cycle'ın fiyat türünü al (with_tax / without_tax) * Default: without_tax (Shop pattern ile uyumlu) */ public function getCyclePriceType(string $cycleKey): string { $cycle = $this->getCycle($cycleKey); return $cycle['price_type'] ?? $this->default_price_type ?? 'without_tax'; } /** * Cycle'ın KDV HARİÇ fiyatını al (her zaman base price döner) */ public function getCycleBasePrice(string $cycleKey): ?float { $cycle = $this->getCycle($cycleKey); if (!$cycle || !isset($cycle['price'])) { return null; } $price = (float) $cycle['price']; $priceType = $this->getCyclePriceType($cycleKey); $taxRate = $this->tax_rate ?? 20.0; // Eğer fiyat KDV dahil girilmişse, KDV'yi ayrıştır if ($priceType === 'with_tax') { return $price / (1 + $taxRate / 100); } // Fiyat zaten KDV hariç return $price; } /** * Cycle'ın KDV DAHİL fiyatını al (runtime hesaplama) */ public function getCyclePriceWithTax(string $cycleKey): ?float { $basePrice = $this->getCycleBasePrice($cycleKey); if (!$basePrice) { return null; } $taxRate = $this->tax_rate ?? 20.0; return $basePrice * (1 + $taxRate / 100); }
3

Bridge - SubscriptionCartBridge.php

Modules/Subscription/App/Services/SubscriptionCartBridge.php (Satır 98-134)
protected function getSubscriptionPriceInfo(SubscriptionPlan $plan, array $cycle): array { $cycleKey = $cycle['key'] ?? 'unknown'; // ✅ YENİ: Model accessor kullan (price_type'a göre otomatik hesaplar) $basePrice = $plan->getCycleBasePrice($cycleKey); if (!$basePrice) { throw new \Exception("Cycle fiyatı bulunamadı: {$cycleKey}"); } $priceType = $plan->getCyclePriceType($cycleKey); $priceWithTax = $plan->getCyclePriceWithTax($cycleKey); $taxRate = $plan->tax_rate ?? 20.0; Log::info('🛒 SubscriptionCartBridge - Flexible Price System', [ 'plan_id' => $plan->subscription_plan_id, 'cycle_key' => $cycleKey, 'input_price' => $cycle['price'], 'price_type' => $priceType, 'base_price' => $basePrice, // Cart'a gönderilen (KDV hariç) 'price_with_tax' => $priceWithTax, 'tax_rate' => $taxRate, ]); return [ 'unit_price' => $basePrice, // ✅ Her zaman KDV hariç (Cart için) 'currency' => $plan->currency ?? 'TRY', 'discount_amount' => 0, 'tax_rate' => $taxRate, ]; }
4

Admin Panel - Cycle Form

Admin Panel - Cycle Ekleme/Düzenleme Formu
{{-- Her cycle için price_type seçimi --}}
KDV Hariç: Girilen fiyata KDV eklenecek (örn: 600 TL → 720 TL toplam)
KDV Dahil: Girilen fiyat son tutar (örn: 720 TL → 720 TL toplam, içinde KDV var)
@if($currentCycle['price_type'] ?? 'without_tax' === 'without_tax') KDV Dahil Toplam: {{ number_format($currentCycle['price'] * 1.20, 2) }} TL @else KDV Hariç: {{ number_format($currentCycle['price'] / 1.20, 2) }} TL (KDV: {{ number_format($currentCycle['price'] - ($currentCycle['price'] / 1.20), 2) }} TL) @endif
5

Frontend - Fiyat Gösterimi

subscription-plans.blade.php
@foreach($cycles as $cycleKey => $cycle) @php $price = $cycle['price'] ?? 0; $priceType = $cycle['price_type'] ?? 'without_tax'; if ($priceType === 'without_tax') { // KDV hariç girilmiş $displayPrice = $price; $priceWithTax = $price * 1.20; $taxAmount = $priceWithTax - $price; } else { // KDV dahil girilmiş $priceWithTax = $price; $displayPrice = $price / 1.20; $taxAmount = $price - $displayPrice; } @endphp
@if($priceType === 'without_tax') {{-- Senaryo 1: KDV Hariç --}}
{{ number_format($displayPrice, 2) }} TL

+ KDV / KDV dahil: {{ number_format($priceWithTax, 2) }} TL

@else {{-- Senaryo 2: KDV Dahil --}}
{{ number_format($priceWithTax, 2) }} TL

(KDV dahil) KDV hariç: {{ number_format($displayPrice, 2) }} TL

@endif
@endforeach

🗄️ Mevcut Planları Güncelleme (Opsiyonel)

Eğer database'de mevcut subscription planları varsa, bunlara price_type eklemen gerekebilir:

SQL Script (Manual)
-- Tüm mevcut cycle'lara price_type ekle (varsayılan: without_tax) UPDATE subscription_plans SET billing_cycles = JSON_SET( billing_cycles, '$.monthly.price_type', 'without_tax', '$.yearly.price_type', 'without_tax' ) WHERE billing_cycles IS NOT NULL; -- VEYA spesifik plan'lar için: UPDATE subscription_plans SET billing_cycles = JSON_SET( billing_cycles, '$.monthly.price_type', 'with_tax' -- Bu plan KDV dahil girilmişse ) WHERE subscription_plan_id = 1;

Öneri: Mevcut planlar için varsayılan without_tax kullan (Shop pattern ile uyumlu). Eğer bir plan KDV dahil girilmişse, sadece o planı with_tax olarak işaretle.

✨ Esnek Sistemin Avantajları

Müşteri Esnekliği

  • Her müşteri kendi isteğine göre girebilir
  • Muhasebe sistemine göre ayarlanabilir
  • Farklı planlar farklı türde olabilir

Teknik Avantajlar

  • Shop modülü ile tutarlı pattern
  • Otomatik hesaplama (hata riski yok)
  • Log'larda price_type görünür (debug kolay)

Hata Önleme

  • Çift KDV sorunu tamamen ortadan kalkar
  • Frontend-checkout uyumsuzluğu olmaz
  • Müşteri kafası karışmaz

Raporlama

  • KDV hariç/dahil ayrımı net
  • Yasal raporlamada kolaylık
  • Admin panelde anlaşılır

🧪 Test Senaryoları

Test 1: KDV hariç plan (600 TL without_tax)
Beklenen: Plan sayfası "600 TL + KDV", Checkout "600 + 120 = 720 TL"
Test 2: KDV dahil plan (720 TL with_tax)
Beklenen: Plan sayfası "720 TL (KDV dahil)", Checkout "600 + 120 = 720 TL"
Test 3: Karışık planlar (monthly without_tax, yearly with_tax)
Beklenen: Her cycle doğru gösterim, checkout hatasız
Test 4: Mevcut planlar (price_type yok)
Beklenen: Default "without_tax" kullanılır, hatasız çalışır
Test 5: Log kontrolü
Beklenen: Bridge log'unda price_type, base_price, price_with_tax görünür

📋 Esnek Sistem Özeti

Esneklik

  • Her cycle için ayrı price_type seçimi
  • "with_tax" veya "without_tax" esnek ayar
  • Admin panelde anlaşılır form
  • Default: without_tax (Shop uyumlu)

Otomasyonlar

  • Model accessor otomatik hesaplama
  • Bridge akıllı KDV ayrıştırma
  • Frontend dinamik gösterim
  • Cart her zaman doğru hesaplama

Sonuç

Esnek KDV sistemi sayesinde hem KDV dahil, hem KDV hariç fiyat girişine izin veriyoruz. Sistem otomatik olarak doğru hesaplamayı yapıyor. Müşteriden müşteriye değişen ihtiyaçlar karşılanıyor, Shop modülü ile de tam uyumluluk sağlanıyor. Çift KDV sorunu tamamen ortadan kalkıyor!

Claude AI Code Analizi - 11 Ocak 2025 (v3 - Esnek Sistem)

Subscription Fiyatlandırma - Esnek KDV Sistemi (with_tax / without_tax)

Müşteriye Göre Ayarlanabilir Otomatik Hesaplama Hata Önleme