Skip to main content

koprogo_api/application/dto/
energy_campaign_dto.rs

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