koprogo_api/infrastructure/web/handlers/
call_for_funds_handlers.rs1use crate::application::dto::{
2 CallForFundsResponse, CreateCallForFundsRequest, SendCallForFundsRequest,
3 SendCallForFundsResponse,
4};
5use crate::domain::entities::ContributionType;
6use crate::infrastructure::web::{AppState, AuthenticatedUser};
7use actix_web::{delete, get, post, put, web, HttpResponse};
8use uuid::Uuid;
9
10#[post("/call-for-funds")]
13pub async fn create_call_for_funds(
14 state: web::Data<AppState>,
15 user: AuthenticatedUser,
16 req: web::Json<CreateCallForFundsRequest>,
17) -> HttpResponse {
18 let organization_id = match user.organization_id {
19 Some(org_id) => org_id,
20 None => return HttpResponse::BadRequest().body("Organization ID required"),
21 };
22
23 let contribution_type = match req.contribution_type.as_str() {
25 "regular" => ContributionType::Regular,
26 "extraordinary" => ContributionType::Extraordinary,
27 "advance" => ContributionType::Advance,
28 "adjustment" => ContributionType::Adjustment,
29 _ => return HttpResponse::BadRequest().body("Invalid contribution type"),
30 };
31
32 match state
33 .call_for_funds_use_cases
34 .create_call_for_funds(
35 organization_id,
36 req.building_id,
37 req.title.clone(),
38 req.description.clone(),
39 req.total_amount,
40 contribution_type,
41 req.call_date,
42 req.due_date,
43 req.account_code.clone(),
44 Some(user.user_id),
45 )
46 .await
47 {
48 Ok(call) => {
49 let response = CallForFundsResponse::from(call);
50 HttpResponse::Created().json(response)
51 }
52 Err(e) => HttpResponse::BadRequest().body(e),
53 }
54}
55
56#[get("/call-for-funds/{id}")]
59pub async fn get_call_for_funds(
60 state: web::Data<AppState>,
61 _user: AuthenticatedUser,
62 id: web::Path<Uuid>,
63) -> HttpResponse {
64 match state.call_for_funds_use_cases.get_call_for_funds(*id).await {
65 Ok(Some(call)) => {
66 let response = CallForFundsResponse::from(call);
67 HttpResponse::Ok().json(response)
68 }
69 Ok(None) => HttpResponse::NotFound().body("Call for funds not found"),
70 Err(e) => HttpResponse::InternalServerError().body(e),
71 }
72}
73
74#[get("/call-for-funds")]
77pub async fn list_call_for_funds(
78 state: web::Data<AppState>,
79 user: AuthenticatedUser,
80 query: web::Query<std::collections::HashMap<String, String>>,
81) -> HttpResponse {
82 if let Some(id_str) = query.get("building_id") {
84 let building_id = match Uuid::parse_str(id_str) {
85 Ok(id) => id,
86 Err(_) => return HttpResponse::BadRequest().body("Invalid building_id format"),
87 };
88
89 match state
90 .call_for_funds_use_cases
91 .list_by_building(building_id)
92 .await
93 {
94 Ok(calls) => {
95 let responses: Vec<CallForFundsResponse> =
96 calls.into_iter().map(Into::into).collect();
97 return HttpResponse::Ok().json(responses);
98 }
99 Err(e) => return HttpResponse::InternalServerError().body(e),
100 }
101 }
102
103 let organization_id = match user.organization_id {
105 Some(org_id) => org_id,
106 None => return HttpResponse::BadRequest().body("Organization ID required"),
107 };
108
109 match state
110 .call_for_funds_use_cases
111 .list_by_organization(organization_id)
112 .await
113 {
114 Ok(calls) => {
115 let responses: Vec<CallForFundsResponse> = calls.into_iter().map(Into::into).collect();
116 HttpResponse::Ok().json(responses)
117 }
118 Err(e) => HttpResponse::InternalServerError().body(e),
119 }
120}
121
122#[get("/call-for-funds/overdue")]
125pub async fn get_overdue_calls(
126 state: web::Data<AppState>,
127 _user: AuthenticatedUser,
128) -> HttpResponse {
129 match state.call_for_funds_use_cases.get_overdue_calls().await {
130 Ok(calls) => {
131 let responses: Vec<CallForFundsResponse> = calls.into_iter().map(Into::into).collect();
132 HttpResponse::Ok().json(responses)
133 }
134 Err(e) => HttpResponse::InternalServerError().body(e),
135 }
136}
137
138#[post("/call-for-funds/{id}/send")]
141pub async fn send_call_for_funds(
142 state: web::Data<AppState>,
143 _user: AuthenticatedUser,
144 id: web::Path<Uuid>,
145 _req: web::Json<SendCallForFundsRequest>,
146) -> HttpResponse {
147 match state
148 .call_for_funds_use_cases
149 .send_call_for_funds(*id)
150 .await
151 {
152 Ok(call) => {
153 let contributions_generated = match state
156 .owner_contribution_use_cases
157 .get_contributions_by_organization(call.organization_id)
158 .await
159 {
160 Ok(contribs) => contribs
161 .iter()
162 .filter(|c| c.call_for_funds_id == Some(call.id))
163 .count(),
164 Err(_) => 0,
165 };
166
167 let response = SendCallForFundsResponse {
168 call_for_funds: CallForFundsResponse::from(call),
169 contributions_generated,
170 };
171 HttpResponse::Ok().json(response)
172 }
173 Err(e) => HttpResponse::BadRequest().body(e),
174 }
175}
176
177#[put("/call-for-funds/{id}/cancel")]
180pub async fn cancel_call_for_funds(
181 state: web::Data<AppState>,
182 _user: AuthenticatedUser,
183 id: web::Path<Uuid>,
184) -> HttpResponse {
185 match state
186 .call_for_funds_use_cases
187 .cancel_call_for_funds(*id)
188 .await
189 {
190 Ok(call) => {
191 let response = CallForFundsResponse::from(call);
192 HttpResponse::Ok().json(response)
193 }
194 Err(e) => HttpResponse::BadRequest().body(e),
195 }
196}
197
198#[delete("/call-for-funds/{id}")]
201pub async fn delete_call_for_funds(
202 state: web::Data<AppState>,
203 _user: AuthenticatedUser,
204 id: web::Path<Uuid>,
205) -> HttpResponse {
206 match state
207 .call_for_funds_use_cases
208 .delete_call_for_funds(*id)
209 .await
210 {
211 Ok(true) => HttpResponse::NoContent().finish(),
212 Ok(false) => HttpResponse::NotFound().body("Call for funds not found"),
213 Err(e) => HttpResponse::BadRequest().body(e),
214 }
215}