Skip to content

Commit 43db85b

Browse files
committed
[#5006] Updated AddressValueSerializer to adapt manual filling of streetname and city
Updated street name and city serializer fields in order to be able to require these fields in case the component is required.
1 parent 46f3859 commit 43db85b

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

src/openforms/contrib/brk/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ class AddressValue(TypedDict):
99
city: NotRequired[str]
1010
streetName: NotRequired[str]
1111
secretStreetCity: NotRequired[str]
12+
autoPopulated: NotRequired[bool]

src/openforms/formio/components/custom.py

+35
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,39 @@ class AddressValueSerializer(serializers.Serializer):
425425
required=False,
426426
allow_blank=True,
427427
)
428+
autoPopulated = serializers.BooleanField(
429+
label=_("city and street name auto populated"),
430+
help_text=_("Whether city and street name have been retrieved from the API"),
431+
default=False,
432+
)
428433

429434
def __init__(self, **kwargs):
430435
self.derive_address = kwargs.pop("derive_address", None)
431436
self.component = kwargs.pop("component", None)
432437
super().__init__(**kwargs)
433438

439+
def get_fields(self):
440+
fields = super().get_fields()
441+
442+
# Some fields have to be treated as required or not dynamically and based on
443+
# specific situations.
444+
if (
445+
self.component
446+
and self.derive_address
447+
and (validate := self.component.get("validate"))
448+
):
449+
if validate["required"] is True:
450+
fields["city"].required = True
451+
fields["city"].allow_blank = False
452+
fields["streetName"].required = True
453+
fields["streetName"].allow_blank = False
454+
elif validate["required"] is False:
455+
fields["postcode"].required = False
456+
fields["postcode"].allow_blank = True
457+
fields["houseNumber"].required = False
458+
fields["houseNumber"].allow_blank = True
459+
return fields
460+
434461
def validate_city(self, value: str) -> str:
435462
if city_regex := glom(
436463
self.component, "openForms.components.city.validate.pattern", default=""
@@ -457,10 +484,16 @@ def validate_postcode(self, value: str) -> str:
457484
def validate(self, attrs):
458485
attrs = super().validate(attrs)
459486

487+
auto_populated = attrs.get("autoPopulated", False)
460488
city = attrs.get("city", "")
461489
street_name = attrs.get("streetName", "")
462490

463491
if self.derive_address:
492+
# when the user fills in manually the city and the street name we do not
493+
# need to check the secret city - street name combination
494+
if not auto_populated:
495+
return attrs
496+
464497
existing_hmac = attrs.get("secretStreetCity", "")
465498
postcode = attrs.get("postcode", "")
466499
number = attrs.get("houseNumber", "")
@@ -501,6 +534,8 @@ def build_serializer_field(
501534
validators.append(PluginValidator(plugin_ids))
502535

503536
extra["validators"] = validators
537+
# if the component is not required, none of the fields should be required
538+
# bakend bug: saving addressnl required -> should be able to save it with partial data
504539

505540
return AddressValueSerializer(
506541
derive_address=component["deriveAddress"],

src/openforms/formio/formatters/custom.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class AddressValue(TypedDict):
4242
city: NotRequired[str]
4343
streetName: NotRequired[str]
4444
secretStreetCity: NotRequired[str]
45+
autoPopulated: NotRequired[bool]
4546

4647

4748
class AddressNLFormatter(FormatterBase):

src/openforms/formio/tests/validation/test_addressnl.py

+80-2
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,13 @@ def test_addressNL_field_regex_pattern_success(self):
106106

107107
self.assertTrue(is_valid)
108108

109-
def test_missing_keys(self):
109+
def test_missing_keys_when_component_optional(self):
110110
component: AddressNLComponent = {
111111
"key": "addressNl",
112112
"type": "addressNL",
113113
"label": "AddressNL missing keys",
114114
"deriveAddress": False,
115+
"validate": {"required": False},
115116
}
116117

117118
invalid_values = {
@@ -129,6 +130,53 @@ def test_missing_keys(self):
129130
self.assertEqual(postcode_error.code, "required")
130131
self.assertEqual(house_number_error.code, "required")
131132

133+
def test_missing_keys_when_autofill_and_component_optional(self):
134+
component: AddressNLComponent = {
135+
"key": "addressNl",
136+
"type": "addressNL",
137+
"label": "AddressNL missing keys",
138+
"deriveAddress": True,
139+
"validate": {"required": False},
140+
}
141+
142+
invalid_values = {
143+
"addressNl": {
144+
"houseLetter": "A",
145+
}
146+
}
147+
148+
is_valid, _ = validate_formio_data(component, invalid_values)
149+
150+
self.assertTrue(is_valid)
151+
152+
def test_missing_keys_when_component_required(self):
153+
component: AddressNLComponent = {
154+
"key": "addressNl",
155+
"type": "addressNL",
156+
"label": "AddressNL missing keys",
157+
"deriveAddress": True,
158+
"validate": {"required": True},
159+
}
160+
161+
invalid_values = {
162+
"addressNl": {
163+
"houseLetter": "A",
164+
}
165+
}
166+
167+
is_valid, errors = validate_formio_data(component, invalid_values)
168+
169+
postcode_error = extract_error(errors["addressNl"], "postcode")
170+
house_number_error = extract_error(errors["addressNl"], "houseNumber")
171+
street_name_error = extract_error(errors["addressNl"], "streetName")
172+
city_error = extract_error(errors["addressNl"], "city")
173+
174+
self.assertFalse(is_valid)
175+
self.assertEqual(postcode_error.code, "required")
176+
self.assertEqual(house_number_error.code, "required")
177+
self.assertEqual(street_name_error.code, "required")
178+
self.assertEqual(city_error.code, "required")
179+
132180
def test_plugin_validator(self):
133181
with replace_validators_registry() as register:
134182
register("postcode_validator")(PostcodeValidator)
@@ -150,6 +198,8 @@ def test_plugin_validator(self):
150198
"houseNumber": "3",
151199
"houseLetter": "A",
152200
"houseNumberAddition": "",
201+
"streetName": "Keizersgracht",
202+
"city": "Amsterdam",
153203
}
154204
},
155205
)
@@ -176,7 +226,7 @@ def test_addressNL_field_secret_success(self):
176226
"key": "addressNl",
177227
"type": "addressNL",
178228
"label": "AddressNL secret success",
179-
"deriveAddress": False,
229+
"deriveAddress": True,
180230
}
181231

