1use super::Error;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_repr::{Deserialize_repr, Serialize_repr};
6use std::str::FromStr;
7
8#[derive(Debug, PartialEq, Clone)]
10pub struct Produto {
11 pub codigo: String,
13 pub gtin: Option<String>,
15 pub descricao: String,
17 pub ncm: String,
19 pub fabricante_cnpj: Option<String>,
21 pub tributacao: ProdutoTributacao,
23 pub unidade: String,
25 pub quantidade: f32,
27 pub valor_unitario: f32,
29 pub valor_bruto: f32,
31 pub valor_frete: Option<f32>,
33 pub valor_seguro: Option<f32>,
35 pub valor_desconto: Option<f32>,
37 pub valor_outros: Option<f32>,
39 pub valor_compoe_total_nota: bool,
41}
42
43#[derive(Debug, PartialEq, Clone)]
45pub struct ProdutoTributacao {
46 pub cest: Option<String>,
48 pub escala_relevante: Option<EscalaRelevante>,
50 pub codigo_beneficio_fiscal: Option<String>,
52 pub codigo_excecao_ipi: Option<String>,
54 pub cfop: String,
56 pub gtin: Option<String>,
58 pub unidade: String,
60 pub quantidade: f32,
62 pub valor_unitario: f32,
64}
65
66#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize_repr, Serialize_repr)]
68#[repr(u8)]
69pub enum EscalaRelevante {
70 Sim = 1,
71 Nao = 2,
72}
73
74impl FromStr for Produto {
75 type Err = Error;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 quick_xml::de::from_str(s).map_err(|e| e.into())
79 }
80}
81
82impl ToString for Produto {
83 fn to_string(&self) -> String {
84 quick_xml::se::to_string(self).expect("Falha ao serializar o produto")
85 }
86}
87
88impl<'de> Deserialize<'de> for Produto {
89 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90 where
91 D: Deserializer<'de>,
92 {
93 let prod = ProdContainer::deserialize(deserializer)?;
95
96 Ok(Self {
97 codigo: prod.codigo,
98 gtin: match prod.gtin.to_lowercase().trim() {
99 "sem gtin" => None,
100 "" => None,
101 _ => Some(prod.gtin),
102 },
103 descricao: prod.descricao,
104 ncm: prod.ncm,
105 fabricante_cnpj: prod.fabricante_cnpj,
106 unidade: prod.unidade,
107 quantidade: prod.quantidade,
108 valor_unitario: prod.valor_unitario,
109 valor_bruto: prod.valor_bruto,
110 valor_frete: prod.valor_frete,
111 valor_seguro: prod.valor_seguro,
112 valor_desconto: prod.valor_desconto,
113 valor_outros: prod.valor_outros,
114 valor_compoe_total_nota: prod.valor_compoe_total_nota == 1,
115 tributacao: ProdutoTributacao {
116 cest: prod.t_cest,
117 escala_relevante: prod.t_escala_relevante,
118 codigo_beneficio_fiscal: prod.t_codigo_beneficio_fiscal,
119 codigo_excecao_ipi: prod.t_codigo_excecao_ipi,
120 cfop: prod.t_cfop,
121 gtin: match prod.t_gtin.to_lowercase().trim() {
122 "sem gtin" => None,
123 "" => None,
124 _ => Some(prod.t_gtin),
125 },
126 unidade: prod.t_unidade,
127 quantidade: prod.t_quantidade,
128 valor_unitario: prod.t_valor_unitario,
129 },
130 })
131 }
132}
133
134impl Serialize for Produto {
135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136 where
137 S: Serializer,
138 {
139 let prod = ProdContainer {
140 codigo: self.codigo.clone(),
141 gtin: match &self.gtin {
142 Some(gt) => gt.clone(),
143 None => "SEM GTIN".to_string(),
144 },
145 descricao: self.descricao.clone(),
146 ncm: self.ncm.clone(),
147 fabricante_cnpj: self.fabricante_cnpj.clone(),
148 unidade: self.unidade.clone(),
149 quantidade: self.quantidade.clone(),
150 valor_unitario: self.valor_unitario.clone(),
151 valor_bruto: self.valor_bruto,
152 valor_frete: self.valor_frete,
153 valor_seguro: self.valor_seguro,
154 valor_desconto: self.valor_desconto,
155 valor_outros: self.valor_outros,
156 valor_compoe_total_nota: if self.valor_compoe_total_nota { 1 } else { 0 },
157 t_cest: self.tributacao.cest.clone(),
158 t_escala_relevante: self.tributacao.escala_relevante,
159 t_codigo_beneficio_fiscal: self.tributacao.codigo_beneficio_fiscal.clone(),
160 t_codigo_excecao_ipi: self.tributacao.codigo_excecao_ipi.clone(),
161 t_cfop: self.tributacao.cfop.clone(),
162 t_gtin: match &self.tributacao.gtin {
163 Some(gt) => gt.clone(),
164 None => "SEM GTIN".to_string(),
165 },
166 t_unidade: self.tributacao.unidade.clone(),
167 t_quantidade: self.tributacao.quantidade,
168 t_valor_unitario: self.tributacao.valor_unitario,
169 };
170
171 prod.serialize(serializer)
172 }
173}
174
175#[derive(Deserialize, Serialize)]
176#[serde(rename = "prod")]
177struct ProdContainer {
178 #[serde(rename = "$unflatten=cProd")]
179 pub codigo: String,
180 #[serde(rename = "$unflatten=cEAN")]
181 pub gtin: String,
182 #[serde(rename = "$unflatten=xProd")]
183 pub descricao: String,
184 #[serde(rename = "$unflatten=NCM")]
185 pub ncm: String,
186 #[serde(rename = "$unflatten=CNPJFab")]
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub fabricante_cnpj: Option<String>,
189 #[serde(rename = "$unflatten=uCom")]
190 pub unidade: String,
191 #[serde(rename = "$unflatten=qCom")]
192 pub quantidade: f32,
193 #[serde(rename = "$unflatten=vUnCom")]
194 pub valor_unitario: f32,
195 #[serde(rename = "$unflatten=vProd")]
196 pub valor_bruto: f32,
197 #[serde(rename = "$unflatten=vFrete")]
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub valor_frete: Option<f32>,
200 #[serde(rename = "$unflatten=vDesc")]
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub valor_seguro: Option<f32>,
203 #[serde(rename = "$unflatten=vSeg")]
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub valor_desconto: Option<f32>,
206 #[serde(rename = "$unflatten=vOutro")]
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub valor_outros: Option<f32>,
209 #[serde(rename = "$unflatten=indTot")]
210 pub valor_compoe_total_nota: u8,
211
212 #[serde(rename = "$unflatten=CEST")]
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub t_cest: Option<String>,
215 #[serde(rename = "$unflatten=indEscala")]
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub t_escala_relevante: Option<EscalaRelevante>,
218 #[serde(rename = "$unflatten=cBenef")]
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub t_codigo_beneficio_fiscal: Option<String>,
221 #[serde(rename = "$unflatten=EXTIPI")]
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub t_codigo_excecao_ipi: Option<String>,
224 #[serde(rename = "$unflatten=CFOP")]
225 pub t_cfop: String,
226 #[serde(rename = "$unflatten=cEANTrib")]
227 pub t_gtin: String,
228 #[serde(rename = "$unflatten=uTrib")]
229 pub t_unidade: String,
230 #[serde(rename = "$unflatten=qTrib")]
231 pub t_quantidade: f32,
232 #[serde(rename = "$unflatten=vUnTrib")]
233 pub t_valor_unitario: f32,
234}