from decimal import Decimal from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.exceptions import ValidationError from django.http import Http404, HttpRequest, HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.http import url_has_allowed_host_and_scheme from django.views.decorators.http import require_POST from items.models import Item from orders.models import CartItem from orders.services import ( add_item_to_cart, format_currency, get_user_cart, remove_item_from_cart, update_cart_item_quantity, ) def resolve_next_url(request: HttpRequest, fallback: str) -> str: next_url = request.POST.get("next") or fallback if not url_has_allowed_host_and_scheme( next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ): return fallback return next_url @login_required def cart(request: HttpRequest) -> HttpResponse: cart_items: list[CartItem] = [] total_quantity = 0 total_amount = "0,00 руб." cart_obj = get_user_cart(request.user) if cart_obj: cart_items = list( cart_obj.items.select_related("item__category", "item__subcategory").prefetch_related("item__images") ) total_quantity = sum(cart_item.quantity for cart_item in cart_items) total_amount_value = sum( (cart_item.item.price * cart_item.quantity for cart_item in cart_items), start=Decimal("0.00"), ) total_amount = format_currency(total_amount_value) return render(request, "orders/cart.html", { "cart_items": cart_items, "total_quantity": total_quantity, "total_amount": total_amount, }) @login_required @require_POST def add(request: HttpRequest) -> HttpResponse: item = get_object_or_404(Item, pk=request.POST.get("item_id")) try: _, created = add_item_to_cart(request.user, item) if created: messages.success(request, f"Товар «{item.name}» добавлен в корзину.") else: messages.info(request, f"Товар «{item.name}» уже есть в корзине.") except ValidationError as error: messages.error(request, error.message) return redirect(resolve_next_url(request, reverse("orders:cart"))) @login_required @require_POST def remove(request: HttpRequest) -> HttpResponse: item = get_object_or_404(Item, pk=request.POST.get("item_id")) removed = remove_item_from_cart(request.user, item.id) if removed: messages.success(request, f"Товар «{item.name}» удалён из корзины.") else: messages.info(request, f"Товар «{item.name}» уже отсутствует в корзине.") return redirect(resolve_next_url(request, reverse("orders:cart"))) @login_required @require_POST def update_quantity(request: HttpRequest) -> HttpResponse: item = get_object_or_404(Item, pk=request.POST.get("item_id")) try: quantity = int(request.POST.get("quantity")) update_cart_item_quantity(request.user, item.id, quantity) messages.success(request, f"Количество товара «{item.name}» обновлено.") except (TypeError, ValueError): messages.error(request, "Некорректное количество товара.") except CartItem.DoesNotExist as error: raise Http404() from error except ValidationError as error: messages.error(request, error.message) return redirect(resolve_next_url(request, reverse("orders:cart")))