signal return after stripe paiement ok

This commit is contained in:
Jonas 12t 2021-11-01 15:18:02 +04:00
parent a6eff19fa2
commit d1a22548cd
14 changed files with 375 additions and 312 deletions

View File

@ -5,11 +5,13 @@ import json
from django.utils.translation import gettext, gettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
import PaiementStripe
from AuthBillet.models import TibilletUser, HumanUser from AuthBillet.models import TibilletUser, HumanUser
from BaseBillet.models import Event, Price, Product, Reservation, Configuration, LigneArticle, Ticket, Paiement_stripe from BaseBillet.models import Event, Price, Product, Reservation, Configuration, LigneArticle, Ticket, Paiement_stripe
from PaiementStripe.views import creation_paiement_stripe from PaiementStripe.views import creation_paiement_stripe
import logging
logger = logging.getLogger(__name__)
class ProductSerializer(serializers.ModelSerializer): class ProductSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -265,9 +267,15 @@ class ReservationValidator(serializers.Serializer):
) )
if new_paiement_stripe.is_valid(): if new_paiement_stripe.is_valid():
reservation.paiement = new_paiement_stripe.paiement_stripe_db paiement_stripe : Paiement_stripe = new_paiement_stripe.paiement_stripe_db
paiement_stripe.lignearticle_set.all().update(status=LigneArticle.UNPAID)
reservation.tickets.all().update(status=Ticket.NOT_ACTIV)
reservation.paiement = paiement_stripe
reservation.status = Reservation.UNPAID reservation.status = Reservation.UNPAID
reservation.save() reservation.save()
print(new_paiement_stripe.checkout_session.stripe_id) print(new_paiement_stripe.checkout_session.stripe_id)
# return new_paiement_stripe.redirect_to_stripe() # return new_paiement_stripe.redirect_to_stripe()
self.checkout_session = new_paiement_stripe.checkout_session self.checkout_session = new_paiement_stripe.checkout_session
@ -278,6 +286,7 @@ class ReservationValidator(serializers.Serializer):
def to_representation(self, instance): def to_representation(self, instance):
representation = super().to_representation(instance) representation = super().to_representation(instance)
logger.info(f"{self.checkout_session.url}")
representation['checkout_url'] = self.checkout_session.url representation['checkout_url'] = self.checkout_session.url
# import ipdb;ipdb.set_trace() # import ipdb;ipdb.set_trace()
return representation return representation

View File

@ -440,41 +440,14 @@
<strong>Grand merci pour votre reservation !</strong> <strong>Grand merci pour votre reservation !</strong>
<br><br> <br><br>
{% if reservation.tickets %} {% if reservation.tickets %}
Vous trouverez vos tickets en pièce jointe. Vous trouverez vos billets en pièce jointe.
{% else %} {% else %}
Aucun ticket pour un concert ? Etrange etrange Aucun billets pour un concert ? Etrange etrange
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
<!-- END BODY COPY --> <!-- END BODY COPY -->
<!-- BUTTON -->
<tr>
<td align="left"
style="padding: 18px 18px 18px 18px; mso-alt-padding: 18px 18px 18px 18px!important;">
<table width="100%" border="0" cellspacing="0"
cellpadding="0">
<tr>
<td>
<table border="0" cellspacing="0"
cellpadding="0">
<tr>
<td align="left"
style="border-radius: 3px;"
bgcolor="#17bef7">
<a class="button raised"
href="{{ config.domaine_cashless }}/rapport/invoice/{{ adhesion.commande }}"
target="_blank"
style="font-size: 14px; line-height: 14px; font-weight: 500; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; border-radius: 3px; padding: 10px 25px; border: 1px solid #17bef7; display: inline-block;">
TELECHARGER REÇU</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- END BUTTON -->
</table> </table>
</td> </td>
</tr> </tr>

View File

