Rename makecommand to checkout

This commit is contained in:
Antoine Bartuccio 2025-04-15 18:42:17 +02:00
parent 262ed7eb4c
commit bc99390b25
6 changed files with 31 additions and 45 deletions

View File

@ -32,48 +32,39 @@ susnommés afin de comprendre comment celui-ci marche.
Cette application contient les vues suivantes : Cette application contient les vues suivantes :
- `eboutic_main` (GET) : la vue retournant la page principale de la boutique en ligne. - `EbouticMainView` (GET/POST) : la vue retournant la page principale de la boutique en ligne.
Cette vue effectue un filtrage des produits à montrer à l'utilisateur en Cette vue effectue un filtrage des produits à montrer à l'utilisateur en
fonction de ce qu'il a le droit d'acheter. fonction de ce qu'il a le droit d'acheter.
Si cette vue est appelée lors d'une redirection parce qu'une erreur Elle est en charge de récupérer le formulaire de création d'un panier et
est survenue au cours de la navigation sur la boutique, il est possible redirige alors vers la vue de checkout.
de donner les messages d'erreur à donner à l'utilisateur dans la session
avec la clef ``"errors"``.
- ``payment_result`` (GET) : retourne une page assez simple disant à l'utilisateur - ``payment_result`` (GET) : retourne une page assez simple disant à l'utilisateur
si son paiement a échoué ou réussi. Cette vue est appelée par redirection si son paiement a échoué ou réussi. Cette vue est appelée par redirection
lorsque l'utilisateur paye son panier avec son argent du compte AE. lorsque l'utilisateur paye son panier avec son argent du compte AE.
- ``EbouticCommand`` (POST) : traite la soumission d'un panier par l'utilisateur. - ``EbouticCheckout`` (GET/POST) : Page récapitulant le contenu d'un panier.
Lors de l'appel de cette vue, la requête doit contenir un cookie avec l'état Permet de sélectionner le moyen de paiement et de mettre à jour ses coordonnées
du panier à valider. Ce panier doit strictement être de la forme : de paiement par carte bancaire.
``` - ``PayWithSith`` (POST) : paie le panier avec l'argent présent sur le compte
[
{"id": <int>, "name": <str>, "quantity": <int>, "unit_price": <float>},
{"id": <int>, "name": <str>, "quantity": <int>, "unit_price": <float>},
<etc.>
]
```
Si le panier est mal formaté ou contient des valeurs invalides,
une redirection est faite vers `eboutic_main`.
- ``pay_with_sith`` (POST) : paie le panier avec l'argent présent sur le compte
AE. Redirige vers `payment_result`. AE. Redirige vers `payment_result`.
- ``ETransactionAutoAnswer`` (GET) : vue destinée à communiquer avec le service - ``ETransactionAutoAnswer`` (GET) : vue destinée à communiquer avec le service
de paiement bancaire pour valider ou non le paiement de l'utilisateur. de paiement bancaire pour valider ou non le paiement de l'utilisateur.
- ``BillingInfoFormFragment`` (GET/POST) : vue destinée à gérer les informations de paiement de l'utilisateur courant.
# Les templates # Les templates
- ``eboutic_payment_result.jinja`` : très court template contenant juste - ``eboutic_payment_result.jinja`` : très court template contenant juste
un message pour dire à l'utilisateur si son achat s'est bien déroulé. un message pour dire à l'utilisateur si son achat s'est bien déroulé.
Retourné par la vue ``payment_result``. Retourné par la vue ``payment_result``.
- ``eboutic_makecommand.jinja`` : template contenant un résumé du panier et deux - ``eboutic_checkout.jinja`` : template contenant un résumé du panier et deux
boutons, un pour payer avec le site AE et l'autre pour payer par carte bancaire. boutons, un pour payer avec le site AE et l'autre pour payer par carte bancaire.
Retourné par la vue ``EbouticCommand`` Retourné par la vue ``EbouticCheckout``
- ``eboutic_billing_info.jinja`` : formulaire de modification des coordonnées bancaires.
Elle permet également de mettre à jour ses coordonnées de paiement
- ``eboutic_main.jinja`` : le plus gros template de cette application. Contient - ``eboutic_main.jinja`` : le plus gros template de cette application. Contient
une interface pour que l'utilisateur puisse consulter les produits et remplir une interface pour que l'utilisateur puisse consulter les produits et remplir
son panier. Les opérations de remplissage du panier se font entièrement côté client. son panier. Les opérations de remplissage du panier se font entièrement côté client.
À chaque clic pour ajouter ou retirer un élément du panier, le script JS À chaque clic pour ajouter ou retirer un élément du panier, le script JS
(AlpineJS, plus précisément) édite en même temps un cookie. (AlpineJS, plus précisément) édite en même temps le localStorage du navigateur.
Au moment de la validation du panier, ce cookie est envoyé au serveur pour Cette vue fabrique dynamiquement un formulaire qui sera soumis au serveur.
vérifier que la commande est valide et payer.
# Les modèles # Les modèles

View File

