gnunet-svn
[Top][All Lists]
Advanced

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

[taler-bank] branch master updated: preparations towards the new bank AP


From: gnunet
Subject: [taler-bank] branch master updated: preparations towards the new bank API
Date: Thu, 16 Jan 2020 18:11:50 +0100

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

dold pushed a commit to branch master
in repository bank.

The following commit(s) were added to refs/heads/master by this push:
     new 47ff439  preparations towards the new bank API
47ff439 is described below

commit 47ff439abf85ecea69d7f281ca30a00143c8a868
Author: Florian Dold <address@hidden>
AuthorDate: Thu Jan 16 18:11:35 2020 +0100

    preparations towards the new bank API
---
 Makefile                                           |   2 +-
 bin/taler-bank-manage                              |   3 +
 run-tests.sh                                       |   2 +-
 setup.py                                           |   2 +-
 talerbank/__init__.py                              |   2 +-
 .../app/management/commands/add_bank_account.py    |  12 +-
 talerbank/app/management/commands/dump_talerdb.py  |   6 +-
 .../app/management/commands/provide_accounts.py    |   7 +-
 talerbank/app/management/commands/top_up.py        |  14 +-
 talerbank/app/management/commands/wire_transfer.py |  23 +-
 talerbank/app/middleware.py                        | 104 ++--
 talerbank/app/migrations/0001_initial.py           |  94 ++-
 talerbank/app/models.py                            | 242 ++++----
 talerbank/app/schemas.py                           |  52 +-
 talerbank/app/templates/profile_page.html          |  46 +-
 talerbank/app/tests.py                             | 691 +++++++--------------
 talerbank/app/urls.py                              |  29 +-
 talerbank/app/views.py                             | 273 +++-----
 talerbank/jinja2.py                                |  20 +-
 talerbank/settings.py                              | 110 ++--
 20 files changed, 644 insertions(+), 1090 deletions(-)

diff --git a/Makefile b/Makefile
index b416c64..21caedf 100644
--- a/Makefile
+++ b/Makefile
@@ -36,4 +36,4 @@ dist:
 
 .PHONY: pretty
 pretty:
-       yapf -r -i talerbank/
+       black talerbank/
diff --git a/bin/taler-bank-manage b/bin/taler-bank-manage
index 4397bae..45047a4 100755
--- a/bin/taler-bank-manage
+++ b/bin/taler-bank-manage
@@ -65,8 +65,11 @@ def handle_django(args):
 def handle_serve_http(args):
     import django
     django.setup()
+    print("migrating")
     call_command('migrate')
+    print("providing accounts")
     call_command('provide_accounts')
+    print("checking")
     call_command('check')
     port = args.port
     TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE", 
args.config))
diff --git a/run-tests.sh b/run-tests.sh
index 62ccabf..d30faea 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -3,4 +3,4 @@
 # See Bug #5850 for some tests that are currently skipped
 
 export TALER_CONFIG_FILE="talerbank/app/testconfigs/bank-check.conf"
-./manage.py test --no-input talerbank.app.tests
+exec ./manage.py test --no-input talerbank.app.tests "$@"
diff --git a/setup.py b/setup.py
index 24a7193..18cbbf8 100755
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ setup(name='talerbank',
       author_email='address@hidden, address@hidden',
       license='GPL',
       packages=find_packages(),
-      install_requires=["django>=2",
+      install_requires=["django>=3",
                         "psycopg2",
                         "requests",
                         "uWSGI",
diff --git a/talerbank/__init__.py b/talerbank/__init__.py
index 6c4125c..80c8cd8 100644
--- a/talerbank/__init__.py
+++ b/talerbank/__init__.py
@@ -1,4 +1,4 @@
 import logging
 
-FMT = '%(asctime)-15s %(module)s %(levelname)s %(message)s'
+FMT = "%(asctime)-15s %(module)s %(levelname)s %(message)s"
 logging.basicConfig(format=FMT, level=logging.WARNING)
diff --git a/talerbank/app/management/commands/add_bank_account.py 
b/talerbank/app/management/commands/add_bank_account.py
index 4378e44..4c4aacf 100644
--- a/talerbank/app/management/commands/add_bank_account.py
+++ b/talerbank/app/management/commands/add_bank_account.py
@@ -40,8 +40,12 @@ class Command(BaseCommand):
     help = "Add bank accounts."
 
     def add_arguments(self, parser):
-        parser.add_argument("accountname", type=str, help="Login name of the 
new bank account")
-        parser.add_argument("--public", action="store_true", help="Make the 
bank account public")
+        parser.add_argument(
+            "accountname", type=str, help="Login name of the new bank account"
+        )
+        parser.add_argument(
+            "--public", action="store_true", help="Make the bank account 
public"
+        )
 
     ##
     # Django-specific definition to invoke the account creator
@@ -56,4 +60,6 @@ class Command(BaseCommand):
             ),
             is_public=False,
         ).save()
-        print(f"Created new bank account {accountname} (password set to random 
password)")
+        print(
+            f"Created new bank account {accountname} (password set to random 
password)"
+        )
diff --git a/talerbank/app/management/commands/dump_talerdb.py 
b/talerbank/app/management/commands/dump_talerdb.py
index 92849eb..bd40064 100644
--- a/talerbank/app/management/commands/dump_talerdb.py
+++ b/talerbank/app/management/commands/dump_talerdb.py
@@ -37,9 +37,7 @@ def dump_accounts():
             print("No accounts created yet..")
             return
         for acc in accounts:
-            print(acc.user.username + \
-                  " has account number " + \
-                  str(acc.account_no))
+            print(acc.user.username + " has account number " + 
str(acc.account_no))
     except (OperationalError, ProgrammingError):
         LOGGER.error("Hard database error, does it exist?")
         sys.exit(1)
@@ -56,7 +54,7 @@ def dump_history():
             msg.append("-%s, " % item.debit_account.account_no)
             msg.append(item.amount.stringify(2))
             msg.append(" '" + item.subject + "'")
-            print(''.join(msg))
+            print("".join(msg))
     except (OperationalError, ProgrammingError):
         LOGGER.error("Hard database error, does it exist?")
         sys.exit(1)
diff --git a/talerbank/app/management/commands/provide_accounts.py 
b/talerbank/app/management/commands/provide_accounts.py
index ce84835..8e729e3 100644
--- a/talerbank/app/management/commands/provide_accounts.py
+++ b/talerbank/app/management/commands/provide_accounts.py
@@ -46,10 +46,9 @@ def make_account(username):
         LOGGER.info("Creating account for '%s'", username)
         BankAccount(
             user=User.objects.create_user(
-                username=username,
-                password=str(uuid.uuid4())
+                username=username, password=str(uuid.uuid4())
             ),
-            is_public=True
+            is_public=True,
         ).save()
 
     except (OperationalError, ProgrammingError):
@@ -58,7 +57,7 @@ def make_account(username):
             " is not migrated.  Try 'taler-bank-manage"
             " django migrate' in the latter case.",
             stack_info=False,
-            exc_info=True
+            exc_info=True,
         )
         sys.exit(1)
 
diff --git a/talerbank/app/management/commands/top_up.py 
b/talerbank/app/management/commands/top_up.py
index c0da7e7..88cca0a 100644
--- a/talerbank/app/management/commands/top_up.py
+++ b/talerbank/app/management/commands/top_up.py
@@ -44,12 +44,14 @@ class Command(BaseCommand):
             "user",
             type=str,
             metavar="USERNAME",
-            help="User that is getting credited with the top-up"
+            help="User that is getting credited with the top-up",
         )
         parser.add_argument(
-            "amount", type=str, metavar="AMOUNT",
-            help="Wire transfer's amount, given in the " \
-            "CURRENCY:X.Y form.")
+            "amount",
+            type=str,
+            metavar="AMOUNT",
+            help="Wire transfer's amount, given in the " "CURRENCY:X.Y form.",
+        )
 
     ##
     # Django-specific definition to invoke the account creator
@@ -60,9 +62,7 @@ class Command(BaseCommand):
         user = User.objects.get(username=options["user"])
         # Take the money from the bank's account
         try:
-            debit_account = BankAccount.objects.get(
-                account_no=1,
-            )
+            debit_account = BankAccount.objects.get(account_no=1,)
         except BankAccount.DoesNotExist:
             LOGGER.error("Debit account (bank's own account) does not exist.")
             sys.exit(1)
diff --git a/talerbank/app/management/commands/wire_transfer.py 
b/talerbank/app/management/commands/wire_transfer.py
index bf6bef1..08372ac 100644
--- a/talerbank/app/management/commands/wire_transfer.py
+++ b/talerbank/app/management/commands/wire_transfer.py
@@ -46,30 +46,29 @@ class Command(BaseCommand):
             "user",
             type=str,
             metavar="USERNAME",
-            help="Which user is performing the wire transfer"
+            help="Which user is performing the wire transfer",
         )
         parser.add_argument(
-            "password",
-            type=str,
-            metavar="PASSWORD",
-            help="Performing user's password."
+            "password", type=str, metavar="PASSWORD", help="Performing user's 
password."
         )
         parser.add_argument(
             "credit-account",
             type=int,
             metavar="CREDIT-ACCOUNT",
-            help="Which account number will *receive* money."
+            help="Which account number will *receive* money.",
         )
         parser.add_argument(
             "subject",
             type=str,
             metavar="SUBJECT",
-            help="SUBJECT will be the wire transfer subject."
+            help="SUBJECT will be the wire transfer subject.",
         )
         parser.add_argument(
-            "amount", type=str, metavar="AMOUNT",
-            help="Wire transfer's amount, given in the " \
-            "CURRENCY:X.Y form.")
+            "amount",
+            type=str,
+            metavar="AMOUNT",
+            help="Wire transfer's amount, given in the " "CURRENCY:X.Y form.",
+        )
 
     ##
     # This callable gets invoked when the user invokes the
@@ -80,9 +79,7 @@ class Command(BaseCommand):
     # @param args arguments list -- currently unused.
     # @param options options given by the user at the command line.
     def handle(self, *args, **options):
-        user = authenticate(
-            username=options["user"], password=options["password"]
-        )
+        user = authenticate(username=options["user"], 
password=options["password"])
         if not user:
             LOGGER.error("Wrong user/password.")
             sys.exit(1)
diff --git a/talerbank/app/middleware.py b/talerbank/app/middleware.py
index eb34b96..e335a1a 100644
--- a/talerbank/app/middleware.py
+++ b/talerbank/app/middleware.py
@@ -5,21 +5,18 @@ from . import urls
 from django.http import JsonResponse
 from django.urls import reverse
 from django.shortcuts import redirect
-from .models import BankAccount, BankTransaction, \
-    BankAccountDoesNotExist, BankTransactionDoesNotExist
-from .views import \
-    (DebitLimitException, SameAccountException,
-     LoginFailed, RejectNoRightsException, UnhandledException,
-     set_profile_hint)
-
-from .schemas import \
-    (JSONFieldException,
-     URLParamValidationError,
-     InvalidSession)
-
-from taler.util.amount import \
-    (CurrencyMismatch, BadFormatAmount,
-     NumberTooBig, NegativeNumber)
+from .models import BankAccount, BankTransaction
+from .views import (
+    DebitLimitException,
+    SameAccountException,
+    LoginFailed,
+    UnhandledException,
+    set_profile_hint,
+)
+
+from .schemas import JSONFieldException, URLParamValidationError, 
InvalidSession
+
+from taler.util.amount import CurrencyMismatchError, AmountFormatError
 
 LOGGER = logging.getLogger()
 
@@ -56,65 +53,56 @@ class DecompressionMiddleware:
 
         return self.get_response(request)
 
-##
-# Class holding data needed by the handling logic.
+
 class ExceptionMiddleware:
+    """
+    Middleware for handling exceptions not caught directly
+    by the application logic.
+    """
 
-    ##
-    # Init constructor.
-    #
-    # @param self the object itself.
-    # @param get_response a Django-provided callable that calls
-    #        whatever comes next in the chain: a further middleware
-    #        or the view itself (please refer to the official
-    #        documentation for more details).
     def __init__(self, get_response):
+        """
+        # Init constructor.
+        #
+        # @param self the object itself.
+        # @param get_response a Django-provided callable that calls
+        #        whatever comes next in the chain: a further middleware
+        #        or the view itself (please refer to the official
+        #        documentation for more details).
+        """
         self.get_response = get_response
 
         # Map between endpoints and Web pages to render
         # after the exception gets managed.
         self.render = {
             reverse("profile", urlconf=urls): "profile",
-            reverse("register", urlconf=urls): "index", 
+            reverse("register", urlconf=urls): "index",
             reverse("public-accounts", urlconf=urls): "index",
         }
 
