"""Admin configuration for membership app.""" from collections.abc import Callable from accounting.models import Account from accounting.models import Order from accounting.models import OrderProduct from django.contrib import admin from django.contrib import messages from django.contrib.admin import ModelAdmin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.db import transaction from django.db.models import QuerySet from django.http import HttpRequest from django.http import HttpResponse from .models import Member from .models import Membership from .models import MembershipType from .models import SubscriptionPeriod from .models import WaitingListEntry # Do not use existing user admin admin.site.unregister(User) @admin.register(Membership) class MembershipAdmin(admin.ModelAdmin): """Admin for Membership model.""" @admin.register(MembershipType) class MembershipTypeAdmin(admin.ModelAdmin): """Admin for MembershipType model.""" @admin.register(SubscriptionPeriod) class SubscriptionPeriodAdmin(admin.ModelAdmin): """Admin for SubscriptionPeriod model.""" class MembershipInlineAdmin(admin.TabularInline): """Inline admin.""" model = Membership def decorate_ensure_membership_type_exists(membership_type: MembershipType, label: str) -> Callable: """Generate an admin action for given membership type and label.""" @admin.action(description=label) def admin_action(modeladmin: ModelAdmin, request: HttpRequest, queryset: QuerySet) -> HttpResponse: # noqa: ARG001 return ensure_membership_type_exists(request, queryset, membership_type) return admin_action @transaction.atomic def ensure_membership_type_exists( request: HttpRequest, queryset: QuerySet, membership_type: MembershipType, ) -> HttpResponse: """Inner function that ensures that a membership exists for a given queryset of Member objects.""" for member in queryset: if member.memberships.filter(membership_type=membership_type).current(): messages.info(request, f"{member} already has a membership {membership_type}") else: # Get the default account of the member. We don't really know what to do if a person owns multiple accounts. account, __ = Account.objects.get_or_create(owner=member) # Create an Order for the products in the membership order = Order.objects.create(member=member, account=account, description=membership_type.name) # Add stuff to the order for product in membership_type.products.all(): OrderProduct.objects.create(order=order, product=product, price=product.price, vat=product.vat) # Create the Membership Membership.objects.create( membership_type=membership_type, user=member, period=SubscriptionPeriod.objects.current(), order=order, ) # Associate the order with that membership messages.success(request, f"{member} has ordered a '{membership_type}' (unpaid)") @admin.register(Member) class MemberAdmin(UserAdmin): """Member admin is actually an admin for User objects.""" inlines = (MembershipInlineAdmin,) actions: list[Callable] = [] # noqa: RUF012 def get_actions(self, request: HttpRequest) -> dict: """Populate actions with dynamic data (MembershipType).""" current_period = SubscriptionPeriod.objects.current() super_dict = super().get_actions(request) if current_period: for i, mtype in enumerate(MembershipType.objects.filter(active=True)): action_label = f"Ensure membership {mtype.name}, {current_period.period}, {mtype.total_including_vat}" action_func = decorate_ensure_membership_type_exists(mtype, action_label) # Django ModelAdmin uses the non-unique __name__ property, so we need to suffix it to make it unique action_func.__name__ += f"_{i}" self.actions.append(action_func) return super_dict @admin.register(WaitingListEntry) class WaitingListEntryAdmin(admin.ModelAdmin): """Admin for WaitingList model."""