Compare commits
13 Commits
003e1a59db
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
9b328c93c7
|
|||
|
6deb73600d
|
|||
|
|
aaf3c6aea8 | ||
| c4e7f514cb | |||
| f6914f5282 | |||
| 88abd05184 | |||
|
|
78578cf1cc | ||
|
|
c5395b89ef | ||
| 0a330a0bc1 | |||
| f89b0cf01d | |||
| 8509c578ca | |||
| 1a447f1954 | |||
| 0676972a83 |
139
README.md
Normal file
139
README.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Money Object Module
|
||||||
|
- Allow to do better with money module
|
||||||
|
- Exchangeable for different currencies with custom override
|
||||||
|
- Add money config able to custom by set of config
|
||||||
|
- So on.
|
||||||
|
|
||||||
|
### Example (Tests)
|
||||||
|
```kotlin
|
||||||
|
import com.cubetiqs.money.*
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MoneyTests {
|
||||||
|
private fun initMoneyConfig() {
|
||||||
|
applyMoneyConfig {
|
||||||
|
setProperties(buildMoneyConfigProperties {
|
||||||
|
setDeliEqual(':')
|
||||||
|
setDeliSplit(',')
|
||||||
|
})
|
||||||
|
|
||||||
|
fromJsonRates(MyBatchRates.getJsonRates())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object MyBatchRates {
|
||||||
|
fun getJsonRates(): String {
|
||||||
|
return """
|
||||||
|
{"USD": 1.0,"KHR": 4000.0, "eur": 0.5}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun exchange_2usd_to_khr_test() {
|
||||||
|
initMoneyConfig()
|
||||||
|
|
||||||
|
// Is valid for money config?
|
||||||
|
Assert.assertTrue(MoneyConfig.isConfigRatesValid())
|
||||||
|
|
||||||
|
// arithmetic operators calculation
|
||||||
|
val moneyUsd =
|
||||||
|
(2 withCurrency "usd") divideWith (2 withCurrency "usd") plusOf 1 minusOf 1 plusOf 1 multiplyOf 2 divideOf 2 divideWith (8000 withCurrency "khr") plusOf 1
|
||||||
|
val moneyKhr = moneyUsd exchangeTo "khr"
|
||||||
|
|
||||||
|
// Is correct exchange?
|
||||||
|
Assert.assertEquals(8000.0, moneyKhr.getValue(), 0.0)
|
||||||
|
|
||||||
|
// complex operators and exchanging
|
||||||
|
val sum =
|
||||||
|
((moneyUsd + moneyKhr) * Money.TEN) exchangeTo MoneyCurrency.KHR minusWith (Money.ONE exchangeTo MoneyCurrency.KHR)
|
||||||
|
|
||||||
|
Assert.assertEquals(156000.0, sum.getValue(), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun moneyGenerator() {
|
||||||
|
initMoneyConfig()
|
||||||
|
|
||||||
|
val moneyGen = MoneyObject(
|
||||||
|
value = 1.0,
|
||||||
|
currency = "usd",
|
||||||
|
operator = MoneyObject.MoneyOperator.PLUS,
|
||||||
|
with = MoneyObject(
|
||||||
|
value = 8000.0,
|
||||||
|
currency = "khr",
|
||||||
|
operator = MoneyObject.MoneyOperator.MINUS,
|
||||||
|
with = MoneyObject(
|
||||||
|
value = 1000.0,
|
||||||
|
currency = "khr",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = moneyGen.compute()
|
||||||
|
Assert.assertEquals(2.75, result.getValue(), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun moneyGeneratorBuilder() {
|
||||||
|
initMoneyConfig()
|
||||||
|
|
||||||
|
val expected = 72000.0
|
||||||
|
|
||||||
|
val builder = MoneyObject.builder()
|
||||||
|
.with(10.0, "usd", '+')
|
||||||
|
.with(1.5, "eur", '+')
|
||||||
|
.with(8000.0, "khr")
|
||||||
|
.with(10000.0, "khr")
|
||||||
|
.with(2000.0, "khr")
|
||||||
|
.with(.5, "eur", '-')
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.withCurrency("khr")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val result = builder.compute()
|
||||||
|
Assert.assertEquals(expected, result.getValue(), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun moneyGeneratorBuilderWithStringValues() {
|
||||||
|
initMoneyConfig()
|
||||||
|
|
||||||
|
val values = "usd:1:+,khr:4000:-,usd:1:+,eur:1:+" // result = 3
|
||||||
|
val expected1 = 3.0
|
||||||
|
val expected2 = 3.5
|
||||||
|
|
||||||
|
val builder = MoneyObject.builder()
|
||||||
|
.withCurrency("usd")
|
||||||
|
.with(1.0, "usd", '-')
|
||||||
|
.with(4000.0, "khr")
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.with(1.0, "eur", '-') // 2 usd
|
||||||
|
.with(1.0, "usd")
|
||||||
|
.with(2.0, "usd", '/')
|
||||||
|
.with(2.0, "usd")
|
||||||
|
.build() // 3.5
|
||||||
|
|
||||||
|
val result1 = MoneyObject.builder()
|
||||||
|
.parseFromString(values)
|
||||||
|
.withCurrency("usd")
|
||||||
|
.build()
|
||||||
|
.compute()
|
||||||
|
val result2 = builder.compute()
|
||||||
|
|
||||||
|
Assert.assertEquals(expected1, result1.getValue(), 0.0)
|
||||||
|
Assert.assertEquals(expected2, result2.getValue(), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun moneyFormatterTest() {
|
||||||
|
val systemCurrency = Currency.getInstance("USD").symbol
|
||||||
|
println(systemCurrency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
package com.cubetiqs.money
|
package com.cubetiqs.money
|
||||||
|
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default money config in static object.
|
* Default money config in static object.
|
||||||
* Sample parse format: USD=1,KHR=4000,EUR=0.99
|
* Sample parse format: USD=1,KHR=4000,EUR=0.99
|
||||||
@@ -8,27 +13,74 @@ package com.cubetiqs.money
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
object MoneyConfig {
|
object MoneyConfig {
|
||||||
|
private const val CONFIG_RATE_INITIAL_SIZE = 10
|
||||||
|
private const val CONFIG_FORMATTER_INITIAL_SIZE = 10
|
||||||
|
private const val LOCALES_INITIAL_SIZE = 5
|
||||||
|
private const val CURRENCIES_INITIAL_SIZE = 5
|
||||||
|
private const val CONFIG_INITIAL_SIZE = 50
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All money currencies and its rate are stored in memory state for exchange or compute the value.
|
* All money currencies and its rate are stored in memory state for exchange or compute the value.
|
||||||
*
|
*
|
||||||
* Key is the currency
|
* Key is the currency
|
||||||
* Value is the rate
|
* Value is the rate
|
||||||
*/
|
*/
|
||||||
private val config: MutableMap<String, Double> = mutableMapOf()
|
private val configRates: ConcurrentMap<String, Double> = ConcurrentHashMap(CONFIG_RATE_INITIAL_SIZE)
|
||||||
|
|
||||||
// use to format the money for each value, if have
|
// use to format the money for each value, if have
|
||||||
private val configFormatter: MutableMap<String, MoneyFormatProvider> = mutableMapOf()
|
private val configFormatter: ConcurrentMap<String, MoneyFormatProvider> =
|
||||||
|
ConcurrentHashMap(CONFIG_FORMATTER_INITIAL_SIZE)
|
||||||
|
|
||||||
|
// use to custom locales and allow to detect auto formatter, if enable auto locale format
|
||||||
|
private val configLocales: ConcurrentMap<String, Locale> = ConcurrentHashMap(LOCALES_INITIAL_SIZE)
|
||||||
|
|
||||||
|
// use to custom currencies and allow to detect auto, when use auto-detect for currencies translate
|
||||||
|
private val configCurrencies: ConcurrentMap<String, Currency> = ConcurrentHashMap(CURRENCIES_INITIAL_SIZE)
|
||||||
|
|
||||||
|
// allow to use config of any states
|
||||||
|
private val configs: ConcurrentMap<String, Any?> = ConcurrentHashMap(CONFIG_INITIAL_SIZE)
|
||||||
|
|
||||||
|
// allow to set the value into custom config
|
||||||
|
fun setConfig(key: String, value: Any?) = apply {
|
||||||
|
if (configs.containsKey(key)) {
|
||||||
|
configs.replace(key, value)
|
||||||
|
} else {
|
||||||
|
this.configs[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow to get the value from custom config
|
||||||
|
fun <T> getConfigOrDefault(key: String, defaultValue: T? = null): T? {
|
||||||
|
return (try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
configs[key] as? T
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
null
|
||||||
|
}) ?: defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
// use to identified for config dataset with prefix mode
|
// use to identified for config dataset with prefix mode
|
||||||
private var configPrefix: String = ""
|
private var configPrefix: String = ""
|
||||||
|
private fun isConfigPrefixValid() = configPrefix.isNotEmpty() && configPrefix.isNotBlank()
|
||||||
|
|
||||||
// use to fallback, if the currency not found
|
// use to fallback, if the currency not found
|
||||||
// if the fallback greater than ZERO, then called it
|
// if the fallback greater than ZERO, then called it
|
||||||
// else throws
|
// else throws
|
||||||
private var fallbackRate: Double = 0.0
|
private var fallbackRate: Double = 0.0
|
||||||
|
|
||||||
// validate the config, if have it's valid
|
fun getConfigs(): Map<String, Any?> {
|
||||||
fun isValid(): Boolean {
|
return mapOf(
|
||||||
return config.isNotEmpty()
|
"configRates" to configRates,
|
||||||
|
"configFormatter" to configFormatter,
|
||||||
|
"configLocales" to configLocales,
|
||||||
|
"configCurrencies" to configCurrencies,
|
||||||
|
"configs" to configs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the config rates, if have it's valid
|
||||||
|
fun isConfigRatesValid(): Boolean {
|
||||||
|
return configRates.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +108,26 @@ object MoneyConfig {
|
|||||||
this.fallbackRate = fallbackRate
|
this.fallbackRate = fallbackRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAutoLocaleFormatter(enabled: Boolean) = apply {
|
||||||
|
setConfig(getConfigKey("autoLocaleFormatter"), enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAutoCurrencyFormatter(enabled: Boolean) = apply {
|
||||||
|
setConfig(getConfigKey("autoCurrencyFormatter"), enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use to auto format, when this true
|
||||||
|
// and formatter for money value, with current locale or custom set locale
|
||||||
|
fun isAutoLocaleFormatterEnabled(): Boolean {
|
||||||
|
return getConfigOrDefault(getConfigKey("autoLocaleFormatter"), false) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// use to auto format, when this true
|
||||||
|
// and formatter for money value, with current currency or custom set currency
|
||||||
|
fun isAutoCurrencyFormatterEnabled(): Boolean {
|
||||||
|
return getConfigOrDefault(getConfigKey("autoCurrencyFormatter"), false) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
// get custom config key within currency generally
|
// get custom config key within currency generally
|
||||||
// example: myOwned_usd
|
// example: myOwned_usd
|
||||||
private fun getConfigKey(key: String): String {
|
private fun getConfigKey(key: String): String {
|
||||||
@@ -75,12 +147,12 @@ object MoneyConfig {
|
|||||||
fun parse(config: String, clearAllStates: Boolean = true) {
|
fun parse(config: String, clearAllStates: Boolean = true) {
|
||||||
// remove all states, if needed
|
// remove all states, if needed
|
||||||
if (clearAllStates) {
|
if (clearAllStates) {
|
||||||
if (configPrefix.isEmpty() || config.isBlank()) {
|
if (!isConfigPrefixValid()) {
|
||||||
MoneyConfig.config.clear()
|
configRates.clear()
|
||||||
} else {
|
} else {
|
||||||
val keys = MoneyConfig.config.filter { it.key.startsWith(prefix = configPrefix) }.keys
|
val keys = configRates.filter { it.key.startsWith(prefix = configPrefix) }.keys
|
||||||
keys.forEach { key ->
|
keys.forEach { key ->
|
||||||
MoneyConfig.config.remove(key)
|
configRates.remove(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,16 +165,16 @@ object MoneyConfig {
|
|||||||
.split(getProperties().deliEqual)
|
.split(getProperties().deliEqual)
|
||||||
if (temp.size == 2) {
|
if (temp.size == 2) {
|
||||||
val currency = temp[0]
|
val currency = temp[0]
|
||||||
.toUpperCase()
|
.uppercase()
|
||||||
.trim()
|
.trim()
|
||||||
val key = getConfigKey(currency)
|
val key = getConfigKey(currency)
|
||||||
val value = temp[1].toDouble()
|
val value = temp[1].toDouble()
|
||||||
|
|
||||||
// set the value into dataset
|
// set the value into dataset
|
||||||
if (MoneyConfig.config.containsKey(key)) {
|
if (configRates.containsKey(key)) {
|
||||||
MoneyConfig.config.replace(key, value)
|
configRates.replace(key, value)
|
||||||
} else {
|
} else {
|
||||||
MoneyConfig.config.put(key, value)
|
configRates.put(key, value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw MoneyCurrencyStateException("money config format $temp is not valid!")
|
throw MoneyCurrencyStateException("money config format $temp is not valid!")
|
||||||
@@ -113,12 +185,12 @@ object MoneyConfig {
|
|||||||
// append the rate into dataset
|
// append the rate into dataset
|
||||||
// for config key are completed change inside
|
// for config key are completed change inside
|
||||||
fun appendRate(currency: String, rate: Double) = apply {
|
fun appendRate(currency: String, rate: Double) = apply {
|
||||||
val currencyKey = currency.toUpperCase().trim()
|
val currencyKey = currency.uppercase().trim()
|
||||||
val key = getConfigKey(currencyKey)
|
val key = getConfigKey(currencyKey)
|
||||||
if (config.containsKey(key)) {
|
if (configRates.containsKey(key)) {
|
||||||
config.replace(key, rate)
|
configRates.replace(key, rate)
|
||||||
} else {
|
} else {
|
||||||
config[key] = rate
|
configRates[key] = rate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,14 +220,81 @@ object MoneyConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// all currencies with its rate
|
// all currencies with its rate
|
||||||
fun getConfig() = config
|
fun getConfigRates() = configRates
|
||||||
|
|
||||||
@Throws(MoneyCurrencyStateException::class)
|
@Throws(MoneyCurrencyStateException::class)
|
||||||
fun getRate(currency: StdMoney.Currency): Double {
|
fun getRate(currency: StdMoney.Currency): Double {
|
||||||
return getConfig()[getConfigKey(currency.getCurrency().toUpperCase().trim())]
|
return getConfigRates()[getConfigKey(currency.getCurrency().uppercase().trim())]
|
||||||
?: if (fallbackRate > 0) fallbackRate else throw MoneyCurrencyStateException("money currency ${currency.getCurrency()} is not valid!")
|
?: if (fallbackRate > 0) fallbackRate else throw MoneyCurrencyStateException("money currency ${currency.getCurrency()} is not valid!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply custom locale below
|
||||||
|
fun applyDefaultLocale(locale: Locale = Locale.getDefault()) = apply {
|
||||||
|
configLocales[MoneyFormatter.DEFAULT_LOCALE] = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
// get default locale from system or user defined
|
||||||
|
private fun getDefaultLocale(): Locale {
|
||||||
|
return configLocales[MoneyFormatter.DEFAULT_LOCALE] ?: Locale.getDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// add custom locale
|
||||||
|
fun applyLocale(locale: Locale) = apply {
|
||||||
|
if (isConfigPrefixValid()) {
|
||||||
|
if (configLocales.containsKey(configPrefix)) {
|
||||||
|
configLocales.replace(configPrefix, locale)
|
||||||
|
} else {
|
||||||
|
configLocales[configPrefix] = locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get locale by config prefix or default if not found
|
||||||
|
fun getLocale(): Locale {
|
||||||
|
if (isConfigPrefixValid() && configLocales.containsKey(configPrefix)) {
|
||||||
|
return configLocales[configPrefix] ?: getDefaultLocale()
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultLocale()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get default currency from system or user defined
|
||||||
|
private fun getDefaultCurrency(): Currency {
|
||||||
|
return configCurrencies[MoneyFormatter.DEFAULT_CURRENCY] ?: Currency.getInstance(getDefaultLocale())
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the default currency for system at runtime
|
||||||
|
fun applyDefaultCurrency(currency: StdMoney.Currency) = apply {
|
||||||
|
configCurrencies[MoneyFormatter.DEFAULT_CURRENCY] = currency.findCurrency()
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the currency into the config for system currency at runtime
|
||||||
|
fun applyCurrency(currency: StdMoney.Currency) = apply {
|
||||||
|
if (isConfigPrefixValid()) {
|
||||||
|
val systemCurrency = currency.findCurrency()
|
||||||
|
if (systemCurrency != null) {
|
||||||
|
if (configCurrencies.containsKey(configPrefix)) {
|
||||||
|
configCurrencies.replace(configPrefix, systemCurrency)
|
||||||
|
} else {
|
||||||
|
configCurrencies[configPrefix] = systemCurrency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get currency by config prefix or default if not found
|
||||||
|
fun getCurrency(): Currency {
|
||||||
|
if (isConfigPrefixValid() && configCurrencies.containsKey(configPrefix)) {
|
||||||
|
return configCurrencies[configPrefix] ?: getDefaultCurrency()
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultCurrency()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrencySymbol(): String {
|
||||||
|
return getCurrency().getSymbol(getDefaultLocale())
|
||||||
|
}
|
||||||
|
|
||||||
// apply default formatter for all not exists
|
// apply default formatter for all not exists
|
||||||
fun applyDefaultFormatter(
|
fun applyDefaultFormatter(
|
||||||
provider: MoneyFormatProvider? = null
|
provider: MoneyFormatProvider? = null
|
||||||
@@ -167,7 +306,7 @@ object MoneyConfig {
|
|||||||
|
|
||||||
// add money formatter by currency of each money value
|
// add money formatter by currency of each money value
|
||||||
fun addFormatter(currency: String, formatter: MoneyFormatProvider) = apply {
|
fun addFormatter(currency: String, formatter: MoneyFormatProvider) = apply {
|
||||||
val key = getConfigKey(currency.toUpperCase().trim())
|
val key = getConfigKey(currency.uppercase().trim())
|
||||||
if (configFormatter.containsKey(key)) {
|
if (configFormatter.containsKey(key)) {
|
||||||
configFormatter.replace(key, formatter)
|
configFormatter.replace(key, formatter)
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +318,7 @@ object MoneyConfig {
|
|||||||
fun getFormatter(currency: String? = null): MoneyFormatter {
|
fun getFormatter(currency: String? = null): MoneyFormatter {
|
||||||
// apply default formatter
|
// apply default formatter
|
||||||
val formatter = (if (!currency.isNullOrEmpty()) {
|
val formatter = (if (!currency.isNullOrEmpty()) {
|
||||||
val key = getConfigKey(currency.toUpperCase().trim())
|
val key = getConfigKey(currency.uppercase().trim())
|
||||||
configFormatter[key]
|
configFormatter[key]
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ open class MoneyCurrency(
|
|||||||
private var configs: Map<String, Any?>? = null,
|
private var configs: Map<String, Any?>? = null,
|
||||||
) : Serializable, StdMoney.Currency {
|
) : Serializable, StdMoney.Currency {
|
||||||
override fun getCurrency(): String {
|
override fun getCurrency(): String {
|
||||||
return name.toUpperCase().trim()
|
return name.uppercase().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSymbol(): String? {
|
fun getSymbol(): String? {
|
||||||
|
|||||||
@@ -14,4 +14,23 @@ object MoneyExchangeUtils {
|
|||||||
fun getBaseCurrency(): StdMoney.Currency {
|
fun getBaseCurrency(): StdMoney.Currency {
|
||||||
return StdMoney.USD
|
return StdMoney.USD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the Base Exchange Price / Converter to any Rates.
|
||||||
|
* Multiple differentiate exchange relation computation.
|
||||||
|
* Example: A -> B -> C meant You can with all variables, but must find the based MEANT of its.
|
||||||
|
*
|
||||||
|
* Sample Explanation: Matrix Multiply
|
||||||
|
* 1 USD -> 0.90 EUR
|
||||||
|
* 1 USD -> 4000 KHR
|
||||||
|
* If I want to exchange from EUR to KHR, I need.
|
||||||
|
* 1 EUR -> 1 * (1 / 0.90) / (1 / 4000)
|
||||||
|
*
|
||||||
|
* @author sombochea
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
fun computeFromBaseRate(amountFrom: Double = 1.0, baseRate: Double = 1.0, rateFrom: Double, rateTo: Double): Double {
|
||||||
|
// amount * ((baseRate / rateFrom) / (baseRate / rateTo))
|
||||||
|
return amountFrom.times((baseRate.div(rateFrom)).div(baseRate.div(rateTo)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
package com.cubetiqs.money
|
package com.cubetiqs.money
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
infix fun StdMoney.exchangeTo(currency: StdMoney.Currency): StdMoney {
|
infix fun StdMoney.exchangeTo(currency: StdMoney.Currency): StdMoney {
|
||||||
return MoneyExchangeUtils.exchange(this, currency)
|
return MoneyExchangeUtils.exchange(this, currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun StdMoney.exchangeTo(currency: String): StdMoney = this exchangeTo object : StdMoney.Currency {
|
infix fun StdMoney.exchangeTo(currency: String): StdMoney = this exchangeTo object : StdMoney.Currency {
|
||||||
override fun getCurrency(): String {
|
override fun getCurrency(): String {
|
||||||
return currency.toUpperCase().trim()
|
return currency.uppercase().trim()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ infix fun Number.withCurrency(currency: StdMoney.Currency): StdMoney = object :
|
|||||||
|
|
||||||
infix fun Number.withCurrency(currency: String): StdMoney = this withCurrency object : StdMoney.Currency {
|
infix fun Number.withCurrency(currency: String): StdMoney = this withCurrency object : StdMoney.Currency {
|
||||||
override fun getCurrency(): String {
|
override fun getCurrency(): String {
|
||||||
return currency.toUpperCase().trim()
|
return currency.uppercase().trim()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,3 +129,8 @@ fun MoneyView.asStdMoney(): StdMoney {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detect currency symbol, if needed
|
||||||
|
fun StdMoney.Currency.findCurrency(): Currency? {
|
||||||
|
return Currency.getInstance(this.getCurrency())
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.cubetiqs.money
|
|||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
import java.text.NumberFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Money Formatter (Final class)
|
* Money Formatter (Final class)
|
||||||
@@ -25,6 +26,12 @@ class MoneyFormatter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allow to force the auto format from money config
|
||||||
|
private var disableAutoFormat: Boolean = true
|
||||||
|
fun setDisableAutoFormat(disabled: Boolean) = apply {
|
||||||
|
this.disableAutoFormat = disabled
|
||||||
|
}
|
||||||
|
|
||||||
// when want to format the value for each of them, need to parse the money value here
|
// when want to format the value for each of them, need to parse the money value here
|
||||||
private var value: StdMoney? = null
|
private var value: StdMoney? = null
|
||||||
fun setValue(value: StdMoney?) = apply { this.value = value }
|
fun setValue(value: StdMoney?) = apply { this.value = value }
|
||||||
@@ -40,8 +47,24 @@ class MoneyFormatter(
|
|||||||
override fun format(): String {
|
override fun format(): String {
|
||||||
value?.getValue() ?: return ""
|
value?.getValue() ?: return ""
|
||||||
|
|
||||||
|
var autoFormat = if (disableAutoFormat) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
autoFormatValueFromConfig(force = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!autoFormat.isNullOrEmpty()) {
|
||||||
|
return autoFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// if don't have the pattern, precision and rounding, will able to use system auto format enabled
|
||||||
if (getPattern() == null && getPrecision() < 0 && getRoundingMode() == null) {
|
if (getPattern() == null && getPrecision() < 0 && getRoundingMode() == null) {
|
||||||
return value?.getValue().toString()
|
autoFormat = autoFormatValueFromConfig(force = true)
|
||||||
|
return if (autoFormat.isNullOrEmpty()) {
|
||||||
|
value?.getValue().toString()
|
||||||
|
} else {
|
||||||
|
autoFormat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getPrecision() > -1) {
|
if (getPrecision() > -1) {
|
||||||
@@ -55,7 +78,30 @@ class MoneyFormatter(
|
|||||||
return value?.asMoneyString(overrideSymbol) ?: ""
|
return value?.asMoneyString(overrideSymbol) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun autoFormatValueFromConfig(force: Boolean): String? {
|
||||||
|
if (force || MoneyConfig.isAutoLocaleFormatterEnabled()) {
|
||||||
|
val systemCurrency = if (MoneyConfig.isAutoCurrencyFormatterEnabled()) {
|
||||||
|
MoneyConfig.getCurrency()
|
||||||
|
} else {
|
||||||
|
value?.getCurrency()?.findCurrency()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (systemCurrency != null) {
|
||||||
|
val numberFormatter = NumberFormat.getNumberInstance(MoneyConfig.getLocale())
|
||||||
|
if (getRoundingMode() != null) {
|
||||||
|
numberFormatter.roundingMode = getRoundingMode()
|
||||||
|
}
|
||||||
|
numberFormatter.currency = systemCurrency
|
||||||
|
return numberFormatter.format(value?.getValue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_FORMATTER = "defaultFormatter"
|
const val DEFAULT_FORMATTER = "defaultFormatter"
|
||||||
|
const val DEFAULT_LOCALE = "defaultLocale"
|
||||||
|
const val DEFAULT_CURRENCY = "defaultCurrency"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ open class MoneyObject(
|
|||||||
if (temp.length == 1) {
|
if (temp.length == 1) {
|
||||||
operator(temp[0])
|
operator(temp[0])
|
||||||
} else {
|
} else {
|
||||||
valueOf(temp.toUpperCase().trim())
|
valueOf(temp.uppercase().trim())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Char -> {
|
is Char -> {
|
||||||
|
|||||||
@@ -4,23 +4,37 @@ open class MoneyView(
|
|||||||
private var value: Number? = null,
|
private var value: Number? = null,
|
||||||
private var currency: String? = null,
|
private var currency: String? = null,
|
||||||
) : MoneyMixin {
|
) : MoneyMixin {
|
||||||
constructor(money: StdMoney) : this() {
|
private var _currency: StdMoney.Currency? = null
|
||||||
|
|
||||||
|
constructor(value: StdMoney) : this() {
|
||||||
|
val money = if (MoneyConfig.isAutoCurrencyFormatterEnabled()) {
|
||||||
|
value exchangeTo MoneyConfig.getCurrency().currencyCode
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currency = money.getCurrency()
|
||||||
this.value = money.getValue()
|
this.value = money.getValue()
|
||||||
this.currency = money.getCurrency().getCurrency()
|
this.currency = this._currency?.getCurrency()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getValue(): Double {
|
fun getValue(): Double {
|
||||||
return value?.toDouble() ?: 0.0
|
return (value?.toDouble() ?: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrency(): String? {
|
fun getCurrency(): String? {
|
||||||
return currency
|
return currency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSymbol(): String? {
|
||||||
|
return this._currency?.findCurrency()?.symbol
|
||||||
|
}
|
||||||
|
|
||||||
fun getFormat(): String {
|
fun getFormat(): String {
|
||||||
return MoneyConfig
|
return MoneyConfig
|
||||||
.getFormatter(getCurrency())
|
.getFormatter(getCurrency())
|
||||||
.setValue(this.asStdMoney())
|
.setValue(this.asStdMoney())
|
||||||
|
.setDisableAutoFormat(true)
|
||||||
.format()
|
.format()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ interface StdMoney : MoneyMixin {
|
|||||||
fun initCurrency(currency: String?): Currency {
|
fun initCurrency(currency: String?): Currency {
|
||||||
return object : Currency {
|
return object : Currency {
|
||||||
override fun getCurrency(): String {
|
override fun getCurrency(): String {
|
||||||
return currency?.toUpperCase()?.trim() ?: "USD"
|
return currency?.uppercase()?.trim() ?: "USD"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import com.cubetiqs.money.*
|
import com.cubetiqs.money.*
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class MoneyTests {
|
class MoneyTests {
|
||||||
private fun initMoneyConfig() {
|
private fun initMoneyConfig() {
|
||||||
@@ -26,19 +27,8 @@ class MoneyTests {
|
|||||||
fun exchange_2usd_to_khr_test() {
|
fun exchange_2usd_to_khr_test() {
|
||||||
initMoneyConfig()
|
initMoneyConfig()
|
||||||
|
|
||||||
// applyMoneyConfig {
|
|
||||||
// setProperties(buildMoneyConfigProperties {
|
|
||||||
// setDeliEqual(':')
|
|
||||||
// setDeliSplit(',')
|
|
||||||
// })
|
|
||||||
// // parse("USD:1,KHR:4000")
|
|
||||||
// // appendRate("usd", 1.0)
|
|
||||||
// // appendRate("khr", 4000.0)
|
|
||||||
// fromJson(MyBatchRates.getJsonRates())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Is valid for money config?
|
// Is valid for money config?
|
||||||
Assert.assertTrue(MoneyConfig.isValid())
|
Assert.assertTrue(MoneyConfig.isConfigRatesValid())
|
||||||
|
|
||||||
// arithmetic operators calculation
|
// arithmetic operators calculation
|
||||||
val moneyUsd =
|
val moneyUsd =
|
||||||
@@ -138,6 +128,7 @@ class MoneyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun moneyFormatterTest() {
|
fun moneyFormatterTest() {
|
||||||
|
val systemCurrency = Currency.getInstance("USD").symbol
|
||||||
|
println(systemCurrency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user