Skip to main content

core_invoice/
validate.rs

1//! Semantic checks on the in-memory invoice.
2
3use crate::invoice::Invoice;
4use crate::report::Report;
5use crate::rules;
6
7/// Semantic checks on the in-memory invoice. Syntax (UBL/CII) lives in `core-invoice-formats`.
8pub fn validate(invoice: &Invoice) -> Report {
9    let core = rules::core_rules();
10    let extra = invoice.profile.extra_rules();
11    let mut report = Report {
12        profile_slug: invoice.profile.slug(),
13        rules_checked: core.len() + extra.len(),
14        ..Report::default()
15    };
16    for rule in core.iter().chain(extra) {
17        (rule.eval)(invoice, &mut report);
18    }
19    #[cfg(feature = "xrechnung")]
20    if crate::xrechnung::claimed(invoice) {
21        for rule in crate::xrechnung::RULES {
22            (rule.eval)(invoice, &mut report);
23        }
24        report.rules_checked += crate::xrechnung::RULES.len();
25    }
26    report.sort_stable();
27    report
28}
29
30#[cfg(all(test, not(feature = "xrechnung")))]
31mod tests {
32    use super::*;
33    use crate::invoice::{Invoice, Party};
34    use crate::profile::Profile;
35
36    #[test]
37    fn xrechnung_claim_without_feature_does_not_emit_br_de() {
38        let mut inv = Invoice::blank(
39            Profile::En16931,
40            "1",
41            "EUR",
42            Party::new("S", "DE"),
43            Party::new("B", "DE"),
44        );
45        inv.specification_id =
46            Some("urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0".into());
47        let report = validate(&inv);
48        assert!(
49            report
50                .findings
51                .iter()
52                .all(|f| !f.id.starts_with("BR-DE-") && f.id != "BR-TMP-2"),
53            "{report}"
54        );
55    }
56}