Skip to main content

bo4e_core/com/
amount.rs

1//! Amount (Betrag) component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::Currency;
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// A monetary amount with currency.
9///
10/// German: Betrag
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::Amount;
16/// use bo4e_core::enums::Currency;
17///
18/// let amount = Amount {
19///     value: Some(100.50),
20///     currency: Some(Currency::Eur),
21///     ..Default::default()
22/// };
23/// ```
24#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
25#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
26#[cfg_attr(feature = "json-schema", schemars(rename = "Betrag"))]
27#[serde(rename_all = "camelCase")]
28pub struct Amount {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// The amount value (Wert)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    #[cfg_attr(feature = "json-schema", schemars(rename = "wert"))]
36    pub value: Option<f64>,
37
38    /// Currency (Waehrung)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    #[cfg_attr(feature = "json-schema", schemars(rename = "waehrung"))]
41    pub currency: Option<Currency>,
42}
43
44impl Bo4eObject for Amount {
45    fn type_name_german() -> &'static str {
46        "Betrag"
47    }
48
49    fn type_name_english() -> &'static str {
50        "Amount"
51    }
52
53    fn meta(&self) -> &Bo4eMeta {
54        &self.meta
55    }
56
57    fn meta_mut(&mut self) -> &mut Bo4eMeta {
58        &mut self.meta
59    }
60}
61
62impl Amount {
63    /// Create an amount in EUR.
64    pub fn eur(value: f64) -> Self {
65        Self {
66            value: Some(value),
67            currency: Some(Currency::Eur),
68            ..Default::default()
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_eur_amount() {
79        let amount = Amount::eur(100.50);
80        assert_eq!(amount.value, Some(100.50));
81        assert_eq!(amount.currency, Some(Currency::Eur));
82    }
83
84    #[test]
85    fn test_default() {
86        let amount = Amount::default();
87        assert!(amount.value.is_none());
88        assert!(amount.currency.is_none());
89    }
90
91    #[test]
92    fn test_serialize() {
93        let amount = Amount::eur(250.75);
94        let json = serde_json::to_string(&amount).unwrap();
95        assert!(json.contains(r#""value":250.75"#));
96        assert!(json.contains(r#""currency":"EUR""#));
97    }
98
99    #[test]
100    fn test_roundtrip() {
101        let amount = Amount::eur(999.99);
102        let json = serde_json::to_string(&amount).unwrap();
103        let parsed: Amount = serde_json::from_str(&json).unwrap();
104        assert_eq!(amount, parsed);
105    }
106
107    #[test]
108    fn test_bo4e_object_impl() {
109        assert_eq!(Amount::type_name_german(), "Betrag");
110        assert_eq!(Amount::type_name_english(), "Amount");
111    }
112}