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#[serde(rename_all = "camelCase")]
26pub struct Amount {
27    /// BO4E metadata
28    #[serde(flatten)]
29    pub meta: Bo4eMeta,
30
31    /// The amount value (Wert)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub value: Option<f64>,
34
35    /// Currency (Waehrung)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub currency: Option<Currency>,
38}
39
40impl Bo4eObject for Amount {
41    fn type_name_german() -> &'static str {
42        "Betrag"
43    }
44
45    fn type_name_english() -> &'static str {
46        "Amount"
47    }
48
49    fn meta(&self) -> &Bo4eMeta {
50        &self.meta
51    }
52
53    fn meta_mut(&mut self) -> &mut Bo4eMeta {
54        &mut self.meta
55    }
56}
57
58impl Amount {
59    /// Create an amount in EUR.
60    pub fn eur(value: f64) -> Self {
61        Self {
62            value: Some(value),
63            currency: Some(Currency::Eur),
64            ..Default::default()
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_eur_amount() {
75        let amount = Amount::eur(100.50);
76        assert_eq!(amount.value, Some(100.50));
77        assert_eq!(amount.currency, Some(Currency::Eur));
78    }
79
80    #[test]
81    fn test_default() {
82        let amount = Amount::default();
83        assert!(amount.value.is_none());
84        assert!(amount.currency.is_none());
85    }
86
87    #[test]
88    fn test_serialize() {
89        let amount = Amount::eur(250.75);
90        let json = serde_json::to_string(&amount).unwrap();
91        assert!(json.contains(r#""value":250.75"#));
92        assert!(json.contains(r#""currency":"EUR""#));
93    }
94
95    #[test]
96    fn test_roundtrip() {
97        let amount = Amount::eur(999.99);
98        let json = serde_json::to_string(&amount).unwrap();
99        let parsed: Amount = serde_json::from_str(&json).unwrap();
100        assert_eq!(amount, parsed);
101    }
102
103    #[test]
104    fn test_bo4e_object_impl() {
105        assert_eq!(Amount::type_name_german(), "Betrag");
106        assert_eq!(Amount::type_name_english(), "Amount");
107    }
108}