From 31760ee901482c751d976c4b9e0cf6f3a73609c6 Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Mon, 8 Feb 2021 12:39:52 +0700 Subject: [PATCH] Add and updated for whole the money modules with new style and some operators and computation But money object not full implementation yet, because we need the exchange and filter the object states for money --- src/main/kotlin/com/cubetiqs/money/Money.kt | 75 +++++++++++++++---- .../com/cubetiqs/money/MoneyArithmetic.kt | 5 +- .../kotlin/com/cubetiqs/money/MoneyConfig.kt | 4 +- .../com/cubetiqs/money/MoneyCurrency.kt | 44 ++++++++++- .../cubetiqs/money/MoneyExchangeAdapter.kt | 5 -- .../cubetiqs/money/MoneyExchangeProvider.kt | 5 ++ .../com/cubetiqs/money/MoneyExchangeUtils.kt | 10 ++- .../com/cubetiqs/money/MoneyExtension.kt | 4 +- .../kotlin/com/cubetiqs/money/MoneyHistory.kt | 15 ++++ .../kotlin/com/cubetiqs/money/MoneyObject.kt | 47 ++++++++++++ .../com/cubetiqs/money/MoneyOperator.kt | 21 ++++-- .../kotlin/com/cubetiqs/money/MoneyState.kt | 15 ++++ .../kotlin/com/cubetiqs/money/StdMoney.kt | 28 ++++++- src/test/kotlin/MoneyTests.kt | 33 +------- 14 files changed, 240 insertions(+), 71 deletions(-) delete mode 100644 src/main/kotlin/com/cubetiqs/money/MoneyExchangeAdapter.kt create mode 100644 src/main/kotlin/com/cubetiqs/money/MoneyExchangeProvider.kt create mode 100644 src/main/kotlin/com/cubetiqs/money/MoneyHistory.kt create mode 100644 src/main/kotlin/com/cubetiqs/money/MoneyObject.kt create mode 100644 src/main/kotlin/com/cubetiqs/money/MoneyState.kt diff --git a/src/main/kotlin/com/cubetiqs/money/Money.kt b/src/main/kotlin/com/cubetiqs/money/Money.kt index bee555f..6e22ac9 100644 --- a/src/main/kotlin/com/cubetiqs/money/Money.kt +++ b/src/main/kotlin/com/cubetiqs/money/Money.kt @@ -1,17 +1,21 @@ package com.cubetiqs.money open class Money( - var value: Double, - @SpecialString(trim = true, upperCase = true) private var currency: String = "USD" -) : StdMoney { - + private var value: Double = 0.0, + private val currency: StdMoney.Currency = MoneyCurrency.USD, +) : StdMoney, StdMoney.Operator, StdMoney.ExchangeOperator { //////////////////// - PROPERTIES - //////////////////// override fun getMoneyValue(): Double { return this.value } - override fun getMoneyCurrency(): String { + // not imply with exchange rate yet + override fun StdMoney.getExchangedTo(currency: StdMoney.Currency): Double { + return getMoneyValue() + } + + override fun getMoneyCurrency(): StdMoney.Currency { return this.currency } @@ -21,11 +25,48 @@ open class Money( return "Money(value=${getMoneyValue()}, currency='${getMoneyCurrency()}')" } + override fun inc(): Money = apply { + this.value += 1 + } + + override fun dec(): Money = apply { + this.value -= 1 + } + + override fun plus(other: StdMoney): Money { + val temp = this.value + other.getExchangedTo(this.currency) + return Money(value = temp, currency = this.currency) + } + + override fun divide(other: StdMoney): Money { + val temp = this.value / other.getExchangedTo(this.currency) + return Money(value = temp, currency = this.currency) + } + + override fun multiply(other: StdMoney): Money { + val temp = this.value * other.getExchangedTo(this.getMoneyCurrency()) + return Money(value = temp, currency = this.currency) + } + + override fun plusAssign(other: StdMoney) { + this.value = this.value + other.getExchangedTo(this.currency) + } + + override fun divideAssign(other: StdMoney) { + this.value = this.value / other.getExchangedTo(this.currency) + } + + override fun multiplyAssign(other: StdMoney) { + this.value = this.value * other.getExchangedTo(this.getMoneyCurrency()) + } + companion object { val ZERO: StdMoney - get() = Money(value = 0.0) + get() = Money() + val ONE: StdMoney get() = Money(value = 1.0) + val TEN: StdMoney get() = Money(value = 10.0) @@ -33,18 +74,22 @@ open class Money( * Create a new money object with custom value * * @param value Double - * @param currency String + * @param currency MoneyCurrency */ - fun create(value: Double, currency: String = MoneyCurrency.USD.name): StdMoney { + fun create(value: Double, currency: StdMoney.Currency): Money { return Money(value = value, currency = currency) } - /** - * Create a new money object with custom value - * - * @param value Double - * @param currency MoneyCurrency - */ - fun create(value: Double, currency: MoneyCurrency = MoneyCurrency.USD): StdMoney = create(value, currency.name) + fun from(value: Double, currency: String): Money { + return create(value, object : StdMoney.Currency { + override fun getCurrency(): String { + return currency.toUpperCase().trim() + } + }) + } + + fun from(money: StdMoney): Money { + return create(value = money.getMoneyValue(), currency = money.getMoneyCurrency()) + } } } diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyArithmetic.kt b/src/main/kotlin/com/cubetiqs/money/MoneyArithmetic.kt index 0d410e2..10b3f82 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyArithmetic.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyArithmetic.kt @@ -1,5 +1,6 @@ package com.cubetiqs.money -fun StdMoney.addMoney(value: Double, currency: String): StdMoney { - return this + Money.create(value, currency) +fun StdMoney.addMoney(value: Double, currency: StdMoney.Currency): StdMoney { + this + Money.create(value, currency) + return this } \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt b/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt index ef919fa..9f3b433 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt @@ -67,8 +67,8 @@ object MoneyConfig { fun getConfig() = config @Throws(MoneyCurrencyStateException::class) - fun getRate(currency: String): Double { - return getConfig()[currency.toUpperCase()] + fun getRate(currency: StdMoney.Currency): Double { + return getConfig()[currency.getCurrency().toUpperCase()] ?: throw MoneyCurrencyStateException("money currency $currency is not valid!") } diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyCurrency.kt b/src/main/kotlin/com/cubetiqs/money/MoneyCurrency.kt index 3bdbf45..fdf7934 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyCurrency.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyCurrency.kt @@ -1,7 +1,43 @@ package com.cubetiqs.money -enum class MoneyCurrency { - USD, - KHR, - EUR +import java.io.Serializable + +/** + * Money Currency Object with flexible currency based on data and configs + * + * @author sombochea + * @since 1.0 + */ +open class MoneyCurrency( + private val name: String, + private val symbol: String? = null, + private val configs: Map? = null, +) : Serializable, StdMoney.Currency { + override fun getCurrency(): String { + return name.toUpperCase().trim() + } + + fun getSymbol(): String? { + return symbol ?: getConfigs()["symbol"]?.toString() + } + + fun getConfigs(): Map { + return configs ?: emptyMap() + } + + override fun toString(): String { + return "MoneyCurrency(name='$name', symbol=$symbol, configs=$configs)" + } + + companion object { + fun create(name: String): MoneyCurrency { + return MoneyCurrency(name = name) + } + + val USD + get() = create("USD") + + val KHR + get() = create("KHR") + } } \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyExchangeAdapter.kt b/src/main/kotlin/com/cubetiqs/money/MoneyExchangeAdapter.kt deleted file mode 100644 index d2cbdb9..0000000 --- a/src/main/kotlin/com/cubetiqs/money/MoneyExchangeAdapter.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.cubetiqs.money - -interface MoneyExchangeAdapter { - fun getRate(currency: String): Double -} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyExchangeProvider.kt b/src/main/kotlin/com/cubetiqs/money/MoneyExchangeProvider.kt new file mode 100644 index 0000000..96547b2 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/money/MoneyExchangeProvider.kt @@ -0,0 +1,5 @@ +package com.cubetiqs.money + +interface MoneyExchangeProvider { + fun getRate(currency: StdMoney.Currency): Double +} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyExchangeUtils.kt b/src/main/kotlin/com/cubetiqs/money/MoneyExchangeUtils.kt index 0b2f01d..7f6a3b5 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyExchangeUtils.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyExchangeUtils.kt @@ -1,7 +1,7 @@ package com.cubetiqs.money object MoneyExchangeUtils { - fun exchange(exchangeFrom: StdMoney, exchangeToCurrency: String): StdMoney { + fun exchange(exchangeFrom: StdMoney, exchangeToCurrency: StdMoney.Currency): StdMoney { val rateFrom = MoneyConfig.getRate(exchangeFrom.getMoneyCurrency()) val rateTo = MoneyConfig.getRate(exchangeToCurrency) return Money(value = computeRate(rateFrom, rateTo, amountFrom = exchangeFrom.getMoneyValue()), currency = exchangeToCurrency) @@ -10,4 +10,12 @@ object MoneyExchangeUtils { private fun computeRate(rateFrom: Double, rateTo: Double, baseRate: Double = 1.0, amountFrom: Double = 1.0): Double { return amountFrom * ((baseRate / rateFrom) / (baseRate / rateTo)) } + + fun getBaseCurrency(): MoneyCurrency { + return MoneyCurrency.USD + } + + fun getExchangeRate(currency: MoneyCurrency): Double { + return 0.0 + } } \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyExtension.kt b/src/main/kotlin/com/cubetiqs/money/MoneyExtension.kt index e4d2783..2afabf3 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyExtension.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyExtension.kt @@ -1,7 +1,7 @@ package com.cubetiqs.money -fun StdMoney.exchangeTo(currency: String): StdMoney { +fun StdMoney.exchangeTo(currency: StdMoney.Currency): StdMoney { return MoneyExchangeUtils.exchange(this, currency) } -fun StdMoney.isMatchedCurrency(currency: String) = this.getMoneyCurrency().equals(currency, ignoreCase = true) \ No newline at end of file +fun StdMoney.isMatchedCurrency(currency: StdMoney.Currency) = this.getMoneyCurrency().getCurrency().equals(currency.getCurrency(), ignoreCase = true) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyHistory.kt b/src/main/kotlin/com/cubetiqs/money/MoneyHistory.kt new file mode 100644 index 0000000..5af4193 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/money/MoneyHistory.kt @@ -0,0 +1,15 @@ +package com.cubetiqs.money + +import java.io.Serializable + +/** + * @author sombochea + * @email sombochea@cubetiqs.com> + * @date 08/02/21 + * @since 1.0 + */ +data class MoneyHistory( + val valueOn: String? = null, + val valueOf: String? = null, + val currency: String? = null +) : Serializable \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt b/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt new file mode 100644 index 0000000..00514bb --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt @@ -0,0 +1,47 @@ +package com.cubetiqs.money + +import java.io.Serializable + +sealed class MoneyObject : Serializable, StdMoney { + private var value: Double = 0.0 + private var currency: MoneyCurrency = MoneyCurrency.USD + private val moneyStates: MutableMap = mutableMapOf() + + constructor(value: Double, currency: MoneyCurrency) { + this.value = value + this.currency = currency + isComputed = false + } + + /** + * Check if computed, set it to true. Because we no need to add the same value again after once + * computed. + */ + @Transient + var isComputed = false + private set + + @Transient + var isInit = false + private set + + /** Calculate the value. Must be call after complete add money. */ + fun compute(): MoneyObject = apply { + + } + + companion object { + @JvmStatic + private fun defaultCurrency(): MoneyCurrency { + return MoneyExchangeUtils.getBaseCurrency() + } + } + + override fun getMoneyValue(): Double { + return value + } + + override fun getMoneyCurrency(): StdMoney.Currency { + return currency + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyOperator.kt b/src/main/kotlin/com/cubetiqs/money/MoneyOperator.kt index 193b67c..d862856 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyOperator.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyOperator.kt @@ -1,12 +1,17 @@ package com.cubetiqs.money +// unary operators operator fun StdMoney.unaryMinus() = (-getMoneyValue()) operator fun StdMoney.unaryPlus() = (+getMoneyValue()) -operator fun Money.inc() = Money(value++) -operator fun Money.dec() = Money(value--) -operator fun StdMoney.plus(other: StdMoney) = Money(getMoneyValue() + other.getMoneyValue()) -operator fun StdMoney.times(other: StdMoney) = Money(getMoneyValue() * other.getMoneyValue()) -operator fun StdMoney.div(other: StdMoney) = Money(getMoneyValue() / other.getMoneyValue()) -operator fun Money.timesAssign(other: StdMoney) { - this.value = this.getMoneyValue() * other.getMoneyValue() -} \ No newline at end of file + +// operators +operator fun StdMoney.inc() = Money.from(this).inc() +operator fun StdMoney.dec() = Money.from(this).dec() +operator fun StdMoney.plus(other: StdMoney) = Money.from(this).plusAssign(other) +operator fun StdMoney.times(other: StdMoney) = Money.from(this).multiplyAssign(other) +operator fun StdMoney.div(other: StdMoney) = Money.from(this).divideAssign(other) + +// assign operators +operator fun Money.timesAssign(other: StdMoney) = this.multiplyAssign(other) +operator fun Money.plusAssign(other: StdMoney) = this.plusAssign(other) +operator fun Money.divAssign(other: StdMoney) = this.divideAssign(other) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyState.kt b/src/main/kotlin/com/cubetiqs/money/MoneyState.kt new file mode 100644 index 0000000..463728e --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/money/MoneyState.kt @@ -0,0 +1,15 @@ +package com.cubetiqs.money + +import java.io.Serializable + +/** + * @author sombochea + * @email sombochea@cubetiqs.com + * @date 08/02/21 + * @since 1.0 + */ +data class MoneyState( + var value: String? = null, + var currency: String? = null, + var history: MoneyHistory? = null +) : Serializable \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/StdMoney.kt b/src/main/kotlin/com/cubetiqs/money/StdMoney.kt index 29d20cc..33074c9 100644 --- a/src/main/kotlin/com/cubetiqs/money/StdMoney.kt +++ b/src/main/kotlin/com/cubetiqs/money/StdMoney.kt @@ -20,6 +20,30 @@ interface StdMoney { * * @return String */ - @SpecialString - fun getMoneyCurrency(): String + fun getMoneyCurrency(): Currency + + /** + * Allow for money currency called and implemented + */ + interface Currency { + fun getCurrency(): String + } + + interface ExchangeOperator { + fun StdMoney.getExchangedTo(currency: Currency): Double + } + + interface Operator { + fun plus(other: StdMoney): T + fun divide(other: StdMoney): T + + fun inc(): T + fun dec(): T + fun multiply(other: StdMoney): T + + // assign operators + fun plusAssign(other: StdMoney) + fun divideAssign(other: StdMoney) + fun multiplyAssign(other: StdMoney) + } } \ No newline at end of file diff --git a/src/test/kotlin/MoneyTests.kt b/src/test/kotlin/MoneyTests.kt index 56fda11..353e46c 100644 --- a/src/test/kotlin/MoneyTests.kt +++ b/src/test/kotlin/MoneyTests.kt @@ -2,23 +2,10 @@ import com.cubetiqs.money.Money import com.cubetiqs.money.MoneyConfig import com.cubetiqs.money.MoneyCurrency import com.cubetiqs.money.MoneyExchangeUtils -import com.cubetiqs.money.SpecialStringProcessor import org.junit.Assert import org.junit.Test class MoneyTests { - @Test - fun money_operator_test() { - // val money = Money(10.0) - // val money2 = Money(20.0) - // money *= money - // println((money + money2) * money2) - // Assert.assertEquals(100.0, money.value, 0.0) - - val test = SpecialStringProcessor().serialize(Money(1.0, " usd ")) - println(test) - } - @Test fun exchange_2usd_to_khr_test() { val properties = MoneyConfig @@ -32,27 +19,13 @@ class MoneyTests { .setProperties(properties) .parse("USD:1,KHR:4000") + // Is valid for money config? Assert.assertTrue(MoneyConfig.isValid()) - println(MoneyConfig.getConfig()) - val moneyUsd = Money(2.0) - val moneyKhr = MoneyExchangeUtils.exchange(moneyUsd, "KHR") + val moneyKhr = MoneyExchangeUtils.exchange(moneyUsd, MoneyCurrency.create("KHR")) + // Is correct exchange? Assert.assertEquals(8000.0, moneyKhr.getMoneyValue(), 0.0) } - - @Test - fun money_exchange_config_builder_test() { - MoneyConfig.propertiesBuilder - .setDeliEqual('=') - .setDeliSplit(';') - - MoneyConfig.parse("USD:1,KHR=4000,EUR=0.99") - - val moneyUsd = Money.ONE - val moneyKhr = Money.create(20000.0, MoneyCurrency.KHR) - - val result = moneyUsd - } } \ No newline at end of file