30136ed1d3
- GET /periods/{id}/daily: dönemin her günü için 4 grubun harcaması
- GET /stats/periods: son dönemlerin harcanabilir/harcanan tutarı + toplam birikim
- Faz 7 adım planı
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
2.8 KiB
Rust
89 lines
2.8 KiB
Rust
//! Panel (Dashboard) verileri (KAKEIBO_PROMPT §8.2). Hiçbiri saklanmaz; her istekte toplanır.
|
||
//!
|
||
//! - `daily`: dönemin her günü için 4 grubun harcaması (harcama olmayan gün 0).
|
||
//! Günlük/haftalık ve kümülatif seriler istemcide bundan türetilir.
|
||
//! - `periods`: son N dönemin harcanabilir/harcanan tutarı (aylık grafik) ve
|
||
//! **toplam birikim** = kapanışlarda birikime aktarılanların toplamı
|
||
//! (varlık değerleri Faz 8'de bunun yerini alacak). Yeniden açılan dönemin son
|
||
//! kapanıştaki tutarı, yeniden kapatılana kadar sayılır.
|
||
|
||
use mongodb::{Database, bson::Uuid};
|
||
|
||
use crate::{
|
||
error::AppResult,
|
||
models::period::Period,
|
||
services::{
|
||
budget::{self, GroupAmounts},
|
||
calendar::{format_date, parse_date},
|
||
period,
|
||
},
|
||
};
|
||
|
||
/// Aylık grafikte en fazla dönem sayısı
|
||
pub const MAX_PERIODS: usize = 24;
|
||
|
||
pub struct DaySpent {
|
||
/// `"YYYY-MM-DD"`
|
||
pub date: String,
|
||
/// `Group::ALL` sırasıyla
|
||
pub spent: GroupAmounts,
|
||
}
|
||
|
||
pub async fn daily(db: &Database, household_id: Uuid, period_id: Uuid) -> AppResult<Vec<DaySpent>> {
|
||
let p = period::get(db, household_id, period_id).await?;
|
||
// Veritabanındaki tarihler her zaman doğrulanmış biçimde yazılır
|
||
let start = parse_date(&p.start_date).expect("geçerli başlangıç tarihi");
|
||
let end = parse_date(&p.end_date).expect("geçerli bitiş tarihi");
|
||
|
||
let mut days = Vec::new();
|
||
let mut d = start;
|
||
while d < end {
|
||
days.push(DaySpent {
|
||
date: format_date(d),
|
||
spent: [0; 4],
|
||
});
|
||
d += time::Duration::days(1);
|
||
}
|
||
for (group, date, amount) in
|
||
budget::spent_by_group_and_date(db, household_id, period_id).await?
|
||
{
|
||
// Dönem tarihleri sonradan daraltıldıysa dışarıda kalan kayıt grafiğe girmez
|
||
if let Some(day) = days.iter_mut().find(|x| x.date == date) {
|
||
day.spent[group.order()] += amount;
|
||
}
|
||
}
|
||
Ok(days)
|
||
}
|
||
|
||
pub struct PeriodStat {
|
||
pub period: Period,
|
||
pub spendable: i64,
|
||
pub spent: i64,
|
||
}
|
||
|
||
pub struct Stats {
|
||
/// Kapanışlarda birikime aktarılanların toplamı (tüm dönemler)
|
||
pub total_saved: i64,
|
||
/// Son `limit` dönem, **eskiden yeniye**
|
||
pub periods: Vec<PeriodStat>,
|
||
}
|
||
|
||
pub async fn periods(db: &Database, household_id: Uuid, limit: usize) -> AppResult<Stats> {
|
||
let list = period::list(db, household_id).await?; // en yeni başta
|
||
let total_saved = list.iter().filter_map(|p| p.to_savings).sum();
|
||
let mut out = Vec::new();
|
||
for p in list.into_iter().take(limit.clamp(1, MAX_PERIODS)) {
|
||
let s = budget::summary(db, household_id, p.id).await?;
|
||
out.push(PeriodStat {
|
||
period: p,
|
||
spendable: s.spendable,
|
||
spent: s.spent_total,
|
||
});
|
||
}
|
||
out.reverse();
|
||
Ok(Stats {
|
||
total_saved,
|
||
periods: out,
|
||
})
|
||
}
|