koprogo_api/application/use_cases/
stats_use_cases.rs

1use crate::application::dto::{
2    AdminDashboardStats, SeedDataStats, SyndicDashboardStats, UrgentTask,
3};
4use crate::application::ports::StatsRepository;
5use std::sync::Arc;
6use uuid::Uuid;
7
8pub struct StatsUseCases {
9    repo: Arc<dyn StatsRepository>,
10}
11
12impl StatsUseCases {
13    pub fn new(repo: Arc<dyn StatsRepository>) -> Self {
14        Self { repo }
15    }
16
17    pub async fn get_admin_dashboard_stats(&self) -> Result<AdminDashboardStats, String> {
18        self.repo.get_admin_dashboard_stats().await
19    }
20
21    pub async fn get_seed_data_stats(&self) -> Result<SeedDataStats, String> {
22        self.repo.get_seed_data_stats().await
23    }
24
25    pub async fn get_syndic_stats(
26        &self,
27        organization_id: Uuid,
28    ) -> Result<SyndicDashboardStats, String> {
29        self.repo.get_syndic_stats(organization_id).await
30    }
31
32    /// Returns owner stats. If the user has no owner record returns empty stats.
33    pub async fn get_owner_stats_by_user_id(
34        &self,
35        user_id: Uuid,
36    ) -> Result<SyndicDashboardStats, String> {
37        match self.repo.find_owner_id_by_user_id(user_id).await? {
38            None => Ok(SyndicDashboardStats {
39                total_buildings: 0,
40                total_units: 0,
41                total_owners: 0,
42                pending_expenses_count: 0,
43                pending_expenses_amount: 0.0,
44                next_meeting: None,
45            }),
46            Some(owner_id) => self.repo.get_owner_stats(owner_id).await,
47        }
48    }
49
50    pub async fn get_syndic_urgent_tasks(
51        &self,
52        organization_id: Uuid,
53    ) -> Result<Vec<UrgentTask>, String> {
54        self.repo.get_syndic_urgent_tasks(organization_id).await
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use async_trait::async_trait;
62
63    struct MockStatsRepository {
64        owner_id: Option<Uuid>,
65    }
66
67    #[async_trait]
68    impl StatsRepository for MockStatsRepository {
69        async fn get_admin_dashboard_stats(&self) -> Result<AdminDashboardStats, String> {
70            Ok(AdminDashboardStats {
71                total_organizations: 5,
72                total_users: 50,
73                total_buildings: 10,
74                active_subscriptions: 4,
75                total_owners: 30,
76                total_units: 100,
77                total_expenses: 200,
78                total_meetings: 20,
79            })
80        }
81        async fn get_seed_data_stats(&self) -> Result<SeedDataStats, String> {
82            Ok(SeedDataStats {
83                seed_organizations: 1,
84                production_organizations: 4,
85                seed_buildings: 3,
86                seed_units: 15,
87                seed_owners: 10,
88                seed_unit_owners: 15,
89                seed_expenses: 20,
90                seed_meetings: 5,
91                seed_users: 8,
92            })
93        }
94        async fn get_syndic_stats(
95            &self,
96            _organization_id: Uuid,
97        ) -> Result<SyndicDashboardStats, String> {
98            Ok(SyndicDashboardStats {
99                total_buildings: 2,
100                total_units: 10,
101                total_owners: 8,
102                pending_expenses_count: 3,
103                pending_expenses_amount: 1500.0,
104                next_meeting: None,
105            })
106        }
107        async fn get_owner_stats(&self, _owner_id: Uuid) -> Result<SyndicDashboardStats, String> {
108            Ok(SyndicDashboardStats {
109                total_buildings: 1,
110                total_units: 2,
111                total_owners: 5,
112                pending_expenses_count: 1,
113                pending_expenses_amount: 500.0,
114                next_meeting: None,
115            })
116        }
117        async fn find_owner_id_by_user_id(&self, _user_id: Uuid) -> Result<Option<Uuid>, String> {
118            Ok(self.owner_id)
119        }
120        async fn get_syndic_urgent_tasks(
121            &self,
122            _organization_id: Uuid,
123        ) -> Result<Vec<UrgentTask>, String> {
124            Ok(vec![])
125        }
126    }
127
128    #[tokio::test]
129    async fn test_get_admin_dashboard_stats() {
130        let repo = Arc::new(MockStatsRepository { owner_id: None });
131        let use_cases = StatsUseCases::new(repo);
132        let stats = use_cases.get_admin_dashboard_stats().await.unwrap();
133        assert_eq!(stats.total_organizations, 5);
134        assert_eq!(stats.total_buildings, 10);
135    }
136
137    #[tokio::test]
138    async fn test_get_owner_stats_no_owner_record_returns_empty() {
139        let repo = Arc::new(MockStatsRepository { owner_id: None });
140        let use_cases = StatsUseCases::new(repo);
141        let stats = use_cases
142            .get_owner_stats_by_user_id(Uuid::new_v4())
143            .await
144            .unwrap();
145        assert_eq!(stats.total_buildings, 0);
146        assert_eq!(stats.total_units, 0);
147        assert!(stats.next_meeting.is_none());
148    }
149
150    #[tokio::test]
151    async fn test_get_owner_stats_with_owner_record() {
152        let owner_id = Uuid::new_v4();
153        let repo = Arc::new(MockStatsRepository {
154            owner_id: Some(owner_id),
155        });
156        let use_cases = StatsUseCases::new(repo);
157        let stats = use_cases
158            .get_owner_stats_by_user_id(Uuid::new_v4())
159            .await
160            .unwrap();
161        assert_eq!(stats.total_buildings, 1);
162        assert_eq!(stats.pending_expenses_count, 1);
163    }
164}