Last bit of major changes

Closes #1
Closes #5
Closes #6
Closes #8
Closes #9
Closes #10
This commit is contained in:
2026-01-26 04:11:38 -06:00
parent 1cd87156bd
commit 739d136209
24 changed files with 1157 additions and 410 deletions

View File

@@ -1,4 +1,5 @@
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import messages
from django.conf import settings
from django.db.models import Q
from django.core.paginator import Paginator
@@ -6,7 +7,7 @@ from django.contrib.auth.decorators import login_required
from .models import Card, Game, Set, Cart, CartItem, CardListing, PackListing, VirtualPack, Order, OrderItem, Seller, Bounty, BountyOffer, SellerReport
from django.db.models import Sum, Value
from django.db.models.functions import Coalesce, Length
from .forms import SellerRegistrationForm, CardListingForm, PackListingForm, AddCardListingForm, SellerEditForm, BountyForm, BountyOfferForm, BulkListingForm
from .forms import SellerRegistrationForm, CardListingForm, PackListingForm, AddCardListingForm, SellerEditForm, BountyForm, BountyOfferForm, BulkListingForm, CheckoutForm
from django.utils.text import slugify
import random
import csv
@@ -367,6 +368,11 @@ def bounty_create(request):
return redirect('store:seller_register')
seller = request.user.seller_profile
# Check for profile completion
if not seller.tax_id or not seller.payout_details:
messages.error(request, "You must complete your seller profile (Tax ID and Payout Details) before posting a bounty.")
return redirect('store:edit_seller_profile')
if request.method == 'POST':
form = BountyForm(request.POST)
@@ -512,109 +518,116 @@ def checkout(request):
if not cart.items.exists():
return redirect('store:cart')
# Group items by seller and check for virtual packs
items_by_seller = {}
has_virtual_packs = False
for item in cart.items.all():
if item.pack_listing and item.pack_listing.listing_type == 'virtual':
has_virtual_packs = True
seller = None
if item.listing and item.listing.seller:
seller = item.listing.seller
elif item.pack_listing and item.pack_listing.seller:
seller = item.pack_listing.seller
if request.method == 'POST':
form = CheckoutForm(request.POST, user=request.user)
if form.is_valid():
shipping_address = form.cleaned_data['shipping_address']
if seller not in items_by_seller:
items_by_seller[seller] = []
items_by_seller[seller].append(item)
# Group items by seller and check for virtual packs
items_by_seller = {}
has_virtual_packs = False
for item in cart.items.all():
if item.pack_listing and item.pack_listing.listing_type == 'virtual':
has_virtual_packs = True
seller = None
if item.listing and item.listing.seller:
seller = item.listing.seller
elif item.pack_listing and item.pack_listing.seller:
seller = item.pack_listing.seller
if seller not in items_by_seller:
items_by_seller[seller] = []
items_by_seller[seller].append(item)
# Process orders per seller
for seller, items in items_by_seller.items():
sub_total = sum(item.total_price for item in items)
shipping_cost = 0
if seller:
if sub_total < seller.minimum_order_amount:
shipping_cost = seller.shipping_cost
total_price = sub_total + shipping_cost
# Format Address Snapshot
addr_str = f"{shipping_address.name}\n{shipping_address.street}\n{shipping_address.city}, {shipping_address.state} {shipping_address.zip_code}"
# Create Order (status paid for MVP)
order = Order.objects.create(
buyer=request.user.buyer_profile,
status='paid',
total_price=total_price,
seller=seller,
shipping_address=addr_str
)
for item in items:
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
)
# 1. Handle Card Listings
if item.listing:
add_to_vault(request.user.buyer_profile, item.listing.card, item.quantity)
# Decrement Stock
if item.listing.quantity >= item.quantity:
item.listing.quantity -= item.quantity
item.listing.save()
else:
item.listing.quantity = 0
item.listing.save()
# 2. Handle Pack Listings
if item.pack_listing:
# Decrement Stock
if item.pack_listing.quantity >= item.quantity:
item.pack_listing.quantity -= item.quantity
item.pack_listing.save()
else:
item.pack_listing.quantity = 0
item.pack_listing.save()
# 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 ONLY if it's a system pack (no seller)
if len(available_packs) < item.quantity:
if item.pack_listing.seller:
pass
else:
needed = item.quantity - len(available_packs)
game = item.pack_listing.game
all_game_cards = list(Card.objects.filter(set__game=game))
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.buyer_profile
pack.save()
# Clear cart
cart.items.all().delete()
if has_virtual_packs:
return redirect('store:my_packs')
return redirect('users:vault')
else:
form = CheckoutForm(user=request.user)
# Process orders per seller
for seller, items in items_by_seller.items():
sub_total = sum(item.total_price for item in items)
shipping_cost = 0
if seller:
if sub_total < seller.minimum_order_amount:
shipping_cost = seller.shipping_cost
total_price = sub_total + shipping_cost
# Create Order (status paid for MVP)
order = Order.objects.create(
buyer=request.user.buyer_profile,
status='paid',
total_price=total_price,
seller=seller # Populate seller
)
for item in items:
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
)
# 1. Handle Card Listings
if item.listing:
add_to_vault(request.user.buyer_profile, item.listing.card, item.quantity)
# Decrement Stock
if item.listing.quantity >= item.quantity:
item.listing.quantity -= item.quantity
item.listing.save()
else:
# Stock issue handling (for now just take what's left or allow negative?
# Ideally check before checkout. Assuming check happened at add-to-cart or cart-view)
item.listing.quantity = 0
item.listing.save()
# 2. Handle Pack Listings
if item.pack_listing:
# Decrement Stock
if item.pack_listing.quantity >= item.quantity:
item.pack_listing.quantity -= item.quantity
item.pack_listing.save()
else:
item.pack_listing.quantity = 0
item.pack_listing.save()
# 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 ONLY if it's a system pack (no seller) or configured to do so
if len(available_packs) < item.quantity:
# Seller packs must be pre-filled
if item.pack_listing.seller:
# We only fulfill what we have.
# Ideally we should have caught this at cart validation.
pass
else:
needed = item.quantity - len(available_packs)
game = item.pack_listing.game
all_game_cards = list(Card.objects.filter(set__game=game))
for _ in range(needed):
pack = VirtualPack.objects.create(listing=item.pack_listing)
if all_game_cards:
# Sample logic (mock)
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.buyer_profile
pack.save()
# Clear cart
cart.items.all().delete()
if has_virtual_packs:
return redirect('store:my_packs')
return redirect('users:vault')
return render(request, 'store/checkout.html', {'form': form, 'cart': cart})
@login_required
def my_packs(request):
@@ -699,6 +712,27 @@ def seller_register(request):
seller.user = request.user
seller.slug = slugify(seller.store_name)
seller.save()
# Create Address and link to seller
from users.models import Address
street = seller_form.cleaned_data.get('street')
city = seller_form.cleaned_data.get('city')
state = seller_form.cleaned_data.get('state')
zip_code = seller_form.cleaned_data.get('zip_code')
if street and city:
address = Address.objects.create(
user=request.user,
name=seller.store_name,
street=street,
city=city,
state=state,
zip_code=zip_code,
address_type='shipping'
)
seller.store_address = address
seller.save()
return redirect('store:seller_dashboard')
else:
user_form = CustomUserCreationForm(request.POST)
@@ -712,6 +746,27 @@ def seller_register(request):
seller.user = user
seller.slug = slugify(seller.store_name)
seller.save()
# Create Address and link to seller
from users.models import Address
street = seller_form.cleaned_data.get('street')
city = seller_form.cleaned_data.get('city')
state = seller_form.cleaned_data.get('state')
zip_code = seller_form.cleaned_data.get('zip_code')
if street and city:
address = Address.objects.create(
user=user,
name=seller.store_name,
street=street,
city=city,
state=state,
zip_code=zip_code,
address_type='shipping'
)
seller.store_address = address
seller.save()
return redirect('store:seller_dashboard')
else:
if request.user.is_authenticated:
@@ -735,10 +790,9 @@ def edit_seller_profile(request):
if request.method == 'POST':
form = SellerEditForm(request.POST, request.FILES, instance=seller)
if form.is_valid():
seller = form.save(commit=False)
if 'store_name' in form.changed_data:
seller.slug = slugify(seller.store_name)
seller.save()
form.instance.slug = slugify(form.cleaned_data['store_name'])
form.save()
return redirect('store:seller_profile', slug=seller.slug)
else:
form = SellerEditForm(instance=seller)
@@ -1057,7 +1111,7 @@ import csv # Added import
import io # Added import
# ... existing imports ...
from .forms import SellerRegistrationForm, CardListingForm, PackListingForm, AddCardListingForm, SellerEditForm, BountyForm, BountyOfferForm, BulkListingForm
from .forms import SellerRegistrationForm, CardListingForm, PackListingForm, AddCardListingForm, SellerEditForm, BountyForm, BountyOfferForm, BulkListingForm, CheckoutForm
# ... [Keep existing code until add_card_listing] ...
@@ -1093,6 +1147,11 @@ def add_card_listing(request):
except Seller.DoesNotExist:
return redirect('store:seller_register')
# Check for profile completion
if not seller.tax_id or not seller.payout_details:
messages.error(request, "You must complete your seller profile (Tax ID and Payout Details) before listing items.")
return redirect('store:edit_seller_profile')
bulk_form = BulkListingForm() # Initialize bulk form
if request.method == 'POST':
@@ -1244,6 +1303,11 @@ def add_pack_listing(request):
if not settings.FEATURE_VIRTUAL_PACKS:
return redirect('store:manage_listings')
seller = request.user.seller_profile
# Check for profile completion
if not seller.tax_id or not seller.payout_details:
messages.error(request, "You must complete your seller profile (Tax ID and Payout Details) before listing items.")
return redirect('store:edit_seller_profile')
bulk_form = BulkListingForm()