gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-bank] 04/04: porting the bank to use the new Amount


From: gnunet
Subject: [GNUnet-SVN] [taler-bank] 04/04: porting the bank to use the new Amount object
Date: Tue, 31 Oct 2017 15:05:11 +0100

This is an automated email from the git hooks/post-receive script.

marcello pushed a commit to branch master
in repository bank.

commit fc6a56f276dfb2fba4d4acbe756c22492430439b
Author: Marcello Stanisci <address@hidden>
AuthorDate: Tue Oct 31 15:04:33 2017 +0100

    porting the bank to use the new Amount object
---
 bank-check.conf                                    |   4 +-
 talerbank/app/Makefile.am                          |   2 +-
 talerbank/app/amount.py                            | 131 +++++++++++++++
 talerbank/app/amounts.py                           | 101 ------------
 talerbank/app/management/commands/dump_talerdb.py  |   2 +-
 .../app/migrations/0002_bankaccount_amount.py      |  21 +++
 .../app/migrations/0003_auto_20171030_1346.py      |  21 +++
 .../app/migrations/0004_auto_20171030_1428.py      |  34 ++++
 .../0005_remove_banktransaction_currency.py        |  19 +++
 .../app/migrations/0006_auto_20171031_0823.py      |  21 +++
 .../app/migrations/0007_auto_20171031_0906.py      |  21 +++
 .../app/migrations/0008_auto_20171031_0938.py      |  31 ++++
 talerbank/app/models.py                            |  48 +-----
 talerbank/app/templates/pin_tan.html               |   2 +-
 talerbank/app/templates/profile_page.html          |   2 +-
 talerbank/app/templates/public_accounts.html       |   2 +-
 talerbank/app/tests.py                             | 142 ++++++++--------
 talerbank/app/tests_alt.py                         |  10 +-
 talerbank/app/views.py                             | 178 +++++++++------------
 talerbank/settings.py                              |   4 +-
 20 files changed, 476 insertions(+), 320 deletions(-)

diff --git a/bank-check.conf b/bank-check.conf
index b02bf28..46658e2 100644
--- a/bank-check.conf
+++ b/bank-check.conf
@@ -10,7 +10,7 @@ DATABASE = postgres:///talercheck
 NDIGITS = 2
 
 # FIXME
-MAX_DEBT = KUDOS:50
+MAX_DEBT = KUDOS:50.0
 
 # FIXME
-MAX_DEBT_BANK = KUDOS:0
+MAX_DEBT_BANK = KUDOS:0.0
diff --git a/talerbank/app/Makefile.am b/talerbank/app/Makefile.am
index 6376a68..b3efdd7 100644
--- a/talerbank/app/Makefile.am
+++ b/talerbank/app/Makefile.am
@@ -5,7 +5,7 @@ EXTRA_DIST = \
   schemas.py \
   urls.py \
   views.py \
-  amounts.py \
+  amount.py \
   checks.py  \
   __init__.py \
   models.py \
