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 gettext_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:
model = StoredBasket
[docs]
def get_queryset(self):
qs = super().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().get_queryset()
return qs.prefetch_related("products")
[docs]
def get_context_data(self, **kwargs):
context = super().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
[docs]
@atomic
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(_(f"{quantity_added} product(s) added to cart.")),
},
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)