use crate::technology::ErzeugungsArt;
use crate::version::EegGesetz;
use rust_decimal::Decimal;
use time::Date;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[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 SanktionAlt {
VerguetungAufNull,
VerguetungAufMarktwert,
VerguetungReduziert20Prozent,
}
#[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 SanktionsTyp {
FernsteuerbarkeitmFehlend,
SpeicherAnforderungNichtErfuellt,
IMssAnforderungNichtErfuellt,
DirektvermarktungspflichtVerletzt,
MastrNichtRegistriert,
InbetriebnahmeVorgabeVerletzt,
VolleinspeisungspflichtVerletzt,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pflichtverstoss {
pub typ: SanktionsTyp,
pub leistung_kw: Decimal,
pub monate_des_verstosses: u32,
pub nachtraeglich_erfuellt: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum SettlementModel {
Verguetung,
Mieterstrom,
Direktvermarktung,
Ausschreibung,
PostEegSpot,
Eigenverbrauch,
KwkgZuschlag,
Flexibilitaet,
FlexibilitaetZuschlag,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum Messkonzept {
Volleinspeisung,
Ueberschusseinspeisung,
Direktlieferung,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CapacityBlock {
pub leistung_kwp: Decimal,
pub verguetungssatz_ct: Decimal,
pub inbetriebnahme: Date,
pub foerderendedatum: Date,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SettleInput {
pub model: SettlementModel,
pub einspeisemenge_kwh: Option<Decimal>,
pub epex_avg_ct_kwh: Option<Decimal>,
pub verguetungssatz_ct: Decimal,
pub direktverm_aw_ct: Option<Decimal>,
pub mieter_zuschlag_ct: Option<Decimal>,
pub flex_praemie_ct_kwh: Option<Decimal>,
pub managementpraemie_ct: Option<Decimal>,
pub kwk_strom_kwh_gesamt: Option<Decimal>,
pub kwk_max_kwh: Option<Decimal>,
pub sanktion: Option<SanktionAlt>,
pub pflichtverstoss: Option<Pflichtverstoss>,
pub kwh_during_negative_epex: Option<Decimal>,
pub inbetriebnahme: Option<Date>,
pub leistung_kwp: Option<Decimal>,
pub foerderendedatum: Option<Date>,
pub billing_date: Option<Date>,
pub capacity_blocks: Vec<CapacityBlock>,
pub messkonzept: Option<Messkonzept>,
pub eeg_gesetz: EegGesetz,
pub erzeugungsart: Option<ErzeugungsArt>,
}
impl Default for SettleInput {
fn default() -> Self {
Self {
model: SettlementModel::Verguetung,
einspeisemenge_kwh: None,
epex_avg_ct_kwh: None,
verguetungssatz_ct: Decimal::ZERO,
direktverm_aw_ct: None,
mieter_zuschlag_ct: None,
flex_praemie_ct_kwh: None,
managementpraemie_ct: None,
kwk_strom_kwh_gesamt: None,
kwk_max_kwh: None,
sanktion: None,
kwh_during_negative_epex: None,
inbetriebnahme: None,
leistung_kwp: None,
foerderendedatum: None,
billing_date: None,
capacity_blocks: vec![],
messkonzept: None,
pflichtverstoss: None,
eeg_gesetz: EegGesetz::default(), erzeugungsart: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SettleOutput {
pub settlement_eur: Option<Decimal>,
pub eligible_kwh: Option<Decimal>,
pub positions: Vec<SettlePosition>,
pub status: SettlementStatus,
pub pflichtzahlung_eur: Option<Decimal>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SettlePosition {
pub description: String,
pub legal_basis: String,
pub kwh: Decimal,
pub rate_ct_kwh: Decimal,
pub eur: Decimal,
}
impl SettlePosition {
pub fn to_line_item(&self) -> billing::LineItem {
use billing::LineItem;
let rate_eur = self.rate_ct_kwh / rust_decimal::Decimal::from(100);
let mut builder =
LineItem::for_usage(&self.description, self.kwh, "kWh", rate_eur, "EUR/kWh")
.meta("legal_basis", self.legal_basis.as_str());
if self.legal_basis.contains("EEG") || self.legal_basis.contains("post-F\u{00f6}rderung") {
builder = builder.tag("eeg");
}
if self.legal_basis.contains("KWKG") {
builder = builder.tag("kwkg");
}
builder = match self.legal_basis.as_str() {
b if b.starts_with("\u{00a7}20 Abs. 3") => builder.tag("managementpraemie"),
b if b.starts_with("\u{00a7}20") || b.starts_with("\u{00a7}\u{00a7}22a") => {
builder.tag("marktpraemie")
}
"\u{00a7}38a EEG 2023" => builder.tag("mieterstrom"),
b if b == "\u{00a7}50b EEG 2023" || b == "\u{00a7}50 EEG 2023" => {
builder.tag("flexibilitaet")
}
b if b.contains("post-F\u{00f6}rderung") => builder.tag("post-eeg-spot"),
"\u{00a7}7 KWKG 2023" => builder.tag("kwk-zuschlag"),
"\u{00a7}21 EEG 2023" => builder.tag("verguetung"),
_ => builder,
};
builder
.build()
.expect("SettlePosition always has a non-empty static description")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum SettlementStatus {
Calculated,
NoData,
PriceMissing,
FoerderungBeendet,
Sanctioned,
}