Skip to content

Commit

Permalink
additional ngo page modifications
Browse files Browse the repository at this point in the history
  • Loading branch information
tudoramariei committed Jan 14, 2025
1 parent 8909b82 commit ff6db1f
Show file tree
Hide file tree
Showing 24 changed files with 881 additions and 443 deletions.
13 changes: 13 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Things to do to make the front-end work properly


## Dockerfile

### Files:

- `docker/dockerfiles/Dockerfile.dev`

### Issues:

- [ ] NPM doesn't install packages properly and uses local packages instead


## Donation form

### Files:
Expand Down Expand Up @@ -41,3 +52,5 @@
- [ ] The disabled form fields need to be styled properly
- [ ] Connect the form to the BE properly
- [ ] Check the data for changes when switching tabs
- [ ] The "save" & "get from NGO Hub" buttons should be styled the same
- [ ] Max characters validation for description & other fields
Empty file.
106 changes: 106 additions & 0 deletions backend/donations/common/validation/registration_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import re
from typing import Dict, List, Optional, Tuple

from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

ALLOWED_CHARACTERS_REGEX = r"[^RO0-9]"
REGISTRATION_NUMBER_REGEX = r"^([A-Z]{2}|)\d{2,10}$"
REGISTRATION_NUMBER_REGEX_SANS_VAT = r"^\d{2,10}$"
REGISTRATION_NUMBER_REGEX_WITH_VAT = r"^[A-Z]{2}\d{2,10}$"


def extract_vat_id(registration_number: str) -> Dict[str, str]:
"""
Extract the VAT ID and the registration number from a valid registration number.
:param registration_number:
A registration number that may or may not contain a VAT ID.
The registration number must have a valid format.
:return: A dictionary containing the VAT ID and the registration number.
{
"vat_id": "RO",
"registration_number": "1234567890",
}
"""

result = {
"vat_id": "",
"registration_number": registration_number,
}

if re.match(REGISTRATION_NUMBER_REGEX_SANS_VAT, registration_number):
return result

result["vat_id"] = registration_number[:2]
result["registration_number"] = registration_number[2:]

return result


def clean_registration_number(registration_number: str) -> Optional[str]:
"""
Clean up a registration number by uppercasing the string, then removing any whitespace or forbidden characters.
:param registration_number: The registration number to clean.
:return: The cleaned registration number.
"""
if re.match(REGISTRATION_NUMBER_REGEX, registration_number):
return registration_number

# uppercase the string and strip of any whitespace
registration_number = registration_number.upper().strip()

# remove all the whitespace
registration_number = re.sub(r"\s+", "", registration_number)

# remove any forbidden characters
registration_number = re.sub(ALLOWED_CHARACTERS_REGEX, "", registration_number)

return registration_number


def ngo_id_number_validator(value):
"""
Validate a registration number for an NGO.
:param value: The registration number to validate.
:return: None
"""

reg_num: str = "".join([char for char in value.upper() if char.isalnum()])

if reg_num == len(reg_num) * "0":
raise ValidationError(_("The ID number cannot be all zeros"))

if not re.match(REGISTRATION_NUMBER_REGEX, reg_num):
raise ValidationError(_("The ID number format is not valid"))

if re.match(REGISTRATION_NUMBER_REGEX_WITH_VAT, reg_num):
reg_num = value[2:]

if not reg_num.isdigit():
raise ValidationError(_("The ID number must contain only digits"))

if 2 > len(reg_num) or len(reg_num) > 10:
raise ValidationError(_("The ID number must be between 2 and 10 digits long"))

if not settings.ENABLE_FULL_CUI_VALIDATION:
return

control_key: str = "753217532"

reversed_key: List[int] = [int(digit) for digit in control_key[::-1]]
reversed_cif: List[int] = [int(digit) for digit in reg_num[::-1]]

cif_control_digit: int = reversed_cif.pop(0)

cif_key_pairs: Tuple[int, ...] = tuple(
cif_digit * key_digit for cif_digit, key_digit in zip(reversed_cif, reversed_key)
)
control_result: int = sum(cif_key_pairs) * 10 % 11

if control_result == cif_control_digit:
return
elif control_result == 10 and cif_control_digit == 0:
return

raise ValidationError(_("The ID number is not valid"))
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
from django.core.exceptions import ValidationError
from django.core.management import BaseCommand

from donations.models.ngos import (
Ngo,
from donations.common.validation.registration_number import (
REGISTRATION_NUMBER_REGEX,
REGISTRATION_NUMBER_REGEX_SANS_VAT,
clean_registration_number,
extract_vat_id,
ngo_id_number_validator,
)
from donations.models.ngos import (
Ngo,
)


class Command(BaseCommand):
Expand Down Expand Up @@ -55,7 +58,7 @@ def clean_ngo(self, ngo_id: int) -> Dict[str, str]:

def clean_ngo_registration_number(self, ngo: Ngo) -> Dict[str, str]:
initial_registration_number = ngo.registration_number
cleaned_registration_number = self._clean_up_registration_number(initial_registration_number)
cleaned_registration_number = clean_registration_number(initial_registration_number)

if not re.match(REGISTRATION_NUMBER_REGEX, cleaned_registration_number):
self.stdout.write(
Expand Down Expand Up @@ -90,7 +93,7 @@ def clean_ngo_registration_number(self, ngo: Ngo) -> Dict[str, str]:
),
}

vat_information = self._extract_vat_id(cleaned_registration_number)
vat_information = extract_vat_id(cleaned_registration_number)

