mirror of https://gitlab.crans.org/bde/nk20
Open and validate activities
This commit is contained in:
parent
d6e202a26f
commit
8c1d902c30
|
@ -4,7 +4,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from member.models import Club
|
from member.models import Club
|
||||||
from note.models import NoteUser
|
from note.models import NoteUser, Note
|
||||||
from note_kfet.inputs import DateTimePickerInput, AutocompleteModelSelect
|
from note_kfet.inputs import DateTimePickerInput, AutocompleteModelSelect
|
||||||
|
|
||||||
from .models import Activity, Guest
|
from .models import Activity, Guest
|
||||||
|
@ -13,12 +13,19 @@ from .models import Activity, Guest
|
||||||
class ActivityForm(forms.ModelForm):
|
class ActivityForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Activity
|
model = Activity
|
||||||
fields = '__all__'
|
exclude = ('valid', 'open', )
|
||||||
widgets = {
|
widgets = {
|
||||||
"organizer": AutocompleteModelSelect(
|
"organizer": AutocompleteModelSelect(
|
||||||
model=Club,
|
model=Club,
|
||||||
attrs={"api_url": "/api/members/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(
|
"attendees_club": AutocompleteModelSelect(
|
||||||
model=Club,
|
model=Club,
|
||||||
attrs={"api_url": "/api/members/club/"},
|
attrs={"api_url": "/api/members/club/"},
|
||||||
|
@ -39,7 +46,7 @@ class GuestForm(forms.ModelForm):
|
||||||
'api_url': '/api/note/note/',
|
'api_url': '/api/note/note/',
|
||||||
# We don't evaluate the content type at launch because the DB might be not initialized
|
# We don't evaluate the content type at launch because the DB might be not initialized
|
||||||
'api_url_suffix':
|
'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 ...',
|
'placeholder': 'Note ...',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from note.models import NoteUser
|
from note.models import NoteUser, Transaction
|
||||||
|
|
||||||
|
|
||||||
class ActivityType(models.Model):
|
class ActivityType(models.Model):
|
||||||
|
@ -44,34 +44,59 @@ class Activity(models.Model):
|
||||||
verbose_name=_('name'),
|
verbose_name=_('name'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
verbose_name=_('description'),
|
verbose_name=_('description'),
|
||||||
)
|
)
|
||||||
|
|
||||||
activity_type = models.ForeignKey(
|
activity_type = models.ForeignKey(
|
||||||
ActivityType,
|
ActivityType,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+',
|
related_name='+',
|
||||||
verbose_name=_('type'),
|
verbose_name=_('type'),
|
||||||
)
|
)
|
||||||
|
|
||||||
organizer = models.ForeignKey(
|
organizer = models.ForeignKey(
|
||||||
'member.Club',
|
'member.Club',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+',
|
related_name='+',
|
||||||
verbose_name=_('organizer'),
|
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(
|
attendees_club = models.ForeignKey(
|
||||||
'member.Club',
|
'member.Club',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+',
|
related_name='+',
|
||||||
verbose_name=_('attendees club'),
|
verbose_name=_('attendees club'),
|
||||||
)
|
)
|
||||||
|
|
||||||
date_start = models.DateTimeField(
|
date_start = models.DateTimeField(
|
||||||
verbose_name=_('start date'),
|
verbose_name=_('start date'),
|
||||||
)
|
)
|
||||||
|
|
||||||
date_end = models.DateTimeField(
|
date_end = models.DateTimeField(
|
||||||
verbose_name=_('end date'),
|
verbose_name=_('end date'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
valid = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_('valid'),
|
||||||
|
)
|
||||||
|
|
||||||
|
open = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_('open'),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("activity")
|
verbose_name = _("activity")
|
||||||
verbose_name_plural = _("activities")
|
verbose_name_plural = _("activities")
|
||||||
|
@ -122,13 +147,17 @@ class Guest(models.Model):
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
entry_transaction = models.ForeignKey(
|
|
||||||
'note.Transaction',
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("guest")
|
verbose_name = _("guest")
|
||||||
verbose_name_plural = _("guests")
|
verbose_name_plural = _("guests")
|
||||||
|
|
||||||
|
|
||||||
|
class GuestTransaction(Transaction):
|
||||||
|
guest = models.OneToOneField(
|
||||||
|
Guest,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return _('Invitation')
|
||||||
|
|
|
@ -37,7 +37,7 @@ class TransactionTemplateForm(forms.ModelForm):
|
||||||
'api_url': '/api/note/note/',
|
'api_url': '/api/note/note/',
|
||||||
# We don't evaluate the content type at launch because the DB might be not initialized
|
# We don't evaluate the content type at launch because the DB might be not initialized
|
||||||
'api_url_suffix':
|
'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 ...',
|
'placeholder': 'Note ...',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -48,9 +48,7 @@ def not_empty_model_change_list(model_name):
|
||||||
return session.get("not_empty_model_change_list_" + model_name) == 1
|
return session.get("not_empty_model_change_list_" + model_name) == 1
|
||||||
|
|
||||||
|
|
||||||
def has_perm(t, obj, field=None):
|
def has_perm(perm, obj):
|
||||||
print(t)
|
|
||||||
perm = "." + t + ("__" + field if field else "_")
|
|
||||||
return PermissionBackend().has_perm(get_current_authenticated_user(), perm, obj)
|
return PermissionBackend().has_perm(get_current_authenticated_user(), perm, obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,35 @@ function addMsg(msg, alert_type) {
|
||||||
+ msg + "</div>\n";
|
+ msg + "</div>\n";
|
||||||
msgDiv.html(html);
|
msgDiv.html(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add Muliple error message from err_obj
|
* 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){
|
function errMsg(errs_obj){
|
||||||
for (const err_msg of Object.values(errs_obj)) {
|
for (const err_msg of Object.values(errs_obj)) {
|
||||||
addMsg(err_msg,'danger');
|
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
|
* Reload the balance of the user on the right top corner
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="card bg-light shadow">
|
<div id="activity_info" class="card bg-light shadow">
|
||||||
<div class="card-header text-center">
|
<div class="card-header text-center">
|
||||||
<h4>{{ activity.name }}</h4>
|
<h4>{{ activity.name }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,11 +38,23 @@
|
||||||
<dt class="col-xl-6">{% trans 'guest entry fee'|capfirst %}</dt>
|
<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>
|
<dd class="col-xl-6">{{ activity.activity_type.guest_entry_fee|pretty_money }}</dd>
|
||||||
{% endif %}
|
{% 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>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-footer text-center">
|
<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>
|
<a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_update' pk=activity.pk %}"> {% trans "edit"|capfirst %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if activity.activity_type.can_invite %}
|
{% if activity.activity_type.can_invite %}
|
||||||
|
@ -77,5 +89,42 @@
|
||||||
errMsg(xhr.responseJSON);
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in New Issue