diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 60c1a94..5179c21 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -141,10 +141,10 @@ impl TariffArgs { None }; - let pricer = if let Some(tariff) = tariff.clone() { - Pricer::with_tariffs(&cdr, &[tariff], self.timezone) - } else { - Pricer::new(&cdr, self.timezone) + let mut pricer = Pricer::new(&cdr); + + if let Some(tariff) = &tariff { + pricer = pricer.with_tariffs([tariff]); }; let report = pricer.build_report().map_err(Error::Internal)?; diff --git a/ocpi-tariffs/src/lib.rs b/ocpi-tariffs/src/lib.rs index 1872084..8334bd4 100644 --- a/ocpi-tariffs/src/lib.rs +++ b/ocpi-tariffs/src/lib.rs @@ -30,9 +30,20 @@ pub enum Error { /// /// A valid tariff must have a start date time before the start of the session and a end date /// time after the start of the session. + /// + /// If the session does not contain any tariffs consider providing a list of tariffs using + /// [`pricer::Pricer::with_tariffs`]. NoValidTariff, /// A numeric overflow occurred during tariff calculation. NumericOverflow, + /// The CDR location did not contain a time-zone. If time zone detection was enabled and this + /// error still occurs it means that the country specified in the CDR has multiple time-zones. + /// Consider explicitly using a time-zone using [`pricer::Pricer::with_time_zone`]. + TimeZoneMissing, + /// The CDR location did not contain a valid time-zone. Consider enabling time-zone detection + /// as a fall back using [`pricer::Pricer::detect_time_zone`] or explicitly providing a time + /// zone using [`pricer::Pricer::with_time_zone`]. + TimeZoneInvalid, } impl From for Error { @@ -48,6 +59,8 @@ impl fmt::Display for Error { let display = match self { Self::NoValidTariff => "No valid tariff has been found in the list of provided tariffs", Self::NumericOverflow => "A numeric overflow occurred during tariff calculation", + Self::TimeZoneMissing => "No time zone could be found in the session information", + Self::TimeZoneInvalid => "The time zone in the CDR is invalid", }; f.write_str(display) diff --git a/ocpi-tariffs/src/ocpi/v211/cdr.rs b/ocpi-tariffs/src/ocpi/v211/cdr.rs index 473bfd5..1bba451 100644 --- a/ocpi-tariffs/src/ocpi/v211/cdr.rs +++ b/ocpi-tariffs/src/ocpi/v211/cdr.rs @@ -27,6 +27,9 @@ pub struct Cdr { #[serde(deserialize_with = "null_default", default)] pub tariffs: Vec, + /// Describes the location that the charge-session took place at. + pub location: OcpiLocation, + /// List of charging periods that make up this charging session> A session should consist of 1 or /// more periods, where each period has a different relevant Tariff. pub charging_periods: Vec, @@ -47,6 +50,15 @@ pub struct Cdr { pub last_updated: DateTime, } +/// Describes the location that the charge-session took place at. +#[derive(Clone, Deserialize, Serialize)] +pub struct OcpiLocation { + /// ISO 3166-1 alpha-3 code for the country of this location. + pub country: String, + /// One of IANA tzdata's TZ-values representing the time zone of the location. Examples: "Europe/Oslo", "Europe/Zurich" + pub time_zone: Option, +} + /// The volume that has been consumed for a specific dimension during a charging period. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type", content = "volume")] @@ -96,6 +108,7 @@ impl From for v221::cdr::Cdr { end_date_time: cdr.stop_date_time, start_date_time: cdr.start_date_time, last_updated: cdr.last_updated, + cdr_location: cdr.location.into(), charging_periods: cdr .charging_periods .into_iter() @@ -118,6 +131,15 @@ impl From for v221::cdr::Cdr { } } +impl From for v221::cdr::OcpiCdrLocation { + fn from(value: OcpiLocation) -> Self { + Self { + country: value.country, + time_zone: value.time_zone, + } + } +} + impl From for Option { fn from(dimension: OcpiCdrDimension) -> Self { use v221::cdr::OcpiCdrDimension as OcpiCdrDimension221; diff --git a/ocpi-tariffs/src/ocpi/v211/tariff.rs b/ocpi-tariffs/src/ocpi/v211/tariff.rs index 8cb8be8..47710f4 100644 --- a/ocpi-tariffs/src/ocpi/v211/tariff.rs +++ b/ocpi-tariffs/src/ocpi/v211/tariff.rs @@ -16,6 +16,9 @@ use crate::ocpi::v221; /// The Tariff object describes a tariff and its properties #[derive(Clone, Deserialize, Serialize)] pub struct OcpiTariff { + /// The OCPI id of this tariff. + pub id: String, + /// Currency of this tariff, ISO 4217 Code pub currency: String, @@ -112,6 +115,7 @@ pub struct OcpiTariffRestriction { impl From for v221::tariff::OcpiTariff { fn from(tariff: OcpiTariff) -> Self { Self { + id: tariff.id, currency: tariff.currency, min_price: None, max_price: None, diff --git a/ocpi-tariffs/src/ocpi/v221/cdr.rs b/ocpi-tariffs/src/ocpi/v221/cdr.rs index 4f3fe3e..8037139 100644 --- a/ocpi-tariffs/src/ocpi/v221/cdr.rs +++ b/ocpi-tariffs/src/ocpi/v221/cdr.rs @@ -26,6 +26,9 @@ pub struct Cdr { #[serde(deserialize_with = "null_default", default)] pub tariffs: Vec, + /// Describes the location that the charge-session took place at. + pub cdr_location: OcpiCdrLocation, + /// List of charging periods that make up this charging session> A session should consist of 1 or /// more periods, where each period has a different relevant Tariff. pub charging_periods: Vec, @@ -61,6 +64,22 @@ pub struct Cdr { pub last_updated: DateTime, } +/// Describes the location that the charge-session took place at. +#[derive(Clone, Deserialize, Serialize)] +pub struct OcpiCdrLocation { + /// ISO 3166-1 alpha-3 code for the country of this location. + pub country: String, + /// Optional time-zone information. + /// + /// NOTE: according to OCPI 2.2.1 the CDR location does not contain this field. It is added + /// here to allow to conversion from OCPI 2.1.1 locations without losing time-zone information. + /// + /// It will not be included when serializing the location in order to stay compliant to OCPI + /// 2.2.1. + #[serde(skip_serializing)] + pub time_zone: Option, +} + /// The volume that has been consumed for a specific dimension during a charging period. #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type", content = "volume")] diff --git a/ocpi-tariffs/src/ocpi/v221/tariff.rs b/ocpi-tariffs/src/ocpi/v221/tariff.rs index 6ec3641..b91c13c 100644 --- a/ocpi-tariffs/src/ocpi/v221/tariff.rs +++ b/ocpi-tariffs/src/ocpi/v221/tariff.rs @@ -13,6 +13,9 @@ use crate::null_default; /// The Tariff object describes a tariff and its properties #[derive(Clone, Deserialize, Serialize)] pub struct OcpiTariff { + /// The OCPI id of this tariff. + pub id: String, + /// Currency of this tariff, ISO 4217 Code pub currency: String, diff --git a/ocpi-tariffs/src/pricer.rs b/ocpi-tariffs/src/pricer.rs index 7021e0a..33df279 100644 --- a/ocpi-tariffs/src/pricer.rs +++ b/ocpi-tariffs/src/pricer.rs @@ -4,12 +4,12 @@ use crate::{ tariff::{CompatibilityVat, OcpiTariff}, }, session::{ChargePeriod, ChargeSession, PeriodData}, - tariff::{PriceComponent, PriceComponents, Tariffs}, + tariff::{PriceComponent, PriceComponents, Tariff}, types::{ electricity::Kwh, money::{Money, Price}, number::Number, - time::HoursDecimal, + time::{try_detect_time_zone, DateTime as OcpiDateTime, HoursDecimal}, }, Error, Result, }; @@ -24,46 +24,89 @@ use serde::Serialize; /// /// Either specify a `Cdr` containing a list of tariffs. /// ```ignore -/// let pricer = Pricer::new(cdr, Tz::Europe__Amsterdam); -/// let report = pricer.build_report(); +/// let report = Pricer::new(cdr) +/// .with_time_zone(Tz::Europe__Amsterdam) +/// .build_report() +/// .unwrap(); /// ``` /// /// Or provide both the `Cdr` and a slice of `OcpiTariff`'s. /// ```ignore -/// let pricer = Pricer::with_tariffs(cdr, tariffs, Tz::Europe__Amsterdam); -/// let report = pricer.build_report(); +/// let pricer = Pricer::new(cdr) +/// .with_tariffs(tariffs) +/// .detect_time_zone(true) +/// .build_report() +/// .unwrap(); /// ``` -pub struct Pricer { - session: ChargeSession, - tariffs: Tariffs, +pub struct Pricer<'a> { + cdr: &'a Cdr, + tariffs: Option>, + time_zone: Option, + detect_time_zone: bool, } -impl Pricer { - /// Instantiate the pricer with a `Cdr` that contains at least on tariff. - /// Provide the `local_timezone` of the area where this charge session was priced. - pub fn new(cdr: &Cdr, local_timezone: Tz) -> Self { +impl<'a> Pricer<'a> { + /// Create a new pricer instance using the specified [`Cdr`]. + pub fn new(cdr: &'a Cdr) -> Self { Self { - session: ChargeSession::new(cdr, local_timezone), - tariffs: Tariffs::new(&cdr.tariffs), + cdr, + time_zone: None, + detect_time_zone: false, + tariffs: None, } } - /// Instantiate the pricer with a `Cdr` and a slice that contains at least on tariff. - /// Provide the `local_timezone` of the area where this charge session was priced. - pub fn with_tariffs(cdr: &Cdr, tariffs: &[OcpiTariff], local_timezone: Tz) -> Self { - Self { - session: ChargeSession::new(cdr, local_timezone), - tariffs: Tariffs::new(tariffs), - } + /// Use a list of [`OcpiTariff`]'s for pricing instead of the tariffs found in the [`Cdr`]. + pub fn with_tariffs(mut self, tariffs: impl IntoIterator) -> Self { + self.tariffs = Some(tariffs.into_iter().collect()); + + self + } + + /// Directly specify a time zone to use for the calculation. This overrides any time zones in + /// the session or any detected time zones if [`Self::detect_time_zone`] is set to true. + pub fn with_time_zone(mut self, time_zone: Tz) -> Self { + self.time_zone = Some(time_zone); + + self } - /// Attempt to apply the first found valid tariff the charge session and build a report + /// Try to detect a time zone from the country code inside the [`Cdr`] if the actual time zone + /// is missing. The detection will only succeed if the country has just one time-zone, + /// nonetheless there are edge cases where the detection will be incorrect. Only use this + /// feature as a fallback when a certain degree of inaccuracy is allowed. + pub fn detect_time_zone(mut self, detect: bool) -> Self { + self.detect_time_zone = detect; + + self + } + + /// Attempt to apply the first applicable tariff to the charge session and build a report /// containing the results. - pub fn build_report(&self) -> Result { - let (tariff_index, tariff) = self - .tariffs - .active_tariff(self.session.start_date_time) - .ok_or(Error::NoValidTariff)?; + pub fn build_report(self) -> Result { + let cdr_tz = self.cdr.cdr_location.time_zone.as_ref(); + + let time_zone = if let Some(tz) = self.time_zone { + tz + } else if let Some(tz) = cdr_tz { + tz.parse().map_err(|_| Error::TimeZoneInvalid)? + } else if self.detect_time_zone { + try_detect_time_zone(&self.cdr.cdr_location.country).ok_or(Error::TimeZoneMissing)? + } else { + return Err(Error::TimeZoneMissing); + }; + + let cdr = ChargeSession::new(self.cdr, time_zone); + + let active = if let Some(tariffs) = self.tariffs { + Self::first_active_tariff(tariffs, cdr.start_date_time) + } else if !self.cdr.tariffs.is_empty() { + Self::first_active_tariff(&self.cdr.tariffs, cdr.start_date_time) + } else { + None + }; + + let (tariff_index, tariff) = active.ok_or(Error::NoValidTariff)?; let mut periods = Vec::new(); let mut step_size = StepSize::new(); @@ -72,7 +115,7 @@ impl Pricer { let mut total_charging_time = HoursDecimal::zero(); let mut total_parking_time = HoursDecimal::zero(); - for (index, period) in self.session.periods.iter().enumerate() { + for (index, period) in cdr.periods.iter().enumerate() { let components = tariff.active_components(period); step_size.update(index, &components, period); @@ -173,6 +216,8 @@ impl Pricer { let report = Report { periods, tariff_index, + tariff_id: tariff.id, + time_zone: time_zone.to_string(), total_cost, total_time_cost, total_charging_time, @@ -190,6 +235,16 @@ impl Pricer { Ok(report) } + + fn first_active_tariff<'b>( + iter: impl IntoIterator, + start_date_time: OcpiDateTime, + ) -> Option<(usize, Tariff)> { + iter.into_iter() + .map(Tariff::new) + .enumerate() + .find(|(_, t)| t.is_active(start_date_time)) + } } struct StepSize { @@ -334,6 +389,10 @@ pub struct Report { pub periods: Vec, /// Index of the tariff that was found to be active. pub tariff_index: usize, + /// Id of the tariff that was found to be active. + pub tariff_id: String, + /// Time zone that was either specified or detected. + pub time_zone: String, /// Total sum of all the costs of this transaction in the specified currency. pub total_cost: Option, /// Total sum of all the cost related to duration of charging during this transaction, in the specified currency. diff --git a/ocpi-tariffs/src/tariff.rs b/ocpi-tariffs/src/tariff.rs index a800575..f2ad40f 100644 --- a/ocpi-tariffs/src/tariff.rs +++ b/ocpi-tariffs/src/tariff.rs @@ -8,29 +8,15 @@ use crate::restriction::{collect_restrictions, Restriction}; use crate::session::ChargePeriod; use crate::types::{money::Money, time::DateTime}; -pub struct Tariffs(Vec); - -impl Tariffs { - pub fn new(tariffs: &[OcpiTariff]) -> Self { - Self(tariffs.iter().map(Tariff::new).collect()) - } - - pub fn active_tariff(&self, start_time: DateTime) -> Option<(usize, &Tariff)> { - self.0 - .iter() - .position(|t| t.is_active(start_time)) - .map(|i| (i, &self.0[i])) - } -} - pub struct Tariff { + pub id: String, elements: Vec, start_date_time: Option, end_date_time: Option, } impl Tariff { - fn new(tariff: &OcpiTariff) -> Self { + pub fn new(tariff: &OcpiTariff) -> Self { let elements = tariff .elements .iter() @@ -39,6 +25,7 @@ impl Tariff { .collect(); Self { + id: tariff.id.clone(), start_date_time: tariff.start_date_time, end_date_time: tariff.end_date_time, elements, diff --git a/ocpi-tariffs/src/types/time.rs b/ocpi-tariffs/src/types/time.rs index 6e685fd..30d7239 100644 --- a/ocpi-tariffs/src/types/time.rs +++ b/ocpi-tariffs/src/types/time.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use chrono::Duration; +use chrono_tz::Tz; use serde::{Deserialize, Serialize, Serializer}; use crate::Error; @@ -237,6 +238,66 @@ impl From for chrono::Weekday { } } +/// Mapping of European countries to time zones with geographical naming +/// +/// This is only possible for countries with a single time zone and only for countries as they +/// currently exist (2024). It's a best effort approach to determine a timezone from just a ALPHA-3 +/// ISO 3166-1 country code. +/// +/// In small edge cases (e.g. Gibraltar) this detection might generate the wrong time-zone. +pub(crate) fn try_detect_time_zone(code: &str) -> Option { + let tz = match code { + "AND" => Tz::Europe__Andorra, + "ALB" => Tz::Europe__Tirane, + "AUT" => Tz::Europe__Vienna, + "BIH" => Tz::Europe__Sarajevo, + "BEL" => Tz::Europe__Brussels, + "BGR" => Tz::Europe__Sofia, + "BLR" => Tz::Europe__Minsk, + "CHE" => Tz::Europe__Zurich, + "CYP" => Tz::Europe__Nicosia, + "CZE" => Tz::Europe__Prague, + "DEU" => Tz::Europe__Berlin, + "DNK" => Tz::Europe__Copenhagen, + "EST" => Tz::Europe__Tallinn, + "ESP" => Tz::Europe__Madrid, + "FIN" => Tz::Europe__Helsinki, + "FRA" => Tz::Europe__Paris, + "GBR" => Tz::Europe__London, + "GRC" => Tz::Europe__Athens, + "HRV" => Tz::Europe__Zagreb, + "HUN" => Tz::Europe__Budapest, + "IRN" => Tz::Europe__Dublin, + "ISL" => Tz::Iceland, + "ITA" => Tz::Europe__Rome, + "LIE" => Tz::Europe__Vaduz, + "LTU" => Tz::Europe__Vilnius, + "LUX" => Tz::Europe__Luxembourg, + "LVA" => Tz::Europe__Riga, + "MCO" => Tz::Europe__Monaco, + "MDA" => Tz::Europe__Chisinau, + "MNE" => Tz::Europe__Podgorica, + "MKD" => Tz::Europe__Skopje, + "MLT" => Tz::Europe__Malta, + "NLD" => Tz::Europe__Amsterdam, + "NOR" => Tz::Europe__Oslo, + "POL" => Tz::Europe__Warsaw, + "PRT" => Tz::Europe__Lisbon, + "ROU" => Tz::Europe__Bucharest, + "SRB" => Tz::Europe__Belgrade, + "RUS" => Tz::Europe__Moscow, + "SWE" => Tz::Europe__Stockholm, + "SVN" => Tz::Europe__Ljubljana, + "SVK" => Tz::Europe__Bratislava, + "SMR" => Tz::Europe__San_Marino, + "TUR" => Tz::Turkey, + "UKR" => Tz::Europe__Kiev, + _ => return None, + }; + + Some(tz) +} + #[cfg(test)] mod hour_decimal_tests { use chrono::Duration; diff --git a/ocpi-tariffs/test_data/025kwh_min_price/cdr1.json b/ocpi-tariffs/test_data/025kwh_min_price/cdr1.json index 8b3ff09..63cac61 100644 --- a/ocpi-tariffs/test_data/025kwh_min_price/cdr1.json +++ b/ocpi-tariffs/test_data/025kwh_min_price/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T15:30:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T14:30:00Z", diff --git a/ocpi-tariffs/test_data/025kwh_min_price/cdr2_less_than_2kWh.json b/ocpi-tariffs/test_data/025kwh_min_price/cdr2_less_than_2kWh.json index 5789ec7..c2143f4 100644 --- a/ocpi-tariffs/test_data/025kwh_min_price/cdr2_less_than_2kWh.json +++ b/ocpi-tariffs/test_data/025kwh_min_price/cdr2_less_than_2kWh.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T15:30:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T14:30:00Z", diff --git a/ocpi-tariffs/test_data/025kwh_start/cdr1.json b/ocpi-tariffs/test_data/025kwh_start/cdr1.json index 442e15b..8458d03 100644 --- a/ocpi-tariffs/test_data/025kwh_start/cdr1.json +++ b/ocpi-tariffs/test_data/025kwh_start/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T15:30:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T14:30:00Z", diff --git a/ocpi-tariffs/test_data/grace_period_parking_time/cdr.json b/ocpi-tariffs/test_data/grace_period_parking_time/cdr.json index 3f9ab7f..d4ce5bf 100644 --- a/ocpi-tariffs/test_data/grace_period_parking_time/cdr.json +++ b/ocpi-tariffs/test_data/grace_period_parking_time/cdr.json @@ -1,44 +1,47 @@ { - "country_code": "BE", - "start_date_time": "2015-06-29T15:00:00Z", - "end_date_time": "2015-06-29T16:15:00Z", - "currency": "EUR", - "tariffs": [], - "charging_periods": [{ + "country_code": "BE", "start_date_time": "2015-06-29T15:00:00Z", - "dimensions": [{ - "type": "PARKING_TIME", - "volume": 0.5 - }] - }, { + "end_date_time": "2015-06-29T16:15:00Z", + "currency": "EUR", + "tariffs": [], + "cdr_location": { + "country": "NLD" + }, + "charging_periods": [{ + "start_date_time": "2015-06-29T15:00:00Z", + "dimensions": [{ + "type": "PARKING_TIME", + "volume": 0.5 + }] + }, { "start_date_time": "2015-06-29T15:30:00Z", "dimensions": [{ - "type": "PARKING_TIME", - "volume": 0.25 + "type": "PARKING_TIME", + "volume": 0.25 }] - }, { + }, { "start_date_time": "2015-06-29T15:45:00Z", "dimensions": [{ - "type": "ENERGY", - "volume": 10 + "type": "ENERGY", + "volume": 10 }] - }], - "total_parking_cost": { - "excl_vat": 0.75, - "incl_vat": 0.75 - }, - "total_energy_cost": { - "excl_vat": 2.5, - "incl_vat": 2.75 - }, - "total_cost": { - "excl_vat": 3.25, - "incl_vat": 3.5 - }, - "total_energy": 10, - "total_time": 1.25, - "total_parking_time": 0.75, - "last_updated": "2015-06-29T22:01:13Z" + }], + "total_parking_cost": { + "excl_vat": 0.75, + "incl_vat": 0.75 + }, + "total_energy_cost": { + "excl_vat": 2.5, + "incl_vat": 2.75 + }, + "total_cost": { + "excl_vat": 3.25, + "incl_vat": 3.5 + }, + "total_energy": 10, + "total_time": 1.25, + "total_parking_time": 0.75, + "last_updated": "2015-06-29T22:01:13Z" } diff --git a/ocpi-tariffs/test_data/grace_period_parking_time/tariff.json b/ocpi-tariffs/test_data/grace_period_parking_time/tariff.json index 71a9a25..e728d24 100644 --- a/ocpi-tariffs/test_data/grace_period_parking_time/tariff.json +++ b/ocpi-tariffs/test_data/grace_period_parking_time/tariff.json @@ -1,44 +1,45 @@ { - "currency": "EUR", - "elements": [ - { - "price_components": [ + "id": "foo", + "currency": "EUR", + "elements": [ { - "type": "PARKING_TIME", - "price": 0.0, - "vat": 0.0, - "step_size": 1 - } - ], - "restrictions": { - "min_duration": 0, - "max_duration": 1800 - } - }, - { - "price_components": [ + "price_components": [ + { + "type": "PARKING_TIME", + "price": 0.0, + "vat": 0.0, + "step_size": 1 + } + ], + "restrictions": { + "min_duration": 0, + "max_duration": 1800 + } + }, { - "type": "PARKING_TIME", - "price": 3.0, - "vat": 0.0, - "step_size": 1 - } - ], - "restrictions": { - "min_duration": 1800 - } - }, - { - "price_components": [ + "price_components": [ + { + "type": "PARKING_TIME", + "price": 3.0, + "vat": 0.0, + "step_size": 1 + } + ], + "restrictions": { + "min_duration": 1800 + } + }, { - "type": "ENERGY", - "price": 0.25, - "vat": 10.0, - "step_size": 1 + "price_components": [ + { + "type": "ENERGY", + "price": 0.25, + "vat": 10.0, + "step_size": 1 + } + ] } - ] - } - ], - "end_date_time": "2019-06-30T23:59:59Z", - "last_updated": "2018-12-17T17:15:01Z" + ], + "end_date_time": "2019-06-30T23:59:59Z", + "last_updated": "2018-12-17T17:15:01Z" } diff --git a/ocpi-tariffs/test_data/simple_025kwh/cdr1.json b/ocpi-tariffs/test_data/simple_025kwh/cdr1.json index cfbe1b7..9e9faaf 100644 --- a/ocpi-tariffs/test_data/simple_025kwh/cdr1.json +++ b/ocpi-tariffs/test_data/simple_025kwh/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T15:30:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T14:30:00Z", diff --git a/ocpi-tariffs/test_data/simple_3hour_5parking/cdr1.json b/ocpi-tariffs/test_data/simple_3hour_5parking/cdr1.json index 29e96ca..d7b1998 100644 --- a/ocpi-tariffs/test_data/simple_3hour_5parking/cdr1.json +++ b/ocpi-tariffs/test_data/simple_3hour_5parking/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T19:12:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T16:00:00Z", diff --git a/ocpi-tariffs/test_data/step_size/cdr1.json b/ocpi-tariffs/test_data/step_size/cdr1.json index 2ebbb83..0c23972 100644 --- a/ocpi-tariffs/test_data/step_size/cdr1.json +++ b/ocpi-tariffs/test_data/step_size/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T17:07:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T16:55:00Z", diff --git a/ocpi-tariffs/test_data/step_size_energy/cdr1.json b/ocpi-tariffs/test_data/step_size_energy/cdr1.json index 03101c4..76d8280 100644 --- a/ocpi-tariffs/test_data/step_size_energy/cdr1.json +++ b/ocpi-tariffs/test_data/step_size_energy/cdr1.json @@ -3,6 +3,9 @@ "end_date_time": "2022-01-13T17:07:00Z", "currency": "EUR", "tariffs": [], + "cdr_location": { + "country": "NLD" + }, "charging_periods": [ { "start_date_time": "2022-01-13T16:55:00Z", diff --git a/ocpi-tariffs/tests/integration.rs b/ocpi-tariffs/tests/integration.rs index dbce2eb..56896e0 100644 --- a/ocpi-tariffs/tests/integration.rs +++ b/ocpi-tariffs/tests/integration.rs @@ -16,8 +16,10 @@ fn test_json(cdr: &str, path: PathBuf) { } pub fn validate_cdr(cdr: &Cdr, tariff: OcpiTariff) -> Result<(), ocpi_tariffs::Error> { - let pricer = Pricer::with_tariffs(cdr, &[tariff], Tz::UTC); - let report = pricer.build_report()?; + let report = Pricer::new(cdr) + .with_tariffs(&[tariff]) + .with_time_zone(Tz::UTC) + .build_report()?; assert_eq!( cdr.total_cost,