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#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
29#[cfg_attr(feature = "json-schema", schemars(rename = "Steuerbetrag"))]
30#[serde(rename_all = "camelCase")]
31pub struct TaxAmount {
32 #[serde(flatten)]
34 pub meta: Bo4eMeta,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 #[cfg_attr(feature = "json-schema", schemars(rename = "steuerart"))]
39 pub tax_type: Option<TaxType>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 #[cfg_attr(feature = "json-schema", schemars(rename = "steuersatz"))]
44 pub tax_rate: Option<f64>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 #[cfg_attr(feature = "json-schema", schemars(rename = "basiswert"))]
49 pub basis_value: Option<f64>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 #[cfg_attr(feature = "json-schema", schemars(rename = "steuerwert"))]
54 pub tax_value: Option<f64>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 #[cfg_attr(feature = "json-schema", schemars(rename = "waehrungscode"))]
59 pub currency: Option<Currency>,
60}
61
62impl Bo4eObject for TaxAmount {
63 fn type_name_german() -> &'static str {
64 "Steuerbetrag"
65 }
66
67 fn type_name_english() -> &'static str {
68 "TaxAmount"
69 }
70
71 fn meta(&self) -> &Bo4eMeta {
72 &self.meta
73 }
74
75 fn meta_mut(&mut self) -> &mut Bo4eMeta {
76 &mut self.meta
77 }
78}
79
80impl TaxAmount {
81 pub fn vat_19(net_amount: f64) -> Self {
83 Self {
84 tax_type: Some(TaxType::ValueAddedTax),
85 tax_rate: Some(19.0),
86 basis_value: Some(net_amount),
87 tax_value: Some(net_amount * 0.19),
88 currency: Some(Currency::Eur),
89 ..Default::default()
90 }
91 }
92
93 pub fn vat_7(net_amount: f64) -> Self {
95 Self {
96 tax_type: Some(TaxType::ValueAddedTax),
97 tax_rate: Some(7.0),
98 basis_value: Some(net_amount),
99 tax_value: Some(net_amount * 0.07),
100 currency: Some(Currency::Eur),
101 ..Default::default()
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_vat_19() {
112 let tax = TaxAmount::vat_19(100.0);
113 assert_eq!(tax.tax_type, Some(TaxType::ValueAddedTax));
114 assert_eq!(tax.tax_rate, Some(19.0));
115 assert_eq!(tax.basis_value, Some(100.0));
116 assert_eq!(tax.tax_value, Some(19.0));
117 }
118
119 #[test]
120 fn test_vat_7() {
121 let tax = TaxAmount::vat_7(100.0);
122 assert_eq!(tax.tax_type, Some(TaxType::ValueAddedTax));
123 assert_eq!(tax.tax_rate, Some(7.0));
124 assert_eq!(tax.basis_value, Some(100.0));
125 assert!((tax.tax_value.unwrap() - 7.0).abs() < 0.0001);
127 }
128
129 #[test]
130 fn test_default() {
131 let tax = TaxAmount::default();
132 assert!(tax.tax_type.is_none());
133 assert!(tax.tax_rate.is_none());
134 assert!(tax.basis_value.is_none());
135 assert!(tax.tax_value.is_none());
136 }
137
138 #[test]
139 fn test_serialize() {
140 let tax = TaxAmount::vat_19(250.0);
141 let json = serde_json::to_string(&tax).unwrap();
142 assert!(json.contains(r#""taxRate":19"#));
143 assert!(json.contains(r#""basisValue":250"#));
144 }
145
146 #[test]
147 fn test_roundtrip() {
148 let tax = TaxAmount {
149 tax_type: Some(TaxType::ValueAddedTax),
150 tax_rate: Some(19.0),
151 basis_value: Some(123.45),
152 tax_value: Some(23.4555),
153 currency: Some(Currency::Eur),
154 ..Default::default()
155 };
156
157 let json = serde_json::to_string(&tax).unwrap();
158 let parsed: TaxAmount = serde_json::from_str(&json).unwrap();
159 assert_eq!(tax, parsed);
160 }
161
162 #[test]
163 fn test_bo4e_object_impl() {
164 assert_eq!(TaxAmount::type_name_german(), "Steuerbetrag");
165 assert_eq!(TaxAmount::type_name_english(), "TaxAmount");
166 }
167}