@ -12,7 +12,7 @@
<body> <body>
<section id="informations"> <section id="informations">
<h1 id="name">{{ ticket.first_name }} {{ ticket.last_name }}</h1> <h1 id="name">{{ ticket.first_name }} {{ ticket.last_name }}</h1>
<h1 id="destination">CDG ✈ LFLL</h1> <h1 id="destination">{{ ticket.reservation.event.name }}</h1>
<dl> <dl>
<dt>Flight</dt> <dt>Flight</dt>
<dd>DL31</dd> <dd>DL31</dd>
@ -33,7 +33,7 @@
<section id="ticket"> <section id="ticket">
<p>1257797706706</p> <p>1257797706706</p>
<h2>Théodore Marcelin</h2> <h2>{{ ticket.first_name }} {{ ticket.last_name }}</h2>
<dl> <dl>
<dt>Flight</dt> <dt>Flight</dt>
<dd>DL31</dd> <dd>DL31</dd>
@ -45,7 +45,7 @@
<dd>4</dd> <dd>4</dd>
</dl> </dl>
<ul> <ul>
<li>CDG ✈ LFLL</li> <li>{{ ticket.reservation.event.name }}</li>
<li>5:10pm</li> <li>5:10pm</li>
</ul> </ul>
</section> </section>

View File

@ -9,8 +9,8 @@ from weasyprint import HTML
from BaseBillet.models import Configuration, Reservation, Ticket from BaseBillet.models import Configuration, Reservation, Ticket
import logging import logging
logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
''' '''
from ApiBillet.thread_mailer import ThreadMaileur from ApiBillet.thread_mailer import ThreadMaileur
@ -23,34 +23,28 @@ mail.send_with_tread()
class ThreadMaileur(): class ThreadMaileur():
def __init__(self, email, title, text=None, html=None, template=None, context=None): def __init__(self,
email: str,
title: str,
text=None,
html=None,
template=None,
context=None,
urls_for_attached_files=None,
):
self.title = title self.title = title
self.email = email self.email = email
self.text = text self.text = text
self.html = html self.html = html
self.config = Configuration.get_solo() self.config = Configuration.get_solo()
self.context = None self.context = context
if template and context : self.urls_for_attached_files = urls_for_attached_files
if template and context:
self.html = render_to_string(template, context=context) self.html = render_to_string(template, context=context)
self.context = context
self.tickets_uuid = self._tickets_uuid()
self.url = self._url()
def _url(self):
url = f"http://{connection.tenant.domains.all()[0].domain}:8002/api/ticket/"
return url
def _tickets_uuid(self):
tickets_uuid = []
if self.context :
if self.context.get('reservation'):
reservation: Reservation = self.context.get('reservation')
tickets = reservation.tickets.filter(status=Ticket.NOT_SCANNED)
if len(tickets) > 0:
for ticket in tickets :
tickets_uuid.append(f"{ticket.uuid}")
return tickets_uuid
def config_valid(self): def config_valid(self):
EMAIL_HOST = os.environ.get('EMAIL_HOST') EMAIL_HOST = os.environ.get('EMAIL_HOST')
@ -58,58 +52,42 @@ class ThreadMaileur():
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
if EMAIL_HOST and EMAIL_PORT and EMAIL_HOST_USER and EMAIL_HOST_PASSWORD and self.config.email : if EMAIL_HOST and EMAIL_PORT and EMAIL_HOST_USER and EMAIL_HOST_PASSWORD and self.config.email:
return True return True
else: else:
return False return False
def send(self): def send(self):
if self.html and self.config_valid() : if self.html and self.config_valid():
logger.info(f' send_mail') logger.info(f' send_mail')
mail = EmailMultiAlternatives( mail = EmailMultiAlternatives(
self.title, self.title,
self.text, self.text,
self.config.email, self.config.email,
[self.email,], [self.email, ],
) )
mail.attach_alternative(self.html, "text/html") mail.attach_alternative(self.html, "text/html")
attached_file = [] for filename, url in self.urls_for_attached_files.items():
for ticket in self.tickets_uuid : response = requests.get(url)
response = requests.get(f"{self.url}{ticket}")
if response.status_code == 200: if response.status_code == 200:
attached_file.append(response.content) mail.attach(filename, response.content, 'application/pdf')
# attached_file.append(render_to_string('ticket/ticket.html', context={'context': 'context'}))
# msg = EmailMessage(subject, html_content, from_email, [to])
# msg.content_subtype = "html" # Main content is now text/html
# msg.send()
# import ipdb; ipdb.set_trace()
i=1
for file in attached_file:
# html_before_pdf = HTML(string=file)
# mail.attach(f'ticket_{i}.pdf', html_before_pdf.write_pdf(), 'application/pdf')
mail.attach(f'ticket_{i}.pdf', file, 'application/pdf')
i += 1
mail_return = mail.send(fail_silently=False) mail_return = mail.send(fail_silently=False)
if mail_return == 1 :
if mail_return == 1:
logger.info(f' mail envoyé : {mail_return} - {self.email}') logger.info(f' mail envoyé : {mail_return} - {self.email}')
else : else:
logger.error(f' mail non envoyé : {mail_return} - {self.email}') logger.error(f' mail non envoyé : {mail_return} - {self.email}')
return mail return mail
else : else:
logger.error(f'Pas de contenu HTML ou de configuration email valide') logger.error(f'Pas de contenu HTML ou de configuration email valide')
raise ValueError('Pas de contenu HTML ou de configuration email valide') raise ValueError('Pas de contenu HTML ou de configuration email valide')
def send_with_tread(self): def send_with_tread(self):
# self.send() # self.send()
logger.info(f'{timezone.now()} on lance le thread email {self.email}') logger.info(f'{timezone.now()} on lance le thread email {self.email}')
thread_email = threading.Thread(target=self.send) thread_email = threading.Thread(target=self.send)
thread_email.start() thread_email.start()
logger.info(f'{timezone.now()} Thread email lancé') logger.info(f'{timezone.now()} Thread email lancé')

