from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
-from .models import User
+from .models import Membership, User
class MemberEditForm(forms.ModelForm):
class MemberEmissionForm(forms.Form):
emission = forms.ChoiceField(label=_('New Emission'), required=True, choices=get_emissions_as_choices)
+
+
+class MembershipForm(forms.ModelForm):
+ class Meta:
+ model = Membership
+ fields = ['year', 'payment_date', 'payment_amount', 'notes']
--- /dev/null
+# Generated by Django 2.2.19 on 2022-01-23 10:42
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('aa', '0006_auto_20210326_1324'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Membership',
+ fields=[
+ (
+ 'id',
+ models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ ('year', models.IntegerField(verbose_name='Year')),
+ ('creation_timestamp', models.DateTimeField(auto_now_add=True)),
+ ('payment_date', models.DateField(blank=True, null=True, verbose_name='Date')),
+ (
+ 'payment_amount',
+ models.DecimalField(
+ blank=True, decimal_places=2, max_digits=6, null=True, verbose_name='Amount'
+ ),
+ ),
+ ('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
+ (
+ 'member',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
+ ),
+ ),
+ ],
+ ),
+ ]
if parts:
s = ' '.join(parts)
return s
+
+
+class Membership(models.Model):
+ member = models.ForeignKey(User, on_delete=models.CASCADE)
+ year = models.IntegerField(_('Year'))
+ creation_timestamp = models.DateTimeField(auto_now_add=True)
+ payment_date = models.DateField(verbose_name=_('Date'), blank=True, null=True)
+ payment_amount = models.DecimalField(
+ verbose_name=_('Amount'), decimal_places=2, max_digits=6, blank=True, null=True
+ )
+ notes = models.TextField(_('Notes'), blank=True, null=True)
),
url(r'^members/(?P<pk>\d+)/mark-as-active/$', views.mark_as_active, name='member-mark-as-active'),
url(r'^members/(?P<pk>\d+)/mark-as-inactive/$', views.mark_as_inactive, name='member-mark-as-inactive'),
+ url(
+ r'^members/(?P<pk>\d+)/register-membership/$',
+ views.register_membership,
+ name='member-register-membership',
+ ),
url(r'^profile/$', views.profile_view, name='profile-view'),
url(r'^profile/edit/$', views.profile_contact_edit, name='profile-contact-edit'),
]
import vobject
+from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse_lazy
+from django.utils.timezone import now
from django.views.generic.base import RedirectView, TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, FormView, UpdateView
from django.views.generic.list import ListView
-from .forms import MemberCreateForm, MemberEditForm, MemberEmissionForm
-from .models import User
+from .forms import MemberCreateForm, MemberEditForm, MemberEmissionForm, MembershipForm
+from .models import Membership, User
class ProfileView(TemplateView):
| Q(emissions__title__icontains=part)
| Q(emissions__slug__icontains=part)
)
+ current_year = now().year
+ if self.request.GET.get('membership') == 'ok':
+ qs = qs.filter(membership__year=current_year)
+ if self.request.GET.get('membership') == 'renew':
+ qs = qs.filter(membership__year=current_year - 1).exclude(membership__year=current_year)
qs = qs.distinct()
return qs
class MemberView(DetailView):
model = User
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ current_year = now().year
+ user = self.get_object()
+ context['current_membership'] = user.membership_set.filter(year=current_year).first()
+ context['past_memberships'] = user.membership_set.exclude(year=current_year).order_by('-year')
+ return context
+
member_view = login_required(MemberView.as_view())
def mark_as_inactive(request, pk):
User.objects.filter(pk=pk).update(is_active=False)
return HttpResponseRedirect(reverse_lazy('member-view', kwargs={'pk': pk}))
+
+
+class RegisterMembershipView(FormView):
+ form_class = MembershipForm
+ template_name = 'aa/register_membership.html'
+
+ def get_context_data(self, **kwargs):
+ if not self.request.user.has_perm('aa.add_membership'):
+ raise PermissionDenied()
+ context = super().get_context_data(**kwargs)
+ context['member'] = User.objects.get(id=self.kwargs['pk'])
+ return context
+
+ def get_initial(self):
+ initial = super().get_initial()
+ initial['year'] = now().year
+ initial['payment_date'] = now().date()
+ initial['payment_amount'] = settings.MEMBERSHIP_DEFAULT_AMOUNT or ''
+ return initial
+
+ def form_valid(self, form):
+ member = User.objects.get(id=self.kwargs['pk'])
+ membership, created = Membership.objects.get_or_create(member=member, year=form.cleaned_data['year'])
+ membership.payment_date = form.cleaned_data['payment_date']
+ membership.payment_amount = form.cleaned_data['payment_amount']
+ membership.notes = form.cleaned_data['notes']
+ membership.save()
+ return super().form_valid(form)
+
+ def get_success_url(self):
+ return reverse_lazy('member-view', kwargs={'pk': self.kwargs['pk']})
+
+
+register_membership = login_required(RegisterMembershipView.as_view())
TAGGIT_TAGS_FROM_STRING = 'emissions.utils.custom_parse_tags'
AUTH_USER_MODEL = 'aa.User'
+MEMBERSHIP_DEFAULT_AMOUNT = 10
+
LOGIN_REDIRECT_URL = '/'
WEBSITE_BASE_URL = 'https://www.radiopanik.org/'
transform: rotate(360deg);
}
}
+
+.register-membership-form {
+ #id_notes {
+ height: 4em;
+ }
+}
+
+.members-filter {
+ display: flex;
+ form input {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ a.button {
+ margin-left: 1em;
+ line-height: normal;
+ }
+}
--- /dev/null
+{% extends "aa/user_list.html" %}
+{% load i18n %}
+
+{% block appbar %}
+{% endblock %}
+
+{% block content %}
+<form method="post" class="register-membership-form">
+{% csrf_token %}
+{{ form.as_p }}
+<div class="buttons">
+<a class="cancel" href="{% url 'member-view' pk=member.id %}">Annuler</a>
+<button>Valider</button>
+</div>
+</form>
+{% endblock %}
+
{% endif %}
</div>
</div>
+
+{% if perms.emissions.add_membership %}
+<div class="section">
+ <h3>Cotisations</h3>
+ <div>
+ {% if current_membership %}
+ <p>En ordre de cotisation.
+ {% if current_membership.payment_date and current_membership.payment_amount %}
+ ({{ current_membership.payment_amount }}€ payés le {{ current_membership.payment_date }})
+ {% endif %}
+ </p>
+ {% if current_membership.notes %}<p>{{ current_membership.notes }}</p>{% endif %}
+ {% else %}
+ <p>Pas en ordre de cotisation pour cette année.</p>
+ <a rel="popup" class="button" href="{% url 'member-register-membership' pk=user.id %}">Enregistrer la cotisation</a>
+ {% endif %}
+ {% if past_memberships %}
+ <p>Années précédentes :</p>
+ <ul>
+ {% for membership in past_memberships %}
+ <li>{{ membership.year }} {% if membership.payment_date and membership.payment_amount %}
+ ({{ membership.payment_amount }}€ payés le {{ membership.payment_date }}){% endif %}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+</div>
+{% endif %}
{% endblock %}
{% block more-user-links %}{{ block.super }} <a class="icon-members" href="{% url 'members-list-view' %}">Annuaire des membres</a>{% endblock %}
{% block content %}
-<div class="section padded">
+<div class="section padded members-filter">
<form>
<input name="q" type="search" value="{{ request.GET.q }}"> <button>{% trans 'Search' %}</button>
</form>
+{% if perms.emissions.add_membership %}
+<a class="button" href="?membership=ok">En ordre de cotisation</a>
+<a class="button" href="?membership=renew">Cotisations à renouveler</a>
+{% endif %}
</div>
<table class="main">