use crate::version::EegGesetz;
use rust_decimal::Decimal;
use time::Date;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "scheme", rename_all = "SCREAMING_SNAKE_CASE")
)]
pub enum SettlementScheme {
FeedInTariff {
verguetungssatz_ct: Decimal,
},
TemporaryFeedInTariff {
verguetungssatz_ct: Decimal,
},
MarketPremium {
direktverm_aw_ct: Decimal,
wind_korrekturfaktor: Option<Decimal>,
wind_standort: Option<crate::wind::WindStandort>,
},
TenantElectricity {
verguetungssatz_ct: Decimal,
mieter_zuschlag_ct: Option<Decimal>,
},
PostEeg {
price_floor: Option<Decimal>,
},
KwkSurcharge {
verguetungssatz_ct: Decimal,
kwh_paid_gesamt: Option<Decimal>,
max_kwh: Option<Decimal>,
jahres_restkontingent_kwh: Option<Decimal>,
},
FlexibilityPremium {
verguetungssatz_ct: Decimal,
flex_praemie_ct_kwh: Option<Decimal>,
},
FlexibilitySurcharge {
rate_eur_per_kw_year: Decimal,
},
Eigenverbrauch,
SonstigeDirektvermarktung,
}
impl Default for SettlementScheme {
fn default() -> Self {
Self::FeedInTariff {
verguetungssatz_ct: Decimal::ZERO,
}
}
}
impl SettlementScheme {
#[must_use]
pub fn requires_marktwert(&self) -> bool {
matches!(self, Self::MarketPremium { .. } | Self::PostEeg { .. })
}
#[must_use]
pub fn is_kwh_based(&self) -> bool {
!matches!(
self,
Self::FlexibilitySurcharge { .. }
| Self::Eigenverbrauch
| Self::SonstigeDirektvermarktung
)
}
#[must_use]
pub fn negativpreis_rule_applicable(&self) -> bool {
matches!(
self,
Self::FeedInTariff { .. }
| Self::MarketPremium { .. }
| Self::TenantElectricity { .. }
| Self::TemporaryFeedInTariff { .. }
| Self::FlexibilityPremium { .. }
)
}
#[must_use]
pub fn verguetungssatz_ct(&self) -> Option<Decimal> {
match self {
Self::FeedInTariff { verguetungssatz_ct }
| Self::TemporaryFeedInTariff { verguetungssatz_ct }
| Self::TenantElectricity {
verguetungssatz_ct, ..
}
| Self::KwkSurcharge {
verguetungssatz_ct, ..
}
| Self::FlexibilityPremium {
verguetungssatz_ct, ..
} => Some(*verguetungssatz_ct),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "source")
)]
pub enum TariffSource {
Statutory,
Auction(AusschreibungMetadata),
Transitional(Paragraph100Rule),
}
#[allow(clippy::derivable_impls)]
impl Default for TariffSource {
fn default() -> Self {
Self::Statutory
}
}
impl TariffSource {
#[must_use]
pub fn is_auction(&self) -> bool {
matches!(self, Self::Auction(_))
}
#[must_use]
pub fn is_transitional(&self) -> bool {
matches!(self, Self::Transitional(_))
}
#[must_use]
pub fn is_biogas_sect51b(&self) -> bool {
matches!(self, Self::Auction(m) if m.is_biogas_sect51b)
}
#[must_use]
pub fn is_innovation_auction(&self) -> bool {
matches!(self, Self::Auction(m) if m.innovation_auction)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AusschreibungMetadata {
pub zuschlag_id: Option<String>,
pub award_ct: Option<Decimal>,
pub award_date: Option<Date>,
pub award_expired: bool,
pub innovation_auction: bool,
pub is_buergerenergie: bool,
pub is_biogas_sect51b: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum Paragraph100Rule {
OldPlantBeforeEeg2023,
Eeg2017Negativpreis6h,
BiomassTransition,
SolarpaketITransition,
Pre2016Bestandsschutz,
KwkgTransition,
BiomassOldFuelClassContinuation,
HydropowerEcologicalModernization,
SmallBiomassBelow150kw,
AuctionPoenalTransition,
MieterstromToGgvTransition,
Eeg2012DegressionSchedule,
}
impl Paragraph100Rule {
#[must_use]
pub fn implied_eeg_gesetz(self) -> Option<EegGesetz> {
match self {
Self::Pre2016Bestandsschutz => Some(EegGesetz::Eeg2012),
Self::Eeg2017Negativpreis6h
| Self::BiomassOldFuelClassContinuation
| Self::SmallBiomassBelow150kw => Some(EegGesetz::Eeg2017),
Self::OldPlantBeforeEeg2023 => Some(EegGesetz::Eeg2021),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum SettlementType {
Initial,
Correction {
original_id: String,
reason: CorrectionReason,
},
Reversal {
original_id: String,
},
}
#[allow(clippy::derivable_impls)]
impl Default for SettlementType {
fn default() -> Self {
Self::Initial
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum CorrectionReason {
MeterDataCorrected,
TariffCorrected,
MastrRegistrationConfirmed,
CapacityCorrected,
RegulatoryReprocessing,
FoerderendedatumCorrected,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum Marktwertserie {
Monatsmarktwert,
Jahresmarktwert,
}
impl Marktwertserie {
#[must_use]
pub fn as_db_str(self) -> &'static str {
match self {
Self::Monatsmarktwert => "MONATSMARKTWERT",
Self::Jahresmarktwert => "JAHRESMARKTWERT",
}
}
#[must_use]
pub fn from_db_str(s: &str) -> Option<Self> {
match s {
"MONATSMARKTWERT" => Some(Self::Monatsmarktwert),
"JAHRESMARKTWERT" => Some(Self::Jahresmarktwert),
_ => None,
}
}
}
#[must_use]
pub fn marktwertserie(
inbetriebnahme: Date,
zuschlag_datum: Option<Date>,
speicher_abgrenzungs_oder_pauschaloption: bool,
) -> Marktwertserie {
if speicher_abgrenzungs_oder_pauschaloption {
return Marktwertserie::Jahresmarktwert;
}
let vor_2023 = |d: Date| d < ANLAGE1_NR2_STICHTAG;
if vor_2023(inbetriebnahme) || zuschlag_datum.is_some_and(vor_2023) {
Marktwertserie::Monatsmarktwert
} else {
Marktwertserie::Jahresmarktwert
}
}
pub const ANLAGE1_NR2_STICHTAG: Date = time::macros::date!(2023 - 01 - 01);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum MarktpreisKategorie {
Solar,
WindOnshore,
WindOffshore,
Biomasse,
Wasserkraft,
Sonstige,
EpexDayAhead,
}