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}