use rust_decimal::Decimal;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NetworkLosses {
#[cfg_attr(feature = "serde", serde(with = "crate::wire::decimal"))]
pub einspeisung_kwh: Decimal,
#[cfg_attr(feature = "serde", serde(with = "crate::wire::decimal"))]
pub entnahme_kwh: Decimal,
#[cfg_attr(feature = "serde", serde(with = "crate::wire::decimal"))]
pub verlust_kwh: Decimal,
#[cfg_attr(feature = "serde", serde(with = "crate::wire::decimal_option"))]
pub verlust_prozent: Option<Decimal>,
}
#[must_use]
pub fn network_losses(einspeisung_kwh: Decimal, entnahme_kwh: Decimal) -> NetworkLosses {
let verlust_kwh = einspeisung_kwh - entnahme_kwh;
let verlust_prozent = (einspeisung_kwh > Decimal::ZERO).then(|| {
(verlust_kwh / einspeisung_kwh * Decimal::ONE_HUNDRED)
.round_dp_with_strategy(2, rust_decimal::RoundingStrategy::MidpointAwayFromZero)
});
NetworkLosses {
einspeisung_kwh,
entnahme_kwh,
verlust_kwh,
verlust_prozent,
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal::dec;
#[test]
fn a_typical_distribution_grid_shows_single_digit_losses() {
let l = network_losses(dec!(1_000_000), dec!(955_000));
assert_eq!(l.verlust_kwh, dec!(45_000));
assert_eq!(l.verlust_prozent, Some(dec!(4.50)));
}
#[test]
fn negative_balance_signals_a_metering_coverage_gap() {
let l = network_losses(dec!(100), dec!(120));
assert_eq!(l.verlust_kwh, dec!(-20));
assert_eq!(l.verlust_prozent, Some(dec!(-20.00)));
}
#[test]
fn zero_infeed_yields_no_percentage() {
let l = network_losses(Decimal::ZERO, dec!(10));
assert_eq!(l.verlust_prozent, None);
}
}