koprogo_api/application/ports/
budget_repository.rs

1use crate::application::dto::PageRequest;
2use crate::domain::entities::{Budget, BudgetStatus};
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7/// Repository trait for Budget persistence
8#[async_trait]
9pub trait BudgetRepository: Send + Sync {
10    /// Create a new budget
11    async fn create(&self, budget: &Budget) -> Result<Budget, String>;
12
13    /// Find budget by ID
14    async fn find_by_id(&self, id: Uuid) -> Result<Option<Budget>, String>;
15
16    /// Find budget by building and fiscal year (should be unique)
17    async fn find_by_building_and_fiscal_year(
18        &self,
19        building_id: Uuid,
20        fiscal_year: i32,
21    ) -> Result<Option<Budget>, String>;
22
23    /// Find all budgets for a building
24    async fn find_by_building(&self, building_id: Uuid) -> Result<Vec<Budget>, String>;
25
26    /// Find active budget for a building (status = Approved, most recent fiscal year)
27    async fn find_active_by_building(&self, building_id: Uuid) -> Result<Option<Budget>, String>;
28
29    /// Find budgets by fiscal year across all buildings in organization
30    async fn find_by_fiscal_year(
31        &self,
32        organization_id: Uuid,
33        fiscal_year: i32,
34    ) -> Result<Vec<Budget>, String>;
35
36    /// Find budgets by status
37    async fn find_by_status(
38        &self,
39        organization_id: Uuid,
40        status: BudgetStatus,
41    ) -> Result<Vec<Budget>, String>;
42
43    /// Find all budgets paginated
44    async fn find_all_paginated(
45        &self,
46        page_request: &PageRequest,
47        organization_id: Option<Uuid>,
48        building_id: Option<Uuid>,
49        status: Option<BudgetStatus>,
50    ) -> Result<(Vec<Budget>, i64), String>;
51
52    /// Update existing budget
53    async fn update(&self, budget: &Budget) -> Result<Budget, String>;
54
55    /// Delete budget by ID
56    async fn delete(&self, id: Uuid) -> Result<bool, String>;
57
58    /// Get budget statistics for dashboard
59    async fn get_stats(&self, organization_id: Uuid) -> Result<BudgetStatsResponse, String>;
60
61    /// Get budget variance analysis (budget vs actual expenses)
62    async fn get_variance(&self, budget_id: Uuid)
63        -> Result<Option<BudgetVarianceResponse>, String>;
64}
65
66/// Statistics response for budgets
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct BudgetStatsResponse {
69    pub total_budgets: i64,
70    pub draft_count: i64,
71    pub submitted_count: i64,
72    pub approved_count: i64,
73    pub rejected_count: i64,
74    pub archived_count: i64,
75    pub average_total_budget: f64,
76    pub average_monthly_provision: f64,
77}
78
79/// Variance analysis response
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct BudgetVarianceResponse {
82    pub budget_id: Uuid,
83    pub fiscal_year: i32,
84    pub building_id: Uuid,
85    pub budgeted_ordinary: f64,
86    pub budgeted_extraordinary: f64,
87    pub budgeted_total: f64,
88    pub actual_ordinary: f64,
89    pub actual_extraordinary: f64,
90    pub actual_total: f64,
91    pub variance_ordinary: f64,
92    pub variance_extraordinary: f64,
93    pub variance_total: f64,
94    pub variance_ordinary_pct: f64,
95    pub variance_extraordinary_pct: f64,
96    pub variance_total_pct: f64,
97    pub has_overruns: bool,
98    pub overrun_categories: Vec<String>,
99    pub months_elapsed: i32,
100    pub projected_year_end_total: f64,
101}