Skip to main content

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}