182232
message = "1015CJ/117/Amsterdam/Keizersgracht"
@@ -190,6 +240,7 @@ def test_addressNL_field_secret_success(self):
190240
"city": "Amsterdam",
191241
"streetName": "Keizersgracht",
192242
"secretStreetCity": secret,
243+
"autoPopulated": True,
193244
}
194245
}
195246

@@ -214,6 +265,7 @@ def test_addressNL_field_secret_failure(self):
214265
"city": "Amsterdam",
215266
"streetName": "Keizersgracht",
216267
"secretStreetCity": "invalid secret",
268+
"autoPopulated": True,
217269
}
218270
}
219271

@@ -224,6 +276,32 @@ def test_addressNL_field_secret_failure(self):
224276
self.assertFalse(is_valid)
225277
self.assertEqual(secret_error.code, "invalid")
226278

279+
def test_addressNL_field_secret_not_used_when_manual_address(self):
280+
component: AddressNLComponent = {
281+
"key": "addressNl",
282+
"type": "addressNL",
283+
"label": "AddressNL secret failure",
284+
"deriveAddress": True,
285+
"validate": {"required": False},
286+
}
287+
288+
data = {
289+
"addressNl": {
290+
"postcode": "1015CJ",
291+
"houseNumber": "117",
292+
"houseLetter": "",
293+
"houseNumberAddition": "",
294+
"city": "Amsterdam",
295+
"streetName": "Keizersgracht",
296+
"secretStreetCity": "a secret",
297+
"autoPopulated": False,
298+
}
299+
}
300+
301+
is_valid, _ = validate_formio_data(component, data)
302+
303+
self.assertTrue(is_valid)
304+
227305
def test_addressNL_field_missing_city(self):
228306
component: AddressNLComponent = {
229307
"key": "addressNl",

0 commit comments

Comments
 (0)