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")