951 lines
35 KiB
Python
951 lines
35 KiB
Python
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.views.generic import ListView, DetailView
|
|
from django.contrib import messages
|
|
from django.contrib.auth import login
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db.models import Q, Avg, Count
|
|
from django.db import transaction
|
|
# from rest_framework import viewsets # Temporär auskommentiert
|
|
# from rest_framework.permissions import IsAuthenticatedOrReadOnly # Temporär auskommentiert
|
|
from .models import Product, Cart, CartItem, Order, OrderItem, Review, UserProfile, Wishlist, FAQ, ContactMessage, CustomOrder, OrderProgress, GalleryImage, Category
|
|
from .forms import OrderForm, CustomUserCreationForm, CustomAuthenticationForm, ReviewForm, UserProfileForm, ContactForm, CustomOrderForm, OrderProgressForm
|
|
# from .serializers import ProductSerializer, ReviewSerializer # Temporär auskommentiert
|
|
import stripe
|
|
from django.conf import settings
|
|
from django.http import JsonResponse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.utils import timezone
|
|
from django.core.mail import send_mail
|
|
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
|
|
import json
|
|
<<<<<<< HEAD
|
|
# import paypalrestsdk # Temporär auskommentiert
|
|
# from payments import get_payment_model, RedirectNeeded # Temporär auskommentiert
|
|
# from paypal.standard.ipn.models import PayPalIPN # Temporär auskommentiert
|
|
from django.urls import reverse
|
|
from django.http import HttpResponse
|
|
# from paypal.standard.forms import PayPalPaymentsForm # Temporär auskommentiert
|
|
import logging
|
|
from django.core.exceptions import ValidationError
|
|
from django.http import Http404
|
|
|
|
logger = logging.getLogger(__name__)
|
|
=======
|
|
import paypalrestsdk
|
|
from payments import get_payment_model, RedirectNeeded
|
|
from paypal.standard.ipn.models import PayPalIPN
|
|
from django.urls import reverse
|
|
from django.http import HttpResponse
|
|
from paypal.standard.forms import PayPalPaymentsForm
|
|
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
|
|
|
|
# Create your views here.
|
|
|
|
class ProductListView(ListView):
|
|
model = Product
|
|
template_name = 'products/product_list.html'
|
|
context_object_name = 'products'
|
|
paginate_by = 12
|
|
|
|
def get_queryset(self):
|
|
<<<<<<< HEAD
|
|
try:
|
|
queryset = Product.objects.all().annotate(
|
|
average_rating=Avg('reviews__rating'),
|
|
review_count=Count('reviews')
|
|
).select_related('category').prefetch_related(
|
|
'reviews__user', # Optimiert N+1 Queries für Reviews
|
|
'wishlist_users' # Optimiert Wishlist-Queries
|
|
)
|
|
|
|
# Erweiterte Suchfilter
|
|
search_query = self.request.GET.get('search')
|
|
if search_query:
|
|
# Verbesserte Suche mit Q-Objekten
|
|
search_terms = search_query.split()
|
|
q_objects = Q()
|
|
for term in search_terms:
|
|
q_objects |= (
|
|
Q(name__icontains=term) |
|
|
Q(description__icontains=term) |
|
|
Q(fursuit_type__icontains=term) |
|
|
Q(style__icontains=term) |
|
|
Q(category__name__icontains=term)
|
|
)
|
|
queryset = queryset.filter(q_objects)
|
|
|
|
# Preisbereich Filter mit Validierung
|
|
min_price = self.request.GET.get('min_price')
|
|
max_price = self.request.GET.get('max_price')
|
|
if min_price and min_price.isdigit():
|
|
queryset = queryset.filter(price__gte=float(min_price))
|
|
if max_price and max_price.isdigit():
|
|
queryset = queryset.filter(price__lte=float(max_price))
|
|
|
|
# Fursuit-Typ Filter
|
|
fursuit_type = self.request.GET.get('fursuit_type')
|
|
if fursuit_type:
|
|
queryset = queryset.filter(fursuit_type=fursuit_type)
|
|
|
|
# Style Filter
|
|
style = self.request.GET.get('style')
|
|
if style:
|
|
queryset = queryset.filter(style=style)
|
|
|
|
# Kategorie Filter
|
|
category = self.request.GET.get('category')
|
|
if category:
|
|
queryset = queryset.filter(category__slug=category)
|
|
|
|
# Verfügbarkeit Filter
|
|
availability = self.request.GET.get('availability')
|
|
if availability == 'in_stock':
|
|
queryset = queryset.filter(stock__gt=0)
|
|
elif availability == 'low_stock':
|
|
queryset = queryset.filter(stock__lte=5, stock__gt=0)
|
|
elif availability == 'out_of_stock':
|
|
queryset = queryset.filter(stock=0)
|
|
|
|
# Sortierung mit verbesserter Performance
|
|
sort = self.request.GET.get('sort', '-created')
|
|
if sort == 'price':
|
|
queryset = queryset.order_by('price')
|
|
elif sort == '-price':
|
|
queryset = queryset.order_by('-price')
|
|
elif sort == 'name':
|
|
queryset = queryset.order_by('name')
|
|
elif sort == '-name':
|
|
queryset = queryset.order_by('-name')
|
|
elif sort == '-rating':
|
|
queryset = queryset.order_by('-average_rating')
|
|
elif sort == 'popularity':
|
|
queryset = queryset.order_by('-review_count')
|
|
else: # Default: Neueste zuerst
|
|
queryset = queryset.order_by('-created')
|
|
|
|
return queryset.distinct()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in ProductListView.get_queryset: {e}")
|
|
return Product.objects.none()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
try:
|
|
context = super().get_context_data(**kwargs)
|
|
# Zusätzliche Context-Daten
|
|
context['categories'] = Category.objects.all()
|
|
context['fursuit_types'] = Product.FURSUIT_TYPE_CHOICES
|
|
context['styles'] = Product.STYLE_CHOICES
|
|
return context
|
|
except Exception as e:
|
|
logger.error(f"Error in ProductListView.get_context_data: {e}")
|
|
return super().get_context_data(**kwargs)
|
|
=======
|
|
queryset = Product.objects.all().annotate(
|
|
average_rating=Avg('reviews__rating'),
|
|
review_count=Count('reviews')
|
|
).select_related('category').prefetch_related('reviews')
|
|
|
|
# Erweiterte Suchfilter
|
|
search_query = self.request.GET.get('search')
|
|
if search_query:
|
|
# Verbesserte Suche mit Q-Objekten
|
|
search_terms = search_query.split()
|
|
q_objects = Q()
|
|
for term in search_terms:
|
|
q_objects |= (
|
|
Q(name__icontains=term) |
|
|
Q(description__icontains=term) |
|
|
Q(fursuit_type__icontains=term) |
|
|
Q(style__icontains=term) |
|
|
Q(category__name__icontains=term)
|
|
)
|
|
queryset = queryset.filter(q_objects)
|
|
|
|
# Preisbereich Filter mit Validierung
|
|
min_price = self.request.GET.get('min_price')
|
|
max_price = self.request.GET.get('max_price')
|
|
if min_price and min_price.isdigit():
|
|
queryset = queryset.filter(price__gte=float(min_price))
|
|
if max_price and max_price.isdigit():
|
|
queryset = queryset.filter(price__lte=float(max_price))
|
|
|
|
# Fursuit-Typ Filter
|
|
fursuit_type = self.request.GET.get('fursuit_type')
|
|
if fursuit_type:
|
|
queryset = queryset.filter(fursuit_type=fursuit_type)
|
|
|
|
# Style Filter
|
|
style = self.request.GET.get('style')
|
|
if style:
|
|
queryset = queryset.filter(style=style)
|
|
|
|
# Kategorie Filter
|
|
category = self.request.GET.get('category')
|
|
if category:
|
|
queryset = queryset.filter(category__slug=category)
|
|
|
|
# Verfügbarkeit Filter
|
|
availability = self.request.GET.get('availability')
|
|
if availability == 'in_stock':
|
|
queryset = queryset.filter(stock__gt=0)
|
|
elif availability == 'low_stock':
|
|
queryset = queryset.filter(stock__lte=5, stock__gt=0)
|
|
elif availability == 'out_of_stock':
|
|
queryset = queryset.filter(stock=0)
|
|
|
|
# Sortierung mit verbesserter Performance
|
|
sort = self.request.GET.get('sort', '-created')
|
|
if sort == 'price':
|
|
queryset = queryset.order_by('price')
|
|
elif sort == '-price':
|
|
queryset = queryset.order_by('-price')
|
|
elif sort == 'name':
|
|
queryset = queryset.order_by('name')
|
|
elif sort == '-name':
|
|
queryset = queryset.order_by('-name')
|
|
elif sort == '-rating':
|
|
queryset = queryset.order_by('-average_rating')
|
|
elif sort == 'popularity':
|
|
queryset = queryset.order_by('-review_count')
|
|
else: # Default: Neueste zuerst
|
|
queryset = queryset.order_by('-created')
|
|
|
|
return queryset.distinct()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
# Kategorien für Filter mit Anzahl
|
|
context['categories'] = Category.objects.annotate(
|
|
product_count=Count('product_items')
|
|
).filter(product_count__gt=0)
|
|
context['selected_category'] = self.request.GET.get('category')
|
|
|
|
# Fursuit-Typen und Styles für Filter
|
|
context['fursuit_types'] = Product.FURSUIT_TYPE_CHOICES
|
|
context['fursuit_styles'] = Product.STYLE_CHOICES
|
|
|
|
# Filter-Werte
|
|
context['min_price'] = self.request.GET.get('min_price')
|
|
context['max_price'] = self.request.GET.get('max_price')
|
|
context['selected_type'] = self.request.GET.get('fursuit_type')
|
|
context['selected_style'] = self.request.GET.get('style')
|
|
context['search_query'] = self.request.GET.get('search')
|
|
context['sort'] = self.request.GET.get('sort', '-created')
|
|
context['availability'] = self.request.GET.get('availability')
|
|
|
|
# Statistiken für Filter
|
|
context['total_products'] = Product.objects.count()
|
|
context['in_stock_count'] = Product.objects.filter(stock__gt=0).count()
|
|
context['low_stock_count'] = Product.objects.filter(stock__lte=5, stock__gt=0).count()
|
|
|
|
return context
|
|
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
|
|
|
|
class ProductDetailView(DetailView):
|
|
model = Product
|
|
template_name = 'products/product_detail.html'
|
|
context_object_name = 'product'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
if self.request.user.is_authenticated:
|
|
context['user_has_reviewed'] = Review.objects.filter(
|
|
product=self.object,
|
|
user=self.request.user
|
|
).exists()
|
|
context['review_form'] = ReviewForm()
|
|
return context
|
|
|
|
def get_or_create_cart(request):
|
|
if request.user.is_authenticated:
|
|
cart, created = Cart.objects.get_or_create(user=request.user)
|
|
else:
|
|
session_key = request.session.session_key
|
|
if not session_key:
|
|
request.session.create()
|
|
session_key = request.session.session_key
|
|
cart, created = Cart.objects.get_or_create(session_id=session_key)
|
|
return cart
|
|
|
|
def add_to_cart(request, product_id):
|
|
if request.method == 'POST':
|
|
product = get_object_or_404(Product, id=product_id)
|
|
quantity = int(request.POST.get('quantity', 1))
|
|
|
|
if quantity > product.stock:
|
|
messages.error(request, 'Nicht genügend Artikel auf Lager!')
|
|
return redirect('products:product_detail', pk=product_id)
|
|
|
|
cart = get_or_create_cart(request)
|
|
cart_item, created = CartItem.objects.get_or_create(
|
|
cart=cart,
|
|
product=product,
|
|
defaults={'quantity': quantity}
|
|
)
|
|
|
|
if not created:
|
|
cart_item.quantity += quantity
|
|
cart_item.save()
|
|
|
|
messages.success(request, f'{quantity}x {product.name} wurde zum Warenkorb hinzugefügt.')
|
|
return redirect('products:cart_detail')
|
|
|
|
def cart_detail(request):
|
|
cart = get_or_create_cart(request)
|
|
return render(request, 'products/cart_detail.html', {'cart': cart})
|
|
|
|
def create_order(request):
|
|
if request.method == 'POST':
|
|
cart = get_or_create_cart(request)
|
|
if not cart.items.exists():
|
|
messages.error(request, 'Ihr Warenkorb ist leer.')
|
|
return redirect('products:cart_detail')
|
|
|
|
# Erstelle eine neue Bestellung
|
|
order = Order.objects.create(
|
|
user=request.user if request.user.is_authenticated else None,
|
|
full_name=f"{request.user.first_name} {request.user.last_name}" if request.user.is_authenticated else "",
|
|
email=request.user.email if request.user.is_authenticated else "",
|
|
total_amount=cart.get_total()
|
|
)
|
|
|
|
# Füge die Artikel aus dem Warenkorb zur Bestellung hinzu
|
|
for cart_item in cart.items.all():
|
|
OrderItem.objects.create(
|
|
order=order,
|
|
product=cart_item.product,
|
|
product_name=cart_item.product.name,
|
|
quantity=cart_item.quantity,
|
|
price=cart_item.product.price
|
|
)
|
|
|
|
# Leere den Warenkorb
|
|
cart.items.all().delete()
|
|
|
|
messages.success(request, 'Ihre Bestellung wurde erfolgreich erstellt.')
|
|
return redirect('products:checkout', order_id=order.id)
|
|
|
|
return redirect('products:cart_detail')
|
|
|
|
@login_required
|
|
def checkout(request, order_id):
|
|
order = get_object_or_404(Order, id=order_id)
|
|
if request.user.is_authenticated and order.user != request.user:
|
|
messages.error(request, 'Sie haben keine Berechtigung, diese Bestellung einzusehen.')
|
|
return redirect('products:product_list')
|
|
|
|
# PayPal Zahlungsformular erstellen
|
|
host = request.get_host()
|
|
|
|
paypal_dict = {
|
|
"business": settings.PAYPAL_RECEIVER_EMAIL,
|
|
"amount": str(order.total_amount),
|
|
"item_name": f"Bestellung #{order.id}",
|
|
"invoice": str(order.id),
|
|
"currency_code": "EUR",
|
|
"notify_url": request.build_absolute_uri('/paypal/'),
|
|
"return_url": request.build_absolute_uri(f'/products/payment/success/{order.id}/'),
|
|
"cancel_return": request.build_absolute_uri(f'/products/payment/failed/{order.id}/'),
|
|
}
|
|
|
|
form = PayPalPaymentsForm(initial=paypal_dict)
|
|
return render(request, 'products/checkout.html', {
|
|
'order': order,
|
|
'form': form
|
|
})
|
|
|
|
@csrf_exempt
|
|
@login_required
|
|
def create_paypal_order(request):
|
|
if request.method != 'POST':
|
|
return JsonResponse({'error': 'Nur POST-Anfragen erlaubt'}, status=405)
|
|
|
|
data = json.loads(request.body)
|
|
order = get_object_or_404(Order, id=data['order_id'], user=request.user)
|
|
|
|
payment = paypalrestsdk.Payment({
|
|
"intent": "sale",
|
|
"payer": {
|
|
"payment_method": "paypal"
|
|
},
|
|
"transactions": [{
|
|
"amount": {
|
|
"total": str(order.total_amount),
|
|
"currency": "EUR"
|
|
},
|
|
"description": f"Bestellung #{order.id}"
|
|
}],
|
|
"redirect_urls": {
|
|
"return_url": f"{settings.SITE_URL}/products/order/confirmation/{order.id}/",
|
|
"cancel_url": f"{settings.SITE_URL}/products/checkout/{order.id}/"
|
|
}
|
|
})
|
|
|
|
if payment.create():
|
|
return JsonResponse({'paypal_order_id': payment.id})
|
|
else:
|
|
return JsonResponse({'error': payment.error}, status=400)
|
|
|
|
@csrf_exempt
|
|
@login_required
|
|
def capture_paypal_order(request):
|
|
if request.method != 'POST':
|
|
return JsonResponse({'error': 'Nur POST-Anfragen erlaubt'}, status=405)
|
|
|
|
data = json.loads(request.body)
|
|
order = get_object_or_404(Order, id=data['order_id'], user=request.user)
|
|
payment_id = data['paypal_order_id']
|
|
|
|
payment = paypalrestsdk.Payment.find(payment_id)
|
|
|
|
if payment.execute({'payer_id': payment.payer.payer_info.payer_id}):
|
|
order.status = 'paid'
|
|
order.payment_id = payment_id
|
|
order.save()
|
|
|
|
# E-Mail-Bestätigung senden
|
|
send_payment_confirmation_email(order)
|
|
|
|
return JsonResponse({'success': True})
|
|
else:
|
|
return JsonResponse({'error': payment.error}, status=400)
|
|
|
|
def order_confirmation(request, order_id):
|
|
order = get_object_or_404(Order, id=order_id)
|
|
if request.user.is_authenticated and order.user != request.user:
|
|
messages.error(request, 'Sie haben keine Berechtigung, diese Bestellung einzusehen.')
|
|
return redirect('products:product_list')
|
|
|
|
return render(request, 'products/order_confirmation.html', {'order': order})
|
|
|
|
def update_cart_item(request, item_id):
|
|
if request.method == 'POST':
|
|
cart_item = get_object_or_404(CartItem, id=item_id)
|
|
quantity = int(request.POST.get('quantity', 1))
|
|
|
|
if quantity > cart_item.product.stock:
|
|
messages.error(request, 'Nicht genügend Artikel auf Lager!')
|
|
elif quantity > 0:
|
|
cart_item.quantity = quantity
|
|
cart_item.save()
|
|
messages.success(request, 'Warenkorb wurde aktualisiert.')
|
|
else:
|
|
cart_item.delete()
|
|
messages.success(request, 'Artikel wurde aus dem Warenkorb entfernt.')
|
|
|
|
return redirect('products:cart_detail')
|
|
|
|
def remove_from_cart(request, item_id):
|
|
if request.method == 'POST':
|
|
cart_item = get_object_or_404(CartItem, id=item_id)
|
|
cart_item.delete()
|
|
messages.success(request, 'Artikel wurde aus dem Warenkorb entfernt.')
|
|
return redirect('products:cart_detail')
|
|
|
|
def register(request):
|
|
if request.method == 'POST':
|
|
form = CustomUserCreationForm(request.POST)
|
|
if form.is_valid():
|
|
user = form.save()
|
|
login(request, user)
|
|
messages.success(request, 'Registrierung erfolgreich!')
|
|
return redirect('products:product_list')
|
|
else:
|
|
form = CustomUserCreationForm()
|
|
return render(request, 'registration/register.html', {'form': form})
|
|
|
|
@login_required
|
|
def add_review(request, product_id):
|
|
product = get_object_or_404(Product, id=product_id)
|
|
if request.method == 'POST':
|
|
form = ReviewForm(request.POST)
|
|
if form.is_valid():
|
|
review = form.save(commit=False)
|
|
review.product = product
|
|
review.user = request.user
|
|
review.save()
|
|
messages.success(request, 'Ihre Bewertung wurde erfolgreich hinzugefügt.')
|
|
else:
|
|
messages.error(request, 'Es gab einen Fehler beim Hinzufügen Ihrer Bewertung.')
|
|
return redirect('product_detail', pk=product_id)
|
|
|
|
@login_required
|
|
def profile_view(request):
|
|
if request.method == 'POST':
|
|
form = UserProfileForm(request.POST, instance=request.user.userprofile)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Profil wurde erfolgreich aktualisiert.')
|
|
return redirect('products:profile')
|
|
else:
|
|
form = UserProfileForm(instance=request.user.userprofile)
|
|
|
|
return render(request, 'products/profile.html', {'form': form})
|
|
|
|
@login_required
|
|
def order_history(request):
|
|
orders = Order.objects.filter(user=request.user).order_by('-created')
|
|
return render(request, 'products/order_history.html', {'orders': orders})
|
|
|
|
@login_required
|
|
def wishlist_view(request):
|
|
wishlist, created = Wishlist.objects.get_or_create(user=request.user)
|
|
return render(request, 'products/wishlist.html', {'wishlist': wishlist})
|
|
|
|
@login_required
|
|
def add_to_wishlist(request, product_id):
|
|
product = get_object_or_404(Product, id=product_id)
|
|
wishlist, created = Wishlist.objects.get_or_create(user=request.user)
|
|
wishlist.products.add(product)
|
|
messages.success(request, f'{product.name} wurde zu Ihrer Wunschliste hinzugefügt.')
|
|
return redirect('product_detail', product_id=product_id)
|
|
|
|
@login_required
|
|
def remove_from_wishlist(request, product_id):
|
|
product = get_object_or_404(Product, id=product_id)
|
|
wishlist = get_object_or_404(Wishlist, user=request.user)
|
|
wishlist.products.remove(product)
|
|
messages.success(request, f'{product.name} wurde von Ihrer Wunschliste entfernt.')
|
|
return redirect('wishlist')
|
|
|
|
def faq_list(request):
|
|
faqs = FAQ.objects.all()
|
|
categories = FAQ.objects.values_list('category', flat=True).distinct()
|
|
return render(request, 'products/faq.html', {
|
|
'faqs': faqs,
|
|
'categories': categories
|
|
})
|
|
|
|
def contact(request):
|
|
if request.method == 'POST':
|
|
form = ContactForm(request.POST, user=request.user)
|
|
if form.is_valid():
|
|
contact_message = form.save(commit=False)
|
|
if request.user.is_authenticated:
|
|
contact_message.user = request.user
|
|
contact_message.save()
|
|
messages.success(
|
|
request,
|
|
'Vielen Dank für Ihre Nachricht! Wir werden uns schnellstmöglich bei Ihnen melden.'
|
|
)
|
|
return redirect('contact_success')
|
|
else:
|
|
form = ContactForm(user=request.user)
|
|
|
|
return render(request, 'products/contact.html', {'form': form})
|
|
|
|
def contact_success(request):
|
|
return render(request, 'products/contact_success.html')
|
|
|
|
@login_required
|
|
def custom_order(request):
|
|
if request.method == 'POST':
|
|
form = CustomOrderForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
custom_order = form.save(commit=False)
|
|
custom_order.user = request.user
|
|
custom_order.save()
|
|
|
|
messages.success(
|
|
request,
|
|
'Ihre Anfrage wurde erfolgreich übermittelt. Wir werden uns in Kürze mit einem Angebot bei Ihnen melden.'
|
|
)
|
|
return redirect('custom_order_success', order_id=custom_order.id)
|
|
else:
|
|
form = CustomOrderForm()
|
|
|
|
return render(request, 'products/custom_order.html', {
|
|
'form': form
|
|
})
|
|
|
|
@login_required
|
|
def custom_order_success(request, order_id):
|
|
order = get_object_or_404(CustomOrder, id=order_id, user=request.user)
|
|
return render(request, 'products/custom_order_success.html', {
|
|
'order': order
|
|
})
|
|
|
|
@login_required
|
|
def custom_order_detail(request, order_id):
|
|
order = get_object_or_404(CustomOrder, id=order_id, user=request.user)
|
|
progress_updates = order.progress_updates.all()
|
|
return render(request, 'products/custom_order_detail.html', {
|
|
'order': order,
|
|
'progress_updates': progress_updates
|
|
})
|
|
|
|
def gallery(request):
|
|
# Hole alle Galeriebilder
|
|
images = GalleryImage.objects.all().order_by('order', '-created')
|
|
|
|
# Filter nach Typ
|
|
fursuit_type = request.GET.get('fursuit_type')
|
|
if fursuit_type:
|
|
images = images.filter(fursuit_type=fursuit_type)
|
|
|
|
# Filter nach Stil
|
|
style = request.GET.get('style')
|
|
if style:
|
|
images = images.filter(style=style)
|
|
|
|
# Sortierung
|
|
sort = request.GET.get('sort', 'order')
|
|
if sort == 'newest':
|
|
images = images.order_by('-created')
|
|
elif sort == 'oldest':
|
|
images = images.order_by('created')
|
|
|
|
context = {
|
|
'images': images,
|
|
'fursuit_types': GalleryImage.FURSUIT_TYPE_CHOICES,
|
|
'fursuit_styles': GalleryImage.STYLE_CHOICES,
|
|
'current_type': fursuit_type,
|
|
'current_style': style,
|
|
'current_sort': sort
|
|
}
|
|
|
|
print(f"Anzahl der geladenen Bilder: {images.count()}") # Debug-Ausgabe
|
|
for img in images:
|
|
print(f"Bild: {img.title}, URL: {img.image.url}") # Debug-Ausgabe
|
|
|
|
return render(request, 'products/gallery.html', context)
|
|
|
|
@login_required
|
|
def add_progress_update(request, order_id):
|
|
order = get_object_or_404(CustomOrder, id=order_id)
|
|
if not request.user.is_staff:
|
|
messages.error(request, 'Sie haben keine Berechtigung für diese Aktion.')
|
|
return redirect('custom_order_detail', order_id=order_id)
|
|
|
|
if request.method == 'POST':
|
|
form = OrderProgressForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
progress = form.save(commit=False)
|
|
progress.custom_order = order
|
|
progress.save()
|
|
|
|
messages.success(request, 'Fortschritts-Update wurde hinzugefügt.')
|
|
|
|
# Wenn alle Schritte abgeschlossen sind, setze den Status auf "ready"
|
|
if all(update.completed for update in order.progress_updates.all()):
|
|
order.status = 'ready'
|
|
order.save()
|
|
|
|
return redirect('custom_order_detail', order_id=order_id)
|
|
else:
|
|
form = OrderProgressForm()
|
|
|
|
return render(request, 'products/add_progress.html', {
|
|
'form': form,
|
|
'order': order
|
|
})
|
|
|
|
@login_required
|
|
def dashboard(request):
|
|
# Basis-Informationen
|
|
user_profile = request.user.userprofile
|
|
wishlist = Wishlist.objects.get_or_create(user=request.user)[0]
|
|
cart = get_or_create_cart(request)
|
|
|
|
# Custom Orders mit Fortschritt
|
|
custom_orders = CustomOrder.objects.filter(user=request.user).order_by('-created')
|
|
for order in custom_orders:
|
|
order.progress_percentage = calculate_order_progress(order)
|
|
|
|
# Letzte Bestellungen mit Details
|
|
recent_orders = Order.objects.filter(user=request.user).order_by('-created')[:5]
|
|
for order in recent_orders:
|
|
order.items_count = order.items.count()
|
|
|
|
# Bewertungsübersicht
|
|
reviews = Review.objects.filter(user=request.user).select_related('product').order_by('-created')[:5]
|
|
|
|
# Statistiken
|
|
stats = {
|
|
'total_orders': Order.objects.filter(user=request.user).count(),
|
|
'total_custom_orders': CustomOrder.objects.filter(user=request.user).count(),
|
|
'total_reviews': Review.objects.filter(user=request.user).count(),
|
|
'wishlist_count': wishlist.products.count(),
|
|
}
|
|
|
|
return render(request, 'products/dashboard.html', {
|
|
'user_profile': user_profile,
|
|
'custom_orders': custom_orders,
|
|
'recent_orders': recent_orders,
|
|
'reviews': reviews,
|
|
'stats': stats,
|
|
'wishlist': wishlist,
|
|
'cart': cart,
|
|
})
|
|
|
|
def calculate_order_progress(order):
|
|
"""Berechne den Fortschritt einer Custom Order in Prozent."""
|
|
if order.status == 'cancelled':
|
|
return 0
|
|
elif order.status == 'completed':
|
|
return 100
|
|
|
|
# Status-Gewichtung
|
|
status_weights = {
|
|
'pending': 10,
|
|
'quoted': 20,
|
|
'approved': 30,
|
|
'in_progress': 50,
|
|
'ready': 90,
|
|
'shipped': 95,
|
|
}
|
|
|
|
base_progress = status_weights.get(order.status, 0)
|
|
|
|
# Zusätzlicher Fortschritt basierend auf OrderProgress
|
|
if order.status == 'in_progress':
|
|
completed_stages = order.progress_updates.filter(completed=True).count()
|
|
total_stages = len(OrderProgress.PROGRESS_CHOICES)
|
|
stage_progress = (completed_stages / total_stages) * 40 # 40% für Fortschrittsstufen
|
|
return base_progress + stage_progress
|
|
|
|
return base_progress
|
|
|
|
# API ViewSets - Temporär auskommentiert
|
|
# class ProductViewSet(viewsets.ModelViewSet):
|
|
# queryset = Product.objects.all()
|
|
# serializer_class = ProductSerializer
|
|
# permission_classes = [IsAuthenticatedOrReadOnly]
|
|
|
|
# class ReviewViewSet(viewsets.ModelViewSet):
|
|
# queryset = Review.objects.all()
|
|
# serializer_class = ReviewSerializer
|
|
# permission_classes = [IsAuthenticatedOrReadOnly]
|
|
|
|
# def perform_create(self, serializer):
|
|
# serializer.save(user=self.request.user)
|
|
|
|
# Stripe-Konfiguration
|
|
stripe.api_key = settings.STRIPE_SECRET_KEY
|
|
|
|
def create_payment_intent(request, order_id):
|
|
"""Erstellt einen Stripe Payment Intent für eine Bestellung."""
|
|
try:
|
|
order = get_object_or_404(Order, id=order_id)
|
|
|
|
# Stripe akzeptiert nur Cent-Beträge
|
|
amount = int(order.total_amount * 100)
|
|
|
|
# Payment Intent erstellen
|
|
intent = stripe.PaymentIntent.create(
|
|
amount=amount,
|
|
currency='eur',
|
|
metadata={
|
|
'order_id': order.id,
|
|
'user_id': request.user.id if request.user.is_authenticated else None
|
|
}
|
|
)
|
|
|
|
# Order aktualisieren
|
|
order.stripe_payment_intent_id = intent.id
|
|
order.save()
|
|
|
|
return JsonResponse({
|
|
'clientSecret': intent.client_secret,
|
|
'amount': amount
|
|
})
|
|
except Exception as e:
|
|
return JsonResponse({'error': str(e)}, status=400)
|
|
|
|
def payment_view(request, order_id):
|
|
"""Zeigt die Zahlungsseite an."""
|
|
order = get_object_or_404(Order, id=order_id)
|
|
|
|
# Überprüfen, ob die Bestellung dem Benutzer gehört
|
|
if request.user.is_authenticated and order.user != request.user:
|
|
messages.error(request, 'Sie haben keine Berechtigung für diese Bestellung.')
|
|
return redirect('dashboard')
|
|
|
|
# Überprüfen, ob die Bestellung bereits bezahlt wurde
|
|
if order.payment_status == 'paid':
|
|
messages.info(request, 'Diese Bestellung wurde bereits bezahlt.')
|
|
return redirect('order_detail', order_id=order.id)
|
|
|
|
context = {
|
|
'order': order,
|
|
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
|
|
}
|
|
return render(request, 'products/payment.html', context)
|
|
|
|
@csrf_exempt
|
|
def stripe_webhook(request):
|
|
"""Webhook für Stripe-Events."""
|
|
payload = request.body
|
|
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
|
|
|
|
try:
|
|
event = stripe.Webhook.construct_event(
|
|
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
|
|
)
|
|
except ValueError as e:
|
|
return JsonResponse({'error': 'Invalid payload'}, status=400)
|
|
except stripe.error.SignatureVerificationError as e:
|
|
return JsonResponse({'error': 'Invalid signature'}, status=400)
|
|
|
|
if event.type == 'payment_intent.succeeded':
|
|
payment_intent = event.data.object
|
|
handle_successful_payment(payment_intent)
|
|
elif event.type == 'payment_intent.payment_failed':
|
|
payment_intent = event.data.object
|
|
handle_failed_payment(payment_intent)
|
|
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
def handle_successful_payment(payment_intent):
|
|
"""Verarbeitet erfolgreiche Zahlungen."""
|
|
order_id = payment_intent.metadata.get('order_id')
|
|
if order_id:
|
|
try:
|
|
order = Order.objects.get(id=order_id)
|
|
order.payment_status = 'paid'
|
|
order.payment_date = timezone.now()
|
|
order.status = 'processing'
|
|
order.stripe_payment_method_id = payment_intent.payment_method
|
|
order.save()
|
|
|
|
# E-Mail-Benachrichtigung senden
|
|
send_payment_confirmation_email(order)
|
|
except Order.DoesNotExist:
|
|
print(f"Bestellung {order_id} nicht gefunden")
|
|
|
|
def handle_failed_payment(payment_intent):
|
|
"""Verarbeitet fehlgeschlagene Zahlungen."""
|
|
order_id = payment_intent.metadata.get('order_id')
|
|
if order_id:
|
|
try:
|
|
order = Order.objects.get(id=order_id)
|
|
order.payment_status = 'failed'
|
|
order.save()
|
|
|
|
# E-Mail-Benachrichtigung senden
|
|
send_payment_failed_email(order)
|
|
except Order.DoesNotExist:
|
|
print(f"Bestellung {order_id} nicht gefunden")
|
|
|
|
@login_required
|
|
def payment_success(request, order_id):
|
|
"""Zeigt die Erfolgsseite nach erfolgreicher Zahlung an."""
|
|
order = get_object_or_404(Order, id=order_id, user=request.user)
|
|
messages.success(request, 'Ihre Zahlung wurde erfolgreich verarbeitet!')
|
|
return render(request, 'products/payment_success.html', {'order': order})
|
|
|
|
@login_required
|
|
def payment_failed(request, order_id):
|
|
"""Zeigt die Fehlerseite nach fehlgeschlagener Zahlung an."""
|
|
order = get_object_or_404(Order, id=order_id, user=request.user)
|
|
messages.error(request, 'Die Zahlung konnte nicht verarbeitet werden. Bitte versuchen Sie es erneut.')
|
|
return render(request, 'products/payment_failed.html', {'order': order})
|
|
|
|
# Hilfsfunktionen für E-Mail-Benachrichtigungen
|
|
def send_payment_confirmation_email(order):
|
|
"""Sendet eine Bestätigungs-E-Mail nach erfolgreicher Zahlung."""
|
|
subject = f'Zahlungsbestätigung für Bestellung #{order.id}'
|
|
message = f"""
|
|
Sehr geehrte(r) {order.full_name},
|
|
|
|
vielen Dank für Ihre Zahlung. Ihre Bestellung #{order.id} wurde erfolgreich bezahlt.
|
|
|
|
Bestelldetails:
|
|
- Gesamtbetrag: {order.total_amount} €
|
|
- Zahlungsmethode: {order.get_payment_method_display()}
|
|
- Zahlungsdatum: {order.payment_date.strftime('%d.%m.%Y %H:%M')}
|
|
|
|
Wir werden Ihre Bestellung schnellstmöglich bearbeiten.
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr Fursuit-Shop Team
|
|
"""
|
|
|
|
send_mail(
|
|
subject,
|
|
message,
|
|
settings.DEFAULT_FROM_EMAIL,
|
|
[order.email],
|
|
fail_silently=False,
|
|
)
|
|
|
|
def send_payment_failed_email(order):
|
|
"""Sendet eine Benachrichtigung nach fehlgeschlagener Zahlung."""
|
|
subject = f'Zahlungsproblem bei Bestellung #{order.id}'
|
|
message = f"""
|
|
Sehr geehrte(r) {order.full_name},
|
|
|
|
leider ist die Zahlung für Ihre Bestellung #{order.id} fehlgeschlagen.
|
|
|
|
Sie können die Zahlung unter folgendem Link erneut versuchen:
|
|
{settings.SITE_URL}/payment/{order.id}/
|
|
|
|
Falls Sie Fragen haben, kontaktieren Sie uns bitte.
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr Fursuit-Shop Team
|
|
"""
|
|
|
|
send_mail(
|
|
subject,
|
|
message,
|
|
settings.DEFAULT_FROM_EMAIL,
|
|
[order.email],
|
|
fail_silently=False,
|
|
)
|
|
|
|
@login_required
|
|
def payment_process(request, order_id):
|
|
order = get_object_or_404(Order, id=order_id, user=request.user)
|
|
|
|
if request.method == 'POST':
|
|
# Initiiere PayPal-Zahlung
|
|
paypal_dict = {
|
|
"business": settings.PAYPAL_RECEIVER_EMAIL,
|
|
"amount": str(order.total_amount),
|
|
"item_name": f"Bestellung #{order.id}",
|
|
"invoice": str(order.id),
|
|
"notify_url": request.build_absolute_uri(reverse('paypal-ipn')),
|
|
"return_url": request.build_absolute_uri(reverse('payment_success', args=[order.id])),
|
|
"cancel_return": request.build_absolute_uri(reverse('payment_failed', args=[order.id])),
|
|
"custom": json.dumps({
|
|
"order_id": order.id,
|
|
"user_id": request.user.id,
|
|
})
|
|
}
|
|
|
|
form = PayPalPaymentsForm(initial=paypal_dict)
|
|
return render(request, 'products/payment_process.html', {'order': order, 'form': form})
|
|
|
|
return render(request, 'products/payment_process.html', {'order': order})
|
|
|
|
@csrf_exempt
|
|
def paypal_ipn(request):
|
|
"""PayPal IPN (Instant Payment Notification) Handler"""
|
|
if request.method == "POST":
|
|
try:
|
|
ipn_obj = PayPalIPN(request.POST)
|
|
ipn_obj.verify()
|
|
|
|
if ipn_obj.payment_status == "Completed":
|
|
# Zahlung war erfolgreich
|
|
try:
|
|
order = Order.objects.get(id=ipn_obj.invoice)
|
|
order.status = 'paid'
|
|
order.payment_method = 'paypal'
|
|
order.payment_id = ipn_obj.txn_id
|
|
order.save()
|
|
|
|
# Sende Bestätigungs-E-Mail
|
|
send_payment_confirmation_email(order)
|
|
except Order.DoesNotExist:
|
|
pass
|
|
|
|
return HttpResponse("OK")
|
|
except Exception as e:
|
|
return HttpResponse(str(e))
|
|
|
|
return HttpResponse("OK")
|