/* * This file is part of GNU Taler * (C) 2020 Taler Systems S.A. * * GNU Taler is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 3, or (at your option) any later version. * * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * GNU Taler; see the file COPYING. If not, see */ @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.taler.wallet import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.deser.std.StdDeserializer import org.json.JSONObject import kotlin.math.round private const val FRACTIONAL_BASE = 1e8 @JsonDeserialize(using = AmountDeserializer::class) data class Amount(val currency: String, val amount: String) { fun isZero(): Boolean { return amount.toDouble() == 0.0 } companion object { fun fromJson(jsonAmount: JSONObject): Amount { val amountCurrency = jsonAmount.getString("currency") val amountValue = jsonAmount.getString("value") val amountFraction = jsonAmount.getString("fraction") val amountIntValue = Integer.parseInt(amountValue) val amountIntFraction = Integer.parseInt(amountFraction) return Amount( amountCurrency, (amountIntValue + amountIntFraction / FRACTIONAL_BASE).toString() ) } fun fromString(strAmount: String): Amount { val components = strAmount.split(":") return Amount(components[0], components[1]) } } override fun toString(): String { return String.format("%.2f $currency", amount.toDouble()) } } class AmountDeserializer : StdDeserializer(Amount::class.java) { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Amount { val node = p.codec.readValue(p, String::class.java) return Amount.fromString(node) } } class ParsedAmount( /** * name of the currency using either a three-character ISO 4217 currency code, * or a regional currency identifier starting with a "*" followed by at most 10 characters. * ISO 4217 exponents in the name are not supported, * although the "fraction" is corresponds to an ISO 4217 exponent of 6. */ val currency: String, /** * unsigned 32 bit value in the currency, * note that "1" here would correspond to 1 EUR or 1 USD, depending on currency, not 1 cent. */ val value: UInt, /** * unsigned 32 bit fractional value to be added to value * representing an additional currency fraction, * in units of one millionth (1e-6) of the base currency value. * For example, a fraction of 500,000 would correspond to 50 cents. */ val fraction: Double ) { companion object { fun parseAmount(str: String): ParsedAmount { val split = str.split(":") check(split.size == 2) val currency = split[0] val valueSplit = split[1].split(".") val value = valueSplit[0].toUInt() val fraction: Double = if (valueSplit.size > 1) { round("0.${valueSplit[1]}".toDouble() * FRACTIONAL_BASE) } else 0.0 return ParsedAmount(currency, value, fraction) } } operator fun minus(other: ParsedAmount): ParsedAmount { check(currency == other.currency) { "Can only subtract from same currency" } var resultValue = value var resultFraction = fraction if (resultFraction < other.fraction) { if (resultValue < 1u) { return ParsedAmount(currency, 0u, 0.0) } resultValue-- resultFraction += FRACTIONAL_BASE } check(resultFraction >= other.fraction) resultFraction -= other.fraction if (resultValue < other.value) { return ParsedAmount(currency, 0u, 0.0) } resultValue -= other.value return ParsedAmount(currency, resultValue, resultFraction) } fun isZero(): Boolean { return value == 0u && fraction == 0.0 } @Suppress("unused") fun toJSONString(): String { return "$currency:${getValueString()}" } override fun toString(): String { return "${getValueString()} $currency" } private fun getValueString(): String { return "$value${(fraction / FRACTIONAL_BASE).toString().substring(1)}" } }