2016-12-22 20:59:38 +01:00
|
|
|
from datetime import timedelta
|
2016-05-26 22:44:10 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
from custommail.shortcuts import send_mass_custom_mail
|
|
|
|
from dal.autocomplete import ModelSelect2
|
|
|
|
from django import forms
|
2012-07-11 17:39:20 +02:00
|
|
|
from django.contrib import admin
|
2019-06-08 15:05:14 +02:00
|
|
|
from django.db.models import Count, Q, Sum
|
2016-11-04 08:35:17 +01:00
|
|
|
from django.template.defaultfilters import pluralize
|
|
|
|
from django.utils import timezone
|
2017-11-19 18:41:39 +01:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
from bda.models import (
|
|
|
|
Attribution,
|
|
|
|
CategorieSpectacle,
|
|
|
|
ChoixSpectacle,
|
|
|
|
Participant,
|
|
|
|
Quote,
|
|
|
|
Salle,
|
|
|
|
Spectacle,
|
|
|
|
SpectacleRevente,
|
|
|
|
Tirage,
|
|
|
|
)
|
2012-07-11 17:39:20 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2017-04-08 17:16:33 +02:00
|
|
|
class ReadOnlyMixin(object):
|
|
|
|
readonly_fields_update = ()
|
|
|
|
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
|
|
readonly_fields = super().get_readonly_fields(request, obj)
|
|
|
|
if obj is None:
|
|
|
|
return readonly_fields
|
|
|
|
else:
|
|
|
|
return readonly_fields + self.readonly_fields_update
|
|
|
|
|
|
|
|
|
2017-11-19 18:41:39 +01:00
|
|
|
class ChoixSpectacleAdminForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
widgets = {
|
2018-10-06 12:35:49 +02:00
|
|
|
"participant": ModelSelect2(url="bda-participant-autocomplete"),
|
|
|
|
"spectacle": ModelSelect2(url="bda-spectacle-autocomplete"),
|
2017-11-19 18:41:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-11 17:39:20 +02:00
|
|
|
class ChoixSpectacleInline(admin.TabularInline):
|
|
|
|
model = ChoixSpectacle
|
2017-11-19 18:41:39 +01:00
|
|
|
form = ChoixSpectacleAdminForm
|
2012-07-11 17:39:20 +02:00
|
|
|
sortable_field_name = "priority"
|
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
class AttributionTabularAdminForm(forms.ModelForm):
|
|
|
|
listing = None
|
2017-04-08 17:16:33 +02:00
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
2018-10-06 12:35:49 +02:00
|
|
|
spectacles = Spectacle.objects.select_related("location")
|
2017-05-10 12:39:56 +02:00
|
|
|
if self.listing is not None:
|
|
|
|
spectacles = spectacles.filter(listing=self.listing)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.fields["spectacle"].queryset = spectacles
|
2017-04-08 17:16:33 +02:00
|
|
|
|
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
class WithoutListingAttributionTabularAdminForm(AttributionTabularAdminForm):
|
|
|
|
listing = False
|
2017-04-08 17:16:33 +02:00
|
|
|
|
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
class WithListingAttributionTabularAdminForm(AttributionTabularAdminForm):
|
|
|
|
listing = True
|
2017-04-08 17:16:33 +02:00
|
|
|
|
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
class AttributionInline(admin.TabularInline):
|
2013-09-05 22:20:52 +02:00
|
|
|
model = Attribution
|
2016-06-10 02:00:50 +02:00
|
|
|
extra = 0
|
2017-05-10 12:39:56 +02:00
|
|
|
listing = None
|
2016-07-10 13:19:10 +02:00
|
|
|
|
2016-06-10 02:00:50 +02:00
|
|
|
def get_queryset(self, request):
|
2017-04-08 17:16:33 +02:00
|
|
|
qs = super().get_queryset(request)
|
2017-05-10 12:39:56 +02:00
|
|
|
if self.listing is not None:
|
2017-09-25 18:26:54 +02:00
|
|
|
qs = qs.filter(spectacle__listing=self.listing)
|
2017-05-10 12:39:56 +02:00
|
|
|
return qs
|
2016-06-10 02:00:50 +02:00
|
|
|
|
2016-07-10 13:19:10 +02:00
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
class WithListingAttributionInline(AttributionInline):
|
2018-10-06 12:35:49 +02:00
|
|
|
exclude = ("given",)
|
2017-05-10 12:39:56 +02:00
|
|
|
form = WithListingAttributionTabularAdminForm
|
|
|
|
listing = True
|
2019-06-08 15:05:14 +02:00
|
|
|
verbose_name_plural = "Attributions sur listing"
|
2016-07-10 13:19:10 +02:00
|
|
|
|
2017-05-10 12:39:56 +02:00
|
|
|
|
|
|
|
class WithoutListingAttributionInline(AttributionInline):
|
|
|
|
form = WithoutListingAttributionTabularAdminForm
|
|
|
|
listing = False
|
2019-06-08 15:05:14 +02:00
|
|
|
verbose_name_plural = "Attributions hors listing"
|
2017-04-08 17:16:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
class ParticipantAdminForm(forms.ModelForm):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.fields["choicesrevente"].queryset = Spectacle.objects.select_related(
|
|
|
|
"location"
|
2017-04-08 17:16:33 +02:00
|
|
|
)
|
2013-09-05 22:20:52 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2019-06-08 15:05:14 +02:00
|
|
|
class ParticipantPaidFilter(admin.SimpleListFilter):
|
|
|
|
"""
|
|
|
|
Permet de filtrer les participants sur s'ils ont payé leurs places ou pas
|
|
|
|
"""
|
|
|
|
|
|
|
|
title = "A payé"
|
|
|
|
parameter_name = "paid"
|
|
|
|
|
|
|
|
def lookups(self, request, model_admin):
|
|
|
|
return ((True, "Oui"), (False, "Non"))
|
|
|
|
|
|
|
|
def queryset(self, request, queryset):
|
|
|
|
return queryset.filter(paid=self.value())
|
|
|
|
|
|
|
|
|
2017-04-08 17:16:33 +02:00
|
|
|
class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
2017-05-10 12:39:56 +02:00
|
|
|
inlines = [WithListingAttributionInline, WithoutListingAttributionInline]
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2016-05-24 00:25:29 +02:00
|
|
|
def get_queryset(self, request):
|
2019-06-17 21:36:09 +02:00
|
|
|
return self.model.objects.annotate_paid().annotate(
|
2019-06-08 15:05:14 +02:00
|
|
|
nb_places=Count("attributions"),
|
|
|
|
remain=Sum(
|
|
|
|
"attribution__spectacle__price", filter=Q(attribution__paid=False)
|
|
|
|
),
|
|
|
|
total=Sum("attributions__price"),
|
2018-10-06 12:35:49 +02:00
|
|
|
)
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2013-09-05 22:20:52 +02:00
|
|
|
def nb_places(self, obj):
|
2014-08-19 12:54:22 +02:00
|
|
|
return obj.nb_places
|
2018-10-06 12:35:49 +02:00
|
|
|
|
2014-08-19 12:54:22 +02:00
|
|
|
nb_places.admin_order_field = "nb_places"
|
2013-09-05 22:20:52 +02:00
|
|
|
nb_places.short_description = "Nombre de places"
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2019-06-08 15:05:14 +02:00
|
|
|
def paid(self, obj):
|
|
|
|
return obj.paid
|
|
|
|
|
|
|
|
paid.short_description = "A payé"
|
|
|
|
paid.boolean = True
|
|
|
|
paid.admin_order_field = "paid"
|
|
|
|
|
2013-09-05 22:20:52 +02:00
|
|
|
def total(self, obj):
|
2014-08-19 12:54:22 +02:00
|
|
|
tot = obj.total
|
2016-07-09 21:19:37 +02:00
|
|
|
if tot:
|
2016-05-26 22:44:10 +02:00
|
|
|
return "%.02f €" % tot
|
2016-07-09 21:19:37 +02:00
|
|
|
else:
|
2016-05-26 22:44:10 +02:00
|
|
|
return "0 €"
|
2018-10-06 12:35:49 +02:00
|
|
|
|
2014-08-19 12:54:22 +02:00
|
|
|
total.admin_order_field = "total"
|
2019-06-08 15:05:14 +02:00
|
|
|
total.short_description = "Total des places"
|
|
|
|
|
|
|
|
def remain(self, obj):
|
|
|
|
rem = obj.remain
|
|
|
|
if rem:
|
|
|
|
return "%.02f €" % rem
|
|
|
|
else:
|
|
|
|
return "0 €"
|
|
|
|
|
|
|
|
remain.admin_order_field = "remain"
|
|
|
|
remain.short_description = "Reste à payer"
|
|
|
|
|
|
|
|
list_display = ("user", "nb_places", "total", "paid", "remain", "tirage")
|
|
|
|
list_filter = (ParticipantPaidFilter, "tirage")
|
2018-10-06 12:35:49 +02:00
|
|
|
search_fields = ("user__username", "user__first_name", "user__last_name")
|
|
|
|
actions = ["send_attribs"]
|
2013-09-05 22:20:52 +02:00
|
|
|
actions_on_bottom = True
|
|
|
|
list_per_page = 400
|
2019-06-08 15:05:14 +02:00
|
|
|
readonly_fields = ("total", "paid")
|
2018-10-06 12:35:49 +02:00
|
|
|
readonly_fields_update = ("user", "tirage")
|
2017-04-08 17:16:33 +02:00
|
|
|
form = ParticipantAdminForm
|
2013-09-05 22:20:52 +02:00
|
|
|
|
|
|
|
def send_attribs(self, request, queryset):
|
2016-12-22 20:59:38 +01:00
|
|
|
datatuple = []
|
2013-09-05 22:20:52 +02:00
|
|
|
for member in queryset.all():
|
|
|
|
attribs = member.attributions.all()
|
2018-10-06 12:35:49 +02:00
|
|
|
context = {"member": member.user}
|
2016-12-22 20:59:38 +01:00
|
|
|
shortname = ""
|
2013-09-05 22:20:52 +02:00
|
|
|
if len(attribs) == 0:
|
2016-12-22 20:59:38 +01:00
|
|
|
shortname = "bda-attributions-decus"
|
2016-06-07 00:00:56 +02:00
|
|
|
else:
|
2016-12-22 20:59:38 +01:00
|
|
|
shortname = "bda-attributions"
|
2018-10-06 12:35:49 +02:00
|
|
|
context["places"] = attribs
|
2016-12-22 20:59:38 +01:00
|
|
|
print(context)
|
2018-10-06 12:35:49 +02:00
|
|
|
datatuple.append((shortname, context, "bda@ens.fr", [member.user.email]))
|
2016-12-22 20:59:38 +01:00
|
|
|
send_mass_custom_mail(datatuple)
|
2013-09-05 22:20:52 +02:00
|
|
|
count = len(queryset.all())
|
|
|
|
if count == 1:
|
2016-05-26 22:44:10 +02:00
|
|
|
message_bit = "1 membre a"
|
2013-09-05 22:20:52 +02:00
|
|
|
plural = ""
|
|
|
|
else:
|
2016-05-26 22:44:10 +02:00
|
|
|
message_bit = "%d membres ont" % count
|
2013-09-05 22:20:52 +02:00
|
|
|
plural = "s"
|
2018-10-06 12:35:49 +02:00
|
|
|
self.message_user(
|
|
|
|
request, "%s été informé%s avec succès." % (message_bit, plural)
|
|
|
|
)
|
|
|
|
|
2016-05-26 22:44:10 +02:00
|
|
|
send_attribs.short_description = "Envoyer les résultats par mail"
|
2013-09-05 22:20:52 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2016-06-25 23:17:31 +02:00
|
|
|
class AttributionAdminForm(forms.ModelForm):
|
2017-04-08 16:11:42 +02:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
2018-10-06 12:35:49 +02:00
|
|
|
if "spectacle" in self.fields:
|
|
|
|
self.fields["spectacle"].queryset = Spectacle.objects.select_related(
|
|
|
|
"location"
|
2017-04-08 16:11:42 +02:00
|
|
|
)
|
2018-10-06 12:35:49 +02:00
|
|
|
if "participant" in self.fields:
|
|
|
|
self.fields["participant"].queryset = Participant.objects.select_related(
|
|
|
|
"user", "tirage"
|
2017-04-08 16:11:42 +02:00
|
|
|
)
|
|
|
|
|
2016-06-25 23:17:31 +02:00
|
|
|
def clean(self):
|
2018-01-16 16:22:52 +01:00
|
|
|
cleaned_data = super().clean()
|
2016-06-25 23:17:31 +02:00
|
|
|
participant = cleaned_data.get("participant")
|
|
|
|
spectacle = cleaned_data.get("spectacle")
|
|
|
|
if participant and spectacle:
|
|
|
|
if participant.tirage != spectacle.tirage:
|
2016-07-09 22:31:56 +02:00
|
|
|
raise forms.ValidationError(
|
2016-07-15 00:02:56 +02:00
|
|
|
"Erreur : le participant et le spectacle n'appartiennent"
|
2018-10-06 12:35:49 +02:00
|
|
|
"pas au même tirage"
|
|
|
|
)
|
2016-06-27 13:18:58 +02:00
|
|
|
return cleaned_data
|
2016-06-25 23:17:31 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2017-04-08 16:11:42 +02:00
|
|
|
class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):
|
2018-10-06 12:35:49 +02:00
|
|
|
|
2013-09-05 22:20:52 +02:00
|
|
|
list_display = ("id", "spectacle", "participant", "given", "paid")
|
2018-10-06 12:35:49 +02:00
|
|
|
search_fields = (
|
|
|
|
"spectacle__title",
|
|
|
|
"participant__user__username",
|
|
|
|
"participant__user__first_name",
|
|
|
|
"participant__user__last_name",
|
|
|
|
)
|
2016-06-25 23:17:31 +02:00
|
|
|
form = AttributionAdminForm
|
2018-10-06 12:35:49 +02:00
|
|
|
readonly_fields_update = ("spectacle", "participant")
|
2013-09-05 22:20:52 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2013-09-05 22:20:52 +02:00
|
|
|
class ChoixSpectacleAdmin(admin.ModelAdmin):
|
2017-11-19 18:41:39 +01:00
|
|
|
form = ChoixSpectacleAdminForm
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2016-06-27 13:18:58 +02:00
|
|
|
def tirage(self, obj):
|
|
|
|
return obj.participant.tirage
|
2018-10-06 12:35:49 +02:00
|
|
|
|
|
|
|
list_display = ("participant", "tirage", "spectacle", "priority", "double_choice")
|
2016-06-27 13:18:58 +02:00
|
|
|
list_filter = ("double_choice", "participant__tirage")
|
2018-10-06 12:35:49 +02:00
|
|
|
search_fields = (
|
|
|
|
"participant__user__username",
|
|
|
|
"participant__user__first_name",
|
|
|
|
"participant__user__last_name",
|
|
|
|
"spectacle__title",
|
|
|
|
)
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2012-07-11 17:39:20 +02:00
|
|
|
|
2016-08-26 05:28:04 +02:00
|
|
|
class QuoteInline(admin.TabularInline):
|
|
|
|
model = Quote
|
|
|
|
|
|
|
|
|
2014-08-19 12:54:22 +02:00
|
|
|
class SpectacleAdmin(admin.ModelAdmin):
|
2016-08-26 05:28:04 +02:00
|
|
|
inlines = [QuoteInline]
|
2014-08-19 12:54:22 +02:00
|
|
|
model = Spectacle
|
2018-10-06 12:35:49 +02:00
|
|
|
list_display = ("title", "date", "tirage", "location", "slots", "price", "listing")
|
|
|
|
list_filter = ("location", "tirage")
|
2014-08-19 12:54:22 +02:00
|
|
|
search_fields = ("title", "location__name")
|
2018-10-06 12:35:49 +02:00
|
|
|
readonly_fields = ("rappel_sent",)
|
2014-08-19 12:54:22 +02:00
|
|
|
|
2016-07-09 22:31:56 +02:00
|
|
|
|
2016-06-10 17:04:00 +02:00
|
|
|
class TirageAdmin(admin.ModelAdmin):
|
|
|
|
model = Tirage
|
2018-10-06 12:35:49 +02:00
|
|
|
list_display = ("title", "ouverture", "fermeture", "active", "enable_do_tirage")
|
|
|
|
readonly_fields = ("tokens",)
|
|
|
|
list_filter = ("active",)
|
|
|
|
search_fields = ("title",)
|
2016-06-10 17:04:00 +02:00
|
|
|
|
2016-07-13 01:01:07 +02:00
|
|
|
|
|
|
|
class SalleAdmin(admin.ModelAdmin):
|
|
|
|
model = Salle
|
2018-10-06 12:35:49 +02:00
|
|
|
search_fields = ("name", "address")
|
2016-07-13 01:01:07 +02:00
|
|
|
|
|
|
|
|
2017-04-08 17:50:36 +02:00
|
|
|
class SpectacleReventeAdminForm(forms.ModelForm):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.fields["confirmed_entry"].queryset = Participant.objects.select_related(
|
|
|
|
"user", "tirage"
|
2017-04-08 17:50:36 +02:00
|
|
|
)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.fields["seller"].queryset = Participant.objects.select_related(
|
|
|
|
"user", "tirage"
|
2017-04-08 17:50:36 +02:00
|
|
|
)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.fields["soldTo"].queryset = Participant.objects.select_related(
|
|
|
|
"user", "tirage"
|
2017-04-08 17:50:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2016-07-22 22:48:09 +02:00
|
|
|
class SpectacleReventeAdmin(admin.ModelAdmin):
|
2016-11-04 08:35:17 +01:00
|
|
|
"""
|
|
|
|
Administration des reventes de spectacles
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
|
2016-07-22 22:48:09 +02:00
|
|
|
model = SpectacleRevente
|
|
|
|
|
|
|
|
def spectacle(self, obj):
|
2016-11-04 08:35:17 +01:00
|
|
|
"""
|
|
|
|
Raccourci vers le spectacle associé à la revente.
|
|
|
|
"""
|
2016-07-22 22:48:09 +02:00
|
|
|
return obj.attribution.spectacle
|
|
|
|
|
2016-09-05 03:10:06 +02:00
|
|
|
list_display = ("spectacle", "seller", "date", "soldTo")
|
2016-07-23 22:21:30 +02:00
|
|
|
raw_id_fields = ("attribution",)
|
2016-11-14 15:52:02 +01:00
|
|
|
readonly_fields = ("date_tirage",)
|
2018-10-06 12:35:49 +02:00
|
|
|
search_fields = [
|
|
|
|
"attribution__spectacle__title",
|
|
|
|
"seller__user__username",
|
|
|
|
"seller__user__first_name",
|
|
|
|
"seller__user__last_name",
|
|
|
|
]
|
|
|
|
|
|
|
|
actions = ["transfer", "reinit"]
|
2016-10-28 03:46:57 +02:00
|
|
|
actions_on_bottom = True
|
2017-04-08 17:50:36 +02:00
|
|
|
form = SpectacleReventeAdminForm
|
2016-10-28 03:46:57 +02:00
|
|
|
|
|
|
|
def transfer(self, request, queryset):
|
2016-11-04 08:35:17 +01:00
|
|
|
"""
|
|
|
|
Effectue le transfert des reventes pour lesquels on connaît l'acheteur.
|
|
|
|
"""
|
|
|
|
reventes = queryset.exclude(soldTo__isnull=True).all()
|
|
|
|
count = reventes.count()
|
|
|
|
for revente in reventes:
|
2016-10-28 18:15:37 +02:00
|
|
|
attrib = revente.attribution
|
|
|
|
attrib.participant = revente.soldTo
|
|
|
|
attrib.save()
|
2016-11-04 08:35:17 +01:00
|
|
|
self.message_user(
|
|
|
|
request,
|
2018-10-06 12:35:49 +02:00
|
|
|
"%d attribution%s %s été transférée%s avec succès."
|
|
|
|
% (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)),
|
|
|
|
)
|
|
|
|
|
2016-10-28 03:46:57 +02:00
|
|
|
transfer.short_description = "Transférer les reventes sélectionnées"
|
|
|
|
|
|
|
|
def reinit(self, request, queryset):
|
2016-11-04 08:35:17 +01:00
|
|
|
"""
|
|
|
|
Réinitialise les reventes.
|
|
|
|
"""
|
|
|
|
count = queryset.count()
|
2016-11-08 13:41:12 +01:00
|
|
|
for revente in queryset.filter(
|
2018-10-06 12:35:49 +02:00
|
|
|
attribution__spectacle__date__gte=timezone.now()
|
|
|
|
):
|
2017-12-19 12:50:20 +01:00
|
|
|
revente.reset(new_date=timezone.now() - timedelta(hours=1))
|
2016-11-04 08:35:17 +01:00
|
|
|
self.message_user(
|
|
|
|
request,
|
2018-10-06 12:35:49 +02:00
|
|
|
"%d attribution%s %s été réinitialisée%s avec succès."
|
|
|
|
% (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)),
|
|
|
|
)
|
|
|
|
|
2016-10-28 03:46:57 +02:00
|
|
|
reinit.short_description = "Réinitialiser les reventes sélectionnées"
|
|
|
|
|
2016-07-22 22:48:09 +02:00
|
|
|
|
2016-08-26 05:28:04 +02:00
|
|
|
admin.site.register(CategorieSpectacle)
|
2014-08-19 12:54:22 +02:00
|
|
|
admin.site.register(Spectacle, SpectacleAdmin)
|
2016-07-13 01:01:07 +02:00
|
|
|
admin.site.register(Salle, SalleAdmin)
|
2012-07-11 17:39:20 +02:00
|
|
|
admin.site.register(Participant, ParticipantAdmin)
|
2013-09-05 22:20:52 +02:00
|
|
|
admin.site.register(Attribution, AttributionAdmin)
|
|
|
|
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
|
2016-06-10 17:04:00 +02:00
|
|
|
admin.site.register(Tirage, TirageAdmin)
|
2016-07-22 22:48:09 +02:00
|
|
|
admin.site.register(SpectacleRevente, SpectacleReventeAdmin)
|