Source

Quiet apps / quiet / shop / session.py

Full commit
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

BASKET_SESSION_KEY='al-basket'

class SessionBasket(object):
    def __init__(self, request):
        #we should have either a request object or an int, bit hacky
        #but sometimes we don't have a request object
        #TODO: MOAR ELEGANCE!
        try:
            self.basket_id = request.session.get(BASKET_SESSION_KEY)
        except:
            self.basket_id = int(request)

        created = False
        try:
            basket = Basket.objects.get(id=self.basket_id)
        except Basket.DoesNotExist:
            basket = Basket()
            basket.save()
            created = True

        if created and request and hasattr(request, 'session'):
            request.session[BASKET_SESSION_KEY] = basket.id
        self.basket = basket

        self.check(remove=True)

    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())

    def tax_price(self):
        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() - self.discount_price()
    
    def total_price_with_deposit(self):
        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.discount_items.all())

    def balance(self):
        grand_total = self.total_price()
        total = self.total_price_with_deposit()
        return grand_total - total

    def count(self):
        return self.basket.items.count()

    def check(self, remove=False):
        if remove:
            items_to_remove = [i for i in self.basket.items.all() if i.item_object==None]
            for itm in items_to_remove:
                self.remove(itm)
        else:
            errors = ''
            for itm in self.basket.items.all():
                if item.item_object == None:
                    errors += 'Item %s has no object \n' % item.id
                if item.price == None or item.price == 0:
                    errors += 'Item %s has no price \n' % item.id
            if errors:
                raise Exception('Check basket found errors.\n%s' % errors)    

    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,created = BasketItem.objects.get_or_create(
            basket=self.basket, 
            item_object_id=object_to_add.id,
            item_content_type=c_type,
            )
        if created:
            if price:
                item.price = price
            else:
                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)

    def add_shipping(self, object_to_add, quantity=1, price=0):
        c_type = ContentType.objects.get_for_model(object_to_add)

        item,created = ShippingItem.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 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)
        try:
            item = BasketItem.objects.get(
                basket=self.basket,
                item_object_id=object_to_remove.id,
                item_content_type=c_type
                )
        except BasketItem.DoesNotExist:
            return
        else:
            item.delete()
        signal_basket_remove_post.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_remove.id)

    def update(self, object_to_update, quantity, tax=None):
        if int(quantity) > 99:
            quantity = 1

        if quantity < 1:
            self.remove(object_to_update)
            return

        c_type = ContentType.objects.get_for_model(object_to_update) 
        signal_basket_update_pre.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_update.id, quantity=quantity)
        try:
            item = BasketItem.objects.get(
                basket=self.basket,
                item_object_id=object_to_update.id,
                item_content_type=c_type
                )
        except BasketItem.DoesNotExist:
            return
        else:
            item.price = object_to_update.price
            item.quantity = quantity
            item.tax = tax
            item.save() 
        signal_basket_update_post.send(sender=self.__class__, basket_id=self.basket_id, content_type=c_type.id, content_id=object_to_update.id, quantity=quantity)

    def clear(self):
        signal_basket_clear_pre.send(sender=self.__class__, basket_id=self.basket_id)
        for item in self.basket.items.all():
            item.delete()
        signal_basket_clear_post.send(sender=self.__class__, basket_id=self.basket_id)

    def clear_shipping(self):
        for item in self.basket.shipping_items.all():
            item.delete()            

    def kill(self, request):
        signal_basket_kill_pre.send(sender=self.__class__, basket_id=self.basket_id)
        self.clear()
        request.session[BASKET_SESSION_KEY] = None
        signal_basket_kill_post.send(sender=self.__class__, basket_id=self.basket_id)