Skip to content

Commit

Permalink
Valid address items extension. (#15877)
Browse files Browse the repository at this point in the history
* Add custom address names mapping to extend valid address inputs.

* Add tests.

* Renaming.

* Apply lower() and strip() when matching values with map.

* Adjust map during its initialization.

* Fix failing test.
  • Loading branch information
zedzior committed May 8, 2024
1 parent c09d8cc commit eabdab1
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -24,6 +24,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Calculate order promotions in draft orders - #15459 by @zedzior
- Prevent name overwriting of Product Variants when Updating Product Types - #15670 by @teddyondieki
- Added support for the `BrokerProperties` custom header to webhooks to support Azure Service Bus - #15899 by @patrys
- Extend valid address values - #15877 by @zedzior

# 3.19.0

Expand Down
14 changes: 14 additions & 0 deletions saleor/account/i18n.py
Expand Up @@ -7,6 +7,7 @@
from django.forms.models import ModelFormMetaclass
from django_countries import countries

from .i18n_valid_address_extension import VALID_ADDRESS_EXTENSION_MAP
from .models import Address
from .validators import validate_possible_number
from .widgets import DatalistTextWidget
Expand Down Expand Up @@ -179,6 +180,7 @@ def validate_address(self, data):
if street_address_1 or street_address_2:
data["street_address"] = f"{street_address_1}\n{street_address_2}"

self.substitute_invalid_values(data)
normalized_data = i18naddress.normalize_address(data)
if getattr(self, "enable_normalization", True):
data = normalized_data
Expand All @@ -191,6 +193,18 @@ def clean(self):
data = super().clean()
return self.validate_address(data)

@staticmethod
def substitute_invalid_values(data):
"""Map invalid address names to the values accepted by i18naddress package."""
country_code = data["country_code"]
if not country_code:
return
if custom_names := VALID_ADDRESS_EXTENSION_MAP.get(country_code):
for field_name, mapping in custom_names.items():
actual_value = data.get(field_name, "").strip().lower()
if actual_value in mapping:
data[field_name] = mapping[actual_value]


def get_address_form_class(country_code):
return COUNTRY_FORMS.get(country_code, AddressForm)
Expand Down
45 changes: 45 additions & 0 deletions saleor/account/i18n_valid_address_extension.py
@@ -0,0 +1,45 @@
from enum import Enum

from ..graphql.account.enums import CountryCodeEnum


class AddressFieldsToSubstitute(Enum):
CITY_AREA = "city_area"
COUNTRY_AREA = "country_area"


IE_COUNTRY_AREA = {
"Carlow": "Co. Carlow",
"Cavan": "Co. Cavan",
"Clare": "Co. Clare",
"Cork": "Co. Cork",
"Donegal": "Co. Donegal",
"Dublin": "Co. Dublin",
"Galway": "Co. Galway",
"Kerry": "Co. Kerry",
"Kildare": "Co. Kildare",
"Kilkenny": "Co. Kilkenny",
"Laois": "Co. Laois",
"Leitrim": "Co. Leitrim",
"Limerick": "Co. Limerick",
"Longford": "Co. Longford",
"Louth": "Co. Louth",
"Mayo": "Co. Mayo",
"Meath": "Co. Meath",
"Monaghan": "Co. Monaghan",
"Offaly": "Co. Offaly",
"Roscommon": "Co. Roscommon",
"Sligo": "Co. Sligo",
"Tipperary": "Co. Tipperary",
"Waterford": "Co. Waterford",
"Westmeath": "Co. Westmeath",
"Wexford": "Co. Wexford",
"Wicklow": "Co. Wicklow",
}
IE_COUNTRY_AREA = {k.strip().lower(): v for k, v in IE_COUNTRY_AREA.items()}

VALID_ADDRESS_EXTENSION_MAP: dict[str, dict[str, dict[str, str]]] = {
CountryCodeEnum.IE.value: {
AddressFieldsToSubstitute.COUNTRY_AREA.value: IE_COUNTRY_AREA
}
}
42 changes: 42 additions & 0 deletions saleor/account/tests/test_account.py
@@ -1,3 +1,4 @@
from unittest.mock import patch
from urllib.parse import urlencode

import i18naddress
Expand All @@ -8,6 +9,7 @@

from ...order.models import Order
from .. import forms, i18n
from ..i18n_valid_address_extension import VALID_ADDRESS_EXTENSION_MAP
from ..models import User
from ..validators import validate_possible_number

Expand Down Expand Up @@ -312,3 +314,43 @@ def test_customers_show_staff_with_order(admin_user, channel_USD):
channel=channel_USD,
)
assert User.objects.customers().count() == 1


