feat(defter): özet ve bütçeye gerçekleşen harcama eklendi (backend)
- Dönem özeti: spent_total ve remaining (harcanabilir − harcanan) - Bütçe görünümü: grup ve hafta bazında spent - Değerler saklanmaz, kayıtlardan anlık toplanır; silinenler sayılmaz Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,7 +88,9 @@ async fn summary_computes_spendable_with_carryovers() {
|
||||
// 4.800.000 − 1.545.000 − 500.000 − 100.000 + 20.000
|
||||
"spendable": 2_675_000,
|
||||
"budget_total": 0,
|
||||
"unallocated": 2_675_000
|
||||
"unallocated": 2_675_000,
|
||||
"spent_total": 0,
|
||||
"remaining": 2_675_000
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -451,3 +451,124 @@ async fn previous_suggestion_uses_real_entries() {
|
||||
|
||||
app.cleanup().await;
|
||||
}
|
||||
|
||||
fn spent_by_week(budget: &Value, group: &str) -> (i64, Vec<i64>) {
|
||||
let g = budget
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|g| g["group"] == group)
|
||||
.unwrap();
|
||||
let weeks = g["weeks"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|w| w["spent"].as_i64().unwrap())
|
||||
.collect();
|
||||
(g["spent"].as_i64().unwrap(), weeks)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn actual_spending_in_summary_and_budget() {
|
||||
let app = TestApp::new().await;
|
||||
let s = app.register("Test", None).await;
|
||||
// Kasım'da bir harcama: Aralık toplamlarına girmemeli
|
||||
let nov = open_period(&app, &s, "2026-11-01", "2026-12-01").await;
|
||||
let needs = category(&app, &s, "needs").await;
|
||||
let wants = category(&app, &s, "wants").await;
|
||||
let culture = category(&app, &s, "culture").await;
|
||||
let (st, _) = add(
|
||||
&app,
|
||||
&s,
|
||||
&nov,
|
||||
json!({ "date": "2026-11-30", "amount": 1_000_000, "category_id": needs }),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(st, StatusCode::CREATED);
|
||||
close(&app, &nov).await;
|
||||
let pid = open_period(&app, &s, "2026-12-01", "2027-01-01").await;
|
||||
|
||||
app.post(
|
||||
&format!("/periods/{pid}/plan-items"),
|
||||
&s,
|
||||
json!({ "kind": "income", "name": "Maaş", "amount": 300_000 }),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Aralık 2026 haftaları: 1–6 · 7–13 · 14–20 · 21–27 · 28–31
|
||||
for (date, amount, cat) in [
|
||||
("2026-12-01", 1_000, &needs),
|
||||
("2026-12-06", 2_000, &needs), // 1. haftanın son günü
|
||||
("2026-12-07", 500, &needs), // 2. haftanın ilk günü
|
||||
("2026-12-31", 300, &wants), // dönemin son günü
|
||||
] {
|
||||
let (st, b) = add(
|
||||
&app,
|
||||
&s,
|
||||
&pid,
|
||||
json!({ "date": date, "amount": amount, "category_id": cat }),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(st, StatusCode::CREATED, "{b}");
|
||||
}
|
||||
// Kategori değişince harcama diğer gruba geçer
|
||||
let (_, moved) = add(
|
||||
&app,
|
||||
&s,
|
||||
&pid,
|
||||
json!({ "date": "2026-12-08", "amount": 400, "category_id": needs }),
|
||||
)
|
||||
.await;
|
||||
app.patch(
|
||||
&format!("/entries/{}", moved["id"].as_str().unwrap()),
|
||||
&s,
|
||||
json!({ "category_id": wants }),
|
||||
)
|
||||
.await;
|
||||
// Silinen harcama sayılmaz
|
||||
let (_, gone) = add(
|
||||
&app,
|
||||
&s,
|
||||
&pid,
|
||||
json!({ "date": "2026-12-15", "amount": 700, "category_id": culture }),
|
||||
)
|
||||
.await;
|
||||
let gone_id = gone["id"].as_str().unwrap();
|
||||
app.delete(&format!("/entries/{gone_id}"), &s).await;
|
||||
|
||||
let (_, sum) = app.get(&format!("/periods/{pid}/summary"), &s).await;
|
||||
// 1.000 + 2.000 + 500 + 300 + 400 = 4.200
|
||||
assert_eq!(
|
||||
(sum["spent_total"].as_i64(), sum["remaining"].as_i64()),
|
||||
(Some(4_200), Some(295_800)),
|
||||
"{sum}"
|
||||
);
|
||||
|
||||
let (st, b) = app.get(&format!("/periods/{pid}/budget"), &s).await;
|
||||
assert_eq!(st, StatusCode::OK);
|
||||
assert_eq!(
|
||||
spent_by_week(&b, "needs"),
|
||||
(3_500, vec![3_000, 500, 0, 0, 0])
|
||||
);
|
||||
assert_eq!(spent_by_week(&b, "wants"), (700, vec![0, 400, 0, 0, 300]));
|
||||
assert_eq!(spent_by_week(&b, "culture"), (0, vec![0; 5]));
|
||||
|
||||
// Geri al → yeniden sayılır
|
||||
app.post(&format!("/entries/{gone_id}/restore"), &s, json!({}))
|
||||
.await;
|
||||
let (_, b) = app.get(&format!("/periods/{pid}/budget"), &s).await;
|
||||
assert_eq!(spent_by_week(&b, "culture"), (700, vec![0, 0, 700, 0, 0]));
|
||||
|
||||
// Harcanabilir aşılırsa kalan negatif
|
||||
add(
|
||||
&app,
|
||||
&s,
|
||||
&pid,
|
||||
json!({ "date": "2026-12-20", "amount": 300_000, "category_id": needs }),
|
||||
)
|
||||
.await;
|
||||
let (_, sum) = app.get(&format!("/periods/{pid}/summary"), &s).await;
|
||||
assert_eq!(sum["remaining"], 300_000 - 304_900);
|
||||
|
||||
app.cleanup().await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user