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 @@
- {% 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 %}
-
{% 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 %} - + {% trans %}Remaining account amount: {% endtrans %} + {{ "%0.2f"|format(customer_amount|float - basket.total) }} € {% endif %} + {% endif %} + +{% 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 %} + + {% 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