nfe_parser/base/mod.rs
1//! Base da NF-e - Estruturas fundamentais da Nota Fiscal Eletrônica
2//!
3//! Este módulo contém os tipos e estruturas base para tratamento da NF-e,
4//! independente do modelo fiscal (NF-e modelo 55 ou NFC-e modelo 65).
5//!
6//! ## Estrutura do XML da NF-e (Layout 4.00 - SEFAZ)
7//!
8//! A NF-e segue a estrutura definida no Manual de Orientação do Contribuinte (MOC):
9//!
10//! ```text
11//! <NFe>
12//! <infNFe versao="4.00" Id="NFe...">
13//! <ide> <!-- Identificação da NF-e -->
14//! <emit> <!-- Emitente -->
15//! <dest> <!-- Destinatário (opcional em alguns casos) -->
16//! <det> <!-- Detalhamento de produtos/serviços (1 a 990 itens) -->
17//! <total> <!-- Totais da NF-e -->
18//! <transp> <!-- Transporte -->
19//! <infAdic> <!-- Informações adicionais -->
20//! </infNFe>
21//! </NFe>
22//! ```
23//!
24//! ## Referências
25//!
26//! - [Manual de Orientação do Contribuinte v6.00](https://www.nfe.fazenda.gov.br/portal/listaConteudo.aspx?tipoConteudo=ndIjl+iEFdE%3D)
27//! - [Esquemas XML NF-e](https://www.nfe.fazenda.gov.br/portal/listaConteudo.aspx?tipoConteudo=BMPFMBoln3w%3D)
28
29use serde::{Deserialize, Deserializer, Serialize, Serializer};
30use std::convert::TryFrom;
31use std::fs::File;
32use std::io::Read;
33use std::str::FromStr;
34
35// Submódulos que compõem a estrutura da NF-e
36pub mod dest; // Destinatário (comprador/cliente)
37pub mod emit; // Emitente (vendedor/empresa)
38pub mod endereco; // Endereço (usado por emit e dest)
39mod error; // Tipos de erro da biblioteca
40pub mod ide; // Identificação da nota fiscal
41pub mod item; // Itens/produtos da nota
42pub mod municipios; // Tabela de municípios IBGE e configurações fiscais
43pub mod nfce; // NFC-e - Nota Fiscal de Consumidor Eletrônica
44pub mod nfse; // NFS-e - Nota Fiscal de Serviços Eletrônica
45pub mod totais; // Totalização de valores
46pub mod transporte; // Dados de transporte/frete
47
48use dest::Destinatario;
49use emit::Emitente;
50pub use error::Error;
51use ide::Identificacao;
52use item::Item;
53use totais::Totalizacao;
54use transporte::Transporte;
55
56/// Estrutura principal da Nota Fiscal Eletrônica (NF-e)
57///
58/// Esta estrutura representa uma NF-e parseada, contendo todos os grupos
59/// de informações definidos no layout 4.00 da SEFAZ.
60///
61/// A NF-e pode ser de dois modelos:
62/// - **Modelo 55 (NF-e)**: Nota Fiscal Eletrônica tradicional para operações B2B
63/// - **Modelo 65 (NFC-e)**: Nota Fiscal de Consumidor Eletrônica para varejo
64///
65/// ## Campos Principais
66///
67/// | Campo | Tag XML | Descrição |
68/// |-------|---------|-----------|
69/// | versao | @versao | Versão do layout (4.00) |
70/// | chave_acesso | @Id | Chave de 44 dígitos que identifica a nota |
71/// | ide | \<ide\> | Dados de identificação |
72/// | emit | \<emit\> | Dados do emitente |
73/// | dest | \<dest\> | Dados do destinatário |
74/// | itens | \<det\> | Lista de produtos (1 a 990) |
75/// | totais | \<total\> | Valores totalizados |
76/// | transporte | \<transp\> | Informações de frete |
77///
78/// ## Exemplo de Uso
79///
80/// ```rust,ignore
81/// use std::fs::File;
82/// use nfe::Nfe;
83///
84/// let file = File::open("nota.xml")?;
85/// let nfe = Nfe::try_from(file)?;
86///
87/// println!("Chave: {}", nfe.chave_acesso);
88/// println!("Total: R$ {:.2}", nfe.totais.valor_total);
89/// ```
90#[derive(Debug, PartialEq)]
91pub struct Nfe {
92 /// Versão do layout XML da NF-e (atualmente 4.00)
93 pub versao: VersaoLayout,
94
95 /// Chave de acesso de 44 dígitos que identifica unicamente a NF-e
96 /// Formato: UF(2) + AAMM(4) + CNPJ(14) + MOD(2) + SERIE(3) + NNF(9) + CODIGO(9) + DV(1)
97 pub chave_acesso: String,
98
99 /// Grupo de identificação da NF-e (tag <ide>)
100 /// Contém: UF, número, série, modelo, datas, tipo de emissão, etc.
101 pub ide: Identificacao,
102
103 /// Dados do emitente da nota fiscal (tag <emit>)
104 /// Contém: CNPJ, razão social, endereço, IE, etc.
105 pub emit: Emitente,
106
107 /// Dados do destinatário/comprador (tag <dest>)
108 /// Opcional em algumas operações (ex: NFC-e para consumidor não identificado)
109 pub dest: Option<Destinatario>,
110
111 /// Lista de itens/produtos da nota fiscal (tags <det>)
112 /// Cada NF-e pode conter de 1 a 990 itens
113 pub itens: Vec<Item>,
114
115 /// Totalização de valores da nota fiscal (tag <total>)
116 /// Contém: BC ICMS, valor ICMS, valor produtos, frete, desconto, total, etc.
117 pub totais: Totalizacao,
118
119 /// Informações de transporte/frete (tag <transp>)
120 /// Contém: modalidade do frete (CIF/FOB), transportador, volumes, etc.
121 pub transporte: Transporte,
122
123 /// Informações complementares de interesse do contribuinte (tag <infCpl>)
124 /// Campo de texto livre para observações adicionais
125 pub informacao_complementar: Option<String>,
126}
127
128/// Versão do layout XML da NF-e conforme definido pela SEFAZ
129///
130/// A versão do layout determina a estrutura do XML e as regras de validação.
131/// Desde 2019, a versão 4.00 é obrigatória em todo o Brasil.
132///
133/// ## Histórico de Versões
134///
135/// | Versão | Vigência | Observação |
136/// |--------|----------|------------|
137/// | 1.00 | 2005-2006 | Versão inicial |
138/// | 2.00 | 2006-2010 | Expansão nacional |
139/// | 3.00 | 2010-2017 | Eventos, cancelamento |
140/// | 3.10 | 2017-2019 | Ajustes menores |
141/// | 4.00 | 2019-atual | Versão atual obrigatória |
142///
143/// ## Referência SEFAZ
144///
145/// Tag XML: `@versao` no elemento `<infNFe>`
146#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize)]
147pub enum VersaoLayout {
148 /// Layout 4.00 - Versão atual e obrigatória desde 2019
149 #[serde(rename = "4.00")]
150 V4_00 = 4,
151}
152
153impl FromStr for Nfe {
154 type Err = Error;
155
156 fn from_str(s: &str) -> Result<Self, Self::Err> {
157 quick_xml::de::from_str(s).map_err(|e| e.into())
158 }
159}
160
161impl TryFrom<File> for Nfe {
162 type Error = Error;
163
164 fn try_from(mut f: File) -> Result<Self, Self::Error> {
165 let mut xml = String::new();
166 f.read_to_string(&mut xml).map_err(|e| Error::Io(e))?;
167
168 xml.parse::<Nfe>()
169 }
170}
171
172impl ToString for Nfe {
173 fn to_string(&self) -> String {
174 quick_xml::se::to_string(self).expect("Falha ao serializar a nota")
175 }
176}
177
178impl<'de> Deserialize<'de> for Nfe {
179 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
180 where
181 D: Deserializer<'de>,
182 {
183 let nfe = NfeRootContainer::deserialize(deserializer)?;
184
185 Ok(Self {
186 versao: nfe.inf.versao,
187 chave_acesso: nfe.inf.chave_acesso.replace("NFe", ""),
188 ide: nfe.inf.ide,
189 emit: nfe.inf.emit,
190 dest: nfe.inf.dest,
191 itens: nfe.inf.itens,
192 totais: nfe.inf.totais,
193 transporte: nfe.inf.transporte,
194 informacao_complementar: match nfe.inf.add {
195 Some(add) => add.informacao_complementar,
196 None => None,
197 },
198 })
199 }
200}
201
202impl Serialize for Nfe {
203 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
204 where
205 S: Serializer,
206 {
207 let inf = NfeInfContainer {
208 versao: self.versao,
209 chave_acesso: format!("NFe{}", self.chave_acesso),
210 ide: self.ide.clone(),
211 emit: self.emit.clone(),
212 dest: self.dest.clone(),
213 itens: self.itens.clone(),
214 totais: self.totais.clone(),
215 transporte: self.transporte.clone(),
216 add: match self.informacao_complementar.clone() {
217 Some(ic) => Some(InfAddContainer {
218 informacao_complementar: Some(ic),
219 }),
220 None => None,
221 },
222 };
223
224 let root = NfeRootContainer { inf };
225
226 root.serialize(serializer)
227 }
228}
229
230impl Serialize for VersaoLayout {
231 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
232 where
233 S: Serializer,
234 {
235 serializer.serialize_str(match self {
236 VersaoLayout::V4_00 => "4.00",
237 })
238 }
239}
240
241/// Container auxiliar para deserialização do elemento raiz <NFe>
242///
243/// O quick-xml necessita de estruturas intermediárias para mapear
244/// a hierarquia do XML corretamente. Esta estrutura representa
245/// o elemento raiz `<NFe>` que contém `<infNFe>`.
246#[derive(Deserialize, Serialize)]
247#[serde(rename = "NFe")]
248struct NfeRootContainer {
249 /// Elemento <infNFe> que contém todas as informações da nota
250 #[serde(rename = "infNFe")]
251 pub inf: NfeInfContainer,
252}
253
254/// Container para informações adicionais (tag <infAdic>)
255///
256/// Grupo opcional que pode conter informações complementares
257/// de interesse do contribuinte e do Fisco.
258#[derive(Deserialize, Serialize)]
259struct InfAddContainer {
260 /// Informações complementares de interesse do contribuinte
261 /// Tag: <infCpl> - Máximo 5000 caracteres
262 #[serde(rename = "$unflatten=infCpl")]
263 pub informacao_complementar: Option<String>,
264}
265
266/// Container para o elemento <infNFe> - Informações da NF-e
267///
268/// Este container mapeia diretamente os atributos e elementos filhos
269/// do grupo `<infNFe>`, que é o container principal de dados da nota.
270///
271/// ## Mapeamento XML -> Rust
272///
273/// | Atributo/Tag | Campo | Descrição |
274/// |--------------|-------|-----------|
275/// | @versao | versao | Versão do layout |
276/// | @Id | chave_acesso | Chave de acesso (44 dígitos com prefixo "NFe") |
277/// | \<ide\> | ide | Identificação |
278/// | \<emit\> | emit | Emitente |
279/// | \<dest\> | dest | Destinatário |
280/// | \<det\> | itens | Itens/produtos (vetor) |
281/// | \<total\> | totais | Totalização |
282/// | \<transp\> | transporte | Transporte |
283/// | \<infAdic\> | add | Informações adicionais |
284#[derive(Deserialize, Serialize)]
285struct NfeInfContainer {
286 /// Versão do layout (atributo @versao)
287 #[serde(rename = "versao")]
288 pub versao: VersaoLayout,
289
290 /// Chave de acesso com prefixo "NFe" (atributo @Id)
291 /// Formato: "NFe" + 44 dígitos
292 #[serde(rename = "Id")]
293 pub chave_acesso: String,
294
295 /// Grupo de identificação da NF-e
296 #[serde(rename = "ide")]
297 pub ide: Identificacao,
298
299 /// Grupo de dados do emitente
300 #[serde(rename = "emit")]
301 pub emit: Emitente,
302
303 /// Grupo de dados do destinatário (opcional)
304 #[serde(rename = "dest")]
305 pub dest: Option<Destinatario>,
306
307 /// Lista de itens/detalhamento de produtos
308 /// Cada elemento <det> representa um item da nota
309 #[serde(rename = "det")]
310 pub itens: Vec<Item>,
311
312 /// Grupo de totais da NF-e
313 #[serde(rename = "total")]
314 pub totais: Totalizacao,
315
316 /// Grupo de informações de transporte
317 #[serde(rename = "transp")]
318 pub transporte: Transporte,
319
320 /// Grupo de informações adicionais (opcional)
321 #[serde(rename = "infAdic")]
322 pub add: Option<InfAddContainer>,
323}