diff --git a/talerbank/app/amount.py b/talerbank/app/amount.py
new file mode 100644
index 0000000..c66691a
--- /dev/null
+++ b/talerbank/app/amount.py
@@ -0,0 +1,131 @@
+#  This file is part of TALER
+#  (C) 2017 TALER SYSTEMS
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License, or (at your option) any later version.
+#
+#  This library 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+#
+#  @author Marcello Stanisci
+#  @version 0.0
+#  @repository https://git.taler.net/copylib.git/
+#  This code is "copylib", it is versioned under the Git repository
+#  mentioned above, and it is meant to be manually copied into any project
+#  which might need it.
+
+class CurrencyMismatch(Exception):
+    pass
+
+class BadFormatAmount(Exception):
+    def __init__(self, faulty_str):
+        self.faulty_str = faulty_str
+
+class Amount:
+    # How many "fraction" units make one "value" unit of currency
+    # (Taler requires 10^8).  Do not change this 'constant'.
+    @staticmethod
+    def FRACTION():
+        return 10 ** 8
+
+    @staticmethod
+    def MAX_VALUE():
+        return (2 ** 53) - 1
+
+    def __init__(self, currency, value=0, fraction=0):
+        # type: (str, int, int) -> Amount
+        assert(value >= 0 and fraction >= 0)
+        self.value = value
+        self.fraction = fraction
+        self.currency = currency
+        self.__normalize()
+        assert(self.value <= Amount.MAX_VALUE())
+
+    # Normalize amount
+    def __normalize(self):
+        if self.fraction >= Amount.FRACTION():
+            self.value += int(self.fraction / Amount.FRACTION())
+            self.fraction = self.fraction % Amount.FRACTION()
+
+    # Parse a string matching the format "A:B.C"
+    # instantiating an amount object.
+    @classmethod
+    def parse(cls, amount_str):
+        exp = '^\s*([-_*A-Za-z0-9]+):([0-9]+)\.([0-9]+)\s*$'
+        import re
+        parsed = re.search(exp, amount_str)
+        if not parsed:
+            raise BadFormatAmount(amount_str)
+        value = int(parsed.group(2))
+        fraction = 0
+        for i, digit in enumerate(parsed.group(3)):
+            fraction += int(int(digit) * (Amount.FRACTION() / 10 ** (i+1)))
+        return cls(parsed.group(1), value, fraction)
+
+    # Comare two amounts, return:
+    # -1 if a < b
+    # 0 if a == b
+    # 1 if a > b
+    @staticmethod
+    def cmp(a, b):
+        if a.currency != b.currency:
+            raise CurrencyMismatch()
+        if a.value == b.value:
+            if a.fraction < b.fraction:
+                return -1
+            if a.fraction > b.fraction:
+                return 1
+            return 0
+        if a.value < b.value:
+            return -1
+        return 1
+
+    def set(self, currency, value=0, fraction=0):
+        self.currency = currency
+        self.value = value
+        self.fraction = fraction
+
+    # Add the given amount to this one
+    def add(self, a):
+        if self.currency != a.currency:
+            raise CurrencyMismatch()
+        self.value += a.value
+        self.fraction += a.fraction
+        self.__normalize()
+
+    # Subtract passed amount from this one
+    def subtract(self, a):
+        if self.currency != a.currency:
+            raise CurrencyMismatch()
+        if self.fraction < a.fraction:
+            self.fraction += Amount.FRACTION()
+            self.value -= 1
+        if self.value < a.value:
+            raise ValueError('self is lesser than amount to be subtracted')
+        self.value -= a.value
+        self.fraction -= a.fraction
+
+    # Dump string from this amount, will put 'ndigits' numbers
+    # after the dot.
+    def stringify(self, ndigits):
+        assert ndigits > 0
+        ret = '%s:%s.' % (self.currency, str(self.value))
+        f = self.fraction
+        for i in range(0, ndigits):
+            ret += str(int(f / (Amount.FRACTION() / 10)))
+            f = (f * 10) % (Amount.FRACTION())
+        return ret
+
+    # Dump the Taler-compliant 'dict' amount
+    def dump(self):
+        return dict(value=self.value,
+                    fraction=self.fraction,
+                    currency=self.currency)
diff --git a/talerbank/app/amounts.py b/talerbank/app/amounts.py
deleted file mode 100644
index f9bdd02..0000000
--- a/talerbank/app/amounts.py
+++ /dev/null
@@ -1,101 +0,0 @@
-#  This file is part of TALER
-#  (C) 2016 INRIA
-#
-#  TALER 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.
-#
-#  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
-#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-#
-#  @author Marcello Stanisci
-#  @author Florian Dold
-
-
-import re
-import math
-import logging
-from django.conf import settings
-
-logger = logging.getLogger(__name__)
-
-FRACTION = 100000000
-
-class CurrencyMismatchException(Exception):
-    def __init__(self, msg=None, status_code=0):
-        self.msg = msg
-        # HTTP status code to be returned as response for
-        # this exception
-        self.status_code = status_code
-
-class BadFormatAmount(Exception):
-    def __init__(self, msg=None, status_code=0):
-        self.msg = msg
-        # HTTP status code to be returned as response for
-        # this exception
-        self.status_code = status_code
-
-
-def check_currency(a1, a2):
-    if a1["currency"] != a2["currency"]:
-        logger.error("Different currencies given: %s vs %s" % (a1["currency"], 
a2["currency"]))
-        raise CurrencyMismatchException
-
-def get_zero():
-    return dict(value=0, fraction=0, currency=settings.TALER_CURRENCY)
-
-def amount_add(a1, a2):
-    check_currency(a1, a2)
-    a1_float = floatify(a1)
-    a2_float = floatify(a2)
-    return parse_amount("%s:%s" % (a2["currency"], str(a1_float + a2_float)))
-
-def amount_sub(a1, a2):
-    check_currency(a1, a2)
-    a1_float = floatify(a1)
-    a2_float = floatify(a2)
-    sub = a1_float - a2_float
-    fmt = "%s:%s" % (a2["currency"], str(sub))
-    return parse_amount(fmt)
-
-# Return -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
-def amount_cmp(a1, a2):
-    check_currency(a1, a2)
-    a1_float = floatify(a1)
-    a2_float = floatify(a2)
-
-    if a1_float < a2_float:
-        return -1
-    elif a1_float == a2_float:
-        return 0
-
-    return 1
-
-
-def floatify(amount_dict):
-    return amount_dict['value'] + (float(amount_dict['fraction']) / 
float(FRACTION))
-
-def stringify(amount_float, digits=2):
-    o = "".join(["%.", "%sf" % digits])
-    return o % amount_float
-
-def parse_amount(amount_str):
-    """
-    Parse amount of return None if not a
-    valid amount string
-    """
-    parsed = re.search("^\s*([-_*A-Za-z0-9]+):([0-9]+)(\.[0-9]+)?\s*$", 
amount_str)
-    if not parsed:
-        raise BadFormatAmount
-    value = int(parsed.group(2))
-    fraction = 0
-    if parsed.group(3) is not None:
-        for i, digit in enumerate(parsed.group(3)[1:]):
-            fraction += int(int(digit) * (FRACTION / 10 ** (i+1)))
-    return {'value': value,
-            'fraction': fraction,
-            'currency': parsed.group(1)}
diff --git a/talerbank/app/management/commands/dump_talerdb.py 
b/talerbank/app/management/commands/dump_talerdb.py
index ab587d9..b4a93fc 100644
--- a/talerbank/app/management/commands/dump_talerdb.py
+++ b/talerbank/app/management/commands/dump_talerdb.py
@@ -50,7 +50,7 @@ def dump_history():
             # as the first/last character on a line makes flake8 complain
             msg.append("+%s, " % item.credit_account.account_no)
             msg.append("-%s, " % item.debit_account.account_no)
-            msg.append("%.2f, " % floatify(item.amount_obj))
+            msg.append(item.amount.stringify(2))
             msg.append(item.subject)
             print(''.join(msg))
     except (OperationalError, ProgrammingError):
