Skip to content

Commit 23e7228

Browse files
authored
add phone number validation for the form (#372)
1 parent 7d01085 commit 23e7228

File tree

6 files changed

+66
-5
lines changed

6 files changed

+66
-5
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Dict
2+
3+
import phonenumbers
4+
from django.utils.translation import gettext_lazy as _
5+
from phonenumbers.phonenumber import PhoneNumber
6+
7+
8+
def validate_phone_number(raw_phone_number) -> Dict[str, str]:
9+
try:
10+
phone_number: PhoneNumber = phonenumbers.parse(raw_phone_number)
11+
except phonenumbers.NumberParseException:
12+
try:
13+
phone_number: PhoneNumber = phonenumbers.parse(raw_phone_number, region="RO")
14+
except phonenumbers.NumberParseException:
15+
return {
16+
"status": "error",
17+
"result": _("Invalid phone number"),
18+
}
19+
20+
if not phonenumbers.is_valid_number(phone_number):
21+
return {
22+
"status": "error",
23+
"result": _("Invalid phone number"),
24+
}
25+
26+
# Format the phone number in E.164 format (e.g. "+40 (72) 12-34-567" -> "+40721234567")
27+
normalized_phone = phonenumbers.format_number(phone_number, phonenumbers.PhoneNumberFormat.E164)
28+
return {
29+
"status": "success",
30+
"result": normalized_phone,
31+
}

backend/donations/forms/ngo_account.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from localflavor.generic.forms import IBANFormField
55
from localflavor.ro.forms import ROCIFField
66

7+
from donations.common.validation.phone_number import validate_phone_number
8+
79

810
class NgoPresentationForm(forms.Form):
911
is_accepting_forms = forms.BooleanField(label=_("Is accepting forms"), required=False)
@@ -60,6 +62,16 @@ def clean_logo(self):
6062

6163
return logo
6264

65+
def clean_contact_phone(self):
66+
raw_phone_number = self.cleaned_data["contact_phone"]
67+
68+
phone_number_validation = validate_phone_number(raw_phone_number)
69+
70+
if phone_number_validation["status"] == "error":
71+
raise forms.ValidationError(phone_number_validation["result"])
72+
73+
return phone_number_validation["result"]
74+
6375

6476
class NgoFormForm(forms.Form):
6577
slug = forms.SlugField(label=_("Slug"), max_length=50, required=True)

backend/donations/forms/redirection.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
from django_recaptcha.widgets import ReCaptchaV2Invisible
66
from localflavor.ro.forms import ROCNPField
77

8+
from donations.common.validation.phone_number import validate_phone_number
9+
810

911
class DonationForm(forms.Form):
1012
l_name = forms.CharField(max_length=100, label=_("Last name"), required=True, strip=True)
1113
f_name = forms.CharField(max_length=100, label=_("First name"), required=True, strip=True)
1214
initial = forms.CharField(max_length=1, label=_("Initial"), required=True)
1315
cnp = ROCNPField(label="CNP", required=True)
1416

15-
email_address = forms.EmailField(label=_("Email"), required=True)
17+
# limit the email address to 200 characters because that is the limit in ANAF's form
18+
email_address = forms.EmailField(label=_("Email"), max_length=200, required=True)
1619
phone_number = forms.CharField(max_length=20, label=_("Phone"), required=False, strip=True)
1720

1821
street_name = forms.CharField(max_length=100, label=_("Street"), required=True, strip=True)
@@ -39,3 +42,13 @@ def __init__(self, *args, **kwargs):
3942

4043
def clean_agree_contact(self):
4144
return not self.cleaned_data["agree_contact"]
45+
46+
def clean_phone_number(self):
47+
raw_phone_number = self.cleaned_data["phone_number"]
48+
49+
phone_number_validation = validate_phone_number(raw_phone_number)
50+
51+
if phone_number_validation["status"] == "error":
52+
raise forms.ValidationError(phone_number_validation["result"])
53+
54+
return phone_number_validation["result"]

backend/requirements-dev.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ black==24.10.0
1313
# via -r requirements-dev.in
1414
blessed==1.20.0
1515
# via -r /Users/tudoramariei/Work/c4ro/ngohub/redirectioneaza/backend/requirements.txt
16-
boto3==1.36.12
16+
boto3==1.36.13
1717
# via
1818
# -r /Users/tudoramariei/Work/c4ro/ngohub/redirectioneaza/backend/requirements.txt
1919
# django-ses
2020
# pycognito
21-
botocore==1.36.12
21+
botocore==1.36.13
2222
# via
2323
# -r /Users/tudoramariei/Work/c4ro/ngohub/redirectioneaza/backend/requirements.txt
2424
# boto3
@@ -148,6 +148,8 @@ packaging==24.2
148148
# pytest
149149
pathspec==0.12.1
150150
# via black
151+
phonenumbers==8.13.54
152+
# via -r /Users/tudoramariei/Work/c4ro/ngohub/redirectioneaza/backend/requirements.txt
151153
pillow==11.1.0
152154
# via
153155
# -r /Users/tudoramariei/Work/c4ro/ngohub/redirectioneaza/backend/requirements.txt

backend/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ django-storages[s3]~=1.14.4
4444

4545
# locale data
4646
django-localflavor~=4.0.0
47+
phonenumbers~=8.13
4748

4849
# pdf creation
4950
reportlab~=4.2.5

backend/requirements.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ asgiref==3.8.1
1010
# django-allauth
1111
blessed==1.20.0
1212
# via -r requirements.in
13-
boto3==1.36.12
13+
boto3==1.36.13
1414
# via
1515
# django-ses
1616
# django-storages
1717
# pycognito
18-
botocore==1.36.12
18+
botocore==1.36.13
1919
# via
2020
# boto3
2121
# s3transfer
@@ -98,6 +98,8 @@ ngohub==0.0.10
9898
# via -r requirements.in
9999
packaging==24.2
100100
# via gunicorn
101+
phonenumbers==8.13.54
102+
# via -r requirements.in
101103
pillow==11.1.0
102104
# via reportlab
103105
psutil==6.1.1

0 commit comments

Comments
 (0)