KRİTİK HATA SHOP KARŞILAŞTIRMASI 11 Ocak 2025

ÇİFT KDV HESAPLAMA HATASI

Kapsamlı Analiz: Subscription vs Shop Modülü Fiyatlandırma Sistemi

⚠️ SORUN ÖZETİ

Subscription modülünde YANLIŞ KDV HESAPLAMA yapılıyor! Shop modülü ise DOĞRU ÇALIŞIYOR.

Plan Sayfasında Gösterilen:
600 TL + KDV
Beklenen: 600 + 120 = 720 TL
Checkout Sayfasında Hesaplanan:
Ara Toplam: 500 TL
KDV (%20): 100 TL
Toplam: 600 TL

Sorun: Bridge servis fiyatı "KDV dahil" sanıyor ve ayrıştırıyor (600 ÷ 1.20 = 500 TL). Sonra Cart tekrar KDV ekliyor (500 × 1.20 = 600 TL). Ama kullanıcıya "600 TL + KDV" gösteriliyor!

📝 Basit Anlatım (Herkes İçin)

Sorun Ne?
Müşteri premium üyelik satın almak istiyor. Plan sayfasında "600 TL + KDV" yazıyor. Müşteri hesap yapıyor: "600 + %20 KDV = 720 TL ödeyeceğim" diye düşünüyor.

Ama sepete eklediğinde checkout sayfasında şöyle görüyor:

Ara Toplam: 500 TL
KDV (%20): 100 TL
Toplam: 600 TL

Müşteri Kafası Karışıyor: "Plan sayfasında 600 TL + KDV diyordu, neden toplam 600 TL oldu? KDV nerede?"

Gerçekte Ne Oluyor?
Sistem arkada şöyle çalışıyor:

  1. Database'de plan fiyatı: 600 TL (yönetici tarafından girilmiş)
  2. Bridge servisi bunu "KDV dahil" sanıyor ve böyle hesaplıyor: 600 ÷ 1.20 = 500 TL (KDV hariç)
  3. Cart modülü 500 TL'ye %20 KDV ekliyor: 500 + 100 = 600 TL
  4. Müşteriye gösterilen: "600 TL + KDV" (ama gerçekte zaten KDV dahil!)

Sonuç: Müşteri 720 TL bekliyor, 600 TL görüyor. Bu kafayı karıştırıyor ve güven kaybına yol açıyor. Ayrıca fiyat gösterimi yanıltıcı! Eğer fiyat zaten KDV dahil ise "600 TL (KDV dahil)" yazmalı.

⚖️ MODÜL KARŞILAŞTIRMASI: Shop (✅ Doğru) vs Subscription (❌ Hatalı)

Kriter Shop Modülü (Doğru) Subscription Modülü (Hatalı)
Database Fiyat Field base_price
KDV HARİÇ fiyat
billing_cycles['price']
BELİRSİZ (KDV dahil mi hariç mi?)
Runtime Hesaplama price_with_tax accessor
base_price × 1.20
getCyclePriceWithTax()
YOK - Bridge'de manuel ayrıştırma
Bridge Servisi
ShopCartBridge.php
'unit_price' => $product->final_price // final_price = base_price (KDV hariç) // DOĞRU! Ayrıştırma YOK!
SubscriptionCartBridge.php
$priceWithTax = $cycle['price']; $priceWithoutTax = $priceWithTax / 1.20; // ❌ YANLIŞ! KDV dahil sanıyor!
Cart'a Gönderilen Fiyat
1000 TL (KDV hariç)
Cart buna KDV ekler → 1200 TL
500 TL (ayrıştırılmış!)
Cart buna KDV ekler → 600 TL
Checkout Sonucu
Ara Toplam: 1000 TL
KDV: 200 TL
Toplam: 1200 TL ✅
Ara Toplam: 500 TL
KDV: 100 TL
Toplam: 600 TL ❌
(Beklenen: 720 TL!)
Frontend Fiyat Gösterimi
1000 TL + KDV
DOĞRU! 1000 + 200 = 1200 TL
600 TL + KDV
YANLIŞ! Checkout'ta 600 TL çıkıyor
price_display_mode Field
✅ VAR
show / hide / request
⚠️ VAR AMA KULLANILMIYOR!
Migration'da var, kod kullanmıyor

Shop Modülü - Doğru Pattern (Model)

