generated from westfarn/web_django_template
## Summary - Slim the public contact form to email, interest, and message. Name, phone, and address live on the customer profile instead. - Customers can register, sign in, save shipping details, and view order history. Logged-in checkout creates a Stripe Customer and saves cards on Stripe (`setup_future_usage`); we only store `stripe_customer_id`. - Shipment tracking: EasyPost tracker lookup + webhook, plus paste-in numbers from Pirate Ship/Shippo. Customers see carrier status on their orders; `dispatch_due` refreshes open shipments. - Product reviews (1–5) only after a paid/fulfilled purchase of that product. Fixes #7 ## Test plan - [ ] Contact form submits with only email + message; extra name/phone/address fields are ignored - [ ] Register, sign in, save profile (name/phone/shipping) - [ ] Guest checkout still works; after signup, prior orders with that email show in history - [ ] Logged-in checkout prefills shipping and does not collect card data locally - [ ] Portal: buy label or paste a Pirate Ship tracking number, confirm status/events; customer order page shows tracking - [ ] Product page: non-buyers cannot review; buyers can leave one 1–5 star review - [ ] Non-staff users hitting `/portal/` redirect to `/account/` Reviewed-on: #8
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
from decimal import Decimal
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.core import mail
|
|
from django.test import Client, TestCase
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from events.models import Event, Ticket, TicketOrder
|
|
from events.services import EventsError, create_ticket_order, mark_paid
|
|
|
|
|
|
def _event(**kwargs):
|
|
defaults = dict(
|
|
title="Friday Night Prerelease",
|
|
starts_at=timezone.now() + timezone.timedelta(days=7),
|
|
venue="Main shop",
|
|
capacity=8,
|
|
price=Decimal("25.00"),
|
|
is_published=True,
|
|
)
|
|
defaults.update(kwargs)
|
|
return Event.objects.create(**defaults)
|
|
|
|
|
|
class EventPublicTests(TestCase):
|
|
def test_list_hides_drafts_and_past(self):
|
|
_event(title="Live prerelease")
|
|
_event(title="Draft night", is_published=False)
|
|
_event(
|
|
title="Last week",
|
|
starts_at=timezone.now() - timezone.timedelta(days=2),
|
|
)
|
|
response = Client().get(reverse("events:list"))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Live prerelease")
|
|
self.assertNotContains(response, "Draft night")
|
|
self.assertNotContains(response, "Last week")
|
|
|
|
def test_buy_form_renders(self):
|
|
event = _event()
|
|
response = Client().get(reverse("events:buy", kwargs={"slug": event.slug}))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Pay with Stripe")
|
|
|
|
|
|
class EventCapacityTests(TestCase):
|
|
def test_capacity_blocks_overbook(self):
|
|
event = _event(capacity=2)
|
|
create_ticket_order(event, email="a@example.com", quantity=2)
|
|
with self.assertRaises(EventsError):
|
|
create_ticket_order(event, email="b@example.com", quantity=1)
|
|
|
|
def test_mark_paid_issues_tickets_and_email(self):
|
|
event = _event(capacity=4)
|
|
order = create_ticket_order(
|
|
event, email="fan@example.com", customer_name="Lee", quantity=2
|
|
)
|
|
mark_paid(order)
|
|
order.refresh_from_db()
|
|
self.assertEqual(order.status, TicketOrder.Status.PAID)
|
|
self.assertEqual(Ticket.objects.filter(order=order).count(), 2)
|
|
self.assertEqual(event.tickets_sold, 2)
|
|
self.assertEqual(len(mail.outbox), 1)
|
|
self.assertIn(event.title, mail.outbox[0].subject)
|
|
|
|
|
|
class EventPortalTests(TestCase):
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user("host", password="test-pass-123", is_staff=True)
|
|
self.client = Client()
|
|
self.client.login(username="host", password="test-pass-123")
|
|
|
|
def test_list_requires_login(self):
|
|
self.assertEqual(Client().get(reverse("events_portal:event_list")).status_code, 302)
|
|
|
|
def test_create_published_event(self):
|
|
starts = (timezone.now() + timezone.timedelta(days=3)).strftime("%Y-%m-%dT%H:%M")
|
|
response = self.client.post(
|
|
reverse("events_portal:event_new"),
|
|
{
|
|
"title": "Commander night",
|
|
"starts_at": starts,
|
|
"venue": "Back room",
|
|
"capacity": "16",
|
|
"price": "10.00",
|
|
"is_published": "on",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 302)
|
|
event = Event.objects.get()
|
|
self.assertEqual(event.slug, "commander-night")
|
|
self.assertTrue(event.is_published)
|
|
self.assertEqual(event.capacity, 16)
|