membersystem/src/accounting/models.py

125 lines
3.6 KiB
Python
Raw Permalink Normal View History

from hashlib import md5
from django.conf import settings
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):
2019-08-31 22:27:36 +00:00
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("auth.User", on_delete=models.PROTECT)
2018-06-23 23:05:02 +00:00
@property
def balance(self):
2019-08-31 22:27:36 +00:00
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,
2019-08-31 22:27:36 +00:00
help_text=_("This will include VAT"),
)
2019-08-31 22:27:36 +00:00
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("auth.User", on_delete=models.PROTECT)
account = models.ForeignKey(Account, on_delete=models.PROTECT)
2019-08-31 22:27:36 +00:00
description = models.CharField(max_length=1024, verbose_name=_("description"))
price = MoneyField(
verbose_name=_("price (excl. VAT)"),
max_digits=16,
decimal_places=2,
)
2019-08-31 22:27:36 +00:00
vat = MoneyField(verbose_name=_("VAT"), max_digits=16, decimal_places=2)
2019-08-31 22:27:36 +00:00
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)
2019-08-31 22:27:36 +00:00
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):
2021-12-13 19:44:09 +00:00
return f"Order ID {self.display_id}"
class Payment(CreatedModifiedAbstract):
2019-08-31 22:27:36 +00:00
amount = MoneyField(max_digits=16, decimal_places=2)
order = models.ForeignKey(Order, on_delete=models.PROTECT)
2019-08-31 22:27:36 +00:00
description = models.CharField(max_length=1024, verbose_name=_("description"))
2019-08-31 22:27:36 +00:00
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):
2021-12-13 19:44:09 +00:00
return f"Payment ID {self.display_id}"
class Meta:
verbose_name = _("payment")
verbose_name_plural = _("payments")