From 058fb62849c63f00223e5dfcf7ed3ee357782e6b Mon Sep 17 00:00:00 2001 From: dankito Date: Mon, 18 Nov 2024 17:04:07 +0100 Subject: [PATCH] Implemented mapping XML validation result items --- .../invoicing/validation/EInvoiceValidator.kt | 41 +++++++++++++++++-- .../validation/InvoiceValidationResult.kt | 26 ++++++++++-- .../validation/ValidationResultItem.kt | 12 ++++++ .../validation/ValidationResultSeverity.kt | 9 ++++ .../net/codinux/invoicing/Demonstration.kt | 2 +- .../validation/EInvoiceValidatorTest.kt | 23 ++++++++--- 6 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultItem.kt create mode 100644 e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultSeverity.kt diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/EInvoiceValidator.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/EInvoiceValidator.kt index c29cda1..8371104 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/EInvoiceValidator.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/EInvoiceValidator.kt @@ -2,19 +2,54 @@ package net.codinux.invoicing.validation import org.mustangproject.validator.ZUGFeRDValidator import java.io.File +import java.lang.reflect.Field class EInvoiceValidator { + companion object { + private val SectionField = getPrivateField("section") + private val CriterionField = getPrivateField("criterion") + private val StacktraceField = getPrivateField("stacktrace") + + private fun getPrivateField(fieldName: String): Field? = try { + org.mustangproject.validator.ValidationResultItem::class.java.getDeclaredField(fieldName).apply { + trySetAccessible() + } + } catch (e: Throwable) { + null + } + } + + fun validate(fileToValidate: File, disableNotices: Boolean = false): InvoiceValidationResult { - val validator = ZUGFeRDValidator() + val validator = object : ZUGFeRDValidator() { + fun getContext() = this.context + } + if (disableNotices) { validator.disableNotices() } - // TODO: this is far from ideal to have to report only as string and not the single failures as objects val report = validator.validate(fileToValidate.absolutePath) - return InvoiceValidationResult(validator.wasCompletelyValid(), report) + val context = validator.getContext() + val isXmlValid = context.isValid + val xmlValidationResults = context.results.map { mapValidationResultItem(it) } + + // TODO: currently it's not possible to get PDF validation result as for PDF validation the same context object + // is used and then in a private method before XML validation context.clear() gets called removing all PDF validation results + + return InvoiceValidationResult(validator.wasCompletelyValid(), isXmlValid, xmlValidationResults, report) + } + + private fun mapValidationResultItem(item: org.mustangproject.validator.ValidationResultItem) = + ValidationResultItem(mapSeverity(item), item.message, item.location, SectionField?.get(item) as? Int, CriterionField?.get(item) as? String, StacktraceField?.get(item) as? String) + + private fun mapSeverity(item: org.mustangproject.validator.ValidationResultItem): ValidationResultSeverity { + var name = item.severity.name + name = name.first().uppercase() + name.substring(1) + + return ValidationResultSeverity.valueOf(name) } } \ No newline at end of file diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/InvoiceValidationResult.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/InvoiceValidationResult.kt index 44061a8..69817cf 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/InvoiceValidationResult.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/InvoiceValidationResult.kt @@ -1,11 +1,31 @@ package net.codinux.invoicing.validation class InvoiceValidationResult( + /** + * If XML and, if supplied, PDF is valid. + */ val isValid: Boolean, - val report: String + /** + * If eInvoice XML is valid. If PDF is invalid, then [isValid] is false, but [isXmlValid] still can be true. + */ + val isXmlValid: Boolean, + /** + * + */ + val xmlValidationResults: List, + /** + * The validation report as a custom XML. + */ + val reportAsXml: String ) { + val countXmlNotices: Int by lazy { xmlValidationResults.count { it.severity == ValidationResultSeverity.Notice } } + + val countXmlErrors: Int by lazy { xmlValidationResults.count { it.severity == ValidationResultSeverity.Error } } + + val countXmlFatalOrExcepton: Int by lazy { xmlValidationResults.count { it.severity == ValidationResultSeverity.Fatal || it.severity == ValidationResultSeverity.Exception } } + override fun toString() = when (isValid) { - true -> "Valid: $report" - false -> "Invalid: $report" + true -> "Valid: $reportAsXml" + false -> "Invalid: $reportAsXml" } } \ No newline at end of file diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultItem.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultItem.kt new file mode 100644 index 0000000..05b6417 --- /dev/null +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultItem.kt @@ -0,0 +1,12 @@ +package net.codinux.invoicing.validation + +class ValidationResultItem( + val severity: ValidationResultSeverity, + val message: String, + val location: String?, + val section: Int?, + val criterion: String?, + val stacktrace: String? = null +) { + override fun toString() = "$severity: $message" +} \ No newline at end of file diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultSeverity.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultSeverity.kt new file mode 100644 index 0000000..71ccac2 --- /dev/null +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/validation/ValidationResultSeverity.kt @@ -0,0 +1,9 @@ +package net.codinux.invoicing.validation + +enum class ValidationResultSeverity { + Notice, + Warning, + Error, + Fatal, + Exception +} \ No newline at end of file diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/Demonstration.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/Demonstration.kt index 218030c..8c276fa 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/Demonstration.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/Demonstration.kt @@ -42,7 +42,7 @@ class Demonstration { val result = validator.validate(invoiceFile) println("Is valid? ${result.isValid}") - println(result.report) + println(result.reportAsXml) } diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/validation/EInvoiceValidatorTest.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/validation/EInvoiceValidatorTest.kt index 618712d..5ebf18f 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/validation/EInvoiceValidatorTest.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/validation/EInvoiceValidatorTest.kt @@ -1,9 +1,7 @@ package net.codinux.invoicing.validation import assertk.assertThat -import assertk.assertions.isFalse -import assertk.assertions.isNotEmpty -import assertk.assertions.isTrue +import assertk.assertions.* import java.io.File import kotlin.test.Test @@ -19,7 +17,11 @@ class EInvoiceValidatorTest { val result = underTest.validate(testFile) assertThat(result.isValid).isFalse() // TODO: add required properties to XRechnung.xml - assertThat(result.report).isNotEmpty() + assertThat(result.reportAsXml).isNotEmpty() + assertThat(result.xmlValidationResults).hasSize(3) + assertThat(result.countXmlNotices).isEqualTo(0) + assertThat(result.countXmlErrors).isEqualTo(3) + assertThat(result.countXmlFatalOrExcepton).isEqualTo(0) } @Test @@ -28,8 +30,13 @@ class EInvoiceValidatorTest { val result = underTest.validate(testFile) + assertThat(result.isValid).isTrue() - assertThat(result.report).isNotEmpty() + assertThat(result.reportAsXml).isNotEmpty() + assertThat(result.xmlValidationResults).hasSize(5) + assertThat(result.countXmlNotices).isEqualTo(5) + assertThat(result.countXmlErrors).isEqualTo(0) + assertThat(result.countXmlFatalOrExcepton).isEqualTo(0) } @Test @@ -39,7 +46,11 @@ class EInvoiceValidatorTest { val result = underTest.validate(testFile) assertThat(result.isValid).isTrue() - assertThat(result.report).isNotEmpty() + assertThat(result.reportAsXml).isNotEmpty() + assertThat(result.xmlValidationResults).hasSize(5) + assertThat(result.countXmlNotices).isEqualTo(5) + assertThat(result.countXmlErrors).isEqualTo(0) + assertThat(result.countXmlFatalOrExcepton).isEqualTo(0) }