diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d72985b..b334a7c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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"] diff --git a/eboutic/api.py b/eboutic/api.py index 0054c02a..797adf20 100644 --- a/eboutic/api.py +++ b/eboutic/api.py @@ -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. diff --git a/eboutic/static/bundled/eboutic/makecommand-index.ts b/eboutic/static/bundled/eboutic/makecommand-index.ts new file mode 100644 index 00000000..c1e4b52f --- /dev/null +++ b/eboutic/static/bundled/eboutic/makecommand-index.ts @@ -0,0 +1,91 @@ +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.data("etransactionData", (initialData) => ({ + data: initialData, + + async fill() { + const button = document.getElementById("bank-submit-button") as HTMLButtonElement; + button.disabled = true; + const res = await etransactioninfoFetchEtransactionData(); + if (res.response.ok) { + this.data = res.data; + button.disabled = false; + } + }, + })); + + Alpine.data("billing_infos", (userId: number) => ({ + /** @type {BillingInfoReqState | null} */ + reqState: null, + + async sendForm() { + this.reqState = BillingInfoReqState.Sending; + const form = document.getElementById("billing_info_form"); + const submitButton = document.getElementById( + "bank-submit-button", + ) as HTMLButtonElement; + submitButton.disabled = true; + const payload = Object.fromEntries( + Array.from(form.querySelectorAll("input, select")) + .filter((elem: HTMLInputElement) => elem.type !== "submit" && elem.value) + .map((elem: HTMLInputElement) => [elem.name, elem.value]), + ); + 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.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), + )) { + elem.setCustomValidity(gettext("Incorrect value")); + elem.reportValidity(); + elem.oninput = () => elem.setCustomValidity(""); + } + } else if (res.response.ok) { + this.$dispatch("billing-infos-filled"); + } + }, + + getAlertColor() { + if (this.reqState === BillingInfoReqState.Success) { + return "green"; + } + if (this.reqState === BillingInfoReqState.Failure) { + return "red"; + } + return ""; + }, + + getAlertMessage() { + if (this.reqState === BillingInfoReqState.Success) { + return gettext("Billing info registration success"); + } + if (this.reqState === BillingInfoReqState.Failure) { + return gettext("Billing info registration failure"); + } + return ""; + }, + })); +}); diff --git a/eboutic/static/eboutic/css/eboutic.css b/eboutic/static/eboutic/css/eboutic.css index abf121d0..6ca6beef 100644 --- a/eboutic/static/eboutic/css/eboutic.css +++ b/eboutic/static/eboutic/css/eboutic.css @@ -158,4 +158,3 @@ flex-direction: column; } } - diff --git a/eboutic/static/eboutic/js/makecommand.js b/eboutic/static/eboutic/js/makecommand.js deleted file mode 100644 index 3ccb4280..00000000 --- a/eboutic/static/eboutic/js/makecommand.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @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, -}; - -document.addEventListener("alpine:init", () => { - Alpine.store("billing_inputs", { - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - data: etData, - - async fill() { - document.getElementById("bank-submit-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(); - document.getElementById("bank-submit-button").disabled = false; - } - }, - }); - - Alpine.data("billing_infos", () => ({ - /** @type {BillingInfoReqState | null} */ - reqState: null, - - async sendForm() { - this.reqState = BillingInfoReqState.SENDING; - const form = document.getElementById("billing_info_form"); - document.getElementById("bank-submit-button").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]), - ); - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - const res = await fetch(billingInfoUrl, { - method: "PUT", - body: JSON.stringify(payload), - }); - this.reqState = res.ok - ? BillingInfoReqState.SUCCESS - : BillingInfoReqState.FAILURE; - if (res.status === 422) { - const errors = (await res.json()).detail.flatMap((err) => err.loc); - for (const elem of Array.from(form.querySelectorAll("input")).filter((elem) => - errors.includes(elem.name), - )) { - elem.setCustomValidity(gettext("Incorrect value")); - elem.reportValidity(); - elem.oninput = () => elem.setCustomValidity(""); - } - } else if (res.ok) { - Alpine.store("billing_inputs").fill(); - } - }, - - getAlertColor() { - if (this.reqState === BillingInfoReqState.SUCCESS) { - return "green"; - } - 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.FAILURE) { - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - return billingInfoFailureMessage; - } - return ""; - }, - })); -}); diff --git a/eboutic/templates/eboutic/eboutic_makecommand.jinja b/eboutic/templates/eboutic/eboutic_makecommand.jinja index e18514e9..62053af5 100644 --- a/eboutic/templates/eboutic/eboutic_makecommand.jinja +++ b/eboutic/templates/eboutic/eboutic_makecommand.jinja @@ -9,7 +9,7 @@ {% endblock %} {% block additional_js %} - + {% endblock %} {% block content %} @@ -33,124 +33,125 @@ {{ item.product_unit_price }} € {% endfor %} - - + + -

