forked from data.coop/membersystem
Compare commits
No commits in common. "5c5153adb6f381d9521afcd96805719f8f4808fa" and "3193cafe4b5d11e17f2cbe81582fe61e02f0654a" have entirely different histories.
5c5153adb6
...
3193cafe4b
|
@ -4,8 +4,6 @@ from django.conf import settings
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils import timezone
|
|
||||||
from membership.models import Membership
|
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
@ -21,19 +19,3 @@ def check_total_amount(sender: models.Payment, instance: models.Payment, **kwarg
|
||||||
settings.DEFAULT_FROM_EMAIL,
|
settings.DEFAULT_FROM_EMAIL,
|
||||||
settings.ADMINS,
|
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,15 +2,10 @@
|
||||||
|
|
||||||
from collections.abc import Callable
|
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 admin
|
||||||
from django.contrib import messages
|
|
||||||
from django.contrib.admin import ModelAdmin
|
from django.contrib.admin import ModelAdmin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
@ -52,34 +47,12 @@ def decorate_ensure_membership_type_exists(membership_type: models.MembershipTyp
|
||||||
return admin_action
|
return admin_action
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def ensure_membership_type_exists(
|
def ensure_membership_type_exists(
|
||||||
request: HttpRequest,
|
request: HttpRequest, # noqa: ARG001
|
||||||
queryset: QuerySet,
|
queryset: QuerySet, # noqa: ARG001
|
||||||
membership_type: models.MembershipType,
|
membership_type: models.MembershipType, # noqa: ARG001
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
"""Inner function that ensures that a membership exists for a given queryset of Member objects."""
|
"""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)
|
@admin.register(models.Member)
|
||||||
|
@ -92,18 +65,11 @@ class MemberAdmin(UserAdmin):
|
||||||
def get_actions(self, request: HttpRequest) -> dict:
|
def get_actions(self, request: HttpRequest) -> dict:
|
||||||
"""Populate actions with dynamic data (MembershipType)."""
|
"""Populate actions with dynamic data (MembershipType)."""
|
||||||
current_period = models.SubscriptionPeriod.objects.current()
|
current_period = models.SubscriptionPeriod.objects.current()
|
||||||
|
|
||||||
super_dict = super().get_actions(request)
|
|
||||||
|
|
||||||
if current_period:
|
if current_period:
|
||||||
for i, mtype in enumerate(models.MembershipType.objects.filter(active=True)):
|
for mtype in models.MembershipType.objects.filter(active=True):
|
||||||
action_label = f"Ensure membership {mtype.name}, {current_period.period}, {mtype.total_including_vat}"
|
action_label = f"Ensure membership {mtype.name}, {current_period.period}, {mtype.total_including_vat}"
|
||||||
action_func = decorate_ensure_membership_type_exists(mtype, action_label)
|
self.actions.append(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
|
return super().get_actions(request)
|
||||||
action_func.__name__ += f"_{i}"
|
|
||||||
self.actions.append(action_func)
|
|
||||||
|
|
||||||
return super_dict
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.WaitingListEntry)
|
@admin.register(models.WaitingListEntry)
|
||||||
|
|
|
@ -99,17 +99,16 @@ class Membership(CreatedModifiedAbstract):
|
||||||
"""Filter memberships for a given member."""
|
"""Filter memberships for a given member."""
|
||||||
return self.filter(user=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:
|
def _current(self) -> Self:
|
||||||
"""Filter memberships for the current period."""
|
"""Filter memberships for the current period."""
|
||||||
return self.filter(period__period__contains=timezone.now())
|
return self.filter(activated=True, revoked=False, period__period__contains=timezone.now())
|
||||||
|
|
||||||
def current(self) -> "Membership | None":
|
def current(self) -> "Membership | None":
|
||||||
"""Get the current membership."""
|
"""Get the current membership."""
|
||||||
return self._current().first()
|
try:
|
||||||
|
return self._current().get()
|
||||||
|
except self.model.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
def previous(self) -> list["Membership"]:
|
def previous(self) -> list["Membership"]:
|
||||||
"""Get previous memberships."""
|
"""Get previous memberships."""
|
||||||
|
@ -120,7 +119,7 @@ class Membership(CreatedModifiedAbstract):
|
||||||
|
|
||||||
objects = QuerySet.as_manager()
|
objects = QuerySet.as_manager()
|
||||||
|
|
||||||
user = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="memberships")
|
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
|
|
||||||
membership_type = models.ForeignKey(
|
membership_type = models.ForeignKey(
|
||||||
"membership.MembershipType",
|
"membership.MembershipType",
|
||||||
|
|
Loading…
Reference in a new issue