Skip to main content

bo4e_core/com/
position_surcharge.rs

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