fiscal_core/xml_builder/
mod.rs1pub mod access_key;
19mod builder;
20pub mod dest;
21pub mod det;
22pub mod emit;
23pub mod ide;
24pub mod optional;
25pub mod pag;
26pub mod tax_id;
27pub mod total;
28pub mod transp;
29
30pub use access_key::build_access_key;
31pub use builder::{Built, Draft, InvoiceBuilder, Signed};
32
33use crate::FiscalError;
34use crate::constants::{NFE_NAMESPACE, NFE_VERSION};
35use crate::newtypes::IbgeCode;
36use crate::state_codes::STATE_IBGE_CODES;
37use crate::tax_icms::{create_icms_totals, merge_icms_totals};
38use crate::types::{AccessKeyParams, InvoiceBuildData, InvoiceXmlResult};
39use crate::xml_utils::{TagContent, tag};
40
41fn generate_xml(data: &InvoiceBuildData) -> Result<InvoiceXmlResult, FiscalError> {
45 let state_ibge = STATE_IBGE_CODES
46 .get(data.issuer.state_code.as_str())
47 .copied()
48 .ok_or_else(|| FiscalError::InvalidStateCode(data.issuer.state_code.clone()))?;
49
50 let numeric_code = access_key::generate_numeric_code();
51 let year_month = access_key::format_year_month(&data.issued_at);
52
53 let ak_params = AccessKeyParams {
54 state_code: IbgeCode(state_ibge.to_string()),
55 year_month,
56 tax_id: data.issuer.tax_id.clone(),
57 model: data.model,
58 series: data.series,
59 number: data.number,
60 emission_type: data.emission_type,
61 numeric_code: numeric_code.clone(),
62 };
63
64 let access_key = build_access_key(&ak_params)?;
65 let inf_nfe_id = format!("NFe{access_key}");
66
67 let mut icms_totals = create_icms_totals();
69 let mut total_products: i64 = 0;
70 let mut total_ipi: i64 = 0;
71 let mut total_pis: i64 = 0;
72 let mut total_cofins: i64 = 0;
73 let mut total_ii: i64 = 0;
74 let mut total_frete: i64 = 0;
75 let mut total_seg: i64 = 0;
76 let mut total_desc: i64 = 0;
77 let mut total_outro: i64 = 0;
78 let mut total_tot_trib: i64 = 0;
79 let mut total_ipi_devol: i64 = 0;
80
81 let mut det_elements = Vec::with_capacity(data.items.len());
82 for item in &data.items {
83 let det_result = det::build_det(item, data)?;
84 if det_result.ind_tot == 1 {
86 total_products += item.total_price.0;
87 total_ipi += det_result.v_ipi;
88 total_pis += det_result.v_pis;
89 total_cofins += det_result.v_cofins;
90 total_ii += det_result.v_ii;
91 total_frete += det_result.v_frete;
92 total_seg += det_result.v_seg;
93 total_desc += det_result.v_desc;
94 total_outro += det_result.v_outro;
95 total_tot_trib += det_result.v_tot_trib;
96 total_ipi_devol += det_result.v_ipi_devol;
97 merge_icms_totals(&mut icms_totals, &det_result.icms_totals);
98 }
99 det_elements.push(det_result.xml);
100 }
101
102 let mut inf_children = vec![
104 ide::build_ide(data, state_ibge, &numeric_code, &access_key),
105 emit::build_emit(data),
106 ];
107
108 if let Some(dest_xml) = dest::build_dest(data) {
109 inf_children.push(dest_xml);
110 }
111
112 if let Some(ref w) = data.withdrawal {
113 inf_children.push(optional::build_withdrawal(w));
114 }
115 if let Some(ref d) = data.delivery {
116 inf_children.push(optional::build_delivery(d));
117 }
118 if let Some(ref auths) = data.authorized_xml {
119 for a in auths {
120 inf_children.push(optional::build_aut_xml(a));
121 }
122 }
123
124 inf_children.extend(det_elements);
125
126 inf_children.push(total::build_total(
127 total_products,
128 &icms_totals,
129 &total::OtherTotals {
130 v_ipi: total_ipi,
131 v_pis: total_pis,
132 v_cofins: total_cofins,
133 v_ii: total_ii,
134 v_frete: total_frete,
135 v_seg: total_seg,
136 v_desc: total_desc,
137 v_outro: total_outro,
138 v_tot_trib: total_tot_trib,
139 v_ipi_devol: total_ipi_devol,
140 },
141 data.ret_trib.as_ref(),
142 data.issqn_tot.as_ref(),
143 data.is_tot.as_ref(),
144 data.ibs_cbs_tot.as_ref(),
145 ));
146
147 inf_children.push(transp::build_transp(data));
148
149 if let Some(ref billing) = data.billing {
150 inf_children.push(optional::build_cobr(billing));
151 }
152
153 inf_children.push(pag::build_pag(
154 &data.payments,
155 data.change_amount,
156 data.payment_card_details.as_deref(),
157 ));
158
159 if let Some(ref intermed) = data.intermediary {
160 inf_children.push(optional::build_intermediary(intermed));
161 }
162
163 let inf_adic = optional::build_inf_adic(data);
164 if !inf_adic.is_empty() {
165 inf_children.push(inf_adic);
166 }
167
168 if let Some(ref exp) = data.export {
169 inf_children.push(optional::build_export(exp));
170 }
171 if let Some(ref purchase) = data.purchase {
172 inf_children.push(optional::build_purchase(purchase));
173 }
174 if let Some(ref cana) = data.cana {
175 inf_children.push(optional::build_cana(cana));
176 }
177 if let Some(ref tech) = data.tech_responsible {
178 inf_children.push(optional::build_tech_responsible_with_key(tech, &access_key));
179 }
180 if let Some(ref agro) = data.agropecuario {
181 inf_children.push(optional::build_agropecuario(agro));
182 }
183
184 let inf_nfe = tag(
187 "infNFe",
188 &[("Id", &inf_nfe_id), ("versao", NFE_VERSION)],
189 TagContent::Children(inf_children),
190 );
191
192 let xml = format!(
193 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>{}",
194 tag(
195 "NFe",
196 &[("xmlns", NFE_NAMESPACE)],
197 TagContent::Children(vec![inf_nfe])
198 ),
199 );
200
201 Ok(InvoiceXmlResult { xml, access_key })
202}