Replace ticket qty input with +/- buttons, show sold/basket counts
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 57s

- Entry page shows tickets sold count, remaining, and "in basket" count
- Replace numeric input + Buy button with add-to-basket / +/- controls
- New POST /tickets/adjust/ route creates/cancels tickets to target count
- Keep buy form active after adding (no confirmation replacement)
- New service functions: get_sold_ticket_count, get_user_reserved_count,
  cancel_latest_reserved_ticket

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-21 08:53:12 +00:00
parent 13064c3772
commit 256eb390b0
5 changed files with 392 additions and 55 deletions

View File

@@ -160,13 +160,22 @@ def register():
@bp.context_processor
async def inject_root():
from ..tickets.services.tickets import get_available_ticket_count
from ..tickets.services.tickets import (
get_available_ticket_count,
get_sold_ticket_count,
get_user_reserved_count,
)
from shared.infrastructure.cart_identity import current_cart_identity
from sqlalchemy.orm import selectinload
view_args = getattr(request, "view_args", {}) or {}
entry_id = view_args.get("entry_id")
calendar_entry = None
entry_posts = []
ticket_remaining = None
ticket_sold_count = 0
user_ticket_count = 0
user_ticket_counts_by_type = {}
stmt = (
select(CalendarEntry)
@@ -174,6 +183,7 @@ def register():
CalendarEntry.id == entry_id,
CalendarEntry.deleted_at.is_(None),
)
.options(selectinload(CalendarEntry.ticket_types))
)
result = await g.s.execute(stmt)
calendar_entry = result.scalar_one_or_none()
@@ -190,11 +200,33 @@ def register():
entry_posts = await get_entry_posts(g.s, calendar_entry.id)
# Get ticket availability
ticket_remaining = await get_available_ticket_count(g.s, calendar_entry.id)
# Get sold count
ticket_sold_count = await get_sold_ticket_count(g.s, calendar_entry.id)
# Get current user's reserved count
ident = current_cart_identity()
user_ticket_count = await get_user_reserved_count(
g.s, calendar_entry.id,
user_id=ident["user_id"],
session_id=ident["session_id"],
)
# Per-type counts for multi-type entries
if calendar_entry.ticket_types:
for tt in calendar_entry.ticket_types:
if tt.deleted_at is None:
user_ticket_counts_by_type[tt.id] = await get_user_reserved_count(
g.s, calendar_entry.id,
user_id=ident["user_id"],
session_id=ident["session_id"],
ticket_type_id=tt.id,
)
return {
"entry": calendar_entry,
"entry_posts": entry_posts,
"ticket_remaining": ticket_remaining,
"ticket_sold_count": ticket_sold_count,
"user_ticket_count": user_ticket_count,
"user_ticket_counts_by_type": user_ticket_counts_by_type,
}
@bp.get("/")
@require_admin