from decimal import Decimal from django.db.models import Sum from django.http.response import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, reverse from django.utils.text import gettext_lazy as _ from django.views.decorators.http import require_http_methods from django.views.generic import CreateView, TemplateView from shop.models import CartItem, Order, OrderLine, Product, ProductCategory, ShippingMethod, WishlistedProduct from shop.redsys import RedsysClient from shop.utils import create_order_from_cart from web.forms import CreateOrderForm from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin from web.models import WebSettings from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME from web.utils import get_or_create_cart class IndexView(TemplateView): template_name = 'web/index.html' def get_context_data(self, **kwargs): settings = WebSettings.load() return {'title': settings.web_title, 'description': settings.web_description} def get(self, request, *args, **kwargs): if self.request.GET.get('next'): return redirect(self.request.GET.get('next')) return super().get(request, *args, **kwargs) class CategoryView(TemplateView): template_name = 'web/index.html' def get_context_data(self, **kwargs): settings = WebSettings.load() slug = kwargs.get('slug') category = ProductCategory.objects.get(slug=slug) return { 'title': _(f'{settings.web_title} - {category.name}'), 'description': settings.web_description, 'category': category, 'filter_by_category': True, } class ProductDetail(TemplateView): template_name = 'web/product_detail.html' def get_context_data(self, pk, slug, **kwargs): settings = WebSettings.load() product = get_object_or_404(Product, pk=pk) return { 'product': product, 'title': f'{settings.web_title} - {product.name}', 'description': product.description, 'image': product.images.first(), } class CartDetail(TemplateView, CreateView): template_name = 'web/cart_detail.html' form_class = CreateOrderForm def get_context_data(self, **kwargs): web_settings = WebSettings.load() cart, created = get_or_create_cart(self.request) cart_items = CartItem.objects.filter(cart=cart).prefetch_related('product') return { 'title': _(f'{web_settings.web_title} - Carrito'), 'cart': cart, 'items': cart_items, 'description': _('resumen del carrito'), } def post(self, request, *args, **kwargs): form = CreateOrderForm(request.POST) if not form.is_valid(): context = self.get_context_data(**kwargs) context.update({'errors': form.errors}) return self.render_to_response(context) shipping_method_id = form.cleaned_data.get('shipping_method') cart, created = get_or_create_cart(request) billing_address_full_name = form.cleaned_data.get('billing_address_full_name') billing_address_address = form.cleaned_data.get('billing_address') billing_address_town = form.cleaned_data.get('billing_address_town') billing_address_state = form.cleaned_data.get('billing_address_state') billing_address_country = form.cleaned_data.get('billing_address_country') billing_address_zip = form.cleaned_data.get('billing_address_zip') shipping_address_full_name = form.cleaned_data.get('shipping_address_full_name') shipping_address_address = form.cleaned_data.get('shipping_address') shipping_address_town = form.cleaned_data.get('shipping_address_town') shipping_address_state = form.cleaned_data.get('shipping_address_state') shipping_address_country = form.cleaned_data.get('shipping_address_country') shipping_address_zip = form.cleaned_data.get('shipping_address_zip') shipping_address_phone = form.cleaned_data.get('shipping_address_phone') email = form.cleaned_data.get('email') shipping_method = get_object_or_404(ShippingMethod, pk=shipping_method_id) order = create_order_from_cart( cart=cart, shipping_method=shipping_method, billing_address_full_name=billing_address_full_name, billing_address_address=billing_address_address, billing_address_town=billing_address_town, billing_address_state=billing_address_state, billing_address_country=billing_address_country, billing_address_zip=billing_address_zip, shipping_address_full_name=shipping_address_full_name, shipping_address_address=shipping_address_address, shipping_address_town=shipping_address_town, shipping_address_state=shipping_address_state, shipping_address_country=shipping_address_country, shipping_address_zip=shipping_address_zip, shipping_address_phone=shipping_address_phone, email=email, ) return redirect(reverse('web:order', kwargs={'uuid': order.uuid})) class OrderDetail(TemplateView): template_name = 'web/order.html' def get_context_data(self, **kwargs): order = get_object_or_404(Order, uuid=kwargs.get('uuid')) redsys_client = RedsysClient() parameters = redsys_client.get_body_for_order(order) items = OrderLine.objects.select_related('product').filter(order=order, product__is_shipping_method=False) shipping_cost = OrderLine.objects.select_related('product').filter( order=order, product__is_shipping_method=True ).aggregate(amount=Sum('total')).get('amount') or Decimal('0') base_total = OrderLine.objects.select_related('product').filter( order=order, product__is_shipping_method=False ).aggregate(amount=Sum('base_total')).get('amount') or Decimal('0') tax_total = OrderLine.objects.select_related('product').filter( order=order, product__is_shipping_method=False ).aggregate(amount=Sum('taxes')).get('amount') or Decimal('0') settings = WebSettings.load() return { 'title': _(f'{settings.web_title} - Comprar'), 'description': _(f'{settings.web_title} - Comprar'), 'order': order, 'items': items, 'signature_version': parameters.get('Ds_SignatureVersion'), 'merchant_parameters': parameters.get('Ds_MerchantParameters'), 'signature': parameters.get('Ds_Signature'), 'redsys_target_url': redsys_client.get_target_url(), 'total': round(order.total, 2), 'base_total': round(base_total, 2), 'tax_total': round(tax_total, 2), 'shipping_cost': round(shipping_cost, 2), } class WishlistView(TemplateView): template_name = 'web/wishlist.html' def get_context_data(self, **kwargs): settings = WebSettings.load() return {'title': _(f'{settings.web_title} - Lista de deseados'), 'description': settings.web_description} class OrdersView(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin): template_name = 'web/orders.html' def get_queryset(self): return Order.objects.filter(user=self.request.user).exclude(status=Order.Statuses.STATUS_PENDING) def get_context_data(self, **kwargs): settings = WebSettings.load() qs = self.get_queryset() page = self.get_paginated_queryset(qs) return { 'title': _(f'{settings.web_title} - Mis pedidos'), 'page': page, 'has_next_page': page.has_next(), 'has_previous_page': page.has_previous(), } @require_http_methods(['POST']) def add_cart_item(request, *args, **kwargs): product = get_object_or_404(Product, pk=request.POST.get('product')) cart, created = get_or_create_cart(request) response = HttpResponse(status=201, headers={'HX-Trigger': 'updated-cart'}) if created and request.user.is_anonymous: response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite='strict') # Comprobamos si existe una línea de carrito para ese carrito de ese producto existing_cart_item = CartItem.objects.filter(cart=cart, product=product).first() # Si existe, simplemente le sumamos la cantidad a la línea ya existente if existing_cart_item is not None: existing_cart_item.quantity += int(request.POST.get('quantity')) existing_cart_item.save() # Si no, lo creamos else: CartItem.objects.create(cart=cart, quantity=request.POST.get('quantity'), product=product) return response @require_http_methods(['POST']) def delete_cart_item(request, pk, *args, **kwargs): cart, created = get_or_create_cart(request) cart_item = get_object_or_404(CartItem, pk=pk, cart=cart) cart_item.delete() return HttpResponse(status=204, headers={'HX-Trigger': 'updated-cart'}) @require_http_methods(['POST']) def add_to_wishlist(request, *args, **kwargs): product = get_object_or_404(Product, pk=request.POST.get('product')) response = HttpResponse(status=201, headers={'HX-Trigger': 'updated-wishlist'}) if request.user.is_anonymous: return HttpResponse(status=400) WishlistedProduct.objects.get_or_create(user=request.user, product=product) return response @require_http_methods(['POST']) def delete_from_wishlist(request, pk, *args, **kwargs): wishlisted_item = get_object_or_404(WishlistedProduct, product_id=pk, user=request.user) wishlisted_item.delete() return HttpResponse(status=204, headers={'HX-Trigger': 'updated-wishlist'}) def manifest(request, *args, **kwargs): settings = WebSettings.load() data = { 'name': settings.web_title, 'short_name': settings.web_title, 'start_url': '/', 'background_color': settings.bg_color, 'theme_color': settings.theme_color, 'icons': [], 'display': 'standalone', 'orientation': 'portrait', } if settings.logo: data['icons'].append({'src': settings.logo.url, 'sizes': '512x512', 'type': 'image/png'}) if settings.logo_256: data['icons'].append({'src': settings.logo_256.url, 'sizes': '256x256', 'type': 'image/png'}) if settings.logo_128: data['icons'].append({'src': settings.logo_128.url, 'sizes': '128x128', 'type': 'image/png'}) return JsonResponse(data) index = IndexView.as_view() category_view = CategoryView.as_view() product_detail = ProductDetail.as_view() cart_detail = CartDetail.as_view() order_detail = OrderDetail.as_view() wishlist = WishlistView.as_view() orders = OrdersView.as_view()