mirror of https://gitlab.crans.org/bde/nk20
Improve activity interface
This commit is contained in:
parent
dd4b24d999
commit
9c7cb07dec
|
@ -4,6 +4,7 @@
|
|||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Pot",
|
||||
"manage_entries": true,
|
||||
"can_invite": true,
|
||||
"guest_entry_fee": 500
|
||||
}
|
||||
|
@ -13,6 +14,7 @@
|
|||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Soir\u00e9e de club",
|
||||
"manage_entries": false,
|
||||
"can_invite": false,
|
||||
"guest_entry_fee": 0
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class GuestForm(forms.ModelForm):
|
|||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if self.activity.date_start > timezone.now():
|
||||
if timezone.now() > timezone.localtime(self.activity.date_start):
|
||||
self.add_error("inviter", _("You can't invite someone once the activity is started."))
|
||||
|
||||
if not self.activity.valid:
|
||||
|
@ -52,20 +52,21 @@ class GuestForm(forms.ModelForm):
|
|||
one_year = timedelta(days=365)
|
||||
|
||||
qs = Guest.objects.filter(
|
||||
first_name=cleaned_data["first_name"],
|
||||
last_name=cleaned_data["last_name"],
|
||||
first_name__iexact=cleaned_data["first_name"],
|
||||
last_name__iexact=cleaned_data["last_name"],
|
||||
activity__date_start__gte=self.activity.date_start - one_year,
|
||||
entry__isnull=False,
|
||||
)
|
||||
if len(qs) >= 5:
|
||||
if qs.count() >= 5:
|
||||
self.add_error("last_name", _("This person has been already invited 5 times this year."))
|
||||
|
||||
qs = qs.filter(activity=self.activity)
|
||||
if qs.exists():
|
||||
self.add_error("last_name", _("This person is already invited."))
|
||||
|
||||
qs = Guest.objects.filter(inviter=cleaned_data["inviter"], activity=self.activity)
|
||||
if len(qs) >= 3:
|
||||
self.add_error("inviter", _("You can't invite more than 3 people to this activity."))
|
||||
if "inviter" in cleaned_data:
|
||||
if Guest.objects.filter(inviter=cleaned_data["inviter"], activity=self.activity).count() >= 3:
|
||||
self.add_error("inviter", _("You can't invite more than 3 people to this activity."))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
|
|
@ -27,11 +27,21 @@ class ActivityType(models.Model):
|
|||
verbose_name=_('name'),
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
manage_entries = models.BooleanField(
|
||||
verbose_name=_('manage entries'),
|
||||
help_text=_('Enable the support of entries for this activity.'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
can_invite = models.BooleanField(
|
||||
verbose_name=_('can invite'),
|
||||
default=False,
|
||||
)
|
||||
|
||||
guest_entry_fee = models.PositiveIntegerField(
|
||||
verbose_name=_('guest entry fee'),
|
||||
default=0,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -236,18 +246,18 @@ class Guest(models.Model):
|
|||
one_year = timedelta(days=365)
|
||||
|
||||
if not force_insert:
|
||||
if self.activity.date_start > timezone.now():
|
||||
if timezone.now() > timezone.localtime(self.activity.date_start):
|
||||
raise ValidationError(_("You can't invite someone once the activity is started."))
|
||||
|
||||
if not self.activity.valid:
|
||||
raise ValidationError(_("This activity is not validated yet."))
|
||||
|
||||
qs = Guest.objects.filter(
|
||||
first_name=self.first_name,
|
||||
last_name=self.last_name,
|
||||
first_name__iexact=self.first_name,
|
||||
last_name__iexact=self.last_name,
|
||||
activity__date_start__gte=self.activity.date_start - one_year,
|
||||
)
|
||||
if len(qs) >= 5:
|
||||
if qs.count() >= 5:
|
||||
raise ValidationError(_("This person has been already invited 5 times this year."))
|
||||
|
||||
qs = qs.filter(activity=self.activity)
|
||||
|
@ -255,7 +265,7 @@ class Guest(models.Model):
|
|||
raise ValidationError(_("This person is already invited."))
|
||||
|
||||
qs = Guest.objects.filter(inviter=self.inviter, activity=self.activity)
|
||||
if len(qs) >= 3:
|
||||
if qs.count() >= 3:
|
||||
raise ValidationError(_("You can't invite more than 3 people to this activity."))
|
||||
|
||||
return super().save(force_insert, force_update, using, update_fields)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
|
@ -66,6 +66,10 @@ def get_row_class(record):
|
|||
qs = Entry.objects.filter(note=record.note, activity=record.activity, guest=None)
|
||||
if qs.exists():
|
||||
c += " table-success"
|
||||
elif not record.note.user.memberships.filter(club=record.activity.attendees_club,
|
||||
date_start__lte=timezone.now(),
|
||||
date_end__gte=timezone.now()).exists():
|
||||
c += " table-info"
|
||||
elif record.note.balance < 0:
|
||||
c += " table-danger"
|
||||
return c
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import F, Q
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_tables2.views import SingleTableView
|
||||
|
@ -46,12 +45,17 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now())
|
||||
upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now())
|
||||
context['upcoming'] = ActivityTable(
|
||||
data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")),
|
||||
prefix='upcoming-',
|
||||
)
|
||||
|
||||
started_activities = Activity.objects\
|
||||
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
|
||||
.filter(open=True, valid=True).all()
|
||||
context["started_activities"] = started_activities
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
@ -67,7 +71,7 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
|||
.filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view")))
|
||||
context["guests"] = table
|
||||
|
||||
context["activity_started"] = datetime.now(timezone.utc) > self.object.date_start
|
||||
context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start)
|
||||
|
||||
return context
|
||||
|
||||
|
@ -148,7 +152,12 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
|
|||
username=F("note__noteuser__user__username"),
|
||||
note_name=F("name"),
|
||||
balance=F("note__balance"))\
|
||||
.filter(note__polymorphic_ctype__model="noteuser")\
|
||||
.filter(note__noteuser__isnull=False)\
|
||||
.filter(
|
||||
note__noteuser__user__memberships__club=activity.attendees_club,
|
||||
note__noteuser__user__memberships__date_start__lte=timezone.now(),
|
||||
note__noteuser__user__memberships__date_end__gte=timezone.now(),
|
||||
)\
|
||||
.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))
|
||||
if pattern:
|
||||
note_qs = note_qs.filter(
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3806feb67fcb1fe822cfdedddbbc4ca7eeef3829
|
||||
Subproject commit 4984159a61642a0d3668e85daf39472b59b86447
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -7,70 +7,7 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
<div id="activity_info" class="card bg-light shadow">
|
||||
<div class="card-header text-center">
|
||||
<h4>{{ activity.name }}</h4>
|
||||
</div>
|
||||
<div class="card-body" id="profile_infos">
|
||||
<dl class="row">
|
||||
<dt class="col-xl-6">{% trans 'description'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"> {{ activity.description }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'type'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"> {{ activity.activity_type }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'start date'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.date_start }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'end date'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.date_end }}</dd>
|
||||
|
||||
{% if ".view_"|has_perm:activity.creater %}
|
||||
<dt class="col-xl-6">{% trans 'creater'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:user_detail" pk=activity.creater.pk %}">{{ activity.creater }}</a></dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-xl-6">{% trans 'organizer'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:club_detail" pk=activity.organizer.pk %}">{{ activity.organizer }}</a></dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'attendees club'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:club_detail" pk=activity.attendees_club.pk %}">{{ activity.attendees_club }}</a></dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'can invite'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.activity_type.can_invite|yesno }}</dd>
|
||||
|
||||
{% if activity.activity_type.can_invite %}
|
||||
<dt class="col-xl-6">{% trans 'guest entry fee'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.activity_type.guest_entry_fee|pretty_money }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-xl-6">{% trans 'valid'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.valid|yesno }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.open|yesno }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center">
|
||||
{% if activity.open and ".change__open"|has_perm:activity %}
|
||||
<a class="btn btn-warning btn-sm my-1" href="{% url 'activity:activity_entry' pk=activity.pk %}"> {% trans "Entry page" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if activity.valid and ".change__open"|has_perm:activity %}
|
||||
<a class="btn btn-warning btn-sm my-1" id="open_activity"> {% if activity.open %}{% trans "close"|capfirst %}{% else %}{% trans "open"|capfirst %}{% endif %}</a>
|
||||
{% endif %}
|
||||
{% if not activity.open and ".change__valid"|has_perm:activity %}
|
||||
<a class="btn btn-success btn-sm my-1" id="validate_activity"> {% if activity.valid %}{% trans "invalidate"|capfirst %}{% else %}{% trans "validate"|capfirst %}{% endif %}</a>
|
||||
{% endif %}
|
||||
{% if ".change_"|has_perm:activity %}
|
||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_update' pk=activity.pk %}"> {% trans "edit"|capfirst %}</a>
|
||||
{% endif %}
|
||||
{% if activity.activity_type.can_invite and not activity_started %}
|
||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_invite' pk=activity.pk %}"> {% trans "Invite" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "activity/activity_info.html" %}
|
||||
|
||||
{% if guests.data %}
|
||||
<hr>
|
||||
|
|
|
@ -76,7 +76,10 @@
|
|||
note: id,
|
||||
guest: null
|
||||
}).done(function () {
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
if (target.hasClass("table-info"))
|
||||
addMsg("Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.", "warning", 10000);
|
||||
else
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
reloadTable(true);
|
||||
}).fail(function(xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
|
@ -104,7 +107,10 @@
|
|||
note: target.attr("data-inviter"),
|
||||
guest: id
|
||||
}).done(function () {
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
if (target.hasClass("table-info"))
|
||||
addMsg("Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.", "warning", 10000);
|
||||
else
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
reloadTable(true);
|
||||
}).fail(function (xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
{% load i18n %}
|
||||
{% load perms %}
|
||||
{% load pretty_money %}
|
||||
|
||||
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
|
||||
|
||||
<div id="activity_info" class="card bg-light shadow">
|
||||
<div class="card-header text-center">
|
||||
<h4>
|
||||
{% if request.path_info != activity_detail_url %}
|
||||
<a href="{{ activity_detail_url }}">{{ activity.name }}</a>
|
||||
{% else %}
|
||||
{{ activity.name }}
|
||||
{% endif %}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body" id="profile_infos">
|
||||
<dl class="row">
|
||||
<dt class="col-xl-6">{% trans 'description'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"> {{ activity.description }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'type'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"> {{ activity.activity_type }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'start date'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.date_start }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'end date'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.date_end }}</dd>
|
||||
|
||||
{% if ".view_"|has_perm:activity.creater %}
|
||||
<dt class="col-xl-6">{% trans 'creater'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:user_detail" pk=activity.creater.pk %}">{{ activity.creater }}</a></dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-xl-6">{% trans 'organizer'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:club_detail" pk=activity.organizer.pk %}">{{ activity.organizer }}</a></dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'attendees club'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url "member:club_detail" pk=activity.attendees_club.pk %}">{{ activity.attendees_club }}</a></dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'can invite'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.activity_type.can_invite|yesno }}</dd>
|
||||
|
||||
{% if activity.activity_type.can_invite %}
|
||||
<dt class="col-xl-6">{% trans 'guest entry fee'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.activity_type.guest_entry_fee|pretty_money }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-xl-6">{% trans 'valid'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.valid|yesno }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.open|yesno }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center">
|
||||
{% if activity.open and activity.activity_type.manage_entries and ".change__open"|has_perm:activity %}
|
||||
<a class="btn btn-warning btn-sm my-1" href="{% url 'activity:activity_entry' pk=activity.pk %}"> {% trans "Entry page" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if request.path_info == activity_detail_url %}
|
||||
{% if activity.valid and ".change__open"|has_perm:activity %}
|
||||
<a class="btn btn-warning btn-sm my-1" id="open_activity"> {% if activity.open %}{% trans "close"|capfirst %}{% else %}{% trans "open"|capfirst %}{% endif %}</a>
|
||||
{% endif %}
|
||||
{% if not activity.open and ".change__valid"|has_perm:activity %}
|
||||
<a class="btn btn-success btn-sm my-1" id="validate_activity"> {% if activity.valid %}{% trans "invalidate"|capfirst %}{% else %}{% trans "validate"|capfirst %}{% endif %}</a>
|
||||
{% endif %}
|
||||
{% if ".change_"|has_perm:activity %}
|
||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_update' pk=activity.pk %}"> {% trans "edit"|capfirst %}</a>
|
||||
{% endif %}
|
||||
{% if activity.activity_type.can_invite and not activity_started %}
|
||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_invite' pk=activity.pk %}"> {% trans "Invite" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
|
@ -2,6 +2,14 @@
|
|||
{% load render_table from django_tables2 %}
|
||||
{% load i18n crispy_forms_tags%}
|
||||
{% block content %}
|
||||
{% if started_activities %}
|
||||
<h2>{% trans "Current activity" %}</h2>
|
||||
{% for activity in started_activities %}
|
||||
{% include "activity/activity_info.html" %}
|
||||
<hr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Upcoming activities" %}</h2>
|
||||
{% if upcoming.data %}
|
||||
{% render_table upcoming %}
|
||||
|
|
|
@ -19,10 +19,12 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||
<input type="radio" name="transaction_type" id="type_credit">
|
||||
{% trans "Credit" %}
|
||||
</label>
|
||||
<label type="type_debit" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="transaction_type" id="type_debit">
|
||||
{% trans "Debit" %}
|
||||
</label>
|
||||
{% if not activities_open %}
|
||||
<label type="type_debit" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="transaction_type" id="type_debit">
|
||||
{% trans "Debit" %}
|
||||
</label>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for activity in activities_open %}
|
||||
<a href="{% url "activity:activity_entry" pk=activity.pk %}" class="btn btn-sm btn-outline-primary">
|
||||
|
|
Loading…
Reference in New Issue