forked from data.coop/membersystem
Compare commits
2 commits
3193cafe4b
...
5c5153adb6
Author | SHA1 | Date | |
---|---|---|---|
Benjamin Bach | 5c5153adb6 | ||
Benjamin Bach | 2499c3227c |
|
@ -4,6 +4,8 @@ from django.conf import settings
|
|||
from django.core.mail import send_mail
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from membership.models import Membership
|
||||
|
||||
from . import models
|
||||
|
||||
|
@ -19,3 +21,19 @@ def check_total_amount(sender: models.Payment, instance: models.Payment, **kwarg
|
|||
settings.DEFAULT_FROM_EMAIL,
|
||||
settings.ADMINS,
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.Payment)
|
||||
def mark_order_paid(sender: models.Payment, instance: models.Payment, **kwargs: dict) -> None: # noqa: ARG001
|
||||
"""Mark an order as paid when payment is received."""
|
||||
instance.order.is_paid = True
|
||||
instance.order.save()
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.Payment)
|
||||
def activate_membership(sender: models.Order, instance: models.Order, **kwargs: dict) -> None: # noqa: ARG001
|
||||
"""Mark a membership as activated when its order is marked as paid."""
|
||||
if instance.is_paid:
|
||||
Membership.objects.filter(order=instance, activated=False, activated_on=None).update(
|
||||
activated=True, activated_on=timezone.now()
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue