Commits

Ian George  committed 6bd0c3d

discount refactor

  • Participants
  • Parent commits 96194a7

Comments (0)

Files changed (3)

File quiet/shop/models/basket.py

 from django.contrib.contenttypes import generic
 from django.contrib.auth.models import User
 
-BASKET_ITEM_TYPE_CHOICES = (
-    (1, 'Product'),
-    (2, 'Shipping'),
-    (3, 'Discount'),
-    )
-
 class Basket(models.Model):
     created = models.DateTimeField(auto_now_add=True)
     checked_out = models.BooleanField()
     def __unicode__(self):
         return 'Basket: %s created at:%s' % (self.id, self.created)
 
-class BasketItem(models.Model):
-    PRODUCT = 1
-    SHIPPING = 2
-    DISCOUNT = 3
-    basket = models.ForeignKey(Basket, related_name='items')
-    
+class AbstractBasketItem(models.Model):
     #generic relation for actual item (covers us for odd products, shipping items and discounts, huzzah!)
     item_content_type = models.ForeignKey(ContentType)
     item_object_id = models.PositiveIntegerField()
     item_object = generic.GenericForeignKey('item_content_type', 'item_object_id')
-    item_type = models.IntegerField(choices=BASKET_ITEM_TYPE_CHOICES)
-
-    quantity = models.IntegerField(default=0)
-    price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=False)
-    tax = models.DecimalField(max_digits=8, decimal_places=2, default=0, null=True, blank=True)
-    deposit = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
-
-    class Meta:
-        app_label='shop'
-        unique_together = (('basket', 'item_object_id', 'item_content_type'),)
-
-    def __unicode__(self):
-        return u'%s' % str(self.item_object)
-
-    def get_absolute_url(self):
-        return self.item_object.get_absolute_url()
-
-    def net_price(self, negate_discount=True, use_deposit=True):
-        price = self.quantity * self.price
-        if self.item_type == BasketItem.DISCOUNT and negate_discount and price > 0:
-            price *= -1
-        if use_deposit and self.deposit > 0:
-            price *= self.deposit
-        return price
-
-    def tax_price(self, negate_discount=True):
-        price = self.quantity * (self.tax or 0)
-        if self.item_type == BasketItem.DISCOUNT and negate_discount and price > 0:
-            price *= -1
-        return price
-
-    def total_price(self, negate_discount=True, use_deposit=True):
-        price = self.quantity * (self.price + (self.tax or 0))
-        if self.item_type == BasketItem.DISCOUNT and negate_discount and price > 0:
-            price *= -1
-        if use_deposit and self.deposit > 0:
-            price *= self.deposit
-        return price
-
-class ShippingItem(models.Model):
-    basket = models.ForeignKey(Basket, related_name='shipping_items')
-    basket_item = models.OneToOneField(BasketItem, related_name='shipping_item', null=True, blank=True)
-
-    item_content_type = models.ForeignKey(ContentType)
-    item_object_id = models.PositiveIntegerField()
-    item_object = generic.GenericForeignKey('item_content_type', 'item_object_id')
 
     quantity = models.IntegerField(default=0)
+    price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=False)
+    tax = models.DecimalField(max_digits=8, decimal_places=2, default=0, null=True, blank=True)
 
-    tax = models.DecimalField(max_digits=8, decimal_places=2, default=0, null=True, blank=True)
-    price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=False)
+    def __unicode__(self):
+        if self.item_object:
+            return u'%s' % str(self.item_object)
+        else:
+            return "%s of id:%s @ %s" % (self.quantity, self.id, self.price)
+
+    def net_price(self):
+        price = self.quantity * self.price
+        return price
+
+    def tax_price(self):
+        price = self.quantity * (self.tax or 0)
+        return price
+
+    def total_price(self):
+        price = self.quantity * (self.price + (self.tax or 0))
+        return price
 
     class Meta:
-        app_label='shop'
+        abstract = True
+        app_label = 'shop'
         unique_together = (('basket', 'item_object_id', 'item_content_type'),)
 
-    def net_price(self):
-        return self.quantity * self.price
+class BasketItem(AbstractBasketItem):
+    basket = models.ForeignKey(Basket, related_name='items')
+    
+    deposit = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
 
-    def tax_price(self):
-        return self.quantity * (self.tax or 0)
+    def get_absolute_url(self):
+        return self.item_object.get_absolute_url()
 
-    def total_price(self):
-        return self.quantity * (self.price + (self.tax or 0))
+    def net_price(self, use_deposit=True):
+        price = super(BasketItem, self).net_price()
+        if use_deposit and self.deposit > 0:
+            price *= self.deposit
+        return price
+
+    def tax_price(self, use_deposit=True):
+        price = super(BasketItem, self).tax_price()
+        if use_deposit and self.deposit > 0:
+            price *= self.deposit
+        return price
+
+    def total_price(self, use_deposit=True):
+        price = super(BasketItem, self).total_price()
+        if use_deposit and self.deposit > 0:
+            price *= self.deposit
+        return price
+
+class ShippingItem(AbstractBasketItem):
+    basket = models.ForeignKey(Basket, related_name='shipping_items')
+
+class DiscountItem(AbstractBasketItem):
+    basket = models.ForeignKey(Basket, related_name='discount_items')
 
 signal_basket_add_pre = django.dispatch.Signal(providing_args=['basket_id', 'content_type', 'content_id', 'quantity', 'item_type'])
 signal_basket_add_post = django.dispatch.Signal(providing_args=['basket_id', 'content_type', 'content_id', 'quantity', 'item_type'])