-    ##
-    # This function is transparently invoked by Django when
-    # a request traverses the chain made of middleware classes
-    # and the view itself as the last element in the chain.
-    #
-    # @param self this class.
-    # @param request Django-specific request object (of the same
-    #        type that is handed to views).
-    # @return Django-specific response object.
     def __call__(self, request):
+        """
+        This function is transparently invoked by Django when
+        a request traverses the chain made of middleware classes
+        and the view itself as the last element in the chain.
+        """
         return self.get_response(request)
 
-    ##
-    # Main logic for processing the exception.  It checks
-    # if the exception captured can be managed, and does it
-    # if so.  Otherwise, it lets the native handler operate.
-    #
-    # @param self a @a ExceptionMiddleware object.
-    # @param request Django-specific HTTP request.
-    # @param exception the exception raised from the bank.
     def process_exception(self, request, exception):
+        """
+        Main logic for processing the exception.  It checks
+        if the exception captured can be managed, and does it
+        if so.  Otherwise, it lets the native handler operate.
+        """
         LOGGER.error(f"Error: {exception}, while serving 
{request.get_full_path()}")
 
-        if not hasattr(exception, "taler_error_code"):
-            print("####### Exception without Taler Error Code ########")
-            traceback.print_exc()
-            print("###################################################")
-            exception = UnhandledException()
-
-        render_to = self.render.get(request.path)
-
-        if not render_to:
-            return JsonResponse({"ec": exception.taler_error_code,
-                                 "error": exception.hint},
-                                 status=exception.http_status_code)
-        set_profile_hint(request, failure=True, success=False, 
hint=exception.hint)
-        return redirect(render_to)
+        if hasattr(exception, "taler_error_code"):
+            render_to = self.render.get(request.path)
 
-# [1] https://git.taler.net/exchange.git/tree/src/include/taler_error_codes.h
+            if not render_to:
+                return JsonResponse(
+                    {"ec": exception.taler_error_code, "error": 
exception.hint},
+                    status=exception.http_status_code,
+                )
+            set_profile_hint(request, failure=True, success=False, 
hint=exception.hint)
+            return redirect(render_to)
diff --git a/talerbank/app/migrations/0001_initial.py 
b/talerbank/app/migrations/0001_initial.py
index e6ca081..91c0e98 100644
--- a/talerbank/app/migrations/0001_initial.py
+++ b/talerbank/app/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.4 on 2019-08-27 18:55
+# Generated by Django 3.0.2 on 2020-01-13 12:57
 
 from django.conf import settings
 from django.db import migrations, models
@@ -17,100 +17,94 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.CreateModel(
-            name='BankAccount',
+            name="BankAccount",
             fields=[
-                ('is_public', models.BooleanField(default=False)),
-                ('debit', models.BooleanField(default=False)),
+                ("is_public", models.BooleanField(default=False)),
+                ("account_no", models.AutoField(primary_key=True, 
serialize=False)),
                 (
-                    'account_no',
-                    models.AutoField(primary_key=True, serialize=False)
+                    "balance",
+                    talerbank.app.models.SignedAmountField(
+                        default=talerbank.app.models.get_zero_signed_amount
+                    ),
                 ),
                 (
-                    'amount',
-                    talerbank.app.models.AmountField(
-                        default=talerbank.app.models.get_zero_amount
-                    )
-                ),
-                (
-                    'user',
+                    "user",
                     models.OneToOneField(
                         on_delete=django.db.models.deletion.CASCADE,
-                        to=settings.AUTH_USER_MODEL
-                    )
+                        to=settings.AUTH_USER_MODEL,
+                    ),
                 ),
             ],
         ),
         migrations.CreateModel(
-            name='TalerWithdrawOperation',
+            name="TalerWithdrawOperation",
             fields=[
                 (
-                    'withdraw_id',
+                    "withdraw_id",
                     models.UUIDField(
                         default=uuid.uuid4,
                         editable=False,
                         primary_key=True,
-                        serialize=False
-                    )
+                        serialize=False,
+                    ),
                 ),
-                ('amount', talerbank.app.models.AmountField(default=False)),
-                ('selection_done', models.BooleanField(default=False)),
-                ('withdraw_done', models.BooleanField(default=False)),
-                ('selected_reserve_pub', models.TextField(null=True)),
+                ("amount", talerbank.app.models.AmountField(default=False)),
+                ("selection_done", models.BooleanField(default=False)),
+                ("withdraw_done", models.BooleanField(default=False)),
+                ("selected_reserve_pub", models.TextField(null=True)),
                 (
-                    'selected_exchange_account',
+                    "selected_exchange_account",
                     models.ForeignKey(
                         null=True,
                         on_delete=django.db.models.deletion.CASCADE,
-                        related_name='selected_exchange_account',
-                        to='app.BankAccount'
-                    )
+                        related_name="selected_exchange_account",
+                        to="app.BankAccount",
+                    ),
                 ),
                 (
-                    'withdraw_account',
+                    "withdraw_account",
                     models.ForeignKey(
                         on_delete=django.db.models.deletion.CASCADE,
-                        related_name='withdraw_account',
-                        to='app.BankAccount'
-                    )
+                        related_name="withdraw_account",
+                        to="app.BankAccount",
+                    ),
                 ),
             ],
         ),
         migrations.CreateModel(
-            name='BankTransaction',
+            name="BankTransaction",
             fields=[
                 (
-                    'id',
+                    "id",
                     models.AutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
-                        verbose_name='ID'
-                    )
+                        verbose_name="ID",
+                    ),
                 ),
-                ('amount', talerbank.app.models.AmountField(default=False)),
+                ("amount", talerbank.app.models.AmountField(default=False)),
                 (
-                    'subject',
-                    models.CharField(
-                        default='(no subject given)', max_length=200
-                    )
+                    "subject",
+                    models.CharField(default="(no subject given)", 
max_length=200),
                 ),
-                ('date', models.DateTimeField(auto_now=True, db_index=True)),
-                ('cancelled', models.BooleanField(default=False)),
+                ("date", models.DateTimeField(auto_now=True, db_index=True)),
+                ("cancelled", models.BooleanField(default=False)),
                 (
-                    'credit_account',
+                    "credit_account",
                     models.ForeignKey(
                         on_delete=django.db.models.deletion.CASCADE,
-                        related_name='credit_account',
-                        to='app.BankAccount'
-                    )
+                        related_name="credit_account",
+                        to="app.BankAccount",
+                    ),
                 ),
                 (
-                    'debit_account',
+                    "debit_account",
                     models.ForeignKey(
                         on_delete=django.db.models.deletion.CASCADE,
-                        related_name='debit_account',
-                        to='app.BankAccount'
-                    )
+                        related_name="debit_account",
+                        to="app.BankAccount",
+                    ),
                 ),
             ],
         ),
diff --git a/talerbank/app/models.py b/talerbank/app/models.py
index f869ea3..08da111 100644
--- a/talerbank/app/models.py
+++ b/talerbank/app/models.py
@@ -23,179 +23,185 @@ from typing import Any, Tuple
 from django.contrib.auth.models import User
 from django.db import models
 from django.conf import settings
-from django.core.exceptions import \
-    ValidationError, \
-    ObjectDoesNotExist
-from taler.util.amount import Amount, BadFormatAmount, NumberTooBig, 
CurrencyMismatch
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
+from taler.util.amount import Amount, SignedAmount, CurrencyMismatchError
 
-class InvalidAmount(Amount):
-    def __init__(self, currency):
-        super(InvalidAmount, self
-              ).__init__(currency, value=float('nan'), fraction=float('nan'))
 
-    def stringify(self, ndigits, pretty):
-        return "Invalid Amount, please report"
+def get_zero_amount() -> Amount:
+    """
+    Helper function that instantiates a zero-valued Amount
+    object in the currency that the bank runs on.
+    """
+    return Amount(settings.TALER_CURRENCY, 0, 0)
 
-    def dump(self):
-        return "Invalid Amount, please report"
 
+def get_zero_signed_amount() -> SignedAmount:
+    """
+    Helper function that instantiates a zero-valued SignedAmount
+    object in the currency that the bank runs on.
+    """
+    return SignedAmount(True, get_zero_amount())
 
-##
-# Helper function that instantiates a zero-valued @a Amount
-# object.
-def get_zero_amount() -> Amount:
-    return Amount(settings.TALER_CURRENCY)
+
+class SignedAmountField(models.Field):
+    """Custom implementation of the SignedAmount class as a database type."""
+
+    description = "Signed amount object in Taler style"
+
+    def db_type(self, connection: Any) -> str:
+        """
+        Return the database type of the serialized amount.
+        """
+        return "varchar"
+
+    def get_prep_value(self, value: SignedAmount) -> str:
+        """
+        Stringifies the Amount object to feed the DB connector.
+        """
+        c = value.amount.currency
+        if settings.TALER_CURRENCY != c:
+            raise CurrencyMismatchError(settings.TALER_CURRENCY, c)
+        return value.stringify()
+
+    @staticmethod
+    def from_db_value(value: str, *args) -> Amount:
+        """
+        Parse the stringified Amount back to Python.
+
+        Parameters
+        ----------
+        value : str
+            Serialized amount coming from the database.
+            (String in the usual CURRENCY:X.Y format)
+        args : any
+            Unused
+        """
+        del args  # pacify PEP checkers
+        return SignedAmount.parse(value)
+
+    def to_python(self, value: Any) -> Amount:
+        """
+        Parse the stringified Amount back to Python. FIXME:
+        why this serializer consider _more_ cases respect to the
+        one above ('from_db_value')?
+
+        Parameters
+        ----------
+        value: serialized amount coming from the database
+
+        """
+
+        if isinstance(value, SignedAmount):
+            return value
+        try:
+            return SignedAmount.parse(value)
+        except BadFormatAmount:
+            raise ValidationError(
+                "Invalid input for a signed amount string: %s" % value
+            )
 
 
-##
-# Custom implementation of the @a Amount class as a database type.
 class AmountField(models.Field):
-    description = 'Amount object in Taler style'
-
-    ##
-    # Return the database type of the serialized amount.
-    #
-    # @param self the object itself.
-    # @param connection the database connection.
-    # @return type of the serialized amount: varchar.
+    """Custom implementation of the Amount class as a database type."""
+
+    description = "Amount object in Taler style"
+
     def db_type(self, connection: Any) -> str:
+        """
+        Return the database type of the serialized amount.
+        """
         return "varchar"
 
-    ##
-    # Stringifies the Amount object to feed the DB connector.
-    #
-    # @param self the object itself.
-    # @para value the @a Amount object to be serialized.
     def get_prep_value(self, value: Amount) -> str:
-        if not value:
-            return "%s:0.0" % settings.TALER_CURRENCY
+        """
+        Stringifies the Amount object to feed the DB connector.
+        """
         if settings.TALER_CURRENCY != value.currency:
-            raise CurrencyMismatch(settings.TALER_CURRENCY, value.currency)
-        return value.stringify(settings.TALER_DIGITS)
-
-    ##
-    # Parse the stringified Amount back to Python.
-    #
-    # @param value serialized amount coming from the database.
-    #        (It is just a string in the usual CURRENCY:X.Y form)
-    # @param args currently unused.
-    # @return the @a Amount object.
+            raise CurrencyMismatchError(settings.TALER_CURRENCY, 
value.currency)
+        return value.stringify()
+
     @staticmethod
     def from_db_value(value: str, *args) -> Amount:
+        """
+        Parse the stringified Amount back to Python.
+
+        Parameters
+        ----------
+        value : str
+            Serialized amount coming from the database.
+            (String in the usual CURRENCY:X.Y format)
+        args : any
+            Unused
+        """
         del args  # pacify PEP checkers
