Envoie vers Stripe et création du ticket
This commit is contained in:
parent
aaa81f6c7f
commit
cfc22159b4
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
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
|
||||
|
||||
|
||||
'''
|
||||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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."))
|
||||
|
|
|
|||
|
|
@ -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}',
|
||||
|
|
|
|||
Loading…
Reference in New Issue