No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

models.py 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # -*- coding: utf-8 -*-
  2. # This file is part of k4ever, a point-of-sale system
  3. # Contact............ <k4ever@lists.someserver.de>
  4. # Website............ http://k4ever.someserver.de/
  5. # Bug tracker........ http://k4ever.someserver.de/report
  6. #
  7. # Licensed under GNU Affero General Public License v3 or later
  8. from django.db import models
  9. from django.contrib.auth.models import User
  10. import datetime
  11. from decimal import Decimal
  12. from django.db.models.signals import pre_delete, post_delete
  13. # Create your models here.
  14. class BuyableType(models.Model):
  15. """ Type/Category for a buyable object """
  16. name = models.CharField(max_length=100)
  17. def __unicode__(self):
  18. return self.name
  19. def itemcount(self):
  20. return self.buyable_set.values().count()
  21. class Buyable(models.Model):
  22. """ Represents a buyable item. """
  23. name = models.CharField(max_length=100)
  24. price = models.DecimalField(max_digits=8, decimal_places=2)
  25. image = models.ImageField(upload_to='img/buyable/', help_text="<strong>The Image needs to have a 1:1 aspect ratio.</strong>")
  26. deposit = models.DecimalField(max_digits=8, decimal_places=2)
  27. description = models.TextField()
  28. buyableType = models.ManyToManyField(BuyableType)
  29. barcode = models.CharField(max_length=100, default='', blank=True)
  30. def hasDeposit(self):
  31. """ Returns True if the item has deposit. """
  32. return self.deposit > Decimal(0)
  33. def __unicode__(self):
  34. item = "%s (%.2f EUR" % (self.name, self.price)
  35. if self.hasDeposit():
  36. item += "/%.2f Pfand" % self.deposit
  37. item += ")"
  38. return item
  39. def types(self):
  40. typelist = ""
  41. for item in self.buyableType.values_list():
  42. typelist += item[1] + ", "
  43. return typelist.rstrip(u", ")
  44. class Order(models.Model):
  45. """ Represents an order by the user, consists of several :class:`Purchases <Purchase>`. """
  46. user = models.ForeignKey(User)
  47. price = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal(0))
  48. dateTime = models.DateTimeField(auto_now_add=True)
  49. def addItems(self, items):
  50. for item in items:
  51. self.purchase.add(item)
  52. self.price += item.price
  53. def updatePrice(self, instance, price, saveOrder=True):
  54. """ Update price of order, save Order if saveOrder is True. """
  55. self.price += price
  56. if self.id and saveOrder:
  57. self.save()
  58. def __unicode__(self):
  59. return "Price %s, User %s" % (self.price, self.user)
  60. def itemList(self):
  61. l = ""
  62. for item in self.purchase_set.all():
  63. l += item.buyable.name + u", "
  64. return l.rstrip(u", ")
  65. def save(self, *args, **kwargs):
  66. profile = self.user.get_profile()
  67. if self.id == None:
  68. # new item, get it!
  69. profile.balance -= self.price
  70. profile.save()
  71. else:
  72. # get old
  73. oldobj = Order.objects.get(id=self.id)
  74. if oldobj.user == self.user:
  75. profile = self.user.get_profile()
  76. profile.balance -= (self.price - oldobj.price)
  77. profile.save()
  78. else:
  79. oldProfile = oldobj.user.get_profile()
  80. newProfile = self.user.get_profile()
  81. oldProfile.balance += oldobj.price
  82. oldProfile.save()
  83. newprofile.balance -= self.price
  84. self.save()
  85. super(Order, self).save(*args, **kwargs)
  86. @staticmethod
  87. def pre_delete_signal(sender, instance, **kwargs):
  88. # Normally we would not need this function as for consistency
  89. # reasons and the behaviour of Buyables delete at deletion
  90. # the orders price should be 0. But for the sake of not getting
  91. # inconsistent with the users balance in the strangest situations
  92. # we will check that here. This means (at least) one extra query
  93. # per order deletion, as we don't get the updated object but the one
  94. # where all the buyables have not updated the price
  95. updOrder = Order.objects.get(id=instance.id)
  96. if updOrder.price != Decimal("0"):
  97. profile = updOrder.user.get_profile()
  98. profile.balance += updOrder.price
  99. profile.save()
  100. pre_delete.connect(Order.pre_delete_signal, sender=Order)
  101. class Purchase(models.Model):
  102. """ Represents a :class:`Buyable` in a :class:`Order`.
  103. Could be viewed as 'one bought item' or 'one item on the voucher'."""
  104. order = models.ForeignKey(Order)
  105. price = models.DecimalField(max_digits=8, decimal_places=2, default=None)
  106. isDeposit = models.BooleanField()
  107. buyable = models.ForeignKey(Buyable)
  108. def updatePrice(self):
  109. if self.isDeposit:
  110. self.price = self.buyable.deposit
  111. else:
  112. self.price = self.buyable.price
  113. def __unicode__(self):
  114. return "%s%s" % (self.buyable.name, (self.isDeposit and " (deposit)" or ""))
  115. def save(self, saveOrder=True, *args, **kwargs):
  116. if self.price == None:
  117. self.updatePrice()
  118. if self.order:
  119. if self.id == None:
  120. self.order.updatePrice(self, self.price, saveOrder=saveOrder)
  121. else:
  122. # object exists, update.
  123. oldobj = Purchase.objects.get(id=self.id)
  124. if oldobj.order == self.order:
  125. self.order.updatePrice(self, self.price - oldobj.price, saveOrder=saveOrder)
  126. else:
  127. oldobj.order.updatePrice(oldobj, -oldobj.price, saveOrder=saveOrder)
  128. self.order.updatePrice(self, self.price, saveOrder=saveOrder)
  129. super(Purchase, self).save(*args, **kwargs)
  130. @staticmethod
  131. def pre_delete_signal(sender, instance, **kwargs):
  132. # FIXME: For many items this could get very inefficient
  133. # Find workarround!
  134. print "pre delete from purchase"
  135. instance.order.updatePrice(instance, -instance.price, saveOrder=True)
  136. pre_delete.connect(Purchase.pre_delete_signal, sender=Purchase)