Implements bulk-creation action for memberships and orders

This commit is contained in:
Benjamin Bach 2024-07-28 18:38:00 +02:00
parent 3193cafe4b
commit 2499c3227c
No known key found for this signature in database
GPG key ID: 486F0D69C845416E
2 changed files with 47 additions and 12 deletions

View file

@ -2,10 +2,15 @@
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
@ -47,12 +52,34 @@ def decorate_ensure_membership_type_exists(membership_type: models.MembershipTyp
return admin_action
@transaction.atomic
def ensure_membership_type_exists(
request: HttpRequest, # noqa: ARG001
queryset: QuerySet, # noqa: ARG001
membership_type: models.MembershipType, # noqa: ARG001
request: HttpRequest,
queryset: QuerySet,
membership_type: models.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)
# 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
models.Membership.objects.create(
membership_type=membership_type,
user=member,
period=models.SubscriptionPeriod.objects.current(),
order=order,
)
# Associate the order with that membership
messages.success(request, f"{member} has ordered a '{membership_type}' (unpaid)")
@admin.register(models.Member)
@ -65,11 +92,18 @@ class MemberAdmin(UserAdmin):
def get_actions(self, request: HttpRequest) -> dict:
"""Populate actions with dynamic data (MembershipType)."""
current_period = models.SubscriptionPeriod.objects.current()
super_dict = super().get_actions(request)
if current_period:
for mtype in models.MembershipType.objects.filter(active=True):
for i, mtype in enumerate(models.MembershipType.objects.filter(active=True)):
action_label = f"Ensure membership {mtype.name}, {current_period.period}, {mtype.total_including_vat}"
self.actions.append(decorate_ensure_membership_type_exists(mtype, action_label))
return super().get_actions(request)
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(models.WaitingListEntry)

View file

@ -99,16 +99,17 @@ class Membership(CreatedModifiedAbstract):
"""Filter memberships for a given member."""
return self.filter(user=member)
def active(self) -> Self:
"""Get only activated, non-revoked memberships (may have expired so use also current())."""
return self.filter(activated=True, revoked=False)
def _current(self) -> Self:
"""Filter memberships for the current period."""
return self.filter(activated=True, revoked=False, period__period__contains=timezone.now())
return self.filter(period__period__contains=timezone.now())
def current(self) -> "Membership | None":
"""Get the current membership."""
try:
return self._current().get()
except self.model.DoesNotExist:
return None
return self._current().first()
def previous(self) -> list["Membership"]:
"""Get previous memberships."""
@ -119,7 +120,7 @@ class Membership(CreatedModifiedAbstract):
objects = QuerySet.as_manager()
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
user = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="memberships")
membership_type = models.ForeignKey(
"membership.MembershipType",