View File

@ -15,6 +15,8 @@ router.register(r'reservations', api_view.ReservationViewset, basename='reservat
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
path('ticket/<uuid:pk_uuid>', TicketPdf.as_view(), name='ticket_uuid'),
#download ticket :
path('ticket/pdf/<uuid:pk_uuid>', TicketPdf.as_view(), name='ticket_uuid_to_pdf'),
] ]

View File

@ -150,11 +150,10 @@ class TicketPdf(WeasyTemplateView):
kwargs['ticket'] = ticket kwargs['ticket'] = ticket
kwargs['config'] = self.config kwargs['config'] = self.config
self.nom_prenom = f"{ticket.first_name.upper()}_{ticket.last_name.capitalize()}" self.pdf_filename = ticket.pdf_filename()
return kwargs return kwargs
def get_pdf_filename(self, **kwargs): def get_pdf_filename(self, **kwargs):
nom_prenom = self.nom_prenom return self.pdf_filename
return f"Ticket_{nom_prenom}.pdf"
# #

View File

@ -9,6 +9,7 @@ from django.db.models.aggregates import Sum
from django.db.models import Q from django.db.models import Q
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from solo.models import SingletonModel from solo.models import SingletonModel
@ -325,7 +326,7 @@ class Reservation(models.Model):
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name="reservation") related_name="reservation")
CANCELED, CREATED, UNPAID, PAID, VALID, = 'C', 'R', 'N', 'P', 'V' CANCELED, CREATED, UNPAID, PAID, VALID, = 'C', 'R', 'U', 'P', 'V'
TYPE_CHOICES = [ TYPE_CHOICES = [
(CANCELED, _('Annulée')), (CANCELED, _('Annulée')),
(CREATED, _('Crée')), (CREATED, _('Crée')),
@ -356,7 +357,7 @@ class Reservation(models.Model):
def articles_paid(self): def articles_paid(self):
articles_paid = [] articles_paid = []
for paiement in self.paiements_paid(): for paiement in self.paiements.all():
for ligne in paiement.lignearticle_set.filter( for ligne in paiement.lignearticle_set.filter(
Q(status=LigneArticle.PAID) | Q(status=LigneArticle.VALID) Q(status=LigneArticle.PAID) | Q(status=LigneArticle.VALID)
): ):
@ -401,16 +402,39 @@ class Ticket(models.Model):
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, related_name="tickets") reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, related_name="tickets")
NOT_ACTIV, NOT_SCANNED, SCANNED = 'N', 'K', 'S' CREATED, NOT_ACTIV, NOT_SCANNED, SCANNED = 'C', 'N', 'K', 'S'
SCAN_CHOICES = [ SCAN_CHOICES = [
(CREATED, _('Crée')),
(NOT_ACTIV, _('Non actif')), (NOT_ACTIV, _('Non actif')),
(NOT_SCANNED, _('Non scanné')), (NOT_SCANNED, _('Non scanné')),
(SCANNED, _('scanné')), (SCANNED, _('scanné')),
] ]
status = models.CharField(max_length=1, choices=SCAN_CHOICES, default=NOT_ACTIV, status = models.CharField(max_length=1, choices=SCAN_CHOICES, default=CREATED,
verbose_name=_("Status du scan")) verbose_name=_("Status du scan"))
seat = models.CharField(max_length=20, default=_('Placement libre'))
def pdf_filename(self):
config = Configuration.get_solo()
return f"{config.organisation.upper()} " \
f"{self.reservation.event.datetime.astimezone().strftime('%d/%m/%Y')} " \
f"{self.first_name.upper()} " \
f"{self.last_name.capitalize()}" \
f".pdf"
def pdf_url(self):
domain = connection.tenant.domains.all().first().domain
api_pdf = reverse("ticket_uuid_to_pdf", args=[f"{self.uuid}"])
protocol = "https://"
port = ""
if settings.DEBUG:
protocol = "http://"
port = ":8002"
return f"{protocol}{domain}{port}{api_pdf}"
def event(self): def event(self):
return self.reservation.event return self.reservation.event
@ -454,11 +478,11 @@ class Paiement_stripe(models.Model):
(VALID, 'Payée et validée'), # envoyé sur serveur cashless (VALID, 'Payée et validée'), # envoyé sur serveur cashless
(CANCELED, 'Annulée'), (CANCELED, 'Annulée'),
) )
status = models.CharField(max_length=1, choices=STATUT_CHOICES, default=NON, verbose_name="Statut de la commande")
reservation = models.ForeignKey(Reservation, on_delete=models.PROTECT, blank=True, null=True, reservation = models.ForeignKey(Reservation, on_delete=models.PROTECT, blank=True, null=True,
related_name="paiements") related_name="paiements")
status = models.CharField(max_length=1, choices=STATUT_CHOICES, default=NON, verbose_name="Statut de la commande")
QRCODE, API_BILLETTERIE = 'Q', 'B' QRCODE, API_BILLETTERIE = 'Q', 'B'
SOURCE_CHOICES = ( SOURCE_CHOICES = (
@ -493,15 +517,16 @@ class LigneArticle(models.Model):
paiement_stripe = models.ForeignKey(Paiement_stripe, on_delete=models.PROTECT, blank=True, null=True) paiement_stripe = models.ForeignKey(Paiement_stripe, on_delete=models.PROTECT, blank=True, null=True)
CANCELED, UNPAID, PAID, VALID, = 'C', 'N', 'P', 'V' CANCELED, CREATED, UNPAID, PAID, VALID, = 'C', 'O', 'U', 'P', 'V'
TYPE_CHOICES = [ TYPE_CHOICES = [
(CANCELED, _('Annulée')), (CANCELED, _('Annulée')),
(CREATED, _('Non envoyé en paiement')),
(UNPAID, _('Non payée')), (UNPAID, _('Non payée')),
(PAID, _('Payée')), (PAID, _('Payée')),
(VALID, _('Validée par serveur cashless')), (VALID, _('Validée par serveur cashless')),
] ]
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=UNPAID, status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=CREATED,
verbose_name=_("Status de ligne article")) verbose_name=_("Status de ligne article"))
class Meta: class Meta:

