1mod builders;
18mod csosn;
19mod cst;
20mod cst_xml;
21mod data;
22mod totals;
23
24use crate::format_utils::format_cents_or_none;
25use crate::newtypes::{Cents, Rate};
26
27fn accum(current: Cents, value: Option<Cents>) -> Cents {
31 current + value.unwrap_or(Cents(0))
32}
33
34fn accum_raw(current: i64, value: Option<i64>) -> i64 {
36 current + value.unwrap_or(0)
37}
38
39fn fc2(v: Option<Cents>) -> Option<String> {
41 format_cents_or_none(v.map(|c| c.0), 2)
42}
43
44fn fc4(v: Option<Rate>) -> Option<String> {
46 format_cents_or_none(v.map(|r| r.0), 4)
47}
48
49fn fc4_raw(v: Option<i64>) -> Option<String> {
51 format_cents_or_none(v, 4)
52}
53
54#[derive(Debug, Clone)]
60#[non_exhaustive]
61pub enum IcmsVariant {
62 Cst(Box<IcmsCst>),
64 Csosn(Box<IcmsCsosn>),
66}
67
68impl From<IcmsCst> for IcmsVariant {
69 fn from(cst: IcmsCst) -> Self {
70 Self::Cst(Box::new(cst))
71 }
72}
73
74impl From<IcmsCsosn> for IcmsVariant {
75 fn from(csosn: IcmsCsosn) -> Self {
76 Self::Csosn(Box::new(csosn))
77 }
78}
79
80pub use builders::{
83 build_icms_part_xml, build_icms_st_xml, build_icms_uf_dest_xml, build_icms_xml,
84};
85pub use csosn::{IcmsCsosn, build_icms_csosn_xml};
86pub use cst::IcmsCst;
87pub use cst_xml::build_icms_cst_xml;
88pub use data::{IcmsPartData, IcmsStData, IcmsUfDestData};
89pub use totals::{IcmsTotals, create_icms_totals, merge_icms_totals};
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::newtypes::{Cents, Rate};
95
96 #[test]
99 fn icms_totals_builder_v_icms_uf_remet() {
100 let t = IcmsTotals::new().v_icms_uf_remet(Cents(500));
101 assert_eq!(t.v_icms_uf_remet, Cents(500));
102 }
103
104 #[test]
105 fn icms_totals_builder_v_icms_mono() {
106 let t = IcmsTotals::new().v_icms_mono(Cents(300));
107 assert_eq!(t.v_icms_mono, Cents(300));
108 }
109
110 #[test]
111 fn icms_totals_builder_v_icms_mono_reten() {
112 let t = IcmsTotals::new().v_icms_mono_reten(Cents(200));
113 assert_eq!(t.v_icms_mono_reten, Cents(200));
114 }
115
116 #[test]
117 fn icms_totals_builder_v_icms_mono_ret() {
118 let t = IcmsTotals::new().v_icms_mono_ret(Cents(100));
119 assert_eq!(t.v_icms_mono_ret, Cents(100));
120 }
121
122 #[test]
123 fn icms_totals_builder_ind_deduz_deson() {
124 let t = IcmsTotals::new().ind_deduz_deson(true);
125 assert!(t.ind_deduz_deson);
126 let t2 = IcmsTotals::new().ind_deduz_deson(false);
127 assert!(!t2.ind_deduz_deson);
128 }
129
130 #[test]
133 fn cst_code_cst30() {
134 let cst = IcmsCst::Cst30 {
135 orig: "0".into(),
136 mod_bc_st: "4".into(),
137 p_mva_st: None,
138 p_red_bc_st: None,
139 v_bc_st: Cents(1000),
140 p_icms_st: Rate(1800),
141 v_icms_st: Cents(180),
142 v_bc_fcp_st: None,
143 p_fcp_st: None,
144 v_fcp_st: None,
145 v_icms_deson: None,
146 mot_des_icms: None,
147 ind_deduz_deson: None,
148 };
149 assert_eq!(cst.cst_code(), "30");
150 }
151
152 #[test]
153 fn cst_code_cst51() {
154 let cst = IcmsCst::Cst51 {
155 orig: "0".into(),
156 mod_bc: None,
157 p_red_bc: None,
158 c_benef_rbc: None,
159 v_bc: None,
160 p_icms: None,
161 v_icms_op: None,
162 p_dif: None,
163 v_icms_dif: None,
164 v_icms: None,
165 v_bc_fcp: None,
166 p_fcp: None,
167 v_fcp: None,
168 p_fcp_dif: None,
169 v_fcp_dif: None,
170 v_fcp_efet: None,
171 };
172 assert_eq!(cst.cst_code(), "51");
173 }
174
175 #[test]
176 fn cst_code_cst53() {
177 let cst = IcmsCst::Cst53 {
178 orig: "0".into(),
179 q_bc_mono: None,
180 ad_rem_icms: None,
181 v_icms_mono_op: None,
182 p_dif: None,
183 v_icms_mono_dif: None,
184 v_icms_mono: None,
185 };
186 assert_eq!(cst.cst_code(), "53");
187 }
188
189 #[test]
190 fn cst_code_cst61() {
191 let cst = IcmsCst::Cst61 {
192 orig: "0".into(),
193 q_bc_mono_ret: None,
194 ad_rem_icms_ret: Rate(100),
195 v_icms_mono_ret: Cents(50),
196 };
197 assert_eq!(cst.cst_code(), "61");
198 }
199
200 #[test]
203 fn csosn_code_102() {
204 let c = IcmsCsosn::Csosn102 {
205 orig: "0".into(),
206 csosn: "102".into(),
207 };
208 assert_eq!(c.csosn_code(), "102");
209 }
210
211 #[test]
212 fn csosn_code_103() {
213 let c = IcmsCsosn::Csosn103 {
214 orig: "0".into(),
215 csosn: "103".into(),
216 };
217 assert_eq!(c.csosn_code(), "103");
218 }
219
220 #[test]
221 fn csosn_code_201() {
222 let c = IcmsCsosn::Csosn201 {
223 orig: "0".into(),
224 csosn: "201".into(),
225 mod_bc_st: "4".into(),
226 p_mva_st: None,
227 p_red_bc_st: None,
228 v_bc_st: Cents(0),
229 p_icms_st: Rate(0),
230 v_icms_st: Cents(0),
231 v_bc_fcp_st: None,
232 p_fcp_st: None,
233 v_fcp_st: None,
234 p_cred_sn: None,
235 v_cred_icms_sn: None,
236 };
237 assert_eq!(c.csosn_code(), "201");
238 }
239
240 #[test]
241 fn csosn_code_202() {
242 let c = IcmsCsosn::Csosn202 {
243 orig: "0".into(),
244 csosn: "202".into(),
245 mod_bc_st: "4".into(),
246 p_mva_st: None,
247 p_red_bc_st: None,
248 v_bc_st: Cents(0),
249 p_icms_st: Rate(0),
250 v_icms_st: Cents(0),
251 v_bc_fcp_st: None,
252 p_fcp_st: None,
253 v_fcp_st: None,
254 };
255 assert_eq!(c.csosn_code(), "202");
256 }
257
258 #[test]
259 fn csosn_code_203() {
260 let c = IcmsCsosn::Csosn203 {
261 orig: "0".into(),
262 csosn: "203".into(),
263 mod_bc_st: "4".into(),
264 p_mva_st: None,
265 p_red_bc_st: None,
266 v_bc_st: Cents(0),
267 p_icms_st: Rate(0),
268 v_icms_st: Cents(0),
269 v_bc_fcp_st: None,
270 p_fcp_st: None,
271 v_fcp_st: None,
272 };
273 assert_eq!(c.csosn_code(), "203");
274 }
275
276 #[test]
277 fn csosn_code_300() {
278 let c = IcmsCsosn::Csosn300 {
279 orig: "0".into(),
280 csosn: "300".into(),
281 };
282 assert_eq!(c.csosn_code(), "300");
283 }
284
285 #[test]
286 fn csosn_code_400() {
287 let c = IcmsCsosn::Csosn400 {
288 orig: "0".into(),
289 csosn: "400".into(),
290 };
291 assert_eq!(c.csosn_code(), "400");
292 }
293
294 #[test]
295 fn csosn_code_500() {
296 let c = IcmsCsosn::Csosn500 {
297 orig: "0".into(),
298 csosn: "500".into(),
299 v_bc_st_ret: None,
300 p_st: None,
301 v_icms_substituto: None,
302 v_icms_st_ret: None,
303 v_bc_fcp_st_ret: None,
304 p_fcp_st_ret: None,
305 v_fcp_st_ret: None,
306 p_red_bc_efet: None,
307 v_bc_efet: None,
308 p_icms_efet: None,
309 v_icms_efet: None,
310 };
311 assert_eq!(c.csosn_code(), "500");
312 }
313
314 #[test]
315 fn csosn_code_900() {
316 let c = IcmsCsosn::Csosn900 {
317 orig: "0".into(),
318 csosn: "900".into(),
319 mod_bc: None,
320 v_bc: None,
321 p_red_bc: None,
322 p_icms: None,
323 v_icms: None,
324 mod_bc_st: None,
325 p_mva_st: None,
326 p_red_bc_st: None,
327 v_bc_st: None,
328 p_icms_st: None,
329 v_icms_st: None,
330 v_bc_fcp_st: None,
331 p_fcp_st: None,
332 v_fcp_st: None,
333 p_cred_sn: None,
334 v_cred_icms_sn: None,
335 };
336 assert_eq!(c.csosn_code(), "900");
337 }
338
339 #[test]
342 fn csosn102_empty_orig_omits_orig_field() {
343 let c = IcmsCsosn::Csosn102 {
344 orig: String::new(),
345 csosn: "102".into(),
346 };
347 let mut totals = IcmsTotals::new();
348 let (tag, fields) = build_icms_csosn_xml(&c, &mut totals).unwrap();
349 assert_eq!(tag, "ICMSSN102");
350 assert!(fields.iter().all(|f| f.name != "orig"));
352 }
353
354 #[test]
357 fn merge_icms_totals_propagates_ind_deduz_deson() {
358 let mut target = IcmsTotals::new();
359 assert!(!target.ind_deduz_deson);
360
361 let source = IcmsTotals::new()
362 .v_bc(Cents(1000))
363 .v_icms(Cents(180))
364 .v_icms_mono(Cents(50))
365 .v_icms_mono_reten(Cents(25))
366 .v_icms_mono_ret(Cents(10))
367 .v_icms_uf_remet(Cents(30))
368 .ind_deduz_deson(true);
369
370 merge_icms_totals(&mut target, &source);
371 assert!(target.ind_deduz_deson);
372 assert_eq!(target.v_bc, Cents(1000));
373 assert_eq!(target.v_icms, Cents(180));
374 assert_eq!(target.v_icms_mono, Cents(50));
375 assert_eq!(target.v_icms_mono_reten, Cents(25));
376 assert_eq!(target.v_icms_mono_ret, Cents(10));
377 assert_eq!(target.v_icms_uf_remet, Cents(30));
378 }
379
380 #[test]
381 fn merge_icms_totals_does_not_set_false_on_target() {
382 let mut target = IcmsTotals::new().ind_deduz_deson(true);
383 let source = IcmsTotals::new(); merge_icms_totals(&mut target, &source);
385 assert!(target.ind_deduz_deson);
387 }
388
389 #[test]
392 fn cst_code_cst00() {
393 let cst = IcmsCst::Cst00 {
394 orig: "0".into(),
395 mod_bc: "3".into(),
396 v_bc: Cents(10000),
397 p_icms: Rate(1800),
398 v_icms: Cents(1800),
399 p_fcp: None,
400 v_fcp: None,
401 };
402 assert_eq!(cst.cst_code(), "00");
403 }
404
405 #[test]
406 fn cst_code_cst02() {
407 let cst = IcmsCst::Cst02 {
408 orig: "0".into(),
409 q_bc_mono: None,
410 ad_rem_icms: Rate(100),
411 v_icms_mono: Cents(50),
412 };
413 assert_eq!(cst.cst_code(), "02");
414 }
415
416 #[test]
417 fn cst_code_cst10() {
418 let cst = IcmsCst::Cst10 {
419 orig: "0".into(),
420 mod_bc: "3".into(),
421 v_bc: Cents(10000),
422 p_icms: Rate(1800),
423 v_icms: Cents(1800),
424 v_bc_fcp: None,
425 p_fcp: None,
426 v_fcp: None,
427 mod_bc_st: "4".into(),
428 p_mva_st: None,
429 p_red_bc_st: None,
430 v_bc_st: Cents(10000),
431 p_icms_st: Rate(1800),
432 v_icms_st: Cents(1800),
433 v_bc_fcp_st: None,
434 p_fcp_st: None,
435 v_fcp_st: None,
436 v_icms_st_deson: None,
437 mot_des_icms_st: None,
438 };
439 assert_eq!(cst.cst_code(), "10");
440 }
441
442 #[test]
443 fn cst_code_cst15() {
444 let cst = IcmsCst::Cst15 {
445 orig: "0".into(),
446 q_bc_mono: None,
447 ad_rem_icms: Rate(100),
448 v_icms_mono: Cents(50),
449 q_bc_mono_reten: None,
450 ad_rem_icms_reten: Rate(80),
451 v_icms_mono_reten: Cents(40),
452 p_red_ad_rem: None,
453 mot_red_ad_rem: None,
454 };
455 assert_eq!(cst.cst_code(), "15");
456 }
457
458 #[test]
459 fn cst_code_cst20() {
460 let cst = IcmsCst::Cst20 {
461 orig: "0".into(),
462 mod_bc: "3".into(),
463 p_red_bc: Rate(5000),
464 v_bc: Cents(5000),
465 p_icms: Rate(1800),
466 v_icms: Cents(900),
467 v_bc_fcp: None,
468 p_fcp: None,
469 v_fcp: None,
470 v_icms_deson: None,
471 mot_des_icms: None,
472 ind_deduz_deson: None,
473 };
474 assert_eq!(cst.cst_code(), "20");
475 }
476
477 #[test]
478 fn cst_code_cst60() {
479 let cst = IcmsCst::Cst60 {
480 orig: "0".into(),
481 v_bc_st_ret: None,
482 p_st: None,
483 v_icms_substituto: None,
484 v_icms_st_ret: None,
485 v_bc_fcp_st_ret: None,
486 p_fcp_st_ret: None,
487 v_fcp_st_ret: None,
488 p_red_bc_efet: None,
489 v_bc_efet: None,
490 p_icms_efet: None,
491 v_icms_efet: None,
492 };
493 assert_eq!(cst.cst_code(), "60");
494 }
495
496 #[test]
497 fn cst_code_cst70() {
498 let cst = IcmsCst::Cst70 {
499 orig: "0".into(),
500 mod_bc: "3".into(),
501 p_red_bc: Rate(5000),
502 v_bc: Cents(5000),
503 p_icms: Rate(1800),
504 v_icms: Cents(900),
505 v_bc_fcp: None,
506 p_fcp: None,
507 v_fcp: None,
508 mod_bc_st: "4".into(),
509 p_mva_st: None,
510 p_red_bc_st: None,
511 v_bc_st: Cents(5000),
512 p_icms_st: Rate(1800),
513 v_icms_st: Cents(900),
514 v_bc_fcp_st: None,
515 p_fcp_st: None,
516 v_fcp_st: None,
517 v_icms_deson: None,
518 mot_des_icms: None,
519 ind_deduz_deson: None,
520 v_icms_st_deson: None,
521 mot_des_icms_st: None,
522 };
523 assert_eq!(cst.cst_code(), "70");
524 }
525
526 #[test]
527 fn cst_code_cst90() {
528 let cst = IcmsCst::Cst90 {
529 orig: "0".into(),
530 mod_bc: None,
531 v_bc: None,
532 p_red_bc: None,
533 c_benef_rbc: None,
534 p_icms: None,
535 v_icms_op: None,
536 p_dif: None,
537 v_icms_dif: None,
538 v_icms: None,
539 v_bc_fcp: None,
540 p_fcp: None,
541 v_fcp: None,
542 p_fcp_dif: None,
543 v_fcp_dif: None,
544 v_fcp_efet: None,
545 mod_bc_st: None,
546 p_mva_st: None,
547 p_red_bc_st: None,
548 v_bc_st: None,
549 p_icms_st: None,
550 v_icms_st: None,
551 v_bc_fcp_st: None,
552 p_fcp_st: None,
553 v_fcp_st: None,
554 v_icms_deson: None,
555 mot_des_icms: None,
556 ind_deduz_deson: None,
557 v_icms_st_deson: None,
558 mot_des_icms_st: None,
559 };
560 assert_eq!(cst.cst_code(), "90");
561 }
562
563 #[test]
566 fn csosn_code_101() {
567 let c = IcmsCsosn::Csosn101 {
568 orig: "0".into(),
569 csosn: "101".into(),
570 p_cred_sn: Rate(150),
571 v_cred_icms_sn: Cents(30),
572 };
573 assert_eq!(c.csosn_code(), "101");
574 }
575}