from django.db import models from django.conf import settings from django.contrib.auth.models import AbstractUser from django_currentuser.db.models import CurrentUserField from django.forms import ValidationError from django.utils import timezone # helper def make_register_transaction(transaction_sum:float): regbalance = Global.objects.get(name="register_balance") regbalance.value_float += float(round(float(transaction_sum), 2)) regbalance.save() # Custom user model class User(AbstractUser): balance = models.DecimalField(max_digits=8, decimal_places=2, default=0.00) allow_order_with_negative_balance = models.BooleanField(default=False) def delete(self, *args, **kwargs): self.balance = 0 self.is_active = False self.username = f"" self.last_name = "" self.first_name = "" self.email = "" super().save() # class Drink(models.Model): product_name = models.CharField(max_length=64) content_litres = models.DecimalField(max_digits=6, decimal_places=3, default=0.5) price = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) available = models.PositiveIntegerField(default=0) deleted = models.BooleanField(default=False) # when the following field is true, the amount of drinks will # not change and will not be displayed. # available > 0 -> there is a indefinetly amount of drinks left # available < 1 -> there are no drinks left do_not_count = models.BooleanField(default=False) def delete(self, *args, **kwargs): self.deleted = True super().save() def __str__(self): return f"{self.product_name} ({str(self.content_litres).rstrip('0')}l) - {self.price}{settings.CURRENCY_SUFFIX}" class RegisterTransaction(models.Model): class Meta: verbose_name = "register transaction" verbose_name_plural = "register" transaction_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) # the following original_transaction_sum is needed when need to be # updated, but the old value needs to be known (field is hidden) old_transaction_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) datetime = models.DateTimeField(default=timezone.now) is_user_deposit = models.BooleanField(default=False) comment = models.TextField(default=" ") user = CurrentUserField() def save(self, *args, **kwargs): if self._state.adding: make_register_transaction(self.transaction_sum) if self.is_user_deposit == True: # update user balance self.user.balance += self.transaction_sum self.user.save() self.old_transaction_sum = self.transaction_sum super().save(*args, **kwargs) else: # update register transaction sum_diff = self.transaction_sum - self.old_transaction_sum make_register_transaction(sum_diff) # update user balance if self.is_user_deposit == True: ub_sum_diff = self.transaction_sum - self.old_transaction_sum self.user.balance += ub_sum_diff self.user.save() self.old_transaction_sum = self.transaction_sum super().save(*args, **kwargs) def delete(self, *args, **kwargs): make_register_transaction(-self.transaction_sum) # update user deposit if self.is_user_deposit: self.user.balance -= self.transaction_sum self.user.save() super().delete(*args, kwargs) def __str__(self): return f"{self.transaction_sum}{settings.CURRENCY_SUFFIX} by {self.user}" class Order(models.Model): drink = models.ForeignKey( "Drink", on_delete=models.SET_NULL, null=True, limit_choices_to=models.Q(available__gt=0) # Query only those drinks with a availability greater than (gt) 0 ) user = CurrentUserField() datetime = models.DateTimeField(default=timezone.now) amount = models.PositiveIntegerField(default=1, editable=False) # the following fields will be set automatically # won't use foreign key, because the values of the foreign objects may change over time. product_name = models.CharField(max_length=64, editable=False) price_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0, editable=False) content_litres = models.DecimalField(max_digits=6, decimal_places=3, default=0, editable=False) # TODO: Add more comments on how and why the save & delete functions are implemented # address this in a refactoring issue. def save(self, *args, **kwargs): drink = Drink.objects.get(pk=self.drink.pk) if self._state.adding and drink.available > 0: if not drink.do_not_count: drink.available -= self.amount drink.save() self.product_name = drink.product_name self.price_sum = drink.price * self.amount self.content_litres = drink.content_litres self.user.balance -= self.price_sum self.user.save() super().save(*args, **kwargs) else: raise ValidationError("This entry can't be changed.") def delete(self, *args, **kwargs): self.user.balance += self.price_sum self.user.save() drink = Drink.objects.get(pk=self.drink.pk) if not drink.do_not_count: drink.available += self.amount drink.save() super().delete(*args, **kwargs) def __str__(self): return f"{self.drink.product_name} ({str(self.drink.content_litres).rstrip('0')}l) x {self.amount} - {self.price_sum}{settings.CURRENCY_SUFFIX}" class Global(models.Model): # this contains global values that are generated/calculated by code # e.g. the current balance of the register, ... name = models.CharField(max_length=42, unique=True, primary_key=True) comment = models.TextField() value_float = models.FloatField(default=0.00) value_string = models.TextField() def __str__(self): return self.name