Implemented creating XRechnung
This commit is contained in:
parent
4293c8a7bb
commit
3273625f7a
|
@ -0,0 +1,31 @@
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val mustangVersion: String by project
|
||||||
|
|
||||||
|
val klfVersion: String by project
|
||||||
|
|
||||||
|
val assertKVersion: String by project
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.mustangproject:library:$mustangVersion")
|
||||||
|
|
||||||
|
implementation("net.codinux.log:klf:$klfVersion")
|
||||||
|
|
||||||
|
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
|
||||||
|
implementation("com.willowtreeapps.assertk:assertk:$assertKVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.invoicing.creation
|
||||||
|
|
||||||
|
import net.codinux.invoicing.model.Invoice
|
||||||
|
import org.mustangproject.ZUGFeRD.Profiles
|
||||||
|
import org.mustangproject.ZUGFeRD.ZUGFeRD2PullProvider
|
||||||
|
|
||||||
|
class EInvoiceCreator(
|
||||||
|
private val mapper: MustangMapper = MustangMapper()
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun createXRechnungXml(invoice: Invoice): String {
|
||||||
|
val provider = ZUGFeRD2PullProvider()
|
||||||
|
provider.profile = Profiles.getByName("XRechnung")
|
||||||
|
provider.generateXML(mapper.mapToTransaction(invoice))
|
||||||
|
|
||||||
|
return String(provider.xml, Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package net.codinux.invoicing.creation
|
||||||
|
|
||||||
|
import net.codinux.invoicing.model.LineItem
|
||||||
|
import org.mustangproject.Invoice
|
||||||
|
import org.mustangproject.Item
|
||||||
|
import org.mustangproject.Product
|
||||||
|
import org.mustangproject.TradeParty
|
||||||
|
import org.mustangproject.ZUGFeRD.IExportableTransaction
|
||||||
|
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MustangMapper {
|
||||||
|
|
||||||
|
fun mapToTransaction(invoice: net.codinux.invoicing.model.Invoice): IExportableTransaction = Invoice().apply {
|
||||||
|
this.number = invoice.invoiceNumber
|
||||||
|
this.issueDate = map(invoice.invoicingDate)
|
||||||
|
this.sender = mapParty(invoice.sender)
|
||||||
|
this.recipient = mapParty(invoice.recipient)
|
||||||
|
|
||||||
|
this.setZFItems(ArrayList(invoice.items.map { mapLineItem(it) }))
|
||||||
|
|
||||||
|
this.dueDate = map(invoice.dueDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapParty(party: net.codinux.invoicing.model.Party): TradeParty = TradeParty(
|
||||||
|
party.name, "${party.streetName} ${party.houseNumber}", party.postalCode, party.city, party.country
|
||||||
|
).apply {
|
||||||
|
this.taxID = party.taxNumber
|
||||||
|
// TODO: vatID?
|
||||||
|
// TODO: ID?
|
||||||
|
// TODO: description?
|
||||||
|
|
||||||
|
this.email = party.email
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapLineItem(item: LineItem): IZUGFeRDExportableItem = Item(
|
||||||
|
// description has to be an empty string if not set
|
||||||
|
Product(item.name, item.description ?: "", item.unit, item.vatPercentage), item.price, item.quantity
|
||||||
|
).apply {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JvmName("mapNullable")
|
||||||
|
private fun map(date: LocalDate?) =
|
||||||
|
date?.let { map(it) }
|
||||||
|
|
||||||
|
private fun map(date: LocalDate): Date =
|
||||||
|
Date.from(mapToInstant(date))
|
||||||
|
|
||||||
|
private fun mapToInstant(date: LocalDate): Instant =
|
||||||
|
date.atStartOfDay(ZoneId.systemDefault()).toInstant()
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.invoicing.model
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
class Invoice(
|
||||||
|
val invoiceNumber: String,
|
||||||
|
val invoicingDate: LocalDate,
|
||||||
|
val sender: Party,
|
||||||
|
val recipient: Party,
|
||||||
|
val items: List<LineItem>,
|
||||||
|
val dueDate: LocalDate? = null,
|
||||||
|
) {
|
||||||
|
override fun toString() = "$invoicingDate $invoiceNumber to $recipient"
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.invoicing.model
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
class LineItem(
|
||||||
|
val name: String,
|
||||||
|
val unit: String,
|
||||||
|
val quantity: BigDecimal,
|
||||||
|
val price: BigDecimal,
|
||||||
|
val vatPercentage: BigDecimal,
|
||||||
|
val description: String? = null,
|
||||||
|
) {
|
||||||
|
override fun toString() = "$name, $quantity x $price, $vatPercentage %"
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.invoicing.model
|
||||||
|
|
||||||
|
class Party(
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
val streetName: String,
|
||||||
|
val houseNumber: String,
|
||||||
|
var postalCode: String?,
|
||||||
|
val city: String,
|
||||||
|
val country: String? = null,
|
||||||
|
|
||||||
|
val taxNumber: String? = null, // better name like vatTaxNumber?
|
||||||
|
|
||||||
|
val email: String? = null,
|
||||||
|
// var telephoneNumber: String? = null, // simply telephone?
|
||||||
|
// var website: String? = null,
|
||||||
|
) {
|
||||||
|
override fun toString() = "$name, $city"
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package net.codinux.invoicing.creation
|
||||||
|
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isNotEmpty
|
||||||
|
import net.codinux.invoicing.model.Invoice
|
||||||
|
import net.codinux.invoicing.model.LineItem
|
||||||
|
import net.codinux.invoicing.model.Party
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.time.LocalDate
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class EInvoiceCreatorTest {
|
||||||
|
|
||||||
|
private val underTest = EInvoiceCreator()
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createXRechnung() {
|
||||||
|
val invoice = createInvoice()
|
||||||
|
|
||||||
|
val result = underTest.createXRechnungXml(invoice)
|
||||||
|
|
||||||
|
assertThat(result).isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createInvoice(
|
||||||
|
invoiceNumber: String = "12345",
|
||||||
|
invoicingDate: LocalDate = LocalDate.of(2015, 10, 21),
|
||||||
|
sender: Party = createParty("Hochwürdiger Leistungserbringer"),
|
||||||
|
recipient: Party = createParty("Untertänigster Leistungsempfänger"),
|
||||||
|
items: List<LineItem> = listOf(createItem()),
|
||||||
|
dueDate: LocalDate? = null
|
||||||
|
) = Invoice(invoiceNumber, invoicingDate, sender, recipient, items, dueDate)
|
||||||
|
|
||||||
|
private fun createParty(
|
||||||
|
name: String,
|
||||||
|
streetName: String = "Fun Street",
|
||||||
|
houseNumber: String = "1",
|
||||||
|
postalCode: String = "12345",
|
||||||
|
city: String = "Glückstadt",
|
||||||
|
country: String? = null,
|
||||||
|
taxNumber: String? = "DE12345678",
|
||||||
|
email: String? = null,
|
||||||
|
) = Party(name, streetName, houseNumber, postalCode, city, country, taxNumber, email)
|
||||||
|
|
||||||
|
private fun createItem(
|
||||||
|
name: String = "Erbrachte Dienstleistungen",
|
||||||
|
unit: String = "",
|
||||||
|
quantity: BigDecimal = BigDecimal(1),
|
||||||
|
price: BigDecimal = BigDecimal(99),
|
||||||
|
vatPercentage: BigDecimal = BigDecimal(0.19),
|
||||||
|
description: String? = null,
|
||||||
|
) = LineItem(name, unit, quantity, price, vatPercentage, description)
|
||||||
|
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ org.gradle.parallel=true
|
||||||
|
|
||||||
kotlinVersion=1.9.25
|
kotlinVersion=1.9.25
|
||||||
|
|
||||||
coroutinesVersion=1.8.1
|
mustangVersion=2.14.2
|
||||||
|
|
||||||
klfVersion=1.6.2
|
klfVersion=1.6.2
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,4 @@ plugins {
|
||||||
|
|
||||||
rootProject.name = "eInvoicing"
|
rootProject.name = "eInvoicing"
|
||||||
|
|
||||||
|
include("e-invoicing-domain")
|
||||||
|
|
Loading…
Reference in New Issue