diff --git a/talerbank/app/migrations/0002_bankaccount_amount.py 
b/talerbank/app/migrations/0002_bankaccount_amount.py
new file mode 100644
index 0000000..beaa1d8
--- /dev/null
+++ b/talerbank/app/migrations/0002_bankaccount_amount.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-30 13:23
+from __future__ import unicode_literals
+
+from django.db import migrations
+import talerbank.app.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='bankaccount',
+            name='amount',
+            field=talerbank.app.models.AmountField(default=False),
+        ),
+    ]
diff --git a/talerbank/app/migrations/0003_auto_20171030_1346.py 
b/talerbank/app/migrations/0003_auto_20171030_1346.py
new file mode 100644
index 0000000..91c6cb9
--- /dev/null
+++ b/talerbank/app/migrations/0003_auto_20171030_1346.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-30 13:46
+from __future__ import unicode_literals
+
+from django.db import migrations
+import talerbank.app.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0002_bankaccount_amount'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='bankaccount',
+            name='amount',
+            field=talerbank.app.models.AmountField(),
+        ),
+    ]
diff --git a/talerbank/app/migrations/0004_auto_20171030_1428.py 
b/talerbank/app/migrations/0004_auto_20171030_1428.py
new file mode 100644
index 0000000..b93ebd4
--- /dev/null
+++ b/talerbank/app/migrations/0004_auto_20171030_1428.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-30 14:28
+from __future__ import unicode_literals
+
+from django.db import migrations
+import talerbank.app.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0003_auto_20171030_1346'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='banktransaction',
+            name='amount_fraction',
+        ),
+        migrations.RemoveField(
+            model_name='banktransaction',
+            name='amount_value',
+        ),
+        migrations.AddField(
+            model_name='banktransaction',
+            name='amount',
+            field=talerbank.app.models.AmountField(default=False),
+        ),
+        migrations.AlterField(
+            model_name='bankaccount',
+            name='amount',
+            field=talerbank.app.models.AmountField(default=False),
+        ),
+    ]
diff --git a/talerbank/app/migrations/0005_remove_banktransaction_currency.py 
b/talerbank/app/migrations/0005_remove_banktransaction_currency.py
new file mode 100644
index 0000000..9cd781f
--- /dev/null
+++ b/talerbank/app/migrations/0005_remove_banktransaction_currency.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-30 14:37
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0004_auto_20171030_1428'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='banktransaction',
+            name='currency',
+        ),
+    ]
diff --git a/talerbank/app/migrations/0006_auto_20171031_0823.py 
b/talerbank/app/migrations/0006_auto_20171031_0823.py
new file mode 100644
index 0000000..67c1a70
--- /dev/null
+++ b/talerbank/app/migrations/0006_auto_20171031_0823.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-31 08:23
+from __future__ import unicode_literals
+
+from django.db import migrations
+import talerbank.app.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0005_remove_banktransaction_currency'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='bankaccount',
+            name='amount',
+            field=talerbank.app.models.AmountField(default=None),
+        ),
+    ]
diff --git a/talerbank/app/migrations/0007_auto_20171031_0906.py 
b/talerbank/app/migrations/0007_auto_20171031_0906.py
new file mode 100644
index 0000000..923cff2
--- /dev/null
+++ b/talerbank/app/migrations/0007_auto_20171031_0906.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-31 09:06
+from __future__ import unicode_literals
+
+from django.db import migrations
+import talerbank.app.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0006_auto_20171031_0823'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='bankaccount',
+            name='amount',
+            
field=talerbank.app.models.AmountField(default=talerbank.app.models.get_zero_amount),
+        ),
+    ]
diff --git a/talerbank/app/migrations/0008_auto_20171031_0938.py 
b/talerbank/app/migrations/0008_auto_20171031_0938.py
new file mode 100644
index 0000000..3b97829
--- /dev/null
+++ b/talerbank/app/migrations/0008_auto_20171031_0938.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-10-31 09:38
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0007_auto_20171031_0906'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance_fraction',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance_value',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='currency',
+        ),
+    ]
diff --git a/talerbank/app/models.py b/talerbank/app/models.py
index cbb47e0..ebbb9f0 100644
--- a/talerbank/app/models.py
+++ b/talerbank/app/models.py
@@ -43,54 +43,31 @@ class AmountField(models.Field):
 
     def from_db_value(self, value, expression, connection, context):
         if None is value:
-            return value
+            return amount.Amount.parse(settings.TALER_CURRENCY)
         return amount.Amount.parse(value)
 
     def to_python(self, value):
-        if isinstance(value, amount.Amount) or None is value:
+        if isinstance(value, amount.Amount):
             return value
         try:
+            if None is value:
+                return amount.Amount.parse(settings.TALER_CURRENCY)
             return amount.Amount.parse(value)
         except amount.BadAmount:
             raise models.ValidationError()
 
+def get_zero_amount():
+    return amount.Amount(settings.TALER_CURRENCY)
 
 class BankAccount(models.Model):
     is_public = models.BooleanField(default=False)
-    # Handier than keeping the amount signed, for two reasons:
-    # (1) checking if someone is in debt is less verbose: with signed
-    # amounts we have to check if the amount is less than zero; this
-    # way we only check if a boolean is true. (2) The bank logic is
-    # ready to welcome a data type for amounts which doesn't have any
-    # sign notion, like Taler amounts do.
     debit = models.BooleanField(default=False)
-    balance_value = models.IntegerField(default=0)
-    balance_fraction = models.IntegerField(default=0)
-    # From today's (16/10/2017) Mumble talk, it emerged that bank shouldn't
-    # store amounts as floats, but: normal banks should not care about
-    # Taler when representing values around in their databases..
-    balance = models.FloatField(default=0)
-    currency = models.CharField(max_length=12, default="")
     account_no = models.AutoField(primary_key=True)
     user = models.OneToOneField(User, on_delete=models.CASCADE)
-
-    # EXPERIMENTAL CODE
-    amount = AmountField()
-
-    def _get_balance(self):
-        return dict(value=self.balance_value,
-                    fraction=self.balance_fraction,
-                    currency=self.currency)
-    def _set_balance(self, amount):
-        self.balance_value = amount["value"]
-        self.balance_fraction = amount["fraction"]
-        self.currency = amount["currency"]
-    balance_obj = property(_get_balance, _set_balance)
+    amount = AmountField(default=get_zero_amount)
 
 class BankTransaction(models.Model):
-    amount_value = models.IntegerField(default=0)
-    amount_fraction = models.IntegerField(default=0)
-    currency = models.CharField(max_length=12)
+    amount = AmountField(default=False)
     debit_account = models.ForeignKey(BankAccount,
                                       on_delete=models.CASCADE,
                                       related_name="debit_account")
@@ -99,12 +76,3 @@ class BankTransaction(models.Model):
                                        related_name="credit_account")
     subject = models.CharField(default="(no subject given)", max_length=200)
     date = models.DateTimeField(auto_now=True)
-    def _get_amount(self):
-        return dict(value=self.amount_value,
-                    fraction=self.amount_fraction,
-                    currency=self.currency)
-    def _set_amount(self, amount):
-        self.amount_value = amount["value"]
-        self.amount_fraction = amount["fraction"]
-        self.currency = amount["currency"]
-    amount_obj = property(_get_amount, _set_amount)
diff --git a/talerbank/app/templates/pin_tan.html 
b/talerbank/app/templates/pin_tan.html
index b545524..0855b3b 100644
--- a/talerbank/app/templates/pin_tan.html
+++ b/talerbank/app/templates/pin_tan.html
@@ -31,7 +31,7 @@
   {% endif %}
   <p>
     {{ settings_value("TALER_CURRENCY") }} Bank needs to verify that you
-    intend to withdraw <b>{{ amount }} {{ settings_value("TALER_CURRENCY") 
}}</b> from
+    intend to withdraw <b>{{ amount }}</b> from
     <b>{{ exchange }}</b>.
     To prove that you are the account owner, please answer the
     following &quot;security question&quot; (*):
diff --git a/talerbank/app/templates/profile_page.html 
b/talerbank/app/templates/profile_page.html
index c64f215..ad8345d 100644
--- a/talerbank/app/templates/profile_page.html
+++ b/talerbank/app/templates/profile_page.html
@@ -125,7 +125,7 @@
           <tr>
             <td style="text-align:right">{{ item.date }}</td>
            <td style="text-align:right">
-              {{ item.float_amount }} {{ item.float_currency }}
+              {{ item.sign }} {{ item.amount }}
             </td>
            <td class="text-align:left">{% if item.counterpart_username %} {{ 
item.counterpart_username }} {% endif %} (account #{{ item.counterpart }})</td>
            <td class="text-align:left">{{ item.subject }}</td>
diff --git a/talerbank/app/templates/public_accounts.html 
b/talerbank/app/templates/public_accounts.html
index 2f38489..3c5b53e 100644
--- a/talerbank/app/templates/public_accounts.html
+++ b/talerbank/app/templates/public_accounts.html
@@ -54,7 +54,7 @@
           <tr>
             <td>{{entry.date}}</td>
             <td>
-              {{ entry.float_amount }} {{ entry.float_currency }}
+              {{ sign }} {{ entry.amount }}
             </td>
             <td>{% if entry.counterpart_username %} {{ 
entry.counterpart_username }} {% endif %} (account #{{ entry.counterpart 
}})</td>
             <td>
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
index feff213..435fd7d 100644
--- a/talerbank/app/tests.py
+++ b/talerbank/app/tests.py
@@ -20,9 +20,9 @@ from django.conf import settings
 from django.contrib.auth.models import User
 from .models import BankAccount, BankTransaction
 from . import urls
-from . import amounts
 from .views import wire_transfer
 import json
+from .amount import Amount, CurrencyMismatch, BadFormatAmount
 
 import logging
 
@@ -39,7 +39,7 @@ class RegisterTestCase(TestCase):
 
     def setUp(self):
         bank = User.objects.create_user(username='Bank')
-        ba = BankAccount(user=bank, currency=settings.TALER_CURRENCY)
+        ba = BankAccount(user=bank)
         ba.account_no = 1
         ba.save() 
 
@@ -62,8 +62,8 @@ class RegisterWrongCurrencyTestCase(TestCase):
 
     # Activating this user with a faulty currency.
     def setUp(self):
-        bank = User.objects.create_user(username='Bank')
-        ba = BankAccount(user=bank, currency="XYZ")
+        ba = BankAccount(user=User.objects.create_user(username='Bank'),
+            amount=Amount('WRONGCURRENCY'))
         ba.account_no = 1
         ba.save() 
 
@@ -88,8 +88,7 @@ class LoginTestCase(TestCase):
     def setUp(self):
         user = User.objects.create_user(username="test_user",
                                         password="test_password")
-        user_account = BankAccount(user=user,
-                                   currency=settings.TALER_CURRENCY)
+        user_account = BankAccount(user=user)
         user_account.save()
 
     def tearDown(self):
@@ -116,20 +115,21 @@ class LoginTestCase(TestCase):
 class AmountTestCase(TestCase):
     
     def test_cmp(self):
-        a1 = dict(value=1, fraction=0, currency="X")
-        _a1 = dict(value=1, fraction=0, currency="X")
-        a2 = dict(value=2, fraction=0, currency="X")
-        self.assertEqual(-1, amounts.amount_cmp(a1, a2))
-        self.assertEqual(1, amounts.amount_cmp(a2, a1))
-        self.assertEqual(0, amounts.amount_cmp(a1, _a1))
+        a1 = Amount("X", 1)
+        _a1 = Amount("X", 1)
+        a2 = Amount("X", 2)
+        
+        self.assertEqual(-1, Amount.cmp(a1, a2))
+        self.assertEqual(1, Amount.cmp(a2, a1))
+        self.assertEqual(0, Amount.cmp(a1, _a1))
 
     # Trying to compare amount of different currencies
     def test_cmp_diff_curr(self):
-        a1 = dict(value=1, fraction=0, currency="X")
-        a2 = dict(value=2, fraction=0, currency="Y")
+        a1 = Amount("X", 1)
+        a2 = Amount("Y", 2)
         try:
-            amounts.amount_cmp(a1, a2)
-        except amounts.CurrencyMismatchException:
+            Amount.cmp(a1, a2)
+        except CurrencyMismatch:
             self.assertTrue(True)
             return
         # Should never get here
@@ -140,14 +140,12 @@ class AddIncomingTestCase(TestCase):
     """Test money transfer's API"""
 
     def setUp(self):
-        bank = User.objects.create_user(username="bank_user",
-                                        password="bank_password")
-        bank_account = BankAccount(user=bank,
-                                   currency=settings.TALER_CURRENCY)
-        user = User.objects.create_user(username="user_user",
-                                        password="user_password")
-        user_account = BankAccount(user=user,
-                                   currency=settings.TALER_CURRENCY)
+        bank_account = BankAccount(user=User.objects.create_user(
+            username="bank_user",
+            password="bank_password"))
+        user_account = BankAccount(user=User.objects.create_user(
+            username="user_user",
+            password="user_password"))
         bank_account.save()
         user_account.save()
 
