création d'evenements
This commit is contained in:
parent
52b413b3a6
commit
1c08754905
|
|
@ -53,6 +53,7 @@ class UserAdminTibillet(UserAdmin):
|
||||||
'first_name',
|
'first_name',
|
||||||
'last_name',
|
'last_name',
|
||||||
'email',
|
'email',
|
||||||
|
'phone',
|
||||||
'client_source',
|
'client_source',
|
||||||
'client_admin',
|
'client_admin',
|
||||||
'client_achat',
|
'client_achat',
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.models import Group
|
||||||
from solo.admin import SingletonModelAdmin
|
from solo.admin import SingletonModelAdmin
|
||||||
|
|
||||||
from AuthBillet.models import HumanUser, SuperHumanUser, TermUser
|
from AuthBillet.models import HumanUser, SuperHumanUser, TermUser
|
||||||
from BaseBillet.models import Configuration, Event, OptionGenerale
|
from BaseBillet.models import Configuration, Event, OptionGenerale, Article, Billet, Reservation, LigneArticle
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
|
||||||
from Customers.models import Client
|
from Customers.models import Client
|
||||||
|
|
@ -124,4 +124,25 @@ class OptionGeneraleAdmin(admin.ModelAdmin):
|
||||||
'poids',
|
'poids',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class ReservationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
'user_mail',
|
||||||
|
'total_billet',
|
||||||
|
'_options_',
|
||||||
|
'total_prix',
|
||||||
|
'status'
|
||||||
|
)
|
||||||
|
readonly_fields = list_display
|
||||||
|
|
||||||
|
staff_admin_site.register(Reservation, ReservationAdmin)
|
||||||
|
|
||||||
|
|
||||||
staff_admin_site.register(OptionGenerale, OptionGeneraleAdmin)
|
staff_admin_site.register(OptionGenerale, OptionGeneraleAdmin)
|
||||||
|
|
||||||
|
staff_admin_site.register(Billet, admin.ModelAdmin)
|
||||||
|
staff_admin_site.register(Article, admin.ModelAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
staff_admin_site.register(LigneArticle, admin.ModelAdmin)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from templated_mail.mail import BaseEmailMessage
|
from templated_mail.mail import BaseEmailMessage
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from djoser import utils
|
from djoser import utils
|
||||||
from djoser.conf import settings
|
from djoser.conf import settings
|
||||||
|
|
||||||
from BaseBillet.models import Configuration
|
|
||||||
|
|
||||||
|
|
||||||
class ActivationEmail(BaseEmailMessage):
|
class ActivationEmail(BaseEmailMessage):
|
||||||
|
|
@ -28,9 +28,8 @@ class ActivationEmail(BaseEmailMessage):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def send(self, to, *args, **kwargs):
|
def send(self, to, *args, **kwargs):
|
||||||
configuration = Configuration.get_solo()
|
|
||||||
if not configuration.email :
|
from_email = kwargs.get('from_email', 'contact@tibillet.re')
|
||||||
return serializers.ValidationError(_(f"Manque l'email de la structure. Merci de configurer votre instance."))
|
|
||||||
|
|
||||||
self.render()
|
self.render()
|
||||||
|
|
||||||
|
|
@ -39,11 +38,12 @@ class ActivationEmail(BaseEmailMessage):
|
||||||
self.bcc = kwargs.pop('bcc', [])
|
self.bcc = kwargs.pop('bcc', [])
|
||||||
self.reply_to = kwargs.pop('reply_to', [])
|
self.reply_to = kwargs.pop('reply_to', [])
|
||||||
self.from_email = kwargs.pop(
|
self.from_email = kwargs.pop(
|
||||||
'from_email', configuration.email
|
'from_email', from_email
|
||||||
)
|
)
|
||||||
|
|
||||||
# import ipdb; ipdb.set_trace()
|
# import ipdb; ipdb.set_trace()
|
||||||
mail_send = super(BaseEmailMessage, self).send(*args, **kwargs)
|
mail_send = super(BaseEmailMessage, self).send(*args, **kwargs)
|
||||||
|
|
||||||
print(f'mail_send to {self.to} from {self.from_email} : {mail_send}')
|
print(f'mail_send to {self.to} from {self.from_email} : {mail_send}')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ class TibilletUser(AbstractUser):
|
||||||
|
|
||||||
email = models.EmailField(_('email'), unique=True) # changes email to unique and blank to false
|
email = models.EmailField(_('email'), unique=True) # changes email to unique and blank to false
|
||||||
username = models.CharField(max_length=200, null=True, blank=True)
|
username = models.CharField(max_length=200, null=True, blank=True)
|
||||||
|
phone = models.CharField(max_length=20, null=True, blank=True)
|
||||||
|
|
||||||
TYPE_TERM, TYPE_HUM, TYPE_ANDR = 'TE', 'HU', 'AN'
|
TYPE_TERM, TYPE_HUM, TYPE_ANDR = 'TE', 'HU', 'AN'
|
||||||
ESPECE_CHOICES = (
|
ESPECE_CHOICES = (
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
|
from AuthBillet import views as auth_view
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
re_path('activate/<str:uid>/<str:token>', auth_view.activate.as_view()),
|
||||||
|
]
|
||||||
|
|
@ -1,3 +1,16 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
|
||||||
|
class activate(APIView):
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def get(self, uid, token):
|
||||||
|
print(uid, token)
|
||||||
|
|
||||||
|
return Response(f'{uid} {token}', status=status.HTTP_200_OK)
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
@ -8,6 +11,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from stdimage import StdImageField
|
from stdimage import StdImageField
|
||||||
from stdimage.validators import MaxSizeValidator
|
from stdimage.validators import MaxSizeValidator
|
||||||
|
|
||||||
|
from TiBillet import settings
|
||||||
|
|
||||||
|
|
||||||
class OptionGenerale(models.Model):
|
class OptionGenerale(models.Model):
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
|
|
@ -60,8 +65,6 @@ class Configuration(SingletonModel):
|
||||||
mollie_api_key = models.CharField(max_length=50,
|
mollie_api_key = models.CharField(max_length=50,
|
||||||
blank=True, null=True)
|
blank=True, null=True)
|
||||||
|
|
||||||
reservation_par_user_max = models.PositiveSmallIntegerField(default=6)
|
|
||||||
|
|
||||||
jauge_max = models.PositiveSmallIntegerField(default=50)
|
jauge_max = models.PositiveSmallIntegerField(default=50)
|
||||||
|
|
||||||
option_generale_radio = models.ManyToManyField(OptionGenerale,
|
option_generale_radio = models.ManyToManyField(OptionGenerale,
|
||||||
|
|
@ -73,11 +76,42 @@ class Configuration(SingletonModel):
|
||||||
related_name="checkbox")
|
related_name="checkbox")
|
||||||
|
|
||||||
|
|
||||||
|
class Billet(models.Model):
|
||||||
|
name = models.CharField(max_length=50,
|
||||||
|
blank=True, null=True)
|
||||||
|
prix = models.FloatField()
|
||||||
|
|
||||||
|
reservation_par_user_max = models.PositiveSmallIntegerField(default=6)
|
||||||
|
|
||||||
|
def range_max(self):
|
||||||
|
return range(self.reservation_par_user_max + 1)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name}"
|
||||||
|
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
name = models.CharField(max_length=50,
|
||||||
|
blank=True, null=True)
|
||||||
|
prix = models.FloatField()
|
||||||
|
stock = models.SmallIntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
reservation_par_user_max = models.PositiveSmallIntegerField(default=10)
|
||||||
|
|
||||||
|
def range_max(self):
|
||||||
|
return range(self.reservation_par_user_max + 1)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name}"
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
short_description = models.CharField(max_length=250)
|
short_description = models.CharField(max_length=250)
|
||||||
long_description = models.TextField(blank=True, null=True)
|
long_description = models.TextField(blank=True, null=True)
|
||||||
datetime = models.DateTimeField()
|
datetime = models.DateTimeField()
|
||||||
|
billets = models.ManyToManyField(Billet)
|
||||||
|
articles = models.ManyToManyField(Article)
|
||||||
|
|
||||||
img = StdImageField(upload_to='images/',
|
img = StdImageField(upload_to='images/',
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
|
|
@ -105,19 +139,74 @@ class Event(models.Model):
|
||||||
verbose_name_plural = _('Evenements')
|
verbose_name_plural = _('Evenements')
|
||||||
|
|
||||||
|
|
||||||
class reservation(models.Model):
|
class Reservation(models.Model):
|
||||||
|
uuid = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4)
|
||||||
|
|
||||||
|
user_commande = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||||
|
|
||||||
event = models.ForeignKey(Event,
|
event = models.ForeignKey(Event,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="reservation")
|
related_name="reservation")
|
||||||
|
|
||||||
ANNULEE, NON_VALIDEE, VALIDEE, PAYEE = 'NAN', 'NOV', 'VAL', 'PAY'
|
ANNULEE, MAIL_NON_VALIDEE, NON_PAYEE, VALIDEE, PAYEE = 'NAN', 'MNV', 'NPA', 'VAL', 'PAY'
|
||||||
TYPE_CHOICES = [
|
TYPE_CHOICES = [
|
||||||
(ANNULEE, _('Annulée')),
|
(ANNULEE, _('Annulée')),
|
||||||
(NON_VALIDEE, _('Email non validé')),
|
(MAIL_NON_VALIDEE, _('Email non validé')),
|
||||||
|
(NON_PAYEE, _('Non payée')),
|
||||||
(VALIDEE, _('Validée')),
|
(VALIDEE, _('Validée')),
|
||||||
(PAYEE, _('Payée')),
|
(PAYEE, _('Payée')),
|
||||||
]
|
]
|
||||||
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=NON_VALIDEE,
|
|
||||||
|
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=NON_PAYEE,
|
||||||
verbose_name=_("Status de la réservation"))
|
verbose_name=_("Status de la réservation"))
|
||||||
|
|
||||||
qty = models.IntegerField()
|
options = models.ManyToManyField(OptionGenerale)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.user_commande.email
|
||||||
|
|
||||||
|
def user_mail(self):
|
||||||
|
return self.user_commande.email
|
||||||
|
|
||||||
|
def total_billet(self):
|
||||||
|
total = 0
|
||||||
|
for ligne in self.lignearticle_set.all():
|
||||||
|
if ligne.billet:
|
||||||
|
total += ligne.qty
|
||||||
|
return total
|
||||||
|
|
||||||
|
def total_prix(self):
|
||||||
|
total = 0
|
||||||
|
for ligne in self.lignearticle_set.all():
|
||||||
|
if ligne.article :
|
||||||
|
total += ligne.qty * ligne.article.prix
|
||||||
|
if ligne.billet :
|
||||||
|
total += ligne.qty * ligne.billet.prix
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def _options_(self):
|
||||||
|
return " - ".join([f"{option.name}" for option in self.options.all()])
|
||||||
|
|
||||||
|
@receiver(post_save, sender=Reservation)
|
||||||
|
def poids_option_generaler(sender, instance: Reservation, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
if not instance.user_commande.is_active :
|
||||||
|
instance.status = instance.MAIL_NON_VALIDEE
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LigneArticle(models.Model):
|
||||||
|
uuid = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4)
|
||||||
|
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, verbose_name="lignes_article")
|
||||||
|
article = models.ForeignKey(Article, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
billet = models.ForeignKey(Billet, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
qty = models.SmallIntegerField()
|
||||||
|
reste = models.SmallIntegerField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.article :
|
||||||
|
return f"{self.reservation.user_commande.email} {self.qty} {self.article}"
|
||||||
|
if self.billet :
|
||||||
|
return f"{self.reservation.user_commande.email} {self.qty} {self.billet}"
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,27 @@
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<!--
|
<!--
|
||||||
Massively by HTML5 UP
|
Massively by HTML5 UP
|
||||||
html5up.net | @ajlkn
|
html5up.net | @ajlkn
|
||||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||||
-->
|
-->
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<title>{{ configuration.organisation }}</title>
|
<title>{{ configuration.organisation }}</title>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<style>
|
<style>
|
||||||
#wrapper > .bg {
|
#wrapper > .bg {
|
||||||
background-image: url("{% static 'html5up-massively/images/overlay.png' %}"), linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("/media/{{ configuration.img }}");
|
background-image: url("{% static 'html5up-massively/images/overlay.png' %}"), linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("/media/{{ configuration.img }}");
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="{% static 'html5up-massively/assets/css/main.css' %}"/>
|
<link rel="stylesheet" href="{% static 'html5up-massively/assets/css/main.css' %}"/>
|
||||||
|
|
||||||
{# <noscript>#}
|
{# <noscript>#}
|
||||||
{# <link rel="stylesheet" href="{% static 'html5up-massively/assets/css/noscript.css' %}"/>#}
|
{# <link rel="stylesheet" href="{% static 'html5up-massively/assets/css/noscript.css' %}"/>#}
|
||||||
{# </noscript>#}
|
{# </noscript>#}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
{% endblock header %}
|
{% endblock header %}
|
||||||
|
|
||||||
|
|
@ -31,23 +31,22 @@
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
|
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
|
|
||||||
{# <footer>#}
|
{# <footer>#}
|
||||||
{# <div class="pagination">#}
|
{# <div class="pagination">#}
|
||||||
{# <!--<a href="#" class="previous">Prev</a>-->#}
|
{# <!--<a href="#" class="previous">Prev</a>-->#}
|
||||||
{# <a href="#" class="page active">1</a>#}
|
{# <a href="#" class="page active">1</a>#}
|
||||||
{# <a href="#" class="page">2</a>#}
|
{# <a href="#" class="page">2</a>#}
|
||||||
{# <a href="#" class="page">3</a>#}
|
{# <a href="#" class="page">3</a>#}
|
||||||
{# <span class="extra">…</span>#}
|
{# <span class="extra">…</span>#}
|
||||||
{# <a href="#" class="page">8</a>#}
|
{# <a href="#" class="page">8</a>#}
|
||||||
{# <a href="#" class="page">9</a>#}
|
{# <a href="#" class="page">9</a>#}
|
||||||
{# <a href="#" class="page">10</a>#}
|
{# <a href="#" class="page">10</a>#}
|
||||||
{# <a href="#" class="next">Next</a>#}
|
{# <a href="#" class="next">Next</a>#}
|
||||||
{# </div>#}
|
{# </div>#}
|
||||||
{# </footer>#}
|
{# </footer>#}
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer id="footer">
|
<footer id="footer">
|
||||||
|
|
@ -83,7 +82,7 @@
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h3>Email</h3>
|
<h3>Email</h3>
|
||||||
<p>{{ configuration.email }}</p>
|
<p><a href="mailto:{{ configuration.email }}">{{ configuration.email }}</a></p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h3>Social</h3>
|
<h3>Social</h3>
|
||||||
|
|
@ -115,19 +114,17 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scripts -->
|
<script src="{% static 'html5up-massively/assets/js/jquery.min.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/jquery.min.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/jquery.scrollex.min.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/jquery.scrollex.min.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/jquery.scrolly.min.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/jquery.scrolly.min.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/browser.min.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/browser.min.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/breakpoints.min.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/breakpoints.min.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/util.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/util.js' %}"></script>
|
<script src="{% static 'html5up-massively/assets/js/main.js' %}"></script>
|
||||||
<script src="{% static 'html5up-massively/assets/js/main.js' %}"></script>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
{% endblock footer %}
|
{% endblock footer %}
|
||||||
|
|
|
||||||
|
|
@ -3,122 +3,153 @@
|
||||||
<!-- Wrapper -->
|
<!-- Wrapper -->
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<header id="header">
|
<header id="header">
|
||||||
<a href="/" class="logo">{{ configuration.organisation }}</a>
|
<a href="/" class="logo">{{ configuration.organisation }}</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Nav -->
|
<!-- Nav -->
|
||||||
<nav id="nav">
|
<nav id="nav">
|
||||||
<ul class="links">
|
<ul class="links">
|
||||||
<li class="active"><a href="index.html">Billetterie</a></li>
|
<li class="active"><a href="index.html">Billetterie</a></li>
|
||||||
<li><a href="">Informations</a></li>
|
<li><a href="">Informations</a></li>
|
||||||
<li><a href="">Cashless</a></li>
|
<li><a href="">Cashless</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="icons">
|
<ul class="icons">
|
||||||
{% if configuration.twitter %}
|
{% if configuration.twitter %}
|
||||||
<li><a href="{{ configuration.twitter }}" class="icon brands fa-twitter"><span
|
<li><a href="{{ configuration.twitter }}" class="icon brands fa-twitter"><span
|
||||||
class="label">Twitter</span></a></li>
|
class="label">Twitter</span></a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if configuration.facebook %}
|
{% if configuration.facebook %}
|
||||||
<li><a href="{{ configuration.facebook }}" class="icon brands fa-facebook-f"><span
|
<li><a href="{{ configuration.facebook }}" class="icon brands fa-facebook-f"><span
|
||||||
class="label">Facebook</span></a>
|
class="label">Facebook</span></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if configuration.instagram %}
|
{% if configuration.instagram %}
|
||||||
<li><a href="{{ configuration.instagram }}" class="icon brands fa-instagram"><span
|
<li><a href="{{ configuration.instagram }}" class="icon brands fa-instagram"><span
|
||||||
class="label">Instagram</span></a>
|
class="label">Instagram</span></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<div id="main">
|
<div id="main">
|
||||||
|
|
||||||
<!-- Post -->
|
<!-- Post -->
|
||||||
<section class="post">
|
<section class="post">
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<span class="date">{{ event.datetime | date:"d F Y" }}<br/>{{ event.datetime | time }}</span>
|
<span class="date">{{ event.datetime | date:"d F Y" }}<br/>{{ event.datetime | time }}</span>
|
||||||
<h1>{{ event.name }}</h1>
|
<h1>{{ event.name }}</h1>
|
||||||
<p>{{ event.short_description }}</p>
|
<p>{{ event.short_description }}</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="image main"><img src="/media/{{ event.img.fhd }}" alt=""/></div>
|
<div class="image main"><img src="/media/{{ event.img.fhd }}" alt=""/></div>
|
||||||
<p>{{ event.long_description | linebreaks }}</p>
|
<p>{{ event.long_description | linebreaks }}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
{% if not event.complet %}
|
{% if not event.complet %}
|
||||||
<!-- Form -->
|
<!-- Form -->
|
||||||
|
|
||||||
<form id="reservation" method="post" action="#">
|
<form id="reservation" method="post" action="#">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h2>RESERVEZ</h2>
|
<h2>RESERVEZ</h2>
|
||||||
<div class="row gtr-uniform">
|
<div class="row gtr-uniform">
|
||||||
<div class="col-6 col-12-xsmall">
|
<div class="col-6 col-12-xsmall">
|
||||||
<input type="text" name="nom" id="nom" value="" placeholder="Nom" required="True"/>
|
<input type="text" name="nom" id="nom" value="" placeholder="Nom" required="True"/>
|
||||||
</div>
|
|
||||||
<div class="col-6 col-12-xsmall">
|
|
||||||
<input type="text" name="prenom" id="prenom" value="" placeholder="Prénom" required="True"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-12-xsmall">
|
|
||||||
<input type="email" name="email" id="email" value="" placeholder="Email" required="True"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-12-xsmall">
|
|
||||||
<input type="text" name="phone" id="phone" value="" placeholder="Téléphone" required="True"/>
|
|
||||||
</div>
|
|
||||||
<!-- Break -->
|
|
||||||
<div class="col-12">
|
|
||||||
<select name="qty" id="qty" required="True">
|
|
||||||
<option value="">- Quantitée -</option>
|
|
||||||
{# {% for nbr in configuration.reservation_max %}#}
|
|
||||||
<option value="{{ configuration.reservation_par_user_max }}">{{ configuration.reservation_par_user_max }}</option>
|
|
||||||
{# {% endfor %}#}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<!-- Break -->
|
|
||||||
{% for option_radio in configuration.option_generale_radio.all %}
|
|
||||||
<div class="col-4 col-12-small">
|
|
||||||
<input type="radio" id="{{ option_radio.name }}" name="radio_generale"
|
|
||||||
value="{{ option_radio.pk }}">
|
|
||||||
<label for="{{ option_radio.name }}">{{ option_radio.name }}</label>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
<div class="col-6 col-12-xsmall">
|
||||||
<!-- Break -->
|
<input type="text" name="prenom" id="prenom" value="" placeholder="Prénom" required="True"/>
|
||||||
{% for option_checkbox in configuration.option_generale_checkbox.all %}
|
</div>
|
||||||
|
<div class="col-12 col-12-xsmall">
|
||||||
<div class="col-6 col-12-small">
|
<input id="email" name="email" placeholder="Email" required="True" type="email" value=""/>
|
||||||
<input type="checkbox" id="{{ option_checkbox.name }}" name="{{ option_checkbox.name }}"
|
</div>
|
||||||
value="{{ option_checkbox.pk }}">
|
<div class="col-12 col-12-xsmall">
|
||||||
<label for="{{ option_checkbox.name }}">{{ option_checkbox.name }}</label>
|
<input id="phone" name="phone" placeholder="Téléphone" required="True" type="text"
|
||||||
|
value=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endfor %}
|
<!-- Break -->
|
||||||
<!-- Break -->
|
{% for billet in event.billets.all %}
|
||||||
{# <div class="col-12">#}
|
<div class="col-4 col-12-medium col-12-small">
|
||||||
{# <textarea name="demo-message" id="demo-message" placeholder="Enter your message"#}
|
<label for="{{ billet.name }}">
|
||||||
{# rows="6"></textarea>#}
|
BILLET {{ billet.name }} : {{ billet.prix | floatformat:"-2" }}€
|
||||||
{# </div>#}
|
</label>
|
||||||
<!-- Break -->
|
<select name="billets" id="{{ billet.name }}">
|
||||||
<div class="col-12">
|
{% for nbr in billet.range_max %}
|
||||||
<ul class="actions">
|
<option value="{{ billet.pk }},{{ nbr }}">{{ nbr }}</option>
|
||||||
<li><input type="submit" value="validez" class="primary"/></li>
|
{% endfor %}
|
||||||
<li><input type="reset" value="Reset"/></li>
|
</select>
|
||||||
</ul>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for article in event.articles.all %}
|
||||||
|
<div class="col-4 col-12-medium col-12-small">
|
||||||
|
<label for="{{ article.name }}">
|
||||||
|
{{ article.name }} : {{ article.prix | floatformat:"-2" }}€
|
||||||
|
</label>
|
||||||
|
<select name="articles" id="{{ article.name }}">
|
||||||
|
{% for nbr in article.range_max %}
|
||||||
|
<option value="{{ article.pk }},{{ nbr }}">{{ nbr }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- Break -->
|
||||||
|
{% for option_radio in configuration.option_generale_radio.all %}
|
||||||
|
<div class="col-12-small">
|
||||||
|
<input type="radio" id="{{ option_radio.name }}" name="radio_generale"
|
||||||
|
value="{{ option_radio.pk }}">
|
||||||
|
<label for="{{ option_radio.name }}">{{ option_radio.name }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<!-- Break -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
{% for option_checkbox in configuration.option_generale_checkbox.all %}
|
||||||
|
|
||||||
|
<div class="col-12-small">
|
||||||
|
<input type="checkbox" id="{{ option_checkbox.pk }}"
|
||||||
|
name="option_checkbox"
|
||||||
|
value="{{ option_checkbox.pk }}">
|
||||||
|
<label for="{{ option_checkbox.pk }}">{{ option_checkbox.name }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Break -->
|
||||||
|
{# <div class="col-12">#}
|
||||||
|
{# <textarea name="demo-message" id="demo-message" placeholder="Enter your message"#}
|
||||||
|
{# rows="6"></textarea>#}
|
||||||
|
{# </div>#}
|
||||||
|
<!-- Break -->
|
||||||
|
<div class="col-12">
|
||||||
|
<ul class="actions">
|
||||||
|
<li><input type="submit" value="validez" class="primary"/></li>
|
||||||
|
<li><input type="reset" value="Reset"/></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</brdiv>
|
</form>
|
||||||
</form>
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<h2>COMPLET</h2>
|
<h2>COMPLET</h2>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,38 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from BaseBillet.models import OptionGenerale, Configuration, Event
|
from BaseBillet.models import OptionGenerale, Configuration, Event, Article, Billet
|
||||||
|
|
||||||
|
|
||||||
class ReservationValidator(serializers.Serializer):
|
class ReservationValidator(serializers.Serializer):
|
||||||
|
|
||||||
nom = serializers.CharField(max_length=100, required=True)
|
nom = serializers.CharField(max_length=100, required=True)
|
||||||
prenom = serializers.CharField(max_length=100, required=True)
|
prenom = serializers.CharField(max_length=100, required=True)
|
||||||
email = serializers.EmailField(required=True)
|
email = serializers.EmailField(required=True)
|
||||||
phone = serializers.CharField(max_length=100, required=True)
|
phone = serializers.CharField(max_length=20, required=True)
|
||||||
qty = serializers.IntegerField(required=True)
|
|
||||||
radio_generale = serializers.PrimaryKeyRelatedField(queryset=OptionGenerale.objects.all(), many=True)
|
radio_generale = serializers.PrimaryKeyRelatedField(queryset=OptionGenerale.objects.all(), many=True)
|
||||||
|
option_checkbox = serializers.PrimaryKeyRelatedField(queryset=OptionGenerale.objects.all(), many=True)
|
||||||
|
articles = serializers.ListField()
|
||||||
|
billets = serializers.ListField()
|
||||||
|
|
||||||
def validate_qty(self, value):
|
def validate_articles(self, value):
|
||||||
configuration = Configuration.get_solo()
|
value_dict = {}
|
||||||
if value <= configuration.reservation_par_user_max :
|
art_obj = Article.objects.all()
|
||||||
return value
|
for couple in value:
|
||||||
else :
|
pk, qty = art_obj.get(pk=couple.split(',')[0]), int(couple.split(',')[1])
|
||||||
raise serializers.ValidationError(_(f"Pas plus de {configuration.reservation_par_user_max} places en même temps."))
|
value_dict[pk] = qty
|
||||||
|
|
||||||
|
return value_dict
|
||||||
|
|
||||||
|
def validate_billets(self, value):
|
||||||
|
value_dict = {}
|
||||||
|
billet_obj = Billet.objects.all()
|
||||||
|
for couple in value:
|
||||||
|
pk, qty = billet_obj.get(pk=couple.split(',')[0]), int(couple.split(',')[1])
|
||||||
|
value_dict[pk] = qty
|
||||||
|
|
||||||
|
return value_dict
|
||||||
|
#
|
||||||
|
# # if value <= configuration.reservation_par_user_max :
|
||||||
|
# return value
|
||||||
|
# else :
|
||||||
|
# raise serializers.ValidationError(_(f"Pas plus de {configuration.reservation_par_user_max} places en même temps."))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
@ -10,8 +11,13 @@ from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from BaseBillet.models import Configuration, Event
|
from AuthBillet.email import ActivationEmail
|
||||||
|
from BaseBillet.models import Configuration, Event, Reservation, LigneArticle
|
||||||
from BaseBillet.validator import ReservationValidator
|
from BaseBillet.validator import ReservationValidator
|
||||||
|
from django.db import connection
|
||||||
|
from AuthBillet.models import TibilletUser
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
|
||||||
class index(APIView):
|
class index(APIView):
|
||||||
|
|
@ -21,9 +27,9 @@ class index(APIView):
|
||||||
configuration = Configuration.get_solo()
|
configuration = Configuration.get_solo()
|
||||||
|
|
||||||
events = Event.objects.filter(datetime__gt=datetime.now())
|
events = Event.objects.filter(datetime__gt=datetime.now())
|
||||||
if len(events) > 0 :
|
if len(events) > 0:
|
||||||
first_event = events[0]
|
first_event = events[0]
|
||||||
else :
|
else:
|
||||||
first_event = None
|
first_event = None
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
|
|
@ -35,7 +41,40 @@ class index(APIView):
|
||||||
return render(request, 'html5up-massively/index.html', context=context)
|
return render(request, 'html5up-massively/index.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
|
def creation_de_la_reservation(user: TibilletUser, event: Event, data):
|
||||||
|
|
||||||
|
reservation = Reservation.objects.create(
|
||||||
|
user_commande = user,
|
||||||
|
event= event,
|
||||||
|
)
|
||||||
|
|
||||||
|
if data.get('radio_generale'):
|
||||||
|
reservation.options.add(data.get('radio_generale')[0])
|
||||||
|
|
||||||
|
for option_checkbox in data.get('option_checkbox'):
|
||||||
|
reservation.options.add(option_checkbox)
|
||||||
|
|
||||||
|
# import ipdb; ipdb.set_trace()
|
||||||
|
#
|
||||||
|
for billet in data.get('billets'):
|
||||||
|
qty = data.get('billets')[billet]
|
||||||
|
LigneArticle.objects.create(
|
||||||
|
reservation = reservation,
|
||||||
|
billet = billet,
|
||||||
|
qty = qty,
|
||||||
|
reste = qty,
|
||||||
|
)
|
||||||
|
|
||||||
|
for article in data.get('articles'):
|
||||||
|
qty = data.get('articles')[article]
|
||||||
|
LigneArticle.objects.create(
|
||||||
|
reservation = reservation,
|
||||||
|
article = article,
|
||||||
|
qty = qty,
|
||||||
|
reste = qty,
|
||||||
|
)
|
||||||
|
|
||||||
|
return reservation
|
||||||
|
|
||||||
class event(APIView):
|
class event(APIView):
|
||||||
permission_classes = [AllowAny]
|
permission_classes = [AllowAny]
|
||||||
|
|
@ -53,21 +92,72 @@ class event(APIView):
|
||||||
|
|
||||||
def post(self, request, id):
|
def post(self, request, id):
|
||||||
|
|
||||||
|
print(request.data)
|
||||||
|
# for key, value in enumerate(request.data):
|
||||||
|
# print(key, value, request.data[value])
|
||||||
|
# print(request.POST)
|
||||||
|
# for key, value in enumerate(request.POST):
|
||||||
|
# print(key, value, request.data[value])
|
||||||
|
|
||||||
|
# import ipdb; ipdb.set_trace()
|
||||||
reservation_validator = ReservationValidator(data=request.data)
|
reservation_validator = ReservationValidator(data=request.data)
|
||||||
|
|
||||||
if reservation_validator.is_valid():
|
if reservation_validator.is_valid():
|
||||||
|
print(reservation_validator.validated_data)
|
||||||
|
configuration = Configuration.get_solo()
|
||||||
|
context = {}
|
||||||
|
|
||||||
data_reservation = reservation_validator.validated_data
|
data_reservation = reservation_validator.validated_data
|
||||||
event = get_object_or_404(Event, pk=id)
|
event = get_object_or_404(Event, pk=id)
|
||||||
configuration = Configuration.get_solo()
|
|
||||||
|
|
||||||
reste_place = configuration.jauge_max - event.reservations
|
# import ipdb; ipdb.set_trace()
|
||||||
if data_reservation.get('qty') > reste_place:
|
# reste_place = configuration.jauge_max - event.reservations
|
||||||
raise serializers.ValidationError(_(f"Il ne reste plus que {reste_place} places"))
|
# if data_reservation.get('qty') > reste_place:
|
||||||
|
# raise serializers.ValidationError(_(f"Il ne reste plus que {reste_place} places"))
|
||||||
|
|
||||||
context = {
|
if request.user.is_anonymous:
|
||||||
'configuration': configuration,
|
User: TibilletUser = get_user_model()
|
||||||
'event': event,
|
email = data_reservation.get('email')
|
||||||
'message': 'Merci de valider votre réservation sur votre boite mail',
|
user, created = User.objects.get_or_create(username=email, email=email)
|
||||||
}
|
|
||||||
|
if created:
|
||||||
|
user.is_active = False
|
||||||
|
user.first_name = data_reservation.get('nom')
|
||||||
|
user.last_name = data_reservation.get('prenom')
|
||||||
|
user.phone = data_reservation.get('phone')
|
||||||
|
user.client_source = connection.tenant
|
||||||
|
user.client_achat.add(connection.tenant)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
request.user = user
|
||||||
|
|
||||||
|
if not request.user.is_active:
|
||||||
|
print(f"{request.user} not active")
|
||||||
|
|
||||||
|
email_activation = ActivationEmail(request)
|
||||||
|
|
||||||
|
# email_activation.send(to=[email,])
|
||||||
|
thread_email = Thread(
|
||||||
|
target=email_activation.send,
|
||||||
|
kwargs={'to': [request.user.email, ],
|
||||||
|
'from_email': configuration.email}
|
||||||
|
)
|
||||||
|
|
||||||
|
thread_email.start()
|
||||||
|
|
||||||
|
context['message'] = "Merci pour votre réservation ! \n" \
|
||||||
|
"Il semble que vous n'avez pas encore de compte TiBillet. \n" \
|
||||||
|
"Merci de vérifier votre boite mail pour valider votre réservation. \n" \
|
||||||
|
"( N'oubliez pas de regarder dans les spams si vous ne voyez rien venir. ) \n" \
|
||||||
|
"Merci !"
|
||||||
|
|
||||||
|
elif request.user.is_active:
|
||||||
|
print("is is_active !")
|
||||||
|
|
||||||
|
reservation = creation_de_la_reservation(request.user, event, data_reservation)
|
||||||
|
|
||||||
|
context['configuration'] = configuration
|
||||||
|
context['event'] = event
|
||||||
|
|
||||||
return render(request, 'html5up-massively/event.html', context=context)
|
return render(request, 'html5up-massively/event.html', context=context)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 2.2 on 2021-06-23 09:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('Customers', '0002_create_tenant_public'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='client',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(db_index=True, max_length=100, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='client',
|
||||||
|
name='on_trial',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='client',
|
||||||
|
name='paid_until',
|
||||||
|
field=models.DateField(default=django.utils.timezone.now),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -29,6 +29,8 @@ urlpatterns = [
|
||||||
re_path(r'^auth/', include('djoser.urls.authtoken')),
|
re_path(r'^auth/', include('djoser.urls.authtoken')),
|
||||||
re_path(r'^auth/', include('djoser.urls.jwt')),
|
re_path(r'^auth/', include('djoser.urls.jwt')),
|
||||||
|
|
||||||
|
path('activate/', include('AuthBillet.urls')),
|
||||||
|
|
||||||
path('', include('BaseBillet.urls')),
|
path('', include('BaseBillet.urls')),
|
||||||
|
|
||||||
# path('admin/', admin.site.urls, name="public_admin_url"),
|
# path('admin/', admin.site.urls, name="public_admin_url"),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue