1use crate::base::dest::{Destinatario, IndicadorContribuicaoIe};
6use crate::base::emit::Emitente;
7use crate::base::endereco::Endereco;
8use crate::base::ide::*;
9use crate::base::item::{Item, Produto, Imposto};
10use crate::base::totais::Totalizacao;
11use crate::base::transporte::{Transporte, ModalidadeFrete};
12use crate::base::{Nfe, VersaoLayout};
13use chrono::{DateTime, Utc};
14
15#[derive(Debug, Default)]
17pub struct NfeBuilder {
18 codigo_uf: Option<u8>,
20 numero: Option<u32>,
21 serie: Option<u16>,
22 modelo: Option<ModeloDocumentoFiscal>,
23 natureza_operacao: Option<String>,
24 tipo_operacao: Option<TipoOperacao>,
25 destino_operacao: Option<DestinoOperacao>,
26 finalidade: Option<FinalidadeEmissao>,
27 ambiente: Option<TipoAmbiente>,
28 codigo_municipio: Option<u32>,
29
30 emit_cnpj: Option<String>,
32 emit_razao_social: Option<String>,
33 emit_nome_fantasia: Option<String>,
34 emit_ie: Option<String>,
35 emit_endereco: Option<Endereco>,
36
37 dest_cnpj: Option<String>,
39 dest_razao_social: Option<String>,
40 dest_indicador_ie: Option<IndicadorContribuicaoIe>,
41 dest_endereco: Option<Endereco>,
42
43 itens: Vec<ItemBuilder>,
45
46 modalidade_frete: Option<ModalidadeFrete>,
48
49 informacao_complementar: Option<String>,
51}
52
53#[derive(Debug, Clone)]
55pub struct ItemBuilder {
56 pub codigo: String,
57 pub descricao: String,
58 pub ncm: String,
59 pub cfop: String,
60 pub unidade: String,
61 pub quantidade: f32,
62 pub valor_unitario: f32,
63 pub gtin: Option<String>,
64 pub valor_desconto: Option<f32>,
65}
66
67impl NfeBuilder {
68 pub fn new() -> Self {
70 Self::default()
71 }
72
73 pub fn codigo_uf(mut self, uf: u8) -> Self {
77 self.codigo_uf = Some(uf);
78 self
79 }
80
81 pub fn numero(mut self, numero: u32) -> Self {
83 self.numero = Some(numero);
84 self
85 }
86
87 pub fn serie(mut self, serie: u16) -> Self {
89 self.serie = Some(serie);
90 self
91 }
92
93 pub fn modelo(mut self, modelo: ModeloDocumentoFiscal) -> Self {
95 self.modelo = Some(modelo);
96 self
97 }
98
99 pub fn natureza_operacao(mut self, natureza: &str) -> Self {
101 self.natureza_operacao = Some(natureza.to_string());
102 self
103 }
104
105 pub fn tipo_operacao(mut self, tipo: TipoOperacao) -> Self {
107 self.tipo_operacao = Some(tipo);
108 self
109 }
110
111 pub fn destino_operacao(mut self, destino: DestinoOperacao) -> Self {
113 self.destino_operacao = Some(destino);
114 self
115 }
116
117 pub fn finalidade(mut self, finalidade: FinalidadeEmissao) -> Self {
119 self.finalidade = Some(finalidade);
120 self
121 }
122
123 pub fn ambiente(mut self, ambiente: TipoAmbiente) -> Self {
125 self.ambiente = Some(ambiente);
126 self
127 }
128
129 pub fn codigo_municipio(mut self, codigo: u32) -> Self {
131 self.codigo_municipio = Some(codigo);
132 self
133 }
134
135 pub fn emit_cnpj(mut self, cnpj: &str) -> Self {
139 self.emit_cnpj = Some(cnpj.replace(&['.', '/', '-'][..], ""));
140 self
141 }
142
143 pub fn emit_razao_social(mut self, razao: &str) -> Self {
145 self.emit_razao_social = Some(razao.to_string());
146 self
147 }
148
149 pub fn emit_nome_fantasia(mut self, fantasia: &str) -> Self {
151 self.emit_nome_fantasia = Some(fantasia.to_string());
152 self
153 }
154
155 pub fn emit_ie(mut self, ie: &str) -> Self {
157 self.emit_ie = Some(ie.to_string());
158 self
159 }
160
161 pub fn emit_endereco(mut self, endereco: Endereco) -> Self {
163 self.emit_endereco = Some(endereco);
164 self
165 }
166
167 pub fn dest_cnpj(mut self, cnpj: &str) -> Self {
171 self.dest_cnpj = Some(cnpj.replace(&['.', '/', '-'][..], ""));
172 self
173 }
174
175 pub fn dest_razao_social(mut self, razao: &str) -> Self {
177 self.dest_razao_social = Some(razao.to_string());
178 self
179 }
180
181 pub fn dest_indicador_ie(mut self, indicador: IndicadorContribuicaoIe) -> Self {
183 self.dest_indicador_ie = Some(indicador);
184 self
185 }
186
187 pub fn dest_endereco(mut self, endereco: Endereco) -> Self {
189 self.dest_endereco = Some(endereco);
190 self
191 }
192
193 pub fn add_item(mut self, item: ItemBuilder) -> Self {
197 self.itens.push(item);
198 self
199 }
200
201 pub fn modalidade_frete(mut self, modalidade: ModalidadeFrete) -> Self {
205 self.modalidade_frete = Some(modalidade);
206 self
207 }
208
209 pub fn informacao_complementar(mut self, info: &str) -> Self {
213 self.informacao_complementar = Some(info.to_string());
214 self
215 }
216
217 pub fn build(self) -> Result<Nfe, String> {
219 let codigo_uf = self.codigo_uf.ok_or("Código UF é obrigatório")?;
221 let numero = self.numero.ok_or("Número é obrigatório")?;
222 let serie = self.serie.unwrap_or(1);
223 let modelo = self.modelo.unwrap_or(ModeloDocumentoFiscal::Nfe);
224 let natureza = self.natureza_operacao.ok_or("Natureza da operação é obrigatória")?;
225 let tipo_op = self.tipo_operacao.unwrap_or(TipoOperacao::Saida);
226 let destino = self.destino_operacao.unwrap_or(DestinoOperacao::Interna);
227 let finalidade = self.finalidade.unwrap_or(FinalidadeEmissao::Normal);
228 let ambiente = self.ambiente.unwrap_or(TipoAmbiente::Homologacao);
229 let codigo_mun = self.codigo_municipio.ok_or("Código do município é obrigatório")?;
230
231 if self.itens.is_empty() {
232 return Err("Pelo menos um item é obrigatório".to_string());
233 }
234
235 let codigo_numerico = format!("{:08}", rand_u32() % 100000000);
237
238 let agora: DateTime<Utc> = Utc::now();
240
241 let mut itens_nfe = Vec::new();
243 let mut total_produtos = 0.0f32;
244 let mut total_desconto = 0.0f32;
245
246 for (idx, item) in self.itens.iter().enumerate() {
247 let valor_bruto = item.quantidade * item.valor_unitario;
248 total_produtos += valor_bruto;
249 if let Some(desc) = item.valor_desconto {
250 total_desconto += desc;
251 }
252
253 let produto = Produto::new(
254 item.codigo.clone(),
255 item.descricao.clone(),
256 item.ncm.clone(),
257 item.cfop.clone(),
258 item.unidade.clone(),
259 item.quantidade,
260 item.valor_unitario,
261 valor_bruto,
262 );
263
264 let imposto = Imposto::default();
266
267 itens_nfe.push(Item {
268 numero: (idx + 1) as u8,
269 produto,
270 imposto,
271 });
272 }
273
274 let valor_total = total_produtos - total_desconto;
276
277 let aamm = agora.format("%y%m").to_string();
279 let cnpj_emit = self.emit_cnpj.clone().unwrap_or_default();
280 let chave_sem_dv = format!(
281 "{:02}{}{:014}{:02}{:03}{:09}{:01}{:08}",
282 codigo_uf,
283 aamm,
284 cnpj_emit,
285 modelo as u8,
286 serie,
287 numero,
288 1, codigo_numerico
290 );
291 let dv = calcular_dv(&chave_sem_dv);
292 let chave_acesso = format!("{}{}", chave_sem_dv, dv);
293
294 let emit_endereco = self.emit_endereco.unwrap_or_else(|| Endereco::default());
296
297 Ok(Nfe {
299 versao: VersaoLayout::V4_00,
300 chave_acesso,
301 ide: Identificacao {
302 codigo_uf,
303 chave: ComposicaoChaveAcesso {
304 codigo: codigo_numerico,
305 digito_verificador: dv,
306 },
307 numero,
308 serie,
309 modelo,
310 emissao: Emissao {
311 horario: agora,
312 tipo: TipoEmissao::Normal,
313 finalidade,
314 processo: TipoProcessoEmissao::ViaAplicativoDoContribuinte,
315 versao_processo: "1.0.0".to_string(),
316 },
317 operacao: Operacao {
318 horario: None,
319 tipo: tipo_op,
320 destino,
321 natureza,
322 consumidor: TipoConsumidor::Normal,
323 presenca: TipoPresencaComprador::Presencial,
324 intermediador: None,
325 },
326 codigo_municipio: codigo_mun,
327 formato_danfe: FormatoImpressaoDanfe::NormalRetrato,
328 ambiente,
329 },
330 emit: Emitente {
331 cnpj: self.emit_cnpj,
332 razao_social: self.emit_razao_social,
333 nome_fantasia: self.emit_nome_fantasia,
334 ie: self.emit_ie,
335 iest: None,
336 endereco: emit_endereco,
337 },
338 dest: if self.dest_cnpj.is_some() {
339 Some(Destinatario {
340 cnpj: self.dest_cnpj.unwrap_or_default(),
341 razao_social: self.dest_razao_social,
342 indicador_ie: self.dest_indicador_ie.unwrap_or(IndicadorContribuicaoIe::NaoContribuinteIe),
343 ie: None,
344 endereco: self.dest_endereco,
345 })
346 } else {
347 None
348 },
349 itens: itens_nfe,
350 totais: Totalizacao {
351 valor_produtos: total_produtos,
352 valor_desconto: total_desconto,
353 valor_total,
354 ..Default::default()
355 },
356 transporte: Transporte {
357 modalidade: self.modalidade_frete.unwrap_or(ModalidadeFrete::SemTransporte),
358 },
359 informacao_complementar: self.informacao_complementar,
360 })
361 }
362}
363
364impl ItemBuilder {
365 pub fn new(codigo: &str, descricao: &str, ncm: &str, cfop: &str) -> Self {
367 Self {
368 codigo: codigo.to_string(),
369 descricao: descricao.to_string(),
370 ncm: ncm.to_string(),
371 cfop: cfop.to_string(),
372 unidade: "UN".to_string(),
373 quantidade: 1.0,
374 valor_unitario: 0.0,
375 gtin: None,
376 valor_desconto: None,
377 }
378 }
379
380 pub fn unidade(mut self, unidade: &str) -> Self {
382 self.unidade = unidade.to_string();
383 self
384 }
385
386 pub fn quantidade(mut self, qtd: f32) -> Self {
388 self.quantidade = qtd;
389 self
390 }
391
392 pub fn valor_unitario(mut self, valor: f32) -> Self {
394 self.valor_unitario = valor;
395 self
396 }
397
398 pub fn gtin(mut self, gtin: &str) -> Self {
400 self.gtin = Some(gtin.to_string());
401 self
402 }
403
404 pub fn desconto(mut self, valor: f32) -> Self {
406 self.valor_desconto = Some(valor);
407 self
408 }
409}
410
411fn calcular_dv(chave: &str) -> u8 {
413 let pesos = [2, 3, 4, 5, 6, 7, 8, 9];
414 let mut soma = 0u32;
415
416 for (i, c) in chave.chars().rev().enumerate() {
417 if let Some(digito) = c.to_digit(10) {
418 soma += digito * pesos[i % 8];
419 }
420 }
421
422 let resto = soma % 11;
423 if resto < 2 { 0 } else { (11 - resto) as u8 }
424}
425
426fn rand_u32() -> u32 {
428 use std::time::{SystemTime, UNIX_EPOCH};
429 let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
430 (duration.as_nanos() % u32::MAX as u128) as u32
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_builder_basico() {
439 let nfe = NfeBuilder::new()
440 .codigo_uf(35)
441 .numero(1)
442 .serie(1)
443 .natureza_operacao("VENDA DE MERCADORIA")
444 .codigo_municipio(3550308)
445 .emit_cnpj("12.345.678/0001-90")
446 .emit_razao_social("EMPRESA TESTE LTDA")
447 .emit_ie("123456789")
448 .add_item(
449 ItemBuilder::new("PROD001", "Produto Teste", "12345678", "5102")
450 .quantidade(10.0)
451 .valor_unitario(100.0)
452 )
453 .build();
454
455 assert!(nfe.is_ok());
456 let nfe = nfe.unwrap();
457 assert_eq!(nfe.ide.numero, 1);
458 assert_eq!(nfe.itens.len(), 1);
459 assert_eq!(nfe.totais.valor_produtos, 1000.0);
460 }
461
462 #[test]
463 fn test_calculo_dv() {
464 let chave = "35150312345678901234550010000000011000000011";
466 let dv = calcular_dv(&chave[..43]);
467 assert!(dv < 10);
468 }
469}