from django.shortcuts import render, get_object_or_404, redirect from django.db.models import Q from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required from .models import Card, Game, Set, Cart, CartItem, CardListing, PackListing, VirtualPack, Order, OrderItem import random def card_list(request): cards = Card.objects.all().select_related('set', 'set__game').prefetch_related('listings') # Filtering game_slug = request.GET.get('game') if game_slug: cards = cards.filter(set__game__slug=game_slug) search_query = request.GET.get('q') if search_query: cards = cards.filter(name__icontains=search_query) set_id = request.GET.get('set') if set_id: cards = cards.filter(set__id=set_id) # Simple logic: only show cards that have listings or show all? # Let's show all for browsing, but indicate stock. paginator = Paginator(cards.order_by('name'), 24) # 24 cards per page page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) games = Game.objects.all() # If a game is selected, getting its sets for the filter dropdown sets = Set.objects.filter(game__slug=game_slug) if game_slug else Set.objects.all()[:50] # Limit default sets return render(request, 'store/card_list.html', { 'page_obj': page_obj, 'games': games, 'sets': sets, 'current_game': game_slug, 'search_query': search_query, }) def card_detail(request, card_id): card = get_object_or_404(Card, id=card_id) listings = card.listings.filter(quantity__gt=0).order_by('price') return render(request, 'store/card_detail.html', {'card': card, 'listings': listings}) @login_required def add_to_cart(request, listing_id): listing = get_object_or_404(CardListing, id=listing_id) cart, _ = Cart.objects.get_or_create(user=request.user) cart_item, created = CartItem.objects.get_or_create(cart=cart, listing=listing) if not created: cart_item.quantity += 1 cart_item.save() return redirect('store:cart') @login_required def cart_view(request): try: cart = request.user.cart except Cart.DoesNotExist: cart = None return render(request, 'store/cart.html', {'cart': cart}) @login_required def remove_from_cart(request, item_id): if hasattr(request.user, 'cart'): item = get_object_or_404(CartItem, id=item_id, cart=request.user.cart) item.delete() return redirect('store:cart') @login_required def toggle_insurance(request): if hasattr(request.user, 'cart'): cart = request.user.cart cart.insurance = not cart.insurance cart.save() return redirect('store:cart') from django.http import JsonResponse def get_card_stock(request, card_id): card = get_object_or_404(Card, id=card_id) listings = card.listings.all() stock_breakdown = {} total_stock = 0 for listing in listings: stock_breakdown[listing.get_condition_display()] = listing.quantity total_stock += listing.quantity return JsonResponse({ 'card_id': card.id, 'total_stock': total_stock, 'breakdown': stock_breakdown }) from .utils import parse_deck_list, find_best_listings_for_deck, get_user_collection, filter_deck_by_collection, add_to_vault @login_required def deck_buyer(request): if request.method == 'POST': action = request.POST.get('action') if action == 'preview': deck_text = request.POST.get('deck_text') ignore_owned = request.POST.get('ignore_owned') == 'on' parsed = parse_deck_list(deck_text) if ignore_owned and request.user.is_authenticated: owned = get_user_collection(request.user) parsed = filter_deck_by_collection(parsed, owned) found, missing = find_best_listings_for_deck(parsed) total_cost = sum(item['total'] for item in found) return render(request, 'store/deck_buyer.html', { 'found_items': found, 'missing_items': missing, 'deck_text': deck_text, 'total_cost': total_cost, 'preview': True, 'ignore_owned': ignore_owned }) elif action == 'add_to_cart': # Re-parse or rely on hidden fields? # Re-parsing is safer/easier for now than passing complex data deck_text = request.POST.get('deck_text') parsed = parse_deck_list(deck_text) found, _ = find_best_listings_for_deck(parsed) cart, _ = Cart.objects.get_or_create(user=request.user) count = 0 for item in found: listing = item['listing'] qty = item['quantity'] # Check stock again? "Live stock" if listing.quantity >= qty: cart_item, created = CartItem.objects.get_or_create(cart=cart, listing=listing) if not created: cart_item.quantity += qty else: cart_item.quantity = qty cart_item.save() count += 1 return redirect('store:cart') return render(request, 'store/deck_buyer.html') from .models import Bounty def bounty_board(request): bounties = Bounty.objects.filter(is_active=True).select_related('card', 'card__set').order_by('-created_at') return render(request, 'store/bounty_board.html', {'bounties': bounties}) def pack_list(request): packs = PackListing.objects.all() return render(request, 'store/pack_list.html', {'packs': packs}) @login_required def add_pack_to_cart(request, pack_listing_id): listing = get_object_or_404(PackListing, id=pack_listing_id) cart, _ = Cart.objects.get_or_create(user=request.user) cart_item, created = CartItem.objects.get_or_create(cart=cart, pack_listing=listing) if not created: cart_item.quantity += 1 cart_item.save() return redirect('store:cart') @login_required def checkout(request): try: cart = request.user.cart except Cart.DoesNotExist: return redirect('store:cart') if not cart.items.exists(): return redirect('store:cart') # Create Order order = Order.objects.create( user=request.user, status='paid', total_price=cart.total_price ) # Move items for item in cart.items.all(): OrderItem.objects.create( order=order, listing=item.listing, pack_listing=item.pack_listing, price_at_purchase=item.listing.price if item.listing else item.pack_listing.price, quantity=item.quantity ) # Add single cards to vault if item.listing: add_to_vault(request.user, item.listing.card, item.quantity) # If it's a pack, assign VirtualPacks to user if item.pack_listing: # Find available sealed packs available_packs = list(VirtualPack.objects.filter( listing=item.pack_listing, owner__isnull=True, status='sealed' )[:item.quantity]) # If not enough, create more if len(available_packs) < item.quantity: needed = item.quantity - len(available_packs) game = item.pack_listing.game all_game_cards = list(Card.objects.filter(set__game=game)) if not all_game_cards: # Fallback if no cards? Should not happen due to management command or basic setup pass for _ in range(needed): pack = VirtualPack.objects.create(listing=item.pack_listing) if all_game_cards: pack.cards.set(random.sample(all_game_cards, min(len(all_game_cards), 5))) available_packs.append(pack) for pack in available_packs: pack.owner = request.user pack.save() # Clear cart cart.items.all().delete() return redirect('store:my_packs') @login_required def my_packs(request): packs = VirtualPack.objects.filter(owner=request.user, status='sealed').select_related('listing') return render(request, 'store/my_packs.html', {'packs': packs}) @login_required def open_pack(request, pack_id): pack = get_object_or_404(VirtualPack, id=pack_id, owner=request.user) if request.method == 'POST': if pack.status == 'sealed': pack.status = 'opened' pack.save() # Add cards to vault for card in pack.cards.all(): add_to_vault(request.user, card) data = { 'cards': [{ 'name': c.name, 'image_url': c.image_url, 'rarity': c.rarity, 'set': c.set.name } for c in pack.cards.all()] } return JsonResponse(data) return render(request, 'store/open_pack.html', {'pack': pack}) @login_required def order_detail(request, order_id): order = get_object_or_404(Order, id=order_id) # Security check: only allow viewing own orders (unless superuser) if order.user != request.user and not request.user.is_superuser: return redirect('users:profile') return render(request, 'store/order_detail.html', {'order': order})