class Cart(models.Model): last_updated = models.DateTimeField(_("Last updated"), null=True) objects = managers.CartManager() def __iter__(self): """ Allow the cart to be iterated giving access to the cart's items, ensuring the items are only retrieved once and cached. """ if not hasattr(self, "_cached_items"): self._cached_items = self.items.all() return iter(self._cached_items) def add_item(self, variation, quantity): """ Increase quantity of existing item if SKU matches, otherwise create new. """ kwargs = {"sku": variation.sku, "unit_price": variation.price()} item, created = self.items.get_or_create(**kwargs) if created: item.description = unicode(variation) item.unit_price = variation.price() item.url = variation.product.get_absolute_url() image = variation.image if image is not None: item.image = unicode(image.file) variation.product.actions.added_to_cart() item.quantity += quantity item.save() def has_items(self): """ Template helper function - does the cart have items? """ return len(list(self)) > 0 def total_quantity(self): """ Template helper function - sum of all item quantities. """ return sum([item.quantity for item in self]) def total_price(self): """ Template helper function - sum of all costs of item quantities. """ return sum([item.total_price for item in self]) def skus(self): """ Returns a list of skus for items in the cart. Used by ``upsell_products`` and ``calculate_discount``. """ return [item.sku for item in self] def upsell_products(self): """ Returns the upsell products for each of the items in the cart. """ cart = Product.objects.filter(variations__sku__in=self.skus()) published_products = Product.objects.published() for_cart = published_products.filter(upsell_products__in=cart) with_cart_excluded = for_cart.exclude(variations__sku__in=self.skus()) return list(with_cart_excluded.distinct()) def calculate_discount(self, discount): """ Calculates the discount based on the items in a cart, some might have the discount, others might not. """ # Discount applies to cart total if not product specific. products = discount.all_products() if products.count() == 0: return discount.calculate(self.total_price()) total = Decimal("0") # Create a list of skus in the cart that are applicable to # the discount, and total the discount for appllicable items. lookup = {"product__in": products, "sku__in": self.skus()} discount_variations = ProductVariation.objects.filter(**lookup) discount_skus = discount_variations.values_list("sku", flat=True) for item in self: if item.sku in discount_skus: total += discount.calculate(item.unit_price) * item.quantity return total
class Cart(models.Model): last_updated = models.DateTimeField(_("Last updated"), null=True) objects = managers.CartManager() def __iter__(self): """ Allow the cart to be iterated giving access to the cart's items, ensuring the items are only retrieved once and cached. """ if not hasattr(self, "_cached_items"): self._cached_items = self.items.all() return iter(self._cached_items) def add_item(self, variation, quantity, from_date=None, to_date=None): """ Increase quantity of existing item if SKU matches, otherwise create new. """ kwargs = {"sku": variation.sku, "unit_price": variation.price()} if variation.product.content_model == 'reservableproduct': # create always kwargs["cart"] = self item = CartItem(**kwargs) created = True else: item, created = self.items.get_or_create(**kwargs) if created: item.description = force_text(variation) item.unit_price = variation.price() item.url = variation.product.get_absolute_url() image = variation.image if image is not None: item.image = force_text(image.file) variation.product.actions.added_to_cart() item.quantity += quantity item.from_date = from_date item.to_date = to_date item.save() if variation.product.content_model == 'reservableproduct': # we also create a reservable cart reservation for date in utils.daterange(from_date, to_date): reservableproduct = ReservableProduct.objects.get( product_ptr=variation.product.id) reservation = ReservableProductReservation( date=date, product=reservableproduct) reservation.save() reservation_cart = ReservableProductCartReservation( cart=self, reservation=reservation) reservation_cart.save() def has_items(self): """ Template helper function - does the cart have items? """ return len(list(self)) > 0 def total_quantity(self): """ Template helper function - sum of all item quantities. """ return sum([item.quantity for item in self]) def total_price(self): """ Template helper function - sum of all costs of item quantities. """ total = sum([item.total_price for item in self]) for special in self.special_prices(): total += special[2] return total def skus(self): """ Returns a list of skus for items in the cart. Used by ``upsell_products`` and ``calculate_discount``. """ return [item.sku for item in self] def upsell_products(self): """ Returns the upsell products for each of the items in the cart. """ if not settings.SHOP_USE_UPSELL_PRODUCTS: return [] cart = Product.objects.filter(variations__sku__in=self.skus()) published_products = Product.objects.published() for_cart = published_products.filter(upsell_products__in=cart) with_cart_excluded = for_cart.exclude(variations__sku__in=self.skus()) return list(with_cart_excluded.distinct()) def calculate_discount(self, discount): """ Calculates the discount based on the items in a cart, some might have the discount, others might not. """ # Discount applies to cart total if not product specific. products = discount.all_products() if products.count() == 0: return discount.calculate(self.total_price()) total = Decimal("0") # Create a list of skus in the cart that are applicable to # the discount, and total the discount for appllicable items. lookup = {"product__in": products, "sku__in": self.skus()} discount_variations = ProductVariation.objects.filter(**lookup) discount_skus = discount_variations.values_list("sku", flat=True) for item in self: if item.sku in discount_skus: total += discount.calculate(item.unit_price) * item.quantity return total def special_prices(self): """ Get all the special prices that affect this cart. """ result = [] items_done = [] for item in self: variation = ProductVariation.objects.get(sku=item.sku) if variation.product.content_model == 'reservableproduct' and item in items_done: # only do each item once for reservables continue specials = SpecialPrice.objects.filter(product=variation.product, special_type='WKD') for special in specials: # for reservables we check against days in reserved period # for other products we check against purchasing date if variation.product.content_model == 'reservableproduct': reserved_days = ReservableProductCartReservation.objects.filter( cart=self) for reservation in reserved_days: reservableproduct = ReservableProduct.objects.get( product_ptr=variation.product.id) if reservableproduct == reservation.reservation.product: if reservation.reservation.date.isoweekday() in [ 5, 6 ]: # reservation occurs on a weekend date result.append( (variation.product.title, 'WKD', special.price_change, _("Weekend"))) else: if datetime.date.today().isoweekday() in [5, 6]: # now occurs on a weekend date result.append((variation.product.title, 'WKD', special.price_change, _("Weekend"))) if variation.product.content_model == 'reservableproduct': reserved_days = ReservableProductCartReservation.objects.filter( cart=self) for reservation in reserved_days: reservableproduct = ReservableProduct.objects.get( product_ptr=variation.product.id) if reservableproduct == reservation.reservation.product: try: special = SpecialPrice.objects.get( product=variation.product, special_type='PER', from_date__lte=reservation.reservation.date, to_date__gte=reservation.reservation.date) result.append( (variation.product.title, 'PER', special.price_change, special.title)) except SpecialPrice.DoesNotExist: pass items_done.append(item) return result def has_reservables(self): for item in self: if item.from_date and item.to_date: return True return False