From fe161584802b8a656ceca2213e6665fef4f97360 Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Mon, 8 Jan 2024 16:04:19 +0100 Subject: [PATCH] :sparkles: [#2002] Implement Zaken notifications for companies task: https://taiga.maykinmedia.nl/project/open-inwoner/task/2002 --- src/open_inwoner/openzaak/notifications.py | 53 +++++++++++++--- .../openzaak/tests/test_notification_data.py | 62 ++++++++++++++++++- .../test_notification_zaak_infoobject.py | 42 +++++++++++++ 3 files changed, 149 insertions(+), 8 deletions(-) diff --git a/src/open_inwoner/openzaak/notifications.py b/src/open_inwoner/openzaak/notifications.py index dbd51e8a4d..a87a701c76 100644 --- a/src/open_inwoner/openzaak/notifications.py +++ b/src/open_inwoner/openzaak/notifications.py @@ -419,16 +419,55 @@ def get_np_initiator_bsns_from_roles(roles: List[Rol]) -> List[str]: return list(ret) +def get_nnp_initiator_nnp_id_from_roles(roles: List[Rol]) -> List[str]: + """ + iterate over Rollen and for all non-natural-person initiators return their nnpId + """ + ret = set() + + for role in roles: + if role.omschrijving_generiek not in ( + RolOmschrijving.initiator, + RolOmschrijving.medeinitiator, + ): + continue + if role.betrokkene_type != RolTypes.niet_natuurlijk_persoon: + continue + if not role.betrokkene_identificatie: + continue + nnp_id = role.betrokkene_identificatie.get("inn_nnp_id") + if not nnp_id: + continue + ret.add(nnp_id) + + return list(ret) + + def get_emailable_initiator_users_from_roles(roles: List[Rol]) -> List[User]: """ iterate over Rollen and return User objects for all natural-person initiators we can notify """ + users = [] + bsn_list = get_np_initiator_bsns_from_roles(roles) - if not bsn_list: - return [] - users = list( - User.objects.filter( - bsn__in=bsn_list, is_active=True, cases_notifications=True - ).having_usable_email() - ) + if bsn_list: + users += list( + User.objects.filter( + bsn__in=bsn_list, is_active=True, cases_notifications=True + ).having_usable_email() + ) + + nnp_id_list = get_nnp_initiator_nnp_id_from_roles(roles) + if nnp_id_list: + config = OpenZaakConfig.get_solo() + if config.fetch_eherkenning_zaken_with_rsin: + id_filter = {"rsin__in": nnp_id_list} + else: + id_filter = {"kvk__in": nnp_id_list} + users += list( + User.objects.filter( + is_active=True, cases_notifications=True, **id_filter + ).having_usable_email() + ) + return users diff --git a/src/open_inwoner/openzaak/tests/test_notification_data.py b/src/open_inwoner/openzaak/tests/test_notification_data.py index 99166b8f82..417f657a9d 100644 --- a/src/open_inwoner/openzaak/tests/test_notification_data.py +++ b/src/open_inwoner/openzaak/tests/test_notification_data.py @@ -8,7 +8,10 @@ from zgw_consumers.constants import APITypes from zgw_consumers.test import generate_oas_component, mock_service_oas_get -from open_inwoner.accounts.tests.factories import DigidUserFactory +from open_inwoner.accounts.tests.factories import ( + DigidUserFactory, + eHerkenningUserFactory, +) from open_inwoner.openzaak.tests.factories import ( NotificationFactory, ServiceFactory, @@ -54,6 +57,11 @@ def __init__(self): bsn="100000001", email="initiator@example.com", ) + self.eherkenning_user_initiator = eHerkenningUserFactory( + kvk="12345678", + rsin="000000000", + email="initiator_kvk@example.com", + ) self.zaak_type = generate_oas_component( "ztc", "schemas/ZaakType", @@ -93,6 +101,15 @@ def __init__(self): resultaat=f"{ZAKEN_ROOT}resultaten/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, ) + self.zaak2 = generate_oas_component( + "zrc", + "schemas/Zaak", + url=f"{ZAKEN_ROOT}zaken/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + zaaktype=self.zaak_type["url"], + status=f"{ZAKEN_ROOT}statussen/aaaaaaaa-aaaa-aaaa-aaaa-222222222222", + resultaat=f"{ZAKEN_ROOT}resultaten/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + ) self.status_initial = generate_oas_component( "zrc", "schemas/Status", @@ -123,6 +140,13 @@ def __init__(self): informatieobject=self.informatie_object["url"], zaak=self.zaak["url"], ) + self.zaak_informatie_object2 = generate_oas_component( + "zrc", + "schemas/ZaakInformatieObject", + url=f"{ZAKEN_ROOT}zaakinformatieobjecten/aaaaaaaa-0002-aaaa-aaaa-aaaaaaaaaaaa", + informatieobject=self.informatie_object["url"], + zaak=self.zaak2["url"], + ) self.role_initiator = generate_oas_component( "zrc", @@ -134,8 +158,32 @@ def __init__(self): "inpBsn": self.user_initiator.bsn, }, ) + self.eherkenning_role_initiator = generate_oas_component( + "zrc", + "schemas/Rol", + url=f"{ZAKEN_ROOT}rollen/aaaaaaaa-0002-aaaa-aaaa-aaaaaaaaaaaa", + omschrijvingGeneriek=RolOmschrijving.initiator, + betrokkeneType=RolTypes.niet_natuurlijk_persoon, + betrokkeneIdentificatie={ + "innNnpId": self.eherkenning_user_initiator.kvk, + }, + ) + self.eherkenning_role_initiator2 = generate_oas_component( + "zrc", + "schemas/Rol", + url=f"{ZAKEN_ROOT}rollen/aaaaaaaa-0003-aaaa-aaaa-aaaaaaaaaaaa", + omschrijvingGeneriek=RolOmschrijving.initiator, + betrokkeneType=RolTypes.niet_natuurlijk_persoon, + betrokkeneIdentificatie={ + "innNnpId": self.eherkenning_user_initiator.rsin, + }, + ) self.case_roles = [self.role_initiator] + self.eherkenning_case_roles = [ + self.eherkenning_role_initiator, + self.eherkenning_role_initiator2, + ] self.status_history = [self.status_initial, self.status_final] self.status_notification = NotificationFactory( @@ -150,6 +198,12 @@ def __init__(self): resource_url=self.zaak_informatie_object["url"], hoofd_object=self.zaak["url"], ) + self.zio_notification2 = NotificationFactory( + resource="zaakinformatieobject", + actie="create", + resource_url=self.zaak_informatie_object2["url"], + hoofd_object=self.zaak2["url"], + ) def setUpOASMocks(self, m): mock_service_oas_get(m, ZAKEN_ROOT, "zrc") @@ -171,6 +225,10 @@ def install_mocks(self, m, *, res404: Optional[List[str]] = None) -> "MockAPIDat f"{ZAKEN_ROOT}rollen?zaak={self.zaak['url']}", json=paginated_response(self.case_roles), ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak2['url']}", + json=paginated_response(self.eherkenning_case_roles), + ) if "status_history" in res404: m.get(f"{ZAKEN_ROOT}statussen?zaak={self.zaak['url']}", status_code=404) @@ -182,6 +240,7 @@ def install_mocks(self, m, *, res404: Optional[List[str]] = None) -> "MockAPIDat for resource_attr in [ "zaak", + "zaak2", "zaak_type", "status_initial", "status_final", @@ -190,6 +249,7 @@ def install_mocks(self, m, *, res404: Optional[List[str]] = None) -> "MockAPIDat "status_type_final", "informatie_object", "zaak_informatie_object", + "zaak_informatie_object2", ]: resource = getattr(self, resource_attr) if resource_attr in res404: diff --git a/src/open_inwoner/openzaak/tests/test_notification_zaak_infoobject.py b/src/open_inwoner/openzaak/tests/test_notification_zaak_infoobject.py index c866f9992f..95f864486c 100644 --- a/src/open_inwoner/openzaak/tests/test_notification_zaak_infoobject.py +++ b/src/open_inwoner/openzaak/tests/test_notification_zaak_infoobject.py @@ -71,6 +71,48 @@ def test_zio_handle_zaken_notification(self, m, mock_handle: Mock): level=logging.INFO, ) + def test_zio_handle_zaken_notification_niet_natuurlijk_persoon_initiator( + self, m, mock_handle: Mock + ): + """ + happy-flow from valid data calls the (mocked) handle_zaakinformatieobject() for + niet natuurlijk persoon initiator + """ + data = MockAPIData().install_mocks(m) + + ZaakTypeInformatieObjectTypeConfigFactory.from_case_type_info_object_dicts( + data.zaak_type, data.informatie_object, document_notification_enabled=True + ) + + config = OpenZaakConfig.get_solo() + for fetch_eherkenning_zaken_with_rsin in [True, False]: + with self.subTest( + fetch_eherkenning_zaken_with_rsin=fetch_eherkenning_zaken_with_rsin + ): + mock_handle.reset_mock() + self.clearTimelineLogs() + + config.fetch_eherkenning_zaken_with_rsin = ( + fetch_eherkenning_zaken_with_rsin + ) + config.save() + + handle_zaken_notification(data.zio_notification2) + + mock_handle.assert_called_once() + + # check call arguments + args = mock_handle.call_args.args + self.assertEqual(args[0], data.eherkenning_user_initiator) + self.assertEqual(args[1].url, data.zaak2["url"]) + self.assertEqual(args[2].url, data.zaak_informatie_object2["url"]) + + self.assertTimelineLog( + "accepted zaakinformatieobject notification: attempt informing users ", + lookup=Lookups.startswith, + level=logging.INFO, + ) + # start of generic checks def test_zio_bails_when_bad_notification_channel(self, m, mock_handle: Mock):