Skip to main content

nfe_parser/base/ide/
mod.rs

1//! Identificação da NF-e - Grupo <ide>
2//!
3//! Este módulo contém as estruturas para o grupo de identificação da NF-e,
4//! que é definido pela tag `<ide>` no XML e contém informações como:
5//!
6//! - Código da UF do emitente
7//! - Código numérico que compõe a chave de acesso
8//! - Natureza da operação
9//! - Modelo do documento (55 = NF-e, 65 = NFC-e)
10//! - Série e número da nota
11//! - Data e hora de emissão
12//! - Tipo de operação (entrada/saída)
13//! - Destino da operação (interna, interestadual, exterior)
14//! - Tipo de ambiente (produção/homologação)
15//!
16//! ## Referência SEFAZ
17//!
18//! Conforme Manual de Orientação do Contribuinte, o grupo `<ide>` é obrigatório
19//! e deve conter no mínimo: cUF, cNF, natOp, mod, serie, nNF, dhEmi, tpNF,
20//! idDest, cMunFG, tpImp, tpEmis, cDV, tpAmb, finNFe, indFinal, indPres, procEmi.
21
22use super::Error;
23use chrono::prelude::*;
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_repr::{Deserialize_repr, Serialize_repr};
26use std::str::FromStr;
27
28mod emissao;
29mod operacao;
30
31pub use emissao::*;
32pub use operacao::*;
33
34/// Identificação da NF-e (Grupo IDE - tag `<ide>`)
35///
36/// Contém os dados de identificação da nota fiscal eletrônica conforme
37/// layout 4.00 da SEFAZ.
38///
39/// ## Campos e Tags XML
40///
41/// | Campo | Tag XML | Descrição | Obrig. |
42/// |-------|---------|-----------|--------|
43/// | codigo_uf | cUF | Código IBGE da UF do emitente | Sim |
44/// | chave.codigo | cNF | Código numérico da chave (8 dígitos) | Sim |
45/// | chave.digito_verificador | cDV | Dígito verificador da chave | Sim |
46/// | numero | nNF | Número da NF-e (1 a 999999999) | Sim |
47/// | serie | serie | Série da NF-e (0 a 999) | Sim |
48/// | modelo | mod | Modelo: 55 (NF-e) ou 65 (NFC-e) | Sim |
49/// | codigo_municipio | cMunFG | Código IBGE do município de ocorrência | Sim |
50/// | formato_danfe | tpImp | Formato de impressão do DANFE | Sim |
51/// | ambiente | tpAmb | 1=Produção, 2=Homologação | Sim |
52#[derive(Debug, PartialEq, Clone)]
53pub struct Identificacao {
54    /// Código IBGE da UF do emitente (2 dígitos)
55    /// Ex: 35 = SP, 33 = RJ, 43 = RS
56    pub codigo_uf: u8,
57
58    /// Componentes que formam parte da chave de acesso
59    pub chave: ComposicaoChaveAcesso,
60
61    /// Número da NF-e (1 a 999.999.999)
62    pub numero: u32,
63
64    /// Série da NF-e (0 a 999)
65    /// Série 0-899: numeração de controle do contribuinte
66    /// Série 900-999: uso exclusivo de regime especial
67    pub serie: u16,
68
69    /// Modelo do documento fiscal
70    /// 55 = NF-e (Nota Fiscal Eletrônica)
71    /// 65 = NFC-e (Nota Fiscal de Consumidor Eletrônica)
72    pub modelo: ModeloDocumentoFiscal,
73
74    /// Dados de emissão da nota (datas, tipo, finalidade)
75    pub emissao: Emissao,
76
77    /// Dados da operação (tipo, destino, natureza)
78    pub operacao: Operacao,
79
80    /// Código IBGE do município de ocorrência do fato gerador
81    /// Geralmente é o município do emitente
82    pub codigo_municipio: u32,
83
84    /// Formato de impressão do DANFE
85    pub formato_danfe: FormatoImpressaoDanfe,
86
87    /// Tipo de ambiente: Produção ou Homologação
88    /// Em homologação, a NF-e não tem validade fiscal
89    pub ambiente: TipoAmbiente,
90}
91
92/// Modelo do documento fiscal eletrônico (tag `<mod>`)
93///
94/// Define se o documento é uma NF-e (modelo 55) para operações B2B
95/// ou NFC-e (modelo 65) para vendas ao consumidor final.
96///
97/// ## Diferenças entre NF-e e NFC-e
98///
99/// | Característica | NF-e (55) | NFC-e (65) |
100/// |----------------|-----------|------------|
101/// | Uso principal | B2B, transferências | Varejo, consumidor final |
102/// | Destinatário | Obrigatório | Opcional |
103/// | Contingência | Vários modos | Offline |
104/// | DANFE | A4 | Cupom/QR Code |
105#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize_repr, Serialize_repr)]
106#[repr(u8)]
107pub enum ModeloDocumentoFiscal {
108    /// Modelo 55 - Nota Fiscal Eletrônica (NF-e)
109    /// Usada para operações B2B, transferências, devoluções
110    Nfe = 55,
111
112    /// Modelo 65 - Nota Fiscal de Consumidor Eletrônica (NFC-e)
113    /// Usada para vendas presenciais ao consumidor final
114    Nfce = 65,
115}
116
117/// Formato de impressão do DANFE (tag `<tpImp>`)
118///
119/// Define como o Documento Auxiliar da NF-e será impresso.
120/// O formato deve ser compatível com o modelo do documento.
121#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize_repr, Serialize_repr)]
122#[repr(u8)]
123pub enum FormatoImpressaoDanfe {
124    /// 0 = Sem geração de DANFE
125    SemGeracao = 0,
126
127    /// 1 = DANFE normal, retrato (formato A4 vertical)
128    NormalRetrato = 1,
129
130    /// 2 = DANFE normal, paisagem (formato A4 horizontal)
131    NormalPaisagem = 2,
132
133    /// 3 = DANFE simplificado
134    Simplificado = 3,
135
136    /// 4 = DANFE NFC-e (formato cupom)
137    Nfce = 4,
138
139    /// 5 = DANFE NFC-e em mensagem eletrônica
140    /// Usado quando o consumidor opta por receber por e-mail/SMS
141    NfceMensagemEletronica = 5,
142}
143
144/// Tipo do ambiente de transmissão (tag `<tpAmb>`)
145///
146/// Define se a NF-e está sendo emitida em ambiente de produção
147/// (com validade fiscal) ou homologação (para testes).
148///
149/// ## Importante
150///
151/// - Em **homologação**, a NF-e NÃO tem validade fiscal
152/// - O campo `<xNome>` do destinatário é substituído por:
153///   "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL"
154#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize_repr, Serialize_repr)]
155#[repr(u8)]
156pub enum TipoAmbiente {
157    /// 1 = Produção - NF-e com validade fiscal real
158    Producao = 1,
159
160    /// 2 = Homologação - Ambiente de testes, sem valor fiscal
161    Homologacao = 2,
162}
163
164/// Componentes que formam a chave de acesso da NF-e
165///
166/// A chave de acesso possui 44 dígitos e é composta por:
167/// - UF (2) + AAMM (4) + CNPJ (14) + MOD (2) + SERIE (3) + NNF (9) + CODIGO (9) + DV (1)
168///
169/// Esta estrutura armazena os campos que são informados separadamente no XML:
170/// - `cNF`: Código numérico aleatório (8 dígitos que compõem os 9 do código)
171/// - `cDV`: Dígito verificador calculado pelo módulo 11
172#[derive(Debug, Eq, PartialEq, Clone)]
173pub struct ComposicaoChaveAcesso {
174    /// Código numérico aleatório que compõe a chave de acesso (tag `<cNF>`)
175    /// São 8 dígitos gerados pelo sistema emissor
176    pub codigo: String,
177
178    /// Dígito verificador da chave de acesso (tag `<cDV>`)
179    /// Calculado usando módulo 11 sobre os 43 primeiros dígitos
180    pub digito_verificador: u8,
181}
182
183impl FromStr for Identificacao {
184    type Err = Error;
185
186    fn from_str(s: &str) -> Result<Self, Self::Err> {
187        quick_xml::de::from_str(s).map_err(|e| e.into())
188    }
189}
190
191impl ToString for Identificacao {
192    fn to_string(&self) -> String {
193        quick_xml::se::to_string(self).expect("Falha ao serializar a identificação")
194    }
195}
196
197impl<'de> Deserialize<'de> for Identificacao {
198    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
199    where
200        D: Deserializer<'de>,
201    {
202        // TODO: voltar a tentar usar o serde flatten
203
204        let ide = IdeContainer::deserialize(deserializer)?;
205
206        Ok(Self {
207            codigo_uf: ide.codigo_uf,
208            numero: ide.numero,
209            serie: ide.serie,
210            modelo: ide.modelo,
211            codigo_municipio: ide.codigo_municipio,
212            formato_danfe: ide.formato_danfe,
213            ambiente: ide.ambiente,
214            chave: ComposicaoChaveAcesso {
215                codigo: ide.c_codigo.clone(),
216                digito_verificador: ide.c_digito_verificador,
217            },
218            operacao: Operacao {
219                horario: ide.o_horario,
220                tipo: ide.o_tipo,
221                destino: ide.o_destino,
222                natureza: ide.o_natureza,
223                consumidor: ide.o_consumidor,
224                presenca: ide.o_presenca,
225                intermediador: ide.o_intermediador,
226            },
227            emissao: Emissao {
228                horario: ide.e_horario,
229                tipo: ide.e_tipo,
230                finalidade: ide.e_finalidade,
231                processo: ide.e_processo,
232                versao_processo: ide.e_versao_processo,
233            },
234        })
235    }
236}
237
238impl Serialize for Identificacao {
239    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
240    where
241        S: Serializer,
242    {
243        let ide = IdeContainer {
244            codigo_uf: self.codigo_uf,
245            numero: self.numero,
246            serie: self.serie,
247            modelo: self.modelo,
248            codigo_municipio: self.codigo_municipio,
249            formato_danfe: self.formato_danfe,
250            ambiente: self.ambiente,
251            c_codigo: self.chave.codigo.clone(),
252            c_digito_verificador: self.chave.digito_verificador,
253            o_horario: self.operacao.horario,
254            o_tipo: self.operacao.tipo,
255            o_destino: self.operacao.destino,
256            o_natureza: self.operacao.natureza.clone(),
257            o_consumidor: self.operacao.consumidor,
258            o_presenca: self.operacao.presenca,
259            o_intermediador: self.operacao.intermediador,
260            e_horario: self.emissao.horario,
261            e_tipo: self.emissao.tipo,
262            e_finalidade: self.emissao.finalidade,
263            e_processo: self.emissao.processo,
264            e_versao_processo: self.emissao.versao_processo.clone(),
265        };
266
267        ide.serialize(serializer)
268    }
269}
270
271#[derive(Deserialize, Serialize)]
272#[serde(rename = "ide")]
273struct IdeContainer {
274    #[serde(rename = "$unflatten=cUF")]
275    pub codigo_uf: u8,
276    #[serde(rename = "$unflatten=nNF")]
277    pub numero: u32,
278    #[serde(rename = "$unflatten=serie")]
279    pub serie: u16,
280    #[serde(rename = "$unflatten=mod")]
281    pub modelo: ModeloDocumentoFiscal,
282    #[serde(rename = "$unflatten=cMunFG")]
283    pub codigo_municipio: u32,
284    #[serde(rename = "$unflatten=tpImp")]
285    pub formato_danfe: FormatoImpressaoDanfe,
286    #[serde(rename = "$unflatten=tpAmb")]
287    pub ambiente: TipoAmbiente,
288
289    #[serde(rename = "$unflatten=cNF")]
290    pub c_codigo: String,
291    #[serde(rename = "$unflatten=cDV")]
292    pub c_digito_verificador: u8,
293
294    #[serde(rename = "$unflatten=dhEmi")]
295    #[serde(serialize_with = "serialize_horario")]
296    pub e_horario: DateTime<Utc>,
297    #[serde(rename = "$unflatten=tpEmis")]
298    pub e_tipo: TipoEmissao,
299    #[serde(rename = "$unflatten=finNFe")]
300    pub e_finalidade: FinalidadeEmissao,
301    #[serde(rename = "$unflatten=procEmi")]
302    pub e_processo: TipoProcessoEmissao,
303    #[serde(rename = "$unflatten=verProc")]
304    pub e_versao_processo: String,
305
306    #[serde(rename = "$unflatten=dhSaiEnt")]
307    #[serde(skip_serializing_if = "Option::is_none")]
308    #[serde(serialize_with = "serialize_horario_op")]
309    #[serde(default)]
310    pub o_horario: Option<DateTime<Utc>>,
311    #[serde(rename = "$unflatten=tpNF")]
312    pub o_tipo: TipoOperacao,
313    #[serde(rename = "$unflatten=idDest")]
314    pub o_destino: DestinoOperacao,
315    #[serde(rename = "$unflatten=natOp")]
316    pub o_natureza: String,
317    #[serde(rename = "$unflatten=indFinal")]
318    pub o_consumidor: TipoConsumidor,
319    #[serde(rename = "$unflatten=indPres")]
320    pub o_presenca: TipoPresencaComprador,
321    #[serde(rename = "$unflatten=indIntermed")]
322    #[serde(skip_serializing_if = "Option::is_none")]
323    #[serde(default)]
324    pub o_intermediador: Option<TipoIntermediador>,
325}
326
327fn serialize_horario<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
328where
329    S: Serializer,
330{
331    serializer.serialize_str(&date.to_rfc3339())
332}
333
334fn serialize_horario_op<S>(date: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
335where
336    S: Serializer,
337{
338    serialize_horario(&date.unwrap(), serializer)
339}