Introduce subscription periods.

This commit is contained in:
Víðir Valberg Guðmundsson 2023-01-02 22:13:25 +01:00
parent 282b500a5e
commit 01715a7704
10 changed files with 159 additions and 11 deletions

View file

@ -1,4 +1,4 @@
DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 venv/bin/docker-compose
DOCKER_RUN = ${DOCKER_COMPOSE} run -u `id -u` DOCKER_RUN = ${DOCKER_COMPOSE} run -u `id -u`
DOCKER_BUILD = DOCKER_BUILDKIT=1 docker build DOCKER_BUILD = DOCKER_BUILDKIT=1 docker build
DOCKER_CONTAINER_NAME = backend DOCKER_CONTAINER_NAME = backend

View file

@ -1,4 +1,4 @@
Django==4.1 Django==4.1.5
django-money==1.3 django-money==1.3
django-allauth==0.46 django-allauth==0.46
psycopg2-binary==2.9.5 psycopg2-binary==2.9.5

View file

@ -2,6 +2,7 @@ from django.contrib import admin
from .models import Membership from .models import Membership
from .models import MembershipType from .models import MembershipType
from .models import SubscriptionPeriod
@admin.register(Membership) @admin.register(Membership)
@ -12,3 +13,8 @@ class MembershipAdmin(admin.ModelAdmin):
@admin.register(MembershipType) @admin.register(MembershipType)
class MembershipTypeAdmin(admin.ModelAdmin): class MembershipTypeAdmin(admin.ModelAdmin):
pass pass
@admin.register(SubscriptionPeriod)
class SubscriptionPeriodAdmin(admin.ModelAdmin):
pass

View file

@ -0,0 +1,63 @@
# Generated by Django 4.1 on 2023-01-02 21:05
import django.contrib.postgres.constraints
import django.contrib.postgres.fields.ranges
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("membership", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="SubscriptionPeriod",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"modified",
models.DateTimeField(auto_now=True, verbose_name="modified"),
),
(
"created",
models.DateTimeField(auto_now_add=True, verbose_name="created"),
),
(
"period",
django.contrib.postgres.fields.ranges.DateRangeField(
verbose_name="period"
),
),
],
),
migrations.RemoveField(
model_name="membership",
name="period",
),
migrations.AlterField(
model_name="membership",
name="created",
field=models.DateTimeField(auto_now_add=True, verbose_name="created"),
),
migrations.AlterField(
model_name="membershiptype",
name="created",
field=models.DateTimeField(auto_now_add=True, verbose_name="created"),
),
migrations.AddConstraint(
model_name="subscriptionperiod",
constraint=django.contrib.postgres.constraints.ExclusionConstraint(
expressions=[("period", "&&")], name="exclude_overlapping_periods"
),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1 on 2023-01-02 21:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("membership", "0002_subscriptionperiod_remove_membership_period_and_more"),
]
operations = [
migrations.AddField(
model_name="membership",
name="period",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="membership.subscriptionperiod",
),
),
]

View file

@ -0,0 +1,22 @@
# Generated by Django 4.1 on 2023-01-02 21:06
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("membership", "0003_membership_period"),
]
operations = [
migrations.AlterField(
model_name="membership",
name="period",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="membership.subscriptionperiod",
),
),
]

View file

@ -1,18 +1,31 @@
from typing import Optional from typing import Optional
from django.contrib.postgres.fields import DateTimeRangeField from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import DateRangeField
from django.contrib.postgres.fields import RangeOperators
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from utils.mixins import CreatedModifiedAbstract
class CreatedModifiedAbstract(models.Model):
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified")) class SubscriptionPeriod(CreatedModifiedAbstract):
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created")) """
Denotes a period for which members should pay their membership fee for.
"""
period = DateRangeField(verbose_name=_("period"))
class Meta: class Meta:
abstract = True constraints = [
ExclusionConstraint(
name="exclude_overlapping_periods",
expressions=[
("period", RangeOperators.OVERLAPS),
],
),
]
class Membership(CreatedModifiedAbstract): class Membership(CreatedModifiedAbstract):
@ -25,7 +38,7 @@ class Membership(CreatedModifiedAbstract):
return self.filter(user=user) return self.filter(user=user)
def _current(self): def _current(self):
return self.filter(period__contains=timezone.now()) return self.filter(period__period__contains=timezone.now())
def current(self) -> Optional["Membership"]: def current(self) -> Optional["Membership"]:
try: try:
@ -54,7 +67,9 @@ class Membership(CreatedModifiedAbstract):
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
period = DateTimeRangeField(help_text=_("The duration this subscription is for. ")) period = models.ForeignKey(
"membership.SubscriptionPeriod", on_delete=models.PROTECT
)
def __str__(self): def __str__(self):
return f"{self.user} - {self.period}" return f"{self.user} - {self.period}"

View file

@ -16,7 +16,8 @@
{% else %} {% else %}
<p>{% trans "You are a member!" %}</p> <p>{% trans "You are a member!" %}</p>
<p>{% trans "Period" %}: {{ current_membership.period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_membership.period.upper|date:"SHORT_DATE_FORMAT" }}</p> {% trans "next general assembly" as next_general_assembly %}
<p>{% trans "Period" %}: {{ current_period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_period.upper|date:"SHORT_DATE_FORMAT"|default:next_general_assembly }}</p>
<p>{% trans "Type" %}: {{ current_membership.membership_type }}</p> <p>{% trans "Type" %}: {{ current_membership.membership_type }}</p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -10,9 +10,16 @@ def membership_overview(request):
current_membership = memberships.current() current_membership = memberships.current()
previous_memberships = memberships.previous() previous_memberships = memberships.previous()
current_period = current_membership.period.period if current_membership else None
context = { context = {
"current_membership": current_membership, "current_membership": current_membership,
"current_period": current_period,
"previous_memberships": previous_memberships, "previous_memberships": previous_memberships,
} }
return render(request, "membership_overview.html", context) return render(
request=request,
template_name="membership_overview.html",
context=context,
)

11
src/utils/mixins.py Normal file
View file

@ -0,0 +1,11 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class CreatedModifiedAbstract(models.Model):
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
class Meta:
abstract = True