-        if value is None:
-            return Amount.parse(settings.TALER_CURRENCY)
-        try:
-            return Amount.parse(value)
-        except NumberTooBig:
-            # Keep the currency right to avoid causing
-            # exceptions if some operation is attempted
-            # against this invalid amount.  NOTE that the
-            # value is defined as NaN, so no actual/useful
-            # amount will ever be generated using this one.
-            # And more, the NaN value will make it easier
-            # to scan the database to find these faulty
-            # amounts.
-            # We also decide to not raise exception here
-            # because they would propagate in too many places
-            # in the code, and it would be too verbose to
-            # just try-cactch any possible exception situation.
-            return InvalidAmount(settings.TALER_CURRENCY)
-
-    ##
-    # Parse the stringified Amount back to Python. FIXME:
-    # why this serializer consider _more_ cases respect to the
-    # one above ('from_db_value')?
-    #
-    # @param value serialized amount coming from the database.
-    #        (It is just a string in the usual CURRENCY:X.Y form)
-    # @param args currently unused.
-    # @return the @a Amount object.
+        return Amount.parse(value)
+
     def to_python(self, value: Any) -> Amount:
+        """
+        Parse the stringified Amount back to Python. FIXME:
+        why this serializer consider _more_ cases respect to the
+        one above ('from_db_value')?
+
+        Parameters
+        ----------
+        value: serialized amount coming from the database
+
+        """
+
         if isinstance(value, Amount):
             return value
         try:
-            if value is None:
-                return Amount.parse(settings.TALER_CURRENCY)
             return Amount.parse(value)
         except BadFormatAmount:
             raise ValidationError("Invalid input for an amount string: %s" % 
value)
 
-class BankAccountDoesNotExist(Exception):
-    def __init__(self, msg):
-        super(BankAccountDoesNotExist, self).__init__(msg)
-        self.hint = msg
-        self.http_status_code = 404
-        self.taler_error_code = 5110
-
-class BankTransactionDoesNotExist(Exception):
-    def __init__(self, msg):
-        super(BankTransactionDoesNotExist, self).__init__(msg)
-        self.hint = msg
-        self.http_status_code = 404
-        self.taler_error_code = 5111
 
 def join_dict(**inputDict):
-    return ", ".join(['%s==%s' % (key, value) for (key, value) in 
inputDict.items()])
+    return ", ".join(["%s==%s" % (key, value) for (key, value) in 
inputDict.items()])
 
-class CustomManager(models.Manager):
 
-    def __init__(self):
-        super(CustomManager, self).__init__()
-
-    def get_queryset(self):
-        return models.QuerySet(self.model, using=self._db)
-
-    def get(self, *args, **kwargs):
-        try:
-            return super(CustomManager, self).get(*args, **kwargs)
-        except BankAccount.DoesNotExist:
-            raise BankAccountDoesNotExist(f"Bank account not found for 
{join_dict(**kwargs)}")
-        except BankTransaction.DoesNotExist:
-            raise BankTransactionDoesNotExist(f"Bank transaction not found for 
{join_dict(**kwargs)}")
-
-##
-# The class representing a bank account.
 class BankAccount(models.Model):
+    """
+    The class representing a bank account.
+    """
+
     is_public = models.BooleanField(default=False)
-    debit = models.BooleanField(default=False)
     account_no = models.AutoField(primary_key=True)
     user = models.OneToOneField(User, on_delete=models.CASCADE)
-    amount = AmountField(default=get_zero_amount)
-    objects = CustomManager()
+    balance = SignedAmountField(default=get_zero_signed_amount)
+
 
-##
-# The class representing a bank transaction.
 class BankTransaction(models.Model):
+    """
+    The class representing a bank transaction.
+    """
+
     amount = AmountField(default=False)
     debit_account = models.ForeignKey(
         BankAccount,
         on_delete=models.CASCADE,
         db_index=True,
-        related_name="debit_account"
+        related_name="debit_account",
     )
     credit_account = models.ForeignKey(
         BankAccount,
         on_delete=models.CASCADE,
         db_index=True,
-        related_name="credit_account"
+        related_name="credit_account",
     )
     subject = models.CharField(default="(no subject given)", max_length=200)
     date = models.DateTimeField(auto_now=True, db_index=True)
     cancelled = models.BooleanField(default=False)
-    objects = CustomManager()
 
 
 class TalerWithdrawOperation(models.Model):
-    withdraw_id = models.UUIDField(
-        primary_key=True, default=uuid.uuid4, editable=False
-    )
+    withdraw_id = models.UUIDField(primary_key=True, default=uuid.uuid4, 
editable=False)
     amount = AmountField(default=False)
     withdraw_account = models.ForeignKey(
         BankAccount,
         on_delete=models.CASCADE,
         db_index=True,
-        related_name="withdraw_account"
+        related_name="withdraw_account",
     )
     selection_done = models.BooleanField(default=False)
     withdraw_done = models.BooleanField(default=False)
@@ -203,6 +209,6 @@ class TalerWithdrawOperation(models.Model):
         BankAccount,
         null=True,
         on_delete=models.CASCADE,
-        related_name="selected_exchange_account"
+        related_name="selected_exchange_account",
     )
     selected_reserve_pub = models.TextField(null=True)
diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
index 2825241..91d6c0a 100644
--- a/talerbank/app/schemas.py
+++ b/talerbank/app/schemas.py
@@ -32,7 +32,7 @@ from urllib.parse import urlparse
 # can handle (because of the wallet).
 # FIXME: also defined in views.py.  Need a common.py to contain
 # such definitions ?
-UINT64_MAX = (2**64) - 1
+UINT64_MAX = (2 ** 64) - 1
 
 ##
 # Pattern for amounts, plain RegEx.
@@ -54,11 +54,13 @@ class InvalidSession(ValueError):
         self.http_status_code = http_status_code
         super().__init__()
 
+
 class InternalServerError(Exception):
     def __init__(self, hint):
         self.hint = hint
         self.http_status_code = 500
-        self.taler_error_code = 1011 # TALER_EC_INTERNAL_LOGIC_ERROR
+        self.taler_error_code = 1011  # TALER_EC_INTERNAL_LOGIC_ERROR
+
 
 ##
 # Exception class to be raised when a JSON
@@ -82,6 +84,7 @@ class JSONFieldException(ValueError):
         self.http_status_code = http_status_code
         self.taler_error_code = 5106
 
+
 ##
 # Exception class to be raised when at least one expected URL
 # parameter is either not found or malformed.
@@ -103,9 +106,7 @@ class URLParamValidationError(ValueError):
 class AuthForm(forms.Form):
     type = forms.CharField(
         validators=[
-            RegexValidator(
-                "^basic$", message="Only 'basic' method provided for now"
-            )
+            RegexValidator("^basic$", message="Only 'basic' method provided 
for now")
         ]
     )
 
@@ -129,36 +130,24 @@ class AuthField(forms.Field):
 
 ##
 # Common logic to inherit from all the other validators
-class BankValidator():
+class BankValidator:
     def __init__(self, validator, data):
         self.validation_result = validator(data)
         if not self.validation_result.is_valid():
             raise JSONFieldException(self.validation_result.errors, 400)
 
     def get(self, name, default=None):
-        ret = self.validation_result.cleaned_data.get(name) 
+        ret = self.validation_result.cleaned_data.get(name)
         if not ret:
             return default
         return ret
 
 
-class RejectData(BankValidator):
-    def __init__(self, data):
-        super(RejectData, self).__init__(self.InnerValidator, data)
-
-    class InnerValidator(forms.Form):
-        auth = AuthField()
-        # FIXME: adjust min/max values.
-        row_id = forms.IntegerField()
-        account_number = forms.IntegerField()
-
-
 class AddIncomingData(BankValidator):
     def __init__(self, data):
         super(AddIncomingData, self).__init__(self.InnerValidator, data)
 
     class InnerValidator(forms.Form):
-        auth = AuthField()
         amount = forms.CharField(
             validators=[
                 RegexValidator(
@@ -174,20 +163,12 @@ class AddIncomingData(BankValidator):
 ##
 # Subset of /history and /history-range input.
 class HistoryParamsBase(forms.Form):
-    auth = forms.CharField(
-        validators=[
-            RegexValidator("^basic$", message="Only 'basic' is allowed")
-        ]
-    )
-
     cancelled = forms.CharField(
         required=False,
         empty_value="show",
         validators=[
-            RegexValidator(
-                "^(omit|show)$", message="Only 'omit' or 'show' are valid"
-            )
-        ]
+            RegexValidator("^(omit|show)$", message="Only 'omit' or 'show' are 
valid")
+        ],
     )
 
     ordering = forms.CharField(
@@ -196,16 +177,16 @@ class HistoryParamsBase(forms.Form):
         validators=[
             RegexValidator(
                 "^(ascending|descending)$",
-                message="Only 'ascending' or 'descending' are valid"
+                message="Only 'ascending' or 'descending' are valid",
             )
-        ]
+        ],
     )
 
     direction = forms.CharField(
         validators=[
             RegexValidator(
                 "^(debit|credit|both|cancel\+|cancel-)$",
-                message="Only: debit/credit/both/cancel+/cancel-"
+                message="Only: debit/credit/both/cancel+/cancel-",
             )
         ]
     )
@@ -217,7 +198,7 @@ class HistoryParamsBase(forms.Form):
 class HistoryParams(BankValidator):
     def __init__(self, data):
         super(HistoryParams, self).__init__(self.InnerValidator, data)
- 
+
     class InnerValidator(HistoryParamsBase):
         # FIXME: adjust min/max values.
         delta = forms.IntegerField()
@@ -225,7 +206,6 @@ class HistoryParams(BankValidator):
 
 
 class HistoryRangeParams(BankValidator):
-
     def __init__(self, data):
         super(HistoryRangeParams, self).__init__(self.InnerValidator, data)
 
@@ -256,12 +236,10 @@ class PaytoField(forms.Field):
 
 
 class WithdrawHeadless(BankValidator):
-
     def __init__(self, data):
         super(WithdrawHeadless, self).__init__(self.InnerValidator, data)
 
     class InnerValidator(forms.Form):
-        auth = AuthField()
         amount = forms.CharField(
             validators=[
                 RegexValidator(
@@ -272,8 +250,8 @@ class WithdrawHeadless(BankValidator):
         reserve_pub = forms.CharField(required=True)
         exchange_wire_details = PaytoField(required=False)
 
-class WithdrawHeadlessUri(BankValidator):
 
+class WithdrawHeadlessUri(BankValidator):
     def __init__(self, data):
         super(WithdrawHeadlessUri, self).__init__(self.InnerValidator, data)
 
diff --git a/talerbank/app/templates/profile_page.html 
b/talerbank/app/templates/profile_page.html
index bb6ce49..cea85ce 100644
--- a/talerbank/app/templates/profile_page.html
+++ b/talerbank/app/templates/profile_page.html
@@ -15,6 +15,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 
   @author Marcello Stanisci
+  @author Florian Dold <address@hidden>
 #}
 
 {% block headermsg %}
@@ -25,13 +26,7 @@
 {% endblock headermsg %}
 {% block content %}
   <section id="menu">
-    <p>Account: # {{ account_no }}</p>
-    {% if is_valid_amount(balance) %}
-      <p>Current balance: <b>{{ sign }} {{ amount_stringify(balance) }}</b></p>
-    {% else %}
-      <p class="informational-fail">Current balance: <b>INVALID AMOUNT, PLEASE 
REPORT</b></p>
-    {% endif%}
-
+    <p>Bank account balance: <br/> <b>{{ amount_stringify(balance) }}</b></p>
   </section>
   <section id="main">
     <article>
@@ -73,47 +68,12 @@
                  class="pure-button pure-button-primary"
                  type="submit"
                  value="Start withdrawal" />
-        </form>
-        <h2>Wire transfer</h2>
-        <form id="wt-form"
-              class="pure-form"
-              action="{{ url('profile') }}"
-              method="post"
-              name="tform">
-
-          <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token 
}}" />
-
-          <div id="transfer-fields">
-          
-          <div class="fieldbox">
-            <label class="fieldlabel" for="id_amount">Amount to 
transfer:</label>
-            {{ wt_form.amount }}<!--
-            --><input type="text" readonly class="currency-indicator" size="{{ 
currency|length }}" tabindex="-1" value="{{ currency }}">
-          </div>
-            
-          <div class="fieldbox">
-            <label class="fieldlabel" for="id_receiver">Receiver:</label>
-            {{ wt_form.receiver }}
-          </div>
-            
-          <div class="fieldbox">
-            <label class="fieldlabel" for="id_receiver">Subject:</label>
-            {{ wt_form.subject }}
-          </div>
-          
-          </div>
-
-
-          <input class="pure-button pure-button-primary"
-                 type="submit"
-                 value="Transfer!" />
-        </form>
       </div>
       <p>
       </p>
     </article>
     <article>
-      <h2>Transaction history</h2>
+      <h2>Transactions for {{ name }}</h2>
       <div id="transactions-history">
         {% if history %}
         <table class="pure-table">
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
index 87d7283..61aec9e 100644
--- a/talerbank/app/tests.py
+++ b/talerbank/app/tests.py
@@ -31,17 +31,23 @@ from django.urls import reverse
 from django.conf import settings
 from django.contrib.auth.models import User
 from mock import patch, MagicMock
-from .models import BankAccount, BankTransaction, \
-    BankAccountDoesNotExist, BankTransactionDoesNotExist
+from .models import BankAccount, BankTransaction
 from . import urls
 from .views import wire_transfer
-from taler.util.amount import Amount, CurrencyMismatch, BadFormatAmount
+from taler.util.amount import (
+    Amount,
+    SignedAmount,
+    CurrencyMismatchError,
+    AmountFormatError,
+)
 
 LOGGER = logging.getLogger()
-LOGGER.setLevel(logging.INFO)
+LOGGER.setLevel(logging.DEBUG)
 
-logging.disable(logging.CRITICAL)
+# logging.disable(logging.CRITICAL)
 # reenable: logging.disable(logging.NOTSET)
+logging.disable(logging.NOTSET)
+
 
 def make_auth_line(username, password):
     credentials = "%s:%s" % (username, password)
@@ -49,14 +55,13 @@ def make_auth_line(username, password):
     header_line = "Basic %s" % b64enc.decode()
     return header_line
 
+
 def clear_db():
     User.objects.all().delete()
     BankAccount.objects.all().delete()
     BankTransaction.objects.all().delete()
     with connection.cursor() as cursor:
-        cursor.execute(
-            "ALTER SEQUENCE app_bankaccount_account_no_seq" \
-            " RESTART")
+        cursor.execute("ALTER SEQUENCE app_bankaccount_account_no_seq" " 
RESTART")
         cursor.execute("ALTER SEQUENCE app_banktransaction_id_seq RESTART")
 
 
@@ -64,13 +69,9 @@ def clear_db():
 # to some endpoint that needs to authenticate the
 # user.
 class MalformedLoginTestCase(TestCase):
-
     def test_malformed_login(self):
-        self.client.generic(
-            "POST",
-            reverse("add-incoming", urlconf=urls),
-            "malformed"
-        )
+        self.client.generic("POST", reverse("add-incoming", urlconf=urls), 
"malformed")
+
 
 class PublicAccountsTestCase(TestCase):
     def setUp(self):
@@ -96,22 +97,20 @@ class WithdrawTestCase(TestCase):
             user=User.objects.create_user(
                 username="test_user", password="test_password"
             ),
-            account_no=100
+            account_no=100,
         )
         self.user_bank_account.save()
 
         self.exchange_bank_account = BankAccount(
-            user=User.objects.create_user(
-                username="test_exchange", password=""
-            ),
-            account_no=99
+            user=User.objects.create_user(username="test_exchange", 
password=""),
+            account_no=99,
         )
         self.exchange_bank_account.save()
         self.client = Client()
 
-    @patch('talerbank.app.views.wire_transfer')
-    @patch('hashlib.new')
-    @patch('time.time')
+    @patch("talerbank.app.views.wire_transfer")
+    @patch("hashlib.new")
+    @patch("time.time")
     @unittest.skip("skip outdated test case")
     def test_withdraw(self, mocked_time, mocked_hashlib, mocked_wire_transfer):
         amount = Amount(settings.TALER_CURRENCY, 0, 1)
@@ -121,13 +120,11 @@ class WithdrawTestCase(TestCase):
             "amount_currency": amount.currency,
             "reserve_pub": "UVZ789",
             "exchange": "https://exchange.example.com/";,
-            "exchange_wire_details": "payto://x-taler-bank/bank.example/99"
+            "exchange_wire_details": "payto://x-taler-bank/bank.example/99",
         }
         self.client.login(username="test_user", password="test_password")
 
-        response = self.client.get(
-            reverse("pin-question", urlconf=urls), params
-        )
+        response = self.client.get(reverse("pin-question", urlconf=urls), 
params)
         self.assertEqual(response.status_code, 200)
         # We mock hashlib in order to fake the CAPTCHA.
         hasher = MagicMock()
@@ -143,10 +140,11 @@ class WithdrawTestCase(TestCase):
         args, kwargs = mocked_wire_transfer.call_args
         del kwargs
         self.assertTrue(
-            args[0].dump() == amount.dump() \
-            and self.user_bank_account in args \
-            and "UVZ789" in args \
-            and self.exchange_bank_account in args)
+            args[0].dump() == amount.dump()
+            and self.user_bank_account in args
+            and "UVZ789" in args
+            and self.exchange_bank_account in args
+        )
 
     def tearDown(self):
         clear_db()
@@ -155,10 +153,10 @@ class WithdrawTestCase(TestCase):
 class InternalWireTransferTestCase(TestCase):
     def setUp(self):
         BankAccount(
-            user=User.objects.create_user(username='give_money', password="gm")
+            user=User.objects.create_user(username="give_money", password="gm")
         ).save()
         self.take_money = BankAccount(
-            user=User.objects.create_user(username='take_money'), account_no=4
+            user=User.objects.create_user(username="take_money"), account_no=4
         )
         self.take_money.save()
 
@@ -169,25 +167,25 @@ class InternalWireTransferTestCase(TestCase):
         client = Client()
         client.login(username="give_money", password="gm")
         response = client.post(
-            reverse("profile", urlconf=urls), {
+            reverse("profile", urlconf=urls),
+            {
                 "amount": 3.0,
                 "receiver": self.take_money.account_no,
-                "subject": "charity"
-            }
+                "subject": "charity",
+            },
         )
         take_money = BankAccount.objects.get(account_no=4)
-        self.assertEqual(
-            0,
-            Amount.cmp(Amount(settings.TALER_CURRENCY, 3), take_money.amount)
-        )
+        r = SignedAmount.parse(f"{settings.TALER_CURRENCY}:3.0")
+        self.assertEqual(take_money.balance, r)
         self.assertEqual(302, response.status_code)
 
 
 class RegisterTestCase(TestCase):
     """User registration"""
+
     def setUp(self):
         clear_db()
-        BankAccount(user=User.objects.create_user(username='Bank')).save()
+        BankAccount(user=User.objects.create_user(username="Bank")).save()
 
     def tearDown(self):
         clear_db()
@@ -195,11 +193,9 @@ class RegisterTestCase(TestCase):
     def test_register(self):
         client = Client()
         response = client.post(
-            reverse("register", urlconf=urls), {
-                "username": "test_register",
-                "password": "test_register"
-            },
-            follow=True
+            reverse("register", urlconf=urls),
+            {"username": "test_register", "password": "test_register"},
+            follow=True,
         )
         self.assertIn(("/profile", 302), response.redirect_chain)
         # this assertion tests "/profile""s view
@@ -210,10 +206,8 @@ class RegisterTestCase(TestCase):
 
         # Normal case.
         response = client.post(
-            reverse("register-headless", urlconf=urls), {
-                "username": "test_register_headless",
-                "password": "password*+#@"
-            }
+            reverse("register-headless", urlconf=urls),
+            {"username": "test_register_headless", "password": "password*+#@"},
         )
         self.assertEqual(200, response.status_code)
 
@@ -226,29 +220,27 @@ class RegisterTestCase(TestCase):
 
         # Try registering unavailable username.
         response = client.post(
-            reverse("register-headless", urlconf=urls), {
-                "username": "test_register_headless",
-                "password": "password"
-            }
+            reverse("register-headless", urlconf=urls),
+            {"username": "test_register_headless", "password": "password"},
         )
         self.assertEqual(409, response.status_code)
 
         # NOTE: Django 2.2.2 allows ANY character!  Is this normal?
         response = client.post(
-            reverse("register-headless", urlconf=urls), {
-                "username": "'''+++;;;'''",
-                "password": "password2"
-            }
+            reverse("register-headless", urlconf=urls),
+            {"username": "'''+++;;;'''", "password": "password2"},
         )
         self.assertEqual(200, response.status_code)
 
 
 class LoginTestCase(TestCase):
     """User login"""
+
     def setUp(self):
         BankAccount(
-            user=User.objects.
-            create_user(username="test_user", password="test_password")
+            user=User.objects.create_user(
+                username="test_user", password="test_password"
+            )
         ).save()
         self.client = Client()
 
@@ -265,178 +257,112 @@ class LoginTestCase(TestCase):
 
     def test_failing_login(self):
         response = self.client.get(
-            reverse("history", urlconf=urls), {"auth": "basic"}, **{
-                "HTTP_AUTHORIZATION": make_auth_line("Wrong", "Credentials")
-            }
+            reverse("history", urlconf=urls),
+            {"auth": "basic"},
+            HTTP_AUTHORIZATION=make_auth_line("Wrong", "Credentials"),
         )
         data = response.content.decode("utf-8")
         self.assertEqual(401, response.status_code)
 
 
-class AmountTestCase(TestCase):
-    def test_cmp(self):
-        amount1 = Amount("X", 1)
-        _amount1 = Amount("X", 1)
-        amount2 = Amount("X", 2)
-
-        self.assertEqual(-1, Amount.cmp(amount1, amount2))
-        self.assertEqual(1, Amount.cmp(amount2, amount1))
-        self.assertEqual(0, Amount.cmp(amount1, _amount1))
-
-    # Trying to compare amount of different currencies
-    def test_cmp_diff_curr(self):
-        amount1 = Amount("X", 1)
-        amount2 = Amount("Y", 2)
-        with self.assertRaises(CurrencyMismatch):
-            Amount.cmp(amount1, amount2)
-
-
-class RejectTestCase(TestCase):
-    def setUp(self):
-        BankAccount(
-            user=User.objects.
-            create_user(username="rejected_user", password="rejected_password")
-        ).save()
-        BankAccount(
-            user=User.objects.create_user(
-                username="rejecting_user", password="rejecting_password"
-            )
-        ).save()
-
-    def tearDown(self):
-        clear_db()
-
-    def test_reject(self):
-        client = Client()
-        rejecting = User.objects.get(username="rejecting_user")
-        data = '{"auth": {"type": "basic"}, \
-                 "credit_account": %d, \
-                 "subject": "TESTWTID", \
-                 "exchange_url": "https://exchange.test";, \
-                 "amount": "%s:5.0"}' \
-               % (rejecting.bankaccount.account_no,
-                  settings.TALER_CURRENCY)
-        response = client.post(
-            reverse("add-incoming", urlconf=urls),
-            data=data,
-            content_type="application/json",
-            follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("rejected_user", 
"rejected_password"),
-            }
-        )
-        self.assertEqual(response.status_code, 200)
-        data = response.content.decode("utf-8")
-        jdata = json.loads(data)
-        rejected = User.objects.get(username="rejected_user")
-        response = client.put(
-            reverse("reject", urlconf=urls),
-            data='{"row_id": %d, \
-                   "auth": {"type": "basic"}, \
-                   "account_number": %d}' \
-                  % (jdata["row_id"],
-                     rejected.bankaccount.account_no),
-            content_type="application/json",
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("rejecting_user", 
"rejecting_password"),
-            })
-        self.assertEqual(response.status_code, 204)
-
-
 class WithdrawHeadlessTestCase(TestCase):
     def setUp(self):
         BankAccount(
             user=User.objects.create_user(
                 username="headless_wallet", password="headless_password"
             ),
-            amount=Amount(settings.TALER_CURRENCY, 10)
+            balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 10, 0)),
         ).save()
         # Gets account #2, in line with config.
         BankAccount(
             user=User.objects.create_user(
                 username="normal_exchange", password="normal_password"
             ),
-            account_no=2
+            account_no=2,
         ).save()
 
     def test_withdraw_headless(self):
         client = Client()
 
         # Use default exchange.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "reserve_pub": "RESERVEPUB", \
-                 "amount": "%s:10"}' % settings.TALER_CURRENCY
+                 "amount": "%s:10"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("withdraw-headless", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("headless_wallet", 
"headless_password")
-
-            }
+            HTTP_AUTHORIZATION=make_auth_line("headless_wallet", 
"headless_password"),
         )
         self.assertEqual(200, response.status_code)
 
         # Try withdrawing more than owning.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "reserve_pub": "RESERVEPUB", \
