Compare commits

...

2 Commits

Author SHA1 Message Date
imperosol 177002b8b8 tweak django-countries settings 2026-06-05 00:43:56 +02:00
imperosol e629b36465 make BillingInfo.phone_number non-nullable 2026-06-05 00:43:56 +02:00
9 changed files with 48 additions and 79 deletions
@@ -15,7 +15,7 @@ class Migration(migrations.Migration):
blank=True,
help_text=(
"If a limit is set, the product won't be purchasable "
"anymore once the latter is reached."
"anymore on the eboutic once the latter is reached."
),
null=True,
verbose_name="clic limit",
@@ -0,0 +1,26 @@
# Generated by Django 5.2.14 on 2026-06-02 10:45
import django_countries.fields
import phonenumber_field.modelfields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("counter", "0040_product_clic_limit")]
operations = [
migrations.AlterField(
model_name="billinginfo",
name="country",
field=django_countries.fields.CountryField(
max_length=2, verbose_name="Country"
),
),
migrations.AlterField(
model_name="billinginfo",
name="phone_number",
field=phonenumber_field.modelfields.PhoneNumberField(
max_length=128, region=None, verbose_name="Phone number"
),
),
]
+2 -9
View File
@@ -228,15 +228,8 @@ class BillingInfo(models.Model):
address_2 = models.CharField(_("Address 2"), max_length=50, blank=True, null=True)
zip_code = models.CharField(_("Zip code"), max_length=16) # code postal
city = models.CharField(_("City"), max_length=50)
country = CountryField(blank_label=_("Country"))
# This table was created during the A22 semester.
# However, later on, CA asked for the phone number to be added to the billing info.
# As the table was already created, this new field had to be nullable,
# even tough it is required by the bank and shouldn't be null.
# If one day there is no null phone number remaining,
# please make the field non-nullable.
phone_number = PhoneNumberField(_("Phone number"), null=True, blank=False)
country = CountryField(_("Country"))
phone_number = PhoneNumberField(_("Phone number"))
def __str__(self):
return f"{self.first_name} {self.last_name}"
+1 -30
View File
@@ -16,7 +16,6 @@ from __future__ import annotations
import hmac
from datetime import datetime
from enum import Enum
from typing import Self
from dict2xml import dict2xml
@@ -40,30 +39,6 @@ from counter.models import (
)
class BillingInfoState(Enum):
VALID = 1
EMPTY = 2
MISSING_PHONE_NUMBER = 3
@classmethod
def from_model(cls, info: BillingInfo | None) -> BillingInfoState:
if info is None:
return cls.EMPTY
for attr in [
"first_name",
"last_name",
"address_1",
"zip_code",
"city",
"country",
]:
if getattr(info, attr) == "":
return cls.EMPTY
if info.phone_number is None:
return cls.MISSING_PHONE_NUMBER
return cls.VALID
class Basket(models.Model):
"""Basket is built when the user connects to an eboutic page."""
@@ -162,11 +137,7 @@ class Basket(models.Model):
if self.is_expired:
raise ValueError("This method cannot be called on an expired basket.")
customer = user.customer
if (
not hasattr(user.customer, "billing_infos")
or BillingInfoState.from_model(user.customer.billing_infos)
!= BillingInfoState.VALID
):
if not hasattr(user.customer, "billing_infos"):
raise BillingInfo.DoesNotExist
cart = {
"shoppingcart": {"total": {"totalQuantity": min(self.items.count(), 99)}}
@@ -24,7 +24,7 @@
x-cloak
>
{% csrf_token %}
{{ form.as_p() }}
{{ form }}
<br>
<input
type="submit" class="btn btn-blue clickable"
+2 -5
View File
@@ -37,12 +37,9 @@ class TestBillingInfo:
def test_edit_infos(self, client: Client, payload: dict[str, str]):
user = subscriber_user.make()
baker.make(BillingInfo, customer=user.customer)
baker.make(BillingInfo, customer=user.customer, phone_number="06 01 02 03 04")
client.force_login(user)
response = client.post(
reverse("eboutic:billing_infos"),
payload,
)
response = client.post(reverse("eboutic:billing_infos"), payload)
user.refresh_from_db()
infos = BillingInfo.objects.get(customer__user=user)
assert response.status_code == 302
+5 -16
View File
@@ -58,7 +58,7 @@ from counter.models import (
Selling,
get_eboutic,
)
from eboutic.models import Basket, BasketItem, BillingInfoState, Invoice, InvoiceItem
from eboutic.models import Basket, BasketItem, Invoice, InvoiceItem
if TYPE_CHECKING:
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
@@ -187,7 +187,7 @@ def payment_result(request, result: str) -> HttpResponse:
class BillingInfoFormFragment(
LoginRequiredMixin, FragmentMixin, SuccessMessageMixin, UpdateView
):
"""Update billing info"""
"""Update or create billing info"""
model = BillingInfo
form_class = BillingInfoForm
@@ -218,24 +218,13 @@ class BillingInfoFormFragment(
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
kwargs["billing_infos_state"] = BillingInfoState.from_model(self.object)
kwargs["action"] = reverse("eboutic:billing_infos")
match BillingInfoState.from_model(self.object):
case BillingInfoState.EMPTY:
if not self.object:
messages.warning(
self.request,
_(
"You must fill your billing infos if you want to pay with your credit card"
),
)
case BillingInfoState.MISSING_PHONE_NUMBER:
messages.warning(
self.request,
_(
"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.",
"You must fill your billing infos "
"if you want to pay with your credit card"
),
)
return kwargs
-12
View File
@@ -4511,18 +4511,6 @@ msgstr ""
"Vous devez renseigner vos coordonnées de facturation si vous voulez payer "
"par carte bancaire"
#: eboutic/views.py
msgid ""
"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 ""
"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 "
"données que vous aviez déjà fourni."
#: eboutic/views.py
msgid "Basket expired"
msgstr "Panier expiré"
+5
View File
@@ -115,6 +115,7 @@ INSTALLED_APPS = (
"django_jinja",
"ninja_extra",
"haystack",
"django_countries",
"django_celery_results",
"django_celery_beat",
"captcha",
@@ -294,7 +295,11 @@ USE_TZ = True
LOCALE_PATHS = [BASE_DIR / "locale"]
# for PhoneNumberField
PHONENUMBER_DEFAULT_REGION = "FR"
# for CountryField
COUNTRIES_FIRST = ["FR", "CH", "DE"]
COUNTRIES_FIRST_BREAK = "───────────"
# Medias
MEDIA_URL = "/data/"