@ -9,7 +9,7 @@
{% endblock %} {% endblock %}
{% block additional_js %} {% block additional_js %}
<script type="module" src="{{ static('bundled/eboutic/makecommand-index.ts') }}"></script> <script type="module" src="{{ static('bundled/eboutic/checkout-index.ts') }}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -30,10 +30,10 @@
{{ form.management_form }} {{ form.management_form }}
</div> </div>
{% if form.non_form_errors() %} {% if form.non_form_errors() or form.errors %}
<div class="alert alert-red"> <div class="alert alert-red">
<div class="alert-main"> <div class="alert-main">
{% for error in form.non_form_errors() %} {% for error in form.non_form_errors() + form.errors %}
<p style="margin: 0">{{ error }}</p> <p style="margin: 0">{{ error }}</p>
{% endfor %} {% endfor %}
</div> </div>
@ -50,7 +50,7 @@
</span> </span>
</li> </li>
<template x-for="(item, index) in Object.values(basket)"> <template x-for="(item, index) in Object.values(basket)" :key="item.id">
<li class="item-row" x-show="item.quantity > 0"> <li class="item-row" x-show="item.quantity > 0">
<div class="item-quantity"> <div class="item-quantity">
<i class="fa fa-minus fa-xs" @click="remove(item.id)"></i> <i class="fa fa-minus fa-xs" @click="remove(item.id)"></i>
@ -94,8 +94,8 @@
<i class="fa fa-check"></i> <i class="fa fa-check"></i>
<input type="submit" value="{% trans %}Validate{% endtrans %}"/> <input type="submit" value="{% trans %}Validate{% endtrans %}"/>
</button> </button>
</form> </div>
</div> </form>
</div> </div>
<div id="catalog"> <div id="catalog">
{% if not request.user.date_of_birth %} {% if not request.user.date_of_birth %}

View File

@ -27,8 +27,8 @@ from django.urls import path, register_converter
from eboutic.converters import PaymentResultConverter from eboutic.converters import PaymentResultConverter
from eboutic.views import ( from eboutic.views import (
BillingInfoFormFragment, BillingInfoFormFragment,
EbouticCommand, EbouticCheckout,
EbouticCreateBasket, EbouticMainView,
EbouticPayWithSith, EbouticPayWithSith,
EtransactionAutoAnswer, EtransactionAutoAnswer,
EurokPartnerFragment, EurokPartnerFragment,
@ -39,8 +39,8 @@ register_converter(PaymentResultConverter, "res")
urlpatterns = [ urlpatterns = [
# Subscription views # Subscription views
path("", EbouticCreateBasket.as_view(), name="main"), path("", EbouticMainView.as_view(), name="main"),
path("command/<int:basket_id>", EbouticCommand.as_view(), name="command"), path("checkout/<int:basket_id>", EbouticCheckout.as_view(), name="checkout"),
path("billing-infos/", BillingInfoFormFragment.as_view(), name="billing_infos"), path("billing-infos/", BillingInfoFormFragment.as_view(), name="billing_infos"),
path( path(
"pay/sith/<int:basket_id>", EbouticPayWithSith.as_view(), name="pay_with_sith" "pay/sith/<int:basket_id>", EbouticPayWithSith.as_view(), name="pay_with_sith"

View File

@ -18,7 +18,6 @@ from __future__ import annotations
import base64 import base64
import contextlib import contextlib
import json import json
from datetime import datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import sentry_sdk import sentry_sdk
@ -75,7 +74,7 @@ EbouticBasketForm = forms.formset_factory(
) )
class EbouticCreateBasket(LoginRequiredMixin, FormView): class EbouticMainView(LoginRequiredMixin, FormView):
"""Main view of the eboutic application. """Main view of the eboutic application.
The purchasable products are those of the eboutic which The purchasable products are those of the eboutic which
@ -98,6 +97,7 @@ class EbouticCreateBasket(LoginRequiredMixin, FormView):
def form_valid(self, formset): def form_valid(self, formset):
if len(formset) == 0: if len(formset) == 0:
formset.errors.append(_("Your basket is empty"))
return self.form_invalid(formset) return self.form_invalid(formset)
with transaction.atomic(): with transaction.atomic():
@ -110,7 +110,7 @@ class EbouticCreateBasket(LoginRequiredMixin, FormView):
return super().form_valid(formset) return super().form_valid(formset)
def get_success_url(self): def get_success_url(self):
return reverse("eboutic:command", kwargs={"basket_id": self.basket.id}) return reverse("eboutic:checkout", kwargs={"basket_id": self.basket.id})
@cached_property @cached_property
def products(self) -> list[Product]: def products(self) -> list[Product]:
@ -196,11 +196,11 @@ class BillingInfoFormFragment(
return self.request.path return self.request.path
class EbouticCommand(CanViewMixin, UseFragmentsMixin, DetailView): class EbouticCheckout(CanViewMixin, UseFragmentsMixin, DetailView):
model = Basket model = Basket
pk_url_kwarg = "basket_id" pk_url_kwarg = "basket_id"
context_object_name = "basket" context_object_name = "basket"
template_name = "eboutic/eboutic_makecommand.jinja" template_name = "eboutic/eboutic_checkout.jinja"
fragments = { fragments = {
"billing_infos_form": BillingInfoFormFragment, "billing_infos_form": BillingInfoFormFragment,
} }
@ -247,13 +247,8 @@ class EbouticPayWithSith(CanViewMixin, SingleObjectMixin, View):
basket.delete() basket.delete()
return redirect("eboutic:payment_result", "success") return redirect("eboutic:payment_result", "success")
except DatabaseError as e: except DatabaseError as e:
with sentry_sdk.push_scope() as scope: sentry_sdk.capture_exception(e)
scope.user = {"username": request.user.username} return redirect("eboutic:payment_result", "failure")
scope.set_extra("someVariable", e.__repr__())
sentry_sdk.capture_message(
f"Erreur le {datetime.now()} dans eboutic.pay_with_sith"
)
return redirect("eboutic:payment_result", "failure")
class EtransactionAutoAnswer(View): class EtransactionAutoAnswer(View):