création d'evenements

This commit is contained in:
Jonas Legion 2021-06-29 16:34:51 +02:00
parent 52b413b3a6
commit 1c08754905
13 changed files with 483 additions and 186 deletions

View File

@ -53,6 +53,7 @@ class UserAdminTibillet(UserAdmin):
'first_name',
'last_name',
'email',
'phone',
'client_source',
'client_admin',
'client_achat',

View File

@ -4,7 +4,7 @@ from django.contrib.auth.models import Group
from solo.admin import SingletonModelAdmin
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 Customers.models import Client
@ -124,4 +124,25 @@ class OptionGeneraleAdmin(admin.ModelAdmin):
'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(Billet, admin.ModelAdmin)
staff_admin_site.register(Article, admin.ModelAdmin)
staff_admin_site.register(LigneArticle, admin.ModelAdmin)

View File

@ -1,11 +1,11 @@
from django.contrib.auth.tokens import default_token_generator
from rest_framework import serializers
from templated_mail.mail import BaseEmailMessage
from django.utils.translation import ugettext_lazy as _
from djoser import utils
from djoser.conf import settings
from BaseBillet.models import Configuration
class ActivationEmail(BaseEmailMessage):
@ -28,9 +28,8 @@ class ActivationEmail(BaseEmailMessage):
return context
def send(self, to, *args, **kwargs):
configuration = Configuration.get_solo()
if not configuration.email :
return serializers.ValidationError(_(f"Manque l'email de la structure. Merci de configurer votre instance."))
from_email = kwargs.get('from_email', 'contact@tibillet.re')
self.render()
@ -39,11 +38,12 @@ class ActivationEmail(BaseEmailMessage):
self.bcc = kwargs.pop('bcc', [])
self.reply_to = kwargs.pop('reply_to', [])
self.from_email = kwargs.pop(
'from_email', configuration.email
'from_email', from_email
)
# import ipdb; ipdb.set_trace()
mail_send = super(BaseEmailMessage, self).send(*args, **kwargs)
print(f'mail_send to {self.to} from {self.from_email} : {mail_send}')

View File

@ -53,6 +53,7 @@ class TibilletUser(AbstractUser):
email = models.EmailField(_('email'), unique=True) # changes email to unique and blank to false
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'
ESPECE_CHOICES = (

View File

@ -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()),
]

View File

@ -1,3 +1,16 @@
from django.shortcuts import render
# 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)

View File