File quiet/shop/models/order.py

     ('SAGEPAY', 'SagePay'),
     ('PHONE', 'Telephone'),
     ('INSTORE', 'In store'),
+    ('GIFT', 'Gift Certificate'),
 )
 ADDRESS_TYPE_CHOICES = (
     ('billing', 'Billing Address'),
         self.total = basket.total_price()
         self.balance = basket.total_price()
     
-        for item in basket:
+        for item in basket.products():
             order_item, created = OrderItem.objects.get_or_create(
                 order=self, 
                 product_content_type=item.item_content_type,
             order_item.total = item.price + (item.tax or 0)
             order_item.save()
             debug("payment", "Added shipping %s to order %s" % (order_item, self.id))
+
+        for item in basket.basket.discount_items.all():
+            order_item, created = OrderItem.objects.get_or_create(
+                order=self,
+                product_content_type = item.item_content_type,
+                product_object_id = item.item_object_id,
+                qty = 1,
+                defaults={'qty':1, 'price':0, 'total':0}
+                )
+            order_item.price = item.price
+            order_item.tax = (item.tax or 0)
+            order_item.total = item.price + (item.tax or 0)
+            order_item.save()
+            debug("payment", "Added discounts %s to order %s" % (order_item, self.id))
             
             self.save()        
 
 
     basket = SessionBasket(kwargs['basket_id'])
 
-    for item in basket:
+    for item in basket.products():
         obj = item.item_object
         if hasattr(obj, 'stock') and hasattr(obj, 'save'):
             if obj.stock > 0:

File quiet/shop/session.py

-from models import Basket, BasketItem, ShippingItem
+from models import Basket, BasketItem, ShippingItem, DiscountItem
 from django.contrib.contenttypes.models import ContentType
 
 from quiet.shop.models import signal_basket_add_pre, signal_basket_add_post, signal_basket_update_pre, signal_basket_update_post, signal_basket_remove_pre, signal_basket_remove_post, signal_basket_clear_pre, signal_basket_clear_post, signal_basket_kill_pre, signal_basket_kill_post
 
         self.check(remove=True)
 
-    def __iter__(self):
-        for item in self.basket.items.all().order_by('item_type'):
-            yield item
+    def products(self):
+        return self.basket.items.all()
+
+    def shipping(self):
+        return self.basket.shipping_items.all()
+
+    def discounts(self):
+        return self.basket.discount_items.all()
 
     def __unicode__(self):
         return unicode(self.basket)
 
     def net_price(self, use_deposit=True):
-        return sum(p.net_price(use_deposit=use_deposit) for p in self.basket.items.all() if p.item_type!=p.DISCOUNT)
+        return sum(p.net_price(use_deposit=use_deposit) for p in self.basket.items.all())
 
     def tax_price(self):
-        return sum(p.tax_price(True) for p in self.basket.items.all() if p.item_type!=p.DISCOUNT)
+        return sum(p.tax_price(True) for p in self.basket.items.all())
 
     def total_price(self):
-        return self.net_price(False) + self.tax_price() + self.shipping_price()
+        return self.net_price(False) + self.tax_price() + self.shipping_price() - self.discount_price()
     
     def total_price_with_deposit(self):
-        return self.net_price(True) + self.tax_price() + self.shipping_price()
+        return self.net_price(True) + self.tax_price() + self.shipping_price() - self.discount_price()
     
     def shipping_price(self):
         return sum(p.net_price() for p in self.basket.shipping_items.all())
 
     def discount_price(self):
-        return sum(p.net_price() for p in self.basket if p.item_type==p.DISCOUNT)
-
-    def shipping_price(self):
-        return sum(p.total_price() for p in self.basket.shipping_items.all())
+        return sum(p.net_price() for p in self.basket.discount_items.all())
 
     def balance(self):
         grand_total = self.total_price()
             if errors:
                 raise Exception('Check basket found errors.\n%s' % errors)    
 
-    def add(self, object_to_add, quantity, i_type=BasketItem.PRODUCT, price=0):
+    def add(self, object_to_add, quantity, price=0):
         if quantity > 99 or quantity < 1:
             quantity = 1
             
         c_type = ContentType.objects.get_for_model(object_to_add)
-        signal_basket_add_pre.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_add.id, quantity=quantity, item_type=i_type)
+        signal_basket_add_pre.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_add.id, quantity=quantity)
         item,created = BasketItem.objects.get_or_create(
             basket=self.basket, 
             item_object_id=object_to_add.id,
             item_content_type=c_type,
-            item_type=i_type
             )
         if created:
             if price:
                 item.price = object_to_add.price
             item.quantity = quantity
             item.save() 
-        signal_basket_add_post.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_add.id, quantity=quantity, item_type=i_type)
+        signal_basket_add_post.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_add.id, quantity=quantity)
 
     def add_shipping(self, object_to_add, quantity=1, price=0):
         c_type = ContentType.objects.get_for_model(object_to_add)
                 item.price = object_to_add.price
         item.save() 
 
+    def add_discount(self, object_to_add, quantity=1, price=0):
+        c_type = ContentType.objects.get_for_model(object_to_add)
+
+        item,created = DiscountItem.objects.get_or_create(
+            basket=self.basket, 
+            item_object_id=object_to_add.id,
+            item_content_type=c_type,
+            )
+        item.quantity = quantity
+        if created:
+            if price:
+                item.price = price
+            else:
+                item.price = object_to_add.price
+        item.save() 
+
     def remove(self, object_to_remove):
         c_type = ContentType.objects.get_for_model(object_to_remove)
         signal_basket_remove_pre.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_remove.id)