refactor eboutic command page

This commit is contained in:
Thomas Girod 2025-04-06 16:25:55 +02:00
parent e35c1d1928
commit d03c425a17
8 changed files with 90 additions and 92 deletions

View File

@ -9,7 +9,7 @@ repos:
# Run the formatter.
- id: ruff-format
- repo: https://github.com/biomejs/pre-commit
rev: "v0.1.0" # Use the sha / tag you want to point at
rev: v0.6.1
hooks:
- id: biome-check
additional_dependencies: ["@biomejs/biome@1.9.4"]

View File

@ -26,7 +26,7 @@ class EtransactionInfoController(ControllerBase):
customer=customer, defaults=info.model_dump(exclude_none=True)
)
@route.get("/data", url_name="etransaction_data", include_in_schema=False)
@route.get("/data", url_name="etransaction_data")
def fetch_etransaction_data(self):
"""Generate the data to pay an eboutic command with paybox.

View File

@ -1,56 +1,61 @@
/**
* @readonly
* @enum {number}
*/
const BillingInfoReqState = {
// biome-ignore lint/style/useNamingConvention: this feels more like an enum
SUCCESS: 1,
// biome-ignore lint/style/useNamingConvention: this feels more like an enum
FAILURE: 2,
// biome-ignore lint/style/useNamingConvention: this feels more like an enum
SENDING: 3,
};
import { exportToHtml } from "#core:utils/globals";
import {
type BillingInfoSchema,
etransactioninfoFetchEtransactionData,
etransactioninfoPutUserBillingInfo,
} from "#openapi";
enum BillingInfoReqState {
Success = "0",
Failure = "1",
Sending = "2",
}
exportToHtml("BillingInfoReqState", BillingInfoReqState);
document.addEventListener("alpine:init", () => {
Alpine.store("billing_inputs", {
// biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja
data: etData,
Alpine.data("etransactionData", (initialData) => ({
data: initialData,
async fill() {
const button = document.getElementById("bank-submit-button") as HTMLButtonElement;
button.disabled = true;
// biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja
const res = await fetch(etDataUrl);
if (res.ok) {
this.data = await res.json();
const res = await etransactioninfoFetchEtransactionData();
if (res.response.ok) {
this.data = res.data;
button.disabled = false;
}
},
});
}));
Alpine.data("billing_infos", () => ({
Alpine.data("billing_infos", (userId: number) => ({
/** @type {BillingInfoReqState | null} */
reqState: null,
async sendForm() {
this.reqState = BillingInfoReqState.SENDING;
this.reqState = BillingInfoReqState.Sending;
const form = document.getElementById("billing_info_form");
document.getElementById("bank-submit-button").disabled = true;
const submitButton = document.getElementById(
"bank-submit-button",
) as HTMLButtonElement;
submitButton.disabled = true;
const payload = Object.fromEntries(
Array.from(form.querySelectorAll("input, select"))
.filter((elem) => elem.type !== "submit" && elem.value)
.map((elem) => [elem.name, elem.value]),
.filter((elem: HTMLInputElement) => elem.type !== "submit" && elem.value)
.map((elem: HTMLInputElement) => [elem.name, elem.value]),
);
// biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja
const res = await fetch(billingInfoUrl, {
method: "PUT",
body: JSON.stringify(payload),
const res = await etransactioninfoPutUserBillingInfo({
// biome-ignore lint/style/useNamingConvention: API is snake_case
path: { user_id: userId },
body: payload as unknown as BillingInfoSchema,
});
this.reqState = res.ok
? BillingInfoReqState.SUCCESS
: BillingInfoReqState.FAILURE;
if (res.status === 422) {
const errors = (await res.json()).detail.flatMap((err) => err.loc);
this.reqState = res.response.ok
? BillingInfoReqState.Success
: BillingInfoReqState.Failure;
if (res.response.status === 422) {
const errors = await res.response
.json()
.detail.flatMap((err: Record<"loc", string>) => err.loc);
for (const elem of Array.from(form.querySelectorAll("input")).filter((elem) =>
errors.includes(elem.name),
)) {
@ -58,29 +63,27 @@ document.addEventListener("alpine:init", () => {
elem.reportValidity();
elem.oninput = () => elem.setCustomValidity("");
}
} else if (res.ok) {
Alpine.store("billing_inputs").fill();
} else if (res.response.ok) {
this.$dispatch("billing-infos-filled");
}
},
getAlertColor() {
if (this.reqState === BillingInfoReqState.SUCCESS) {
if (this.reqState === BillingInfoReqState.Success) {
return "green";
}
if (this.reqState === BillingInfoReqState.FAILURE) {
if (this.reqState === BillingInfoReqState.Failure) {
return "red";
}
return "";
},
getAlertMessage() {
if (this.reqState === BillingInfoReqState.SUCCESS) {
// biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja
return billingInfoSuccessMessage;
if (this.reqState === BillingInfoReqState.Success) {
return gettext("Billing info registration success");
}
if (this.reqState === BillingInfoReqState.FAILURE) {
// biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja
return billingInfoFailureMessage;
if (this.reqState === BillingInfoReqState.Failure) {
return gettext("Billing info registration failure");
}
return "";
},

View File

@ -158,4 +158,3 @@
flex-direction: column;
}
}

View File

@ -9,7 +9,7 @@
{% endblock %}
{% block additional_js %}
<script src="{{ static('bundled/eboutic/makecommand-index.ts') }}" defer></script>
<script type="module" src="{{ static('bundled/eboutic/makecommand-index.ts') }}"></script>
{% endblock %}
{% block content %}
@ -56,7 +56,7 @@
<div
class="collapse"
:class="{'shadow': collapsed}"
x-data="{collapsed: !billingInfoExist}"
x-data="{collapsed: !{{ "true" if billing_infos else "false" }}}"
x-cloak
>
<div class="collapse-header clickable" @click="collapsed = !collapsed">
@ -70,7 +70,7 @@
<form
class="collapse-body"
id="billing_info_form"
x-data="billing_infos"
x-data="billing_infos({{ user.id }})"
x-show="collapsed"
x-transition.scale.origin.top
@submit.prevent="await sendForm()"
@ -79,7 +79,7 @@
{{ billing_form }}
<br />
<div
x-show="[BillingInfoReqState.SUCCESS, BillingInfoReqState.FAILURE].includes(reqState)"
x-show="[BillingInfoReqState.Success, BillingInfoReqState.Failure].includes(reqState)"
class="alert"
:class="'alert-' + getAlertColor()"
x-transition
@ -92,19 +92,20 @@
<input
type="submit" class="btn btn-blue clickable"
value="{% trans %}Validate{% endtrans %}"
:disabled="reqState === BillingInfoReqState.SENDING"
:disabled="reqState === BillingInfoReqState.Sending"
>
</form>
</div>
<br>
{% if billing_infos_state == BillingInfoState.EMPTY %}
<div class="alert alert-yellow">
{% trans %}You must fill your billing infos if you want to pay with your credit
card{% endtrans %}
{% trans trimmed %}
You must fill your billing infos if you want to pay with your credit card
{% endtrans %}
</div>
{% elif billing_infos_state == BillingInfoState.MISSING_PHONE_NUMBER %}
<div class="alert alert-yellow">
{% trans %}
{% trans trimmed %}
The Crédit Agricole changed its policy related to the billing
information that must be provided in order to pay with a credit card.
If you want to pay with your credit card, you must add a phone number
@ -112,8 +113,14 @@
{% endtrans %}
</div>
{% endif %}
<form method="post" action="{{ settings.SITH_EBOUTIC_ET_URL }}" name="bank-pay-form">
<template x-data x-for="[key, value] in Object.entries($store.billing_inputs.data)">
<form
method="post"
action="{{ settings.SITH_EBOUTIC_ET_URL }}"
name="bank-pay-form"
x-data="etransactionData(initialEtData)"
@billing-infos-filled.window="await fill()"
>
<template x-for="[key, value] in Object.entries(data)" :key="key">
<input type="hidden" :name="key" :value="value">
</template>
<input
@ -140,17 +147,11 @@
{% block script %}
<script>
const billingInfoUrl = '{{ url("api:put_billing_info", user_id=request.user.id) }}';
const etDataUrl = '{{ url("api:etransaction_data") }}';
const billingInfoExist = {{ "true" if billing_infos else "false" }};
const billingInfoSuccessMessage = "{% trans %}Billing info registration success{% endtrans %}";
const billingInfoFailureMessage = "{% trans %}Billing info registration failure{% endtrans %}";
{% if billing_infos %}
const etData = {{ billing_infos|safe }}
{% else %}
const etData = {}
{% endif %}
{% if billing_infos -%}
const initialEtData = {{ billing_infos|safe }}
{%- else -%}
const initialEtData = {}
{%- endif %}
</script>
{{ super() }}
{% endblock %}

View File

@ -26,7 +26,9 @@ from cryptography.hazmat.primitives.hashes import SHA1
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import (
LoginRequiredMixin,
)
from django.core.exceptions import SuspiciousOperation
from django.db import DatabaseError, transaction
from django.http import HttpRequest, HttpResponse

View File

@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-06 16:58+0200\n"
"POT-Creation-Date: 2025-04-06 15:54+0200\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -3831,25 +3831,18 @@ msgstr "Informations de facturation"
#: eboutic/templates/eboutic/eboutic_makecommand.jinja
msgid ""
"You must fill your billing infos if you want to pay with your credit\n"
" card"
"You must fill your billing infos if you want to pay with your credit card"
msgstr ""
"Vous devez renseigner vos coordonnées de facturation si vous voulez payer "
"par carte bancaire"
#: eboutic/templates/eboutic/eboutic_makecommand.jinja
msgid ""
"\n"
" The Crédit Agricole changed its policy related to the "
"billing\n"
" information that must be provided in order to pay with a "
"credit card.\n"
" If you want to pay with your credit card, you must add a "
"phone number\n"
" to the data you already provided.\n"
" "
"The Crédit Agricole changed its policy related to the billing information "
"that must be provided in order to pay with a credit card. If you want to pay "
"with your credit card, you must add a phone number to the data you already "
"provided."
msgstr ""
"\n"
"Le Crédit Agricole a changé sa politique relative aux informations à "
"fournir pour effectuer un paiement par carte bancaire. De ce fait, si vous "
"souhaitez payer par carte, vous devez rajouter un numéro de téléphone aux "
@ -3876,14 +3869,6 @@ msgstr ""
msgid "Pay with Sith account"
msgstr "Payer avec un compte AE"
#: eboutic/templates/eboutic/eboutic_makecommand.jinja
msgid "Billing info registration success"
msgstr "Informations de facturation enregistrées"
#: eboutic/templates/eboutic/eboutic_makecommand.jinja
msgid "Billing info registration failure"
msgstr "Echec de l'enregistrement des informations de facturation."
#: eboutic/templates/eboutic/eboutic_payment_result.jinja
msgid "Payment successful"
msgstr "Le paiement a été effectué"

View File

@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-28 13:52+0100\n"
"POT-Creation-Date: 2025-04-06 15:47+0200\n"
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
"Last-Translator: Sli <antoine@bartuccio.fr>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -201,10 +201,18 @@ msgstr "Types de produits réordonnés !"
msgid "Product type reorganisation failed with status code : %d"
msgstr "La réorganisation des types de produit a échoué avec le code : %d"
#: eboutic/static/eboutic/js/makecommand.js
#: eboutic/static/bundled/eboutic/makecommand-index.ts
msgid "Incorrect value"
msgstr "Valeur incorrecte"
#: eboutic/static/bundled/eboutic/makecommand-index.ts
msgid "Billing info registration success"
msgstr "Informations de facturation enregistrées"
#: eboutic/static/bundled/eboutic/makecommand-index.ts
msgid "Billing info registration failure"
msgstr "Echec de l'enregistrement des informations de facturation."
#: sas/static/bundled/sas/pictures-download-index.ts
msgid "pictures.%(extension)s"
msgstr "photos.%(extension)s"