koprogo_api/application/use_cases/
individual_member_use_cases.rs1use crate::application::dto::individual_member_dto::IndividualMemberResponseDto;
2use crate::application::ports::individual_member_repository::IndividualMemberRepository;
3use crate::domain::entities::IndividualMember;
4use chrono::Utc;
5use std::sync::Arc;
6use uuid::Uuid;
7
8pub struct IndividualMemberUseCases {
9 pub repo: Arc<dyn IndividualMemberRepository>,
10}
11
12impl IndividualMemberUseCases {
13 pub fn new(repo: Arc<dyn IndividualMemberRepository>) -> Self {
14 Self { repo }
15 }
16
17 pub async fn join_campaign(
19 &self,
20 campaign_id: Uuid,
21 email: String,
22 postal_code: String,
23 ) -> Result<IndividualMemberResponseDto, String> {
24 if let Some(_existing) = self
26 .repo
27 .find_by_email_and_campaign(&email, campaign_id)
28 .await?
29 {
30 return Err("Email already registered for this campaign".to_string());
31 }
32
33 let member = IndividualMember::new(campaign_id, email, postal_code)?;
34 let created = self.repo.create(&member).await?;
35 Ok(IndividualMemberResponseDto::from(created))
36 }
37
38 pub async fn grant_consent(
40 &self,
41 campaign_id: Uuid,
42 member_id: Uuid,
43 ) -> Result<IndividualMemberResponseDto, String> {
44 let mut member = self
45 .repo
46 .find_by_id(member_id)
47 .await?
48 .ok_or_else(|| format!("Member {} not found", member_id))?;
49
50 if member.campaign_id != campaign_id {
51 return Err("Member does not belong to this campaign".to_string());
52 }
53
54 member.has_gdpr_consent = true;
55 member.consent_at = Some(Utc::now());
56
57 let updated = self.repo.update(&member).await?;
58 Ok(IndividualMemberResponseDto::from(updated))
59 }
60
61 pub async fn update_consumption(
63 &self,
64 campaign_id: Uuid,
65 member_id: Uuid,
66 annual_kwh: Option<f64>,
67 current_provider: Option<String>,
68 ean_code: Option<String>,
69 ) -> Result<IndividualMemberResponseDto, String> {
70 let mut member = self
71 .repo
72 .find_by_id(member_id)
73 .await?
74 .ok_or_else(|| format!("Member {} not found", member_id))?;
75
76 if member.campaign_id != campaign_id {
77 return Err("Member does not belong to this campaign".to_string());
78 }
79
80 if let Some(kwh) = annual_kwh {
81 if kwh < 0.0 {
82 return Err("Consumption cannot be negative".to_string());
83 }
84 member.annual_consumption_kwh = Some(kwh);
85 }
86 if let Some(provider) = current_provider {
87 member.current_provider = Some(provider);
88 }
89 if let Some(ean) = ean_code {
90 member.ean_code = Some(ean);
91 }
92
93 let updated = self.repo.update(&member).await?;
94 Ok(IndividualMemberResponseDto::from(updated))
95 }
96
97 pub async fn withdraw(&self, campaign_id: Uuid, member_id: Uuid) -> Result<String, String> {
99 let member = self
100 .repo
101 .find_by_id(member_id)
102 .await?
103 .ok_or_else(|| format!("Member {} not found", member_id))?;
104
105 if member.campaign_id != campaign_id {
106 return Err("Member does not belong to this campaign".to_string());
107 }
108
109 let email = member.email.clone();
110 self.repo.withdraw_consent(member_id).await?;
111 Ok(email)
112 }
113}