2024-07-15 00:19:37 +02:00
|
|
|
"""Admin configuration for membership app."""
|
|
|
|
|
2024-08-03 17:55:32 +00:00
|
|
|
from collections.abc import Callable
|
|
|
|
|
|
|
|
from accounting.models import Account
|
|
|
|
from accounting.models import Order
|
|
|
|
from accounting.models import OrderProduct
|
2018-06-23 21:08:56 +02:00
|
|
|
from django.contrib import admin
|
2024-08-03 17:55:32 +00:00
|
|
|
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
|
2018-06-23 21:08:56 +02:00
|
|
|
|
2024-08-03 17:55:32 +00:00
|
|
|
from .models import Member
|
2021-02-28 23:03:32 +01:00
|
|
|
from .models import Membership
|
|
|
|
from .models import MembershipType
|
2023-01-02 22:13:25 +01:00
|
|
|
from .models import SubscriptionPeriod
|
2024-07-31 22:49:46 +00:00
|
|
|
from .models import WaitingListEntry
|
2018-06-23 21:08:56 +02:00
|
|
|
|
2024-08-03 17:55:32 +00:00
|
|
|
# Do not use existing user admin
|
|
|
|
admin.site.unregister(User)
|
|
|
|
|
2018-06-23 21:08:56 +02:00
|
|
|
|
2021-02-28 23:00:11 +01:00
|
|
|
@admin.register(Membership)
|
2018-06-23 21:08:56 +02:00
|
|
|
class MembershipAdmin(admin.ModelAdmin):
|
2024-07-15 00:19:37 +02:00
|
|
|
"""Admin for Membership model."""
|
2021-02-28 23:00:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
@admin.register(MembershipType)
|
|
|
|
class MembershipTypeAdmin(admin.ModelAdmin):
|
2024-07-15 00:19:37 +02:00
|
|
|
"""Admin for MembershipType model."""
|
2023-01-02 22:13:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
@admin.register(SubscriptionPeriod)
|
|
|
|
class SubscriptionPeriodAdmin(admin.ModelAdmin):
|
2024-07-15 00:19:37 +02:00
|
|
|
"""Admin for SubscriptionPeriod model."""
|
2024-07-31 22:49:46 +00:00
|
|
|
|
|
|
|
|
2024-08-03 17:55:32 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2024-07-31 22:49:46 +00:00
|
|
|
@admin.register(WaitingListEntry)
|
|
|
|
class WaitingListEntryAdmin(admin.ModelAdmin):
|
|
|
|
"""Admin for WaitingList model."""
|