@@ -206,22 +204,22 @@ class HistoryTestCase(TestCase):
 
     def setUp(self):
         user = User.objects.create_user(username='User', password="Password")
-        ub = BankAccount(user=user, currency=settings.TALER_CURRENCY)
+        ub = BankAccount(user=user, amount=Amount(settings.TALER_CURRENCY, 
100))
         ub.account_no = 1
-        ub.balance_obj = dict(value=100, fraction=0, 
currency=settings.TALER_CURRENCY)
         ub.save() 
         user_passive = User.objects.create_user(username='UserP', 
password="PasswordP")
-        ub_p = BankAccount(user=user_passive, currency=settings.TALER_CURRENCY)
+        ub_p = BankAccount(user=user_passive)
         ub_p.account_no = 2
         ub_p.save() 
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="a")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="b")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="c")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="d")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="e")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="f")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="g")
-        wire_transfer(dict(value=1, fraction=0, 
currency=settings.TALER_CURRENCY), ub, ub_p, subject="h")
+        one = Amount(settings.TALER_CURRENCY, 1)
+        wire_transfer(one, ub, ub_p, subject="a")
+        wire_transfer(one, ub, ub_p, subject="b")
+        wire_transfer(one, ub, ub_p, subject="c")
+        wire_transfer(one, ub, ub_p, subject="d")
+        wire_transfer(one, ub, ub_p, subject="e")
+        wire_transfer(one, ub, ub_p, subject="f")
+        wire_transfer(one, ub, ub_p, subject="g")
+        wire_transfer(one, ub, ub_p, subject="h")
 
     def tearDown(self):
         clearDb()
@@ -267,16 +265,34 @@ class HistoryTestCase(TestCase):
                          **{"HTTP_X_TALER_BANK_USERNAME": "User", 
"HTTP_X_TALER_BANK_PASSWORD": "Password"})
         self.assertEqual(404, response.status_code)
 
+class DBAmountSubtraction(TestCase):
+    def setUp(self):
+        a = BankAccount(user=User.objects.create_user(username='U'),
+            amount=Amount(settings.TALER_CURRENCY, 3))
+        a.save()
+    
+    def test_subtraction(self):
+        a = BankAccount.objects.get(user=User.objects.get(username='U'))
+        a.amount.subtract(Amount(settings.TALER_CURRENCY, 2))
+        self.assertEqual(0, Amount.cmp(Amount(settings.TALER_CURRENCY, 1), 
a.amount))
+
+
+class DBCustomColumnTestCase(TestCase):
+
+    def setUp(TestCase):
+        a = BankAccount(user=User.objects.create_user(username='U'))
+        a.save()
+
+    def test_exists(self):
+        a = BankAccount.objects.get(user=User.objects.get(username='U'))
+        self.assertTrue(isinstance(a.amount, Amount))
 
 # This tests whether a bank account goes debit and then goes >=0  again
 class DebitTestCase(TestCase):
 
     def setUp(self):
-        u = User.objects.create_user(username='U')
-        u0 = User.objects.create_user(username='U0')
-        ua = BankAccount(user=u, currency=settings.TALER_CURRENCY)
-        u0a = BankAccount(user=u0, currency=settings.TALER_CURRENCY)
-
+        ua = BankAccount(user=User.objects.create_user(username='U'))
+        u0a = BankAccount(user=User.objects.create_user(username='U0'))
         ua.save()
         u0a.save()
 
@@ -286,46 +302,40 @@ class DebitTestCase(TestCase):
         self.assertEqual(False, ub.debit)
 
     def test_red(self):
-        u = User.objects.get(username='U')
-        u0 = User.objects.get(username='U0')
-
-        ub = BankAccount.objects.get(user=u)
-        ub0 = BankAccount.objects.get(user=u0)
+        ub = BankAccount.objects.get(user=User.objects.get(username='U'))
+        ub0 = BankAccount.objects.get(user=User.objects.get(username='U0'))
 
