models.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #!/usr/bin/env python
  2. """ Django models for cart """
  3. # -*- coding: utf-8 -*-
  4. from decimal import Decimal
  5. from django.db import models
  6. from django.utils.translation import ugettext_lazy as _
  7. from django.contrib.contenttypes.models import ContentType
  8. from django.contrib.auth.models import User
  9. from .managers import CartManager, CartItemManager
  10. from pbp_core.utils import temp_code, pbp_converter
  11. class Cart(models.Model):
  12. """ Shopping cart """
  13. # basically session_key
  14. cart_id = models.CharField(max_length=50, db_index=True)
  15. owner = models.ForeignKey(User, related_name="cart", null=True, blank=True)
  16. creation_date = models.DateTimeField(auto_now_add=True)
  17. last_update = models.DateTimeField(auto_now_add=True, auto_now=True)
  18. checked_out = models.BooleanField(default=False)
  19. objects = CartManager()
  20. class Meta:
  21. """ Django model meta class """
  22. verbose_name = _("Cart")
  23. verbose_name_plural = _("Carts")
  24. ordering = ("-creation_date",)
  25. def __unicode__(self):
  26. """ unicode name of model """
  27. return u"Cart: {0}".format(unicode(self.cart_id))
  28. @property
  29. def has_items(self):
  30. """ is the cart empty?"""
  31. return self.item_count > 0
  32. @property
  33. def cart_items(self):
  34. """ return all cart items"""
  35. return self.items.all()
  36. @property
  37. def item_count(self):
  38. """ number of items """
  39. return self.items.count()
  40. @property
  41. def cart_shipping_total(self):
  42. """ Total shipping cost"""
  43. price = Decimal(0.0)
  44. for item in self.cart_items:
  45. price = price + item.total_shipping_cost
  46. return price
  47. @property
  48. def cart_subtotal(self):
  49. """ cart subtotal"""
  50. price = Decimal(0.0)
  51. for item in self.cart_items:
  52. price = price + item.total_price
  53. return price
  54. @property
  55. def cart_total(self):
  56. """ total cart cost"""
  57. return self.cart_subtotal + self.cart_shipping_total
  58. @property
  59. def purchase_description(self):
  60. """ the purchase description for carts """
  61. description = ""
  62. for item in self.cart_items:
  63. description = description + "<br>" + item.by_line
  64. return description
  65. @property
  66. def buyer_id(self):
  67. """ the buyer for the person who created the cart"""
  68. if self.owner:
  69. return self.owner.pk
  70. return None
  71. class CartItemGiftDetail(models.Model):
  72. """ Cart Item gift detail """
  73. first_name = models.CharField(max_length=30)
  74. last_name = models.CharField(max_length=30)
  75. company = models.CharField(max_length=30, blank=True, null=True)
  76. address1 = models.CharField(max_length=30)
  77. address2 = models.CharField(max_length=30, blank=True, null=True)
  78. city = models.CharField(max_length=30)
  79. region = models.CharField(max_length=30)
  80. phone = models.CharField(max_length=30)
  81. country = models.CharField(max_length=30)
  82. country_other = models.CharField(max_length=30)
  83. postal_code = models.CharField(max_length=30)
  84. plus_four = models.CharField(max_length=4)
  85. email = models.CharField(max_length=30)
  86. bill_on_renewal = models.CharField(max_length=10)
  87. gift_card_delivery = models.CharField(max_length=10)
  88. message = models.CharField(max_length=72, null=True, blank=True)
  89. class CartItem(models.Model):
  90. """ Cart items """
  91. cart = models.ForeignKey(Cart, verbose_name=_("cart"), related_name="items")
  92. quantity = models.PositiveIntegerField(verbose_name=_("quantity"))
  93. # need a unique value we can reference when editing
  94. # that won't be easy to spoof
  95. cart_token = models.CharField(_("Cart Token"), max_length=255, db_index=True)
  96. # product as generic relation
  97. content_type = models.ForeignKey(ContentType)
  98. object_id = models.PositiveIntegerField()
  99. gift_detail = models.OneToOneField(CartItemGiftDetail, null=True, blank=True)
  100. account_number = models.CharField(max_length=7, null=True, blank=True)
  101. class Meta:
  102. """ Model meta class"""
  103. verbose_name = _("item")
  104. verbose_name_plural = _("items")
  105. ordering = ("cart",)
  106. objects = CartItemManager()
  107. def __unicode__(self):
  108. """ unicode repr of class """
  109. return "Item in cart: " + unicode(self.product)
  110. def save(self, *args, **kwargs):
  111. """ We need to set the cart_token to make sure it is unique """
  112. # This check should be skipped since cart_token is required.
  113. # keeping it in for now to clean up stale data
  114. if not self.cart_token:
  115. if not self.pk:
  116. self.cart_token = temp_code()
  117. super(CartItem, self).save(*args, **kwargs)
  118. # saving with force_insert twice leads to sad
  119. kwargs.pop("force_insert", None)
  120. encode_value = pbp_converter.encode(str(self.pk))
  121. self.cart_token = "P%s" % encode_value
  122. super(CartItem, self).save(*args, **kwargs)
  123. @property
  124. def is_gift(self):
  125. """ is this a gift? """
  126. if self.gift_detail:
  127. return True
  128. return False
  129. @property
  130. def image(self):
  131. """ get the image """
  132. return self.product.image
  133. @property
  134. def unit_price(self):
  135. """ unit price """
  136. return self.product.price
  137. @property
  138. def name(self):
  139. """ name """
  140. return self.product.title
  141. @property
  142. def description(self):
  143. """ description """
  144. return self.product.description
  145. @property
  146. def by_line(self):
  147. """ get the byline"""
  148. return "{0} {1}".format(self.name, self.description)
  149. @property
  150. def sku(self):
  151. """ get the sku """
  152. return self.product.sku
  153. @property
  154. def total_shipping_cost(self):
  155. """ Shipping costs. If there is just one item take the shipping_cost
  156. from the product, if there is more then one, for each item after the
  157. first one, the shipping cost is shipping_cost_multiple for each item
  158. total them together to get the total shipping cost
  159. """
  160. initial_cost = self.product.shipping_cost
  161. extra_item_costs = self.product.shipping_cost_multiple
  162. if self.quantity == 0:
  163. return 0.0
  164. if self.quantity == 1:
  165. return initial_cost
  166. elif self.quantity > 1:
  167. return initial_cost + ((self.quantity - 1) * extra_item_costs)
  168. @property
  169. def total_price(self):
  170. """ total price """
  171. return self.quantity * self.unit_price
  172. @property
  173. def sub_total(self):
  174. """ price including shipping """
  175. return self.total_price + self.total_shipping_cost
  176. def get_product(self):
  177. """ get the product """
  178. return self.content_type.get_object_for_this_type(id=self.object_id)
  179. def set_product(self, product):
  180. """ set the product """
  181. self.content_type = ContentType.objects.get_for_model(type(product))
  182. self.object_id = product.pk
  183. product = property(get_product, set_product)