Source code for django_program.registration.admin

"""Django admin configuration for the registration app."""

from django.contrib import admin
from django.http import HttpRequest  # noqa: TC002

from django_program.registration.models import (
    AddOn,
    Cart,
    CartItem,
    Credit,
    EventProcessingException,
    Order,
    OrderLineItem,
    Payment,
    StripeCustomer,
    StripeEvent,
    TicketType,
    Voucher,
)


[docs] @admin.register(TicketType) class TicketTypeAdmin(admin.ModelAdmin): """Admin interface for managing ticket types. Provides filtering by conference and active status, search by name and slug, and auto-population of the slug from the ticket name. """ list_display = ("name", "conference", "price", "is_active", "order") list_filter = ("conference", "is_active") search_fields = ("name", "slug") prepopulated_fields = {"slug": ("name",)}
[docs] @admin.register(AddOn) class AddOnAdmin(admin.ModelAdmin): """Admin interface for managing add-ons. Uses ``filter_horizontal`` for the ``requires_ticket_types`` many-to-many field to provide a friendlier selection widget. """ list_display = ("name", "conference", "price", "is_active") list_filter = ("conference", "is_active") search_fields = ("name", "slug") prepopulated_fields = {"slug": ("name",)} filter_horizontal = ("requires_ticket_types",)
[docs] @admin.register(Voucher) class VoucherAdmin(admin.ModelAdmin): """Admin interface for managing vouchers. Displays usage counts alongside the voucher configuration and allows filtering by conference, type, and active status. """ list_display = ( "code", "conference", "voucher_type", "discount_value", "times_used", "max_uses", "is_active", ) list_filter = ("conference", "voucher_type", "is_active") search_fields = ("code",) filter_horizontal = ("applicable_ticket_types", "applicable_addons")
[docs] class CartItemInline(admin.TabularInline): """Inline display of cart items within the cart admin. Items are shown as read-only since they are managed through the storefront, not directly in the admin. """ model = CartItem extra = 0 readonly_fields = ("ticket_type", "addon", "quantity")
[docs] @admin.register(Cart) class CartAdmin(admin.ModelAdmin): """Admin interface for viewing shopping carts. Carts are primarily managed by the storefront; the admin provides a read-oriented view with inline cart items. """ list_display = ("user", "conference", "status", "voucher", "expires_at") list_filter = ("conference", "status") inlines = (CartItemInline,)
[docs] class OrderLineItemInline(admin.TabularInline): """Inline display of order line items within the order admin. Line items are immutable snapshots from checkout and are shown read-only. """ model = OrderLineItem extra = 0 readonly_fields = ( "description", "quantity", "unit_price", "discount_amount", "line_total", "ticket_type", "addon", )
[docs] class PaymentInline(admin.TabularInline): """Inline display of payments within the order admin.""" model = Payment extra = 0
[docs] @admin.register(Order) class OrderAdmin(admin.ModelAdmin): """Admin interface for managing orders. Displays order reference, user, status, and financial totals. Money fields are read-only to prevent accidental edits; changes should flow through the payment and refund workflows instead. """ list_display = ("reference", "user", "conference", "status", "total", "created_at") list_filter = ("conference", "status") search_fields = ("reference", "user__email", "billing_email") readonly_fields = ("subtotal", "discount_amount", "total") inlines = (OrderLineItemInline, PaymentInline)
[docs] @admin.register(Credit) class CreditAdmin(admin.ModelAdmin): """Admin interface for managing store credits. Provides filtering by conference and credit status, and displays the amount and creation date at a glance. """ list_display = ("user", "conference", "amount", "status", "created_at") list_filter = ("conference", "status")
[docs] @admin.register(StripeCustomer) class StripeCustomerAdmin(admin.ModelAdmin): """Read-only admin for Stripe customer mappings.""" list_display = ("user", "conference", "stripe_customer_id", "created_at") list_filter = ("conference",) search_fields = ("user__email", "stripe_customer_id") readonly_fields = ("user", "conference", "stripe_customer_id", "created_at")
[docs] def has_add_permission(self, request: HttpRequest) -> bool: # noqa: ARG002, D102 return False
[docs] def has_change_permission(self, request: HttpRequest, obj: StripeCustomer | None = None) -> bool: # noqa: ARG002, D102 return False
[docs] def has_delete_permission(self, request: HttpRequest, obj: StripeCustomer | None = None) -> bool: # noqa: ARG002, D102 return False
[docs] @admin.register(StripeEvent) class StripeEventAdmin(admin.ModelAdmin): """Read-only admin for Stripe webhook events.""" list_display = ("stripe_id", "kind", "processed", "livemode", "created_at") list_filter = ("kind", "processed", "livemode") search_fields = ("stripe_id", "customer_id") readonly_fields = ( "stripe_id", "kind", "livemode", "payload", "customer_id", "processed", "api_version", "created_at", )
[docs] def has_add_permission(self, request: HttpRequest) -> bool: # noqa: ARG002, D102 return False
[docs] def has_change_permission(self, request: HttpRequest, obj: StripeEvent | None = None) -> bool: # noqa: ARG002, D102 return False
[docs] def has_delete_permission(self, request: HttpRequest, obj: StripeEvent | None = None) -> bool: # noqa: ARG002, D102 return False
[docs] @admin.register(EventProcessingException) class EventProcessingExceptionAdmin(admin.ModelAdmin): """Read-only admin for webhook processing errors.""" list_display = ("message", "event", "created_at") list_filter = ("created_at",) search_fields = ("message",) readonly_fields = ("event", "data", "message", "traceback", "created_at")
[docs] def has_add_permission(self, request: HttpRequest) -> bool: # noqa: ARG002, D102 return False
[docs] def has_change_permission(self, request: HttpRequest, obj: EventProcessingException | None = None) -> bool: # noqa: ARG002, D102 return False
[docs] def has_delete_permission(self, request: HttpRequest, obj: EventProcessingException | None = None) -> bool: # noqa: ARG002, D102 return False