View File

@ -9,8 +9,9 @@ from ApiBillet.thread_mailer import ThreadMaileur
from BaseBillet.models import Reservation, LigneArticle, Ticket, Product, Configuration, Paiement_stripe from BaseBillet.models import Reservation, LigneArticle, Ticket, Product, Configuration, Paiement_stripe
import logging import logging
logger = logging.getLogger(__name__) from TiBillet import settings
logger = logging.getLogger(__name__)
logger.info(f'import basebillet.signals') logger.info(f'import basebillet.signals')
@ -26,31 +27,58 @@ logger.info(f'import basebillet.signals')
######################################################################## ########################################################################
######################## TRIGGER PAIEMENT STRIPE ########################
def set_ligne_article_paid(old_instance, new_instance):
# Type :
old_instance: Paiement_stripe
new_instance: Paiement_stripe
logger.info(f" TRIGGER PAIEMENT STRIPE set_ligne_article_paid {old_instance}.")
logger.info(f" On passe toutes les lignes d'article non validées en payées !")
lignes_article = new_instance.lignearticle_set.exclude(status=LigneArticle.VALID)
for ligne_article in lignes_article:
logger.info(f" {ligne_article.price} {ligne_article.status} to P")
ligne_article.status = LigneArticle.PAID
ligne_article.save()
# si ya une reservation, on la met aussi en payée :
# try :
if new_instance.reservation:
new_instance.reservation.status = Reservation.PAID
new_instance.reservation.save()
# except new_instance.reservation.RelatedObjectDoesNotExist:
def expire_paiement_stripe(old_instance, new_instance):
logger.info(f" TRIGGER PAIEMENT STRIPE expire_paiement_stripe {old_instance.status} to {new_instance.status}")
pass
def valide_stripe_paiement(old_instance, new_instance):
logger.info(f" TRIGGER PAIEMENT STRIPE valide_stripe_paiement {old_instance.status} to {new_instance.status}")
pass
######################## TRIGGER LIGNE ARTICLE ######################## ######################## TRIGGER LIGNE ARTICLE ########################
# post_save ici nécéssaire pour mettre a jour le status du paiement stripe en validé # post_save ici nécéssaire pour mettre a jour le status du paiement stripe en validé
# si toutes les lignes articles sont VALID. # si toutes les lignes articles sont save en VALID.
@receiver(post_save, sender=LigneArticle) # @receiver(post_save, sender=LigneArticle)
def set_paiement_and_reservation_valid(sender, instance: LigneArticle, **kwargs):
if instance.status == LigneArticle.VALID: def set_paiement_and_reservation_valid(old_instance, new_instance):
lignes_dans_paiement_stripe = instance.paiement_stripe.lignearticle_set.all() lignes_dans_paiement_stripe = new_instance.paiement_stripe.lignearticle_set.all()
if len(lignes_dans_paiement_stripe) == len(lignes_dans_paiement_stripe.filter(status=LigneArticle.VALID)): # TODO: calculer -1 ??
# on passe le status du paiement stripe en VALID if len(lignes_dans_paiement_stripe) == len(lignes_dans_paiement_stripe.filter(status=LigneArticle.VALID)) :
logger.info(f" TRIGGER LIGNE ARTICLE set_paiement_and_reservation_valid {instance.price} " # on passe le status du paiement stripe en VALID
f"paiement stripe {instance.paiement_stripe} {instance.paiement_stripe.status} à VALID") logger.info(f" TRIGGER LIGNE ARTICLE set_paiement_and_reservation_valid {new_instance.price} "
instance.paiement_stripe.status = Paiement_stripe.VALID f"paiement stripe {new_instance.paiement_stripe} {new_instance.paiement_stripe.status} à VALID")
instance.paiement_stripe.save() new_instance.paiement_stripe.status = Paiement_stripe.VALID
new_instance.paiement_stripe.save()
def check_paid(old_instance, new_instance):
# Type :
old_instance: LigneArticle
new_instance: LigneArticle
logger.info(f" TRIGGER LIGNE ARTICLE check_paid {old_instance.price}")
if new_instance.price.product.categorie_article in \
[Product.RECHARGE_CASHLESS, Product.ADHESION]:
send_to_cashless(new_instance)
def send_to_cashless(instance): def send_to_cashless(instance):
@ -89,56 +117,40 @@ def send_to_cashless(instance):
f"erreur réponse serveur cashless {r.status_code} {r.text} pour paiement stripe {instance.price} uuid {instance.uuid}") f"erreur réponse serveur cashless {r.status_code} {r.text} pour paiement stripe {instance.price} uuid {instance.uuid}")
######################## TRIGGER PAIEMENT STRIPE ######################## def check_paid(old_instance, new_instance):
def set_ligne_article_paid(old_instance, new_instance):
# Type : # Type :
old_instance: Paiement_stripe old_instance: LigneArticle
new_instance: Paiement_stripe new_instance: LigneArticle
logger.info(f" TRIGGER LIGNE ARTICLE check_paid {old_instance.price}")
logger.info(f" TRIGGER PAIEMENT STRIPE set_ligne_article_paid {old_instance}.") if new_instance.price.product.categorie_article in \
logger.info(f" On passe toutes les lignes d'article non validées en payées !") [Product.RECHARGE_CASHLESS, Product.ADHESION]:
send_to_cashless(new_instance)
lignes_article = new_instance.lignearticle_set.exclude(status=LigneArticle.VALID)
for ligne_article in lignes_article:
logger.info(f" {ligne_article.price} {ligne_article.status} to P]")
ligne_article.status = LigneArticle.PAID
ligne_article.save()
# si ya une reservation, on la met aussi en payée :
# try :
if new_instance.reservation:
new_instance.reservation.status = Reservation.PAID
new_instance.reservation.save()
# except new_instance.reservation.RelatedObjectDoesNotExist:
def expire_paiement_stripe(old_instance, new_instance):
logger.info(f" TRIGGER PAIEMENT STRIPE expire_paiement_stripe {old_instance.status} to {new_instance.status}")
pass
def valide_stripe_paiement(old_instance, new_instance):
logger.info(f" TRIGGER PAIEMENT STRIPE valide_stripe_paiement {old_instance.status} to {new_instance.status}")
pass
######################## TRIGGER RESERVATION ######################## ######################## TRIGGER RESERVATION ########################
# @receiver(post_save, sender=Reservation)
# def send_billet_to_mail(sender, instance: Reservation, **kwargs):
def send_billet_to_mail(old_instance, new_instance): def send_billet_to_mail(old_instance, new_instance):
# On active les tickets # On active les tickets
urls_for_attached_files = {}
if new_instance.tickets: if new_instance.tickets:
for ticket in new_instance.tickets.filter(status=Ticket.NOT_ACTIV): # On prend aussi ceux qui sont déja activé ( avec les Q() )
# pour pouvoir les envoyer par mail en cas de nouvelle demande
for ticket in new_instance.tickets.filter(Q(status=Ticket.NOT_ACTIV) | Q(status=Ticket.NOT_SCANNED)):
logger.info(f'trigger_reservation, activation des tickets {ticket} NOT_SCANNED') logger.info(f'trigger_reservation, activation des tickets {ticket} NOT_SCANNED')
ticket.status = Ticket.NOT_SCANNED ticket.status = Ticket.NOT_SCANNED
ticket.save() ticket.save()
# On vérifier qu'on a pas déja envoyé le mail # on rajoute les urls du pdf pour le thread async
urls_for_attached_files[ticket.pdf_filename()] = ticket.pdf_url()
# import ipdb; ipdb.set_trace()
# On vérifie qu'on a pas déja envoyé le mail
if not new_instance.mail_send : if not new_instance.mail_send :
logger.info(f" TRIGGER RESERVATION send_billet_to_mail {old_instance.status} to {new_instance.status}") logger.info(f" TRIGGER RESERVATION send_billet_to_mail {new_instance.status}")
new_instance : Reservation new_instance : Reservation
config = Configuration.get_solo() config = Configuration.get_solo()
@ -152,13 +164,15 @@ def send_billet_to_mail(old_instance, new_instance):
'config': config, 'config': config,
'reservation': new_instance, 'reservation': new_instance,
}, },
urls_for_attached_files = urls_for_attached_files,
) )
# import ipdb; ipdb.set_trace() # import ipdb; ipdb.set_trace()
mail.send_with_tread() mail.send_with_tread()
except Exception as e : except Exception as e :
logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {new_instance} : {e}") logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {new_instance} : {e}")
else : else :
logger.info(f" TRIGGER RESERVATION mail déja envoyé {new_instance} : {new_instance.mail_send} - status : {old_instance.status} to {new_instance.status}") logger.info(f" TRIGGER RESERVATION mail déja envoyé {new_instance} : {new_instance.mail_send} - status : {new_instance.status}")
######################## MOTEUR TRIGGER ######################## ######################## MOTEUR TRIGGER ########################
@ -174,19 +188,6 @@ def error_regression(old_instance, new_instance):
# Exemple première ligne : Si status passe de PENDING vers PAID, alors on lance set_ligne_article_paid # Exemple première ligne : Si status passe de PENDING vers PAID, alors on lance set_ligne_article_paid
TRANSITIONS = { TRANSITIONS = {
'RESERVATION': {
Reservation.UNPAID: {
Reservation.PAID: send_billet_to_mail,
},
Reservation.PAID: {
Reservation.VALID: send_billet_to_mail,
Reservation.PAID: send_billet_to_mail,
'_else_': error_regression,
},
Reservation.VALID: {
'_all_': error_regression,
}
},
'PAIEMENT_STRIPE': { 'PAIEMENT_STRIPE': {
Paiement_stripe.PENDING: { Paiement_stripe.PENDING: {
Paiement_stripe.PAID: set_ligne_article_paid, Paiement_stripe.PAID: set_ligne_article_paid,
@ -202,19 +203,34 @@ TRANSITIONS = {
'_all_': error_regression, '_all_': error_regression,
} }
}, },
'LIGNEARTICLE': { 'LIGNEARTICLE': {
LigneArticle.UNPAID: { LigneArticle.UNPAID: {
LigneArticle.PAID: check_paid, LigneArticle.PAID: check_paid,
}, },
LigneArticle.PAID: { LigneArticle.PAID: {
LigneArticle.PAID: check_paid, LigneArticle.PAID: check_paid,
# LigneArticle.VALID: valide_stripe_paiement, LigneArticle.VALID: set_paiement_and_reservation_valid,
'_else_': error_regression, '_else_': error_regression,
}, },
LigneArticle.VALID: { LigneArticle.VALID: {
'_all_': error_regression, '_all_': error_regression,
} }
}, },
'RESERVATION': {
Reservation.UNPAID: {
Reservation.PAID: send_billet_to_mail,
},
Reservation.PAID: {
Reservation.VALID: send_billet_to_mail,
Reservation.PAID: send_billet_to_mail,
'_else_': error_regression,
},
Reservation.VALID: {
'_all_': error_regression,
}
},
} }
@receiver(pre_save) @receiver(pre_save)

View File

@ -3,6 +3,6 @@ from django.urls import include, path, re_path
from BaseBillet import views as base_view from BaseBillet import views as base_view
urlpatterns = [ urlpatterns = [
path('event/<str:id>', base_view.event.as_view()), path('ticket/<uuid:pk_uuid>', base_view.Ticket_html_view.as_view()),
path('', base_view.index.as_view(), name="index"), path('', base_view.index.as_view(), name="index"),
] ]

View File

@ -1,24 +1,12 @@
from datetime import datetime from datetime import datetime
from django.contrib.auth import get_user_model
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect from django.shortcuts import render
# Create your views here.
from rest_framework import serializers, status
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from BaseBillet.models import Configuration, Event, Ticket
from django.utils.translation import ugettext_lazy as _
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): class index(APIView):
@ -44,128 +32,14 @@ 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): class Ticket_html_view(APIView):
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,
)
for article in data.get('products'):
qty = data.get('products')[article]
LigneArticle.objects.create(
reservation = reservation,
article = article,
qty = qty,
)
return reservation
#Modèle MVC
class event(APIView):
permission_classes = [AllowAny] permission_classes = [AllowAny]
def get(self, request, pk_uuid):
ticket = get_object_or_404(Ticket, uuid=pk_uuid)
def get(self, request, id):
event = get_object_or_404(Event, pk=id)
configuration = Configuration.get_solo()
context = { context = {
'configuration': configuration, 'ticket': ticket,
'event': event, 'config': Configuration.get_solo(),
} }
return render(request, 'html5up-massively/event.html', context=context) return render(request, 'ticket/ticket.html', context=context)
def post(self, request, id):
print(request.data)
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)
# 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"))
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 or not request.user.password :
print(f"{request.user} not active or no password")
# on retire les commande non validé pour éviter les doublons
# et on le remet non actif si pas de mot de passe :
asupr = Reservation.objects.filter(user_commande=request.user, status=Reservation.MAIL_NON_VALIDEE, event=event)
asupr.delete()
request.user.is_active = False
request.user.save()
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:
print(f"validator.errors : {reservation_validator.errors}")
return Response(reservation_validator.errors, status=status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,171 @@
from datetime import datetime
from django.contrib.auth import get_user_model
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect
# Create your views here.
from rest_framework import serializers, status
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework.response import Response
from django.utils.translation import ugettext_lazy as _
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):
permission_classes = [AllowAny]
def get(self, request):
configuration = Configuration.get_solo()
if not configuration.activer_billetterie :
return HttpResponseRedirect('https://www.tibillet.re')
events = Event.objects.filter(datetime__gt=datetime.now())
if len(events) > 0:
first_event = events[0]
else:
first_event = None
context = {
'configuration': configuration,
'events': events[1:],
'first_event': first_event,
}
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,
)
for article in data.get('products'):
qty = data.get('products')[article]
LigneArticle.objects.create(
reservation = reservation,
article = article,
qty = qty,
)
return reservation
#Modèle MVC
class event(APIView):
permission_classes = [AllowAny]
def get(self, request, id):
event = get_object_or_404(Event, pk=id)
configuration = Configuration.get_solo()
context = {
'configuration': configuration,
'event': event,
}
return render(request, 'html5up-massively/event.html', context=context)
def post(self, request, id):
print(request.data)
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)
# 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"))
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 or not request.user.password :
print(f"{request.user} not active or no password")
# on retire les commande non validé pour éviter les doublons
# et on le remet non actif si pas de mot de passe :
asupr = Reservation.objects.filter(user_commande=request.user, status=Reservation.MAIL_NON_VALIDEE, event=event)
asupr.delete()
request.user.is_active = False
request.user.save()
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:
print(f"validator.errors : {reservation_validator.errors}")
return Response(reservation_validator.errors, status=status.HTTP_400_BAD_REQUEST)

