diff --git a/src/open_inwoner/cms/cases/tests/test_contactform.py b/src/open_inwoner/cms/cases/tests/test_contactform.py index 9c2db2860..bba63ea24 100644 --- a/src/open_inwoner/cms/cases/tests/test_contactform.py +++ b/src/open_inwoner/cms/cases/tests/test_contactform.py @@ -19,8 +19,8 @@ DigidUserFactory, eHerkenningUserFactory, ) -from open_inwoner.openklant.constants import Status -from open_inwoner.openklant.models import ESuiteKlantConfig +from open_inwoner.openklant.constants import KlantenServiceType, Status +from open_inwoner.openklant.models import ESuiteKlantConfig, KlantenSysteemConfig from open_inwoner.openklant.services import eSuiteVragenService from open_inwoner.openklant.tests.data import CONTACTMOMENTEN_ROOT, KLANTEN_ROOT from open_inwoner.openzaak.models import CatalogusConfig, OpenZaakConfig @@ -82,21 +82,25 @@ def setUp(self): ) self.oz_config.save() - # klant config - self.klant_config = ESuiteKlantConfig.get_solo() + # klant configurations + self.klant_config = KlantenSysteemConfig.get_solo() + self.klant_config.primary_backend = KlantenServiceType.ESUITE.value + self.klant_config.register_contact_via_api = True self.klant_config.send_email_confirmation = True - self.klant_config.register_contact_moment = True - self.klant_config.register_bronorganisatie_rsin = "123456788" - self.klant_config.register_type = "Melding" - self.klant_config.register_employee_id = "FooVonBar" - self.klant_config.register_channel = "the-designated-channel" - self.klant_config.klanten_service = ServiceFactory( + self.klant_config.save() + + self.esuite_config = ESuiteKlantConfig.get_solo() + self.esuite_config.register_bronorganisatie_rsin = "123456788" + self.esuite_config.register_type = "Melding" + self.esuite_config.register_employee_id = "FooVonBar" + self.esuite_config.register_channel = "the-designated-channel" + self.esuite_config.klanten_service = ServiceFactory( api_root=KLANTEN_ROOT, api_type=APITypes.kc ) - self.klant_config.contactmomenten_service = ServiceFactory( + self.esuite_config.contactmomenten_service = ServiceFactory( api_root=CONTACTMOMENTEN_ROOT, api_type=APITypes.cmc ) - self.klant_config.save() + self.esuite_config.save() self.zaak = generate_oas_component_cached( "zrc", @@ -371,7 +375,7 @@ def test_form_is_shown_if_open_klant_api_configured( self._setUpMocks(m) self._setUpExtraMocks(m) - self.assertTrue(self.klant_config.has_api_configuration()) + self.assertTrue(self.klant_config.has_api_configuration) response = self.app.get(self.case_detail_url, user=self.user) contact_form = response.pyquery("#contact-form") @@ -387,12 +391,14 @@ def test_form_is_shown_if_open_klant_email_configured( self._setUpMocks(m) self._setUpExtraMocks(m) - self.klant_config.register_email = "example@example.com" - self.klant_config.register_contact_moment = False + self.esuite_config.klanten_service = None + self.esuite_config.save() + + self.klant_config.register_contact_email = "example@example.com" self.klant_config.save() - self.assertFalse(self.klant_config.has_api_configuration()) - self.assertTrue(self.klant_config.has_register()) + self.assertFalse(self.klant_config.has_api_configuration) + self.assertTrue(self.klant_config.contact_registration_enabled) response = self.app.get(self.case_detail_url, user=self.user) contact_form = response.pyquery("#contact-form") @@ -411,8 +417,8 @@ def test_form_is_shown_if_open_klant_email_and_api_configured( self.klant_config.register_email = "example@example.com" self.klant_config.save() - self.assertTrue(self.klant_config.has_api_configuration()) - self.assertTrue(self.klant_config.has_register()) + self.assertTrue(self.klant_config.has_api_configuration) + self.assertTrue(self.klant_config.contact_registration_enabled) response = self.app.get(self.case_detail_url, user=self.user) contact_form = response.pyquery("#contact-form") @@ -428,15 +434,14 @@ def test_no_form_shown_if_open_klant_not_configured( self._setUpMocks(m) # reset - self.klant_config.klanten_service = None - self.klant_config.contactmomenten_service = None - self.klant_config.register_email = "" - self.klant_config.register_contact_moment = False - self.klant_config.register_bronorganisatie_rsin = "" - self.klant_config.register_type = "" - self.klant_config.register_employee_id = "" - self.klant_config.save() - self.assertFalse(self.klant_config.has_api_configuration()) + self.esuite_config.klanten_service = None + self.esuite_config.contactmomenten_service = None + self.esuite_config.register_bronorganisatie_rsin = "" + self.esuite_config.register_type = "" + self.esuite_config.register_employee_id = "" + self.esuite_config.save() + + self.assertFalse(self.klant_config.has_api_configuration) response = self.app.get(self.case_detail_url, user=self.user) contact_form = response.pyquery("#contact-form") @@ -665,8 +670,8 @@ def test_form_success_with_email(self, m, mock_contactmoment, mock_send_confirm) self._setUpMocks(m) self._setUpExtraMocks(m) - self.klant_config.register_email = "example@example.com" - self.klant_config.register_contact_moment = False + self.klant_config.register_contact_email = "example@example.com" + self.klant_config.register_contact_via_api = False self.klant_config.save() response = self.app.get(self.case_detail_url, user=self.user) @@ -708,7 +713,7 @@ def test_form_success_with_both_email_and_api( self._setUpMocks(m) self._setUpExtraMocks(m) - self.klant_config.register_email = "example@example.com" + self.klant_config.register_contact_email = "example@example.com" self.klant_config.save() response = self.app.get(self.case_detail_url, user=self.user) @@ -746,7 +751,7 @@ def test_send_email_confirmation_is_configurable__send_enabled( self._setUpMocks(m) self._setUpExtraMocks(m) - config = ESuiteKlantConfig.get_solo() + config = KlantenSysteemConfig.get_solo() config.send_email_confirmation = True config.save() @@ -766,7 +771,7 @@ def test_send_email_confirmation_is_configurable__send_disabled( self._setUpMocks(m) self._setUpExtraMocks(m) - config = ESuiteKlantConfig.get_solo() + config = KlantenSysteemConfig.get_solo() config.send_email_confirmation = False config.save() diff --git a/src/open_inwoner/cms/cases/views/status.py b/src/open_inwoner/cms/cases/views/status.py index c3fcb7591..f57507928 100644 --- a/src/open_inwoner/cms/cases/views/status.py +++ b/src/open_inwoner/cms/cases/views/status.py @@ -32,7 +32,11 @@ from open_inwoner.accounts.models import User from open_inwoner.mail.service import send_contact_confirmation_mail from open_inwoner.openklant.constants import KlantenServiceType -from open_inwoner.openklant.models import ESuiteKlantConfig +from open_inwoner.openklant.models import ( + ESuiteKlantConfig, + KlantenSysteemConfig, + OpenKlant2Config, +) from open_inwoner.openklant.services import ( OpenKlant2Service, Question, @@ -513,7 +517,7 @@ def get_upload_info_context(self, case: Zaak): if not case: return {} - open_klant_config = ESuiteKlantConfig.get_solo() + klanten_config = KlantenSysteemConfig.get_solo() case_type_config_description = "" case_type_document_upload_description = "" @@ -558,7 +562,7 @@ def get_upload_info_context(self, case: Zaak): and not getattr(self.case, "einddatum", None), "external_upload_url": external_upload_url, "contact_form_enabled": ( - contact_form_enabled and open_klant_config.has_register() + contact_form_enabled and klanten_config.contact_registration_enabled ), } @@ -909,23 +913,25 @@ def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): - config = ESuiteKlantConfig.get_solo() + klant_config = KlantenSysteemConfig.get_solo() email_success = False api_success = False send_confirmation = False - if config.register_email: + if klant_config.register_contact_email: form.cleaned_data[ "question" ] += f"\n\nCase number: {self.case.identificatie}" - email_success = self.register_by_email(form, config.register_email) + email_success = self.register_by_email( + form, klant_config.register_contact_email + ) send_confirmation = email_success - if config.register_contact_moment: - api_success = self.register_by_api(form, config) + if klant_config.register_contact_via_api: + api_success = self.register_by_api(form, config=klant_config) if api_success: - send_confirmation = config.send_email_confirmation + send_confirmation = klant_config.send_email_confirmation # else keep the send_confirmation if email set it if send_confirmation: @@ -990,7 +996,15 @@ def register_by_email(self, form, recipient_email): ) return False - def register_by_api(self, form, config: ESuiteKlantConfig): + def register_by_api(self, form, config: KlantenSysteemConfig): + if config.primary_backend == KlantenServiceType.ESUITE.value: + return self._register_via_esuite(form, config=ESuiteKlantConfig.get_solo()) + return self._register_via_openklant2(form, config=OpenKlant2Config.get_solo()) + + def _register_via_openklant2(self, form, config: OpenKlant2Config): + pass + + def _register_via_esuite(self, form, config: ESuiteKlantConfig): assert config.has_api_configuration() try: diff --git a/src/open_inwoner/configurations/bootstrap/openklant.py b/src/open_inwoner/configurations/bootstrap/openklant.py index ea8eb8f9a..1c4d16ed0 100644 --- a/src/open_inwoner/configurations/bootstrap/openklant.py +++ b/src/open_inwoner/configurations/bootstrap/openklant.py @@ -9,7 +9,11 @@ from zgw_consumers.models import Service from open_inwoner.configurations.bootstrap.utils import get_service -from open_inwoner.openklant.models import ESuiteKlantConfig, OpenKlant2Config +from open_inwoner.openklant.models import ( + ESuiteKlantConfig, + KlantenSysteemConfig, + OpenKlant2Config, +) class OpenKlant2Configuration(ConfigurationModel): @@ -41,13 +45,22 @@ class KlantenApiConfigurationModel(ConfigurationModel): class Meta: django_model_refs = { ESuiteKlantConfig: ( - "register_email", - "register_contact_moment", "register_bronorganisatie_rsin", "register_channel", "register_type", "register_employee_id", "use_rsin_for_innNnpId_query_parameter", + ) + } + + +class KlantSysteemConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + KlantenSysteemConfig: ( + "primary_backend", + "register_contact_via_api", + "register_contact_email", "send_email_confirmation", ) } @@ -58,9 +71,9 @@ class ESuiteKlantConfigurationStep(BaseConfigurationStep[KlantenApiConfiguration Configure the KIC settings and set any feature flags or other options if specified """ - verbose_name = "Klantinteractie APIs configuration" - enable_setting = "openklant_config_enable" - namespace = "openklant_config" + verbose_name = "eSuite Klant APIs configuration" + enable_setting = "esuiteklant_config_enable" + namespace = "esuiteklant_config" config_model = KlantenApiConfigurationModel def execute(self, model: KlantenApiConfigurationModel): @@ -139,3 +152,10 @@ def execute(self, model: OpenKlant2Configuration): config.full_clean() config.save() + + +# TODO: complete config step +class KlantSysteemConfigurationStep( + BaseConfigurationStep[KlantSysteemConfigurationModel] +): + pass diff --git a/src/open_inwoner/configurations/tests/bootstrap/files/esuiteklant_config_step_full.yaml b/src/open_inwoner/configurations/tests/bootstrap/files/esuiteklant_config_step_full.yaml index b2dd801ee..bae7088b1 100644 --- a/src/open_inwoner/configurations/tests/bootstrap/files/esuiteklant_config_step_full.yaml +++ b/src/open_inwoner/configurations/tests/bootstrap/files/esuiteklant_config_step_full.yaml @@ -1,13 +1,10 @@ -openklant_config_enable: true -openklant_config: +esuiteklant_config_enable: true +esuiteklant_config: klanten_service_identifier: klanten-service contactmomenten_service_identifier: contactmomenten-service exclude_contactmoment_kanalen: [] - register_email: admin@oip.org - register_contact_moment: true register_bronorganisatie_rsin: '837194569' register_channel: email register_type: bericht register_employee_id: '1234' use_rsin_for_innNnpId_query_parameter: true - send_email_confirmation: false diff --git a/src/open_inwoner/configurations/tests/bootstrap/test_setup_openklant_config.py b/src/open_inwoner/configurations/tests/bootstrap/test_setup_openklant_config.py index 5cf64b619..18580abe7 100644 --- a/src/open_inwoner/configurations/tests/bootstrap/test_setup_openklant_config.py +++ b/src/open_inwoner/configurations/tests/bootstrap/test_setup_openklant_config.py @@ -43,8 +43,6 @@ def test_configure(self): self.assertEqual(config.klanten_service, kc) self.assertEqual(config.contactmomenten_service, cmc) - self.assertEqual(config.register_email, "admin@oip.org") - self.assertEqual(config.register_contact_moment, True) self.assertEqual(config.register_bronorganisatie_rsin, "837194569") self.assertEqual(config.register_channel, "email") self.assertEqual(config.register_type, "bericht") @@ -149,8 +147,8 @@ def assert_values(): self.assertEqual(config.klanten_service, kc) self.assertEqual(config.contactmomenten_service, cmc) - self.assertEqual(config.register_email, "admin@oip.org") - self.assertEqual(config.register_contact_moment, True) + # self.assertEqual(config.register_email, "admin@oip.org") + # self.assertEqual(config.register_contact_moment, True) self.assertEqual(config.register_bronorganisatie_rsin, "837194569") self.assertEqual(config.register_channel, "email") self.assertEqual(config.register_type, "bericht") @@ -164,8 +162,8 @@ def assert_values(): assert_values() config = ESuiteKlantConfig.get_solo() - config.register_email = "not-admin@oip.org" - config.register_contact_moment = False + # config.register_email = "not-admin@oip.org" + # config.register_contact_moment = False config.register_bronorganisatie_rsin = "800000009" config.register_channel = "not-email" config.register_type = "not-bericht" diff --git a/src/open_inwoner/openklant/admin.py b/src/open_inwoner/openklant/admin.py index 7fcfd2c91..b96ec6e42 100644 --- a/src/open_inwoner/openklant/admin.py +++ b/src/open_inwoner/openklant/admin.py @@ -157,7 +157,21 @@ class OpenKlant2ConfigAdmin(SingletonModelAdmin): @admin.register(KlantenSysteemConfig) class KlantenSysteemConfigAdmin(SingletonModelAdmin): + model = KlantenSysteemConfig change_form_template = "admin/openklant/klantensysteemconfig/change_form.html" - - class Meta: - model = KlantenSysteemConfig + fieldsets = [ + ( + _("API configuration globals"), + {"fields": ["primary_backend"]}, + ), + ( + _("Vragen/contactmomenten"), + { + "fields": [ + "register_contact_via_api", + "register_contact_email", + "send_email_confirmation", + ] + }, + ), + ] diff --git a/src/open_inwoner/openklant/migrations/0019_klantensysteemconfig_register_contact_email_and_more.py b/src/open_inwoner/openklant/migrations/0019_klantensysteemconfig_register_contact_email_and_more.py new file mode 100644 index 000000000..b4ca1aef7 --- /dev/null +++ b/src/open_inwoner/openklant/migrations/0019_klantensysteemconfig_register_contact_email_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.16 on 2025-01-14 13:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("openklant", "0018_rename_openklantconfig_esuiteklantconfig_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="klantensysteemconfig", + name="register_contact_email", + field=models.EmailField( + blank=True, + help_text="Contacts initiated or questions submitted by a client (e.g. via a contact form) will be registered via email.", + max_length=254, + verbose_name="Registreer vragen/contactmomenten op email adres", + ), + ), + migrations.AddField( + model_name="klantensysteemconfig", + name="register_contact_via_api", + field=models.BooleanField( + default=False, + help_text="Contacts initiated or questions submitted by a client (e.g. via a contact form) will be registered in the appropriate API (eSuite or OpenKlant2).", + verbose_name="Registreer vragen/contactmomenten op API", + ), + ), + migrations.AddField( + model_name="klantensysteemconfig", + name="send_email_confirmation", + field=models.BooleanField( + help_text="If enabled the 'contactform_confirmation' email template will be sent. If disabled the external API will send a confirmation email.", + verbose_name="Stuur contactformulier e-mailbevestiging", + default=False, + ), + ), + ] diff --git a/src/open_inwoner/openklant/migrations/0020_alter_klantensysteemconfig_primary_backend.py b/src/open_inwoner/openklant/migrations/0020_alter_klantensysteemconfig_primary_backend.py new file mode 100644 index 000000000..9a1649f91 --- /dev/null +++ b/src/open_inwoner/openklant/migrations/0020_alter_klantensysteemconfig_primary_backend.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.16 on 2025-01-14 13:18 + +from django.db import migrations, models + +import open_inwoner.openklant.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("openklant", "0019_klantensysteemconfig_register_contact_email_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="klantensysteemconfig", + name="primary_backend", + field=models.CharField( + choices=[("ESUITE", "esuite"), ("OPENKLANT2", "openklant2")], + help_text="Choose the primary backend for retrieving klanten data. Changes to klanten data will be saved to both backends (if configured).", + max_length=10, + validators=[open_inwoner.openklant.models.validate_primary_backend], + ), + ), + ] diff --git a/src/open_inwoner/openklant/migrations/0021_contactmoment_register_config.py b/src/open_inwoner/openklant/migrations/0021_contactmoment_register_config.py new file mode 100644 index 000000000..3976a9f87 --- /dev/null +++ b/src/open_inwoner/openklant/migrations/0021_contactmoment_register_config.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.16 on 2025-01-14 13:42 + +from django.db import migrations + + +def migrate_registration_defaults(apps, _): + ESuiteKlantConfig = apps.get_model("openklant", "ESuiteKlantConfig") + KlantenSysteemConfig = apps.get_model("openklant", "KlantenSysteemConfig") + + es_config = ESuiteKlantConfig.objects.first() + klanten_config = KlantenSysteemConfig.objects.first() + + if es_config and klanten_config: + klanten_config.register_contact_via_api = es_config.register_contact_moment + klanten_config.register_contact_email = es_config.register_email + klanten_config.send_email_confirmation = es_config.send_email_confirmation + klanten_config.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("openklant", "0020_alter_klantensysteemconfig_primary_backend"), + ] + + operations = [ + migrations.RunPython( + code=migrate_registration_defaults, + reverse_code=migrations.RunPython.noop, + ) + ] diff --git a/src/open_inwoner/openklant/migrations/0022_remove_esuiteklantconfig_register_contact_moment_and_more.py b/src/open_inwoner/openklant/migrations/0022_remove_esuiteklantconfig_register_contact_moment_and_more.py new file mode 100644 index 000000000..bf6cdabe4 --- /dev/null +++ b/src/open_inwoner/openklant/migrations/0022_remove_esuiteklantconfig_register_contact_moment_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.16 on 2025-01-14 13:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("openklant", "0021_contactmoment_register_config"), + ] + + operations = [ + migrations.RemoveField( + model_name="esuiteklantconfig", + name="register_contact_moment", + ), + migrations.RemoveField( + model_name="esuiteklantconfig", + name="register_email", + ), + migrations.RemoveField( + model_name="esuiteklantconfig", + name="send_email_confirmation", + ), + migrations.AlterField( + model_name="klantensysteemconfig", + name="register_contact_email", + field=models.EmailField( + blank=True, + help_text="Contacts initiated or questions submitted by a client (e.g. via a contact form) will be registered via email.", + max_length=254, + verbose_name="Registreer op email adres", + ), + ), + migrations.AlterField( + model_name="klantensysteemconfig", + name="register_contact_via_api", + field=models.BooleanField( + default=False, + help_text="Contacts initiated or questions submitted by a client (e.g. via a contact form) will be registered in the appropriate API (eSuite or OpenKlant2).", + verbose_name="Registreer op API", + ), + ), + ] diff --git a/src/open_inwoner/openklant/models.py b/src/open_inwoner/openklant/models.py index 9c0d4f53c..e20fdc86f 100644 --- a/src/open_inwoner/openklant/models.py +++ b/src/open_inwoner/openklant/models.py @@ -42,14 +42,6 @@ class ESuiteKlantConfig(SingletonModel): blank=True, ) - register_email = models.EmailField( - verbose_name=_("Registreer op email adres"), - blank=True, - ) - register_contact_moment = models.BooleanField( - verbose_name=_("Registreer in Contactmomenten API"), - default=False, - ) register_bronorganisatie_rsin = models.CharField( verbose_name=_("Organisatie RSIN"), max_length=9, @@ -89,14 +81,6 @@ class ESuiteKlantConfig(SingletonModel): ), default=False, ) - send_email_confirmation = models.BooleanField( - verbose_name=_("Stuur contactformulier e-mailbevestiging"), - help_text=_( - "If enabled the 'contactform_confirmation' email template will be sent. " - "If disabled the external API will send a confirmation email." - ), - default=False, - ) exclude_contactmoment_kanalen = ArrayField( base_field=models.CharField( blank=True, @@ -113,7 +97,6 @@ class ESuiteKlantConfig(SingletonModel): ) register_api_required_fields = ( - "register_contact_moment", "contactmomenten_service", "klanten_service", "register_bronorganisatie_rsin", @@ -125,11 +108,8 @@ class ESuiteKlantConfig(SingletonModel): class Meta: verbose_name = _("eSuite Klant configuration") - def has_register(self) -> bool: - return self.register_email or self.has_api_configuration() - def has_form_configuration(self) -> bool: - return self.has_register() and self.contactformsubject_set.exists() + return self.has_api_configuration() and self.contactformsubject_set.exists() def has_api_configuration(self): return all(getattr(self, f, "") for f in self.register_api_required_fields) @@ -254,16 +234,53 @@ def validate_primary_backend(value): class KlantenSysteemConfig(SingletonModel): primary_backend = models.CharField( max_length=10, - choices=[(service.value, service.name) for service in KlantenServiceType], + choices=[(service.name, service.value) for service in KlantenServiceType], help_text=_( "Choose the primary backend for retrieving klanten data. " "Changes to klanten data will be saved to both backends (if configured)." ), validators=[validate_primary_backend], ) + register_contact_via_api = models.BooleanField( + verbose_name=_("Registreer op API"), + default=False, + help_text=_( + "Contacts initiated or questions submitted by a client (e.g. via a contact form) will be " + "registered in the appropriate API (eSuite or OpenKlant2)." + ), + ) + register_contact_email = models.EmailField( + verbose_name=_("Registreer op email adres"), + blank=True, + help_text=_( + "Contacts initiated or questions submitted by a client (e.g. via a contact form) will be " + "registered via email." + ), + ) + send_email_confirmation = models.BooleanField( + verbose_name=_("Stuur contactformulier e-mailbevestiging"), + help_text=_( + "If enabled the 'contactform_confirmation' email template will be sent. " + "If disabled the external API will send a confirmation email." + ), + default=False, + ) class Meta: verbose_name = _("Configuratie Klanten Systeem") def __str__(self): return "Configuratie Klanten Systeem" + + @property + def has_api_configuration(self): + if self.primary_backend == KlantenServiceType.ESUITE.value: + esuite_config = ESuiteKlantConfig.get_solo() + return esuite_config.has_api_configuration() + + # TODO: support `has_api_configuration` check for OK2? + return True + + @property + def contact_registration_enabled(self) -> bool: + return self.register_contact_email or self.has_api_configuration