nfe/base/
mod.rs

1//! Base da NF-e
2//!
3//! Tipos e estruturas para tratamento da NF-e sem
4//! distinção dos modelos.
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::convert::TryFrom;
8use std::fs::File;
9use std::io::Read;
10use std::str::FromStr;
11pub mod dest;
12pub mod emit;
13pub mod endereco;
14mod error;
15pub mod ide;
16pub mod item;
17pub mod totais;
18pub mod transporte;
19use dest::Destinatario;
20use emit::Emitente;
21pub use error::Error;
22use ide::Identificacao;
23use item::Item;
24use totais::Totalizacao;
25use transporte::Transporte;
26
27/// Base da Nota Fiscal Eletrônica
28///
29/// Representa o documento ainda sem a interface
30/// do seu modelo(NF-e x NFC-e)
31#[derive(Debug, PartialEq)]
32pub struct Nfe {
33    pub versao: VersaoLayout,
34    pub chave_acesso: String,
35    pub ide: Identificacao,
36    pub emit: Emitente,
37    pub dest: Option<Destinatario>,
38    pub itens: Vec<Item>,
39    pub totais: Totalizacao,
40    pub transporte: Transporte,
41    /// Informações complementares de interesse do contribuinte
42    pub informacao_complementar: Option<String>,
43}
44
45/// Versão do layout da NF-e
46#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize)]
47pub enum VersaoLayout {
48    #[serde(rename = "4.00")]
49    V4_00 = 4,
50}
51
52impl FromStr for Nfe {
53    type Err = Error;
54
55    fn from_str(s: &str) -> Result<Self, Self::Err> {
56        quick_xml::de::from_str(s).map_err(|e| e.into())
57    }
58}
59
60impl TryFrom<File> for Nfe {
61    type Error = Error;
62
63    fn try_from(mut f: File) -> Result<Self, Self::Error> {
64        let mut xml = String::new();
65        f.read_to_string(&mut xml).map_err(|e| Error::Io(e))?;
66
67        xml.parse::<Nfe>()
68    }
69}
70
71impl ToString for Nfe {
72    fn to_string(&self) -> String {
73        quick_xml::se::to_string(self).expect("Falha ao serializar a nota")
74    }
75}
76
77impl<'de> Deserialize<'de> for Nfe {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: Deserializer<'de>,
81    {
82        let nfe = NfeRootContainer::deserialize(deserializer)?;
83
84        Ok(Self {
85            versao: nfe.inf.versao,
86            chave_acesso: nfe.inf.chave_acesso.replace("NFe", ""),
87            ide: nfe.inf.ide,
88            emit: nfe.inf.emit,
89            dest: nfe.inf.dest,
90            itens: nfe.inf.itens,
91            totais: nfe.inf.totais,
92            transporte: nfe.inf.transporte,
93            informacao_complementar: match nfe.inf.add {
94                Some(add) => add.informacao_complementar,
95                None => None,
96            },
97        })
98    }
99}
100
101impl Serialize for Nfe {
102    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103    where
104        S: Serializer,
105    {
106        let inf = NfeInfContainer {
107            versao: self.versao,
108            chave_acesso: format!("NFe{}", self.chave_acesso),
109            ide: self.ide.clone(),
110            emit: self.emit.clone(),
111            dest: self.dest.clone(),
112            itens: self.itens.clone(),
113            totais: self.totais.clone(),
114            transporte: self.transporte.clone(),
115            add: match self.informacao_complementar.clone() {
116                Some(ic) => Some(InfAddContainer {
117                    informacao_complementar: Some(ic),
118                }),
119                None => None,
120            },
121        };
122
123        let root = NfeRootContainer { inf };
124
125        root.serialize(serializer)
126    }
127}
128
129impl Serialize for VersaoLayout {
130    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131    where
132        S: Serializer,
133    {
134        serializer.serialize_str(match self {
135            VersaoLayout::V4_00 => "4.00",
136        })
137    }
138}
139
140#[derive(Deserialize, Serialize)]
141#[serde(rename = "NFe")]
142struct NfeRootContainer {
143    #[serde(rename = "infNFe")]
144    pub inf: NfeInfContainer,
145}
146
147#[derive(Deserialize, Serialize)]
148struct InfAddContainer {
149    #[serde(rename = "$unflatten=infCpl")]
150    pub informacao_complementar: Option<String>,
151}
152
153#[derive(Deserialize, Serialize)]
154struct NfeInfContainer {
155    #[serde(rename = "versao")]
156    pub versao: VersaoLayout,
157    #[serde(rename = "Id")]
158    pub chave_acesso: String,
159    #[serde(rename = "ide")]
160    pub ide: Identificacao,
161    #[serde(rename = "emit")]
162    pub emit: Emitente,
163    #[serde(rename = "dest")]
164    pub dest: Option<Destinatario>,
165    #[serde(rename = "det")]
166    pub itens: Vec<Item>,
167    #[serde(rename = "total")]
168    pub totais: Totalizacao,
169    #[serde(rename = "transp")]
170    pub transporte: Transporte,
171    #[serde(rename = "infAdic")]
172    pub add: Option<InfAddContainer>,
173}