@pytest.mark.parametrize(
("country_area_input", "country_area_output", "is_valid"),
[
("Dublin", "Co. Dublin", True),
("Co. Dublin", "Co. Dublin", True),
("Dummy Area", None, False),
("dublin", "Co. Dublin", True),
(" dublin ", "Co. Dublin", True),
("", "", True),
(None, "", True),
],
)
@patch.dict(
VALID_ADDRESS_EXTENSION_MAP,
{"IE": {"country_area": {"dublin": "Co. Dublin"}, "city_area": {"dummy": "dummy"}}},
)
def test_substitute_invalid_values(country_area_input, country_area_output, is_valid):
# given
data = {
"first_name": "John",
"last_name": "Doe",
"country": "IE",
"country_area": country_area_input,
"city": "Dublin",
"street_address_1": "1 Anglesea St",
"postal_code": "D02 FK84",
}

# when
form = forms.get_address_form(data, country_code="PL")
errors = form.errors

# then
assert form.cleaned_data.get("country_area") == country_area_output
if not is_valid:
assert "country_area" in errors
else:
assert not errors
48 changes: 48 additions & 0 deletions saleor/graphql/warehouse/tests/mutations/test_warehouse_create.py
Expand Up @@ -5,6 +5,7 @@
import pytest
from django.utils.functional import SimpleLazyObject

from .....account.i18n_valid_address_extension import VALID_ADDRESS_EXTENSION_MAP
from .....account.models import Address
from .....core.utils.json_serializer import CustomJsonEncoder
from .....warehouse.error_codes import WarehouseErrorCode
Expand All @@ -23,6 +24,7 @@
externalReference
address {
id
countryArea
metadata {
key
value
Expand Down Expand Up @@ -341,3 +343,49 @@ def test_create_warehouse_with_non_unique_external_reference(
assert error["field"] == "externalReference"
assert error["code"] == WarehouseErrorCode.UNIQUE.name
assert error["message"] == "Warehouse with this External reference already exists."


def test_create_warehouse_with_address_item_from_valid_address_extension_map(
staff_api_client, permission_manage_products, shipping_zone
):
# given
country_area = "dublin"
cleaned_country_area = "Co. Dublin"
address = {
"country": "IE",
"countryArea": country_area,
"streetAddress1": "1 Anglesea St",
"postalCode": "D02 FK84",
"city": "Dublin",
}

ireland_country_areas_extension = VALID_ADDRESS_EXTENSION_MAP["IE"]["country_area"]
assert country_area in ireland_country_areas_extension
assert ireland_country_areas_extension[country_area] == cleaned_country_area

variables = {
"input": {
"name": "Test warehouse",
"slug": "test-warhouse",
"email": "test-admin@example.com",
"address": address,
}
}

# when
response = staff_api_client.post_graphql(
MUTATION_CREATE_WAREHOUSE,
variables=variables,
permissions=[permission_manage_products],
)

# then
content = get_graphql_content(response)
assert not content["data"]["createWarehouse"]["errors"]
assert Warehouse.objects.count() == 1

address = content["data"]["createWarehouse"]["warehouse"]["address"]
assert address["countryArea"] == cleaned_country_area

address_db = Warehouse.objects.first().address
assert address_db.country_area == cleaned_country_area

0 comments on commit eabdab1

Please sign in to comment.