From 2d42e58d0fd5dbb792b8da46c6b1bee5dfb82e72 Mon Sep 17 00:00:00 2001 From: dankito Date: Thu, 21 Nov 2024 01:52:14 +0100 Subject: [PATCH] Implemented attaching invoice XML to a PDF via API --- .../invoicing/api/InvoicingResource.kt | 41 ++++++++++++++----- .../invoicing/service/InvoicingService.kt | 22 +++++++--- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/e-invoice-api/src/main/kotlin/net/codinux/invoicing/api/InvoicingResource.kt b/e-invoice-api/src/main/kotlin/net/codinux/invoicing/api/InvoicingResource.kt index abf2fdd..1ad537a 100644 --- a/e-invoice-api/src/main/kotlin/net/codinux/invoicing/api/InvoicingResource.kt +++ b/e-invoice-api/src/main/kotlin/net/codinux/invoicing/api/InvoicingResource.kt @@ -6,37 +6,51 @@ import jakarta.ws.rs.core.Response import net.codinux.invoicing.model.Invoice import net.codinux.invoicing.service.InvoicingService import org.eclipse.microprofile.openapi.annotations.Operation +import org.jboss.resteasy.reactive.PartType +import org.jboss.resteasy.reactive.RestForm +import org.jboss.resteasy.reactive.multipart.FileUpload @Path("") @Consumes(MediaType.APPLICATION_JSON) -@Produces(MediaType.APPLICATION_OCTET_STREAM) +@Produces(MediaType.APPLICATION_XML) class InvoicingResource( private val service: InvoicingService ) { @Path("xrechnung") @POST - @Produces(MediaType.APPLICATION_XML) @Operation(summary = "Create a XRechnung XML") fun createXRechnung(invoice: Invoice) = service.createXRechnung(invoice) @Path("facturx/xml") @POST - @Produces(MediaType.APPLICATION_XML) @Operation(summary = "Create a Factur-X / ZUGFeRD XML (ZUGFeRD is a synonym for Factur-X)") fun createFacturXXml(invoice: Invoice) = service.createFacturXXml(invoice) @Path("facturx/pdf") @POST + @Produces(MediaType.APPLICATION_OCTET_STREAM) @Operation(summary = "Create a Factur-X / ZUGFeRD XML, transforms it to PDF and attaches before created XML to it") fun createFacturXPdf(invoice: Invoice): Response { val pdfFile = service.createFacturXPdf(invoice) - return Response.ok(pdfFile) - .header("Content-Disposition", "attachment;filename=\"Invoice.pdf\"") - .build() + return createPdfFileResponse(pdfFile) + } + + @Path("attach") + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Operation(summary = "Attaches the invoice data as EN 16931 XML to a PDF file, combining them to a Factur-X / ZUGFeRD hybrid PDF with XML invoice file") + fun attachInvoiceXmlToPdf( + @RestForm @PartType(MediaType.APPLICATION_JSON) invoice: Invoice, + @RestForm("pdf") pdf: FileUpload + ): Response { + val pdfFile = service.attachInvoiceXmlToPdf(invoice, pdf.uploadedFile()) + + return createPdfFileResponse(pdfFile) } @@ -46,15 +60,20 @@ class InvoicingResource( @Consumes(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Extract invoice data from a Factur-x / ZUGFeRD or XRechnung file") - fun extractInvoiceData(invoice: java.nio.file.Path) = - service.extractInvoiceData(invoice) + fun extractInvoiceData(invoice: FileUpload) = + service.extractInvoiceData(invoice.uploadedFile()) @Path("validate") @POST @Consumes(MediaType.APPLICATION_OCTET_STREAM) - @Produces(MediaType.APPLICATION_XML) @Operation(summary = "Validate a Factur-x / ZUGFeRD or XRechnung file") - fun validateInvoiceXml(invoice: java.nio.file.Path) = - service.validateInvoice(invoice).reportAsXml + fun validateInvoiceXml(invoice: FileUpload) = + service.validateInvoice(invoice.uploadedFile()).reportAsXml + + + private fun createPdfFileResponse(pdfFile: java.nio.file.Path): Response = + Response.ok(pdfFile) + .header("Content-Disposition", "attachment;filename=\"Invoice.pdf\"") + .build() } \ No newline at end of file diff --git a/e-invoice-api/src/main/kotlin/net/codinux/invoicing/service/InvoicingService.kt b/e-invoice-api/src/main/kotlin/net/codinux/invoicing/service/InvoicingService.kt index 31805b9..36d502d 100644 --- a/e-invoice-api/src/main/kotlin/net/codinux/invoicing/service/InvoicingService.kt +++ b/e-invoice-api/src/main/kotlin/net/codinux/invoicing/service/InvoicingService.kt @@ -25,12 +25,18 @@ class InvoicingService { fun createFacturXXml(invoice: Invoice): String = creator.createZugferdXml(invoice) - fun createFacturXPdf(invoice: Invoice): File { - val resultFile = File.createTempFile("factur-x", ".pdf").also { - it.deleteOnExit() - } + fun createFacturXPdf(invoice: Invoice): Path { + val resultFile = createTempPdfFile() - creator.createZugferdPdf(invoice, resultFile) + creator.createZugferdPdf(invoice, resultFile.toFile()) + + return resultFile + } + + fun attachInvoiceXmlToPdf(invoice: Invoice, pdf: Path): Path { + val resultFile = createTempPdfFile() + + creator.combinePdfAndInvoiceXml(invoice, pdf.toFile(), resultFile.toFile()) return resultFile } @@ -46,4 +52,10 @@ class InvoicingService { fun validateInvoice(invoice: Path) = validator.validate(invoice.toFile()) + + private fun createTempPdfFile(): Path = + File.createTempFile("factur-x", ".pdf") + .also { it.deleteOnExit() } + .toPath() + } \ No newline at end of file