ShopProduct.php (Satır 1357-1366)
/** * KDV dahil fiyat hesaplama (Runtime calculation) * base_price (KDV hariç) üzerinden hesaplanır */ public function getPriceWithTaxAttribute(): float { if (!$this->base_price) { return 0.0; } $taxRate = $this->tax_rate ?? 20.0; // ✅ DOĞRU: base_price KDV hariç, runtime'da KDV ekleniyor return (float) ($this->base_price * (1 + $taxRate / 100)); }
ShopCartBridge.php (Satır 112-119)
protected function getProductPriceInfo(ShopProduct $product): array { return [ 'unit_price' => $product->final_price, // ✅ final_price = base_price (KDV hariç) 'currency' => $product->currency ?? 'TRY', 'discount_amount' => 0, ]; // ✅ DOĞRU: KDV hariç fiyat Cart'a gönderiliyor, ayrıştırma YOK! }

🔧 Teknik Detaylar - Subscription Hatası (Geliştiriciler İçin)

Mevcut Durum (HATA) - 5 Adımda Çift KDV

1

Database - Subscription Plan

Admin panelde cycle price girilir:

billing_cycles: { "monthly": { "price": 600, // ← Bu KDV dahil mi, hariç mi? BELİRSİZ! "label": {"tr": "Aylık"} } }
Sorun: Field adı "price" - KDV dahil/hariç belirtilmemiş! Shop'ta "base_price" kullanıyor (açıkça KDV hariç).
2

Frontend - subscription-plans.blade.php

Plan sayfasında gösterilen:

{{ number_format($price, 2) }} TL + KDV // ← "600 TL + KDV" gösteriliyor

Müşteri 720 TL ödeyeceğini düşünüyor!

3

Bridge - SubscriptionCartBridge.php (Satır 100-115)

Cart'a eklerken:

// Cycle'dan fiyat al (KDV dahil!) ← ❌ YANLIŞ VARSAYIM! $priceWithTax = $cycle['price'] ?? 0; // 600 TL $taxRate = $plan->tax_rate ?? 20; // Subscription fiyatları KDV DAHİL gelir! // KDV'yi ayrıştır $priceWithoutTax = $priceWithTax / (1 + ($taxRate / 100)); // $priceWithoutTax = 600 / 1.20 = 500 TL return [ 'unit_price' => $priceWithoutTax, // ← 500 TL 'tax_rate' => $taxRate, // ← 20% ];

Ana Hata: Bridge fiyatı "KDV dahil" sanıp ayrıştırıyor!
Shop ile fark: Shop'ta final_price direkt alınıyor (zaten KDV hariç).

4

Cart - CartItem.php (Satır 170-180)

Sepet hesaplaması (Shop ve Subscription için AYNI):

public function recalculate(): void { // unit_price = 500 TL (Bridge'den gelen) $this->subtotal = $this->final_price * $this->quantity; // subtotal = 500 * 1 = 500 TL $this->tax_amount = $this->subtotal * ($this->tax_rate / 100); // tax_amount = 500 * 0.20 = 100 TL $this->total = $this->subtotal + $this->tax_amount; // total = 500 + 100 = 600 TL }
✅ Shop İçin (Doğru)
unit_price: 1000 TL (KDV hariç)
KDV: 200 TL
Total: 1200 TL
❌ Subscription İçin (Hatalı)
unit_price: 500 TL (ayrıştırılmış!)
KDV: 100 TL
Total: 600 TL
5

Checkout - CheckoutPage.php (Satır 299-301)

Müşteriye gösterilen:

$this->subtotal = $this->items->sum('subtotal'); // 500 TL $this->taxAmount = $this->items->sum('tax_amount'); // 100 TL $this->total = $this->subtotal + $this->taxAmount; // 600 TL
Ara Toplam: 500 TL
KDV (%20): 100 TL
Toplam: 600 TL

Müşteri 720 TL bekliyordu, 600 TL gördü!

İlgili Dosyalar

❌ Subscription (Hatalı)
SubscriptionCartBridge.php:98-134
subscription-plans.blade.php:383-390
SubscriptionPlan.php:166-177
✅ Shop (Doğru - Referans)
ShopCartBridge.php:112-119
ShopProduct.php:1357-1366
ShopProduct.php:1348-1350
🔄 Ortak Dosyalar
CartService.php:153-233
CartItem.php:170-182
CheckoutPage.php:299-301

✅ Çözüm Önerileri (Shop Pattern İle Uyumlu)

1

Shop Pattern'i Kullan: Cycle Price = KDV HARİÇ ÖNERİLEN SHOP İLE AYNI

Database'de cycle price'lar KDV hariç girilir (Shop'taki base_price pattern'i). Bridge ayrıştırma yapmaz, direkt KDV hariç fiyatı gönderir.

Değişiklikler:

ÖNCELİK 1 SubscriptionPlan.php - Accessor Ekle
/** * Belirli bir cycle'ın fiyatını al (KDV hariç - base_price pattern) * Shop modülündeki getFinalPriceAttribute() ile aynı mantık */ public function getCycleBasePrice(string $cycleKey): ?float { $cycles = $this->billing_cycles ?? []; // ✅ Direkt price döndür (KDV hariç - database'de bu şekilde saklanıyor) return isset($cycles[$cycleKey]['price']) ? (float) $cycles[$cycleKey]['price'] : null; } /** * KDV dahil fiyat hesaplama (Runtime - Shop pattern) */ public function getCyclePriceWithTax(string $cycleKey): ?float { $basePrice = $this->getCycleBasePrice($cycleKey); if (!$basePrice) { return null; } $taxRate = $this->tax_rate ?? 20.0; // ✅ Shop pattern: base_price * (1 + tax_rate / 100) return $basePrice * (1 + $taxRate / 100); }
ÖNCELİK 2 SubscriptionCartBridge.php (Satır 98-134)
// ❌ ESKİ KOD (Kaldır - KDV ayrıştırma yapıyor) $priceWithTax = $cycle['price'] ?? 0; $taxRate = $plan->tax_rate ?? 20; $priceWithoutTax = $priceWithTax / (1 + ($taxRate / 100)); return [ 'unit_price' => $priceWithoutTax, // ❌ Ayrıştırılmış fiyat 'tax_rate' => $taxRate, ]; // ✅ YENİ KOD (Shop pattern - Ayrıştırma YOK!) $basePrice = $plan->getCycleBasePrice($cycleKey); // KDV hariç fiyat if (!$basePrice) { throw new \Exception("Cycle fiyatı bulunamadı: {$cycleKey}"); } $taxRate = $plan->tax_rate ?? 20.0; // Shop ile AYNI pattern - log ekle Log::info('🛒 SubscriptionCartBridge - Price Info', [ 'plan_id' => $plan->subscription_plan_id, 'cycle_key' => $cycleKey, 'base_price' => $basePrice, // KDV hariç (database'den direkt) 'tax_rate' => $taxRate, 'price_with_tax' => $basePrice * (1 + $taxRate / 100), // Sadece log için 'note' => 'Price WITHOUT tax - same as Shop base_price pattern', ]); return [ 'unit_price' => $basePrice, // ✅ KDV hariç (ayrıştırma YOK!) 'tax_rate' => $taxRate, ];
OPSIYONEL subscription-plans.blade.php (Satır 383-390)
{{-- Değişiklik YOK - zaten doğru gösteriyor --}} {{ number_format($price, 2) }} TL + KDV {{-- Eğer daha net göstermek istersen (Shop gibi): --}} {{ number_format($price, 2) }} TL (KDV hariç)
KDV dahil: {{ number_format($price * 1.20, 2) }} TL
Database - Admin Panel (DEĞİŞİKLİK YOK)
// Admin panelde plan oluştururken (AYNI KALIYOR): billing_cycles: { "monthly": { "price": 600, // ← KDV HARİÇ fiyat (Shop base_price gibi) "label": {"tr": "Aylık"} } } // ✅ Artık "price" field'i açıkça KDV HARİÇ olarak yorumlanıyor (Shop pattern)

Sonuç (Shop İle AYNI):

Database
600 TL (KDV hariç)
Shop: base_price pattern
Plan Sayfası
600 TL + KDV
Shop ile uyumlu
Checkout
600 + 120 = 720 TL
DOĞRU! ✅
2

Alternatif: Cycle Price = KDV DAHİL SHOP'TAN FARKLI

Database'de cycle price'lar KDV dahil girilir. Bridge ayrıştırma yapar (mevcut kod kalır). Sadece frontend gösterimi düzeltilir.

Değişiklikler:

SubscriptionCartBridge.php
// ✅ Mevcut kod KORU (Bridge ayrıştırma yapıyor, bu doğru) $priceWithTax = $cycle['price'] ?? 0; // 600 TL (KDV dahil) $priceWithoutTax = $priceWithTax / (1 + ($taxRate / 100)); // 500 TL return [ 'unit_price' => $priceWithoutTax, // 500 TL (KDV hariç) 'tax_rate' => $taxRate, // %20 ]; // ⚠️ ANCAK: price_display_mode kontrolü ekle // Eğer admin "KDV dahil göster" seçerse, frontend'i ayarla
subscription-plans.blade.php (Satır 380-420)
{{-- SEÇENEK A: KDV hariç + KDV göster --}} @php $basePrice = $price / 1.20; // KDV'yi ayrıştır $taxAmount = $price - $basePrice; @endphp
{{ number_format($basePrice, 2) }} TL

+ KDV ({{ number_format($taxAmount, 2) }} TL) = {{ number_format($price, 2) }} TL toplam

{{-- VEYA SEÇENEK B: KDV dahil göster --}}
{{ number_format($price, 2) }} TL

(KDV dahil)

Sonuç:

Database
600 TL (KDV dahil)
Plan Sayfası
500 TL + KDV
veya "600 TL KDV dahil"
Checkout
500 + 100 = 600 TL
Uyarı: Bu seçenek Shop modülünden FARKLI bir pattern kullanır. Tutarlılık için Seçenek 1 önerilir.

💡 Hangi Çözümü Seçmeli?

Seçenek 1 (KDV Hariç - Shop Pattern) KESİNLİKLE ÖNERİLİR çünkü:

  • Shop modülü ile AYNI pattern: base_price (KDV hariç) + price_with_tax (runtime accessor)
  • Kod tutarlılığı: Tüm modüller aynı fiyatlandırma mantığını kullanır
  • Türkiye standartları: Fiyatlar genelde "X TL + KDV" gösterilir
  • Admin panel netliği: "price" = KDV hariç (Shop'taki gibi)
  • Yasal raporlama: KDV hariç fiyat ayrıştırması kolay
  • İxtif.com uyumluluğu: Shop modülü (forklift, transpalet) aynı pattern kullanıyor

Uygulama Önerisi: Seçenek 1'i uygula. Model'e accessor ekle, Bridge'i düzelt. Böylece Subscription modülü Shop ile tamamen uyumlu hale gelir. Mevcut database'i değiştirmeye gerek yok, sadece "price" field'inin yorumlanmasını değiştiriyoruz (KDV hariç olarak).

🏢 İxtif.com (Tenant 2) - Shop Modülü Kullanımı

İxtif.com: Endüstriyel ekipman satışı (forklift, transpalet, istif makinesi)

Fiyatlandırma Yapısı (Shop Modülü):

  • Database: base_price field (KDV hariç)
  • Model: price_with_tax accessor (runtime hesaplama)
  • Cart: final_price (base_price - KDV hariç)
  • Sonuç: Doğru çalışıyor! Çift KDV sorunu YOK.

Sonuç: İxtif.com'da Shop modülü DOĞRU çalışıyor. Subscription modülünü de AYNI pattern'e uyarlarsak, tüm sistem tutarlı hale gelir ve bu KDV hatası tamamen ortadan kalkar.

🔍 Uygulama Sonrası Kontrol Listesi

Model Accessor: getCycleBasePrice() ve getCyclePriceWithTax() metodları eklenmiş mi?

Bridge Güncelleme: SubscriptionCartBridge.php KDV ayrıştırma kodunu kaldırdı mı?

Log Kontrolü: Bridge log'unda "Price WITHOUT tax - same as Shop base_price pattern" mesajı var mı?

Test - Plan Sayfası: "600 TL + KDV" gösterimi doğru mu?

Test - Sepete Ekle: Cart item'da unit_price = 600 TL (KDV hariç) mi?

Test - Checkout: Ara Toplam 600 TL, KDV 120 TL, Toplam 720 TL mi?

Mevcut Planlar: Database'deki cycle price'lar kontrol edildi mi? (KDV hariç olarak yorumlanıyor artık)

Admin Döküman: "Cycle price KDV hariç girilmelidir" notu eklendi mi?

Shop Karşılaştırma: Subscription ve Shop aynı pattern'i kullanıyor mu?

📋 Kapsamlı Özet

Sorun

  • Bridge fiyatı "KDV dahil" sanıp ayrıştırıyor
  • Cart tekrar KDV ekliyor (çift hesaplama)
  • Frontend "X TL + KDV" gösteriyor ama checkout'ta farklı
  • Shop modülü ile pattern uyumsuzluğu
  • Müşteri kafası karışıyor, güven kaybı

Çözüm (Shop Pattern)

  • Model'e getCycleBasePrice() accessor ekle
  • Bridge'den KDV ayrıştırma kodunu kaldır
  • Cycle price = KDV hariç (Shop base_price pattern)
  • Shop modülü ile tam uyumluluk
  • İxtif.com (Shop) ve Muzibu (Subscription) tutarlı

❌ Mevcut Durum (Hatalı)

Plan: 600 TL + KDV gösterimi
Checkout: 500 + 100 = 600 TL
→ Müşteri 720 TL bekledi!

✅ Düzeltme Sonrası (Doğru)

Plan: 600 TL + KDV gösterimi
Checkout: 600 + 120 = 720 TL
→ Beklenti ile uyumlu! ✅

Claude AI Code Analizi - 11 Ocak 2025 (v2)

Subscription & Shop Modül Karşılaştırması - KDV Hesaplama Sistemi - İxtif.com Analizi

Shop Pattern Uyumlu İxtif.com Referanslı Detaylı Kod Örnekleri