Made all service classes open

This commit is contained in:
dankito 2024-11-22 20:33:12 +01:00
parent 35a2a6c518
commit 0f97b8dae9
7 changed files with 75 additions and 71 deletions

View File

@ -6,12 +6,12 @@ import org.mustangproject.CII.CIIToUBL
import org.mustangproject.ZUGFeRD.ZUGFeRDVisualizer import org.mustangproject.ZUGFeRD.ZUGFeRDVisualizer
import java.io.File import java.io.File
class EInvoiceConverter { open class EInvoiceConverter {
fun convertInvoiceToHtml(invoice: Invoice, outputFile: File, language: ZUGFeRDVisualizer.Language = ZUGFeRDVisualizer.Language.DE) = open fun convertInvoiceToHtml(invoice: Invoice, outputFile: File, language: ZUGFeRDVisualizer.Language = ZUGFeRDVisualizer.Language.DE) =
convertInvoiceToHtml(createXRechnungXml(invoice), outputFile, language) convertInvoiceToHtml(createXRechnungXml(invoice), outputFile, language)
fun convertInvoiceToHtml(invoiceXml: String, outputFile: File, language: ZUGFeRDVisualizer.Language = ZUGFeRDVisualizer.Language.DE): String { open fun convertInvoiceToHtml(invoiceXml: String, outputFile: File, language: ZUGFeRDVisualizer.Language = ZUGFeRDVisualizer.Language.DE): String {
val xmlFile = File.createTempFile("Zugferd", ".xml") val xmlFile = File.createTempFile("Zugferd", ".xml")
.also { it.writeText(invoiceXml) } .also { it.writeText(invoiceXml) }
@ -32,12 +32,12 @@ class EInvoiceConverter {
/** /**
* Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language). * Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language).
*/ */
fun convertCiiToUbl(invoice: Invoice) = convertCiiToUbl(createXRechnungXml(invoice)) open fun convertCiiToUbl(invoice: Invoice) = convertCiiToUbl(createXRechnungXml(invoice))
/** /**
* Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language). * Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language).
*/ */
fun convertCiiToUbl(invoiceXml: String): String { open fun convertCiiToUbl(invoiceXml: String): String {
// TODO: extract a common method for this // TODO: extract a common method for this
val xmlFile = File.createTempFile("Zugferd", ".xml") val xmlFile = File.createTempFile("Zugferd", ".xml")
.also { it.writeText(invoiceXml) } .also { it.writeText(invoiceXml) }
@ -56,15 +56,15 @@ class EInvoiceConverter {
/** /**
* Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language). * Converts a CII (Cross Industry Invoice) invoice, e.g. a ZUGFeRD or Factur-X invoice, to UBL (Universal Business Language).
*/ */
fun convertCiiToUbl(xmlFile: File, outputFile: File) { open fun convertCiiToUbl(xmlFile: File, outputFile: File) {
val cii2Ubl = CIIToUBL() val cii2Ubl = CIIToUBL()
cii2Ubl.convert(xmlFile, outputFile) cii2Ubl.convert(xmlFile, outputFile)
} }
private fun createXRechnungXml(invoice: Invoice): String = EInvoiceCreator().createXRechnungXml(invoice) protected open fun createXRechnungXml(invoice: Invoice): String = EInvoiceCreator().createXRechnungXml(invoice)
private fun copyResource(resourceName: String, outputFile: File, outputFileExtension: String) { protected open fun copyResource(resourceName: String, outputFile: File, outputFileExtension: String) {
javaClass.classLoader.getResourceAsStream(resourceName).use { javaClass.classLoader.getResourceAsStream(resourceName).use {
it?.copyTo(File(outputFile.parentFile, outputFile.nameWithoutExtension + outputFileExtension).outputStream()) it?.copyTo(File(outputFile.parentFile, outputFile.nameWithoutExtension + outputFileExtension).outputStream())
} }

View File

@ -5,11 +5,11 @@ import net.codinux.invoicing.model.Invoice
import org.mustangproject.ZUGFeRD.* import org.mustangproject.ZUGFeRD.*
import java.io.File import java.io.File
class EInvoiceCreator( open class EInvoiceCreator(
private val mapper: MustangMapper = MustangMapper() protected open val mapper: MustangMapper = MustangMapper()
) { ) {
fun createXRechnungXml(invoice: Invoice): String { open fun createXRechnungXml(invoice: Invoice): String {
val provider = ZUGFeRD2PullProvider() val provider = ZUGFeRD2PullProvider()
provider.profile = Profiles.getByName("XRechnung") provider.profile = Profiles.getByName("XRechnung")
@ -20,9 +20,9 @@ class EInvoiceCreator(
/** /**
* Synonym for [createFacturXXml] (ZUGFeRD 2 is a synonym for Factur-X). * Synonym for [createFacturXXml] (ZUGFeRD 2 is a synonym for Factur-X).
*/ */
fun createZugferdXml(invoice: Invoice) = createFacturXXml(invoice) open fun createZugferdXml(invoice: Invoice) = createFacturXXml(invoice)
fun createFacturXXml(invoice: Invoice): String { open fun createFacturXXml(invoice: Invoice): String {
val exporter = ZUGFeRDExporterFromA3() val exporter = ZUGFeRDExporterFromA3()
.setProfile("EN16931") // required for XML? .setProfile("EN16931") // required for XML?
@ -32,9 +32,9 @@ class EInvoiceCreator(
/** /**
* Synonym for [createFacturXPdf] (ZUGFeRD 2 is a synonym for Factur-X). * Synonym for [createFacturXPdf] (ZUGFeRD 2 is a synonym for Factur-X).
*/ */
fun createZugferdPdf(invoice: Invoice, outputFile: File) = createFacturXPdf(invoice, outputFile) open fun createZugferdPdf(invoice: Invoice, outputFile: File) = createFacturXPdf(invoice, outputFile)
fun createFacturXPdf(invoice: Invoice, outputFile: File) { open fun createFacturXPdf(invoice: Invoice, outputFile: File) {
val xml = createFacturXXml(invoice) val xml = createFacturXXml(invoice)
val xmlFile = File.createTempFile(outputFile.nameWithoutExtension, ".xml") val xmlFile = File.createTempFile(outputFile.nameWithoutExtension, ".xml")
.also { it.writeText(xml) } .also { it.writeText(xml) }
@ -50,10 +50,10 @@ class EInvoiceCreator(
} }
fun attachInvoiceXmlToPdf(invoice: Invoice, pdfFile: File, outputFile: File) = open fun attachInvoiceXmlToPdf(invoice: Invoice, pdfFile: File, outputFile: File) =
attachInvoiceXmlToPdf(createFacturXXml(invoice), pdfFile, outputFile) attachInvoiceXmlToPdf(createFacturXXml(invoice), pdfFile, outputFile)
fun attachInvoiceXmlToPdf(invoiceXml: String, pdfFile: File, outputFile: File) { open fun attachInvoiceXmlToPdf(invoiceXml: String, pdfFile: File, outputFile: File) {
val exporter = ZUGFeRDExporterFromA3() val exporter = ZUGFeRDExporterFromA3()
.setZUGFeRDVersion(2) .setZUGFeRDVersion(2)
.setProfile("EN16931") // available values: MINIMUM, BASICWL, BASIC, CIUS, EN16931, EXTENDED, XRECHNUNG .setProfile("EN16931") // available values: MINIMUM, BASICWL, BASIC, CIUS, EN16931, EXTENDED, XRECHNUNG
@ -68,7 +68,7 @@ class EInvoiceCreator(
} }
private fun createXml(provider: IXMLProvider, invoice: Invoice): String { protected open fun createXml(provider: IXMLProvider, invoice: Invoice): String {
val transaction = mapper.mapToTransaction(invoice) val transaction = mapper.mapToTransaction(invoice)
provider.generateXML(transaction) provider.generateXML(transaction)

View File

@ -6,16 +6,16 @@ import net.codinux.log.logger
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.* import kotlin.io.path.*
class FilesystemInvoiceReader( open class FilesystemInvoiceReader(
private val eInvoiceReader: EInvoiceReader = EInvoiceReader() protected open val eInvoiceReader: EInvoiceReader = EInvoiceReader()
) { ) {
private val log by logger() private val log by logger()
fun readAllInvoicesOfDirectory(directory: Path, recursive: Boolean = false) = open fun readAllInvoicesOfDirectory(directory: Path, recursive: Boolean = false) =
readInvoicesFromFiles(collectFiles(directory, recursive)) readInvoicesFromFiles(collectFiles(directory, recursive))
private fun collectFiles(directory: Path, recursive: Boolean): List<Path> = buildList { protected open fun collectFiles(directory: Path, recursive: Boolean): List<Path> = buildList {
directory.listDirectoryEntries().forEach { child -> directory.listDirectoryEntries().forEach { child ->
if (child.isRegularFile()) { if (child.isRegularFile()) {
add(child) add(child)
@ -25,13 +25,13 @@ class FilesystemInvoiceReader(
} }
} }
fun readInvoicesFromFiles(vararg files: Path) = open fun readInvoicesFromFiles(vararg files: Path) =
readInvoicesFromFiles(files.toList()) readInvoicesFromFiles(files.toList())
fun readInvoicesFromFiles(files: List<Path>): List<InvoiceOnFilesystem> = open fun readInvoicesFromFiles(files: List<Path>): List<InvoiceOnFilesystem> =
files.mapNotNull { file -> readInvoiceFromFile(file)?.let { InvoiceOnFilesystem(file, it) } } files.mapNotNull { file -> readInvoiceFromFile(file)?.let { InvoiceOnFilesystem(file, it) } }
fun readInvoiceFromFile(file: Path): Invoice? = try { open fun readInvoiceFromFile(file: Path): Invoice? = try {
val extension = file.extension.lowercase() val extension = file.extension.lowercase()
if (extension == "pdf") { if (extension == "pdf") {

View File

@ -19,22 +19,22 @@ import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.max import kotlin.math.max
class MailReader( open class MailReader(
private val eInvoiceReader: EInvoiceReader = EInvoiceReader() protected open val eInvoiceReader: EInvoiceReader = EInvoiceReader()
) { ) {
private data class MessagePart( protected data class MessagePart(
val mediaType: String, val mediaType: String,
val part: Part val part: Part
) )
private val mailDispatcher = Executors.newFixedThreadPool(max(24, Runtime.getRuntime().availableProcessors() * 4)).asCoroutineDispatcher() protected open val mailDispatcher = Executors.newFixedThreadPool(max(24, Runtime.getRuntime().availableProcessors() * 4)).asCoroutineDispatcher()
private val log by logger() protected val log by logger()
fun listenForNewReceivedEInvoices(account: MailAccount, downloadMessageBody: Boolean = false, emailFolderName: String = "INBOX", eInvoiceReceived: (MailWithInvoice) -> Unit) = runBlocking { open fun listenForNewReceivedEInvoices(account: MailAccount, downloadMessageBody: Boolean = false, emailFolderName: String = "INBOX", eInvoiceReceived: (MailWithInvoice) -> Unit) = runBlocking {
try { try {
connect(account) { store -> connect(account) { store ->
val folder = store.getFolder(emailFolderName) val folder = store.getFolder(emailFolderName)
@ -61,7 +61,7 @@ class MailReader(
log.info { "Stopped listening to new received eInvoices of '${account.username}'" } log.info { "Stopped listening to new received eInvoices of '${account.username}'" }
} }
private suspend fun keepConnectionOpen(account: MailAccount, folder: Folder) { protected open suspend fun keepConnectionOpen(account: MailAccount, folder: Folder) {
log.info { "Listening to new mails of ${account.username}" } log.info { "Listening to new mails of ${account.username}" }
// Use IMAP IDLE to keep the connection alive // Use IMAP IDLE to keep the connection alive
@ -78,7 +78,7 @@ class MailReader(
} }
fun listAllMessagesWithEInvoice(account: MailAccount, downloadMessageBody: Boolean = false, emailFolderName: String = "INBOX"): List<MailWithInvoice> { open fun listAllMessagesWithEInvoice(account: MailAccount, downloadMessageBody: Boolean = false, emailFolderName: String = "INBOX"): List<MailWithInvoice> {
try { try {
return connect(account) { store -> return connect(account) { store ->
val inbox = store.getFolder(emailFolderName) val inbox = store.getFolder(emailFolderName)
@ -95,7 +95,7 @@ class MailReader(
return emptyList() return emptyList()
} }
private fun listAllMessagesWithEInvoiceInFolder(folder: Folder, downloadMessageBody: Boolean): List<MailWithInvoice> = runBlocking { protected open fun listAllMessagesWithEInvoiceInFolder(folder: Folder, downloadMessageBody: Boolean): List<MailWithInvoice> = runBlocking {
val messageCount = folder.messageCount val messageCount = folder.messageCount
if (messageCount <= 0) { if (messageCount <= 0) {
return@runBlocking emptyList() return@runBlocking emptyList()
@ -115,7 +115,7 @@ class MailReader(
.filterNotNull() .filterNotNull()
} }
private fun findEInvoice(message: Message, downloadMessageBody: Boolean): MailWithInvoice? { protected open fun findEInvoice(message: Message, downloadMessageBody: Boolean): MailWithInvoice? {
try { try {
val parts = getAllMessageParts(message) val parts = getAllMessageParts(message)
@ -139,7 +139,7 @@ class MailReader(
return null return null
} }
private fun findEInvoice(messagePart: MessagePart): MailAttachmentWithEInvoice? { protected open fun findEInvoice(messagePart: MessagePart): MailAttachmentWithEInvoice? {
try { try {
val part = messagePart.part val part = messagePart.part
val invoice = tryToReadEInvoice(part, messagePart.mediaType) val invoice = tryToReadEInvoice(part, messagePart.mediaType)
@ -160,7 +160,7 @@ class MailReader(
return null return null
} }
private fun tryToReadEInvoice(part: Part, mediaType: String?): Invoice? = try { protected open fun tryToReadEInvoice(part: Part, mediaType: String?): Invoice? = try {
val filename = part.fileName?.lowercase() ?: "" val filename = part.fileName?.lowercase() ?: ""
if (filename.endsWith(".pdf") || mediaType == "application/pdf" || mediaType == "application/octet-stream") { if (filename.endsWith(".pdf") || mediaType == "application/pdf" || mediaType == "application/octet-stream") {
@ -176,7 +176,7 @@ class MailReader(
} }
private fun getAllMessageParts(part: Part): List<MessagePart> { protected open fun getAllMessageParts(part: Part): List<MessagePart> {
return if (part.isMimeType("multipart/*")) { return if (part.isMimeType("multipart/*")) {
val multipart = part.content as Multipart val multipart = part.content as Multipart
val parts = IntRange(0, multipart.count - 1).map { multipart.getBodyPart(it) } val parts = IntRange(0, multipart.count - 1).map { multipart.getBodyPart(it) }
@ -202,7 +202,7 @@ class MailReader(
* *
* -> This method removes parameters and return media type (first part) only * -> This method removes parameters and return media type (first part) only
*/ */
private fun getMediaType(part: Part): String? = part.contentType?.lowercase()?.let { contentType -> protected open fun getMediaType(part: Part): String? = part.contentType?.lowercase()?.let { contentType ->
val indexOfSeparator = contentType.indexOf(';') val indexOfSeparator = contentType.indexOf(';')
if (indexOfSeparator > -1) { if (indexOfSeparator > -1) {
@ -212,11 +212,11 @@ class MailReader(
} }
} }
private fun getPlainTextBody(parts: Collection<MessagePart>) = getBodyWithMediaType(parts, "text/plain") protected open fun getPlainTextBody(parts: Collection<MessagePart>) = getBodyWithMediaType(parts, "text/plain")
private fun getHtmlBody(parts: Collection<MessagePart>) = getBodyWithMediaType(parts, "text/html") protected open fun getHtmlBody(parts: Collection<MessagePart>) = getBodyWithMediaType(parts, "text/html")
private fun getBodyWithMediaType(parts: Collection<MessagePart>, mediaType: String): String? = try { protected open fun getBodyWithMediaType(parts: Collection<MessagePart>, mediaType: String): String? = try {
val partsForMediaType = parts.filter { it.mediaType == mediaType } val partsForMediaType = parts.filter { it.mediaType == mediaType }
if (partsForMediaType.size == 1) { if (partsForMediaType.size == 1) {
@ -239,11 +239,11 @@ class MailReader(
null null
} }
private fun map(date: Date): Instant = protected open fun map(date: Date): Instant =
date.toInstant() date.toInstant()
private fun <T> connect(account: MailAccount, connected: (Store) -> T): T { protected open fun <T> connect(account: MailAccount, connected: (Store) -> T): T {
val properties = mapAccountToJavaMailProperties(account) val properties = mapAccountToJavaMailProperties(account)
val session = Session.getInstance(properties) val session = Session.getInstance(properties)
@ -254,7 +254,7 @@ class MailReader(
} }
} }
private fun mapAccountToJavaMailProperties(account: MailAccount) = Properties().apply { protected open fun mapAccountToJavaMailProperties(account: MailAccount) = Properties().apply {
put("mail.store.protocol", "imap") put("mail.store.protocol", "imap")
put("mail.imap.host", account.serverAddress) put("mail.imap.host", account.serverAddress)

View File

@ -10,9 +10,9 @@ import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
import java.util.* import java.util.*
class MustangMapper { open class MustangMapper {
fun mapToTransaction(invoice: net.codinux.invoicing.model.Invoice): IExportableTransaction = Invoice().apply { open fun mapToTransaction(invoice: net.codinux.invoicing.model.Invoice): IExportableTransaction = Invoice().apply {
this.number = invoice.invoiceNumber this.number = invoice.invoiceNumber
this.issueDate = map(invoice.invoicingDate) this.issueDate = map(invoice.invoicingDate)
this.sender = mapParty(invoice.sender) this.sender = mapParty(invoice.sender)
@ -26,7 +26,7 @@ class MustangMapper {
this.referenceNumber = invoice.buyerReference this.referenceNumber = invoice.buyerReference
} }
fun mapParty(party: Party): TradeParty = TradeParty( open fun mapParty(party: Party): TradeParty = TradeParty(
party.name, party.street, party.postalCode, party.city, party.countryIsoCode party.name, party.street, party.postalCode, party.city, party.countryIsoCode
).apply { ).apply {
this.setVATID(party.vatId) this.setVATID(party.vatId)
@ -45,7 +45,7 @@ class MustangMapper {
} }
} }
fun mapLineItem(item: LineItem): IZUGFeRDExportableItem = Item( open fun mapLineItem(item: LineItem): IZUGFeRDExportableItem = Item(
// description has to be an empty string if not set // description has to be an empty string if not set
Product(item.name, item.description ?: "", item.unit, item.vatPercentage), item.price, item.quantity Product(item.name, item.description ?: "", item.unit, item.vatPercentage), item.price, item.quantity
).apply { ).apply {
@ -53,7 +53,7 @@ class MustangMapper {
} }
fun mapToInvoice(invoice: Invoice) = net.codinux.invoicing.model.Invoice( open fun mapToInvoice(invoice: Invoice) = net.codinux.invoicing.model.Invoice(
invoiceNumber = invoice.number, invoiceNumber = invoice.number,
invoicingDate = map(invoice.issueDate), invoicingDate = map(invoice.issueDate),
sender = mapParty(invoice.sender), sender = mapParty(invoice.sender),
@ -66,32 +66,32 @@ class MustangMapper {
buyerReference = invoice.referenceNumber buyerReference = invoice.referenceNumber
) )
fun mapParty(party: TradeParty) = Party( open fun mapParty(party: TradeParty) = Party(
party.name, party.street, party.zip, party.location, party.country, party.vatID, party.name, party.street, party.zip, party.location, party.country, party.vatID,
party.email ?: party.contact?.eMail, party.contact?.phone, party.contact?.fax, party.contact?.name, party.email ?: party.contact?.eMail, party.contact?.phone, party.contact?.fax, party.contact?.name,
party.bankDetails?.firstOrNull()?.let { net.codinux.invoicing.model.BankDetails(it.iban, it.bic, it.accountName) } party.bankDetails?.firstOrNull()?.let { net.codinux.invoicing.model.BankDetails(it.iban, it.bic, it.accountName) }
) )
fun mapLineItem(item: IZUGFeRDExportableItem) = LineItem( open fun mapLineItem(item: IZUGFeRDExportableItem) = LineItem(
item.product.name, item.product.unit, item.quantity, item.price, item.product.vatPercent, item.product.description.takeUnless { it.isBlank() } item.product.name, item.product.unit, item.quantity, item.price, item.product.vatPercent, item.product.description.takeUnless { it.isBlank() }
) )
@JvmName("mapNullable") @JvmName("mapNullable")
private fun map(date: LocalDate?) = protected fun map(date: LocalDate?) =
date?.let { map(it) } date?.let { map(it) }
private fun map(date: LocalDate): Date = protected open fun map(date: LocalDate): Date =
Date.from(mapToInstant(date)) Date.from(mapToInstant(date))
private fun mapToInstant(date: LocalDate): Instant = protected open fun mapToInstant(date: LocalDate): Instant =
date.atStartOfDay(ZoneId.systemDefault()).toInstant() date.atStartOfDay(ZoneId.systemDefault()).toInstant()
@JvmName("mapNullable") @JvmName("mapNullable")
private fun map(date: Date?) = protected fun map(date: Date?) =
date?.let { map(it) } date?.let { map(it) }
private fun map(date: Date): LocalDate = protected open fun map(date: Date): LocalDate =
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
} }

View File

@ -6,39 +6,39 @@ import org.mustangproject.ZUGFeRD.ZUGFeRDInvoiceImporter
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
class EInvoiceReader( open class EInvoiceReader(
private val mapper: MustangMapper = MustangMapper() protected open val mapper: MustangMapper = MustangMapper()
) { ) {
fun extractFromXml(xmlFile: File) = extractFromXml(xmlFile.inputStream()) open fun extractFromXml(xmlFile: File) = extractFromXml(xmlFile.inputStream())
fun extractFromXml(stream: InputStream) = extractFromXml(stream.reader().readText()) open fun extractFromXml(stream: InputStream) = extractFromXml(stream.reader().readText())
fun extractFromXml(xml: String): Invoice { open fun extractFromXml(xml: String): Invoice {
val importer = ZUGFeRDInvoiceImporter() // XRechnungImporter only reads properties but not to a Invoice object val importer = ZUGFeRDInvoiceImporter() // XRechnungImporter only reads properties but not to a Invoice object
importer.fromXML(xml) importer.fromXML(xml)
return extractInvoice(importer) return extractInvoice(importer)
} }
fun extractFromPdf(pdfFile: File) = extractFromPdf(pdfFile.inputStream()) open fun extractFromPdf(pdfFile: File) = extractFromPdf(pdfFile.inputStream())
fun extractFromPdf(stream: InputStream): Invoice { open fun extractFromPdf(stream: InputStream): Invoice {
val importer = ZUGFeRDInvoiceImporter(stream) val importer = ZUGFeRDInvoiceImporter(stream)
return extractInvoice(importer) return extractInvoice(importer)
} }
fun extractXmlFromPdf(pdfFile: File) = extractXmlFromPdf(pdfFile.inputStream()) open fun extractXmlFromPdf(pdfFile: File) = extractXmlFromPdf(pdfFile.inputStream())
fun extractXmlFromPdf(stream: InputStream): String { open fun extractXmlFromPdf(stream: InputStream): String {
val importer = ZUGFeRDInvoiceImporter(stream) val importer = ZUGFeRDInvoiceImporter(stream)
return String(importer.rawXML, Charsets.UTF_8) return String(importer.rawXML, Charsets.UTF_8)
} }
private fun extractInvoice(importer: ZUGFeRDInvoiceImporter): Invoice { protected open fun extractInvoice(importer: ZUGFeRDInvoiceImporter): Invoice {
val invoice = importer.extractInvoice() val invoice = importer.extractInvoice()
// TODO: the values LineTotalAmount, ChargeTotalAmount, AllowanceTotalAmount, TaxBasisTotalAmount, TaxTotalAmount, // TODO: the values LineTotalAmount, ChargeTotalAmount, AllowanceTotalAmount, TaxBasisTotalAmount, TaxTotalAmount,

View File

@ -1,27 +1,31 @@
package net.codinux.invoicing.validation package net.codinux.invoicing.validation
import net.codinux.log.logger
import org.mustangproject.validator.ZUGFeRDValidator import org.mustangproject.validator.ZUGFeRDValidator
import java.io.File import java.io.File
import java.lang.reflect.Field import java.lang.reflect.Field
class EInvoiceValidator { open class EInvoiceValidator {
companion object { companion object {
private val SectionField = getPrivateField("section") private val SectionField = getPrivateField("section")
private val CriterionField = getPrivateField("criterion") private val CriterionField = getPrivateField("criterion")
private val StacktraceField = getPrivateField("stacktrace") private val StacktraceField = getPrivateField("stacktrace")
private val log by logger()
private fun getPrivateField(fieldName: String): Field? = try { private fun getPrivateField(fieldName: String): Field? = try {
org.mustangproject.validator.ValidationResultItem::class.java.getDeclaredField(fieldName).apply { org.mustangproject.validator.ValidationResultItem::class.java.getDeclaredField(fieldName).apply {
trySetAccessible() trySetAccessible()
} }
} catch (e: Throwable) { } catch (e: Throwable) {
log.error(e) { "Could not access private field '$fieldName' of Mustang ValidationResultItem" }
null null
} }
} }
fun validate(fileToValidate: File, disableNotices: Boolean = false): InvoiceValidationResult { open fun validate(fileToValidate: File, disableNotices: Boolean = false): InvoiceValidationResult {
val validator = object : ZUGFeRDValidator() { val validator = object : ZUGFeRDValidator() {
fun getContext() = this.context fun getContext() = this.context
} }
@ -42,10 +46,10 @@ class EInvoiceValidator {
return InvoiceValidationResult(validator.wasCompletelyValid(), isXmlValid, xmlValidationResults, report) return InvoiceValidationResult(validator.wasCompletelyValid(), isXmlValid, xmlValidationResults, report)
} }
private fun mapValidationResultItem(item: org.mustangproject.validator.ValidationResultItem) = protected open 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) 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 { protected open fun mapSeverity(item: org.mustangproject.validator.ValidationResultItem): ValidationResultSeverity {
var name = item.severity.name var name = item.severity.name
name = name.first().uppercase() + name.substring(1) name = name.first().uppercase() + name.substring(1)