diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/reader/EInvoiceReader.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/reader/EInvoiceReader.kt index 498fecd..dda5e44 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/reader/EInvoiceReader.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/reader/EInvoiceReader.kt @@ -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, diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt index 215f84d..87cf26d 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt @@ -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) } } \ No newline at end of file diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/reader/EInvoiceReaderTest.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/reader/EInvoiceReaderTest.kt index 7d599d1..4f691a9 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/reader/EInvoiceReaderTest.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/reader/EInvoiceReaderTest.kt @@ -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) } } \ No newline at end of file diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/InvoiceAsserter.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/InvoiceAsserter.kt new file mode 100644 index 0000000..8461b1e --- /dev/null +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/InvoiceAsserter.kt @@ -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) + } + +} \ No newline at end of file