//! Sabit gelir / gider kalemleri: //! //! | Yöntem | Yol | İş | //! |---|---|---| //! | GET | `/api/periods/{id}/plan-items` | dönemin kalemleri | //! | POST | `/api/periods/{id}/plan-items` | kalem ekle | //! | POST | `/api/periods/{id}/plan-items/copy-previous` | önceki dönemden kopyala | //! | PATCH | `/api/plan-items/{id}` | düzenle | //! | DELETE | `/api/plan-items/{id}` | sil | use axum::{ Json, extract::{Path, State}, http::StatusCode, }; use mongodb::bson::Uuid; use serde::{Deserialize, Serialize}; use ts_rs::TS; use validator::Validate; use crate::{ auth::AuthUser, error::{AppError, AppResult}, extract::ValidJson, models::plan_item::{PlanItem, PlanKind}, services::plan::{self, ItemChanges, NewItem}, state::AppState, }; #[derive(Deserialize, Validate, TS)] #[ts(export, rename = "PlanItemCreateRequest", optional_fields)] pub struct CreateRequest { kind: PlanKind, name: String, /// Kuruş, > 0 #[ts(type = "number")] amount: i64, /// `"YYYY-MM-DD"`, dönem içinde (isteğe bağlı) date: Option, } #[derive(Deserialize, Validate, TS)] #[ts(export, rename = "PlanItemUpdateRequest", optional_fields)] pub struct UpdateRequest { name: Option, #[ts(type = "number", optional)] amount: Option, /// Boş metin → tarihi kaldır date: Option, version: Option, } #[derive(Serialize, TS)] #[ts(export, rename = "PlanItem")] pub struct PlanItemDto { id: String, kind: PlanKind, name: String, /// Kuruş #[ts(type = "number")] amount: i64, date: Option, sort_order: i32, version: i32, } #[derive(Serialize, TS)] #[ts(export)] pub struct CopyPreviousResult { /// Eklenen kalem sayısı (zaten var olanlar atlanır) added: usize, } impl From for PlanItemDto { fn from(i: PlanItem) -> Self { Self { id: i.id.to_string(), kind: i.kind, name: i.name, amount: i.amount, date: i.date, sort_order: i.sort_order, version: i.version, } } } fn parse_id(id: &str) -> AppResult { Uuid::parse_str(id).map_err(|_| AppError::NotFound) } pub async fn list( State(s): State, auth: AuthUser, Path(period_id): Path, ) -> AppResult>> { let items = plan::list(&s.db, auth.household_id, parse_id(&period_id)?).await?; Ok(Json(items.into_iter().map(PlanItemDto::from).collect())) } pub async fn create( State(s): State, auth: AuthUser, Path(period_id): Path, ValidJson(req): ValidJson, ) -> AppResult<(StatusCode, Json)> { let item = plan::create( &s.db, auth.household_id, parse_id(&period_id)?, NewItem { kind: req.kind, name: req.name, amount: req.amount, date: req.date, }, ) .await?; Ok((StatusCode::CREATED, Json(item.into()))) } pub async fn copy_previous( State(s): State, auth: AuthUser, Path(period_id): Path, ) -> AppResult> { let added = plan::copy_previous(&s.db, auth.household_id, parse_id(&period_id)?).await?; Ok(Json(CopyPreviousResult { added })) } pub async fn update( State(s): State, auth: AuthUser, Path(id): Path, ValidJson(req): ValidJson, ) -> AppResult> { let item = plan::update( &s.db, auth.household_id, parse_id(&id)?, ItemChanges { name: req.name, amount: req.amount, date: req.date, version: req.version, }, ) .await?; Ok(Json(item.into())) } pub async fn delete( State(s): State, auth: AuthUser, Path(id): Path, ) -> AppResult { plan::delete(&s.db, auth.household_id, parse_id(&id)?).await?; Ok(StatusCode::NO_CONTENT) }