models.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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'),
  92. related_name='items')
  93. quantity = models.PositiveIntegerField(verbose_name=_('quantity'))
  94. # need a unique value we can reference when editing
  95. # that won't be easy to spoof
  96. cart_token = models.CharField(
  97. _("Cart Token"),
  98. max_length=255,
  99. db_index=True
  100. )
  101. # product as generic relation
  102. content_type = models.ForeignKey(ContentType)
  103. object_id = models.PositiveIntegerField()
  104. gift_detail = models.OneToOneField(CartItemGiftDetail, null=True,
  105. blank=True)
  106. account_number = models.CharField(max_length=7, null=True, blank=True)
  107. class Meta:
  108. """ Model meta class"""
  109. verbose_name = _('item')
  110. verbose_name_plural = _('items')
  111. ordering = ('cart',)
  112. objects = CartItemManager()
  113. def __unicode__(self):
  114. """ unicode repr of class """
  115. return 'Item in cart: ' + unicode(self.product)
  116. def save(self, *args, **kwargs):
  117. """ We need to set the cart_token to make sure it is unique """
  118. # This check should be skipped since cart_token is required.
  119. # keeping it in for now to clean up stale data
  120. if not self.cart_token:
  121. if not self.pk:
  122. self.cart_token = temp_code()
  123. super(CartItem, self).save(*args, **kwargs)
  124. # saving with force_insert twice leads to sad
  125. kwargs.pop('force_insert', None)
  126. encode_value = pbp_converter.encode(str(self.pk))
  127. self.cart_token = "P%s" % encode_value
  128. super(CartItem, self).save(*args, **kwargs)
  129. @property
  130. def is_gift(self):
  131. """ is this a gift? """
  132. if self.gift_detail:
  133. return True
  134. return False
  135. @property
  136. def image(self):
  137. """ get the image """
  138. return self.product.image
  139. @property
  140. def unit_price(self):
  141. """ unit price """
  142. return self.product.price
  143. @property
  144. def name(self):
  145. """ name """
  146. return self.product.title
  147. @property
  148. def description(self):
  149. """ description """
  150. return self.product.description
  151. @property
  152. def by_line(self):
  153. """ get the byline"""
  154. return "{0} {1}".format(self.name, self.description)
  155. @property
  156. def sku(self):
  157. """ get the sku """
  158. return self.product.sku
  159. @property
  160. def total_shipping_cost(self):
  161. """ Shipping costs. If there is just one item take the shipping_cost
  162. from the product, if there is more then one, for each item after the
  163. first one, the shipping cost is shipping_cost_multiple for each item
  164. total them together to get the total shipping cost
  165. """
  166. initial_cost = self.product.shipping_cost
  167. extra_item_costs = self.product.shipping_cost_multiple
  168. if self.quantity == 0:
  169. return 0.0
  170. if self.quantity == 1:
  171. return initial_cost
  172. elif self.quantity > 1:
  173. return initial_cost + ((self.quantity - 1) * extra_item_costs)
  174. @property
  175. def total_price(self):
  176. """ total price """
  177. return self.quantity * self.unit_price
  178. @property
  179. def sub_total(self):
  180. """ price including shipping """
  181. return self.total_price + self.total_shipping_cost
  182. def get_product(self):
  183. """ get the product """
  184. return self.content_type.get_object_for_this_type(id=self.object_id)
  185. def set_product(self, product):
  186. """ set the product """
  187. self.content_type = ContentType.objects.get_for_model(type(product))
  188. self.object_id = product.pk
  189. product = property(get_product, set_product)