k4ever/k4ever/transaction/models.py

127 lines
4.3 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of k4ever, a point-of-sale system
# Contact............ <k4ever@lists.someserver.de>
# 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):
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, instance.amount)
pre_delete.connect(VirtualTransaction.pre_delete_signal, sender=VirtualTransaction)