koprogo_api/application/ports/
budget_repository.rs1use crate::application::dto::PageRequest;
2use crate::application::error::AppError;
3use crate::domain::entities::{Budget, BudgetStatus};
4use async_trait::async_trait;
5use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[async_trait]
11pub trait BudgetRepository: Send + Sync {
12 async fn create(&self, budget: &Budget) -> Result<Budget, AppError>;
14
15 async fn find_by_id(&self, id: Uuid) -> Result<Option<Budget>, AppError>;
17
18 async fn find_by_building_and_fiscal_year(
20 &self,
21 building_id: Uuid,
22 fiscal_year: i32,
23 ) -> Result<Option<Budget>, AppError>;
24
25 async fn find_by_building(&self, building_id: Uuid) -> Result<Vec<Budget>, AppError>;
27
28 async fn find_active_by_building(&self, building_id: Uuid) -> Result<Option<Budget>, AppError>;
30
31 async fn find_by_fiscal_year(
33 &self,
34 organization_id: Uuid,
35 fiscal_year: i32,
36 ) -> Result<Vec<Budget>, AppError>;
37
38 async fn find_by_status(
40 &self,
41 organization_id: Uuid,
42 status: BudgetStatus,
43 ) -> Result<Vec<Budget>, AppError>;
44
45 async fn find_all_paginated(
47 &self,
48 page_request: &PageRequest,
49 organization_id: Option<Uuid>,
50 building_id: Option<Uuid>,
51 status: Option<BudgetStatus>,
52 ) -> Result<(Vec<Budget>, i64), AppError>;
53
54 async fn update(&self, budget: &Budget) -> Result<Budget, AppError>;
56
57 async fn delete(&self, id: Uuid) -> Result<bool, AppError>;
59
60 async fn get_stats(&self, organization_id: Uuid) -> Result<BudgetStatsResponse, AppError>;
62
63 async fn get_variance(
65 &self,
66 budget_id: Uuid,
67 ) -> Result<Option<BudgetVarianceResponse>, AppError>;
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct BudgetStatsResponse {
73 pub total_budgets: i64,
74 pub draft_count: i64,
75 pub submitted_count: i64,
76 pub approved_count: i64,
77 pub rejected_count: i64,
78 pub archived_count: i64,
79 #[serde(with = "rust_decimal::serde::float")]
81 pub average_total_budget: Decimal,
82 #[serde(with = "rust_decimal::serde::float")]
83 pub average_monthly_provision: Decimal,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct BudgetVarianceResponse {
95 pub budget_id: Uuid,
96 pub fiscal_year: i32,
97 pub building_id: Uuid,
98 #[serde(with = "rust_decimal::serde::float")]
99 pub budgeted_ordinary: Decimal,
100 #[serde(with = "rust_decimal::serde::float")]
101 pub budgeted_extraordinary: Decimal,
102 #[serde(with = "rust_decimal::serde::float")]
103 pub budgeted_total: Decimal,
104 #[serde(with = "rust_decimal::serde::float")]
105 pub actual_ordinary: Decimal,
106 #[serde(with = "rust_decimal::serde::float")]
107 pub actual_extraordinary: Decimal,
108 #[serde(with = "rust_decimal::serde::float")]
109 pub actual_total: Decimal,
110 #[serde(with = "rust_decimal::serde::float")]
111 pub variance_ordinary: Decimal,
112 #[serde(with = "rust_decimal::serde::float")]
113 pub variance_extraordinary: Decimal,
114 #[serde(with = "rust_decimal::serde::float")]
115 pub variance_total: Decimal,
116 #[serde(with = "rust_decimal::serde::float")]
117 pub variance_ordinary_pct: Decimal,
118 #[serde(with = "rust_decimal::serde::float")]
119 pub variance_extraordinary_pct: Decimal,
120 #[serde(with = "rust_decimal::serde::float")]
121 pub variance_total_pct: Decimal,
122 pub has_overruns: bool,
123 pub overrun_categories: Vec<String>,
124 pub months_elapsed: i32,
125 #[serde(with = "rust_decimal::serde::float")]
126 pub projected_year_end_total: Decimal,
127}