-        wire_transfer(dict(value=10, fraction=0, 
currency=settings.TALER_CURRENCY),
+        wire_transfer(Amount(settings.TALER_CURRENCY, 10, 0),
                       ub0,
                       ub,
                       "Go green")
-        tmp = amounts.get_zero()
-        tmp["value"] = 10
+        tmp = Amount(settings.TALER_CURRENCY, 10)
+        self.assertEqual(0, Amount.cmp(ub.amount, tmp))
+        self.assertEqual(0, Amount.cmp(ub0.amount, tmp))
+        self.assertFalse(ub.debit)
 
-        self.assertEqual(0, amounts.amount_cmp(ub.balance_obj, tmp))
-        self.assertEqual(False, ub.debit)
-        self.assertEqual(True, ub0.debit)
+        self.assertTrue(ub0.debit)
 
-        wire_transfer(dict(value=11, fraction=0, 
currency=settings.TALER_CURRENCY),
+        wire_transfer(Amount(settings.TALER_CURRENCY, 11),
                       ub,
                       ub0,
                       "Go red")
 
-        self.assertEqual(True, ub.debit)
-        self.assertEqual(False, ub0.debit)
-
-        tmp["value"] = 1
-
-        self.assertEqual(0, amounts.amount_cmp(ub0.balance_obj, tmp))
+        tmp.value = 1
+        self.assertTrue(ub.debit)
+        self.assertFalse(ub0.debit)
+        self.assertEqual(0, Amount.cmp(ub.amount, tmp))
+        self.assertEqual(0, Amount.cmp(ub0.amount, tmp))
 
 class ParseAmountTestCase(TestCase):
      def test_parse_amount(self):
-         ret = amounts.parse_amount("KUDOS:4")
-         self.assertJSONEqual('{"value": 4, "fraction": 0, "currency": 
"KUDOS"}', json.dumps(ret))
-         ret = amounts.parse_amount("KUDOS:4.00")
-         self.assertJSONEqual('{"value": 4, "fraction": 0, "currency": 
"KUDOS"}', json.dumps(ret))
-         ret = amounts.parse_amount("KUDOS:4.3")
-         self.assertJSONEqual('{"value": 4, "fraction": 30000000, "currency": 
"KUDOS"}', json.dumps(ret))
+         ret = Amount.parse("KUDOS:4.0")
+         self.assertJSONEqual('{"value": 4, "fraction": 0, "currency": 
"KUDOS"}', ret.dump())
+         ret = Amount.parse("KUDOS:4.3")
+         self.assertJSONEqual('{"value": 4, "fraction": 30000000, "currency": 
"KUDOS"}', ret.dump())
          try:
-             amounts.parse_amount("Buggy")
-         except amounts.BadFormatAmount:
+             Amount.parse("Buggy")
+         except BadFormatAmount:
              return
          # make sure the control doesn't get here
          self.assertEqual(True, False)
diff --git a/talerbank/app/tests_alt.py b/talerbank/app/tests_alt.py
index cf782d1..4ab9b65 100644
--- a/talerbank/app/tests_alt.py
+++ b/talerbank/app/tests_alt.py
@@ -20,7 +20,7 @@ from django.conf import settings
 from django.contrib.auth.models import User
 from .models import BankAccount, BankTransaction
 from . import urls
-from . import amounts
+from .amount import Amount, BadFormatAmount
 from .views import wire_transfer
 import json
 
@@ -35,13 +35,13 @@ class BadDatabaseStringTestCase(TestCase):
 class BadMaxDebtOptionTestCase(TestCase):
     def test_badmaxdebtoption(self):
         try:
-            amounts.parse_amount(settings.TALER_MAX_DEBT)
-        except amounts.BadFormatAmount:
+            Amount.parse(settings.TALER_MAX_DEBT)
+        except BadFormatAmount:
             self.assertTrue(True)
             return
         try:
-            amounts.parse_amount(settings.TALER_MAX_DEBT_BANK)
-        except amounts.BadFormatAmount:
+            Amount.parse(settings.TALER_MAX_DEBT_BANK)
+        except BadFormatAmount:
             self.assertTrue(True)
             return
         # Force to have at least one bad amount in config
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index 45328f8..fd6f570 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -37,9 +37,9 @@ import time
 import hashlib
 import requests
 from urllib.parse import urljoin
-from . import amounts
 from . import schemas
 from .models import BankAccount, BankTransaction
+from .amount import Amount, CurrencyMismatch, BadFormatAmount
 
 logger = logging.getLogger(__name__)
 
@@ -98,9 +98,9 @@ def profile_page(request):
 
     context = dict(
         name=user_account.user.username,
-        balance=amounts.stringify(amounts.floatify(user_account.balance_obj)),
+        balance=user_account.amount.stringify(settings.TALER_DIGITS),
         sign = "-" if user_account.debit else "",
-        currency=user_account.currency,
+        currency=user_account.amount.currency,
         precision=settings.TALER_DIGITS,
         account_no=user_account.account_no,
         history=history,
@@ -149,9 +149,9 @@ def pin_tan_question(request):
         currency = request.GET.get("amount_currency", None)
     except ValueError:
         return HttpResponseBadRequest("invalid parameters: \"amount_currency\" 
not given")
-    amount = {"value": value,
-              "fraction": fraction,
-              "currency": currency}
+    if currency != settings.TALER_CURRENCY:
+        return HttpResponse("Such currency (%s) is not accepted" % currency, 
status=422)
+    amount = Amount(currency, value, fraction)
     user_account = BankAccount.objects.get(user=request.user)
     wiredetails = json.loads(request.GET["wire_details"])
     if not isinstance(wiredetails, dict) or "test" not in wiredetails:
@@ -160,7 +160,6 @@ def pin_tan_question(request):
                 "The exchange does not seem to support it.")
     try:
         schemas.validate_wiredetails(wiredetails)
-        schemas.validate_amount(amount)
     except ValueError as error:
         return HttpResponseBadRequest("invalid parameters (%s)" % error)
     # parameters we store in the session are (more or less) validated
@@ -176,7 +175,7 @@ def pin_tan_question(request):
     previous_failed = get_session_flag(request, "captcha_failed")
     context = dict(
         form=Pin(auto_id=False),
-        amount=amounts.floatify(amount),
+        amount=amount.stringify(settings.TALER_DIGITS),
         previous_failed=previous_failed,
         exchange=request.GET["exchange"],
     )
@@ -223,7 +222,7 @@ def pin_tan_verify(request):
             sender_account_details=sender_wiredetails,
              # just something unique
             transfer_details=dict(timestamp=int(time.time() * 1000)),
-            amount=amount,
+            amount=amount.dump(),
     )
     user_account = BankAccount.objects.get(user=request.user)
     exchange_account = 
BankAccount.objects.get(account_no=exchange_account_number)
@@ -233,13 +232,16 @@ def pin_tan_verify(request):
         logger.warning("Withdrawal impossible due to debt limit exceeded")
         request.session["debt_limit"] = True
         return redirect("profile")
-    except amounts.BadFormatAmount as e:
-        return HttpResponse(e.msg, status=e.status_code) 
-    except amounts.CurrencyMismatchException as e:
-        return HttpResponse(e.msg, status=e.status_code) 
     except SameAccountException:
         logger.error("Odd situation: SameAccountException should NOT occur in 
this function")
-        return HttpResponse("internal server error", status=500)
+    except BadFormatAmount:
+        logger.error("parsing MAX_DEBT or MAX_BANK_DEBT failed")
+    except CurrencyMismatch:
+        # The only currency mismatch which can occur here is
+        # between the bank and credit/debit accounts', should
+        # never happen.
+        return HttpResponse("Internal server error", status=500) 
+
 
     request_url = urljoin(exchange_url, "admin/add/incoming")
     res = requests.post(request_url, json=json_body)
@@ -273,22 +275,25 @@ def register(request):
         return render(request, "register.html", dict(not_available=True))
     with transaction.atomic():
         user = User.objects.create_user(username=username, password=password)
-        user_account = BankAccount(user=user, currency=settings.TALER_CURRENCY)
+        user_account = BankAccount(user=user)
         user_account.save()
     bank_internal_account = BankAccount.objects.get(account_no=1)
-    amount = dict(value=100, fraction=0, currency=settings.TALER_CURRENCY)
+    print('bank_internal_account currency: ' + 
bank_internal_account.amount.currency)
     try:
-        wire_transfer(amount, bank_internal_account, user_account, "Joining 
bonus")
+        wire_transfer(Amount(settings.TALER_CURRENCY, 100, 0), 
bank_internal_account, user_account, "Joining bonus")
     except DebtLimitExceededException:
         logger.info("Debt situation encountered")
         request.session["no_initial_bonus"] = True
-    except amounts.CurrencyMismatchException as e:
-        return HttpResponse(e.msg, status=e.status_code)
-    except amounts.BadFormatAmount as e:
-        return HttpResponse(e.msg, status=e.status_code)
+        return HttpResponseServerError()
+    except CurrencyMismatch:
+        logger.error("Currency mismatch internal to the bank")
+        return HttpResponseServerError()
+    except BadFormatAmount:
+        logger.error("Could not parse MAX_DEBT|MAX_BANK_DEBT")
+        return HttpResponseServerError()
     except SameAccountException:
         logger.error("Odd situation: SameAccountException should NOT occur in 
this function")
-        return HttpResponse("internal server error", status=500)
+        return HttpResponseServerError()
         
     request.session["just_registered"] = True
     user = django.contrib.auth.authenticate(username=username, 
password=password)
@@ -312,13 +317,13 @@ def extract_history(account):
     for item in related_transactions:
         if item.credit_account == account:
             counterpart = item.debit_account
-            sign = 1
+            sign = ""
         else:
             counterpart = item.credit_account
-            sign = -1
+            sign = "-"
         entry = dict(
-            float_amount=amounts.stringify(amounts.floatify(item.amount_obj) * 
sign),
-            float_currency=item.currency,
+            sign = sign,
+            amount = item.amount.stringify(settings.TALER_DIGITS),
             counterpart=counterpart.account_no,
             counterpart_username=counterpart.user.username,
             subject=item.subject,
@@ -421,7 +426,7 @@ def history(request):
             counterpart = entry.debit_account.account_no
             sign_ = "+"
         history.append(dict(counterpart=counterpart,
-                            amount=entry.amount_obj,
+                            amount=entry.amount.dump(),
                             sign=sign_,
                             wt_subject=entry.subject,
                             row_id=entry.id,
@@ -484,29 +489,37 @@ def add_incoming(request):
     except BankAccount.DoesNotExist:
         return HttpResponse(status=404)
     try:
-        transaction = wire_transfer(data["amount"],
+        schemas.validate_amount(data["amount"])
+        if settings.TALER_CURRENCY != data["amount"]["currency"]:
+            logger.error("Currency differs from bank's")
+            return JsonResponse(dict(error="Currency differs from bank's"), 
status=406)
+        transaction = wire_transfer(Amount(**data["amount"]),
                                     user_account.bankaccount,
                                     credit_account,
                                     subject)
         return JsonResponse(dict(serial_id=transaction.id, 
timestamp="/Date(%s)/" % int(transaction.date.timestamp())))
-    except amounts.BadFormatAmount as e:
-        return JsonResponse(dict(error=e.msg), status=e.status_code)
+    except ValueError as e:
+        return JsonResponse(dict(error=e), status=400)
+
+    except BadFormatAmount:
+        logger("Bad MAX_DEBT|MAX_BANK_DEBT format")
+    except CurrencyMismatch:
+        logger.error("Internal currency inconsistency")
+        return JsonResponse(dict(error="Internal server error"), status=500)
     except SameAccountException:
         return JsonResponse(dict(error="debit and credit account are the 
same"), status=422)
     except DebtLimitExceededException:
         logger.info("Prevenetd transfer, debit account would go beyond debt 
threshold")
         return JsonResponse(dict(error="debit count has reached its debt 
limit", status=403 ),
                              status=403)
-    except amounts.CurrencyMismatchException as e:
-        return JsonResponse(dict(error=e.msg), status=e.status_code)
 
 @login_required
 @require_POST
 def withdraw_nojs(request):
 
     try:
-        amount = amounts.parse_amount(request.POST.get("kudos_amount", ""))
-    except amounts.BadFormatAmount:
+        amount = Amount.parse(request.POST.get("kudos_amount", ""))
+    except BadFormatAmount:
         logger.error("Amount did not pass parsing")
         return HttpResponseBadRequest()
 
@@ -516,7 +529,7 @@ def withdraw_nojs(request):
     response["X-Taler-Operation"] = "create-reserve"
     response["X-Taler-Callback-Url"] = reverse("pin-question")
     response["X-Taler-Wt-Types"] = '["test"]'
-    response["X-Taler-Amount"] = json.dumps(amount)
+    response["X-Taler-Amount"] = amount.stringify(settings.TALER_CURRENCY)
     response["X-Taler-Sender-Wire"] = json.dumps(dict(
         type="test",
         bank_uri=request.build_absolute_uri(reverse("index")),
@@ -527,81 +540,48 @@ def withdraw_nojs(request):
     return response
 
 
-def wire_transfer(amount,
-                  debit_account,
-                  credit_account,
-                  subject):
+def wire_transfer(amount, debit_account, credit_account, subject):
     if debit_account.pk == credit_account.pk:
         logger.error("Debit and credit account are the same!")
         raise SameAccountException()
 
-    transaction_item = BankTransaction(amount_value=amount["value"],
-                                       amount_fraction=amount["fraction"],
-                                       currency=amount["currency"],
+    transaction_item = BankTransaction(amount=amount,
                                        credit_account=credit_account,
                                        debit_account=debit_account,
                                        subject=subject)
-
-    try:
-        if debit_account.debit:
-            debit_account.balance_obj = 
amounts.amount_add(debit_account.balance_obj,
-                                                           amount)
-    
-        elif -1 == amounts.amount_cmp(debit_account.balance_obj, amount):
-            debit_account.debit = True
-            debit_account.balance_obj = amounts.amount_sub(amount,
-                                                           
debit_account.balance_obj)
-        else:
-            debit_account.balance_obj = 
amounts.amount_sub(debit_account.balance_obj,
-                                                           amount)
-
-        if False == credit_account.debit:
-            credit_account.balance_obj = 
amounts.amount_add(credit_account.balance_obj,
-                                                            amount)
-    
-        elif 1 == amounts.amount_cmp(amount, credit_account.balance_obj):
-            credit_account.debit = False
-            credit_account.balance_obj = amounts.amount_sub(amount,
-                                                            
credit_account.balance_obj)
-        else:
-            credit_account.balance_obj = 
amounts.amount_sub(credit_account.balance_obj,
-                                                            amount)
-    except amounts.CurrencyMismatchException:
-        msg = "The amount to be transferred (%s) doesn't match the bank's 
currency (%s)" % (amount["currency"], settings.TALER_CURRENCY)
-        status_code = 406
-        if settings.TALER_CURRENCY != credit_account.balance_obj["currency"]:
-            logger.error("Internal inconsistency: credit account's currency 
(%s) differs from bank's (%s)" % (credit_account.balance_obj["currency"], 
settings.TALER_CURRENCY))
-            msg = "Internal server error"
-            status_code = 500
-        elif settings.TALER_CURRENCY != debit_account.balance_obj["currency"]:
-            logger.error("Internal inconsistency: debit account's currency 
(%s) differs from bank's (%s)" % (debit_account.balance_obj["currency"], 
settings.TALER_CURRENCY))
-            msg = "Internal server error"
-            status_code = 500
-        logger.error(msg)
-        raise amounts.CurrencyMismatchException(msg=msg, 
status_code=status_code)
+    if debit_account.debit:
+        debit_account.amount.add(amount)
+   
+    elif -1 == Amount.cmp(debit_account.amount, amount):
+        debit_account.debit = True
+        tmp = Amount(**amount.dump())
+        tmp.subtract(debit_account.amount)
+        debit_account.amount.set(**tmp.dump())
+    else:
+        debit_account.amount.subtract(amount)
+
+    if not credit_account.debit:
+        credit_account.amount.add(amount)
+    elif 1 == Amount.cmp(amount, credit_account.amount):
+        credit_account.debit = False
+        tmp = Amount(**amount.dump())
+        tmp.subtract(credit_account.amount)
+        credit_account.amount.set(**tmp.dump())
+    else:
+        credit_account.amount.subtract(amount)
 
     # Check here if any account went beyond the allowed
     # debit threshold.
 
-    try:
-        threshold = amounts.parse_amount(settings.TALER_MAX_DEBT)
-
-        if debit_account.user.username == "Bank":
-            threshold = amounts.parse_amount(settings.TALER_MAX_DEBT_BANK)
-    except amounts.BadFormatAmount:
-        logger.error("MAX_DEBT|MAX_DEBT_BANK had the wrong format")
-        raise amounts.BadFormatAmount(msg="internal server error", 
status_code=500)
-
-    try:
-        if 1 == amounts.amount_cmp(debit_account.balance_obj, threshold) \
-           and 0 != amounts.amount_cmp(amounts.get_zero(), threshold) \
-           and debit_account.debit:
-            logger.info("Negative balance '%s' not allowed." % 
json.dumps(debit_account.balance_obj))
-            logger.info("%s's threshold is: '%s'." % 
(debit_account.user.username, json.dumps(threshold)))
-            raise DebtLimitExceededException()
-    except amounts.CurrencyMismatchException:
-        logger.error("(Internal) currency mismatch between debt threshold and 
debit account")
-        raise amounts.CurrencyMismatchException(msg="internal server error", 
status_code=500)
+    threshold = Amount.parse(settings.TALER_MAX_DEBT)
+    if debit_account.user.username == "Bank":
+        threshold = Amount.parse(settings.TALER_MAX_DEBT_BANK)
+    if 1 == Amount.cmp(debit_account.amount, threshold) \
+        and 0 != Amount.cmp(Amount(settings.TALER_CURRENCY), threshold) \
+        and debit_account.debit:
+        logger.info("Negative balance '%s' not allowed." % 
json.dumps(debit_account.amount.dump()))
+        logger.info("%s's threshold is: '%s'." % (debit_account.user.username, 
json.dumps(threshold.dump())))
+        raise DebtLimitExceededException()
 
     with transaction.atomic():
         debit_account.save()
diff --git a/talerbank/settings.py b/talerbank/settings.py
index ccaee76..3dad905 100644
--- a/talerbank/settings.py
+++ b/talerbank/settings.py
@@ -187,8 +187,8 @@ except ConfigurationError as e:
     logger.error(e)
     sys.exit(3)
 
-TALER_MAX_DEBT = tc.value_string("bank", "MAX_DEBT", default="%s:50" % 
TALER_CURRENCY)
-TALER_MAX_DEBT_BANK = tc.value_string("bank", "MAX_DEBT_BANK", default="%s:0" 
% TALER_CURRENCY)
+TALER_MAX_DEBT = tc.value_string("bank", "MAX_DEBT", default="%s:50.0" % 
TALER_CURRENCY)
+TALER_MAX_DEBT_BANK = tc.value_string("bank", "MAX_DEBT_BANK", 
default="%s:0.0" % TALER_CURRENCY)
 
 TALER_DIGITS = tc.value_int("bank", "NDIGITS", default=2)
 TALER_PREDEFINED_ACCOUNTS = ['Tor', 'GNUnet', 'Taler', 'FSF', 'Tutorial']

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]