Skip to main content

energy_billing/
lib.rs

1//! Pure multi-product retail energy billing for German markets.
2//!
3//! ## Architecture
4//!
5//! This crate is the **commercial billing engine for the Lieferant (LF)**. It
6//! answers: *"What does the customer's invoice look like?"*
7//!
8//! ```text
9//! metering       — "What quantities are billable?"
10//!     ↓
11//! eeg-billing    — "What EEG remuneration applies?" (NB-side)
12//!     ↓
13//! energy-billing — "What does the customer's invoice look like?" (LF-side)
14//!     ↓
15//! accountingd    — Payments, Ledger, Dunning
16//! ```
17//!
18//! ## Primary API — `Product::build_engine`
19//!
20//! ```rust
21//! use energy_billing::{BillingContext, BillingPeriod, GridInput, InvoiceType, MeterInput, Product, Quantities, RegulatoryRates};
22//! use rust_decimal::dec;
23//! use time::macros::date;
24//!
25//! let json = r#"{"category":"STROM","arbeitspreis_ct_per_kwh":"30.0","grundpreis_ct_per_day":"8.0"}"#;
26//! let product: Product = serde_json::from_str(json).unwrap();
27//! let ctx = BillingContext {
28//!     malo_id:         "51238696012".to_owned(),
29//!     lf_mp_id:        "9900000000001".to_owned(),
30//!     rechnungsnummer: "R2026-001".to_owned(),
31//!     period: BillingPeriod::new(date!(2026-01-01), date!(2026-01-31)).unwrap(),
32//!     invoice_type:     InvoiceType::Initial,
33//!     contract_id:      None,
34//!     regulatory_rates: RegulatoryRates::default(),
35//!     ..Default::default()
36//! };
37//! let quantities = Quantities {
38//!     electricity: Some(MeterInput { arbeitsmenge_kwh: dec!(500), ..Default::default() }),
39//!     ..Default::default()
40//! };
41//! let invoice = product.build_engine(&GridInput::default(), &RegulatoryRates::default())
42//!     .bill(ctx, &quantities).unwrap();
43//! assert!(invoice.brutto_eur > invoice.netto_eur);
44//! ```
45//!
46//! ## Product categories
47//!
48//! | Category | Provider | Legal basis |
49//! |---|---|---|
50//! | `STROM` | `ElectricityProvider` | §41 EnWG |
51//! | `WAERMEPUMPE` | `ControllableLoadProvider` (§14a) | §14a EnWG |
52//! | `WALLBOX` | `ControllableLoadProvider` (§14a) | §14a EnWG |
53//! | `GAS` | `GasProvider` | §41 EnWG |
54//! | `WAERME` | `HeatProvider` | §41 EnWG; AVBFernwärmeV §24; CO2KostAufG §3; §14 WPG |
55//! | `WASSER` | `WaterProvider` | AVBWasserV; §12 Abs. 2 Nr. 1 UStG (7 %); gesplittete Abwassergebühr |
56//! | `SOLAR` | `SolarProvider` | §42a Abs. 4 EnWG (Mieterstrom-Preisdeckel) / §42b EnWG (GGV) |
57//! | `EEG` | `EegProvider` (→ eeg-billing) | §§20–21 EEG 2023 |
58//! | `EINSPEISUNG` | `EinspeisungProvider` | §20 EEG 2023 |
59//! | `HEMS` | `HemsProvider` | — |
60//! | `EMOBILITY` | `EmobilityProvider` | §41a EnWG |
61//! | `ENERGIEDIENSTLEISTUNG` | `ServiceProvider` | — |
62//! | `STROM` + `dynamic_epex=true` | `DynamicElectricityProvider` | §41a EnWG |
63//! | `SHARING` | `ElectricityProvider` + `EnergyShareProvider` | §42c EnWG |
64
65#![deny(unsafe_code)]
66
67// ── Modules ───────────────────────────────────────────────────────────────────
68
69pub mod context;
70/// EN 16931 semantic-model bridge (`Invoice::to_en16931`), behind `en16931`.
71#[cfg(feature = "en16931")]
72pub mod en16931_map;
73pub mod engine;
74pub mod error;
75pub mod invoice;
76pub mod position;
77pub mod provider;
78pub mod providers;
79pub mod quantities;
80pub mod rates;
81/// Verbrauchsteuerliche Begünstigungen — Befreiung, Ermäßigung, Entlastung.
82///
83/// Only the first two change what a supplier invoices; the third is the
84/// customer's own claim at the Hauptzollamt. Keeping them apart is what stops a
85/// § 9b StromStG relief from being billed as a § 9 Abs. 1 exemption.
86pub mod steuer;
87pub mod tariff;
88
89// ── Primary API re-exports ────────────────────────────────────────────────────
90
91// Core billing types
92pub use context::{
93    AbschlagDeduction, BillingContext, BillingPeriod, CustomerKategorie, InvoiceType,
94    Rechnungsempfaenger, SettlementForm, Verbraucherinformationen, Verbrauchshistorie, Vertragsart,
95    Vertragsinformationen,
96};
97pub use engine::BillingEngine;
98pub use error::EngineError;
99pub use invoice::{
100    Invoice, TaxSubtotal, VatCategory, negate_rechnung_json_for_correction, tax_subtotals_of,
101};
102pub use position::{
103    BillingPosition, BillingWarning, PositionCategory, PositionTrace, WarningSeverity,
104};
105pub use provider::{BillingProvider, MTU_MINUTES, mtu_start};
106pub use quantities::{
107    Ablesungsart, Abschlagsplan, AbschlagsplanEntry, Absetzung, AbsetzungsGrund, DayApportionment,
108    DynamicInterval, EegMeterInput, EmobilityMeterInput, EnergyShareMeterInput, GasMeterInput,
109    GgvNutzungsplan, GgvNutzungsplanEntry, GgvSolarInput, GridInput, HemsMeterInput, MeterInput,
110    MeteringMode, ProsumerMeterInput, Quantities, Sect14aModul3Verbrauch, Sect41aAnnualComparison,
111    ServiceMeterInput, SolarMeterInput, WaermeMeterInput, WasserMeterInput,
112};
113pub use rates::{
114    ERDGAS_UMRECHNUNGSFAKTOR_GJ_PER_MWH, RegulatoryRates, RoundMoney, behg_ct_per_kwh_for_year,
115    behg_ct_per_kwh_from_price, energiesteuer_gas_for_year, erdgas_emissionsfaktor_kg_per_kwh,
116    mwst_rate_for_gas_waerme_period, mwst_rate_for_period, round_money,
117    steuer_stichtage_im_zeitraum, stromsteuer_for_year,
118};
119pub use steuer::{
120    EnergiesteuerBefreiung, EnergiesteuerTarif, Steuerentlastung, StromsteuerBefreiung,
121    StromsteuerErmaessigung, StromsteuerTarif,
122};
123
124// Typed Product enum + per-category product structs
125pub use tariff::{
126    AbwasserRegime, BlockTierInput, ControllableLoadProduct, EegProduct, EinspeisungProduct,
127    ElectricityProduct, EmobilityProduct, EnergieQuellen, GasProduct, HeatProduct, HemsProduct,
128    IndexedPriceConfig, Product, SeasonalPriceOverride, ServiceProduct, SharingProduct,
129    SolarProduct, WaterProduct,
130};
131
132// Concrete providers
133pub use providers::{
134    ControllableLoadProvider, DynamicElectricityProvider, EegProvider, EinspeisungProvider,
135    ElectricityProvider, EmobilityProvider, EnergyShareProvider, GasProvider, HeatProvider,
136    HemsProvider, MwStProvider, ServiceProvider, SolarProvider, WaterProvider,
137};
138
139// The arithmetic core — `Amount<P>` fixed-point money, the canonical
140// `RoundingStrategy` (kaufmännisch by convention in this workspace), and the
141// error reachable through [`EngineError::Arithmetic`]. `round_money` /
142// `RoundMoney` delegate their mode to this crate; use `Amount` directly
143// where the precision is statutory (cents, 10⁻⁵-EUR unit prices).
144pub use billing::{Amount, BillingError, RoundingStrategy};
145
146/// A monetary amount in euro at 10⁻⁵-EUR resolution.
147///
148/// `billing` 0.12 dropped its own `EuroAmount` alias — the engine is
149/// currency-agnostic and the name asserted a currency the type does not carry.
150/// German retail energy billing *is* euro-denominated, so the alias is correct
151/// here; it just belongs to the domain crate rather than the engine.
152pub type EuroAmount = Amount<5>;