@ -1,3 +1,6 @@
import uuid
from django.contrib.auth import get_user_model
from django.db import models
# Create your models here.
@ -8,6 +11,8 @@ from django.utils.translation import ugettext_lazy as _
from stdimage import StdImageField
from stdimage.validators import MaxSizeValidator
from TiBillet import settings
class OptionGenerale(models.Model):
name = models.CharField(max_length=30)
@ -60,8 +65,6 @@ class Configuration(SingletonModel):
mollie_api_key = models.CharField(max_length=50,
blank=True, null=True)
reservation_par_user_max = models.PositiveSmallIntegerField(default=6)
jauge_max = models.PositiveSmallIntegerField(default=50)
option_generale_radio = models.ManyToManyField(OptionGenerale,
@ -73,11 +76,42 @@ class Configuration(SingletonModel):
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):
name = models.CharField(max_length=200)
short_description = models.CharField(max_length=250)
long_description = models.TextField(blank=True, null=True)
datetime = models.DateTimeField()
billets = models.ManyToManyField(Billet)
articles = models.ManyToManyField(Article)
img = StdImageField(upload_to='images/',
null=True, blank=True,
@ -105,19 +139,74 @@ class Event(models.Model):
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,
on_delete=models.CASCADE,
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 = [
(ANNULEE, _('Annulée')),
(NON_VALIDEE, _('Email non validé')),
(MAIL_NON_VALIDEE, _('Email non validé')),
(NON_PAYEE, _('Non payée')),
(VALIDEE, _('Validé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"))
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}"

View File

@ -1,12 +1,12 @@
{% block header %}
<!DOCTYPE HTML>
<!--
<!DOCTYPE HTML>
<!--
Massively by HTML5 UP
html5up.net | @ajlkn
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html lang="fr">
<head>
-->
<html lang="fr">
<head>
<title>{{ configuration.organisation }}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
@ -21,7 +21,7 @@
{# <noscript>#}
{# <link rel="stylesheet" href="{% static 'html5up-massively/assets/css/noscript.css' %}"/>#}
{# </noscript>#}
</head>
</head>
{% endblock header %}
@ -47,7 +47,6 @@
{# </div>#}
{# </footer>#}
</div>
<!-- Footer -->
<footer id="footer">
@ -83,7 +82,7 @@
</section>
<section>
<h3>Email</h3>
<p>{{ configuration.email }}</p>
<p><a href="mailto:{{ configuration.email }}">{{ configuration.email }}</a></p>
</section>
<section>
<h3>Social</h3>
@ -115,19 +114,17 @@
</ul>
</div>
</div>
<!-- Scripts -->
<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.scrolly.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/util.js' %}"></script>
<script src="{% static 'html5up-massively/assets/js/main.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.scrolly.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/util.js' %}"></script>
<script src="{% static 'html5up-massively/assets/js/main.js' %}"></script>
</body>
</html>
</body>
</html>
{% endblock footer %}

View File

@ -62,38 +62,68 @@
<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"/>
<input id="email" name="email" placeholder="Email" required="True" type="email" value=""/>
</div>
<div class="col-12 col-12-xsmall">
<input type="text" name="phone" id="phone" value="" placeholder="Téléphone" required="True"/>
<input id="phone" name="phone" placeholder="Téléphone" required="True" type="text"
value=""/>
</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 %}#}
{% for billet in event.billets.all %}
<div class="col-4 col-12-medium col-12-small">
<label for="{{ billet.name }}">
BILLET {{ billet.name }} : {{ billet.prix | floatformat:"-2" }}€
</label>
<select name="billets" id="{{ billet.name }}">
{% for nbr in billet.range_max %}
<option value="{{ billet.pk }},{{ nbr }}">{{ nbr }}</option>
{% endfor %}
</select>
</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-4 col-12-small">
<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-6 col-12-small">
<input type="checkbox" id="{{ option_checkbox.name }}" name="{{ option_checkbox.name }}"
<div class="col-12-small">
<input type="checkbox" id="{{ option_checkbox.pk }}"
name="option_checkbox"
value="{{ option_checkbox.pk }}">
<label for="{{ option_checkbox.name }}">{{ option_checkbox.name }}</label>
<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"#}
@ -106,7 +136,7 @@
<li><input type="reset" value="Reset"/></li>
</ul>
</div>
</brdiv>
</div>
</form>
<hr/>
@ -121,4 +151,5 @@
</div>
</div>
{% endblock content %}

View File

@ -1,22 +1,38 @@
from rest_framework import serializers
from django.utils import timezone
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):
nom = serializers.CharField(max_length=100, required=True)
prenom = serializers.CharField(max_length=100, required=True)
email = serializers.EmailField(required=True)
phone = serializers.CharField(max_length=100, required=True)
qty = serializers.IntegerField(required=True)
phone = serializers.CharField(max_length=20, required=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):
configuration = Configuration.get_solo()
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."))
def validate_articles(self, value):
value_dict = {}
art_obj = Article.objects.all()
for couple in value:
pk, qty = art_obj.get(pk=couple.split(',')[0]), int(couple.split(',')[1])
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."))

View File

@ -1,5 +1,6 @@
from datetime import datetime
from django.contrib.auth import get_user_model
from django.shortcuts import render, redirect
# Create your views here.
@ -10,8 +11,13 @@ from rest_framework.views import APIView
from rest_framework.response import Response
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 django.db import connection
from AuthBillet.models import TibilletUser
from threading import Thread
class index(APIView):
@ -21,9 +27,9 @@ class index(APIView):
configuration = Configuration.get_solo()
events = Event.objects.filter(datetime__gt=datetime.now())
if len(events) > 0 :
if len(events) > 0:
first_event = events[0]
else :
else:
first_event = None
context = {
@ -35,7 +41,40 @@ class index(APIView):
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):
permission_classes = [AllowAny]
@ -53,21 +92,72 @@ class event(APIView):
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)
if reservation_validator.is_valid():
print(reservation_validator.validated_data)
configuration = Configuration.get_solo()
context = {}
data_reservation = reservation_validator.validated_data
event = get_object_or_404(Event, pk=id)
configuration = Configuration.get_solo()
reste_place = configuration.jauge_max - event.reservations
if data_reservation.get('qty') > reste_place:
raise serializers.ValidationError(_(f"Il ne reste plus que {reste_place} places"))
# import ipdb; ipdb.set_trace()
# reste_place = configuration.jauge_max - event.reservations
# if data_reservation.get('qty') > reste_place:
# raise serializers.ValidationError(_(f"Il ne reste plus que {reste_place} places"))
context = {
'configuration': configuration,
'event': event,
'message': 'Merci de valider votre réservation sur votre boite mail',
}
if request.user.is_anonymous:
User: TibilletUser = get_user_model()
email = data_reservation.get('email')
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)
else:

View File

@ -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),
),
]

View File

@ -29,6 +29,8 @@ urlpatterns = [
re_path(r'^auth/', include('djoser.urls.authtoken')),
re_path(r'^auth/', include('djoser.urls.jwt')),
path('activate/', include('AuthBillet.urls')),
path('', include('BaseBillet.urls')),
# path('admin/', admin.site.urls, name="public_admin_url"),