-                 "amount": "%s:100"}' % settings.TALER_CURRENCY
+                 "amount": "%s:100"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("withdraw-headless", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("headless_wallet", 
"headless_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("headless_wallet", 
"headless_password"),
         )
         self.assertEqual(406, response.status_code)
 
         # Try withdrawing giving exchange field.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "exchange_wire_details": 
"payto://x-taler-bank/bank.example.com/2", \
                  "reserve_pub": "RESERVEPUB", \
-                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+                 "amount": "%s:0.4"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("withdraw-headless", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("headless_wallet", 
"headless_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("headless_wallet", 
"headless_password"),
         )
         self.assertEqual(200, response.status_code)
 
         # Try withdrawing giving non-existent recipient.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "exchange_wire_details": 
"payto://x-taler-bank/bank.example.com/2222", \
                  "reserve_pub": "RESERVEPUB", \
-                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+                 "amount": "%s:0.4"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("withdraw-headless", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("headless_wallet", 
"headless_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("headless_wallet", 
"headless_password"),
         )
         self.assertEqual(404, response.status_code)
 
         # Try withdrawing giving invalid JSON.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "XXX": "YYY", \
-                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+                 "amount": "%s:0.4"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("withdraw-headless", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("headless_wallet", 
"headless_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("headless_wallet", 
"headless_password"),
         )
         self.assertEqual(400, response.status_code)
 
@@ -446,14 +372,17 @@ class WithdrawHeadlessTestCase(TestCase):
 
 class AddIncomingTestCase(TestCase):
     """Test money transfer's API"""
+
     def setUp(self):
         BankAccount(
-            user=User.objects.
-            create_user(username="bank_user", password="bank_password")
+            user=User.objects.create_user(
+                username="bank_user", password="bank_password"
+            )
         ).save()
         BankAccount(
-            user=User.objects.
-            create_user(username="user_user", password="user_password")
+            user=User.objects.create_user(
+                username="user_user", password="user_password"
+            )
         ).save()
 
     def tearDown(self):
@@ -461,20 +390,20 @@ class AddIncomingTestCase(TestCase):
 
     def test_add_incoming(self):
         client = Client()
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "credit_account": 1, \
                  "subject": "TESTWTID", \
                  "exchange_url": "https://exchange.test";, \
-                 "amount": "%s:1.0"}' \
-               % settings.TALER_CURRENCY
+                 "amount": "%s:1.0"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("add-incoming", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("user_user", 
"user_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
         )
         self.assertEqual(200, response.status_code)
 
@@ -485,10 +414,8 @@ class AddIncomingTestCase(TestCase):
             data=zdata,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("user_user", 
"user_password"),
-                "HTTP_CONTENT_ENCODING": "deflate"
-            }
+            HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
+            HTTP_CONTENT_ENCODING="deflate",
         )
         self.assertEqual(200, response.status_code)
 
@@ -503,360 +430,181 @@ class AddIncomingTestCase(TestCase):
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("user_user", 
"user_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
         )
         # note: a bad currency request gets 400.
-        self.assertRaises(CurrencyMismatch)
-        self.assertEqual(406, response.status_code)
+        self.assertEqual(400, response.status_code)
         LOGGER.info(response.content.decode("utf-8"))
 
         # Try to go debit
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "credit_account": 1, \
                  "subject": "TESTWTID", \
                  "exchange_url": "https://exchange.test";, \
-                 "amount": "%s:50.1"}' % settings.TALER_CURRENCY
+                 "amount": "%s:50.1"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("add-incoming", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                 "HTTP_AUTHORIZATION": make_auth_line("user_user", 
"user_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
         )
         self.assertEqual(406, response.status_code)
         # Try use a non-existent recipient.
-        data = '{"auth": {"type": "basic"}, \
+        data = (
+            '{"auth": {"type": "basic"}, \
                  "credit_account": 1987, \
                  "subject": "TESTWTID", \
                  "exchange_url": "https://exchange.test";, \
-                 "amount": "%s:1"}' % settings.TALER_CURRENCY
+                 "amount": "%s:1"}'
+            % settings.TALER_CURRENCY
+        )
         response = client.post(
             reverse("add-incoming", urlconf=urls),
             data=data,
             content_type="application/json",
             follow=True,
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("user_user", 
"user_password")
-            }
+            HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
         )
         self.assertEqual(404, response.status_code)
 
 
-class HistoryContext:
-    def __init__(self, expected_resp, **kwargs):
-        self.expected_resp = expected_resp
-        self.urlargs = kwargs
-        self.urlargs.update({"auth": "basic"})
-
-    def dump(self):
-        return self.urlargs
-
 class CustomDoesNotExistTestCase(TestCase):
-
     def test_bankaccount_doesnotexist(self):
-        with self.assertRaises(BankAccountDoesNotExist):
+        with self.assertRaises(BankAccount.DoesNotExist):
             BankAccount.objects.get(account_no=1000)
-        with self.assertRaises(BankTransactionDoesNotExist):
+        with self.assertRaises(BankTransaction.DoesNotExist):
             BankTransaction.objects.get(subject="1000")
 
+
 class HistoryTestCase(TestCase):
     def setUp(self):
         clear_db()
         debit_account = BankAccount(
-            user=User.objects.create_user(username='User', 
password="Password"),
-            amount=Amount(settings.TALER_CURRENCY, 100)
+            user=User.objects.create_user(username="User", 
password="Password"),
+            balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 
0)),
         )
         debit_account.save()
         credit_account = BankAccount(
-            user=User.objects.
-            create_user(username='User0', password="Password0")
+            user=User.objects.create_user(username="User0", 
password="Password0")
         )
         credit_account.save()
         for subject in ("a", "b", "c", "d", "e", "f", "g", "h", "i"):
             wire_transfer(
-                Amount(settings.TALER_CURRENCY, 1), debit_account,
-                credit_account, subject
+                Amount(settings.TALER_CURRENCY, 1, 0),
+                debit_account,
+                credit_account,
+                subject,
             )
-        # reject transaction 'i'.
-        trans_i = BankTransaction.objects.get(subject="i")
-        self.client = Client()
-        self.client.post(
-            reverse("reject", urlconf=urls),
-            data='{"auth": {"type": "basic"}, \
-                   "row_id": %d, \
-                   "account_number": 44}' % trans_i.id,  # Ignored
-            content_type="application/json",
-            follow=True,
-            **{
-                 "HTTP_AUTHORIZATION": make_auth_line("User0", "Password0")
-            }
-        )
 
     def tearDown(self):
         clear_db()
 
-    def assert_result(self, response, ctx):
-
-        data = response.content.decode("utf-8")
-        try:
-            # FIXME, not always data is found this way.
-            data = json.loads(data)["data"][0]
-        except (json.JSONDecodeError, KeyError):
-            data = {}
-        self.assertEqual(
-            ctx.expected_resp.get("status"),
-            response.status_code,
-            "Failing request: %s?%s => raw body: %s" % \
-                (response.request["PATH_INFO"],
-                 unquote(response.request["QUERY_STRING"]),
-                 response.content.decode("utf-8")))
-
-        # extract expected data from response
-        expected_data = {}
-        response_data = {}
-        for key, value in ctx.expected_resp.get("fields", []):
-            response_data.update({key: data.get(key)})
-            expected_data.update({key: value})
-
-        self.assertEqual(expected_data, response_data)
-
-    def test_history_range(self):
-        now = int(time.time())
-
-        for ctx in (
-
-            # Expect empty results, range too ancient.
-            HistoryContext(
-                expected_resp={"status": 204}, start=1, end=2, direction="both"
-            ),
-
-            # Expect empty results, range too ahead.
-            HistoryContext(
-                expected_resp={"status": 200},
-                start=now + 40,
-                end=now + 50,
-                direction="both"
-            ),
-
-            # Expect non empty results.
-            HistoryContext(
-                expected_resp={"status": 200},
-                start=now - 30,
-                end=now + 30,
-                direction="both"
-            )
-        ):
-
-            response = self.client.get(
-                reverse("history-range", urlconf=urls), ctx.urlargs, **{
-                    "HTTP_AUTHORIZATION": make_auth_line("User", "Password")
-                }
-            )
-
-        self.assert_result(response, ctx)
-
     def test_history(self):
-        for ctx in (
-            HistoryContext(
-                expected_resp={"status": 200}, delta="-4", direction="both"
-            ),
-            HistoryContext(
-                expected_resp={
-                    "fields": [("row_id", 9)],
-                    "status": 200
-                },
-                delta="+1",
-                start="5",
-                direction="both"
-            ),
-            HistoryContext(
-                expected_resp={
-                    "fields": [("wt_subject", "c")],
-                    "status": 200
-                },
-                delta="1",
-                start=2,
-                direction="both",
-                ordering="ascending"
-            ),
-            HistoryContext(
-                expected_resp={
-                    "fields": [("wt_subject", "a")],
-                    "status": 200
-                },
-                delta="-1",
-                start=2,
-                direction="both"
-            ),
-            HistoryContext(
-                expected_resp={"status": 204},
-                delta="1",
-                start="11",
-                direction="both"
-            ),
-            HistoryContext(
-                expected_resp={
-                    "status": 200,
-                    "fields": [("wt_subject", "i"), ("sign", "cancel-")]
-                },
-                start=8,
-                delta="+1",
-                direction="cancel-"
-            ),
-            HistoryContext(
-                expected_resp={"status": 204},
-                start=8,
-                delta="+1",
-                direction="cancel-",
-                cancelled="omit"
-            ),
-            HistoryContext(
-                expected_resp={"status": 204},
-                start=8,
-                delta="-1",
-                direction="cancel-"
-            ),
-            HistoryContext(
-                expected_resp={"status": 204}, delta="+1", direction="cancel+"
-            ),
-            HistoryContext(
-                expected_resp={"status": 200}, delta="-1", direction="debit"
-            )
-        ):
 
+        def histquery(**urlargs):
             response = self.client.get(
-                reverse("history", urlconf=urls), ctx.urlargs, **{
-                    "HTTP_AUTHORIZATION": make_auth_line("User", "Password")
-                }
+                reverse("history", urlconf=urls),
+                urlargs,
+                HTTP_AUTHORIZATION=make_auth_line("User", "Password"),
             )
-            self.assert_result(response, ctx)
-
-
-class DBAmountSubtraction(TestCase):
-    def setUp(self):
-        BankAccount(
-            user=User.objects.create_user(username='U'),
-            amount=Amount(settings.TALER_CURRENCY, 3)
-        ).save()
-
-    def tearDown(self):
-        clear_db()
+            return response
+
+        # test query #1
+        r = histquery(delta="-4", direction="both")
+        rd = json.loads(r.content)
+        self.assertEqual(r.status_code, 200)
+
+        # test query #2
+        r = histquery(delta="+1", start="5", direction="both")
+        self.assertEqual(r.status_code, 200)
+        rd = json.loads(r.content)
+        self.assertEqual(r.status_code, 200)
+        self.assertEqual(rd["data"][0]["row_id"], 6)
+
+        # test query #3
+        r = histquery(delta="+1", start="2", direction="both")
+        self.assertEqual(r.status_code, 200)
+        rd = json.loads(r.content)
+        self.assertEqual(rd["data"][0]["wt_subject"], "c")
+
+        # test query #4
+        r = histquery(delta="-1", start="2", direction="both")
+        self.assertEqual(r.status_code, 200)
+        rd = json.loads(r.content)
+        self.assertEqual(rd["data"][0]["wt_subject"], "a")
+
+        # test query #5
+        r = histquery(delta="1", start="11", direction="both")
+        self.assertEqual(r.status_code, 200)
+        rd = json.loads(r.content)
+        self.assertEqual(len(rd["data"]), 0)
 
-    def test_subtraction(self):
-        user_bankaccount = BankAccount.objects.get(
-            user=User.objects.get(username='U')
-        )
-        user_bankaccount.amount.subtract(Amount(settings.TALER_CURRENCY, 2))
-        self.assertEqual(
-            Amount.cmp(Amount(settings.TALER_CURRENCY, 1), 
user_bankaccount.amount), 0
-        )
 
 class DBCustomColumnTestCase(TestCase):
     def setUp(self):
-        BankAccount(user=User.objects.create_user(username='U')).save()
+        BankAccount(user=User.objects.create_user(username="U")).save()
 
     def tearDown(self):
         clear_db()
 
     def test_exists(self):
