from datetime import date import uuid from django.db import models from django.forms import ModelForm from django.core.validators import validate_unicode_slug, FileExtensionValidator class Category(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField( max_length=64, validators=[validate_unicode_slug], default="New Category" ) icon = models.CharField(max_length=64, default="folder") def __str__(self): return self.name class Meta: ordering = ["name"] class CategoryForm(ModelForm): class Meta: model = Category fields = ["name", "icon"] class Transaction(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=256, default="New Transaction") description = models.TextField(null=True, blank=True) value = models.DecimalField(max_digits=12, decimal_places=2, default=0) date = models.DateField(default=date.today) trader = models.CharField(max_length=128, blank=True, null=True) category = models.ForeignKey( Category, on_delete=models.SET_NULL, blank=True, null=True ) def __str__(self): res = f"{self.date} {self.name}: {self.value}€" if self.category: return f"{res} ({self.category})" return res class Meta: ordering = ["-date"] class TransactionForm(ModelForm): class Meta: model = Transaction fields = ["date", "name", "value", "trader", "category", "description"] class Invoice(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=256, default="New Invoice") file = models.FileField( upload_to="invoices/", validators=[FileExtensionValidator(["pdf"])] ) transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE) def __str__(self): return f"{self.name}: {self.transaction}" def delete(self, *args, **kwargs): self.file.delete() super().delete(*args, **kwargs) class InvoiceForm(ModelForm): class Meta: model = Invoice fields = ["name", "file"] class Snapshot(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) date = models.DateField(default=date.today, unique=True) value = models.DecimalField(max_digits=12, decimal_places=2, default=0) previous = models.OneToOneField( "self", on_delete=models.SET_NULL, blank=True, null=True, editable=False ) diff = models.DecimalField( max_digits=12, decimal_places=2, default=0, editable=False ) def __str__(self): if self.previous is None: return f"Snapshot {self.date}: {self.value}€" return f"Snapshot {self.date}: {self.value}€ (Previous: {self.previous.date})" def save(self, *args, only_super=False, **kwargs): if not only_super: _prev = ( self.__class__.objects.order_by("-date") .exclude(id=self.id) .filter(date__lt=self.date) .first() ) print(_prev) try: _next = self.__class__.objects.exclude(id=self.id).get(previous=_prev) except self.__class__.DoesNotExist: pass else: _next.previous = self _next.save(only_super=True) self.previous = _prev if self.previous is None: self.diff = 0 else: self.diff = self.value - self.previous.value super().save(*args, **kwargs) def delete(self, *args, only_super=False, **kwargs): if not only_super: try: _next = self.__class__.objects.get(previous=self) except self.__class__.DoesNotExist: super().delete(*args, **kwargs) else: _next.previous = self.previous super().delete(*args, **kwargs) _next.save(only_super=True) else: super().delete(*args, **kwargs) @property def sum(self): if self.previous is None: return None trans = Transaction.objects.filter( date__lt=self.date, date__gte=self.previous.date ).aggregate(sum=models.Sum("value")) return trans["sum"] or 0 class Meta: ordering = ["-date"] class SnapshotForm(ModelForm): class Meta: model = Snapshot fields = ["date", "value"]