mirror of
https://github.com/corda/corda.git
synced 2025-01-11 15:32:49 +00:00
Add functions for converting Amount to/from decimal
Add new functions for converting amounts to/from decimal representation. Also adds clarification that the constructor which takes in a BigDecimal drops any fractional part. Signed-off-by: Ross Nicoll <ross.nicoll@r3.com>
This commit is contained in:
parent
70bf16be99
commit
c1d5e5c82e
@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
|||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
import com.google.common.annotations.VisibleForTesting
|
import com.google.common.annotations.VisibleForTesting
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
import java.math.BigInteger
|
||||||
import java.time.DayOfWeek
|
import java.time.DayOfWeek
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
@ -34,6 +35,19 @@ import java.util.*
|
|||||||
* @param T the type of the token, for example [Currency].
|
* @param T the type of the token, for example [Currency].
|
||||||
*/
|
*/
|
||||||
data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
|
data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Build an amount from a decimal representation. For example, with an input of "12.34" GBP,
|
||||||
|
* returns an amount with a quantity of "1234".
|
||||||
|
*
|
||||||
|
* @see Amount<Currency>.toDecimal
|
||||||
|
*/
|
||||||
|
fun fromDecimal(quantity: BigDecimal, currency: Currency) : Amount<Currency> {
|
||||||
|
val longQuantity = quantity.movePointRight(currency.defaultFractionDigits).toLong()
|
||||||
|
return Amount(longQuantity, currency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Negative amounts are of course a vital part of any ledger, but negative values are only valid in certain
|
// Negative amounts are of course a vital part of any ledger, but negative values are only valid in certain
|
||||||
// contexts: you cannot send a negative amount of cash, but you can (sometimes) have a negative balance.
|
// contexts: you cannot send a negative amount of cash, but you can (sometimes) have a negative balance.
|
||||||
@ -41,7 +55,12 @@ data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
|
|||||||
require(quantity >= 0) { "Negative amounts are not allowed: $quantity" }
|
require(quantity >= 0) { "Negative amounts are not allowed: $quantity" }
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(amount: BigDecimal, currency: T) : this(amount.toLong(), currency)
|
/**
|
||||||
|
* Construct the amount using the given decimal value as quantity. Any fractional part
|
||||||
|
* is discarded. To convert and use the fractional part, see [fromDecimal].
|
||||||
|
*/
|
||||||
|
constructor(quantity: BigDecimal, token: T) : this(quantity.toLong(), token)
|
||||||
|
constructor(quantity: BigInteger, token: T) : this(quantity.toLong(), token)
|
||||||
|
|
||||||
operator fun plus(other: Amount<T>): Amount<T> {
|
operator fun plus(other: Amount<T>): Amount<T> {
|
||||||
checkCurrency(other)
|
checkCurrency(other)
|
||||||
@ -70,6 +89,14 @@ data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a currency [Amount] to a decimal representation. For example, with an amount with a quantity
|
||||||
|
* of "1234" GBP, returns "12.34".
|
||||||
|
*
|
||||||
|
* @see Amount.Companion.fromDecimal
|
||||||
|
*/
|
||||||
|
fun Amount<Currency>.toDecimal() : BigDecimal = BigDecimal(quantity).movePointLeft(token.defaultFractionDigits)
|
||||||
|
|
||||||
fun <T> Iterable<Amount<T>>.sumOrNull() = if (!iterator().hasNext()) null else sumOrThrow()
|
fun <T> Iterable<Amount<T>>.sumOrNull() = if (!iterator().hasNext()) null else sumOrThrow()
|
||||||
fun <T> Iterable<Amount<T>>.sumOrThrow() = reduce { left, right -> left + right }
|
fun <T> Iterable<Amount<T>>.sumOrThrow() = reduce { left, right -> left + right }
|
||||||
fun <T> Iterable<Amount<T>>.sumOrZero(currency: T) = if (iterator().hasNext()) sumOrThrow() else Amount(0, currency)
|
fun <T> Iterable<Amount<T>>.sumOrZero(currency: T) = if (iterator().hasNext()) sumOrThrow() else Amount(0, currency)
|
||||||
|
26
core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt
Normal file
26
core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package net.corda.core.contracts
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests of the [Amount] class.
|
||||||
|
*/
|
||||||
|
class AmountTests {
|
||||||
|
@Test
|
||||||
|
fun basicCurrency() {
|
||||||
|
val expected = 1000L
|
||||||
|
val amount = Amount(expected, GBP)
|
||||||
|
assertEquals(expected, amount.quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decimalConversion() {
|
||||||
|
val quantity = 1234L
|
||||||
|
val amount = Amount(quantity, GBP)
|
||||||
|
val expected = BigDecimal("12.34")
|
||||||
|
assertEquals(expected, amount.toDecimal())
|
||||||
|
assertEquals(amount, Amount.fromDecimal(amount.toDecimal(), amount.token))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user