# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from django.db.transaction import atomic
from django.http.response import JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, ListView, View
from django.views.generic.detail import SingleObjectMixin
from shuup.core.excs import ProductNotOrderableProblem
from shuup.core.models import OrderLineType, Product, ShopProduct
from shuup.core.utils.users import real_user_or_none
from shuup.front.models import StoredBasket
from shuup.front.views.dashboard import DashboardViewMixin
from shuup.utils.django_compat import force_text
[docs]class CartViewMixin(object):
model = StoredBasket
[docs] def get_queryset(self):
qs = super(CartViewMixin, self).get_queryset()
return qs.filter(persistent=True, deleted=False, customer=self.request.customer, shop=self.request.shop)
[docs]class CartListView(DashboardViewMixin, CartViewMixin, ListView):
template_name = "shuup/saved_carts/cart_list.jinja"
context_object_name = "carts"
[docs]class CartDetailView(DashboardViewMixin, CartViewMixin, DetailView):
template_name = "shuup/saved_carts/cart_detail.jinja"
context_object_name = "cart"
[docs] def get_queryset(self):
qs = super(CartDetailView, self).get_queryset()
return qs.prefetch_related("products")
[docs] def get_context_data(self, **kwargs):
context = super(CartDetailView, self).get_context_data(**kwargs)
lines = []
product_dict = {}
for product in self.object.products.all():
product_dict[product.id] = product
for line in self.object.data.get("lines", []):
if line.get("type", None) != OrderLineType.PRODUCT:
continue
product = product_dict[line["product_id"]]
quantity = line.get("quantity", 0)
lines.append(
{
"product": product,
"quantity": quantity,
}
)
context["lines"] = lines
return context
[docs]class CartSaveView(View):
[docs] def post(self, request, *args, **kwargs):
title = request.POST.get("title", "")
basket = request.basket
if not request.customer:
return JsonResponse({"ok": False}, status=403)
if not title:
return JsonResponse({"ok": False, "error": force_text(_("Please enter a basket title."))}, status=400)
if basket.is_empty:
return JsonResponse({"ok": False, "error": force_text(_("Can't save an empty basket."))}, status=400)
saved_basket = StoredBasket(
shop=basket.shop,
customer=basket.customer,
orderer=basket.orderer,
creator=real_user_or_none(basket.creator),
currency=basket.currency,
prices_include_tax=basket.prices_include_tax,
persistent=True,
title=title,
data=basket.storage.load(basket=basket),
product_count=basket.smart_product_count,
)
saved_basket.save()
saved_basket.products.set(set(basket.product_ids))
return JsonResponse({"ok": True}, status=200)
[docs]class CartAddAllProductsView(CartViewMixin, SingleObjectMixin, View):
[docs] def get_object(self):
return get_object_or_404(self.get_queryset(), pk=self.kwargs.get("pk"))
def _get_supplier(self, shop_product, supplier_id, customer, quantity, shipping_address):
if supplier_id:
supplier = shop_product.suppliers.enabled(shop=shop_product.shop).filter(pk=supplier_id).first()
else:
supplier = shop_product.get_supplier(customer, quantity, shipping_address)
return supplier
@atomic
[docs] def post(self, request, *args, **kwargs):
cart = self.get_object()
basket = request.basket
product_ids_to_quantities = basket.get_product_ids_and_quantities()
errors = []
quantity_added = 0
for line in cart.data.get("lines", []):
if line.get("type", None) != OrderLineType.PRODUCT:
continue
product = Product.objects.get(id=line.get("product_id", None))
try:
shop_product = product.get_shop_instance(shop=request.shop)
except ShopProduct.DoesNotExist:
errors.append({"product": line.text, "message": _("Product is not available in this shop.")})
continue
supplier = self._get_supplier(
shop_product, line.get("supplier_id"), basket.customer, line.get("quantity"), basket.shipping_address
)
if not supplier:
errors.append({"product": line.text, "message": _("Invalid supplier.")})
continue
try:
quantity = line.get("quantity", 0)
quantity_added += quantity
product_quantity = quantity + product_ids_to_quantities.get(line["product_id"], 0)
shop_product.raise_if_not_orderable(
supplier=supplier, quantity=product_quantity, customer=request.customer
)
basket.add_product(supplier=supplier, shop=request.shop, product=product, quantity=quantity)
except ProductNotOrderableProblem as e:
errors.append({"product": line["text"], "message": force_text(e.message)})
return JsonResponse(
{"errors": errors, "success": force_text(_("%d product(s) added to cart." % quantity_added))}, status=200
)
[docs]class CartDeleteView(CartViewMixin, SingleObjectMixin, View):
[docs] def post(self, request, *args, **kwargs):
cart = self.get_object()
cart.deleted = True
cart.save()
return JsonResponse({"status": "success"}, status=200)