View File

@ -229,9 +229,13 @@ class retour_stripe(View):
return HttpResponseRedirect(f"/qr/{ligne_article.carte.uuid}#erreurpaiement") return HttpResponseRedirect(f"/qr/{ligne_article.carte.uuid}#erreurpaiement")
elif paiement_stripe.source == Paiement_stripe.API_BILLETTERIE : elif paiement_stripe.source == Paiement_stripe.API_BILLETTERIE :
if paiement_stripe.status == Paiement_stripe.PAID :
return HttpResponse(
'Paiement okay, on lance les process de validation.')
if paiement_stripe.status == Paiement_stripe.VALID : if paiement_stripe.status == Paiement_stripe.VALID :
return HttpResponse( return HttpResponse(
'Coucou') 'Paiement validé !')
raise Http404(f'{paiement_stripe.status}') raise Http404(f'{paiement_stripe.status}')

View File

@ -224,11 +224,11 @@ LOGGING = {
'disable_existing_loggers': False, 'disable_existing_loggers': False,
'handlers': { 'handlers': {
'console': { 'console': {
'level': 'DEBUG', 'level': 'INFO',
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
}, },
'logfile': { 'logfile': {
'level': 'DEBUG', 'level': 'INFO',
'class': 'logging.FileHandler', 'class': 'logging.FileHandler',
'filename': f"{BASE_DIR}/www/Djangologfile", 'filename': f"{BASE_DIR}/www/Djangologfile",
}, },

View File

@ -1,8 +1,10 @@
from python:3.8-slim from python:3.8-bullseye
## UPDATE ## UPDATE
RUN apt-get update RUN apt-get update
RUN apt-get upgrade -y RUN apt-get upgrade -y
RUN apt install -y python3-pip
RUN pip install pip --upgrade RUN pip install pip --upgrade
## PYTHON ## PYTHON
@ -61,17 +63,27 @@ RUN pip install django-stdimage
RUN pip install stripe RUN pip install stripe
RUN apt-get install -y fonts-font-awesome # RUN apt-get install -y fonts-font-awesome
RUN apt-get install -y libffi-dev # RUN apt-get install -y libffi-dev
RUN apt-get install -y libgdk-pixbuf2.0-0 # RUN apt-get install -y libgdk-pixbuf2.0-0
RUN apt-get install -y libpango1.0-0 # RUN apt-get install -y libpango1.0-0
RUN apt-get install -y python-dev # RUN apt-get install -y python-dev
# RUN apt-get install -y python-lxml # RUN apt-get install -y python-lxml
RUN apt-get install -y shared-mime-info # RUN apt-get install -y shared-mime-info
RUN apt-get install -y libcairo2 # RUN apt-get install -y libcairo2
RUN apt install -y python3-cffi
RUN apt install -y python3-brotli
RUN apt install -y libpango-1.0-0
RUN apt install -y libpangoft2-1.0-0
RUN pip install django-weasyprint RUN pip install django-weasyprint
RUN apt-get -y clean RUN apt-get -y clean
RUN python --version RUN python --version
RUN django-admin --version RUN django-admin --version
RUN pip freeze
RUN weasyprint --version
RUN pip freeze | grep weasyprint