Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Extract events/calendar functionality into standalone microservice: - app.py and events_api.py from apps/events/ - Calendar blueprints (calendars, calendar, calendar_entries, calendar_entry, day, slots, slot, ticket_types, ticket_type) - Templates for all calendar/event views including admin - Dockerfile (APP_MODULE=app:app, IMAGE=events) - entrypoint.sh (no Alembic - migrations managed by blog app) - Gitea CI workflow for build and deploy Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
2.3 KiB
Python
88 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
from decimal import Decimal
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from models.calendars import CalendarEntry
|
|
|
|
|
|
async def update_ticket_config(
|
|
session: AsyncSession,
|
|
entry_id: int,
|
|
ticket_price: Optional[Decimal],
|
|
ticket_count: Optional[int],
|
|
) -> tuple[bool, Optional[str]]:
|
|
"""
|
|
Update ticket configuration for a calendar entry.
|
|
|
|
Args:
|
|
session: Database session
|
|
entry_id: Calendar entry ID
|
|
ticket_price: Price per ticket (None = no tickets)
|
|
ticket_count: Total available tickets (None = unlimited)
|
|
|
|
Returns:
|
|
(success, error_message)
|
|
"""
|
|
# Get the entry
|
|
entry = await session.scalar(
|
|
select(CalendarEntry).where(
|
|
CalendarEntry.id == entry_id,
|
|
CalendarEntry.deleted_at.is_(None)
|
|
)
|
|
)
|
|
|
|
if not entry:
|
|
return False, "Calendar entry not found"
|
|
|
|
# Validate inputs
|
|
if ticket_price is not None and ticket_price < 0:
|
|
return False, "Ticket price cannot be negative"
|
|
|
|
if ticket_count is not None and ticket_count < 0:
|
|
return False, "Ticket count cannot be negative"
|
|
|
|
# Update ticket configuration
|
|
entry.ticket_price = ticket_price
|
|
entry.ticket_count = ticket_count
|
|
|
|
return True, None
|
|
|
|
|
|
async def get_available_tickets(
|
|
session: AsyncSession,
|
|
entry_id: int,
|
|
) -> tuple[Optional[int], Optional[str]]:
|
|
"""
|
|
Get the number of available tickets for a calendar entry.
|
|
|
|
Returns:
|
|
(available_count, error_message)
|
|
- available_count is None if unlimited tickets
|
|
- available_count is the remaining count if limited
|
|
"""
|
|
entry = await session.scalar(
|
|
select(CalendarEntry).where(
|
|
CalendarEntry.id == entry_id,
|
|
CalendarEntry.deleted_at.is_(None)
|
|
)
|
|
)
|
|
|
|
if not entry:
|
|
return None, "Calendar entry not found"
|
|
|
|
# If no ticket configuration, return None (unlimited)
|
|
if entry.ticket_price is None:
|
|
return None, None
|
|
|
|
# If ticket_count is None, unlimited tickets
|
|
if entry.ticket_count is None:
|
|
return None, None
|
|
|
|
# TODO: Subtract booked tickets when ticket booking is implemented
|
|
# For now, just return the total count
|
|
return entry.ticket_count, None
|