diff --git a/requirements/base.in b/requirements/base.in index 34a2d811..bd639ef0 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -2,3 +2,5 @@ open-api-framework django-setup-configuration mozilla-django-oidc-db[setup-configuration] + +django-localflavor \ No newline at end of file diff --git a/requirements/base.txt b/requirements/base.txt index a4ed7b8a..202c6a20 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -81,6 +81,7 @@ django==4.2.17 # django-filter # django-formtools # django-jsonform + # django-localflavor # django-log-outgoing-requests # django-markup # django-otp @@ -126,6 +127,8 @@ django-jsonform==2.22.0 # via # mozilla-django-oidc-db # open-api-framework +django-localflavor==4.0 + # via -r requirements/base.in django-log-outgoing-requests==0.6.1 # via open-api-framework django-markup==1.8.1 @@ -279,6 +282,8 @@ python-dotenv==1.0.1 # via # open-api-framework # pydantic-settings +python-stdnum==1.20 + # via django-localflavor pytz==2024.1 # via flower pyyaml==6.0.1 diff --git a/requirements/ci.txt b/requirements/ci.txt index 1f533b77..acfac866 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -162,6 +162,7 @@ django==4.2.17 # django-filter # django-formtools # django-jsonform + # django-localflavor # django-log-outgoing-requests # django-markup # django-otp @@ -229,6 +230,8 @@ django-jsonform==2.22.0 # -r requirements/base.txt # mozilla-django-oidc-db # open-api-framework +django-localflavor==4.0 + # via -r requirements/base.txt django-log-outgoing-requests==0.6.1 # via # -c requirements/base.txt @@ -619,6 +622,10 @@ python-dotenv==1.0.1 # -r requirements/base.txt # open-api-framework # pydantic-settings +python-stdnum==1.20 + # via + # -r requirements/base.txt + # django-localflavor pytz==2024.1 # via # -c requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index f9135f69..ddfa3163 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -197,6 +197,7 @@ django==4.2.17 # django-filter # django-formtools # django-jsonform + # django-localflavor # django-log-outgoing-requests # django-markup # django-otp @@ -268,6 +269,8 @@ django-jsonform==2.22.0 # -r requirements/ci.txt # mozilla-django-oidc-db # open-api-framework +django-localflavor==4.0 + # via -r requirements/base.txt django-log-outgoing-requests==0.6.1 # via # -c requirements/ci.txt @@ -731,6 +734,10 @@ python-dotenv==1.0.1 # -r requirements/ci.txt # open-api-framework # pydantic-settings +python-stdnum==1.20 + # via + # -r requirements/base.txt + # django-localflavor pytz==2024.1 # via # -c requirements/ci.txt diff --git a/src/openklant/components/contactgegevens/api/tests/test_apis.py b/src/openklant/components/contactgegevens/api/tests/test_apis.py index 4e2ebdec..37272b19 100644 --- a/src/openklant/components/contactgegevens/api/tests/test_apis.py +++ b/src/openklant/components/contactgegevens/api/tests/test_apis.py @@ -21,8 +21,8 @@ def test_persoon_detail(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:persoon-detail", @@ -34,7 +34,7 @@ def test_persoon_detail(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", } expected_data = { "uuid": str(persoon.uuid), @@ -46,7 +46,7 @@ def test_persoon_detail(self): "voorvoegsel": "", "voornamen": "John", "adres": expected_adres, - "land": "5001", + "land": "CA", } response = self.client.get(detail_url) @@ -68,9 +68,9 @@ def test_create_persoon(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, - "land": "5001", + "land": "CA", } response = self.client.post(list_url, data) @@ -92,10 +92,10 @@ def test_create_persoon(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") def test_update_persoon(self): persoon = PersoonFactory.create( @@ -109,8 +109,8 @@ def test_update_persoon(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:persoon-detail", @@ -136,10 +136,10 @@ def test_update_persoon(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") data = { "geboortedatum": "1972-05-06", @@ -153,12 +153,13 @@ def test_update_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "6713", + "land": "FR", }, - "land": "6713", + "land": "FR", } response = self.client.put(detail_url, data) data = response.json() + self.assertEqual(data["geboortedatum"], "1972-05-06") self.assertEqual(data["overlijdensdatum"], "2023-11-22") self.assertEqual(data["geslachtsnaam"], "changed") @@ -172,10 +173,10 @@ def test_update_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "6713", + "land": "FR", }, ) - self.assertEqual(data["land"], "6713") + self.assertEqual(data["land"], "FR") def test_update_partial_persoon(self): persoon = PersoonFactory.create( @@ -189,8 +190,8 @@ def test_update_partial_persoon(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:persoon-detail", @@ -216,10 +217,10 @@ def test_update_partial_persoon(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") data = { "overlijdensdatum": "2023-11-22", @@ -240,10 +241,10 @@ def test_update_partial_persoon(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") def test_list_pagination_pagesize_param(self): list_url = reverse("contactgegevens:persoon-list") @@ -269,8 +270,8 @@ def test_organisatie_detail(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:organisatie-detail", @@ -282,7 +283,7 @@ def test_organisatie_detail(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", } expected_data = { "uuid": str(organisatie.uuid), @@ -291,7 +292,7 @@ def test_organisatie_detail(self): "opheffingsdatum": "2020-09-05", "handelsnaam": "Devin Townsend", "adres": expected_adres, - "land": "5001", + "land": "CA", } response = self.client.get(detail_url) @@ -311,9 +312,9 @@ def test_create_organisatie(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, - "land": "5001", + "land": "CA", } response = self.client.post(list_url, data) @@ -332,10 +333,10 @@ def test_create_organisatie(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") def test_update_organisatie(self): organisatie = OrganisatieFactory.create( @@ -346,8 +347,8 @@ def test_update_organisatie(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:organisatie-detail", @@ -370,10 +371,10 @@ def test_update_organisatie(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") data = { "handelsnaam": "changed", @@ -384,9 +385,9 @@ def test_update_organisatie(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "6713", + "land": "FR", }, - "land": "6713", + "land": "FR", } response = self.client.put(detail_url, data) data = response.json() @@ -400,10 +401,10 @@ def test_update_organisatie(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "6713", + "land": "FR", }, ) - self.assertEqual(data["land"], "6713") + self.assertEqual(data["land"], "FR") def test_update_partial_organisatie(self): organisatie = OrganisatieFactory.create( @@ -414,8 +415,8 @@ def test_update_partial_organisatie(self): adres_adresregel1="adresregel1", adres_adresregel2="adresregel2", adres_adresregel3="adresregel3", - adres_land="5001", - land="5001", + adres_land="CA", + land="CA", ) detail_url = reverse( "contactgegevens:organisatie-detail", @@ -438,10 +439,10 @@ def test_update_partial_organisatie(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") data = { "opheffingsdatum": "2023-11-22", @@ -460,10 +461,10 @@ def test_update_partial_organisatie(self): "adresregel1": "adresregel1", "adresregel2": "adresregel2", "adresregel3": "adresregel3", - "land": "5001", + "land": "CA", }, ) - self.assertEqual(data["land"], "5001") + self.assertEqual(data["land"], "CA") def test_list_pagination_pagesize_param(self): list_url = reverse("contactgegevens:organisatie-list") diff --git a/src/openklant/components/contactgegevens/migrations/0004_alter_organisatie_adres_land_alter_organisatie_land_and_more.py b/src/openklant/components/contactgegevens/migrations/0004_alter_organisatie_adres_land_alter_organisatie_land_and_more.py new file mode 100644 index 00000000..39afb751 --- /dev/null +++ b/src/openklant/components/contactgegevens/migrations/0004_alter_organisatie_adres_land_alter_organisatie_land_and_more.py @@ -0,0 +1,134 @@ +# Generated by Django 4.2.15 on 2025-01-09 15:19 +import logging +import django.core.validators +from django.db import migrations, models +import openklant.utils.validators +from django.db import IntegrityError + +from openklant.utils.constants import COUNTRIES_DICT + +logger = logging.getLogger(__name__) + +FIELDS = ["adres_land", "land"] + + +def _check_records(records): + total_failed_records = 0 + for record in records: + record_failed = False + for field_name in FIELDS: + field_value = getattr(record, field_name, None) + iso_code = COUNTRIES_DICT.get(field_value, "") + if field_value and not iso_code: + record_failed = True + logger.warning( + "%s(pk=%s, uuid=%s) Field: '%s'. No match found for nl_code (not found in Tabel 34 Landentabel): '%s'", + type(record).__qualname__, + record.pk, + record.uuid, + field_name, + field_value, + ) + + if record_failed: + total_failed_records += 1 + return total_failed_records + + +def _update_records(records): + for record in records: + updated = False + for field_name in FIELDS: + if field_value := getattr(record, field_name, None): + iso_code = COUNTRIES_DICT.get(field_value, "") + setattr(record, field_name, iso_code) + updated = True + + if updated: + record.save() + + +def _check_and_update_records(apps, schema_editor): + Organisatie = apps.get_model("contactgegevens", "Organisatie") + Persoon = apps.get_model("contactgegevens", "Persoon") + + for model in [Organisatie, Persoon]: + records = model.objects.all() + if total_failed_records := _check_records(records): + raise IntegrityError( + "The migration cannot proceed due to %s records that don't comply with the %s model's requirements. " + "Possible data inconsistency or mapping error." + % (total_failed_records, model.__qualname__) + ) + else: + _update_records(records) + + +class Migration(migrations.Migration): + + dependencies = [ + ("contactgegevens", "0003_alter_persoon_overlijdensdatum"), + ] + + operations = [ + migrations.RunPython( + code=_check_and_update_records, + reverse_code=migrations.RunPython.noop, + ), + migrations.AlterField( + model_name="organisatie", + name="adres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="organisatie", + name="land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="persoon", + name="adres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="persoon", + name="land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + ] diff --git a/src/openklant/components/contactgegevens/mixins.py b/src/openklant/components/contactgegevens/mixins.py index c9a3a6ce..3c444930 100644 --- a/src/openklant/components/contactgegevens/mixins.py +++ b/src/openklant/components/contactgegevens/mixins.py @@ -1,9 +1,11 @@ -from django.core.validators import MinLengthValidator, validate_integer +from django.core.validators import MinLengthValidator from django.db import models from django.utils.translation import gettext_lazy as _ from vng_api_common.descriptors import GegevensGroepType +from openklant.utils.validators import validate_country + class AdresMixin(models.Model): adres_nummeraanduiding_id = models.CharField( @@ -41,14 +43,13 @@ class AdresMixin(models.Model): adres_land = models.CharField( _("land"), help_text=_( - "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " - "aangeeft alwaar de ingeschrevene verblijft." + "ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft." ), validators=[ - MinLengthValidator(limit_value=4), - validate_integer, + MinLengthValidator(limit_value=2), + validate_country, ], - max_length=4, + max_length=2, blank=True, ) diff --git a/src/openklant/components/contactgegevens/models.py b/src/openklant/components/contactgegevens/models.py index 33b98b63..c6321f84 100644 --- a/src/openklant/components/contactgegevens/models.py +++ b/src/openklant/components/contactgegevens/models.py @@ -1,11 +1,12 @@ import uuid -from django.core.validators import MinLengthValidator, validate_integer +from django.core.validators import MinLengthValidator from django.db import models from django.utils.translation import gettext_lazy as _ from openklant.components.contactgegevens.constants import GeslachtChoices from openklant.components.contactgegevens.mixins import AdresMixin +from openklant.utils.validators import validate_country class Organisatie(AdresMixin): @@ -42,14 +43,13 @@ class Organisatie(AdresMixin): land = models.CharField( _("land"), help_text=_( - "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " - "aangeeft alwaar de ingeschrevene verblijft." + "ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft." ), validators=[ - MinLengthValidator(limit_value=4), - validate_integer, + MinLengthValidator(limit_value=2), + validate_country, ], - max_length=4, + max_length=2, blank=True, ) @@ -128,14 +128,13 @@ class Persoon(AdresMixin): land = models.CharField( _("land"), help_text=_( - "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " - "aangeeft alwaar de ingeschrevene verblijft." + "ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft." ), validators=[ - MinLengthValidator(limit_value=4), - validate_integer, + MinLengthValidator(limit_value=2), + validate_country, ], - max_length=4, + max_length=2, blank=True, ) diff --git a/src/openklant/components/contactgegevens/openapi.yaml b/src/openklant/components/contactgegevens/openapi.yaml index ade2386b..2d0c03f9 100644 --- a/src/openklant/components/contactgegevens/openapi.yaml +++ b/src/openklant/components/contactgegevens/openapi.yaml @@ -367,10 +367,10 @@ components: description: De adres gegevens van een organisatie. land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 required: - handelsnaam - url @@ -400,10 +400,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 PaginatedOrganisatieList: type: object required: @@ -494,10 +494,10 @@ components: description: De adres gegevens van een organisatie. land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 PatchedPersoon: type: object description: |- @@ -560,10 +560,10 @@ components: description: De adres gegevens van een organisatie. land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 Persoon: type: object description: |- @@ -626,10 +626,10 @@ components: description: De adres gegevens van een organisatie. land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 required: - geboortedatum - geslachtsnaam @@ -660,10 +660,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 securitySchemes: tokenAuth: type: apiKey diff --git a/src/openklant/components/contactgegevens/tests/__init__.py b/src/openklant/components/contactgegevens/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/openklant/components/contactgegevens/tests/test_migrations.py b/src/openklant/components/contactgegevens/tests/test_migrations.py new file mode 100644 index 00000000..06e73f95 --- /dev/null +++ b/src/openklant/components/contactgegevens/tests/test_migrations.py @@ -0,0 +1,195 @@ +from django.db import IntegrityError + +from openklant.tests.test_migrate import BaseMigrationTest + + +class TestCountryConverter(BaseMigrationTest): + app = "contactgegevens" + migrate_from = "0003_alter_persoon_overlijdensdatum" + migrate_to = "0004_alter_organisatie_adres_land_alter_organisatie_land_and_more" + + def test_ok_migration_organisatie_model(self): + + Organisatie = self.old_app_state.get_model("contactgegevens", "Organisatie") + + Organisatie.objects.create(land="6030", adres_land="6030") + + self._perform_migration() + + Organisatie = self.apps.get_model("contactgegevens", "Organisatie") + + records = Organisatie.objects.all() + self.assertEqual(records.count(), 1) + self.assertEqual(records[0].land, "NL") + self.assertEqual(records[0].adres_land, "NL") + self.assertNotEqual(records[0].land, "6030") + self.assertNotEqual(records[0].adres_land, "6030") + + def test_ok_migration_organisatie_model_empty_code(self): + + Organisatie = self.old_app_state.get_model("contactgegevens", "Organisatie") + org1 = Organisatie.objects.create(land="", adres_land="") + org2 = Organisatie.objects.create(land="6030", adres_land="6030") + + self._perform_migration() + Organisatie = self.apps.get_model("contactgegevens", "Organisatie") + + records = Organisatie.objects.all() + org1 = records.get(pk=org1.pk) + org2 = records.get(pk=org2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(org1.land, "") + self.assertEqual(org1.adres_land, "") + self.assertEqual(org2.land, "NL") + self.assertEqual(org2.adres_land, "NL") + self.assertNotEqual(org2.land, "6030") + self.assertNotEqual(org2.adres_land, "6030") + + def test_ko_migration_organisatie_model_wrong_code(self): + + Organisatie = self.old_app_state.get_model("contactgegevens", "Organisatie") + org1 = Organisatie.objects.create(land="9999", adres_land="9999") + org2 = Organisatie.objects.create(land="5001", adres_land="5001") + + with self.assertRaises(IntegrityError) as error: + self._perform_migration() + + self.assertEqual( + ( + "The migration cannot proceed due to 1 records that don't comply with the " + "Organisatie model's requirements. Possible data inconsistency or mapping error." + ), + str(error.exception), + ) + + records = Organisatie.objects.all() + org1 = records.get(pk=org1.pk) + org2 = records.get(pk=org2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(org1.land, "9999") + self.assertEqual(org1.adres_land, "9999") + self.assertEqual(org2.land, "5001") + self.assertEqual(org2.adres_land, "5001") + + # Update manually + org1 = records.get(pk=org1.pk) + org1.land = "6030" + org1.adres_land = "6030" + org1.save() + + # Re-Run the migration + self._perform_migration() + Organisatie = self.apps.get_model("contactgegevens", "Organisatie") + + records = Organisatie.objects.all() + org1 = records.get(pk=org1.pk) + org2 = records.get(pk=org2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(org1.land, "NL") + self.assertEqual(org1.adres_land, "NL") + self.assertEqual(org2.land, "CA") + self.assertEqual(org2.adres_land, "CA") + + self.assertNotEqual(org1.land, "6030") + self.assertNotEqual(org1.adres_land, "6030") + self.assertNotEqual(org2.land, "5001") + self.assertNotEqual(org2.adres_land, "5001") + + def test_ok_migrate_persoon_model(self): + + Persoon = self.old_app_state.get_model("contactgegevens", "Persoon") + + Persoon.objects.create( + land="6030", adres_land="6030", geboortedatum="1980-02-23" + ) + + self._perform_migration() + Persoon = self.apps.get_model("contactgegevens", "Persoon") + + records = Persoon.objects.all() + self.assertEqual(records.count(), 1) + self.assertEqual(records[0].land, "NL") + self.assertEqual(records[0].adres_land, "NL") + self.assertNotEqual(records[0].land, "6030") + self.assertNotEqual(records[0].adres_land, "6030") + + def test_ok_migration_persoon_model_empty_code(self): + + Persoon = self.old_app_state.get_model("contactgegevens", "Persoon") + + persoon1 = Persoon.objects.create( + land="", adres_land="", geboortedatum="1980-02-23" + ) + persoon2 = Persoon.objects.create( + land="6030", adres_land="6030", geboortedatum="1980-02-23" + ) + + self._perform_migration() + Persoon = self.apps.get_model("contactgegevens", "Persoon") + + records = Persoon.objects.all() + persoon1 = records.get(pk=persoon1.pk) + persoon2 = records.get(pk=persoon2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(persoon1.land, "") + self.assertEqual(persoon1.adres_land, "") + self.assertEqual(persoon2.land, "NL") + self.assertEqual(persoon2.adres_land, "NL") + + def test_ko_migration_persoon_model_wrong_code(self): + + Persoon = self.old_app_state.get_model("contactgegevens", "Persoon") + persoon1 = Persoon.objects.create( + land="9999", adres_land="9999", geboortedatum="1980-02-23" + ) + persoon2 = Persoon.objects.create( + land="5001", adres_land="5001", geboortedatum="1980-02-23" + ) + + with self.assertRaises(IntegrityError) as error: + self._perform_migration() + + self.assertEqual( + ( + "The migration cannot proceed due to 1 records that don't comply with the " + "Persoon model's requirements. Possible data inconsistency or mapping error." + ), + str(error.exception), + ) + + records = Persoon.objects.all() + self.assertEqual(records.count(), 2) + self.assertEqual(persoon1.land, "9999") + self.assertEqual(persoon1.adres_land, "9999") + self.assertEqual(persoon2.land, "5001") + self.assertEqual(persoon2.adres_land, "5001") + + # Update manually + persoon1 = records.get(pk=persoon1.pk) + persoon2 = records.get(pk=persoon2.pk) + persoon1.land = "6030" + persoon1.adres_land = "6030" + persoon1.save() + + # Re-Run the migration + self._perform_migration() + Persoon = self.apps.get_model("contactgegevens", "Persoon") + + records = Persoon.objects.all() + persoon1 = records.get(pk=persoon1.pk) + persoon2 = records.get(pk=persoon2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(persoon1.land, "NL") + self.assertEqual(persoon1.adres_land, "NL") + self.assertEqual(persoon2.land, "CA") + self.assertEqual(persoon2.adres_land, "CA") + + self.assertNotEqual(persoon1.land, "6030") + self.assertNotEqual(persoon1.adres_land, "6030") + self.assertNotEqual(persoon2.land, "5001") + self.assertNotEqual(persoon2.adres_land, "5001") diff --git a/src/openklant/components/klantinteracties/api/tests/factories.py b/src/openklant/components/klantinteracties/api/tests/factories.py index 3b1baf17..cf2b7981 100644 --- a/src/openklant/components/klantinteracties/api/tests/factories.py +++ b/src/openklant/components/klantinteracties/api/tests/factories.py @@ -17,7 +17,7 @@ class BezoekAdresDataFactory(factory.DictFactory): adresregel1 = "adres1" adresregel2 = "adres2" adresregel3 = "adres3" - land = "6030" + land = "NL" class CorrespondentieAdresDataFactory(factory.DictFactory): @@ -25,7 +25,7 @@ class CorrespondentieAdresDataFactory(factory.DictFactory): adresregel1 = "adres1" adresregel2 = "adres2" adresregel3 = "adres3" - land = "6030" + land = "NL" class ContactNaamDataFactory(factory.DictFactory): diff --git a/src/openklant/components/klantinteracties/api/tests/test_klantcontacten.py b/src/openklant/components/klantinteracties/api/tests/test_klantcontacten.py index bb74129a..e2c64e64 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_klantcontacten.py +++ b/src/openklant/components/klantinteracties/api/tests/test_klantcontacten.py @@ -414,14 +414,14 @@ def test_create_betrokkene_with_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "c06918d9-899b-4d98-a10d-08436ebc6c20", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "contactnaam": { "voorletters": "P", @@ -448,7 +448,7 @@ def test_create_betrokkene_with_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -458,7 +458,7 @@ def test_create_betrokkene_with_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -487,14 +487,14 @@ def test_create_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "c06918d9-899b-4d98-a10d-08436ebc6c20", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "contactnaam": { "voorletters": "P", @@ -521,7 +521,7 @@ def test_create_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -531,7 +531,7 @@ def test_create_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -559,12 +559,12 @@ def test_update_betrokkene(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="c06918d9-899b-4d98-a10d-08436ebc6c20", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", contactnaam_voorletters="P", contactnaam_voornaam="Phil", contactnaam_voorvoegsel_achternaam="", @@ -588,7 +588,7 @@ def test_update_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -598,7 +598,7 @@ def test_update_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -624,14 +624,14 @@ def test_update_betrokkene(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "c06918d9-899b-4d98-a10d-08436ebc6c20", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "contactnaam": { "voorletters": "changed", @@ -658,7 +658,7 @@ def test_update_betrokkene(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -668,7 +668,7 @@ def test_update_betrokkene(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -696,12 +696,12 @@ def test_partial_update_betrokkene(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="c06918d9-899b-4d98-a10d-08436ebc6c20", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", contactnaam_voorletters="P", contactnaam_voornaam="Phil", contactnaam_voorvoegsel_achternaam="", @@ -725,7 +725,7 @@ def test_partial_update_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -735,7 +735,7 @@ def test_partial_update_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -759,7 +759,7 @@ def test_partial_update_betrokkene(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, } @@ -775,7 +775,7 @@ def test_partial_update_betrokkene(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -785,7 +785,7 @@ def test_partial_update_betrokkene(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1578,14 +1578,14 @@ def test_create_success(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "c06918d9-899b-4d98-a10d-08436ebc6c20", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "contactnaam": { "voorletters": "P", diff --git a/src/openklant/components/klantinteracties/api/tests/test_partijen.py b/src/openklant/components/klantinteracties/api/tests/test_partijen.py index fa925abd..bb6e4d1b 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_partijen.py +++ b/src/openklant/components/klantinteracties/api/tests/test_partijen.py @@ -114,14 +114,14 @@ def test_create_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "partijIdentificatie": { "contactnaam": { @@ -160,7 +160,7 @@ def test_create_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -170,7 +170,7 @@ def test_create_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -216,7 +216,7 @@ def test_create_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -226,7 +226,7 @@ def test_create_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -395,14 +395,14 @@ def test_create_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "soortPartij": "persoon", "partijIdentificatie": { @@ -438,7 +438,7 @@ def test_create_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -448,7 +448,7 @@ def test_create_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -487,14 +487,14 @@ def test_create_organisatie(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "soortPartij": "organisatie", "partijIdentificatie": { @@ -526,7 +526,7 @@ def test_create_organisatie(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -536,7 +536,7 @@ def test_create_organisatie(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -562,14 +562,14 @@ def test_create_contactpersoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f", "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, "soortPartij": "contactpersoon", "partijIdentificatie": { @@ -608,7 +608,7 @@ def test_create_contactpersoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -618,7 +618,7 @@ def test_create_contactpersoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -652,12 +652,12 @@ def test_update_partij(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) PersoonFactory.create( partij=partij, @@ -712,7 +712,7 @@ def test_update_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -722,7 +722,7 @@ def test_update_partij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -754,14 +754,14 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "partijIdentificatie": { "contactnaam": { @@ -814,7 +814,7 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -824,7 +824,7 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -932,14 +932,14 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, } @@ -964,7 +964,7 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -974,7 +974,7 @@ def test_update_partij(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) @@ -992,12 +992,12 @@ def test_update_partij_persoon(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) PersoonFactory.create( partij=partij, @@ -1029,7 +1029,7 @@ def test_update_partij_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1039,7 +1039,7 @@ def test_update_partij_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1071,14 +1071,14 @@ def test_update_partij_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "partijIdentificatie": { "contactnaam": { @@ -1111,7 +1111,7 @@ def test_update_partij_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1121,7 +1121,7 @@ def test_update_partij_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1151,12 +1151,12 @@ def test_update_partij_organisatie(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) OrganisatieFactory(partij=partij, naam="Whitechapel") detail_url = reverse( @@ -1182,7 +1182,7 @@ def test_update_partij_organisatie(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1192,7 +1192,7 @@ def test_update_partij_organisatie(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual(data["partijIdentificatie"], {"naam": "Whitechapel"}) @@ -1213,14 +1213,14 @@ def test_update_partij_organisatie(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "partijIdentificatie": { "naam": "The Acacia Strain", @@ -1248,7 +1248,7 @@ def test_update_partij_organisatie(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1258,7 +1258,7 @@ def test_update_partij_organisatie(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1280,12 +1280,12 @@ def test_update_partij_contactpersoon(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) organisatie = PartijFactory.create(soort_partij="organisatie") ContactpersoonFactory.create( @@ -1327,7 +1327,7 @@ def test_update_partij_contactpersoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1337,7 +1337,7 @@ def test_update_partij_contactpersoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1373,14 +1373,14 @@ def test_update_partij_contactpersoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "partijIdentificatie": { "werkteVoorPartij": {"uuid": str(organisatie2.uuid)}, @@ -1414,7 +1414,7 @@ def test_update_partij_contactpersoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1424,7 +1424,7 @@ def test_update_partij_contactpersoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1458,12 +1458,12 @@ def test_update_partij_contactpersoon_to_persoon(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) organisatie = PartijFactory.create(soort_partij="organisatie") ContactpersoonFactory.create( @@ -1497,7 +1497,7 @@ def test_update_partij_contactpersoon_to_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1507,7 +1507,7 @@ def test_update_partij_contactpersoon_to_persoon(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1543,14 +1543,14 @@ def test_update_partij_contactpersoon_to_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "correspondentieadres": { "nummeraanduidingId": "sd76f7sd-j4nr-a9s8-83ec-sad89f79a7sd", "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, "partijIdentificatie": { "contactnaam": { @@ -1583,7 +1583,7 @@ def test_update_partij_contactpersoon_to_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1593,7 +1593,7 @@ def test_update_partij_contactpersoon_to_persoon(self): "adresregel1": "changed", "adresregel2": "changed", "adresregel3": "changed", - "land": "3060", + "land": "NL", }, ) self.assertEqual( @@ -1623,12 +1623,12 @@ def test_partial_update_parij(self): bezoekadres_adresregel1="adres1", bezoekadres_adresregel2="adres2", bezoekadres_adresregel3="adres3", - bezoekadres_land="6030", + bezoekadres_land="NL", correspondentieadres_nummeraanduiding_id="095be615-a8ad-4c33-8e9c-c7612fbf6c9f", correspondentieadres_adresregel1="adres1", correspondentieadres_adresregel2="adres2", correspondentieadres_adresregel3="adres3", - correspondentieadres_land="6030", + correspondentieadres_land="NL", ) digitaal_adres = DigitaalAdresFactory.create(partij=partij) digitaal_adres2 = DigitaalAdresFactory.create(partij=None) @@ -1666,7 +1666,7 @@ def test_partial_update_parij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1676,7 +1676,7 @@ def test_partial_update_parij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1723,7 +1723,7 @@ def test_partial_update_parij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( @@ -1733,7 +1733,7 @@ def test_partial_update_parij(self): "adresregel1": "adres1", "adresregel2": "adres2", "adresregel3": "adres3", - "land": "6030", + "land": "NL", }, ) self.assertEqual( diff --git a/src/openklant/components/klantinteracties/migrations/0026_alter_betrokkene_bezoekadres_land_and_more.py b/src/openklant/components/klantinteracties/migrations/0026_alter_betrokkene_bezoekadres_land_and_more.py new file mode 100644 index 00000000..5254b4ea --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0026_alter_betrokkene_bezoekadres_land_and_more.py @@ -0,0 +1,138 @@ +# Generated by Django 4.2.17 on 2025-01-27 14:35 + +import logging +import django.core.validators +import openklant.utils.validators +from django.db import migrations, models +import openklant.utils.validators +from openklant.utils.constants import COUNTRIES_DICT +from django.db import IntegrityError + +logger = logging.getLogger(__name__) + +FIELDS = ["bezoekadres_land", "correspondentieadres_land"] + + +def _check_records(records): + total_failed_records = 0 + for record in records: + record_failed = False + for field_name in FIELDS: + field_value = getattr(record, field_name, None) + iso_code = COUNTRIES_DICT.get(field_value, "") + if field_value and not iso_code: + record_failed = True + logger.warning( + "%s(pk=%s, uuid=%s) Field: '%s'. No match found for nl_code (not found in Tabel 34 Landentabel): '%s'", + type(record).__qualname__, + record.pk, + record.uuid, + field_name, + field_value, + ) + + if record_failed: + total_failed_records += 1 + return total_failed_records + + +def _update_records(records): + for record in records: + updated = False + for field_name in FIELDS: + if field_value := getattr(record, field_name, None): + iso_code = COUNTRIES_DICT.get(field_value, "") + setattr(record, field_name, iso_code) + updated = True + + if updated: + record.save() + + +def _check_and_update_records(apps, schema_editor): + Betrokkene = apps.get_model("klantinteracties", "Betrokkene") + Partij = apps.get_model("klantinteracties", "Partij") + + for model in [Betrokkene, Partij]: + records = model.objects.all() + if total_failed_records := _check_records(records): + raise IntegrityError( + "The migration cannot proceed due to %s records that don't comply with the %s model's requirements. " + "Possible data inconsistency or mapping error." + % (total_failed_records, model.__qualname__) + ) + else: + _update_records(records) + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "klantinteracties", + "0025_alter_partijidentificator_partij_identificator_code_objecttype_and_more", + ), + ] + + operations = [ + migrations.RunPython( + code=_check_and_update_records, + reverse_code=migrations.RunPython.noop, + ), + migrations.AlterField( + model_name="betrokkene", + name="bezoekadres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="betrokkene", + name="correspondentieadres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="partij", + name="bezoekadres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + migrations.AlterField( + model_name="partij", + name="correspondentieadres_land", + field=models.CharField( + blank=True, + help_text="ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=2, + validators=[ + django.core.validators.MinLengthValidator(limit_value=2), + openklant.utils.validators.validate_country, + ], + verbose_name="land", + ), + ), + ] diff --git a/src/openklant/components/klantinteracties/models/mixins.py b/src/openklant/components/klantinteracties/models/mixins.py index 3ee69758..1d73c018 100644 --- a/src/openklant/components/klantinteracties/models/mixins.py +++ b/src/openklant/components/klantinteracties/models/mixins.py @@ -1,9 +1,11 @@ -from django.core.validators import MinLengthValidator, validate_integer +from django.core.validators import MinLengthValidator from django.db import models from django.utils.translation import gettext_lazy as _ from vng_api_common.descriptors import GegevensGroepType +from openklant.utils.validators import validate_country + class BezoekadresMixin(models.Model): # TODO: Check if this is correct. @@ -42,14 +44,13 @@ class BezoekadresMixin(models.Model): bezoekadres_land = models.CharField( _("land"), help_text=_( - "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " - "aangeeft alwaar de ingeschrevene verblijft." + "ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft." ), validators=[ - MinLengthValidator(limit_value=4), - validate_integer, + MinLengthValidator(limit_value=2), + validate_country, ], - max_length=4, + max_length=2, blank=True, ) @@ -111,14 +112,13 @@ class CorrespondentieadresMixin(models.Model): correspondentieadres_land = models.CharField( _("land"), help_text=_( - "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " - "aangeeft alwaar de ingeschrevene verblijft." + "ISO 3166-code die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft." ), validators=[ - MinLengthValidator(limit_value=4), - validate_integer, + MinLengthValidator(limit_value=2), + validate_country, ], - max_length=4, + max_length=2, blank=True, ) diff --git a/src/openklant/components/klantinteracties/openapi.yaml b/src/openklant/components/klantinteracties/openapi.yaml index 415c1319..7c954eb3 100644 --- a/src/openklant/components/klantinteracties/openapi.yaml +++ b/src/openklant/components/klantinteracties/openapi.yaml @@ -3222,10 +3222,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 BetrokkeneForeignKey: type: object properties: @@ -3352,10 +3352,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 Bijlage: type: object description: |- @@ -4647,10 +4647,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 PartijCorrespondentieadres: type: object properties: @@ -4676,10 +4676,10 @@ components: maxLength: 80 land: type: string - description: Een code, opgenomen in Tabel 34, Landentabel, die het land - (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft. - maxLength: 4 - minLength: 4 + description: ISO 3166-code die het land (buiten Nederland) aangeeft waar + de abonnee woont. + maxLength: 2 + minLength: 2 PartijForeignKey: type: object properties: diff --git a/src/openklant/components/klantinteracties/tests/test_migrations.py b/src/openklant/components/klantinteracties/tests/test_migrations.py new file mode 100644 index 00000000..512e89b5 --- /dev/null +++ b/src/openklant/components/klantinteracties/tests/test_migrations.py @@ -0,0 +1,248 @@ +from django.db import IntegrityError + +from openklant.tests.test_migrate import BaseMigrationTest + + +class TestCountryConverter(BaseMigrationTest): + app = "klantinteracties" + migrate_from = ( + "0025_alter_partijidentificator_partij_identificator_code_objecttype_and_more" + ) + migrate_to = "0026_alter_betrokkene_bezoekadres_land_and_more" + + def test_ok_migration_betrokkene_model(self): + + Betrokkene = self.old_app_state.get_model("klantinteracties", "Betrokkene") + Klantcontact = self.old_app_state.get_model("klantinteracties", "Klantcontact") + + Betrokkene.objects.create( + partij=None, + klantcontact=Klantcontact.objects.create(vertrouwelijk=False), + initiator=False, + bezoekadres_land="6030", + correspondentieadres_land="6030", + ) + + self._perform_migration() + + Betrokkene = self.apps.get_model("klantinteracties", "Betrokkene") + + records = Betrokkene.objects.all() + self.assertEqual(records.count(), 1) + self.assertEqual(records[0].bezoekadres_land, "NL") + self.assertEqual(records[0].correspondentieadres_land, "NL") + self.assertNotEqual(records[0].bezoekadres_land, "6030") + self.assertNotEqual(records[0].correspondentieadres_land, "6030") + + def test_ok_migration_betrokkene_model_empty_code(self): + + Betrokkene = self.old_app_state.get_model("klantinteracties", "Betrokkene") + Klantcontact = self.old_app_state.get_model("klantinteracties", "Klantcontact") + + betrokken1 = Betrokkene.objects.create( + partij=None, + klantcontact=Klantcontact.objects.create(vertrouwelijk=False, nummer=123), + initiator=False, + bezoekadres_land="", + correspondentieadres_land="", + ) + + betrokken2 = Betrokkene.objects.create( + partij=None, + klantcontact=Klantcontact.objects.create(vertrouwelijk=False, nummer=456), + initiator=False, + bezoekadres_land="6030", + correspondentieadres_land="6030", + ) + + self._perform_migration() + + Betrokkene = self.apps.get_model("klantinteracties", "Betrokkene") + + records = Betrokkene.objects.all() + betrokken1 = records.get(pk=betrokken1.pk) + betrokken2 = records.get(pk=betrokken2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(betrokken1.bezoekadres_land, "") + self.assertEqual(betrokken1.correspondentieadres_land, "") + self.assertEqual(betrokken2.bezoekadres_land, "NL") + self.assertEqual(betrokken2.correspondentieadres_land, "NL") + self.assertNotEqual(betrokken2.bezoekadres_land, "6030") + self.assertNotEqual(betrokken2.correspondentieadres_land, "6030") + + def test_ko_migration_betrokkene_model_wrong_code(self): + + Betrokkene = self.old_app_state.get_model("klantinteracties", "Betrokkene") + Klantcontact = self.old_app_state.get_model("klantinteracties", "Klantcontact") + + betrokken1 = Betrokkene.objects.create( + partij=None, + klantcontact=Klantcontact.objects.create(vertrouwelijk=False, nummer=123), + initiator=False, + bezoekadres_land="9999", + correspondentieadres_land="9999", + ) + + betrokken2 = Betrokkene.objects.create( + partij=None, + klantcontact=Klantcontact.objects.create(vertrouwelijk=False, nummer=456), + initiator=False, + bezoekadres_land="5001", + correspondentieadres_land="5001", + ) + + with self.assertRaises(IntegrityError) as error: + self._perform_migration() + + self.assertEqual( + ( + "The migration cannot proceed due to 1 records that don't comply with the " + "Betrokkene model's requirements. Possible data inconsistency or mapping error." + ), + str(error.exception), + ) + + records = Betrokkene.objects.all() + betrokken1 = records.get(pk=betrokken1.pk) + betrokken2 = records.get(pk=betrokken2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(betrokken1.bezoekadres_land, "9999") + self.assertEqual(betrokken1.correspondentieadres_land, "9999") + self.assertEqual(betrokken2.bezoekadres_land, "5001") + self.assertEqual(betrokken2.correspondentieadres_land, "5001") + + # Update manually + betrokken1 = records.get(pk=betrokken1.pk) + betrokken1.bezoekadres_land = "6030" + betrokken1.correspondentieadres_land = "6030" + betrokken1.save() + + # Re-Run the migration + self._perform_migration() + + Betrokkene = self.apps.get_model("klantinteracties", "Betrokkene") + + records = Betrokkene.objects.all() + betrokken1 = records.get(pk=betrokken1.pk) + betrokken2 = records.get(pk=betrokken2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(betrokken1.bezoekadres_land, "NL") + self.assertEqual(betrokken1.correspondentieadres_land, "NL") + self.assertEqual(betrokken2.bezoekadres_land, "CA") + self.assertEqual(betrokken2.correspondentieadres_land, "CA") + + def test_ok_migration_partij_model(self): + + Partij = self.old_app_state.get_model("klantinteracties", "Partij") + + Partij.objects.create( + indicatie_actief=True, + bezoekadres_land="6030", + correspondentieadres_land="6030", + ) + + self._perform_migration() + + Partij = self.apps.get_model("klantinteracties", "Partij") + + records = Partij.objects.all() + self.assertEqual(records.count(), 1) + self.assertEqual(records[0].bezoekadres_land, "NL") + self.assertEqual(records[0].correspondentieadres_land, "NL") + self.assertNotEqual(records[0].bezoekadres_land, "6030") + self.assertNotEqual(records[0].correspondentieadres_land, "6030") + + def test_ok_migration_partij_model_empty_code(self): + + Partij = self.old_app_state.get_model("klantinteracties", "Partij") + + partij1 = Partij.objects.create( + indicatie_actief=True, + bezoekadres_land="", + correspondentieadres_land="", + nummer=123, + ) + partij2 = Partij.objects.create( + indicatie_actief=True, + bezoekadres_land="6030", + correspondentieadres_land="6030", + nummer=456, + ) + + self._perform_migration() + + Partij = self.apps.get_model("klantinteracties", "Partij") + + records = Partij.objects.all() + partij1 = records.get(pk=partij1.pk) + partij2 = records.get(pk=partij2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(partij1.bezoekadres_land, "") + self.assertEqual(partij1.correspondentieadres_land, "") + self.assertEqual(partij2.bezoekadres_land, "NL") + self.assertEqual(partij2.correspondentieadres_land, "NL") + self.assertNotEqual(partij2.bezoekadres_land, "6030") + self.assertNotEqual(partij2.correspondentieadres_land, "6030") + + def test_ko_migration_partij_model_wrong_code(self): + + Partij = self.old_app_state.get_model("klantinteracties", "Partij") + + partij1 = Partij.objects.create( + indicatie_actief=True, + bezoekadres_land="9999", + correspondentieadres_land="9999", + nummer=123, + ) + partij2 = Partij.objects.create( + indicatie_actief=True, + bezoekadres_land="5001", + correspondentieadres_land="5001", + nummer=456, + ) + + with self.assertRaises(IntegrityError) as error: + self._perform_migration() + + self.assertEqual( + ( + "The migration cannot proceed due to 1 records that don't comply with the " + "Partij model's requirements. Possible data inconsistency or mapping error." + ), + str(error.exception), + ) + + records = Partij.objects.all() + partij1 = records.get(pk=partij1.pk) + partij2 = records.get(pk=partij2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(partij1.bezoekadres_land, "9999") + self.assertEqual(partij1.correspondentieadres_land, "9999") + self.assertEqual(partij2.bezoekadres_land, "5001") + self.assertEqual(partij2.correspondentieadres_land, "5001") + + # Update manually + partij1 = records.get(pk=partij1.pk) + partij1.bezoekadres_land = "6030" + partij1.correspondentieadres_land = "6030" + partij1.save() + + # Re-Run the migration + self._perform_migration() + + Partij = self.apps.get_model("klantinteracties", "Partij") + + records = Partij.objects.all() + partij1 = records.get(pk=partij1.pk) + partij2 = records.get(pk=partij2.pk) + + self.assertEqual(records.count(), 2) + self.assertEqual(partij1.bezoekadres_land, "NL") + self.assertEqual(partij1.correspondentieadres_land, "NL") + self.assertEqual(partij2.bezoekadres_land, "CA") + self.assertEqual(partij2.correspondentieadres_land, "CA") diff --git a/src/openklant/components/token/tests/test_migrations.py b/src/openklant/components/token/tests/test_migrations.py index 47745a56..586669ee 100644 --- a/src/openklant/components/token/tests/test_migrations.py +++ b/src/openklant/components/token/tests/test_migrations.py @@ -1,53 +1,4 @@ -from django.core.management import call_command -from django.db import connection -from django.db.migrations.executor import MigrationExecutor -from django.db.migrations.state import StateApps -from django.test import TransactionTestCase - - -class BaseMigrationTest(TransactionTestCase): - app: str - migrate_from: str # The migration before the one we want to test - migrate_to: str # The migration we want to test - - setting_overrides: dict = {} - - old_app_state: StateApps - app_state: StateApps - - def setUp(self) -> None: - """ - Setup the migration test by reversing to `migrate_from` state, - then applying the `migrate_to` state. - """ - assert self.app is not None, "You must define the `app` attribute" - assert self.migrate_from is not None, "You must define `migrate_from`" - assert self.migrate_to is not None, "You must define `migrate_to`" - - # Step 1: Set up the MigrationExecutor - executor = MigrationExecutor(connection) - - # Step 2: Reverse to the starting migration state - migrate_from = [(self.app, self.migrate_from)] - old_migrate_state = executor.migrate(migrate_from) - - self.old_app_state = old_migrate_state.apps - - def _perform_migration(self) -> None: - migrate_to = [(self.app, self.migrate_to)] - - executor = MigrationExecutor(connection) - executor.loader.build_graph() # reload the graph in case of dependency changes - executor.migrate(migrate_to) - - self.apps = executor.loader.project_state(migrate_to).apps - - @classmethod - def tearDownClass(cls) -> None: - super().tearDownClass() - - # reset to latest migration - call_command("migrate", verbosity=0, database=connection._alias) +from openklant.tests.test_migrate import BaseMigrationTest class TestTokenAuthUniqueness(BaseMigrationTest): diff --git a/src/openklant/conf/base.py b/src/openklant/conf/base.py index 27679db8..f58d3f4d 100644 --- a/src/openklant/conf/base.py +++ b/src/openklant/conf/base.py @@ -15,6 +15,8 @@ "openklant.components.token", "openklant.components.klantinteracties", "openklant.components.contactgegevens", + # Django libraries + "localflavor", ] # `django.contrib.sites` is installed by Open API Framework by default # but we don't want to rely on it anymore (e.g. when generating the label for 2FA) diff --git a/src/openklant/fixtures/contactgegevens.json b/src/openklant/fixtures/contactgegevens.json index 4c3df531..ebdbc6a6 100644 --- a/src/openklant/fixtures/contactgegevens.json +++ b/src/openklant/fixtures/contactgegevens.json @@ -7,12 +7,12 @@ "adres_adresregel1": "Keizersgracht 117", "adres_adresregel2": "1015 CJ Amsterdam", "adres_adresregel3": "Noord-Holland", - "adres_land": "6030", + "adres_land": "NL", "uuid": "9df246f7-027b-4305-bf43-043b3b926ed3", "handelsnaam": "Maykin Media", "oprichtingsdatum": "2008-01-01", "opheffingsdatum": null, - "land": "6030" + "land": "NL" } }, { @@ -23,7 +23,7 @@ "adres_adresregel1": "Keizersgracht 117", "adres_adresregel2": "1015 CJ Amsterdam", "adres_adresregel3": "Noord-Holland", - "adres_land": "6030", + "adres_land": "NL", "uuid": "729ff687-e6ab-4627-9a0b-5eca4098b746", "geboortedatum": "2000-01-01", "overlijdensdatum": null, @@ -31,7 +31,7 @@ "geslacht": "v", "voorvoegsel": "van", "voornamen": "Nagihan", - "land": "6030" + "land": "NL" } } ] diff --git a/src/openklant/fixtures/klantinteracties.json b/src/openklant/fixtures/klantinteracties.json index 36b1c834..6aa0b4f5 100644 --- a/src/openklant/fixtures/klantinteracties.json +++ b/src/openklant/fixtures/klantinteracties.json @@ -155,7 +155,7 @@ "bezoekadres_adresregel1": "Keizersgracht 117", "bezoekadres_adresregel2": "1015 CJ Amsterdam", "bezoekadres_adresregel3": "", - "bezoekadres_land": "6030", + "bezoekadres_land": "NL", "correspondentieadres_nummeraanduiding_id": "", "correspondentieadres_adresregel1": "", "correspondentieadres_adresregel2": "", @@ -231,7 +231,7 @@ "bezoekadres_adresregel1": "Keizersgracht 117", "bezoekadres_adresregel2": "1015 CJ Amsterdam", "bezoekadres_adresregel3": "", - "bezoekadres_land": "6030", + "bezoekadres_land": "NL", "correspondentieadres_nummeraanduiding_id": "", "correspondentieadres_adresregel1": "", "correspondentieadres_adresregel2": "", diff --git a/src/openklant/tests/test_migrate.py b/src/openklant/tests/test_migrate.py index e6f73880..2d72a53e 100644 --- a/src/openklant/tests/test_migrate.py +++ b/src/openklant/tests/test_migrate.py @@ -5,7 +5,10 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.management import CommandError, call_command -from django.test import LiveServerTestCase +from django.db import connection +from django.db.migrations.executor import MigrationExecutor +from django.db.migrations.state import StateApps +from django.test import LiveServerTestCase, TransactionTestCase from requests import Request from vcr.config import RecordMode @@ -34,6 +37,51 @@ def vcr_request_filter(request: Request): return request +class BaseMigrationTest(TransactionTestCase): + app: str + migrate_from: str # The migration before the one we want to test + migrate_to: str # The migration we want to test + + setting_overrides: dict = {} + + old_app_state: StateApps + app_state: StateApps + + def setUp(self) -> None: + """ + Setup the migration test by reversing to `migrate_from` state, + then applying the `migrate_to` state. + """ + assert self.app is not None, "You must define the `app` attribute" + assert self.migrate_from is not None, "You must define `migrate_from`" + assert self.migrate_to is not None, "You must define `migrate_to`" + + # Step 1: Set up the MigrationExecutor + executor = MigrationExecutor(connection) + + # Step 2: Reverse to the starting migration state + migrate_from = [(self.app, self.migrate_from)] + old_migrate_state = executor.migrate(migrate_from) + + self.old_app_state = old_migrate_state.apps + + def _perform_migration(self) -> None: + migrate_to = [(self.app, self.migrate_to)] + + executor = MigrationExecutor(connection) + executor.loader.build_graph() # reload the graph in case of dependency changes + executor.migrate(migrate_to) + + self.apps = executor.loader.project_state(migrate_to).apps + + @classmethod + def tearDownClass(cls) -> None: + super().tearDownClass() + + # reset to latest migration + call_command("migrate", verbosity=0, database=connection._alias) + + class MigrateTestCase(VCRMixin, LiveServerTestCase): host = LIVE_SERVER_HOST port = LIVE_SERVER_PORT diff --git a/src/openklant/utils/constants.py b/src/openklant/utils/constants.py new file mode 100644 index 00000000..19d4c54a --- /dev/null +++ b/src/openklant/utils/constants.py @@ -0,0 +1,229 @@ +# Mapping from Tabel 34 landcodes to ISO 3166-1 alpha-2 country codes +# Source: +# https://publicaties.rvig.nl/Landelijke_tabellen/Landelijke_tabellen_32_t_m_61_excl_tabel_35/Landelijke_Tabellen_32_t_m_61_in_csv_formaat/Tabel_34_Landen_gesorteerd_op_omschrijving + +COUNTRIES_DICT = { + "5001": "CA", + "5002": "FR", + "5003": "CH", + "5005": "MW", + "5006": "CU", + "5007": "SR", + "5008": "TN", + "5009": "AT", + "5010": "BE", + "5011": "BW", + "5012": "IR", + "5013": "NZ", + "5014": "ZA", + "5015": "DK", + "5017": "HU", + "5018": "SA", + "5019": "LR", + "5020": "ET", + "5021": "CL", + "5022": "MA", + "5023": "TG", + "5024": "GH", + "5025": "LA", + "5026": "AO", + "5027": "PH", + "5028": "ZM", + "5029": "ML", + "5030": "CI", + "5032": "MC", + "5033": "CO", + "5034": "AL", + "5035": "CM", + "5037": "SG", + "5038": "PY", + "5039": "SE", + "5040": "CY", + "5042": "BN", + "5043": "IQ", + "5044": "MU", + "5045": "VA", + "5047": "MM", + "5048": "YE", + "5049": "SI", + "5051": "HR", + "5052": "TW", + "5053": "RU", + "5054": "AM", + "5057": "BH", + "5058": "BT", + "5060": "KM", + "5061": "FK", + "5062": "GF", + "5065": "GL", + "5066": "GP", + "5068": "MO", + "5069": "MQ", + "5070": "MZ", + "5071": "PN", + "5072": "GW", + "5073": "RE", + "5076": "TO", + "5077": "WF", + "5084": "YT", + "5092": "VC", + "5095": "AW", + "5096": "BF", + "5097": "AZ", + "5099": "KZ", + "5103": "RS", + "5104": "ME", + "5107": "CW", + "5110": "SX", + "5111": "SS", + "6000": "MD", + "6001": "BI", + "6002": "FI", + "6003": "GR", + "6004": "GT", + "6005": "NG", + "6006": "LY", + "6007": "IE", + "6008": "BR", + "6009": "RW", + "6010": "VE", + "6011": "IS", + "6012": "LI", + "6013": "SO", + "6015": "BO", + "6016": "AU", + "6017": "JM", + "6018": "LU", + "6019": "TD", + "6020": "MR", + "6021": "KG", + "6022": "CN", + "6023": "AF", + "6024": "ID", + "6025": "GY", + "6027": "NO", + "6028": "SM", + "6029": "DE", + "6030": "NL", + "6031": "KH", + "6032": "FJ", + "6033": "BS", + "6034": "IL", + "6035": "NP", + "6036": "KR", + "6037": "ES", + "6038": "UA", + "6040": "NE", + "6041": "HT", + "6042": "JO", + "6043": "TR", + "6044": "TT", + "6047": "DZ", + "6048": "GA", + "6049": "KP", + "6050": "UZ", + "6051": "SL", + "6054": "PF", + "6055": "GI", + "6057": "TJ", + "6063": "TM", + "6064": "GE", + "6066": "CZ", + "6067": "SK", + "7003": "MT", + "7004": "BB", + "7005": "AD", + "7006": "MX", + "7007": "CR", + "7008": "GM", + "7009": "SY", + "7014": "EG", + "7015": "AR", + "7016": "LS", + "7017": "HN", + "7018": "NI", + "7020": "PK", + "7021": "SN", + "7024": "BG", + "7026": "MY", + "7027": "DO", + "7028": "PL", + "7030": "VG", + "7031": "TZ", + "7032": "SV", + "7033": "LK", + "7034": "SD", + "7035": "JP", + "7036": "HK", + "7037": "PA", + "7038": "UY", + "7039": "EC", + "7040": "GN", + "7041": "MV", + "7042": "TH", + "7043": "LB", + "7044": "IT", + "7045": "KW", + "7046": "IN", + "7047": "RO", + "7049": "PE", + "7050": "PT", + "7051": "OM", + "7052": "MN", + "7053": "WS", + "7054": "AE", + "7057": "NR", + "7060": "PS", + "7064": "LV", + "7065": "EE", + "7066": "LT", + "7084": "BD", + "7088": "VI", + "7096": "IO", + "7097": "CK", + "7098": "TK", + "7099": "NC", + "8001": "GU", + "8002": "AS", + "8008": "GD", + "8012": "CX", + "8013": "CC", + "8014": "FO", + "8015": "MS", + "8016": "NF", + "8017": "BZ", + "8019": "TC", + "8020": "PR", + "8021": "PG", + "8022": "SB", + "8023": "BJ", + "8024": "VN", + "8025": "CV", + "8026": "SC", + "8027": "KI", + "8028": "TV", + "8029": "LC", + "8030": "DM", + "8031": "ZW", + "8035": "IM", + "8036": "AI", + "8037": "KN", + "8044": "PW", + "8045": "AG", + "9003": "ER", + "9009": "CD", + "9010": "MG", + "9013": "CG", + "9023": "NA", + "9036": "SZ", + "9037": "QA", + "9043": "GQ", + "9048": "BM", + "9056": "MH", + "9086": "CF", + "9087": "DJ", + "9090": "VU", + "9091": "NU", + "9093": "EH", + "9094": "FM", +} diff --git a/src/openklant/utils/tests/test_validators.py b/src/openklant/utils/tests/test_validators.py index 143dc9d5..bdc158e4 100644 --- a/src/openklant/utils/tests/test_validators.py +++ b/src/openklant/utils/tests/test_validators.py @@ -3,6 +3,7 @@ from openklant.utils.validators import ( validate_charfield_entry, + validate_country, validate_iban, validate_no_space, validate_phone_number, @@ -147,3 +148,22 @@ def test_validate_iban(self): self.assertIsNone(validate_iban("ab1299999999999")) self.assertIsNone(validate_iban("ab129")) self.assertIsNone(validate_iban("ab12aaaaaaaaaa")) + + def test_validate_country(self): + invalid_codes = [ + "", + "1", + "10", + "ZZ", + "1Z", + "nl", + ] + for code in invalid_codes: + self.assertRaisesMessage( + ValidationError, + "Ongeldige landcode, de code moet behoren tot de ISO 3166-standaard", + validate_country, + code, + ) + + validate_country("NL") diff --git a/src/openklant/utils/validators.py b/src/openklant/utils/validators.py index d69dbd05..c2780adc 100644 --- a/src/openklant/utils/validators.py +++ b/src/openklant/utils/validators.py @@ -3,6 +3,21 @@ from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ +from localflavor.generic.countries.iso_3166 import ISO_3166_1_ALPHA2_COUNTRY_CODES + + +def validate_country(value: str) -> None: + """ + Validate an ISO 3166-1 alpha-2 country code + + :param value: + :return: None if validation passed. Otherwise, raises a ``ValidationError`` exception. + """ + if value not in ISO_3166_1_ALPHA2_COUNTRY_CODES: + raise ValidationError( + _("Ongeldige landcode, de code moet behoren tot de ISO 3166-standaard") + ) + def validate_charfield_entry(value, allow_apostrophe=False): """