forked from data.coop/membersystem
Introduce subscription periods.
This commit is contained in:
parent
282b500a5e
commit
01715a7704
2
Makefile
2
Makefile
|
@ -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_BUILD = DOCKER_BUILDKIT=1 docker build
|
||||
DOCKER_CONTAINER_NAME = backend
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Django==4.1
|
||||
Django==4.1.5
|
||||
django-money==1.3
|
||||
django-allauth==0.46
|
||||
psycopg2-binary==2.9.5
|
||||
|
|
|
@ -2,6 +2,7 @@ from django.contrib import admin
|
|||
|
||||
from .models import Membership
|
||||
from .models import MembershipType
|
||||
from .models import SubscriptionPeriod
|
||||
|
||||
|
||||
@admin.register(Membership)
|
||||
|
@ -12,3 +13,8 @@ class MembershipAdmin(admin.ModelAdmin):
|
|||
@admin.register(MembershipType)
|
||||
class MembershipTypeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(SubscriptionPeriod)
|
||||
class SubscriptionPeriodAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
|
|
@ -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"
|
||||
),
|
||||
),
|
||||
]
|
23
src/membership/migrations/0003_membership_period.py
Normal file
23
src/membership/migrations/0003_membership_period.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
22
src/membership/migrations/0004_alter_membership_period.py
Normal file
22
src/membership/migrations/0004_alter_membership_period.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,18 +1,31 @@
|
|||
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.utils import timezone
|
||||
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"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
class SubscriptionPeriod(CreatedModifiedAbstract):
|
||||
"""
|
||||
Denotes a period for which members should pay their membership fee for.
|
||||
"""
|
||||
|
||||
period = DateRangeField(verbose_name=_("period"))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
constraints = [
|
||||
ExclusionConstraint(
|
||||
name="exclude_overlapping_periods",
|
||||
expressions=[
|
||||
("period", RangeOperators.OVERLAPS),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class Membership(CreatedModifiedAbstract):
|
||||
|
@ -25,7 +38,7 @@ class Membership(CreatedModifiedAbstract):
|
|||
return self.filter(user=user)
|
||||
|
||||
def _current(self):
|
||||
return self.filter(period__contains=timezone.now())
|
||||
return self.filter(period__period__contains=timezone.now())
|
||||
|
||||
def current(self) -> Optional["Membership"]:
|
||||
try:
|
||||
|
@ -54,7 +67,9 @@ class Membership(CreatedModifiedAbstract):
|
|||
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):
|
||||
return f"{self.user} - {self.period}"
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
{% else %}
|
||||
<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>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,9 +10,16 @@ def membership_overview(request):
|
|||
current_membership = memberships.current()
|
||||
previous_memberships = memberships.previous()
|
||||
|
||||
current_period = current_membership.period.period if current_membership else None
|
||||
|
||||
context = {
|
||||
"current_membership": current_membership,
|
||||
"current_period": current_period,
|
||||
"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
11
src/utils/mixins.py
Normal 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
|
Loading…
Reference in a new issue