summaryrefslogtreecommitdiff
path: root/util/src/main/kotlin/DBTypes.kt
blob: c38e63cfae35daf3d9e459f9594a943bb6a74588 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
 * This file is part of LibEuFin.
 * Copyright (C) 2020 Taler Systems S.A.
 *
 * LibEuFin is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3, or
 * (at your option) any later version.
 *
 * LibEuFin 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 Affero General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with LibEuFin; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>
 */

package tech.libeufin.util

import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.ColumnType
import org.jetbrains.exposed.sql.Table
import java.math.BigDecimal
import java.math.RoundingMode

const val SCALE_TWO = 2
const val NUMBER_MAX_DIGITS = 20
class BadAmount(badValue: Any?) : Exception("Value '${badValue}' is not a valid amount")
typealias Amount = BigDecimal

class AmountColumnType : ColumnType() {
    override fun sqlType(): String  = "DECIMAL(${NUMBER_MAX_DIGITS}, ${SCALE_TWO})"
        override fun valueFromDB(value: Any): Any {
            val valueFromDB = super.valueFromDB(value)
            try {
                return when (valueFromDB) {
                    is BigDecimal -> valueFromDB.setScale(SCALE_TWO, RoundingMode.UNNECESSARY)
                    is Double -> BigDecimal.valueOf(valueFromDB).setScale(SCALE_TWO, RoundingMode.UNNECESSARY)
                    is Float -> BigDecimal(valueFromDB.toString()).setScale(
                        SCALE_TWO,
                        RoundingMode.UNNECESSARY
                    )
                    is Int -> BigDecimal(valueFromDB)
                    is Long -> BigDecimal.valueOf(valueFromDB)
                    else -> valueFromDB
                }
            } catch (e: Exception) {
                e.printStackTrace()
                throw BadAmount(value)
            }
        }

        override fun valueToDB(value: Any?): Any? {
            try {
                (value as BigDecimal).setScale(SCALE_TWO, RoundingMode.UNNECESSARY)
            } catch (e: Exception) {
                e.printStackTrace()
                throw BadAmount(value)
            }

            if (value.compareTo(BigDecimal.ZERO) == 0) {
                throw BadAmount(value)
            }
            return super.valueToDB(value)
        }
}

/**
 * Make sure the number entered by upper layers does not need any rounding
 * to conform to scale == 2
 */
fun Table.amount(name: String): Column<Amount> {
    return registerColumn(name, AmountColumnType())
}