bo4e_core/com/
discount.rs

1//! Discount (Rabatt) component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::{Currency, SurchargeType};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// A discount applied to a price.
9///
10/// German: Rabatt
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::Discount;
16///
17/// let discount = Discount {
18///     description: Some("Stammkundenrabatt".to_string()),
19///     value: Some(10.0),
20///     ..Default::default()
21/// };
22/// ```
23#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub struct Discount {
26    /// BO4E metadata
27    #[serde(flatten)]
28    pub meta: Bo4eMeta,
29
30    /// Description/name of the discount (Bezeichnung)
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub description: Option<String>,
33
34    /// Type of discount (Rabatttyp)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub discount_type: Option<SurchargeType>,
37
38    /// Discount value (Wert)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub value: Option<f64>,
41
42    /// Currency (Waehrung)
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub currency: Option<Currency>,
45
46    /// Conditions for the discount (Bedingungen)
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub conditions: Option<String>,
49}
50
51impl Bo4eObject for Discount {
52    fn type_name_german() -> &'static str {
53        "Rabatt"
54    }
55
56    fn type_name_english() -> &'static str {
57        "Discount"
58    }
59
60    fn meta(&self) -> &Bo4eMeta {
61        &self.meta
62    }
63
64    fn meta_mut(&mut self) -> &mut Bo4eMeta {
65        &mut self.meta
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_percentage_discount() {
75        let discount = Discount {
76            description: Some("10% Rabatt".to_string()),
77            discount_type: Some(SurchargeType::Relative),
78            value: Some(10.0),
79            ..Default::default()
80        };
81
82        assert_eq!(discount.discount_type, Some(SurchargeType::Relative));
83        assert_eq!(discount.value, Some(10.0));
84    }
85
86    #[test]
87    fn test_absolute_discount() {
88        let discount = Discount {
89            description: Some("50 EUR Rabatt".to_string()),
90            discount_type: Some(SurchargeType::Absolute),
91            value: Some(50.0),
92            currency: Some(Currency::Eur),
93            ..Default::default()
94        };
95
96        assert_eq!(discount.discount_type, Some(SurchargeType::Absolute));
97    }
98
99    #[test]
100    fn test_default() {
101        let discount = Discount::default();
102        assert!(discount.description.is_none());
103        assert!(discount.value.is_none());
104    }
105
106    #[test]
107    fn test_roundtrip() {
108        let discount = Discount {
109            description: Some("Frühbucherrabatt".to_string()),
110            discount_type: Some(SurchargeType::Relative),
111            value: Some(15.0),
112            conditions: Some("Bei Buchung bis 31.12.".to_string()),
113            ..Default::default()
114        };
115
116        let json = serde_json::to_string(&discount).unwrap();
117        let parsed: Discount = serde_json::from_str(&json).unwrap();
118        assert_eq!(discount, parsed);
119    }
120
121    #[test]
122    fn test_bo4e_object_impl() {
123        assert_eq!(Discount::type_name_german(), "Rabatt");
124        assert_eq!(Discount::type_name_english(), "Discount");
125    }
126}