1use crate::FiscalError;
4use crate::format_utils::{format_cents, format_decimal};
5use crate::newtypes::{Cents, Rate, Rate4};
6use crate::tax_icms::{self, IcmsCsosn, IcmsCst, IcmsTotals, IcmsVariant};
7use crate::tax_pis_cofins_ipi::{self, CofinsData, IiData, IpiData, PisData};
8use crate::types::{InvoiceBuildData, InvoiceItemData, TaxRegime};
9use crate::xml_utils::{TagContent, tag};
10
11#[derive(Debug, Clone)]
13pub struct DetResult {
14 pub xml: String,
16 pub icms_totals: IcmsTotals,
18 pub v_ipi: i64,
20 pub v_pis: i64,
22 pub v_cofins: i64,
24 pub v_ii: i64,
26}
27
28fn build_icms_variant(
30 item: &InvoiceItemData,
31 is_simples: bool,
32) -> Result<IcmsVariant, FiscalError> {
33 let orig = item.orig.clone().unwrap_or_else(|| "0".to_string());
34
35 if is_simples {
36 let csosn_code = if item.icms_cst.is_empty() {
37 "102"
38 } else {
39 item.icms_cst.as_str()
40 };
41
42 let csosn = match csosn_code {
43 "101" => IcmsCsosn::Csosn101 {
44 orig,
45 csosn: csosn_code.to_string(),
46 p_cred_sn: item.icms_p_cred_sn.ok_or_else(|| {
47 FiscalError::MissingRequiredField {
48 field: "pCredSN".to_string(),
49 }
50 })?,
51 v_cred_icms_sn: item.icms_v_cred_icms_sn.ok_or_else(|| {
52 FiscalError::MissingRequiredField {
53 field: "vCredICMSSN".to_string(),
54 }
55 })?,
56 },
57 "102" | "103" | "300" | "400" => IcmsCsosn::Csosn102 {
58 orig,
59 csosn: csosn_code.to_string(),
60 },
61 "201" => IcmsCsosn::Csosn201 {
62 orig,
63 csosn: csosn_code.to_string(),
64 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()).ok_or_else(|| {
65 FiscalError::MissingRequiredField {
66 field: "modBCST".to_string(),
67 }
68 })?,
69 p_mva_st: item.icms_p_mva_st,
70 p_red_bc_st: item.icms_red_bc_st,
71 v_bc_st: item
72 .icms_v_bc_st
73 .ok_or_else(|| FiscalError::MissingRequiredField {
74 field: "vBCST".to_string(),
75 })?,
76 p_icms_st: item.icms_p_icms_st.ok_or_else(|| {
77 FiscalError::MissingRequiredField {
78 field: "pICMSST".to_string(),
79 }
80 })?,
81 v_icms_st: item.icms_v_icms_st.ok_or_else(|| {
82 FiscalError::MissingRequiredField {
83 field: "vICMSST".to_string(),
84 }
85 })?,
86 v_bc_fcp_st: item.icms_v_bc_fcp_st,
87 p_fcp_st: item.icms_p_fcp_st,
88 v_fcp_st: item.icms_v_fcp_st,
89 p_cred_sn: item.icms_p_cred_sn,
90 v_cred_icms_sn: item.icms_v_cred_icms_sn,
91 },
92 "202" | "203" => IcmsCsosn::Csosn202 {
93 orig,
94 csosn: csosn_code.to_string(),
95 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()).ok_or_else(|| {
96 FiscalError::MissingRequiredField {
97 field: "modBCST".to_string(),
98 }
99 })?,
100 p_mva_st: item.icms_p_mva_st,
101 p_red_bc_st: item.icms_red_bc_st,
102 v_bc_st: item
103 .icms_v_bc_st
104 .ok_or_else(|| FiscalError::MissingRequiredField {
105 field: "vBCST".to_string(),
106 })?,
107 p_icms_st: item.icms_p_icms_st.ok_or_else(|| {
108 FiscalError::MissingRequiredField {
109 field: "pICMSST".to_string(),
110 }
111 })?,
112 v_icms_st: item.icms_v_icms_st.ok_or_else(|| {
113 FiscalError::MissingRequiredField {
114 field: "vICMSST".to_string(),
115 }
116 })?,
117 v_bc_fcp_st: item.icms_v_bc_fcp_st,
118 p_fcp_st: item.icms_p_fcp_st,
119 v_fcp_st: item.icms_v_fcp_st,
120 },
121 "500" => IcmsCsosn::Csosn500 {
122 orig,
123 csosn: csosn_code.to_string(),
124 v_bc_st_ret: None,
125 p_st: None,
126 v_icms_substituto: item.icms_v_icms_substituto,
127 v_icms_st_ret: None,
128 v_bc_fcp_st_ret: None,
129 p_fcp_st_ret: None,
130 v_fcp_st_ret: None,
131 p_red_bc_efet: None,
132 v_bc_efet: None,
133 p_icms_efet: None,
134 v_icms_efet: None,
135 },
136 "900" => IcmsCsosn::Csosn900 {
137 orig,
138 csosn: csosn_code.to_string(),
139 mod_bc: item.icms_mod_bc.map(|v| v.to_string()),
140 v_bc: Some(item.total_price),
141 p_red_bc: item.icms_red_bc,
142 p_icms: Some(item.icms_rate),
143 v_icms: Some(item.icms_amount),
144 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()),
145 p_mva_st: item.icms_p_mva_st,
146 p_red_bc_st: item.icms_red_bc_st,
147 v_bc_st: item.icms_v_bc_st,
148 p_icms_st: item.icms_p_icms_st,
149 v_icms_st: item.icms_v_icms_st,
150 v_bc_fcp_st: item.icms_v_bc_fcp_st,
151 p_fcp_st: item.icms_p_fcp_st,
152 v_fcp_st: item.icms_v_fcp_st,
153 p_cred_sn: item.icms_p_cred_sn,
154 v_cred_icms_sn: item.icms_v_cred_icms_sn,
155 },
156 other => return Err(FiscalError::UnsupportedIcmsCsosn(other.to_string())),
157 };
158 Ok(csosn.into())
159 } else {
160 let cst_code = item.icms_cst.as_str();
161 let cst = match cst_code {
162 "00" => IcmsCst::Cst00 {
163 orig,
164 mod_bc: item
165 .icms_mod_bc
166 .map(|v| v.to_string())
167 .unwrap_or_else(|| "3".to_string()),
168 v_bc: item.total_price,
169 p_icms: item.icms_rate,
170 v_icms: item.icms_amount,
171 p_fcp: item.icms_p_fcp,
172 v_fcp: item.icms_v_fcp,
173 },
174 "10" => IcmsCst::Cst10 {
175 orig,
176 mod_bc: item
177 .icms_mod_bc
178 .map(|v| v.to_string())
179 .unwrap_or_else(|| "3".to_string()),
180 v_bc: item.total_price,
181 p_icms: item.icms_rate,
182 v_icms: item.icms_amount,
183 v_bc_fcp: item.icms_v_bc_fcp,
184 p_fcp: item.icms_p_fcp,
185 v_fcp: item.icms_v_fcp,
186 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()).ok_or_else(|| {
187 FiscalError::MissingRequiredField {
188 field: "modBCST".to_string(),
189 }
190 })?,
191 p_mva_st: item.icms_p_mva_st,
192 p_red_bc_st: item.icms_red_bc_st,
193 v_bc_st: item
194 .icms_v_bc_st
195 .ok_or_else(|| FiscalError::MissingRequiredField {
196 field: "vBCST".to_string(),
197 })?,
198 p_icms_st: item.icms_p_icms_st.ok_or_else(|| {
199 FiscalError::MissingRequiredField {
200 field: "pICMSST".to_string(),
201 }
202 })?,
203 v_icms_st: item.icms_v_icms_st.ok_or_else(|| {
204 FiscalError::MissingRequiredField {
205 field: "vICMSST".to_string(),
206 }
207 })?,
208 v_bc_fcp_st: item.icms_v_bc_fcp_st,
209 p_fcp_st: item.icms_p_fcp_st,
210 v_fcp_st: item.icms_v_fcp_st,
211 v_icms_st_deson: None,
212 mot_des_icms_st: None,
213 },
214 "20" => IcmsCst::Cst20 {
215 orig,
216 mod_bc: item
217 .icms_mod_bc
218 .map(|v| v.to_string())
219 .unwrap_or_else(|| "3".to_string()),
220 p_red_bc: item.icms_red_bc.unwrap_or(Rate(0)),
221 v_bc: item.total_price,
222 p_icms: item.icms_rate,
223 v_icms: item.icms_amount,
224 v_bc_fcp: item.icms_v_bc_fcp,
225 p_fcp: item.icms_p_fcp,
226 v_fcp: item.icms_v_fcp,
227 v_icms_deson: item.icms_v_icms_deson,
228 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
229 ind_deduz_deson: None,
230 },
231 "30" => IcmsCst::Cst30 {
232 orig,
233 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()).ok_or_else(|| {
234 FiscalError::MissingRequiredField {
235 field: "modBCST".to_string(),
236 }
237 })?,
238 p_mva_st: item.icms_p_mva_st,
239 p_red_bc_st: item.icms_red_bc_st,
240 v_bc_st: item
241 .icms_v_bc_st
242 .ok_or_else(|| FiscalError::MissingRequiredField {
243 field: "vBCST".to_string(),
244 })?,
245 p_icms_st: item.icms_p_icms_st.ok_or_else(|| {
246 FiscalError::MissingRequiredField {
247 field: "pICMSST".to_string(),
248 }
249 })?,
250 v_icms_st: item.icms_v_icms_st.ok_or_else(|| {
251 FiscalError::MissingRequiredField {
252 field: "vICMSST".to_string(),
253 }
254 })?,
255 v_bc_fcp_st: item.icms_v_bc_fcp_st,
256 p_fcp_st: item.icms_p_fcp_st,
257 v_fcp_st: item.icms_v_fcp_st,
258 v_icms_deson: item.icms_v_icms_deson,
259 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
260 ind_deduz_deson: None,
261 },
262 "40" => IcmsCst::Cst40 {
263 orig,
264 v_icms_deson: item.icms_v_icms_deson,
265 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
266 ind_deduz_deson: None,
267 },
268 "41" => IcmsCst::Cst41 {
269 orig,
270 v_icms_deson: item.icms_v_icms_deson,
271 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
272 ind_deduz_deson: None,
273 },
274 "50" => IcmsCst::Cst50 {
275 orig,
276 v_icms_deson: item.icms_v_icms_deson,
277 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
278 ind_deduz_deson: None,
279 },
280 "51" => IcmsCst::Cst51 {
281 orig,
282 mod_bc: item.icms_mod_bc.map(|v| v.to_string()),
283 p_red_bc: item.icms_red_bc,
284 c_benef_rbc: None,
285 v_bc: Some(item.total_price),
286 p_icms: Some(item.icms_rate),
287 v_icms_op: None,
288 p_dif: None,
289 v_icms_dif: None,
290 v_icms: Some(item.icms_amount),
291 v_bc_fcp: item.icms_v_bc_fcp,
292 p_fcp: item.icms_p_fcp,
293 v_fcp: item.icms_v_fcp,
294 p_fcp_dif: None,
295 v_fcp_dif: None,
296 v_fcp_efet: None,
297 },
298 "60" => IcmsCst::Cst60 {
299 orig,
300 v_bc_st_ret: None,
301 p_st: None,
302 v_icms_substituto: item.icms_v_icms_substituto,
303 v_icms_st_ret: None,
304 v_bc_fcp_st_ret: None,
305 p_fcp_st_ret: None,
306 v_fcp_st_ret: None,
307 p_red_bc_efet: None,
308 v_bc_efet: None,
309 p_icms_efet: None,
310 v_icms_efet: None,
311 },
312 "70" => IcmsCst::Cst70 {
313 orig,
314 mod_bc: item
315 .icms_mod_bc
316 .map(|v| v.to_string())
317 .unwrap_or_else(|| "3".to_string()),
318 p_red_bc: item.icms_red_bc.unwrap_or(Rate(0)),
319 v_bc: item.total_price,
320 p_icms: item.icms_rate,
321 v_icms: item.icms_amount,
322 v_bc_fcp: item.icms_v_bc_fcp,
323 p_fcp: item.icms_p_fcp,
324 v_fcp: item.icms_v_fcp,
325 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()).ok_or_else(|| {
326 FiscalError::MissingRequiredField {
327 field: "modBCST".to_string(),
328 }
329 })?,
330 p_mva_st: item.icms_p_mva_st,
331 p_red_bc_st: item.icms_red_bc_st,
332 v_bc_st: item
333 .icms_v_bc_st
334 .ok_or_else(|| FiscalError::MissingRequiredField {
335 field: "vBCST".to_string(),
336 })?,
337 p_icms_st: item.icms_p_icms_st.ok_or_else(|| {
338 FiscalError::MissingRequiredField {
339 field: "pICMSST".to_string(),
340 }
341 })?,
342 v_icms_st: item.icms_v_icms_st.ok_or_else(|| {
343 FiscalError::MissingRequiredField {
344 field: "vICMSST".to_string(),
345 }
346 })?,
347 v_bc_fcp_st: item.icms_v_bc_fcp_st,
348 p_fcp_st: item.icms_p_fcp_st,
349 v_fcp_st: item.icms_v_fcp_st,
350 v_icms_deson: item.icms_v_icms_deson,
351 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
352 ind_deduz_deson: None,
353 v_icms_st_deson: None,
354 mot_des_icms_st: None,
355 },
356 "90" => IcmsCst::Cst90 {
357 orig,
358 mod_bc: item.icms_mod_bc.map(|v| v.to_string()),
359 v_bc: Some(item.total_price),
360 p_red_bc: item.icms_red_bc,
361 c_benef_rbc: None,
362 p_icms: Some(item.icms_rate),
363 v_icms_op: None,
364 p_dif: None,
365 v_icms_dif: None,
366 v_icms: Some(item.icms_amount),
367 v_bc_fcp: item.icms_v_bc_fcp,
368 p_fcp: item.icms_p_fcp,
369 v_fcp: item.icms_v_fcp,
370 p_fcp_dif: None,
371 v_fcp_dif: None,
372 v_fcp_efet: None,
373 mod_bc_st: item.icms_mod_bc_st.map(|v| v.to_string()),
374 p_mva_st: item.icms_p_mva_st,
375 p_red_bc_st: item.icms_red_bc_st,
376 v_bc_st: item.icms_v_bc_st,
377 p_icms_st: item.icms_p_icms_st,
378 v_icms_st: item.icms_v_icms_st,
379 v_bc_fcp_st: item.icms_v_bc_fcp_st,
380 p_fcp_st: item.icms_p_fcp_st,
381 v_fcp_st: item.icms_v_fcp_st,
382 v_icms_deson: item.icms_v_icms_deson,
383 mot_des_icms: item.icms_mot_des_icms.map(|v| v.to_string()),
384 ind_deduz_deson: None,
385 v_icms_st_deson: None,
386 mot_des_icms_st: None,
387 },
388 other => return Err(FiscalError::UnsupportedIcmsCst(other.to_string())),
389 };
390 Ok(cst.into())
391 }
392}
393
394pub(crate) fn build_det(
396 item: &InvoiceItemData,
397 data: &InvoiceBuildData,
398) -> Result<DetResult, FiscalError> {
399 let is_simples = matches!(
400 data.issuer.tax_regime,
401 TaxRegime::SimplesNacional | TaxRegime::SimplesExcess
402 );
403
404 let icms_variant = build_icms_variant(item, is_simples)?;
406 let mut icms_totals = IcmsTotals::default();
407 let icms_xml = tax_icms::build_icms_xml(&icms_variant, &mut icms_totals)?;
408
409 let pis_xml = tax_pis_cofins_ipi::build_pis_xml(&PisData {
411 cst: item.pis_cst.clone(),
412 v_bc: item.pis_v_bc.or(Some(Cents(0))),
413 p_pis: item.pis_p_pis.or(Some(Rate4(0))),
414 v_pis: item.pis_v_pis.or(Some(Cents(0))),
415 q_bc_prod: item.pis_q_bc_prod,
416 v_aliq_prod: item.pis_v_aliq_prod,
417 });
418
419 let cofins_xml = tax_pis_cofins_ipi::build_cofins_xml(&CofinsData {
421 cst: item.cofins_cst.clone(),
422 v_bc: item.cofins_v_bc.or(Some(Cents(0))),
423 p_cofins: item.cofins_p_cofins.or(Some(Rate4(0))),
424 v_cofins: item.cofins_v_cofins.or(Some(Cents(0))),
425 q_bc_prod: item.cofins_q_bc_prod,
426 v_aliq_prod: item.cofins_v_aliq_prod,
427 });
428
429 let mut ipi_xml = String::new();
431 let mut v_ipi = 0i64;
432 if let Some(ref ipi_cst) = item.ipi_cst {
433 ipi_xml = tax_pis_cofins_ipi::build_ipi_xml(&IpiData {
434 cst: ipi_cst.clone(),
435 c_enq: item.ipi_c_enq.clone().unwrap_or_else(|| "999".to_string()),
436 v_bc: item.ipi_v_bc,
437 p_ipi: item.ipi_p_ipi,
438 v_ipi: item.ipi_v_ipi,
439 q_unid: item.ipi_q_unid,
440 v_unid: item.ipi_v_unid,
441 ..IpiData::default()
442 });
443 v_ipi = item.ipi_v_ipi.map(|c| c.0).unwrap_or(0);
444 }
445
446 let mut ii_xml = String::new();
448 let mut v_ii = 0i64;
449 if let Some(ii_vbc) = item.ii_v_bc {
450 ii_xml = tax_pis_cofins_ipi::build_ii_xml(&IiData {
451 v_bc: ii_vbc,
452 v_desp_adu: item.ii_v_desp_adu.unwrap_or(Cents(0)),
453 v_ii: item.ii_v_ii.unwrap_or(Cents(0)),
454 v_iof: item.ii_v_iof.unwrap_or(Cents(0)),
455 });
456 v_ii = item.ii_v_ii.map(|c| c.0).unwrap_or(0);
457 }
458
459 let prod_options = build_prod_options(item);
461
462 let det_extras = build_det_extras(item);
464
465 let mut imposto_children: Vec<String> = vec![icms_xml];
467 if !ipi_xml.is_empty() {
468 imposto_children.push(ipi_xml);
469 }
470 imposto_children.push(pis_xml);
471 imposto_children.push(cofins_xml);
472 if !ii_xml.is_empty() {
473 imposto_children.push(ii_xml);
474 }
475
476 let fc2 = |c: i64| format_cents(c, 2);
478 let fc10 = |c: i64| format_cents(c, 10);
479 let fd4 = |v: f64| format_decimal(v, 4);
480
481 let mut prod_children = vec![
482 tag("cProd", &[], TagContent::Text(&item.product_code)),
483 tag(
484 "cEAN",
485 &[],
486 TagContent::Text(item.c_ean.as_deref().unwrap_or("SEM GTIN")),
487 ),
488 tag("xProd", &[], TagContent::Text(&item.description)),
489 tag("NCM", &[], TagContent::Text(&item.ncm)),
490 ];
491 if let Some(ref cest) = item.cest {
492 prod_children.push(tag("CEST", &[], TagContent::Text(cest)));
493 }
494 prod_children.extend([
495 tag("CFOP", &[], TagContent::Text(&item.cfop)),
496 tag("uCom", &[], TagContent::Text(&item.unit_of_measure)),
497 tag("qCom", &[], TagContent::Text(&fd4(item.quantity))),
498 tag("vUnCom", &[], TagContent::Text(&fc10(item.unit_price.0))),
499 tag("vProd", &[], TagContent::Text(&fc2(item.total_price.0))),
500 tag(
501 "cEANTrib",
502 &[],
503 TagContent::Text(item.c_ean_trib.as_deref().unwrap_or("SEM GTIN")),
504 ),
505 tag("uTrib", &[], TagContent::Text(&item.unit_of_measure)),
506 tag("qTrib", &[], TagContent::Text(&fd4(item.quantity))),
507 tag("vUnTrib", &[], TagContent::Text(&fc10(item.unit_price.0))),
508 ]);
509 if let Some(v) = item.v_frete {
510 prod_children.push(tag("vFrete", &[], TagContent::Text(&fc2(v.0))));
511 }
512 if let Some(v) = item.v_seg {
513 prod_children.push(tag("vSeg", &[], TagContent::Text(&fc2(v.0))));
514 }
515 if let Some(v) = item.v_desc {
516 prod_children.push(tag("vDesc", &[], TagContent::Text(&fc2(v.0))));
517 }
518 if let Some(v) = item.v_outro {
519 prod_children.push(tag("vOutro", &[], TagContent::Text(&fc2(v.0))));
520 }
521 prod_children.push(tag("indTot", &[], TagContent::Text("1")));
522 prod_children.extend(prod_options);
523
524 let nitem = item.item_number.to_string();
526 let mut det_children = vec![
527 tag("prod", &[], TagContent::Children(prod_children)),
528 tag("imposto", &[], TagContent::Children(imposto_children)),
529 ];
530 det_children.extend(det_extras);
531
532 let xml = tag(
533 "det",
534 &[("nItem", &nitem)],
535 TagContent::Children(det_children),
536 );
537
538 Ok(DetResult {
539 xml,
540 icms_totals,
541 v_ipi,
542 v_pis: item.pis_v_pis.map(|c| c.0).unwrap_or(0),
543 v_cofins: item.cofins_v_cofins.map(|c| c.0).unwrap_or(0),
544 v_ii,
545 })
546}
547
548fn build_prod_options(item: &InvoiceItemData) -> Vec<String> {
549 let mut opts = Vec::new();
550
551 if let Some(ref rastros) = item.rastro {
553 for r in rastros.iter().take(500) {
554 let mut rastro_children = vec![
555 tag("nLote", &[], TagContent::Text(&r.n_lote)),
556 tag("qLote", &[], TagContent::Text(&format_decimal(r.q_lote, 3))),
557 tag("dFab", &[], TagContent::Text(&r.d_fab)),
558 tag("dVal", &[], TagContent::Text(&r.d_val)),
559 ];
560 if let Some(ref agreg) = r.c_agreg {
561 rastro_children.push(tag("cAgreg", &[], TagContent::Text(agreg)));
562 }
563 opts.push(tag("rastro", &[], TagContent::Children(rastro_children)));
564 }
565 }
566
567 if let Some(ref v) = item.veic_prod {
569 opts.push(tag(
570 "veicProd",
571 &[],
572 TagContent::Children(vec![
573 tag("tpOp", &[], TagContent::Text(&v.tp_op)),
574 tag("chassi", &[], TagContent::Text(&v.chassi)),
575 tag("cCor", &[], TagContent::Text(&v.c_cor)),
576 tag("xCor", &[], TagContent::Text(&v.x_cor)),
577 tag("pot", &[], TagContent::Text(&v.pot)),
578 tag("cilin", &[], TagContent::Text(&v.cilin)),
579 tag("pesoL", &[], TagContent::Text(&v.peso_l)),
580 tag("pesoB", &[], TagContent::Text(&v.peso_b)),
581 tag("nSerie", &[], TagContent::Text(&v.n_serie)),
582 tag("tpComb", &[], TagContent::Text(&v.tp_comb)),
583 tag("nMotor", &[], TagContent::Text(&v.n_motor)),
584 tag("CMT", &[], TagContent::Text(&v.cmt)),
585 tag("dist", &[], TagContent::Text(&v.dist)),
586 tag("anoMod", &[], TagContent::Text(&v.ano_mod)),
587 tag("anoFab", &[], TagContent::Text(&v.ano_fab)),
588 tag("tpPint", &[], TagContent::Text(&v.tp_pint)),
589 tag("tpVeic", &[], TagContent::Text(&v.tp_veic)),
590 tag("espVeic", &[], TagContent::Text(&v.esp_veic)),
591 tag("VIN", &[], TagContent::Text(&v.vin)),
592 tag("condVeic", &[], TagContent::Text(&v.cond_veic)),
593 tag("cMod", &[], TagContent::Text(&v.c_mod)),
594 tag("cCorDENATRAN", &[], TagContent::Text(&v.c_cor_denatran)),
595 tag("lota", &[], TagContent::Text(&v.lota)),
596 tag("tpRest", &[], TagContent::Text(&v.tp_rest)),
597 ]),
598 ));
599 } else if let Some(ref m) = item.med {
600 let mut med_children = Vec::new();
601 if let Some(ref code) = m.c_prod_anvisa {
602 med_children.push(tag("cProdANVISA", &[], TagContent::Text(code)));
603 }
604 if let Some(ref reason) = m.x_motivo_isencao {
605 med_children.push(tag("xMotivoIsencao", &[], TagContent::Text(reason)));
606 }
607 med_children.push(tag(
608 "vPMC",
609 &[],
610 TagContent::Text(&format_cents(m.v_pmc.0, 2)),
611 ));
612 opts.push(tag("med", &[], TagContent::Children(med_children)));
613 } else if let Some(ref arms) = item.arma {
614 for a in arms.iter().take(500) {
615 opts.push(tag(
616 "arma",
617 &[],
618 TagContent::Children(vec![
619 tag("tpArma", &[], TagContent::Text(&a.tp_arma)),
620 tag("nSerie", &[], TagContent::Text(&a.n_serie)),
621 tag("nCano", &[], TagContent::Text(&a.n_cano)),
622 tag("descr", &[], TagContent::Text(&a.descr)),
623 ]),
624 ));
625 }
626 } else if let Some(ref recopi) = item.n_recopi {
627 if !recopi.is_empty() {
628 opts.push(tag("nRECOPI", &[], TagContent::Text(recopi)));
629 }
630 }
631
632 opts
633}
634
635fn build_det_extras(item: &InvoiceItemData) -> Vec<String> {
636 let mut extras = Vec::new();
637
638 if let Some(ref info) = item.inf_ad_prod {
639 extras.push(tag("infAdProd", &[], TagContent::Text(info)));
640 }
641
642 if let Some(ref obs) = item.obs_item {
643 let mut obs_children = Vec::new();
644 if let Some(ref cont) = obs.obs_cont {
645 obs_children.push(tag(
646 "obsCont",
647 &[("xCampo", &cont.x_campo)],
648 TagContent::Children(vec![tag("xTexto", &[], TagContent::Text(&cont.x_texto))]),
649 ));
650 }
651 if let Some(ref fisco) = obs.obs_fisco {
652 obs_children.push(tag(
653 "obsFisco",
654 &[("xCampo", &fisco.x_campo)],
655 TagContent::Children(vec![tag("xTexto", &[], TagContent::Text(&fisco.x_texto))]),
656 ));
657 }
658 extras.push(tag("obsItem", &[], TagContent::Children(obs_children)));
659 }
660
661 if let Some(ref dfe) = item.dfe_referenciado {
662 let mut dfe_children = vec![tag("chaveAcesso", &[], TagContent::Text(&dfe.chave_acesso))];
663 if let Some(ref n) = dfe.n_item {
664 dfe_children.push(tag("nItem", &[], TagContent::Text(n)));
665 }
666 extras.push(tag(
667 "DFeReferenciado",
668 &[],
669 TagContent::Children(dfe_children),
670 ));
671 }
672
673 extras
674}