Implemented extractXmlFromPdf(); extracted invoice assertions to InvoiceAsserter

This commit is contained in:
dankito 2024-11-15 00:51:52 +01:00
parent 7ab88390f1
commit 658c02296a
4 changed files with 135 additions and 107 deletions

View File

@ -10,24 +10,35 @@ class EInvoiceReader(
private val mapper: MustangMapper = MustangMapper()
) {
fun readFromXml(file: File) = readFromXml(file.inputStream())
fun readFromXml(xmlFile: File) = readFromXml(xmlFile.inputStream())
fun readFromXml(stream: InputStream): Invoice? {
fun readFromXml(stream: InputStream) = readFromXml(stream.reader().readText())
fun readFromXml(xml: String): Invoice {
val importer = ZUGFeRDInvoiceImporter() // XRechnungImporter only reads properties but not to a Invoice object
importer.fromXML(stream.reader().readText())
importer.fromXML(xml)
return extractInvoice(importer)
}
fun extractFromPdf(file: File) = extractFromPdf(file.inputStream())
fun extractFromPdf(pdfFile: File) = extractFromPdf(pdfFile.inputStream())
fun extractFromPdf(stream: InputStream): Invoice? {
fun extractFromPdf(stream: InputStream): Invoice {
val importer = ZUGFeRDInvoiceImporter(stream)
return extractInvoice(importer)
}
private fun extractInvoice(importer: ZUGFeRDInvoiceImporter): Invoice? {
fun extractXmlFromPdf(pdfFile: File) = extractXmlFromPdf(pdfFile.inputStream())
fun extractXmlFromPdf(stream: InputStream): String {
val importer = ZUGFeRDInvoiceImporter(stream)
return String(importer.rawXML, Charsets.UTF_8)
}
private fun extractInvoice(importer: ZUGFeRDInvoiceImporter): Invoice {
val invoice = importer.extractInvoice()
// TODO: the values LineTotalAmount, ChargeTotalAmount, AllowanceTotalAmount, TaxBasisTotalAmount, TaxTotalAmount,

View File

@ -1,12 +1,9 @@
package net.codinux.invoicing.creation
import assertk.assertThat
import assertk.assertions.isNotEmpty
import net.codinux.invoicing.test.DataGenerator
import net.codinux.invoicing.test.XPathAsserter
import net.codinux.invoicing.test.InvoiceAsserter
import org.mustangproject.ZUGFeRD.ZUGFeRDInvoiceImporter
import java.io.File
import java.math.BigDecimal
import kotlin.test.Test
class EInvoiceCreatorTest {
@ -49,47 +46,7 @@ class EInvoiceCreatorTest {
private fun createInvoice() = DataGenerator.createInvoice()
private fun assertInvoiceXml(xml: String) {
assertThat(xml).isNotEmpty()
val asserter = XPathAsserter(xml)
asserter.xpathHasValue("//rsm:ExchangedDocument/ram:ID", DataGenerator.InvoiceNumber)
asserter.xpathHasValue("//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString", DataGenerator.InvoicingDate.toString().replace("-", ""))
val senderXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty"
assertParty(asserter, senderXPath, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderVatId, DataGenerator.SenderEmail, DataGenerator.SenderPhone)
val receiverXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty"
assertParty(asserter, receiverXPath, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail, DataGenerator.RecipientPhone)
val lineItemXPath = "//rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem"
assertLineItem(asserter, lineItemXPath, DataGenerator.ItemName, DataGenerator.ItemUnit, DataGenerator.ItemQuantity, DataGenerator.ItemPrice, DataGenerator.ItemVatPercentage, DataGenerator.ItemDescription)
}
private fun assertParty(asserter: XPathAsserter, partyXPath: String, name: String, street: String, postalCode: String, city: String, vatId: String, email: String, phone: String) {
asserter.xpathHasValue("$partyXPath/ram:Name", name)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:LineOne", street)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:PostcodeCode", postalCode)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:CityName", city)
asserter.xpathHasValue("$partyXPath/ram:SpecifiedTaxRegistration/ram:ID", vatId)
asserter.xpathHasValue("$partyXPath/ram:URIUniversalCommunication/ram:URIID", email)
asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:EmailURIUniversalCommunication/ram:URIID", email)
asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber", phone)
}
private fun assertLineItem(asserter: XPathAsserter, itemXPath: String, name: String, unit: String, quantity: BigDecimal, price: BigDecimal, vatPercentage: BigDecimal, description: String?) {
asserter.xpathHasValue("$itemXPath/ram:SpecifiedTradeProduct/ram:Name", name)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode", unit)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity", quantity, 4)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount", price, 2)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent", vatPercentage, 2)
// asserter.xpathHasValue("$partyXPath/ram:URIUniversalCommunication/ram:URIID", description)
InvoiceAsserter.assertInvoiceXml(xml)
}
}

View File

@ -1,17 +1,8 @@
package net.codinux.invoicing.reader
import assertk.assertThat
import assertk.assertions.hasSize
import assertk.assertions.isEqualTo
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import net.codinux.invoicing.model.BankDetails
import net.codinux.invoicing.model.Invoice
import net.codinux.invoicing.model.LineItem
import net.codinux.invoicing.model.Party
import net.codinux.invoicing.test.DataGenerator
import net.codinux.invoicing.test.InvoiceAsserter
import java.io.InputStream
import java.math.BigDecimal
import kotlin.test.Test
class EInvoiceReaderTest {
@ -33,57 +24,19 @@ class EInvoiceReaderTest {
assertInvoice(result)
}
@Test
fun extractXmlFromPdf() {
val result = underTest.extractXmlFromPdf(getTestFile("ZUGFeRD.pdf"))
InvoiceAsserter.assertInvoiceXml(result)
}
private fun getTestFile(filename: String): InputStream =
this.javaClass.classLoader.getResourceAsStream("files/$filename")!!
private fun assertInvoice(invoice: Invoice?) {
assertThat(invoice).isNotNull()
assertThat(invoice!!.invoiceNumber).isEqualTo(DataGenerator.InvoiceNumber)
assertThat(invoice.invoicingDate).isEqualTo(DataGenerator.InvoicingDate)
assertParty(invoice.sender, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderCountry, DataGenerator.SenderVatId, DataGenerator.SenderEmail, DataGenerator.SenderPhone, DataGenerator.SenderBankDetails)
assertParty(invoice.recipient, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientCountry, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail, DataGenerator.RecipientPhone, DataGenerator.RecipientBankDetails)
assertThat(invoice.items).hasSize(1)
assertLineItem(invoice.items.first(), DataGenerator.ItemName, DataGenerator.ItemUnit, DataGenerator.ItemQuantity, DataGenerator.ItemPrice, DataGenerator.ItemVatPercentage, DataGenerator.ItemDescription)
}
private fun assertParty(party: Party, name: String, street: String, postalCode: String, city: String, country: String?, vatId: String, email: String, phone: String, bankDetails: BankDetails?) {
assertThat(party.name).isEqualTo(name)
assertThat(party.street).isEqualTo(street)
assertThat(party.postalCode).isEqualTo(postalCode)
assertThat(party.city).isEqualTo(city)
assertThat(party.countryIsoCode).isEqualTo(country)
assertThat(party.vatId).isEqualTo(vatId)
assertThat(party.email).isEqualTo(email)
assertThat(party.phone).isEqualTo(phone)
if (bankDetails == null) {
assertThat(party.bankDetails).isNull()
} else {
assertThat(party.bankDetails!!.accountNumber).isEqualTo(bankDetails.accountNumber)
assertThat(party.bankDetails!!.bankCode).isEqualTo(bankDetails.bankCode)
// due to a bug in Mustang accountName doesn't get extracted from XML, see https://github.com/ZUGFeRD/mustangproject/issues/558
// assertThat(party.bankDetails!!.accountHolderName).isEqualTo(bankDetails.accountHolderName)
}
}
private fun assertLineItem(item: LineItem, name: String, unit: String, quantity: BigDecimal, price: BigDecimal, vatPercentage: BigDecimal, description: String?) {
assertThat(item.name).isEqualTo(name)
assertThat(item.unit).isEqualTo(unit)
assertThat(item.quantity).isEqualTo(quantity.setScale(4))
assertThat(item.price).isEqualTo(price.setScale(4))
assertThat(item.vatPercentage).isEqualTo(vatPercentage.setScale(2))
// assertThat(item.description).isEqualTo(description)
InvoiceAsserter.assertInvoice(invoice)
}
}

View File

@ -0,0 +1,107 @@
package net.codinux.invoicing.test
import assertk.assertThat
import assertk.assertions.*
import net.codinux.invoicing.model.BankDetails
import net.codinux.invoicing.model.Invoice
import net.codinux.invoicing.model.LineItem
import net.codinux.invoicing.model.Party
import java.math.BigDecimal
object InvoiceAsserter {
fun assertInvoiceXml(xml: String) {
assertThat(xml).isNotEmpty()
val asserter = XPathAsserter(xml)
asserter.xpathHasValue("//rsm:ExchangedDocument/ram:ID", DataGenerator.InvoiceNumber)
asserter.xpathHasValue("//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString", DataGenerator.InvoicingDate.toString().replace("-", ""))
val senderXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty"
assertParty(asserter, senderXPath, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderVatId, DataGenerator.SenderEmail, DataGenerator.SenderPhone)
val receiverXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty"
assertParty(asserter, receiverXPath, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail, DataGenerator.RecipientPhone)
val lineItemXPath = "//rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem"
assertLineItem(asserter, lineItemXPath, DataGenerator.ItemName, DataGenerator.ItemUnit, DataGenerator.ItemQuantity, DataGenerator.ItemPrice, DataGenerator.ItemVatPercentage, DataGenerator.ItemDescription)
}
private fun assertParty(asserter: XPathAsserter, partyXPath: String, name: String, street: String, postalCode: String, city: String, vatId: String, email: String, phone: String) {
asserter.xpathHasValue("$partyXPath/ram:Name", name)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:LineOne", street)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:PostcodeCode", postalCode)
asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:CityName", city)
asserter.xpathHasValue("$partyXPath/ram:SpecifiedTaxRegistration/ram:ID", vatId)
asserter.xpathHasValue("$partyXPath/ram:URIUniversalCommunication/ram:URIID", email)
asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:EmailURIUniversalCommunication/ram:URIID", email)
asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber", phone)
}
private fun assertLineItem(asserter: XPathAsserter, itemXPath: String, name: String, unit: String, quantity: BigDecimal, price: BigDecimal, vatPercentage: BigDecimal, description: String?) {
asserter.xpathHasValue("$itemXPath/ram:SpecifiedTradeProduct/ram:Name", name)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode", unit)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity", quantity, 4)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount", price, 2)
asserter.xpathHasValue("$itemXPath/ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent", vatPercentage, 2)
// asserter.xpathHasValue("$partyXPath/ram:URIUniversalCommunication/ram:URIID", description)
}
fun assertInvoice(invoice: Invoice?) {
assertThat(invoice).isNotNull()
assertThat(invoice!!.invoiceNumber).isEqualTo(DataGenerator.InvoiceNumber)
assertThat(invoice.invoicingDate).isEqualTo(DataGenerator.InvoicingDate)
assertParty(invoice.sender, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderCountry, DataGenerator.SenderVatId, DataGenerator.SenderEmail, DataGenerator.SenderPhone, DataGenerator.SenderBankDetails)
assertParty(invoice.recipient, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientCountry, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail, DataGenerator.RecipientPhone, DataGenerator.RecipientBankDetails)
assertThat(invoice.items).hasSize(1)
assertLineItem(invoice.items.first(), DataGenerator.ItemName, DataGenerator.ItemUnit, DataGenerator.ItemQuantity, DataGenerator.ItemPrice, DataGenerator.ItemVatPercentage, DataGenerator.ItemDescription)
}
private fun assertParty(party: Party, name: String, street: String, postalCode: String, city: String, country: String?, vatId: String, email: String, phone: String, bankDetails: BankDetails?) {
assertThat(party.name).isEqualTo(name)
assertThat(party.street).isEqualTo(street)
assertThat(party.postalCode).isEqualTo(postalCode)
assertThat(party.city).isEqualTo(city)
assertThat(party.countryIsoCode).isEqualTo(country)
assertThat(party.vatId).isEqualTo(vatId)
assertThat(party.email).isEqualTo(email)
assertThat(party.phone).isEqualTo(phone)
if (bankDetails == null) {
assertThat(party.bankDetails).isNull()
} else {
assertThat(party.bankDetails!!.accountNumber).isEqualTo(bankDetails.accountNumber)
assertThat(party.bankDetails!!.bankCode).isEqualTo(bankDetails.bankCode)
// due to a bug in Mustang accountName doesn't get extracted from XML, see https://github.com/ZUGFeRD/mustangproject/issues/558
// assertThat(party.bankDetails!!.accountHolderName).isEqualTo(bankDetails.accountHolderName)
}
}
private fun assertLineItem(item: LineItem, name: String, unit: String, quantity: BigDecimal, price: BigDecimal, vatPercentage: BigDecimal, description: String?) {
assertThat(item.name).isEqualTo(name)
assertThat(item.unit).isEqualTo(unit)
assertThat(item.quantity).isEqualTo(quantity.setScale(4))
assertThat(item.price).isEqualTo(price.setScale(4))
assertThat(item.vatPercentage).isEqualTo(vatPercentage.setScale(2))
// assertThat(item.description).isEqualTo(description)
}
}