# -*- coding: utf-8 -*- # This file is part of k4ever, a point-of-sale system # Contact............ # Website............ http://k4ever.someserver.de/ # Bug tracker........ http://k4ever.someserver.de/report # # Licensed under GNU Affero General Public License v3 or later from decimal import Decimal from django.core.validators import MinValueValidator from django.contrib.auth.models import User from django.db import models from django.db.models.deletion import SET_NULL from django.db.models.signals import pre_delete from validator import validate_notZero class TransactionType(models.Model): """ Type for a :class:`Transaction`. States how the money has been added to the account and if somebody needs to check if it was payed. """ name = models.CharField(max_length=100) needsCheck = models.BooleanField(default=True) def __unicode__(self): return unicode(self.name) class Transaction(models.Model): """Represents a money transaction into the users account. """ user = models.ForeignKey(User) transactionType = models.ForeignKey(TransactionType, verbose_name='Typ') dateTime = models.DateTimeField(auto_now_add=True) amount = models.DecimalField(max_digits=8, decimal_places=2, validators=[validate_notZero]) checked = models.BooleanField(default=False) def __unicode__(self): return u"%s for user %s (%s),%schecked" % (self.amount, self.user, self.transactionType, (self.checked and " " or " not ")) def save(self, *args, **kwargs): profile = self.user.get_profile() if self.id == None: # insert profile.balance += self.amount else: # update oldobj = Transaction.objects.get(id=self.id) if oldobj.user != self.user: oldprofile = oldobj.user.get_profile() oldprofile.balance -= oldobj.amount oldprofile.save() # just to be save, reget profile profile = self.user.get_profile() profile.balance += self.amount else: profile.balance += (self.amount-oldobj.amount) profile.save() super(Transaction, self).save(*args, **kwargs) @staticmethod def pre_delete_signal(sender, instance, **kwargs): profile = instance.user.get_profile() profile.balance -= instance.amount profile.save() pre_delete.connect(Transaction.pre_delete_signal, sender=Transaction) class VirtualTransaction(models.Model): """ Represents a transaction between two users. """ user = models.ForeignKey(User, null=True, on_delete=SET_NULL) recipient = models.ForeignKey(User, related_name='receivedtransaction', null=True, on_delete=SET_NULL, verbose_name=u'Empfänger') dateTime = models.DateTimeField(auto_now_add=True) amount = models.DecimalField(max_digits=8, decimal_places=2, validators=[MinValueValidator(Decimal("0.01"))], verbose_name='Betrag') comment = models.CharField(max_length=100, verbose_name='Verwendungszweck') def __unicode__(self): return u"%s ==> %s: %s Euro" % (self.user, self.recipient, self.amount) @staticmethod def moveMoney(fromUser, toUser, amount, commit=True): print fromUser, toUser, fromUser == toUser, fromUser.id, toUser.id if not (fromUser == toUser): fromProfile = fromUser.get_profile() toProfile = toUser.get_profile() fromProfile.balance -= amount toProfile.balance += amount if commit: fromProfile.save() toProfile.save() def save(self, *args, **kwargs): if self.user and self.recipient: oldobj = None try: oldobj = VirtualTransaction.objects.get(id=self.id) except VirtualTransaction.DoesNotExist: pass if oldobj and (oldobj.user != self.user or oldobj.recipient != self.recipient): VirtualTransaction.moveMoney(oldobj.recipient, oldobj.user, oldobj.amount) VirtualTransaction.moveMoney(self.user, self.recipient, self.amount) else: tmpAmount = None if oldobj == None: tmpAmount = self.amount else: tmpAmount = self.amount - oldobj.amount VirtualTransaction.moveMoney(self.user, self.recipient, tmpAmount) super(VirtualTransaction, self).save(*args, **kwargs) @staticmethod def pre_delete_signal(sender, instance, **kwargs): """ Pre delete signal to ensure consistent balances on object delete. """ # Only revert if both users exist. if instance.user and instance.recipient: # revert transaction VirtualTransaction.moveMoney(instance.recipient, instance.user, amount) pre_delete.connect(VirtualTransaction.pre_delete_signal, sender=VirtualTransaction)