feat(borç): borç takibi backend (ölçümler, taksit planları, yol haritası, dönem borcu, içe aktarma)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-14 04:27:48 +03:00
parent a2eff1a984
commit 11402ba660
28 changed files with 2188 additions and 1 deletions
+60
View File
@@ -0,0 +1,60 @@
# Borç takibi (Kakeibo uyarlaması)
Kaynak: `butcem/docs/BORC_TAKIBI.md` (kullanıcının özellik tanımı). Kişisel rakamlar (seed) bu repoya
**girmez**; canlıya "İçe aktar (JSON)" ile yüklenir (dosya repoda tutulmaz).
## Amaç
Gerçek banka borcu (KMH + kredi kartı + ekstreye gelmemiş taksitler) Panel'in en üstünde görünsün ve
her ay küçülmesi izlensin. Hedef: toplam borcu yol haritasındaki son aya kadar sıfırlamak.
## Kararlar (kullanıcı, 2026-11-14)
- **Ayrı "Borç" modülü.** Kakeibo'daki "devreden borç" (bütçe aşımının devri, `carryovers`) ile
karıştırılmaz; banka borcu **harcanabilir tutarı düşürmez** (aylara yayılarak birikimle ödenir).
Borç sıfırlanana kadar dönemin birikim hedefi (C) "Borç ödemesi" olarak etiketlenir.
- Başlangıç verisi canlıya **içe aktarma dosyasıyla** (önizleme → onay).
## Formüller (tutarlar kuruş, `i64`)
```
toplam borç = KMH eksi bakiyesi + kart borcu (provizyon dahil) + kalan taksitler − maaş hesabı artı bakiyesi
devreden borç = toplam borç − kalan taksitler (aylık planda gösterilen)
taksit yükü(ay) = Σ aylık tutar (ilk taksit ayı ≤ ay ≤ son taksit ayı)
kalan taksit(ay) = Σ aylık tutar × (ay'dan SONRAKİ taksit sayısı) (ölçümün ayına göre önerilir)
ay sonu hedefi = ay başı toplam borç − birikim (C) − o ayın taksit yükü
aylık başarı = ay başı toplam − ay sonu toplam ≥ C + o ayın taksit yükü
```
- Toplam **saklanmaz**, her okumada hesaplanır.
- Taksit planı "ödenen taksit sayısı" yerine **ilk taksit ayı** (`first_month`) ile saklanır; ödenen /
kalan sayılar her ay kendiliğinden hesaplanır. İçe aktarmada `asOf` ayı + `paidCount` →
`first_month = asOf − paidCount + 1`.
- Ay başı borç: dönemin başlangıcından önceki (≤ başlangıç günü) son ölçüm; dönemde elle düzeltilebilir.
## Veri modeli (MongoDB, ortak alanlar: `_id` UUID v7, `household_id`, zaman damgaları, `deleted_at`, `version`)
- `debt_snapshots`: `date`, `kmh`, `positive_balance`, `card_balance`, `remaining_installments`,
`note`, `source` (`manual` | `import`)
- `installment_plans`: `merchant`, `total_amount`, `monthly_amount`, `total_count`, `first_month` (`YYYY-MM`)
- `debt_targets`: `month` (`YYYY-MM`), `closing_target` (yol haritası, toplam borç cinsinden)
- `periods.opening_debt` (isteğe bağlı elle ay başı toplam borç)
**Güvenlik:** IBAN, kart ve müşteri numarası saklanmaz; açıklama/işyeri alanlarında 12+ haneli sayı
dizisi reddedilir.
## Ekranlar
1. Panel en üstü: güncel toplam borç, geçen aya göre değişim, başlangıç → 0 ilerleme, tahmini bitiş,
KMH · Kart · Kalan taksit dökümü.
2. `/borc`: gerçek (ölçümler) + hedef (yol haritası, kesikli) çizgi grafik, ölçüm tablosu, "Yeni ölçüm"
(toplam canlı hesaplanır, kalan taksit önerilir), hedefleri düzenleme, içe aktarma.
3. Aylık plan: ay başı borç (otomatik, düzenlenebilir), devreden borç, "Borç ödemesi" (C), taksit yükü,
hedef ay sonu borç (salt okunur); taksit yükü Sabit giderlere "Taksitler" olarak önerilir.
4. `/taksitler`: açık planlar, önümüzdeki 6 ayın taksit yükü (çubuk); yeni taksitte uyarı
"Borç sıfırlanana kadar yeni taksit planı yok."
## Kabul ölçütleri (kaynak belgeyle aynı)
- Ölçüm toplamları kaynak belgedeki iki ölçümle birebir (78.530,48 ve 79.078,85).
- Kasım planında devreden borç 64.352,79.
- Taksit yükü: Kasım ve Aralık 4.311,82 · Ocak 3.724,44 · Şubat 887,38 · Mart 760,21 · Nisan 182,02.
- Gerçek ve hedef aynı grafikte; hiçbir yerde IBAN / kart numarası yok.
## Adımlar
- B1 backend (modeller, hesaplar, uç noktalar, içe aktarma, testler)
- B2 arayüz: Panel kartı, `/borc`, `/taksitler`
- B3 aylık plan entegrasyonu + içe aktarma dosyasının hazırlanması (kullanıcıya, repo dışı)
+11
View File
@@ -230,6 +230,17 @@ Renkler ve ikonlar frontend tasarım token'larında.
- Geri yükleme = **tam değiştirme** (birleştirme yok), tek transaction. Aynı hane → kimlikler korunur; başka hane → tüm `_id`'ler yeniden üretilir, `*_id` referansları çevrilir, `household_id` hedef hane, `*_by` hanede yoksa geri yükleyen kişi. Hane adı yalnız aynı hanede geri gelir; ayarlar her zaman.
- Sürüm: `format_version`/`schema_version` daha yeniyse reddedilir; eskiyse hane düzeyi yükseltmeler uygulanır (v<2 → varsayılan varlıklar).
### 3.14c Borç takibi (docs/BORC_TAKIBI.md)
```js
debt_snapshots { _id, household_id, date: "YYYY-MM-DD", kmh, positive_balance, card_balance,
remaining_installments, note, source: "manual" | "import", … } // toplam saklanmaz
installment_plans { _id, household_id, merchant, total_amount, monthly_amount, total_count,
first_month: "YYYY-MM", … } // ödenen/kalan her ay hesaplanır
debt_targets { _id, household_id, month: "YYYY-MM", closing_target, … } // yol haritası
periods.opening_debt // isteğe bağlı elle ay başı toplam borç
```
- Kakeibo "devreden borç"u (`carryovers`, bütçe aşımı) ile ilgisizdir; harcanabilir tutarı etkilemez.
### 3.15 `schema_migrations`
```js
{ _id: 1 /* sürüm numarası */, name: "initial", applied_at }
+3 -1
View File
@@ -1,10 +1,11 @@
# 🧠 Proje Hafızası
## ▶️ Sıradaki Adım
**Faz 10 ✅ tamamlandı.** Sıradaki: kullanıcının **geniş kapsamlı testi** (canlıda, telefonda). Kullanıcının bulduğu hatalar/istekler önce. Faz 11 (Tauri: Windows, Linux, Android) **yalnızca kullanıcı istediğinde** başlar. Kullanıcıya hatırlat: canlı sunucu yedeği (DEPLOY §6) gerçek veriden önce; ilk iş JSON yedeği indirmek.
**Borç takibi – B2 (arayüz):** Panel en üstüne borç kartı (toplam, geçen aya göre ± renkli, başlangıç→0 ilerleme + tahmini bitiş, KMH·Kart·Kalan taksit), `/borc` (Recharts: ölçümler düz çizgi + `debt_targets` kesikli; tablo tarih/KMH/kart/taksit/toplam/değişim; "Yeni ölçüm" formu: toplam canlı, kalan taksit önerisi = taksit planlarından `remaining_after(ay)`; hedefleri düzenle; "İçe aktar (JSON)" ön izleme→onay), `/taksitler` (açık planlar, 6 ay çubuk; "Yeni taksit"te uyarı "Borç sıfırlanana kadar yeni taksit planı yok."). Tipler hazır: `DebtSummary`, `DebtSnapshot`, `InstallmentPlan`, `DebtTarget`, `MonthLoad`, `DebtImportReport`, `PeriodDebt`. Sonra B3. Kullanıcı canlıyı (kakeibo.ta1sph.website) kullanıyor → içe aktarma dosyası B3'te scratchpad'de hazırlanıp verilecek.
**Bekleyen (kullanıcıda):** Faz 5 – Adım 5 canlı yedek kurulumu (`docs/DEPLOY.md` §6.1–6.3) → çıktılar gelince Faz 5 ✅.
## 📌 Alınan Kararlar
- **Borç takibi (kullanıcı, 2026-11-14):** `butcem/docs/BORC_TAKIBI.md` Kakeibo'ya uyarlanıyor (`docs/BORC_TAKIBI.md`). Ayrı Borç modülü (harcanabilir tutarı düşürmez; C = "Borç ödemesi"); başlangıç verisi canlıya içe aktarma dosyasıyla (repo dışı). Kullanıcı asıl olarak canlıyı kullanıyor.
- **Giriş güvenliği (kullanıcı, 2026-11-14):** reCAPTCHA istendi → seçeneklerden **Cloudflare Turnstile** seçildi (KVKK dostu); kilit **IP'ye göre** (e-postaya göre değil: sahibi kilitleme saldırısı olmasın). Üst üste 3 hata → giriş + kayıt 15 dk.
- **Tauri / mobil + masaüstü uygulamalar (kullanıcı, 2026-11-13):** Faz 11 (Windows, Linux, Android) **kullanıcının geniş kapsamlı testinden sonra, kullanıcı prompt verdiğinde** yapılır; kendiliğinden başlanmaz. Şimdi Faz 10: web uygulamasının eksikleri + arayüz cilası (PWA web kurulumu Faz 10'da kalır).
- **Aylık rapor tasarımı (kullanıcı onayı, 2026-11-13):** 7 bölüm — Bir bakışta (4 soru + önceki dönem) · Ne yaptım (gruplar ✅≤%90/⚠️≤%100/❌, ilk 5 kategori + ortalamaya göre, haftalar, ödeme türü, birikim) · İyi gitti · Zorladı · Ne yapılabilirdi ("olsaydı" hesapları) · Kendi notların (plan notları + yansıtma, yorumsuz) · Gelecek dönem (önerilen bütçe = son ≤4 dönem ortalaması 50 ₺ yukarı, ≤3 somut adım, devir, boş hedef kutuları) + Ek: tüm kategoriler. Eşik: kategori %20 **ve** 250 ₺. Karşılaştırma: önceki ≤3 kapalı/arşiv dönem. "Sen" dili. Açık dönem → ara rapor (hedef yargısı yok, bitmemiş hafta yok).
@@ -155,3 +156,4 @@
- **Faz 10 – Adım 4 (2026-11-13):** Önbellek başlıkları zaten doğruydu (Faz 5: `/assets/*` 1 yıl immutable, HTML no-cache, sıkıştırma). Ana JS 950 KB (gz 284) tek paket → sayfa bazlı `lazy` route: ana 402 KB (gz 128), sayfalar 2–23 KB; Markdown (49 KB gz) ve zod/form (40 KB gz) yalnız kullanan sayfada. İlk sürümde Defter yavaşlamıştı (lazy route → `/auth/me` sıralı) → `prefetchMe` ile paralel; `HydrateFallback: Splash`. Boşta alt menü sayfalarını önden indirme + `useNavigation` ilerleme çizgisi. Ölçüm (scratchpad `e2e/perf.mjs`: yavaş 4G 1,6 Mbps/150 ms + CPU 4×, soğuk; eski sürüm `git worktree` ile ayrı derlenip aynı koşulda, 2 tekrar ort.): Giriş 2,97→2,23 sn, Panel 2,42→2,03, Raporlar 2,36→2,11, Defter 2,33→2,37 (aynı); hazırda JS 295→171–221 KB. Regresyon: katalog (25) + rapor (21) e2e geçti. Not: e2e rapor başlığı artık `.md-h1`.
- **Faz 10 – Adım 5 (2026-11-13) → Faz 10 ✅:** `vite-plugin-pwa` 2.0 + `workbox-window` (dev): manifest (tr, standalone, ikonlar + maskable, kısayollar Harcama ekle/Panel), Workbox precache 98–101 dosya (xlsx ve vietnamca/yunanca/kiril font alt kümeleri hariç → runtime CacheFirst `/assets/`), `navigateFallback` index.html (`/api` hariç). `registerType: 'prompt'` → `PwaUpdater` toast + saatlik `reg.update()`. `OfflineBanner` (useSyncExternalStore). Backend `spa.rs` `cache_policy` (sw.js, manifest, registerSW → no-cache) + birim testi. **Bulunan hata:** çevrimdışı açılışta `/auth/me` ağ hatası → RequireAuth kullanıcıyı giriş sayfasına atıyordu; ayrıca TanStack onlineManager açılış durumunu bilmediği için birkaç sn boş açılış → `SessionUnknown` ("Oturum bilgisi alınamadı… Tekrar dene") + `onlineManager.setOnline(navigator.onLine)`; bağlantı gelince kendiliğinden toparlanır. E2E 14 kontrol (CDP `Page.getInstallabilityErrors` boş, önbellekte /api 0, çevrimdışı, güncelleme: dist kopyası + sw.js index revizyonu değiştirilip sunucu yeniden başlatıldı → bildirim → Yenile → yeni sürüm, oturum/sayfa korunur). npm audit: yeni `glob` (orta) yalnız derleme zamanı; diğer uyarılar mevcut `shadcn` CLI'dan. Docker web aşaması geçti. DEPLOY §7 kurulum.
- **Giriş güvenliği (2026-11-14):** Backend `auth/guard.rs` (AttemptGuard, ardışık sayaç, başarıda sıfır, süre bitince yeni tur, 1 sa dokunulmayan kayıt silinir), `auth/captcha.rs` (siteverify, 8 sn zaman aşımı, ağ hatası → `Unavailable` sayılmaz ama reddedilir, `internal-error` de öyle), `auth/client_ip.rs` (en sağ XFF, yoksa ConnectInfo; `main.rs` `into_make_service_with_connect_info`). Login/register `precheck` (kilit → captcha) + sayılan hatalar (yanlış şifre `InvalidCredentialsLeft(n)`, RegistrationClosed / BadRequest davet / Conflict e-posta). E-postaya göre giriş limiter'ı kaldırıldı (şifre değiştirmede kullanıcı başına kaldı). Config `TURNSTILE_SITE_KEY`/`SECRET_KEY` (ikisi birden), `TRUST_PROXY` (varsayılan true: Traefik arkasında herkes aynı IP olmasın). Testler `tests/security.rs` (4) + guard/XFF birim; 2 bilinçli bozma yakalandı; backend 214. Frontend `useAuthSecurity` (jeton, her hatada widget reset, kilit → `useAuthGuard` refetch, `dataUpdatedAt`+saniye geri sayım), Turnstile explicit render (tr, tema, flexible), betik yüklenemezse açıklama + düğme kapalı. E2E: Cloudflare test anahtarlarıyla gerçek widget + siteverify; kalan hak 2/1, kilit 14:57 sayaç, yenileme/kayıt/API kilitli; Cloudflare engelli → düğme kapalı, jetonsuz API 400 captcha. **Kullanıcıda:** Cloudflare'den anahtar alıp Dokploy'a girmek (DEPLOY §8).
- **Borç takibi B1 (2026-11-14):** Migration v4 (3 koleksiyon) + indeksler + `backup::KINDS`. Taksit planı `first_month` ile (içe aktarmada `asOf − paidCount + 1`); kaynak belgedeki tüm kabul rakamları birim + entegrasyon testinde birebir (78.530,48 / 79.078,85 / devreden 64.352,79 / yükler 4.311,82…182,02 / kalan 14.177,69 ve 9.865,87). Ay sonu hedefi toplam borç cinsinden (= ay başı − C − taksit; yol haritası da toplam). Kart/IBAN: 12+ haneli sayı reddedilir. Testler `tests/debt.rs` (3) + 6 birim; 2 bilinçli bozma yakalandı.
+5
View File
@@ -92,3 +92,8 @@
- ✅ 3. Arayüz cilası ve erişilebilirlik: tüm sayfaların telefon/masaüstü/koyu tema taraması, boş durumlar, skeleton, %80 bütçe uyarısı, odak/klavye, kontrast, onay diyalogları
- ✅ 4. Performans: sayfa bazlı kod bölme (lazy route), statik dosyalarda uzun önbellek, paket boyutu
- ✅ 5. PWA: manifest + service worker (uygulama kabuğu; `/api` asla önbelleğe alınmaz), kurulum, güncelleme bildirimi
## Borç Takibi Adımları (docs/BORC_TAKIBI.md, kullanıcı isteği 2026-11-14)
- ✅ B1. Backend: ölçümler, taksit planları (ilk ay + adet → aylık yük/kalan kendiliğinden), yol haritası, özet, dönem borcu (ay başı, devreden, hedef), elle ay başı, kaynak belge JSON'unu içe aktarma (ön izleme/onay), JSON yedeğine dahil
- ⏳ B2. Arayüz: Panel borç kartı, `/borc` (gerçek + hedef grafik, tablo, yeni ölçüm, hedefler, içe aktar), `/taksitler`
- ⏳ B3. Aylık plan entegrasyonu ("Borç ödemesi", ay başı/devreden/hedef, taksit önerisi) + içe aktarma dosyası (repo dışı)