ngo.vat_id = vat_information["vat_id"]
ngo.registration_number = vat_information["registration_number"]
Expand Down Expand Up @@ -120,21 +123,6 @@ def _clean_up_registration_number(reg_num: str) -> Optional[str]:

return reg_num

@staticmethod
def _extract_vat_id(reg_num: str) -> Dict[str, str]:
result = {
"vat_id": "",
"registration_number": reg_num,
}

if re.match(REGISTRATION_NUMBER_REGEX_SANS_VAT, reg_num):
return result

result["vat_id"] = reg_num[:2]
result["registration_number"] = reg_num[2:]

return result

@staticmethod
def _validate_registration_number(reg_num: str) -> bool:
try:
Expand Down
3 changes: 2 additions & 1 deletion backend/donations/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import django.db.models.functions.text
from django.db import migrations, models

import donations.common.validation.registration_number
import donations.models.donors
import donations.models.ngos

Expand Down Expand Up @@ -138,7 +139,7 @@ class Migration(migrations.Migration):
db_index=True,
max_length=100,
unique=True,
validators=[donations.models.ngos.ngo_id_number_validator],
validators=[donations.common.validation.registration_number.ngo_id_number_validator],
verbose_name="registration number",
),
),
Expand Down
57 changes: 9 additions & 48 deletions backend/donations/models/ngos.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
import re
from functools import partial
from typing import List, Tuple

from django.conf import settings
from django.core.cache import cache
Expand All @@ -13,16 +12,13 @@
from django.utils.translation import gettext_lazy as _

from donations.common.models_hashing import hash_id_secret
from donations.common.validation.registration_number import REGISTRATION_NUMBER_REGEX_WITH_VAT, ngo_id_number_validator

ALL_NGOS_CACHE_KEY = "ALL_NGOS"
ALL_NGO_IDS_CACHE_KEY = "ALL_NGO_IDS"
FRONTPAGE_NGOS_KEY = "FRONTPAGE_NGOS"
FRONTPAGE_STATS_KEY = "FRONTPAGE_NGOS_STATS"

REGISTRATION_NUMBER_REGEX = r"^([A-Z]{2}|)\d{2,10}$"
REGISTRATION_NUMBER_REGEX_SANS_VAT = r"^\d{2,10}$"
REGISTRATION_NUMBER_REGEX_WITH_VAT = r"^[A-Z]{2}\d{2,10}$"

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -58,47 +54,6 @@ def ngo_slug_validator(value):
raise ValidationError(error_message)


def ngo_id_number_validator(value):
reg_num: str = "".join([char for char in value.upper() if char.isalnum()])

if reg_num == len(reg_num) * "0":
raise ValidationError(_("The ID number cannot be all zeros"))

if not re.match(REGISTRATION_NUMBER_REGEX, reg_num):
raise ValidationError(_("The ID number format is not valid"))

if re.match(REGISTRATION_NUMBER_REGEX_WITH_VAT, reg_num):
reg_num = value[2:]

if not reg_num.isdigit():
raise ValidationError(_("The ID number must contain only digits"))

if 2 > len(reg_num) or len(reg_num) > 10:
raise ValidationError(_("The ID number must be between 2 and 10 digits long"))

if not settings.ENABLE_FULL_CUI_VALIDATION:
return

control_key: str = "753217532"

reversed_key: List[int] = [int(digit) for digit in control_key[::-1]]
reversed_cif: List[int] = [int(digit) for digit in reg_num[::-1]]

cif_control_digit: int = reversed_cif.pop(0)

cif_key_pairs: Tuple[int, ...] = tuple(
cif_digit * key_digit for cif_digit, key_digit in zip(reversed_cif, reversed_key)
)
control_result: int = sum(cif_key_pairs) * 10 % 11

if control_result == cif_control_digit:
return
elif control_result == 10 and cif_control_digit == 0:
return

raise ValidationError(_("The ID number is not valid"))


class NgoActiveManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
Expand Down Expand Up @@ -194,10 +149,12 @@ class Ngo(models.Model):
db_index=True,
)

# originally: tel
phone = models.CharField(verbose_name=_("telephone"), blank=True, null=False, default="", max_length=30)

email = models.EmailField(verbose_name=_("email"), blank=True, null=False, default="", db_index=True)

display_email = models.BooleanField(verbose_name=_("display email"), db_index=True, default=False)
display_phone = models.BooleanField(verbose_name=_("display phone"), db_index=True, default=False)

website = models.URLField(verbose_name=_("website"), blank=True, null=False, default="")

# originally: verified
Expand Down Expand Up @@ -283,6 +240,10 @@ def deactivate(self, commit: bool = True):
if commit:
self.save()

@property
def full_registration_number(self):
return f"{self.vat_id}{self.registration_number}" if self.vat_id else self.registration_number

@staticmethod
def delete_prefilled_form(ngo_id):
try:
Expand Down
3 changes: 2 additions & 1 deletion backend/donations/views/donations_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
from django.utils.translation import gettext_lazy as _
from localflavor.ro.ro_counties import COUNTIES_CHOICES

from donations.common.validation.registration_number import REGISTRATION_NUMBER_REGEX_SANS_VAT
from donations.models.donors import Donor
from donations.models.jobs import Job, JobDownloadError, JobStatusChoices
from donations.models.ngos import REGISTRATION_NUMBER_REGEX_SANS_VAT, Ngo
from donations.models.ngos import Ngo
from redirectioneaza.common.messaging import send_email

logger = logging.getLogger(__name__)
Expand Down
Loading

0 comments on commit ff6db1f

Please sign in to comment.