-        user_bankaccount = BankAccount.objects.get(
-            user=User.objects.get(username='U')
-        )
-        self.assertTrue(isinstance(user_bankaccount.amount, Amount))
+        user_bankaccount = 
BankAccount.objects.get(user=User.objects.get(username="U"))
+        self.assertTrue(isinstance(user_bankaccount.balance, SignedAmount))
 
 
 ## This tests whether a bank account goes debit and then goes >=0
 ## again
 class DebitTestCase(TestCase):
     def setUp(self):
-        BankAccount(user=User.objects.create_user(username='U')).save()
-        BankAccount(user=User.objects.create_user(username='U0')).save()
+        BankAccount(user=User.objects.create_user(username="U")).save()
+        BankAccount(user=User.objects.create_user(username="U0")).save()
 
     def tearDown(self):
         clear_db()
 
     def test_green(self):
-        user_bankaccount = BankAccount.objects.get(
-            user=User.objects.get(username='U')
-        )
-        self.assertEqual(False, user_bankaccount.debit)
+        user_bankaccount = 
BankAccount.objects.get(user=User.objects.get(username="U"))
+        self.assertTrue(user_bankaccount.balance.is_zero())
 
     def test_red(self):
-        user_bankaccount = BankAccount.objects.get(
-            user=User.objects.get(username='U')
-        )
+        user_bankaccount = 
BankAccount.objects.get(user=User.objects.get(username="U"))
         user_bankaccount0 = BankAccount.objects.get(
-            user=User.objects.get(username='U0')
+            user=User.objects.get(username="U0")
         )
 
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 10, 0), user_bankaccount0,
-            user_bankaccount, "Go green"
+            Amount(settings.TALER_CURRENCY, 10, 0),
+            user_bankaccount0,
+            user_bankaccount,
+            "Go green",
         )
-        tmp = Amount(settings.TALER_CURRENCY, 10)
-        self.assertEqual(0, Amount.cmp(user_bankaccount.amount, tmp))
-        self.assertEqual(0, Amount.cmp(user_bankaccount0.amount, tmp))
-        self.assertFalse(user_bankaccount.debit)
-
-        self.assertTrue(user_bankaccount0.debit)
 
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 11), user_bankaccount,
-            user_bankaccount0, "Go red"
+            Amount(settings.TALER_CURRENCY, 11, 0),
+            user_bankaccount,
+            user_bankaccount0,
+            "Go red",
         )
 
-        tmp.value = 1
-        self.assertTrue(user_bankaccount.debit)
-        self.assertFalse(user_bankaccount0.debit)
-        self.assertEqual(0, Amount.cmp(user_bankaccount.amount, tmp))
-        self.assertEqual(0, Amount.cmp(user_bankaccount0.amount, tmp))
+        amt_one = SignedAmount.parse(f"{settings.TALER_CURRENCY}:1")
 
-
-class ParseAmountTestCase(TestCase):
-    def test_parse_amount(self):
-        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()
-        )
-        ret = Amount.parse("KUDOS:4")
-        self.assertJSONEqual(
-            '{"value": 4, "fraction": 0, "currency": "KUDOS"}', ret.dump()
-        )
-        ret = Amount.parse("KUDOS:4.")  # forbid?
-        self.assertJSONEqual(
-            '{"value": 4, "fraction": 0, "currency": "KUDOS"}', ret.dump()
-        )
-        try:
-            Amount.parse("Buggy")
-        except BadFormatAmount as err:
-            return
-        # make sure the control doesn't get here
-        self.assertEqual(True, False)
+        self.assertEqual(user_bankaccount.balance, -amt_one)
+        self.assertEqual(user_bankaccount0.balance, amt_one)
 
 
 class MeasureHistory(TestCase):
     def setUp(self):
         self.user_bankaccount0 = BankAccount(
-            user=User.objects.create_user(username='U0'),
-            amount=Amount(settings.TALER_CURRENCY, 3000)
+            user=User.objects.create_user(username="U0"),
+            balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 3000, 
0)),
         )
         self.user_bankaccount0.save()
 
-        user_bankaccount = BankAccount(
-            user=User.objects.create_user(username='U')
-        )
+        user_bankaccount = 
BankAccount(user=User.objects.create_user(username="U"))
         user_bankaccount.save()
 
         self.ntransfers = 1000
@@ -866,8 +614,10 @@ class MeasureHistory(TestCase):
         for i in range(self.ntransfers):
             del i  # to pacify PEP checkers
             wire_transfer(
-                Amount(settings.TALER_CURRENCY, 1), self.user_bankaccount0,
-                user_bankaccount, "bulk"
+                Amount(settings.TALER_CURRENCY, 1, 0),
+                self.user_bankaccount0,
+                user_bankaccount,
+                "bulk",
             )
 
     def tearDown(self):
@@ -880,7 +630,7 @@ class MeasureHistory(TestCase):
         timer = timeit.Timer(
             stmt="extract_history(self.user_bankaccount0, False)",
             setup="from talerbank.app.views import extract_history",
-            globals=locals()
+            globals=locals(),
         )
         total_time = timer.timeit(number=1)
         allowed_time_per_record = 0.003
@@ -890,14 +640,14 @@ class MeasureHistory(TestCase):
 class BalanceTestCase(TestCase):
     def setUp(self):
         self.the_bank = BankAccount(
-            user=User.objects.create_user(username='U0', password='U0PASS'),
-            amount=Amount(settings.TALER_CURRENCY, 3)
+            user=User.objects.create_user(username="U0", password="U0PASS"),
+            balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 3, 0)),
         )
         self.the_bank.save()
 
         user = BankAccount(
-            user=User.objects.create_user(username='U'),
-            amount=Amount(settings.TALER_CURRENCY, 10)
+            user=User.objects.create_user(username="U"),
+            balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 10, 0)),
         )
         user.save()
 
@@ -905,27 +655,27 @@ class BalanceTestCase(TestCase):
 
         # bank: 2, user: 11
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 1), self.the_bank, user, "mock"
+            Amount(settings.TALER_CURRENCY, 1, 0), self.the_bank, user, "mock"
         )
 
         # bank: 4, user: 9
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 2), user, self.the_bank, "mock"
+            Amount(settings.TALER_CURRENCY, 2, 0), user, self.the_bank, "mock"
         )
 
         # bank: -1, user: 14
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 5), self.the_bank, user, "mock"
+            Amount(settings.TALER_CURRENCY, 5, 0), self.the_bank, user, "mock"
         )
 
         # bank: 7, user: 6 (END)
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 8), user, self.the_bank, "mock"
+            Amount(settings.TALER_CURRENCY, 8, 0), user, self.the_bank, "mock"
         )
 
         # bank: -3, user: 16 (END)
         wire_transfer(
-            Amount(settings.TALER_CURRENCY, 10), user, self.the_bank, "mock"
+            Amount(settings.TALER_CURRENCY, 10, 0), self.the_bank, user, "mock"
         )
 
         self.client = Client()
@@ -937,44 +687,23 @@ class BalanceTestCase(TestCase):
         self.client.login(username="U0", password="U0PASS")
         response = self.client.get(
             reverse("history", urlconf=urls),
-            {
-                "auth": "basic",
-                "delta": -30,
-                "direction": "both",
-                "account_number": 55
-            },  # unused
-            **{
-                "HTTP_AUTHORIZATION": make_auth_line("U0", "U0PASS")
-            }
+            {"delta": -30, "direction": "both", "account_number": 55},
+            HTTP_AUTHORIZATION=make_auth_line("U0", "U0PASS"),
         )
         data = response.content.decode("utf-8")
         self.assertEqual(response.status_code, 200)
         entries = json.loads(data)
 
-        acc_in = Amount(settings.TALER_CURRENCY)
-        acc_out = Amount(settings.TALER_CURRENCY)
+        acc_bal = SignedAmount(True, Amount(settings.TALER_CURRENCY, 10, 0))
+        print("acc_bal start", acc_bal)
 
-        for entry in entries["data"]:
-            if entry["sign"] == "+":
-                acc_in.add(Amount(**entry["amount"]))
+        for entry in reversed(entries["data"]):
+            print("entry", entry)
             if entry["sign"] == "-":
-                acc_out.add(Amount(**entry["amount"]))
-
-        expected_amount = Amount(settings.TALER_CURRENCY, 3)
-        try:
-            debit = False
-            acc_in.subtract(acc_out)
-            expected_amount.add(acc_in)
-        except ValueError:
-            # "out" is bigger than "in"
-            LOGGER.info("out > in")
-            acc_out.subtract(acc_in)
-            try:
-                expected_amount.subtract(acc_out)
-            except ValueError:
-                # initial amount wasn't enough to cover expenses
-                debit = True
-                acc_out.subtract(expected_amount)
-                expected_amount = acc_out
-
-        self.assertEqual(Amount.cmp(expected_amount, self.the_bank.amount), 0)
+                acc_bal += SignedAmount.parse(entry["amount"])
+            if entry["sign"] == "+":
+                acc_bal -= SignedAmount.parse(entry["amount"])
+            print("acc_bal after", acc_bal)
+
+        expected_amount = SignedAmount.parse(f"{settings.TALER_CURRENCY}:16.0")
+        self.assertEqual(acc_bal, expected_amount)
diff --git a/talerbank/app/urls.py b/talerbank/app/urls.py
index 28230ed..cabdc22 100644
--- a/talerbank/app/urls.py
+++ b/talerbank/app/urls.py
@@ -31,54 +31,45 @@ urlpatterns = [
         "login/",
         auth_views.LoginView.as_view(
             template_name="login.html",
-            authentication_form=views.TalerAuthenticationForm
+            authentication_form=views.TalerAuthenticationForm,
         ),
-        name="login"
+        name="login",
     ),
     path("logout/", views.logout_view, name="logout"),
     path("accounts/register", views.register, name="register"),
     path("profile", views.profile_page, name="profile"),
     path("history", views.serve_history, name="history"),
     path("history-range", views.serve_history_range, name="history-range"),
-    path("reject", views.reject, name="reject"),
     path(
         "api/withdraw-operation/<str:withdraw_id>",
         views.api_withdraw_operation,
-        name="api-withdraw-operation"
-    ),
-    path(
-        "api/withdraw-headless",
-        views.withdraw_headless,
-        name="withdraw-headless"
+        name="api-withdraw-operation",
     ),
+    path("api/withdraw-headless", views.withdraw_headless, 
name="withdraw-headless"),
     path(
         "api/withdraw-headless-uri",
         views.withdraw_headless_uri,
-        name="withdraw-headless-uri"
+        name="withdraw-headless-uri",
     ),
     path("api/register", views.register_headless, name="register-headless"),
     path("start-withdrawal", views.start_withdrawal, name="start-withdrawal"),
     path(
-        "show-withdrawal/<str:withdraw_id>",
-        views.show_withdrawal,
-        name="withdraw-show"
+        "show-withdrawal/<str:withdraw_id>", views.show_withdrawal, 
name="withdraw-show"
     ),
     path(
         "confirm-withdrawal/<str:withdraw_id>",
         views.confirm_withdrawal,
-        name="withdraw-confirm"
-    ),
-    path(
-        "public-accounts", views.serve_public_accounts, name="public-accounts"
+        name="withdraw-confirm",
     ),
+    path("public-accounts", views.serve_public_accounts, 
name="public-accounts"),
     path(
         "public-accounts/<str:name>",
         views.serve_public_accounts,
-        name="public-accounts"
+        name="public-accounts",
     ),
     path(
         "public-accounts/<str:name>/<int:page>",
         views.serve_public_accounts,
-        name="public-accounts"
+        name="public-accounts",
     ),
 ]
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index a6902be..3df8505 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -45,7 +45,7 @@ from django.shortcuts import render, redirect
 from django.core.exceptions import ObjectDoesNotExist
 from datetime import datetime
 from .models import BankAccount, BankTransaction, TalerWithdrawOperation
-from taler.util.amount import Amount
+from taler.util.amount import Amount, SignedAmount
 import qrcode
 import qrcode.image.svg
 import lxml
