feat(rapor): aylık rapor ekranı (görüntüle, yazdır/PDF, Markdown indir)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 21:54:37 +03:00
parent dc0d783e46
commit c3ec91b2af
20 changed files with 271 additions and 23 deletions
+27 -8
View File
@@ -686,13 +686,21 @@ pub fn analyze(f: &Facts) -> Report {
let weeks: Vec<WeekRow> = f
.weeks
.iter()
.map(|w| WeekRow {
index: w.index,
range: format!("{} – {}", short(&w.start), short(&w.last)),
planned: w.planned,
spent: w.spent,
usage: ratio(w.spent, w.planned),
mark: mark(w.spent, w.planned),
.map(|w| {
// Ara raporda henüz başlamamış hafta: oran ve işaret yok ("✅ %0" yanıltır)
let future = interim && w.start.as_str() > f.today.as_str();
WeekRow {
index: w.index,
range: format!("{} – {}", short(&w.start), short(&w.last)),
planned: w.planned,
spent: w.spent,
usage: if future {
None
} else {
ratio(w.spent, w.planned)
},
mark: if future { "" } else { mark(w.spent, w.planned) },
}
})
.collect();
let finished_weeks: Vec<&WeekFact> = f
@@ -1310,6 +1318,12 @@ mod tests {
"3. hafta henüz bitmedi"
);
assert_eq!(r.suggestions[0].basis, "bu dönemin harcaması");
// 13 Kasım: 3. ve 4. hafta henüz başlamadı → oran / işaret yok
assert_eq!(
r.weeks[1].mark, "⚠️",
"2. hafta (6.000 / 6.650 = %90,2) sürüyor, oranı var"
);
assert!(r.weeks[2].usage.is_none() && r.weeks[3].mark.is_empty());
let md = render(&r).unwrap();
assert!(
@@ -1342,6 +1356,11 @@ mod tests {
assert!(md.contains("1. Market için haftalık"));
assert!(md.contains("- [ ] ____"));
assert!(md.contains("| Kredi Kartı | 20.000,00 ₺ | %72 |"));
assert!(md.contains("Rapor tarihi: 02.12.2026."));
assert!(md.contains("Rapor tarihi: 02.12.2026._"));
assert!(
md.contains("\n_Nasıl hesaplandı:"),
"dipnot düz Markdown (ham HTML gösterilmez)"
);
assert!(!md.contains('<'), "şablonda ham HTML yok");
}
}