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:
Ross Nicoll 2017-01-11 14:07:38 +00:00
parent 70bf16be99
commit c1d5e5c82e
2 changed files with 54 additions and 1 deletions

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.google.common.annotations.VisibleForTesting
import java.math.BigDecimal
import java.math.BigInteger
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@ -34,6 +35,19 @@ import java.util.*
* @param T the type of the token, for example [Currency].
*/
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 {
// 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.
@ -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" }
}
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> {
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>>.sumOrThrow() = reduce { left, right -> left + right }
fun <T> Iterable<Amount<T>>.sumOrZero(currency: T) = if (iterator().hasNext()) sumOrThrow() else Amount(0, currency)

View 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))
}
}