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