koprogo_api/infrastructure/web/handlers/
marketplace_handlers.rs

1use actix_web::{get, post, web, HttpResponse};
2use chrono::Datelike;
3use uuid::Uuid;
4
5use crate::application::dto::{
6    ContractEvaluationsAnnualReportDto, CreateServiceProviderDto, SearchServiceProvidersQuery,
7    ServiceProviderResponseDto,
8};
9use crate::domain::entities::{ServiceProvider, TradeCategory};
10use crate::infrastructure::web::middleware::AuthenticatedUser;
11use crate::infrastructure::web::AppState;
12
13/// GET /api/v1/marketplace/providers
14/// Search for service providers (public - no authentication required)
15/// Query params: trade_category, postal_code, min_rating, is_verified_only
16#[get("/marketplace/providers")]
17pub async fn search_service_providers(
18    _query: web::Query<SearchServiceProvidersQuery>,
19) -> Result<HttpResponse, actix_web::Error> {
20    // TODO: Implement search with filters
21    // For now, return empty list as placeholder
22    let providers: Vec<ServiceProviderResponseDto> = vec![];
23    Ok(HttpResponse::Ok().json(providers))
24}
25
26/// GET /api/v1/marketplace/providers/{slug}
27/// Get public service provider profile (no authentication required)
28#[get("/marketplace/providers/{slug}")]
29pub async fn get_provider_by_slug(
30    slug: web::Path<String>,
31) -> Result<HttpResponse, actix_web::Error> {
32    let slug_str = slug.into_inner();
33    // TODO: Query provider by slug, return public profile
34    Ok(HttpResponse::NotFound().json(serde_json::json!({
35        "error": format!("Provider not found: {}", slug_str)
36    })))
37}
38
39/// POST /api/v1/service-providers
40/// Create a new service provider (authenticated - syndic/admin only)
41#[post("/service-providers")]
42pub async fn create_service_provider(
43    _state: web::Data<AppState>,
44    request: web::Json<CreateServiceProviderDto>,
45    user: AuthenticatedUser,
46) -> Result<HttpResponse, actix_web::Error> {
47    let org_id = user
48        .organization_id
49        .ok_or_else(|| actix_web::error::ErrorBadRequest("Organization ID required"))?;
50
51    // Parse trade category
52    let trade_category = TradeCategory::from_sql(&request.trade_category)
53        .map_err(actix_web::error::ErrorBadRequest)?;
54
55    // Create service provider
56    let provider = ServiceProvider::new(
57        org_id,
58        request.company_name.clone(),
59        trade_category,
60        request.bce_number.clone(),
61    )
62    .map_err(actix_web::error::ErrorBadRequest)?;
63
64    // TODO: Save to database using repository
65    // For now, just return the created provider
66    let response = ServiceProviderResponseDto::from(provider);
67    Ok(HttpResponse::Created().json(response))
68}
69
70/// GET /api/v1/buildings/{building_id}/reports/contract-evaluations/annual
71/// Get annual contract evaluations report (L13 legal report)
72/// Query param: year (default: current year)
73#[get("/buildings/{building_id}/reports/contract-evaluations/annual")]
74pub async fn get_contract_evaluations_annual(
75    _state: web::Data<AppState>,
76    building_id: web::Path<Uuid>,
77    web::Query(params): web::Query<std::collections::HashMap<String, String>>,
78    _user: AuthenticatedUser,
79) -> Result<HttpResponse, actix_web::Error> {
80    let building_id = building_id.into_inner();
81    let year = params
82        .get("year")
83        .and_then(|y| y.parse::<i32>().ok())
84        .unwrap_or_else(|| chrono::Local::now().year());
85
86    // TODO: Query contract evaluations for building and year
87    // Filter: is_legal_evaluation = true
88    // Calculate: average score, recommendation rate
89    let report = ContractEvaluationsAnnualReportDto {
90        building_id: building_id.to_string(),
91        report_year: year,
92        total_evaluations: 0,
93        total_providers_evaluated: 0,
94        average_global_score: 0.0,
95        recommendation_rate: 0.0,
96        evaluations: vec![],
97    };
98
99    Ok(HttpResponse::Ok().json(report))
100}
101
102#[cfg(test)]
103mod tests {
104    #[test]
105    fn test_marketplace_handlers_compile() {
106        // Placeholder test to verify handler structure
107    }
108}