summaryrefslogtreecommitdiff
path: root/talerbank/app/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'talerbank/app/models.py')
-rw-r--r--talerbank/app/models.py242
1 files changed, 124 insertions, 118 deletions
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)