1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! Pure EEG/KWKG feed-in settlement calculation for German energy markets.
//!
//! Implements all settlement models defined in **EEG** (all versions 2000–2023)
//! and **KWKG**. Zero I/O, zero async — every function is deterministic
//! and synchronous. No floating-point money: amounts are computed in
//! `rust_decimal::Decimal`, and every EUR result is rounded and
//! range-checked through [`crate::EuroAmount`] (`i64 × 10⁻⁵ EUR`).
//!
//! # Settlement schemes (10)
//!
//! | `SettlementScheme` | Formula | Legal basis |
//! |---|---|---|
//! | `FeedInTariff` | `kwh × verguetungssatz_ct / 100` | §21 EEG |
//! | `TenantElectricity` | Vergütung + `kwh × mieter_zuschlag_ct / 100` | §21 Abs. 3 EEG 2023 |
//! | `MarketPremium` | `max(0, AW − Marktwert) × kwh / 100` | §23a EEG + Anlage 1 |
//! | `MarketPremium` + `TariffSource::Auction` | same formula, AW from BNetzA tender | §§22a,28 EEG 2023 |
//! | `PostEeg` | `kwh × EPEX / 100` (§23b cap: 10 ct; configurable floor) | §21 EEG (post-Förderung) |
//! | `Eigenverbrauch` | EUR 0 (no feed-in remuneration) | §21 Abs. 3 EEG |
//! | `KwkSurcharge` | `eligible_kwh × rate / 100` (Vollbenutzungsstunden-Grenze) | §§7, 8 KWKG |
//! | `FlexibilityPremium` | Vergütung + `kwh × flex_praemie_ct / 100` | §50b EEG 2023 (bestehende Anlagen) |
//! | `FlexibilitySurcharge` | `kw × rate / 12` (monthly capacity payment) | §50a EEG 2023 (neue Anlagen) |
//! | `TemporaryFeedInTariff` | `kwh × verguetungssatz_ct / 100` (reduced Ausfallvergütung) | §21 Abs. 1 Satz 1 Nr. 3 EEG 2023 |
//! | `SonstigeDirektvermarktung` | EUR 0 — revenue on the open market, no NB payment | §21a EEG |
//!
//! # One formula — all EEG versions (2000–2023)
//!
//! **No separate tariff per EEG version is needed.** The settlement formula is
//! identical across all EEG versions. What differs between versions:
//!
//! 1. **Vergütungssatz (rate)** — fixed at commissioning for 20 years; caller provides it.
//! Use [`rates::solar_pv_ueberschuss_aw_ct`] or `einsd`'s `lookup_verguetungssatz`.
//!
//! 2. **§51 Negativpreisregel** — applied automatically, and keyed on the
//! **Inbetriebnahmedatum** rather than the law year: the Solarspitzengesetz
//! rewrote §51 with effect from 25.02.2025, mid-year and inside the EEG 2023
//! range. [`negativpreis::NegativpreisRegime::fuer_inbetriebnahme`] derives it
//! — ≥6 h (2016–2020), ≥4 h (2021–2022), the staged 4-3-2-1 h ladder
//! (2023–24.02.2025), the first negative quarter-hour from 25.02.2025, and
//! never for a plant commissioned before 2016.
//!
//! 3. **§100 EEG 2023 Übergangsregelung** — old plants continue under their EEG version's
//! rules (§100 Abs. 1). The rate is the only thing that changes per plant.
//!
//! # Umsatzsteuer (not Mehrwertsteuer)
//!
//! "Umsatzsteuer" (USt) is the legal term; "Mehrwertsteuer" (MwSt) is colloquial.
//! A feed-in Gutschrift has two VAT treatments — see [`ust`]:
//! - **§19 UStG Kleinunternehmer**: turnover ≤€25 000/yr → **no USt** (category `E`)
//! - **Regelbesteuerung**: all others → **19 % USt** (category `S`)
//!
//! The status is a declared property of the operator (masterdata), not something
//! the plant size decides. Use [`ust::ust_tax_layers`] to get the right
//! `billing::TaxLayer` for a document.
//!
//! # Multi-EEG-version support
//!
//! Supply `inbetriebnahme` and `leistung_kwp` in [`SettleInput`] for automatic
//! version-specific rule enforcement:
//! - §51 Negativpreisregel guard — the size exemption is per regime
//! ([`negativpreis::NegativpreisRegime::kw_grenze`]): Wind < 3 MW / others
//! < 500 kW, then < 500 kW, < 400 kW, and < 100 kW until an iMSys is in
//! (< 2 kW after)
//! - Automatic `FoerderungBeendet` when `billing_date > foerderendedatum`
//!
//! # §24 EEG Anlagenerweiterung
//!
//! Plants extended with additional capacity blocks use [`CapacityBlock`].
//! Settlement is proportionally allocated across all blocks by installed kWp.
//!
//! # Quick start
//!
//! ```rust
//! use eeg_billing::{SettleInput, SettlementScheme, calculate_settlement, SettlementStatus};
//! use rust_decimal::Decimal;
//! use std::str::FromStr;
//!
//! fn d(s: &str) -> Decimal { Decimal::from_str(s).unwrap() }
//!
//! // §21 EEG 2023 — 100 kWh × 8.51 ct/kWh (Solarpaket I, ≤10 kWp Überschuss) = 8.51 EUR
//! let out = calculate_settlement(&SettleInput {
//! scheme: eeg_billing::SettlementScheme::FeedInTariff { verguetungssatz_ct: d("8.51") },
//! einspeisemenge_kwh: Some(d("100")),
//! ..SettleInput::default()
//! });
//! assert_eq!(out.status, SettlementStatus::Calculated);
//! assert_eq!(out.settlement_eur, Some(d("8.51")));
//! ```
/// A monetary amount in euro at 10⁻⁵-EUR resolution.
///
/// `billing` 0.12 dropped its own `EuroAmount` alias — the engine is
/// currency-agnostic and the name asserted a currency the type does not carry.
/// EEG/KWKG Vergütung is euro-denominated by statute, so the alias is correct
/// here; it just belongs to the domain crate rather than the engine.
pub type EuroAmount = Amount;
pub use ;
pub use ;
pub use SettlementError;
pub use ;
pub use calculate_settlement;
pub use ;
pub use ;
pub use ;
pub use RoundMoney;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
// Domain module guide:
// degression: §49 semi-annual solar AW degression — degressionsstufen, abgesenkter_wert
// direktverm: §§20–22 Veräußerungsformen — Direktvermarktungspflicht (§21 Abs.1 S.1 Nr.1),
// Ausschreibungspflicht (§22 Abs.2–5), Zuordnung und Wechsel (§21b/§21c)
// (Metering topology, Eigenverbrauch/Überschuss split and §42b EnWG GGV allocation live in the
// external `metering` crate — AggregationRule, compute_virtual_meter, MeasurementPoint, Messtyp.)
// reductions: §52 Pflichtzahlungen — apply_sect52_netting, ReductionPipeline (euro level)
// aw_reductions: §§53b–54 — cuts to the anzulegender Wert, applied before the formula
// zusammenfassung: §24 Abs. 1 — sind_eine_anlage, the full Sätze 1–5 decision
// settlement_state: Monthly lifecycle state machine — SettlementPeriodState, derive_settlement_state
// wind: §36h Korrekturfaktor, Standortklasse, reference yield model
// biomasse: §§42–44 fuel classes, Güllekleinanlage (≤75 kW, ≥80 % Gülle), §39i Abs. 1
// kwkg: §§7–9 KWKG — Zuschlag je Leistungsanteil, Vollbenutzungsstunden, Jahreshöchstbetrag
// seed: the §§40–49 net Einspeisevergütung series as flat rows, for a service's reference table
// foerderungsende: FoerderendeGrund enum, SanktionStatus lifecycle
// scheme: SettlementScheme, TariffSource, Paragraph100Rule, SettlementType