From 4acfd27ab10532d47207fd822dba331145bf1b79 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Tue, 22 Sep 2026 06:06:52 +0300 Subject: [PATCH 1/2] =?UTF-8?q?fix(kredi):=20paketliden=20liste=20ve=20rev?= =?UTF-8?q?izyon=20kredisi=20d=C3=BC=C5=9Fmez?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /paket "liste ve revizyon dâhil" diyor, kod her üretimde 3 kredi düşüyordu. Tek `maliyet = user.hasPaket ? 0 : RAPOR_KREDI` değişkeni harcama, iade ve dönen bakiye satırlarını birden kapatıyor. spendCredits/grantCredits çağrıları bilerek duruyor: ledger'daki UNIQUE(reason, ref_id) üretimin tek idempotency çapası ve DUPLICATE dalındaki "önceki deneme başarısızdı" çıkarımı refund satırının varlığına dayanıyor. İade tutarı da koşullu — yoksa harcaması 0 olan paketli her başarısız denemede bedava 3 kredi kazanırdı. Co-Authored-By: Claude Opus 5 --- src/features/rapor/rapor-actions.ts | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/features/rapor/rapor-actions.ts b/src/features/rapor/rapor-actions.ts index 66c6acd..cad49f7 100644 --- a/src/features/rapor/rapor-actions.ts +++ b/src/features/rapor/rapor-actions.ts @@ -149,10 +149,18 @@ async function listeOlusturKilitli( params: RaporParams, requestId: string, ): Promise { + // Paket vaadi (docs/urun/vizyon.md): liste ve revizyon pakete dâhil; krediler + // yalnız danışman sorusu için. Çağrı BİLEREK duruyor — ledger'daki + // UNIQUE(reason, ref_id) (appdb/schema.ts:133) üretimin tek idempotency + // çapası; paketliye delta 0 satır yazılır, aynı requestId ikinci kez + // DUPLICATE döner. hasPaket tek yerden okunur (zaten elimizdeki user + // nesnesi) ki harcama ile iade aynı değeri görsün. + const maliyet = user.hasPaket ? 0 : RAPOR_KREDI; + // 3 kredi düş (idempotent: aynı requestId ile retry çifte harcamaz) const harcama = await spendCredits({ userId: user.id, - amount: RAPOR_KREDI, + amount: maliyet, reason: "report_generate", refId: requestId, }); @@ -208,10 +216,13 @@ async function listeOlusturKilitli( try { sonuc = await raporUret(params); } catch (err) { - // Üretim başarısız → krediyi iade et + // Üretim başarısız → krediyi iade et. Satır paketlide de yazılır (delta 0): + // yukarıdaki DUPLICATE dalı "önceki deneme başarısız mıydı" sorusunu tam + // olarak bu refund kaydının varlığıyla cevaplıyor. Tutar koşullu, yoksa + // harcaması 0 olan paketliye her başarısız denemede bedava kredi yazardık. await grantCredits({ userId: user.id, - delta: RAPOR_KREDI, + delta: maliyet, reason: "refund", refId: requestId, }).catch(() => {}); @@ -240,7 +251,7 @@ async function listeOlusturKilitli( rapor: user.hasPaket ? sonuc : raporMaskele(sonuc), params, revisionCount: 0, - kredi: user.creditBalance - RAPOR_KREDI, + kredi: user.creditBalance - maliyet, hasPaket: user.hasPaket, }; } @@ -282,9 +293,15 @@ export async function listeRevize(input: { const params = rapor.params as RaporParams; const onceki = rapor.result as RaporSonuc; + // Revizyon pakete dâhil (bkz. listeOlusturKilitli'deki aynı not). Yukarıdaki + // hasPaket kapısı yüzünden burada maliyet her zaman 0; ifade yine de koşullu + // yazılıyor ki kapı ileride gevşerse paketsiz kullanıcıdan ücret düşmeye + // devam etsin. Revizyon hakkını MAX_REVIZYON sayacı sınırlıyor, kredi değil. + const maliyet = user.hasPaket ? 0 : RAPOR_KREDI; + const harcama = await spendCredits({ userId: user.id, - amount: RAPOR_KREDI, + amount: maliyet, reason: "report_revision", refId: input.requestId, }); @@ -336,9 +353,11 @@ export async function listeRevize(input: { listeOzetiCikar(onceki, params.sira), ); } catch (err) { + // İade satırı burada da yazılır (paketlide delta 0) — DUPLICATE dalındaki + // "önceki deneme başarısızdı" çıkarımı buna dayanıyor. await grantCredits({ userId: user.id, - delta: RAPOR_KREDI, + delta: maliyet, reason: "refund", refId: input.requestId, }).catch(() => {}); @@ -360,7 +379,7 @@ export async function listeRevize(input: { rapor: sonuc, params, revisionCount: rapor.revisionCount + 1, - kredi: user.creditBalance - RAPOR_KREDI, + kredi: user.creditBalance - maliyet, hasPaket: true, }; } -- 2.49.1 From 43b6ceca3e0425be94ad5817b5b0c5860abfe0c6 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Tue, 22 Sep 2026 06:07:01 +0300 Subject: [PATCH 2/2] =?UTF-8?q?fix(listem):=20fark=20modal=C4=B1=20paketli?= =?UTF-8?q?ye=20kredi=20bedeli=20g=C3=B6stermez?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sunucu artık paketliden kredi düşmüyor; arayüz hâlâ "3 kredi düşer" ve "Listemi güncelle — 3 kredi" diyordu. raporKredi prop'u paketliye 0 iner, modal tek `ucretsiz` bayrağıyla üç metin yerini buna bağlar. Kredi mantığı (krediYeter, KrediYetersiz dalı) olduğu gibi kalır. Co-Authored-By: Claude Opus 5 --- .../rapor/components/listem-icerik.tsx | 7 +++- .../components/tercih-degisti-modali.tsx | 33 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/features/rapor/components/listem-icerik.tsx b/src/features/rapor/components/listem-icerik.tsx index 388a9c6..f593730 100644 --- a/src/features/rapor/components/listem-icerik.tsx +++ b/src/features/rapor/components/listem-icerik.tsx @@ -164,7 +164,12 @@ export async function ListemIcerik({ kilitAcilisi={kilitAcilisi} kilitliBaslangic={user.hasPaket ? undefined : ACIK_SATIR} kredi={user.creditBalance} - raporKredi={RAPOR_KREDI} + raporKredi={ + /* Liste ve revizyon pakete dâhil (bkz. rapor-actions.ts'teki + maliyet): paketliye bedel 0 iner ki arayüz sunucunun yaptığını + söylesin. */ + user.hasPaket ? 0 : RAPOR_KREDI + } sohbetGecmisi={sohbetGecmisi as Mesaj[]} ozet={danismanOzeti} ilkYorum={ilkYorum} diff --git a/src/features/rapor/components/tercih-degisti-modali.tsx b/src/features/rapor/components/tercih-degisti-modali.tsx index eb62071..837dec3 100644 --- a/src/features/rapor/components/tercih-degisti-modali.tsx +++ b/src/features/rapor/components/tercih-degisti-modali.tsx @@ -6,10 +6,12 @@ // alanların değiştiğini gösterir ve listeyi yeni tercihlere göre yeniden // kurmayı önerir. // -// Yeniden kurulum normal üretimle AYNI kapıdan geçer (listeOlustur): 3 kredi, -// aynı requestId idempotency'si, aynı kredi-yetersiz/iade davranışı. Ayrı bir -// "güncelleme" ürünü ya da indirimli yol yok — kullanıcı yeni bir liste alıyor, -// bedeli de yeni liste bedeli. +// Yeniden kurulum normal üretimle AYNI kapıdan geçer (listeOlustur): aynı +// bedel, aynı requestId idempotency'si, aynı kredi-yetersiz/iade davranışı. +// Ayrı bir "güncelleme" ürünü ya da indirimli yol yok — kullanıcı yeni bir +// liste alıyor, bedeli de yeni liste bedeli. Paketli kullanıcıda o bedel 0: +// liste ve revizyon pakete dâhil (raporKredi prop'u listem-icerik.tsx'te +// koşullanır), krediler yalnız danışman sorusuna gider. // // Renk: modalın kabuğu marka primary'si (bg-primary/10 ikon hücresi), ana // aksiyon ise ürünün CTA turuncusu — kilidi açan, kredi harcayan, ödemeye @@ -127,6 +129,10 @@ export function TercihDegistiModali({ } const krediYeter = kredi >= raporKredi; + // Paketliye liste ve revizyon dâhil olduğu için bedel 0 iner (raporKredi + // prop'u listem-icerik.tsx'te koşullanıyor). Tek türetilmiş bayrak: modalın + // üç kredi cümlesi buna bakar, kredi mantığı olduğu gibi kalır. + const ucretsiz = raporKredi === 0; return ( - Listen eski tercihlerinle kuruldu. Yeni liste bunun yerine - geçer ve {raporKredi} kredi düşer. + {ucretsiz ? ( + <> + Listen eski tercihlerinle kuruldu. Yeni liste bunun yerine + geçer; güncelleme paketine dâhil, kredi düşmez. + + ) : ( + <> + Listen eski tercihlerinle kuruldu. Yeni liste bunun yerine + geçer ve {raporKredi} kredi düşer. + + )} @@ -245,7 +260,7 @@ export function TercihDegistiModali({ {durum.ad === "hata" ? (

{durum.mesaj}

) : null} - {!krediYeter && durum.ad !== "hata" ? ( + {!ucretsiz && !krediYeter && durum.ad !== "hata" ? (

Güncelleme {raporKredi} kredi; bakiyen {kredi} kredi.

@@ -272,7 +287,9 @@ export function TercihDegistiModali({ onClick={() => void guncelle()} className="cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600" > - Listemi güncelle — {raporKredi} kredi + {ucretsiz + ? "Listemi güncelle" + : `Listemi güncelle — ${raporKredi} kredi`} ) : ( -- 2.49.1