Envoie vers Stripe et création du ticket

This commit is contained in:
Jonas 12t 2021-10-25 15:54:55 +04:00
parent aaa81f6c7f
commit cfc22159b4
7 changed files with 279 additions and 71 deletions

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, Product, Price, Reservation, LigneArticle
from BaseBillet.models import Configuration, Event, OptionGenerale, Product, Price, Reservation, LigneArticle, Ticket
from django.contrib.auth.admin import UserAdmin
from Customers.models import Client
@ -133,6 +133,7 @@ class ConfigurationAdmin(SingletonModelAdmin):
('Billetterie', {
'fields': (
'activer_billetterie',
'name_required_for_ticket',
'jauge_max',
'option_generale_radio',
'option_generale_checkbox',
@ -175,16 +176,28 @@ class OptionGeneraleAdmin(admin.ModelAdmin):
class ReservationAdmin(admin.ModelAdmin):
list_display = (
'user_mail',
'total_billet',
'_options_',
'total_prix',
'status'
'datetime',
'user_commande',
'event',
'status',
)
readonly_fields = list_display
staff_admin_site.register(Reservation, ReservationAdmin)
class TicketAdmin(admin.ModelAdmin):
list_display = [
'first_name',
'last_name',
'reservation',
'scan_status',
]
readonly_fields = list_display
staff_admin_site.register(Ticket, TicketAdmin)
class ProductAdmin(admin.ModelAdmin):
list_display = (
'name',

View File

@ -1,11 +1,14 @@
from django.contrib.auth import get_user_model
from django.db import connection
from rest_framework import serializers
import json
from django.utils.translation import gettext, gettext_lazy as _
from rest_framework.generics import get_object_or_404
from BaseBillet.models import Event, Price, Product
import PaiementStripe
from AuthBillet.models import TibilletUser, HumanUser
from BaseBillet.models import Event, Price, Product, Reservation, Configuration, LigneArticle, Ticket
from PaiementStripe.views import creation_paiement_stripe
class ProductSerializer(serializers.ModelSerializer):
@ -27,6 +30,7 @@ class ProductSerializer(serializers.ModelSerializer):
'prices',
]
class PriceSerializer(serializers.ModelSerializer):
product = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all())
@ -72,7 +76,6 @@ class EventSerializer(serializers.ModelSerializer):
read_only_fields = ['uuid', 'reservations']
depth = 1
def validate(self, attrs):
products = self.initial_data.get('products')
@ -96,12 +99,183 @@ class EventSerializer(serializers.ModelSerializer):
instance.products.add(product)
return instance
'''
products = [
{"uuid":"9340a9a1-1b90-488e-ab68-7b358b213dd7"},
{"uuid":"60db1531-fd0a-4d92-a785-f384e77cd213"}
]
'''
products = [
{"uuid":"9340a9a1-1b90-488e-ab68-7b358b213dd7"},
{"uuid":"60db1531-fd0a-4d92-a785-f384e77cd213"}
]
'''
class ReservationSerializer(serializers.ModelSerializer):
class Meta:
model = Reservation
fields = [
'uuid',
'datetime',
'user_commande',
'event',
'status',
'options',
'tickets',
'paiements',
]
read_only_fields = [
'uuid',
'datetime',
'status',
]
depth = 1
class ReservationValidator(serializers.Serializer):
email = serializers.EmailField()
event = serializers.PrimaryKeyRelatedField(queryset=Event.objects.all())
prices = serializers.JSONField(required=True)
def validate_email(self, value):
User: TibilletUser = get_user_model()
user_paiement, created = User.objects.get_or_create(
email=value)
if created:
user_paiement: HumanUser
user_paiement.client_source = connection.tenant
user_paiement.client_achat.add(connection.tenant)
user_paiement.is_active = False
else:
user_paiement.client_achat.add(connection.tenant)
user_paiement.save()
self.user_commande = user_paiement
return user_paiement.email
def validate_prices(self, value):
print(value)
# on vérifie que chaque article existe et a sa quantité.
# et qu'il y ai au moins un billet pour la reservation.
config = Configuration.get_solo()
self.nbr_ticket = 0
self.prices_list = []
for entry in value:
try:
price = Price.objects.get(pk=entry['uuid'])
price_object = {
'price': price,
'qty': float(entry['qty']),
}
if price.product.categorie_article == Product.BILLET:
self.nbr_ticket += entry['qty']
# Si les noms sont requis pour la billetterie
if config.name_required_for_ticket and entry['qty'] > 0:
if not entry.get('customers'):
raise serializers.ValidationError(_(f'customers non trouvés'))
if len(entry.get('customers')) != entry['qty']:
raise serializers.ValidationError(_(f'nombre customers non conforme'))
price_object['customers'] = entry.get('customers')
self.prices_list.append(price_object)
except Price.DoesNotExist as e:
raise serializers.ValidationError(_(f'price non trouvé : {e}'))
except ValueError as e:
raise serializers.ValidationError(_(f'qty doit être un entier ou un flottant : {e}'))
'''
products = [
{
"uuid": "8c419d35-11a1-43b6-b500-b79db665d560",
"qty": 2,
"customers": [
{
"first_name": "Jean-Michel",
"last_name": "Amoitié"
},
{
"first_name": "Ellen",
"last_name": "Ripley"
}
]
},
{
"uuid": "c6e847d4-baaa-4d21-a4f0-a572b8319615",
"qty": 1,
"customers": [
{
"first_name": "Douglas",
"last_name": "Adams"
}
]
}
]
products = [
{
"uuid": "8c419d35-11a1-43b6-b500-b79db665d560",
"qty": 2
},
{
"uuid": "c6e847d4-baaa-4d21-a4f0-a572b8319615",
"qty": 1
}
]
'''
return value
def validate(self, attrs):
if self.nbr_ticket == 0:
raise serializers.ValidationError(_(f'pas de billet dans la reservation'))
config = Configuration.get_solo()
reservation = Reservation.objects.create(
user_commande=self.user_commande,
event=attrs.get('event'),
)
lignes_article = []
for price in self.prices_list:
ligne_article = LigneArticle.objects.create(
reservation=reservation,
price=price.get('price'),
qty=price.get('qty'),
)
lignes_article.append(ligne_article)
if config.name_required_for_ticket and price.get('customers'):
for customer in price.get('customers'):
ticket = Ticket.objects.create(
reservation=reservation,
first_name=customer.get('first_name'),
last_name=customer.get('last_name'),
)
metadata = {'reservation':f'{reservation.uuid}'}
new_paiement_stripe = creation_paiement_stripe(
email_paiement=self.user_commande.email,
liste_ligne_article=lignes_article,
metadata=metadata,
absolute_domain=self.context.get('request').build_absolute_uri().partition('/api')[0],
)
if new_paiement_stripe.is_valid():
print(new_paiement_stripe.checkout_session.stripe_id)
# return new_paiement_stripe.redirect_to_stripe()
self.checkout_session = new_paiement_stripe.checkout_session
return super().validate(attrs)
else:
raise serializers.ValidationError(_(f'checkout strip not valid'))
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['checkout_url'] = self.checkout_session.url
# import ipdb;ipdb.set_trace()
return representation
'''

View File

@ -8,6 +8,7 @@ router = routers.DefaultRouter()
router.register(r'events', api_view.EventsViewSet, basename='event')
router.register(r'products', api_view.ProductViewSet, basename='product')
router.register(r'prices', api_view.TarifBilletViewSet, basename='price')
router.register(r'reservations', api_view.ReservationViewset, basename='reservation')
urlpatterns = [

View File

@ -4,10 +4,11 @@ from django.shortcuts import render
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from ApiBillet.serializers import EventSerializer, PriceSerializer, ProductSerializer
from ApiBillet.serializers import EventSerializer, PriceSerializer, ProductSerializer, ReservationSerializer, \
ReservationValidator
from AuthBillet.models import TenantAdminPermission
from Customers.models import Client, Domain
from BaseBillet.models import Event, Price, Product
from BaseBillet.models import Event, Price, Product, Reservation
from rest_framework import viewsets, permissions, status
import os
@ -113,3 +114,23 @@ class EventsViewSet(viewsets.ViewSet):
else:
permission_classes = [TenantAdminPermission]
return [permission() for permission in permission_classes]
class ReservationViewset(viewsets.ViewSet):
def list(self, request):
queryset = Reservation.objects.all().order_by('-datetime')
serializer = ReservationSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)
def create(self, request):
print(request.data)
validator = ReservationValidator(data=request.data, context={'request': request})
if validator.is_valid():
# serializer.save()
return Response(validator.data, status=status.HTTP_201_CREATED)
return Response(validator.errors, status=status.HTTP_400_BAD_REQUEST)
def get_permissions(self):
permission_classes = [TenantAdminPermission]
return [permission() for permission in permission_classes]

View File

@ -58,6 +58,8 @@ class Configuration(SingletonModel):
adhesion_obligatoire = models.BooleanField(default=False)
name_required_for_ticket = models.BooleanField(default=False,verbose_name=_("Billet nominatifs"))
carte_restaurant = StdImageField(upload_to='images/',
null=True, blank=True,
validators=[MaxSizeValidator(1920, 1920)],
@ -118,7 +120,6 @@ class Configuration(SingletonModel):
)
class Product(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True, db_index=True)
@ -191,6 +192,7 @@ class Product(models.Model):
self.id_product_stripe = None
self.save()
class Price(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True, db_index=True)
@ -223,7 +225,6 @@ class Price(models.Model):
def __str__(self):
return f"{self.product.name} {self.name}"
def get_id_price_stripe(self):
configuration = Configuration.get_solo()
if configuration.stripe_api_key and not self.id_price_stripe:
@ -236,6 +237,7 @@ class Price(models.Model):
unit_amount=int("{0:.2f}".format(self.prix).replace('.', '')),
currency="eur",
product=self.product.get_id_product_stripe(),
nickname= self.name,
)
self.id_price_stripe = price.id
@ -306,7 +308,8 @@ class Event(models.Model):
class Reservation(models.Model):
uuid = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4)
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True, db_index=True)
datetime = models.DateTimeField(auto_now=True)
user_commande = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
@ -314,46 +317,40 @@ class Reservation(models.Model):
on_delete=models.PROTECT,
related_name="reservation")
ANNULEE, MAIL_NON_VALIDEE, NON_PAYEE, VALIDEE, PAYEE = 'NAN', 'MNV', 'NPA', 'VAL', 'PAY'
CANCELED, UNPAID, PAID, VALID, = 'C', 'N', 'P', 'V'
TYPE_CHOICES = [
(ANNULEE, _('Annulée')),
(MAIL_NON_VALIDEE, _('Email non validé')),
(NON_PAYEE, _('Non payée')),
(VALIDEE, _('Validée')),
(PAYEE, _('Payée')),
(CANCELED, _('Annulée')),
(UNPAID, _('Non payée')),
(PAID, _('Payée')),
(VALID, _('Validée')),
]
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=NON_PAYEE,
status = models.CharField(max_length=3, choices=TYPE_CHOICES, default=UNPAID,
verbose_name=_("Status de la réservation"))
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.product:
total += ligne.qty * ligne.product.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()])
#
# def total_billet(self):
# total = 0
# for ligne in self.paiements.all():
# if ligne.billet:
# total += ligne.qty
# return total
#
# def total_prix(self):
# total = 0
# for ligne in self.paiements.all():
# if ligne.product:
# total += ligne.qty * ligne.product.prix
#
# return total
#
# def _options_(self):
# return " - ".join([f"{option.name}" for option in self.options.all()])
#
@receiver(post_save, sender=Reservation)
def verif_mail_valide(sender, instance: Reservation, created, **kwargs):
@ -364,6 +361,25 @@ def verif_mail_valide(sender, instance: Reservation, created, **kwargs):
instance.save()
class Ticket(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True, db_index=True)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, related_name="tickets")
NOT_ACTIV, NOT_SCANNED, SCANNED = 'N', 'K', 'S'
SCAN_CHOICES = [
(NOT_ACTIV, _('Non actif')),
(NOT_SCANNED, _('Non scanné')),
(SCANNED, _('scanné')),
]
scan_status = models.CharField(max_length=1, choices=SCAN_CHOICES, default=NOT_ACTIV,
verbose_name=_("Status du scan"))
class LigneArticle(models.Model):
uuid = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4)
datetime = models.DateTimeField(auto_now=True)
@ -372,7 +388,7 @@ class LigneArticle(models.Model):
qty = models.SmallIntegerField()
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, blank=True, null=True)
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, blank=True, null=True, related_name='paiements')
carte = models.ForeignKey(CarteCashless, on_delete=models.PROTECT, blank=True, null=True)
paiement_stripe = models.ForeignKey(Paiement_stripe, on_delete=models.PROTECT, blank=True, null=True)

View File

@ -12,7 +12,6 @@ class ReservationValidator(serializers.Serializer):
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_articles(self, value):
value_dict = {}
@ -22,17 +21,3 @@ class ReservationValidator(serializers.Serializer):
value_dict[pk] = qty
return value_dict
def validate_billets(self, value):
value_dict = {}
billet_obj = Price.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.max_per_user :
# return value
# else :
# raise serializers.ValidationError(_(f"Pas plus de {configuration.max_per_user} places en même temps."))

View File

@ -103,8 +103,6 @@ class creation_paiement_stripe():
return line_items
def _checkout_session(self):
checkout_session = stripe.checkout.Session.create(
success_url=f'{self.absolute_domain}/stripe/return/{self.paiement_stripe_db.uuid}',
cancel_url=f'{self.absolute_domain}/stripe/return/{self.paiement_stripe_db.uuid}',