- {% trans %}Basket amount: {% endtrans %}{{ "%0.2f"|format(basket.total) }} € +

+ {% trans %}Basket amount: {% endtrans %}{{ "%0.2f"|format(basket.total) }} € - {% if customer_amount != None %} -
- {% trans %}Current account amount: {% endtrans %} - {{ "%0.2f"|format(customer_amount) }} € - - {% if not basket.contains_refilling_item %} -
- {% trans %}Remaining account amount: {% endtrans %} - {{ "%0.2f"|format(customer_amount|float - basket.total) }} € - {% endif %} - {% endif %} -

+ {% if customer_amount != None %}
- {% if settings.SITH_EBOUTIC_CB_ENABLED %} -
-
- - {% trans %}Billing information{% endtrans %} - - - - -
-
- {% csrf_token %} - {{ billing_form }} -
-
-
-
- -
-
- -
-
+ {% trans %}Current account amount: {% endtrans %} + {{ "%0.2f"|format(customer_amount) }} € + + {% if not basket.contains_refilling_item %}
- {% if billing_infos_state == BillingInfoState.EMPTY %} -
- {% trans %}You must fill your billing infos if you want to pay with your credit - card{% endtrans %} -
- {% elif billing_infos_state == BillingInfoState.MISSING_PHONE_NUMBER %} -
- {% trans %} - 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. - {% endtrans %} -
- {% endif %} -
- - -
- {% endif %} - {% if basket.contains_refilling_item %} -

{% trans %}AE account payment disabled because your basket contains refilling items.{% endtrans %}

- {% elif basket.total > user.account_balance %} -

{% trans %}AE account payment disabled because you do not have enough money remaining.{% endtrans %}

- {% else %} -
- {% csrf_token %} - - -
+ {% trans %}Remaining account amount: {% endtrans %} + {{ "%0.2f"|format(customer_amount|float - basket.total) }} € {% endif %} + {% endif %} +

+
+ {% if settings.SITH_EBOUTIC_CB_ENABLED %} +
+
+ + {% trans %}Billing information{% endtrans %} + + + + +
+
+ {% csrf_token %} + {{ billing_form }} +
+
+
+
+ +
+
+ +
+
+ {% if billing_infos_state == BillingInfoState.EMPTY %} +
+ {% trans trimmed %} + You must fill your billing infos if you want to pay with your credit card + {% endtrans %} +
+ {% elif billing_infos_state == BillingInfoState.MISSING_PHONE_NUMBER %} +
+ {% 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 + to the data you already provided. + {% endtrans %} +
+ {% endif %} +
+ + +
+ {% endif %} + {% if basket.contains_refilling_item %} +

{% trans %}AE account payment disabled because your basket contains refilling items.{% endtrans %}

+ {% elif basket.total > user.account_balance %} +

{% trans %}AE account payment disabled because you do not have enough money remaining.{% endtrans %}

+ {% else %} +
+ {% csrf_token %} + + +
+ {% endif %} + {% endblock %} {% block script %} {{ super() }} {% endblock %} diff --git a/eboutic/views.py b/eboutic/views.py index 14f129fd..dfa79c22 100644 --- a/eboutic/views.py +++ b/eboutic/views.py @@ -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 diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index f3a6345f..1e944299 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -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 \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é" diff --git a/locale/fr/LC_MESSAGES/djangojs.po b/locale/fr/LC_MESSAGES/djangojs.po index 9b967354..7952baa4 100644 --- a/locale/fr/LC_MESSAGES/djangojs.po +++ b/locale/fr/LC_MESSAGES/djangojs.po @@ -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 \n" "Language-Team: AE info \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"