diff --git a/apps/activity/forms.py b/apps/activity/forms.py index 1602eb6384ab1c80cebc79cc423d7bd9d4bc9aa1..7fafd15eb950d64282e829340e423ccca6930c2e 100644 --- a/apps/activity/forms.py +++ b/apps/activity/forms.py @@ -4,7 +4,7 @@ from django import forms from django.contrib.contenttypes.models import ContentType from member.models import Club -from note.models import NoteUser +from note.models import NoteUser, Note from note_kfet.inputs import DateTimePickerInput, AutocompleteModelSelect from .models import Activity, Guest @@ -13,12 +13,19 @@ from .models import Activity, Guest class ActivityForm(forms.ModelForm): class Meta: model = Activity - fields = '__all__' + exclude = ('valid', 'open', ) widgets = { "organizer": AutocompleteModelSelect( model=Club, attrs={"api_url": "/api/members/club/"}, ), + "note": AutocompleteModelSelect( + model=Note, + attrs={ + "api_url": "/api/note/note/", + 'placeholder': 'Note de l\'événement sur laquelle envoyer les crédits d\'invitation ...' + }, + ), "attendees_club": AutocompleteModelSelect( model=Club, attrs={"api_url": "/api/members/club/"}, @@ -39,7 +46,7 @@ class GuestForm(forms.ModelForm): 'api_url': '/api/note/note/', # We don't evaluate the content type at launch because the DB might be not initialized 'api_url_suffix': - lambda value: '&polymorphic_ctype=' + str(ContentType.objects.get_for_model(NoteUser).pk), + lambda: '&polymorphic_ctype=' + str(ContentType.objects.get_for_model(NoteUser).pk), 'placeholder': 'Note ...', }, ), diff --git a/apps/activity/models.py b/apps/activity/models.py index 7151a2a0569df0d7db61d9ca2d77acaac61f42b8..4bf92e23a431c11739f1b01d20aa1c800f19603d 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from note.models import NoteUser +from note.models import NoteUser, Transaction class ActivityType(models.Model): @@ -44,34 +44,59 @@ class Activity(models.Model): verbose_name=_('name'), max_length=255, ) + description = models.TextField( verbose_name=_('description'), ) + activity_type = models.ForeignKey( ActivityType, on_delete=models.PROTECT, related_name='+', verbose_name=_('type'), ) + organizer = models.ForeignKey( 'member.Club', on_delete=models.PROTECT, related_name='+', verbose_name=_('organizer'), ) + + note = models.ForeignKey( + 'note.Note', + on_delete=models.PROTECT, + related_name='+', + null=True, + blank=True, + verbose_name=_('note'), + ) + attendees_club = models.ForeignKey( 'member.Club', on_delete=models.PROTECT, related_name='+', verbose_name=_('attendees club'), ) + date_start = models.DateTimeField( verbose_name=_('start date'), ) + date_end = models.DateTimeField( verbose_name=_('end date'), ) + valid = models.BooleanField( + default=False, + verbose_name=_('valid'), + ) + + open = models.BooleanField( + default=False, + verbose_name=_('open'), + ) + class Meta: verbose_name = _("activity") verbose_name_plural = _("activities") @@ -122,13 +147,17 @@ class Guest(models.Model): null=True, ) - entry_transaction = models.ForeignKey( - 'note.Transaction', - on_delete=models.PROTECT, - blank=True, - null=True, - ) - class Meta: verbose_name = _("guest") verbose_name_plural = _("guests") + + +class GuestTransaction(Transaction): + guest = models.OneToOneField( + Guest, + on_delete=models.PROTECT, + ) + + @property + def type(self): + return _('Invitation') diff --git a/apps/note/forms.py b/apps/note/forms.py index 43c7b395520104a350347d8a299c92501da81196..8c9a04cea94f9e5ffe384cc087c47c24ecc606da 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -37,7 +37,7 @@ class TransactionTemplateForm(forms.ModelForm): 'api_url': '/api/note/note/', # We don't evaluate the content type at launch because the DB might be not initialized 'api_url_suffix': - lambda value: '&polymorphic_ctype=' + str(ContentType.objects.get_for_model(NoteClub).pk), + lambda: '&polymorphic_ctype=' + str(ContentType.objects.get_for_model(NoteClub).pk), 'placeholder': 'Note ...', }, ), diff --git a/apps/permission/templatetags/perms.py b/apps/permission/templatetags/perms.py index f1ecfacb71fbf62c1356f377550f9d62d5482725..aa2feeca1f6a110493e1626be689df06cc30cafd 100644 --- a/apps/permission/templatetags/perms.py +++ b/apps/permission/templatetags/perms.py @@ -48,9 +48,7 @@ def not_empty_model_change_list(model_name): return session.get("not_empty_model_change_list_" + model_name) == 1 -def has_perm(t, obj, field=None): - print(t) - perm = "." + t + ("__" + field if field else "_") +def has_perm(perm, obj): return PermissionBackend().has_perm(get_current_authenticated_user(), perm, obj) diff --git a/static/js/base.js b/static/js/base.js index d21bd43389aa624c6fd149ac63557616068864ca..22d1366a88a672ea753fc91ea607236a32e0ce8a 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -28,15 +28,35 @@ function addMsg(msg, alert_type) { + msg + "</div>\n"; msgDiv.html(html); } + /** * add Muliple error message from err_obj - * @param err_obj {error_code:erro_message} + * @param errs_obj [{error_code:erro_message}] */ function errMsg(errs_obj){ for (const err_msg of Object.values(errs_obj)) { addMsg(err_msg,'danger'); } } + +var reloadWithTurbolinks = (function () { + var scrollPosition; + + function reload () { + scrollPosition = [window.scrollX, window.scrollY]; + Turbolinks.visit(window.location.toString(), { action: 'replace' }) + } + + document.addEventListener('turbolinks:load', function () { + if (scrollPosition) { + window.scrollTo.apply(window, scrollPosition); + scrollPosition = null + } + }); + + return reload; +})(); + /** * Reload the balance of the user on the right top corner */ diff --git a/templates/activity/activity_detail.html b/templates/activity/activity_detail.html index 339d087bc7b55cb24d7e6e55146e098f5f0a8a21..184cc6cddd1f52db65289f335e99929a913aac17 100644 --- a/templates/activity/activity_detail.html +++ b/templates/activity/activity_detail.html @@ -7,7 +7,7 @@ {% block content %} - <div class="card bg-light shadow"> + <div id="activity_info" class="card bg-light shadow"> <div class="card-header text-center"> <h4>{{ activity.name }}</h4> </div> @@ -38,11 +38,23 @@ <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 "view"|has_perm:activity %} + {% 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 "view_"|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 %} @@ -77,5 +89,42 @@ errMsg(xhr.responseJSON); }); } + + $("#open_activity").click(function() { + $.ajax({ + url: "/api/activity/activity/{{ activity.pk }}/", + type: "PATCH", + dataType: "json", + headers: { + "X-CSRFTOKEN": CSRF_TOKEN + }, + data: { + open: {{ activity.open|yesno:'false,true' }} + } + }).done(function () { + reloadWithTurbolinks(); + }).fail(function (xhr) { + errMsg(xhr.responseJSON); + }); + }); + + $("#validate_activity").click(function () { + console.log(42); + $.ajax({ + url: "/api/activity/activity/{{ activity.pk }}/", + type: "PATCH", + dataType: "json", + headers: { + "X-CSRFTOKEN": CSRF_TOKEN + }, + data: { + valid: {{ activity.valid|yesno:'false,true' }} + } + }).done(function () { + reloadWithTurbolinks(); + }).fail(function (xhr) { + errMsg(xhr.responseJSON); + }); + }); </script> {% endblock %}