koprogo_api/application/dto/
energy_campaign_dto.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use crate::domain::entities::{
6    CampaignStatus, CampaignType, ContractType, EnergyCampaign, EnergyType, ProviderOffer,
7};
8
9/// DTO for creating a new energy campaign
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CreateEnergyCampaignRequest {
12    pub building_id: Option<Uuid>,
13    pub campaign_name: String,
14    pub deadline_participation: DateTime<Utc>,
15    pub energy_types: Vec<EnergyType>,
16    pub contract_duration_months: Option<i32>,
17    pub contract_type: Option<ContractType>,
18}
19
20/// DTO for energy campaign response
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct EnergyCampaignResponse {
23    pub id: Uuid,
24    pub organization_id: Uuid,
25    pub building_id: Option<Uuid>,
26    pub campaign_name: String,
27    pub campaign_type: CampaignType,
28    pub status: CampaignStatus,
29    pub deadline_participation: DateTime<Utc>,
30    pub deadline_vote: Option<DateTime<Utc>>,
31    pub contract_start_date: Option<DateTime<Utc>>,
32    pub energy_types: Vec<EnergyType>,
33    pub contract_duration_months: i32,
34    pub contract_type: ContractType,
35    pub total_participants: i32,
36    pub total_kwh_electricity: Option<f64>,
37    pub total_kwh_gas: Option<f64>,
38    pub avg_kwh_per_unit: Option<f64>,
39    pub offers_received: Vec<ProviderOfferResponse>,
40    pub selected_offer_id: Option<Uuid>,
41    pub estimated_savings_pct: Option<f64>,
42    pub created_by: Uuid,
43    pub created_at: DateTime<Utc>,
44    pub updated_at: DateTime<Utc>,
45}
46
47impl From<EnergyCampaign> for EnergyCampaignResponse {
48    fn from(campaign: EnergyCampaign) -> Self {
49        Self {
50            id: campaign.id,
51            organization_id: campaign.organization_id,
52            building_id: campaign.building_id,
53            campaign_name: campaign.campaign_name,
54            campaign_type: campaign.campaign_type,
55            status: campaign.status,
56            deadline_participation: campaign.deadline_participation,
57            deadline_vote: campaign.deadline_vote,
58            contract_start_date: campaign.contract_start_date,
59            energy_types: campaign.energy_types,
60            contract_duration_months: campaign.contract_duration_months,
61            contract_type: campaign.contract_type,
62            total_participants: campaign.total_participants,
63            total_kwh_electricity: campaign.total_kwh_electricity,
64            total_kwh_gas: campaign.total_kwh_gas,
65            avg_kwh_per_unit: campaign.avg_kwh_per_unit,
66            offers_received: campaign
67                .offers_received
68                .into_iter()
69                .map(ProviderOfferResponse::from)
70                .collect(),
71            selected_offer_id: campaign.selected_offer_id,
72            estimated_savings_pct: campaign.estimated_savings_pct,
73            created_by: campaign.created_by,
74            created_at: campaign.created_at,
75            updated_at: campaign.updated_at,
76        }
77    }
78}
79
80/// DTO for campaign status update
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct UpdateCampaignStatusRequest {
83    pub status: CampaignStatus,
84}
85
86/// DTO for campaign statistics (anonymized)
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct CampaignStatsResponse {
89    pub total_participants: i32,
90    pub participation_rate: f64,
91    pub total_kwh_electricity: Option<f64>,
92    pub total_kwh_gas: Option<f64>,
93    pub avg_kwh_per_unit: Option<f64>,
94    pub can_negotiate: bool,
95    pub estimated_savings_pct: Option<f64>,
96    pub k_anonymity_met: bool, // True if >= 5 participants
97}
98
99/// DTO for creating provider offer
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct CreateProviderOfferRequest {
102    pub provider_name: String,
103    pub price_kwh_electricity: Option<f64>,
104    pub price_kwh_gas: Option<f64>,
105    pub fixed_monthly_fee: f64,
106    pub green_energy_pct: f64,
107    pub contract_duration_months: i32,
108    pub estimated_savings_pct: f64,
109    pub offer_valid_until: DateTime<Utc>,
110}
111
112/// DTO for provider offer response
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct ProviderOfferResponse {
115    pub id: Uuid,
116    pub campaign_id: Uuid,
117    pub provider_name: String,
118    pub price_kwh_electricity: Option<f64>,
119    pub price_kwh_gas: Option<f64>,
120    pub fixed_monthly_fee: f64,
121    pub green_energy_pct: f64,
122    pub green_score: i32, // Calculated 0/5/10
123    pub contract_duration_months: i32,
124    pub estimated_savings_pct: f64,
125    pub offer_valid_until: DateTime<Utc>,
126    pub created_at: DateTime<Utc>,
127    pub updated_at: DateTime<Utc>,
128}
129
130impl From<ProviderOffer> for ProviderOfferResponse {
131    fn from(offer: ProviderOffer) -> Self {
132        let green_score = offer.green_score();
133
134        Self {
135            id: offer.id,
136            campaign_id: offer.campaign_id,
137            provider_name: offer.provider_name,
138            price_kwh_electricity: offer.price_kwh_electricity,
139            price_kwh_gas: offer.price_kwh_gas,
140            fixed_monthly_fee: offer.fixed_monthly_fee,
141            green_energy_pct: offer.green_energy_pct,
142            green_score,
143            contract_duration_months: offer.contract_duration_months,
144            estimated_savings_pct: offer.estimated_savings_pct,
145            offer_valid_until: offer.offer_valid_until,
146            created_at: offer.created_at,
147            updated_at: offer.updated_at,
148        }
149    }
150}
151
152/// DTO for selecting winning offer
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct SelectOfferRequest {
155    pub offer_id: Uuid,
156    pub poll_id: Option<Uuid>, // Reference to voting poll (Issue #51)
157}