From f1a364866c161b2baf780ce13bd54c8979947cf1 Mon Sep 17 00:00:00 2001 From: dankito Date: Mon, 25 Nov 2024 17:27:26 +0100 Subject: [PATCH] Added AmountAdjustments --- .../codinux/invoicing/mapper/MustangMapper.kt | 50 ++++++++++++++++++- .../invoicing/model/AmountAdjustments.kt | 22 ++++++++ .../invoicing/model/ChargeOrAllowance.kt | 44 ++++++++++++++++ .../net/codinux/invoicing/model/Invoice.kt | 4 +- 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/AmountAdjustments.kt create mode 100644 e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/ChargeOrAllowance.kt diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt index c45200a..c1177d8 100644 --- a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt @@ -1,10 +1,13 @@ package net.codinux.invoicing.mapper +import net.codinux.invoicing.model.AmountAdjustments +import net.codinux.invoicing.model.ChargeOrAllowance import net.codinux.invoicing.model.InvoiceItem import net.codinux.invoicing.model.Party import org.mustangproject.* import org.mustangproject.ZUGFeRD.IExportableTransaction import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem +import java.math.BigDecimal import java.time.Instant import java.time.LocalDate import java.time.ZoneId @@ -24,6 +27,12 @@ open class MustangMapper { this.paymentTermDescription = invoice.paymentDescription this.referenceNumber = invoice.buyerReference + + invoice.amountAdjustments?.let { adjustments -> + this.totalPrepaidAmount = adjustments.prepaidAmounts + adjustments.charges.forEach { this.addCharge(mapCharge(it)) } + adjustments.allowances.forEach { this.addAllowance(mapAllowance(it)) } + } } open fun mapParty(party: Party): TradeParty = TradeParty( @@ -52,6 +61,26 @@ open class MustangMapper { } + protected open fun mapCharge(charge: ChargeOrAllowance) = Charge(charge.actualAmount).apply { + this.percent = charge.calculationPercent + + this.reason = charge.reason + this.reasonCode = charge.reasonCode + + this.taxPercent = charge.taxRateApplicablePercent + this.categoryCode = charge.taxCategoryCode + } + + protected open fun mapAllowance(allowance: ChargeOrAllowance) = Allowance(allowance.actualAmount).apply { + this.percent = allowance.calculationPercent + + this.reason = allowance.reason + this.reasonCode = allowance.reasonCode + + this.taxPercent = allowance.taxRateApplicablePercent + this.categoryCode = allowance.taxCategoryCode + } + open fun mapToInvoice(invoice: Invoice) = net.codinux.invoicing.model.Invoice( invoiceNumber = invoice.number, @@ -63,7 +92,9 @@ open class MustangMapper { dueDate = map(invoice.dueDate ?: invoice.paymentTerms?.dueDate), paymentDescription = invoice.paymentTermDescription ?: invoice.paymentTerms?.description, - buyerReference = invoice.referenceNumber + buyerReference = invoice.referenceNumber, + + amountAdjustments = mapAmountAdjustments(invoice), ) open fun mapParty(party: TradeParty) = Party( @@ -76,6 +107,23 @@ open class MustangMapper { item.product.name, item.quantity, item.product.unit, item.price, item.product.vatPercent, item.product.description.takeUnless { it.isBlank() } ) + protected open fun mapAmountAdjustments(invoice: Invoice): AmountAdjustments? { + if ((invoice.totalPrepaidAmount == null || invoice.totalPrepaidAmount == BigDecimal.ZERO) && invoice.zfCharges.isEmpty() && invoice.zfAllowances.isEmpty()) { + return null + } + + return AmountAdjustments( + invoice.totalPrepaidAmount, + invoice.zfCharges.mapNotNull { mapChargeOrAllowance(it as? Charge) }, + invoice.zfAllowances.mapNotNull { mapChargeOrAllowance(it as? Allowance ?: it as? Charge) } + ) + } + + private fun mapChargeOrAllowance(chargeOrAllowance: Charge?) = chargeOrAllowance?.let { + ChargeOrAllowance(it.totalAmount, null, null, it.percent, it.reason, it.reasonCode, it.taxPercent, it.categoryCode) + } + + @JvmName("mapNullable") protected fun map(date: LocalDate?) = diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/AmountAdjustments.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/AmountAdjustments.kt new file mode 100644 index 0000000..97683d1 --- /dev/null +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/AmountAdjustments.kt @@ -0,0 +1,22 @@ +package net.codinux.invoicing.model + +import java.math.BigDecimal + +class AmountAdjustments( + /** + * Vorauszahlungen. + */ + val prepaidAmounts: BigDecimal = BigDecimal.ZERO, + + /** + * Zusätzliche Gebühren. + */ + val charges: List = emptyList(), + + /** + * Abzüge / Nachlässe. + */ + val allowances: List = emptyList() +) { + override fun toString() = "${prepaidAmounts.toPlainString()} prepaid, ${charges.size} charges, ${allowances.size} allowances" +} \ No newline at end of file diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/ChargeOrAllowance.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/ChargeOrAllowance.kt new file mode 100644 index 0000000..cefad73 --- /dev/null +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/ChargeOrAllowance.kt @@ -0,0 +1,44 @@ +package net.codinux.invoicing.model + +import java.math.BigDecimal + +class ChargeOrAllowance( + /** + * Gesamtbetrag der Gebühr oder des Nachlasses. + * Evtl. berechnet aus [basisAmount] und [calculationPercent]. + */ + val actualAmount: BigDecimal, + + /** + * Der Ausgangsbetrag auf den die Gebühr oder der Nachlass angewendet wird. + */ + val basisAmount: BigDecimal? = null, + /** + * Menge der Ware oder Dienstleistung auf die die Gebühr oder der Nachlass angewendet wird. + */ + val basisQuantity: BigDecimal? = null, + /** + * Der auf [basisAmount] anwendbare Prozentsatz der Gebühr oder des Nachlasses. + */ + val calculationPercent: BigDecimal? = null, + + /** + * Menschenlesbare Beschreibung der Gebühr oder des Nachlasses. + */ + val reason: String? = null, + /** + * Code für Begründung der Gebühr oder des Nachlasses aus [UNTDID 5189](https://unece.org/fileadmin/DAM/trade/untdid/d16b/tred/tred5189.htm). + */ + val reasonCode: String? = null, + + /** + * Anzuwendender Steuersatz (z. B. in Deutschland 7 % oder 19 %). + */ + val taxRateApplicablePercent: BigDecimal? = null, + /** + * Steuer Kategorie Code + */ + val taxCategoryCode: String? = null +) { + override fun toString() = "${reason?.let { "$it: " } ?: ""}${actualAmount.toPlainString()}" +} \ No newline at end of file diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/Invoice.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/Invoice.kt index 2e09a04..2ea6655 100644 --- a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/Invoice.kt +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/model/Invoice.kt @@ -15,7 +15,9 @@ class Invoice( /** * Unique reference number of the buyer, e.g. the Leitweg-ID required by German authorities (Behörden) */ - val buyerReference: String? = null + val buyerReference: String? = null, + + val amountAdjustments: AmountAdjustments? = null, ) { override fun toString() = "$invoicingDate $invoiceNumber to $recipient" } \ No newline at end of file