bo4e_core/com/
tax_amount.rs1use serde::{Deserialize, Serialize};
4
5use crate::enums::{Currency, TaxType};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct TaxAmount {
30 #[serde(flatten)]
32 pub meta: Bo4eMeta,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub tax_type: Option<TaxType>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub tax_rate: Option<f64>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub basis_value: Option<f64>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub tax_value: Option<f64>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub currency: Option<Currency>,
53}
54
55impl Bo4eObject for TaxAmount {
56 fn type_name_german() -> &'static str {
57 "Steuerbetrag"
58 }
59
60 fn type_name_english() -> &'static str {
61 "TaxAmount"
62 }
63
64 fn meta(&self) -> &Bo4eMeta {
65 &self.meta
66 }
67
68 fn meta_mut(&mut self) -> &mut Bo4eMeta {
69 &mut self.meta
70 }
71}
72
73impl TaxAmount {
74 pub fn vat_19(net_amount: f64) -> Self {
76 Self {
77 tax_type: Some(TaxType::ValueAddedTax),
78 tax_rate: Some(19.0),
79 basis_value: Some(net_amount),
80 tax_value: Some(net_amount * 0.19),
81 currency: Some(Currency::Eur),
82 ..Default::default()
83 }
84 }
85
86 pub fn vat_7(net_amount: f64) -> Self {
88 Self {
89 tax_type: Some(TaxType::ValueAddedTax),
90 tax_rate: Some(7.0),
91 basis_value: Some(net_amount),
92 tax_value: Some(net_amount * 0.07),
93 currency: Some(Currency::Eur),
94 ..Default::default()
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_vat_19() {
105 let tax = TaxAmount::vat_19(100.0);
106 assert_eq!(tax.tax_type, Some(TaxType::ValueAddedTax));
107 assert_eq!(tax.tax_rate, Some(19.0));
108 assert_eq!(tax.basis_value, Some(100.0));
109 assert_eq!(tax.tax_value, Some(19.0));
110 }
111
112 #[test]
113 fn test_vat_7() {
114 let tax = TaxAmount::vat_7(100.0);
115 assert_eq!(tax.tax_type, Some(TaxType::ValueAddedTax));
116 assert_eq!(tax.tax_rate, Some(7.0));
117 assert_eq!(tax.basis_value, Some(100.0));
118 assert!((tax.tax_value.unwrap() - 7.0).abs() < 0.0001);
120 }
121
122 #[test]
123 fn test_default() {
124 let tax = TaxAmount::default();
125 assert!(tax.tax_type.is_none());
126 assert!(tax.tax_rate.is_none());
127 assert!(tax.basis_value.is_none());
128 assert!(tax.tax_value.is_none());
129 }
130
131 #[test]
132 fn test_serialize() {
133 let tax = TaxAmount::vat_19(250.0);
134 let json = serde_json::to_string(&tax).unwrap();
135 assert!(json.contains(r#""taxRate":19"#));
136 assert!(json.contains(r#""basisValue":250"#));
137 }
138
139 #[test]
140 fn test_roundtrip() {
141 let tax = TaxAmount {
142 tax_type: Some(TaxType::ValueAddedTax),
143 tax_rate: Some(19.0),
144 basis_value: Some(123.45),
145 tax_value: Some(23.4555),
146 currency: Some(Currency::Eur),
147 ..Default::default()
148 };
149
150 let json = serde_json::to_string(&tax).unwrap();
151 let parsed: TaxAmount = serde_json::from_str(&json).unwrap();
152 assert_eq!(tax, parsed);
153 }
154
155 #[test]
156 fn test_bo4e_object_impl() {
157 assert_eq!(TaxAmount::type_name_german(), "Steuerbetrag");
158 assert_eq!(TaxAmount::type_name_english(), "TaxAmount");
159 }
160}