from hashlib import md5 from django.conf import settings from django.contrib.auth import get_user_model from django.db import models from django.db.models.aggregates import Sum from django.utils.translation import gettext as _ from django.utils.translation import pgettext_lazy from djmoney.models.fields import MoneyField class CreatedModifiedAbstract(models.Model): modified = models.DateTimeField( auto_now=True, verbose_name=_("modified"), ) created = models.DateTimeField( auto_now_add=True, verbose_name=_("created"), ) class Meta: abstract = True class Account(CreatedModifiedAbstract): """ This is the model where we can give access to several users, such that they can decide which account to use to pay for something. """ owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT) @property def balance(self): return self.transactions.all().aggregate(Sum('amount')).get('amount', 0) class Transaction(CreatedModifiedAbstract): """ Tracks in and outgoing events of an account. When an order is received, an amount is subtracted, when a payment is received, an amount is added. """ account = models.ForeignKey( Account, on_delete=models.PROTECT, related_name="transactions", ) amount = MoneyField( verbose_name=_("amount"), max_digits=16, decimal_places=2, help_text=_("This will include VAT") ) description = models.CharField( max_length=1024, verbose_name=_("description") ) class Order(CreatedModifiedAbstract): """ Scoped out: Contents of invoices will have to be tracked either here or in a separate Invoice model. This is undecided because we are not generating invoices at the moment. """ user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT) account = models.ForeignKey(Account, on_delete=models.PROTECT) is_paid = models.BooleanField(default=False) description = models.CharField( max_length=1024, verbose_name=_("description") ) price = MoneyField( verbose_name=_("price (excl. VAT)"), max_digits=16, decimal_places=2, ) vat = MoneyField( verbose_name=_("VAT"), max_digits=16, decimal_places=2, ) is_paid = models.BooleanField( default=False, verbose_name=_("is paid"), ) @property def total(self): return self.price + self.vat @property def display_id(self): return str(self.id).zfill(6) @property def payment_token(self): pk = str(self.pk).encode("utf-8") x = md5() x.update(pk) extra_hash = (settings.SECRET_KEY + 'blah').encode('utf-8') x.update(extra_hash) return x.hexdigest() class Meta: verbose_name = pgettext_lazy("accounting term", "Order") verbose_name_plural = pgettext_lazy("accounting term", "Orders") def __str__(self): return "Order ID {id}".format(id=self.display_id) class Payment(CreatedModifiedAbstract): amount = MoneyField( max_digits=16, decimal_places=2, ) order = models.ForeignKey(Order, on_delete=models.PROTECT) description = models.CharField( max_length=1024, verbose_name=_("description") ) stripe_charge_id = models.CharField( max_length=255, null=True, blank=True, ) @property def display_id(self): return str(self.id).zfill(6) @classmethod def from_order(cls, order): return cls.objects.create( order=order, user=order.user, amount=order.total, description=order.description, ) def __str__(self): return "Payment ID {id}".format(id=self.display_id) class Meta: verbose_name = _("payment") verbose_name_plural = _("payments")