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.forms import CheckoutForm from orders.models import CartItem, Order from orders.services import ( add_item_to_cart, calculate_checkout_totals, create_order_from_cart, format_currency, get_user_cart_items, remove_item_from_cart, send_order_confirmation_email, 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 = get_user_cart_items(request.user) total_quantity = sum(cart_item.quantity for cart_item in cart_items) totals = calculate_checkout_totals(cart_items, Order.DeliveryMethod.COURIER) form = CheckoutForm(initial={ "customer_name": request.user.get_full_name() or request.user.get_username(), "email": request.user.email, }) return render(request, "orders/cart.html", { "cart_items": cart_items, "checkout_form": form, "total_quantity": total_quantity, "products_total_value": totals["products_total"], "products_total": format_currency(totals["products_total"]), "discount_total": format_currency(totals["discount_total"]), "delivery_total": format_currency(totals["delivery_total"]), "floor_lift_total": format_currency(totals["floor_lift_total"]), "total_amount": format_currency(totals["total"]), }) @login_required @require_POST def checkout(request: HttpRequest) -> HttpResponse: cart_items = get_user_cart_items(request.user) if not cart_items: messages.error(request, "Корзина пуста. Добавьте товары перед оформлением заказа.") return redirect("orders:cart") form = CheckoutForm(request.POST) if not form.is_valid(): total_quantity = sum(cart_item.quantity for cart_item in cart_items) delivery_method = request.POST.get("delivery_method") or Order.DeliveryMethod.COURIER totals = calculate_checkout_totals(cart_items, delivery_method) return render(request, "orders/cart.html", { "cart_items": cart_items, "checkout_form": form, "total_quantity": total_quantity, "products_total_value": totals["products_total"], "products_total": format_currency(totals["products_total"]), "discount_total": format_currency(totals["discount_total"]), "delivery_total": format_currency(totals["delivery_total"]), "floor_lift_total": format_currency(totals["floor_lift_total"]), "total_amount": format_currency(totals["total"]), }, status=400) try: order = create_order_from_cart(request.user, form.cleaned_data) except ValidationError as error: messages.error(request, error.message) return redirect("orders:cart") try: send_order_confirmation_email(order) except Exception: messages.error(request, "Заказ создан, но письмо с деталями не удалось отправить. Мы свяжемся с вами.") else: messages.success(request, "Оплата прошла успешно. Детали заказа отправлены на почту.") return redirect("orders:success", order_id=order.id) @login_required def success(request: HttpRequest, order_id: int) -> HttpResponse: order = get_object_or_404( Order.objects.prefetch_related("items"), pk=order_id, user=request.user, ) return render(request, "orders/success.html", {"order": order}) @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")))