@@ -53,7 +53,6 @@ from .schemas import (
     HistoryParams,
     HistoryRangeParams,
     URLParamValidationError,
-    RejectData,
     AddIncomingData,
     JSONFieldException,
     InvalidSession,
@@ -129,18 +128,6 @@ class SameAccountException(Exception):
         self.taler_error_code = 5102
 
 
-##
-# Exception raised when someone tries to reject a
-# transaction, but they have no rights to accomplish
-# such operation.
-class RejectNoRightsException(Exception):
-    def __init__(self, msg):
-        super(RejectNoRightsException, self).__init__(msg)
-        self.hint = "Only original payer can reject."
-        self.http_status_code = 403
-        self.taler_error_code = 5200
-
-
 class UnhandledException(Exception):
     def __init__(self, msg="Unhandled exception happened!"):
         super(UnhandledException, self).__init__(msg)
@@ -278,11 +265,12 @@ class InputDatalist(forms.TextInput):
         return html + datalist
 
 
-##
-# Form for sending wire transfers.  It usually appears in the
-# user profile page.
-#
 class WTForm(forms.Form):
+    """
+    Form for sending wire transfers.  It usually appears in the
+    user profile page.
+    """
+
     amount = forms.FloatField(
         min_value=0.1, widget=forms.NumberInput(attrs={"class": 
"currency-input"})
     )
@@ -333,13 +321,12 @@ def profile_page(request):
     is_success, is_failure, hint = get_session_hint(request, "profile_hint")
     context = dict(
         name=request.user.username,
-        balance=request.user.bankaccount.amount,
-        sign="-" if request.user.bankaccount.debit else "",
+        balance=request.user.bankaccount.balance,
         fail_message=is_failure,
         success_message=is_success,
         hint=hint,
         precision=settings.TALER_DIGITS,
-        currency=request.user.bankaccount.amount.currency,
+        currency=request.user.bankaccount.balance.amount.currency,
         account_no=request.user.bankaccount.account_no,
         wt_form=wtf,
         history=extract_history(request.user.bankaccount, -1 * (UINT64_MAX / 2 
/ 2)),
@@ -423,16 +410,6 @@ def internal_register(request):
         user_account.save()
     bank_internal_account = BankAccount.objects.get(account_no=1)
 
-    # Raise:
-    #
-    #  SameAccountException
-    #  DebitLimitException
-    #  CurrencyMismatch
-    #
-    #  Amount group:
-    #    BadFormatAmount
-    #    NumberTooBig
-    #    NegativeNumber
     wire_transfer(
         Amount(settings.TALER_CURRENCY, 100, 0),
         bank_internal_account,
@@ -443,15 +420,13 @@ def internal_register(request):
     return user
 
 
-##
-# This method serves the request for programmatically
-# registering a user.
-#
-# @param request Django-specific HTTP request object.
-# @return Django-specific HTTP response object.
 @require_POST
 @csrf_exempt
 def register_headless(request):
+    """
+    This method serves the request for programmatically
+    registering a user.
+    """
 
     try:
         user = internal_register(request)
@@ -465,15 +440,13 @@ def register_headless(request):
     return HttpResponse(status=200)
 
 
-##
-# This method serves the request for registering a user.
-# If successful, it redirects the user to their profile page;
-# otherwise it will show again the same form (currently, without
-# displaying _any_ error/warning message.)
-#
-# @param request Django-specific HTTP request object.
-# @return Django-specific HTTP response object.
 def register(request):
+    """
+    This method serves the request for registering a user.
+    If successful, it redirects the user to their profile page;
+    otherwise it will show again the same form (currently, without
+    displaying _any_ error/warning message.)
+    """
     if request.method != "POST":
         return render(request, "register.html")
 
@@ -535,7 +508,7 @@ def logout_view(request):
 # @return the history array.
 def extract_history(account, delta, start=None):
     history = []
-    qs = query_history(account, "both", delta, start, "descending")
+    qs = query_history(account, "both", delta, start)
     for item in qs:
         if item.credit_account == account:
             counterpart = item.debit_account
@@ -589,7 +562,6 @@ def serve_public_accounts(request, name=None, page=None):
         # and django/python is not allowing slicing with big numbers.
         UINT64_MAX / 2 / 2,
         0,
-        "descending",
     ).count()
     DELTA = 30
     # '//' operator is NO floating point.
@@ -708,7 +680,7 @@ def query_history_range(bank_account, direction, start, 
end, descending):
 # @param sign this value ("+"/"-") determines whether the history
 #        entries will be younger / older than @a start.
 # @param ordering "descending" or anything else (for "ascending").
-def query_history(bank_account, direction, delta, start, ordering):
+def query_history(bank_account, direction, delta, start):
     if start is None:
         if delta > 0:
             start = -1
@@ -722,7 +694,7 @@ def query_history(bank_account, direction, delta, start, 
ordering):
     qs = BankTransaction.objects.filter(
         direction_switch(bank_account, direction), sign_filter
     )
-    order = "-id" if "descending" == ordering else "id"
+    order = "id" if (delta > 0) else "-id"
     return qs.order_by(order)[: abs(delta)]
 
 
@@ -751,11 +723,11 @@ def build_history_response(qs, cancelled, user_account):
         history.append(
             dict(
                 counterpart=counterpart,
-                amount=entry.amount.dump(),
+                amount=entry.amount.stringify(),
                 sign=sign_,
                 wt_subject=entry.subject,
                 row_id=entry.id,
-                date=dict(t_ms=int(entry.date.timestamp())*1000)
+                date=dict(t_ms=int(entry.date.timestamp()) * 1000),
             )
         )
     return history
@@ -786,8 +758,6 @@ def serve_history_range(request, user_account):
 
     history = build_history_response(qs, args.get("cancelled", "show"), 
user_account)
 
-    if not history:
-        return HttpResponse(status=204)
     return JsonResponse(dict(data=history), status=200)
 
 
@@ -806,15 +776,13 @@ def serve_history(request, user_account):
         args.get("direction"),
         args.get("delta"),
         args.get("start", None),
-        args.get("ordering", "descending"),
     )
 
     history = build_history_response(qs, args.get("cancelled", "show"), 
user_account)
 
-    if not history:
-        return HttpResponse(status=204)
     return JsonResponse(dict(data=history), status=200)
 
+
 ##
 # Implements the HTTP basic auth schema.
 #
@@ -830,64 +798,13 @@ def basic_auth(request):
     if len(tokens) != 2:
         raise LoginFailed("invalid Authorization header")
 
-    # decode the base64 content. 
+    # decode the base64 content.
     if tokens[0] != "Basic":
         raise LoginFailed("Not supporting '%s' authorization method" % 
tokens[0])
 
     username, password = base64.b64decode(tokens[1]).decode("utf-8").split(":")
     return django.contrib.auth.authenticate(username=username, 
password=password)
 
-##
-# Serve a request of /reject (for rejecting wire transfers).
-#
-# @param request Django-specific HTTP request object.
-# @param user_account the account that is going to reject the
-#        transfer.  Used to check whether they have this right
-#        or not (only accounts which _got_ payed can cancel the
-#        transaction.)
-@transaction.atomic
-@csrf_exempt
-@require_http_methods(["PUT", "POST"])
-@login_via_headers
-def reject(request, user_account):
-
-    data = RejectData(json.loads(decode_body(request)))
-
-    trans = BankTransaction.objects.get(id=data.get("row_id"))
-    if trans.credit_account.account_no != user_account.bankaccount.account_no:
-        raise RejectNoRightsException()
-    trans.cancelled = True
-    if trans.debit_account.debit:
-        # balance is negative
-        if 1 > Amount.cmp(trans.debit_account.amount, trans.amount):
-            # debit_account.amount <= trans.amount
-            trans.debit_account.debit = False
-            tmp = Amount(**trans.amount.dump())
-            tmp.subtract(trans.debit_account.amount)
-            trans.debit_account.amount.set(**tmp.dump())
-        else:
-            # debit_account > trans.amount
-            trans.debit_account.amount.subtract(trans.amount)
-    else:
-        # balance is positive, simply add
-        trans.debit_account.amount.add(trans.amount)
-    if trans.credit_account.debit:
-        # credit account balance is already negative
-        trans.credit_account.amount.add(trans.amount)
-    else:
-        if -1 == Amount.cmp(trans.credit_account.amount, trans.amount):
-            # credit_account.amount < trans.amount
-            trans.credit_account.debit = True
-            tmp = Amount(**trans.amount.dump())
-            tmp.subtract(trans.credit_account.amount)
-            trans.credit_account.amount.set(**tmp.dump())
-        else:
-            # credit_account.amount >= trans.amount
-            trans.credit_account.amount.subtract(trans.amount)
-
-    trans.save()
-    return HttpResponse(status=204)
-
 
 ##
 # Serve a request to make a wire transfer.  Allows fintech
@@ -906,17 +823,40 @@ def add_incoming(request, user_account):
 
     subject = "%s %s" % (data.get("subject"), data.get("exchange_url"))
 
-    credit_account = 
BankAccount.objects.get(account_no=data.get("credit_account"))
+    try:
+        credit_account = 
BankAccount.objects.get(account_no=data.get("credit_account"))
+    except BankAccount.DoesNotExist:
+        return JsonResponse(
+            {
+                "error": "Bank account not found"
+            },
+            status=404,
+        )
+
+
+    amount = Amount.parse(data.get("amount"))
+
+    if amount.currency != settings.TALER_CURRENCY:
+        return JsonResponse(
+            {
+                "error": "Incorrect currency"
+            },
+            status=400,
+        )
+
 
     wtrans = wire_transfer(
-        Amount.parse(data.get("amount")),
+        amount,
         user_account.bankaccount,
         credit_account,
         subject,
     )
 
     return JsonResponse(
-        {"row_id": wtrans.id, "timestamp": 
dict(t_ms=(int(wtrans.date.timestamp())*1000))}
+        {
+            "row_id": wtrans.id,
+            "timestamp": dict(t_ms=(int(wtrans.date.timestamp()) * 1000)),
+        }
     )
 
 
@@ -927,10 +867,10 @@ def withdraw_headless_uri(request, user):
     data = WithdrawHeadlessUri(json.loads(decode_body(request)))
     amount = Amount.parse(data.get("amount"))
     user_account = BankAccount.objects.get(user=user)
-    debt_threshold = Amount.parse(settings.TALER_MAX_DEBT)
-    if not check_transfer_allowed(
-        user_account.amount, user_account.debit, debt_threshold, amount
-    ):
+    withdraw_amount = SignedAmount(True, amount)
+    debt_threshold = SignedAmount.parse(settings.TALER_MAX_DEBT)
+    user_balance = SignedAmount(not user_account.debit, user_account.amount)
+    if user_balance - amount < -debt_threshold:
         raise DebitLimitException(
             f"Aborting payment initiated by '{user_account.user.username}', 
debit limit crossed."
         )
@@ -941,15 +881,13 @@ def withdraw_headless_uri(request, user):
     return JsonResponse({"taler_withdraw_uri": taler_withdraw_uri,})
 
 
-##
-# Serves a headless withdrawal request for the Taler protocol.
-#
-# @param request Django-specific HTTP request.
-# @return Django-specific HTTP response object.
 @login_via_headers
 @csrf_exempt
 @require_POST
 def withdraw_headless(request, user):
+    """
+    Serves a headless withdrawal request for the Taler protocol.
+    """
 
     data = WithdrawHeadless(json.loads(decode_body(request)))
     sender_payto = "payto://x-taler-bank/%s/%d" % (
@@ -965,7 +903,11 @@ def withdraw_headless(request, user):
     else:
         exchange_accno = get_acct_from_payto(exchange_payto)
 
-    exchange_bankaccount = BankAccount.objects.get(account_no=exchange_accno)
+    try:
+        exchange_bankaccount = 
BankAccount.objects.get(account_no=exchange_accno)
+    except ObjectDoesNotExist:
+        err = dict(hint="Bank account not found")
+        return JsonResponse(err, status=404)
 
     wire_transfer(
         Amount.parse(data.get("amount")),
@@ -977,10 +919,12 @@ def withdraw_headless(request, user):
     return JsonResponse(ret_obj)
 
 
-# Endpoint used by the browser and wallet to check withdraw status and
-# put in the exchange info.
 @csrf_exempt
 def api_withdraw_operation(request, withdraw_id):
+    """
+    Endpoint used by the browser and wallet to check withdraw status and
+    put in the exchange info.
+    """
     try:
         op = TalerWithdrawOperation.objects.get(withdraw_id=withdraw_id)
     except ObjectDoesNotExist:
@@ -1038,32 +982,20 @@ def api_withdraw_operation(request, withdraw_id):
         return JsonResponse(dict(error="only GET and POST are allowed"), 
status=305)
 
 
-def check_transfer_allowed(balance, balance_is_debit, debt_limit, 
transfer_amount):
-    if balance_is_debit:
-        total_debt = Amount(**transfer_amount.dump())
-        total_debt.add(balance)
-        return Amount.cmp(total_debt, debt_limit) <= 0
-    max_transfer = Amount(**balance.dump())
-    max_transfer.add(debt_limit)
-    return Amount.cmp(transfer_amount, max_transfer) <= 0
-
-
-##
-# Serve a Taler withdrawal request; takes the amount chosen
-# by the user, and builds a response to trigger the wallet into
-# the withdrawal protocol
-#
-# @param request Django-specific HTTP request.
-# @return Django-specific HTTP response object.
 @login_required
 @require_POST
 def start_withdrawal(request):
+    """
+    Serve a Taler withdrawal request; takes the amount chosen
+    by the user, and builds a response to trigger the wallet into
+    the withdrawal protocol
+    """
     user_account = BankAccount.objects.get(user=request.user)
     amount = Amount.parse(request.POST.get("kudos_amount", "not-given"))
-    debt_threshold = Amount.parse(settings.TALER_MAX_DEBT)
-    if not check_transfer_allowed(
-        user_account.amount, user_account.debit, debt_threshold, amount
-    ):
+    withdraw_amount = SignedAmount(True, amount)
+    debt_threshold = SignedAmount.parse(settings.TALER_MAX_DEBT)
+    user_balance = user_account.balance
+    if user_balance - withdraw_amount < -debt_threshold:
         raise DebitLimitException(
             f"Aborting payment initiated by '{user_account.user.username}', 
debit limit crossed."
         )
@@ -1147,17 +1079,12 @@ def confirm_withdrawal(request, withdraw_id):
     raise Exception("not reached")
 
 
-##
-# Make a wire transfer between two accounts (internal to the bank)
-#
-# @param amount (object type) how much money the wire transfer is worth.
-#        FIXME: a check about whether this value is zero is missing
-# @param debit_account the account that gives money.
-# @param credit_account the account that receives money.
-# @return a @a BankTransaction object.
 def wire_transfer(amount, debit_account, credit_account, subject):
+    """
+    Make a wire transfer between two accounts (internal to the bank)
+    """
     LOGGER.debug(
-        "%s => %s, %s, %s"
+        "transfering %s => %s, %s, %s"
         % (
             debit_account.account_no,
             credit_account.account_no,
@@ -1176,45 +1103,19 @@ def wire_transfer(amount, debit_account, 
credit_account, subject):
         subject=subject,
     )
 
-    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 Amount.cmp(amount, credit_account.amount) == 1:
-        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.
-
-    threshold = Amount.parse(settings.TALER_MAX_DEBT)
     if debit_account.user.username == "Bank":
-        threshold = Amount.parse(settings.TALER_MAX_DEBT_BANK)
-
-    LOGGER.info(
-        f"Account {debit_account.user.username} "
-        + f"(bal {debit_account.amount.stringify()}, thr 
{threshold.stringify()} requested transfer of"
-        + f"{threshold.stringify()} to account {credit_account.user.username}"
-    )
+        threshold = -SignedAmount.parse(settings.TALER_MAX_DEBT_BANK)
+    else:
+        threshold = -SignedAmount.parse(settings.TALER_MAX_DEBT)
 
-    if Amount.cmp(debit_account.amount, threshold) > 0 and debit_account.debit:
+    if debit_account.balance - SignedAmount(True, amount) < threshold:
         raise DebitLimitException(
             f"Aborting payment initiated by '{debit_account.user.username}', 
debit limit crossed."
         )
 
+    debit_account.balance -= SignedAmount(True, amount)
+    credit_account.balance += SignedAmount(True, amount)
+
     with transaction.atomic():
         debit_account.save()
         credit_account.save()
diff --git a/talerbank/jinja2.py b/talerbank/jinja2.py
index 7287e1b..4decadd 100644
--- a/talerbank/jinja2.py
+++ b/talerbank/jinja2.py
@@ -131,13 +131,15 @@ def amount_stringify(amount):
 
 def environment(**options):
     env = Environment(**options)
-    env.globals.update({
-        'static': static,
-        'url': url,
-        'settings_value': settings_value,
-        'env': env_get,
-        'is_valid_amount': is_valid_amount,
-        'amount_stringify': amount_stringify,
-        'tojson': tojson,
-    })
+    env.globals.update(
+        {
+            "static": static,
+            "url": url,
+            "settings_value": settings_value,
+            "env": env_get,
+            "is_valid_amount": is_valid_amount,
+            "amount_stringify": amount_stringify,
+            "tojson": tojson,
+        }
+    )
     return env
diff --git a/talerbank/settings.py b/talerbank/settings.py
index d50808b..6b39226 100644
--- a/talerbank/settings.py
+++ b/talerbank/settings.py
@@ -18,8 +18,7 @@ from taler.util.talerconfig import TalerConfig, 
ConfigurationError
 
 LOGGER = logging.getLogger(__name__)
 
-LOGGER.info("DJANGO_SETTINGS_MODULE: %s" \
-            % os.environ.get("DJANGO_SETTINGS_MODULE"))
+LOGGER.info("DJANGO_SETTINGS_MODULE: %s" % 
os.environ.get("DJANGO_SETTINGS_MODULE"))
 
 TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
 
@@ -38,7 +37,7 @@ if not SECRET_KEY:
         " TALER_BANK_SECRET_KEY env variable,"
         " generating random secret"
     )
-    SECRET_KEY = base64.b64encode(os.urandom(32)).decode('utf-8')
+    SECRET_KEY = base64.b64encode(os.urandom(32)).decode("utf-8")
 
 # SECURITY WARNING: don't run with debug turned on in production!
 
@@ -58,41 +57,43 @@ LOGIN_REDIRECT_URL = "index"
 # Application definition
 
 INSTALLED_APPS = [
-    'django.contrib.auth', 'django.contrib.contenttypes',
-    'django.contrib.sessions', 'django.contrib.messages',
-    'django.contrib.staticfiles', 'talerbank.app'
+    "django.contrib.auth",
+    "django.contrib.contenttypes",
+    "django.contrib.sessions",
+    "django.contrib.messages",
+    "django.contrib.staticfiles",
+    "talerbank.app",
 ]
 
 MIDDLEWARE = [
-    'django.middleware.security.SecurityMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'talerbank.app.middleware.ExceptionMiddleware',
-    'talerbank.app.middleware.DecompressionMiddleware'
+    "django.middleware.security.SecurityMiddleware",
+    "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.middleware.common.CommonMiddleware",
+    "django.middleware.csrf.CsrfViewMiddleware",
+    "django.contrib.auth.middleware.AuthenticationMiddleware",
+    "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.contrib.messages.middleware.MessageMiddleware",
+    "django.middleware.clickjacking.XFrameOptionsMiddleware",
+    "talerbank.app.middleware.ExceptionMiddleware",
+    "talerbank.app.middleware.DecompressionMiddleware",
 ]
 
-TEMPLATES = [{
-    'BACKEND':
-    'django.template.backends.jinja2.Jinja2',
-    'DIRS': [
-        os.path.join(BASE_DIR, "talerbank/app/static/web-common/"),
-        os.path.join(BASE_DIR, "talerbank/app/templates")
-    ],
-    'OPTIONS': {
-        'environment': 'talerbank.jinja2.environment'
+TEMPLATES = [
+    {
+        "BACKEND": "django.template.backends.jinja2.Jinja2",
+        "DIRS": [
+            os.path.join(BASE_DIR, "talerbank/app/static/web-common/"),
+            os.path.join(BASE_DIR, "talerbank/app/templates"),
+        ],
+        "OPTIONS": {"environment": "talerbank.jinja2.environment"},
     }
-}]
+]
 
 # Disable those, since they don't work with
 # jinja2 anyways.
 TEMPLATE_CONTEXT_PROCESSORS = []
 
-WSGI_APPLICATION = 'talerbank.wsgi.application'
+WSGI_APPLICATION = "talerbank.wsgi.application"
 
 # Database
 # https://docs.djangoproject.com/en/1.9/ref/settings/#databases
@@ -103,17 +104,15 @@ DBNAME = TC.value_string("bank", "database", 
required=True)
 DBNAME = os.environ.get("TALER_BANK_ALTDB", DBNAME)
 
 if not DBNAME:
-    raise Exception("DB not specified (neither in config or as" \
-                    "cli argument)")
+    raise Exception("DB not specified (neither in config or as" "cli 
argument)")
 
 LOGGER.info("dbname: %s" % DBNAME)
 
-CHECK_DBSTRING_FORMAT = re.search(
-    r"[a-z]+:///[a-z]+([\?][a-z]+=[a-z/]+)?", DBNAME
-)
+CHECK_DBSTRING_FORMAT = re.search(r"[a-z]+:///[a-z]+([\?][a-z]+=[a-z/]+)?", 
DBNAME)
 if not CHECK_DBSTRING_FORMAT:
-    LOGGER.error("Bad db string given '%s', respect the format" \
-                 "'dbtype:///dbname'" % DBNAME)
+    LOGGER.error(
+        "Bad db string given '%s', respect the format" "'dbtype:///dbname'" % 
DBNAME
+    )
     sys.exit(2)
 
 DBCONFIG = {}
@@ -124,7 +123,7 @@ if DB_URL.scheme not in ("postgres") or DB_URL.scheme == "":
     LOGGER.error("DB '%s' is not supported" % DB_URL.scheme)
     sys.exit(1)
 if DB_URL.scheme == "postgres":
-    DBCONFIG["ENGINE"] = 'django.db.backends.postgresql_psycopg2'
+    DBCONFIG["ENGINE"] = "django.db.backends.postgresql_psycopg2"
     DBCONFIG["NAME"] = DB_URL.path.lstrip("/")
 
 if not DB_URL.netloc:
@@ -145,21 +144,21 @@ DATABASES["default"] = DBCONFIG
 # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
 
 AUTH_PASSWORD_VALIDATORS = [
-    {'NAME': 'django.contrib.auth.password_validation' \
-             '.UserAttributeSimilarityValidator'},
-    {'NAME': 'django.contrib.auth.password_validation' \
-             '.MinimumLengthValidator'},
-    {'NAME': 'django.contrib.auth.password_validation' \
-             '.CommonPasswordValidator'},
-    {'NAME': 'django.contrib.auth.password_validation' \
-             '.NumericPasswordValidator'}]
+    {
+        "NAME": "django.contrib.auth.password_validation"
+        ".UserAttributeSimilarityValidator"
+    },
+    {"NAME": "django.contrib.auth.password_validation" 
".MinimumLengthValidator"},
+    {"NAME": "django.contrib.auth.password_validation" 
".CommonPasswordValidator"},
+    {"NAME": "django.contrib.auth.password_validation" 
".NumericPasswordValidator"},
+]
 
 # Internationalization
 # https://docs.djangoproject.com/en/1.9/topics/i18n/
 
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
 
 USE_I18N = True
 
@@ -173,7 +172,7 @@ USE_TZ = False
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/1.9/howto/static-files/
 
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
 
 STATICFILES_DIRS = [
     os.path.join(BASE_DIR, "talerbank/app/static"),
@@ -189,9 +188,7 @@ except ConfigurationError as exc:
     LOGGER.error(exc)
     sys.exit(3)
 
-TALER_MAX_DEBT = TC.value_string(
-    "bank", "MAX_DEBT", default="%s:50.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
 )
@@ -199,10 +196,15 @@ TALER_MAX_DEBT_BANK = TC.value_string(
 TALER_DIGITS = TC.value_int("bank", "NDIGITS", default=2)
 # Order matters
 TALER_PREDEFINED_ACCOUNTS = [
-    'Bank', 'Exchange', 'Tor', 'GNUnet', 'Taler', 'FSF', 'Tutorial', 'Survey'
+    "Bank",
+    "Exchange",
+    "Tor",
+    "GNUnet",
+    "Taler",
+    "FSF",
+    "Tutorial",
+    "Survey",
 ]
-TALER_EXPECTS_DONATIONS = ['Tor', 'GNUnet', 'Taler', 'FSF']
+TALER_EXPECTS_DONATIONS = ["Tor", "GNUnet", "Taler", "FSF"]
 TALER_SUGGESTED_EXCHANGE = TC.value_string("bank", "suggested_exchange")
-TALER_SUGGESTED_EXCHANGE_PAYTO = TC.value_string(
-    "bank", "suggested_exchange_payto"
-)
+TALER_SUGGESTED_EXCHANGE_PAYTO = TC.value_string("bank", 
"suggested_exchange_payto")

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



reply via email to

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