From 06227ab3f2ebdf3f90aedb84349cf9ad93724ea8 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Wed, 11 Oct 2023 15:38:19 +0200 Subject: [PATCH 1/2] [#1799] Create cms plugin for mijn aanvragen --- src/open_inwoner/cms/cases/cms_plugins.py | 42 ++++ .../cms/cases/tests/test_plugin_cases.py | 34 +++ src/open_inwoner/cms/products/cms_plugins.py | 8 +- src/open_inwoner/cms/utils/plugin_mixins.py | 10 +- src/open_inwoner/conf/base.py | 1 + src/open_inwoner/openzaak/tests/mixins.py | 202 ++++++++++++++++++ .../templates/cms/cases/cases_plugin.html | 31 +++ 7 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 src/open_inwoner/cms/cases/cms_plugins.py create mode 100644 src/open_inwoner/cms/cases/tests/test_plugin_cases.py create mode 100644 src/open_inwoner/openzaak/tests/mixins.py create mode 100644 src/open_inwoner/templates/cms/cases/cases_plugin.html diff --git a/src/open_inwoner/cms/cases/cms_plugins.py b/src/open_inwoner/cms/cases/cms_plugins.py new file mode 100644 index 0000000000..1d18eb40ab --- /dev/null +++ b/src/open_inwoner/cms/cases/cms_plugins.py @@ -0,0 +1,42 @@ +from django.utils.translation import ugettext_lazy as _ + +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool + +# TODO: import `preprocess_data` after merge of PR 804 +from open_inwoner.openzaak.cases import fetch_cases +from open_inwoner.openzaak.formapi import fetch_open_submissions + +from ..utils.plugin_mixins import CMSActiveAppMixin + + +# TODO: refactor after merge of PR 804 +@plugin_pool.register_plugin +class CasesPlugin(CMSActiveAppMixin, CMSPluginBase): + module = _("Openzaak") + name = _("Cases Plugin") + render_template = "cms/cases/cases_plugin.html" + app_hook = "CasesApphook" + cache = False + + limit = 4 + + def render(self, context, instance, placeholder): + request = context["request"] + + raw_cases = [ + case for case in fetch_cases(request.user.bsn) if not case.einddatum + ] + # TODO + # preprocessed_cases = preprocess_data(raw_cases) + + subs = fetch_open_submissions(request.user.bsn) + + # replce raw_cases with preprocessed_cases + all_cases = raw_cases + subs + + # processed_cases = [case.preprocess_data() for case in all_cases] + + context["cases"] = all_cases[: self.limit] + + return context diff --git a/src/open_inwoner/cms/cases/tests/test_plugin_cases.py b/src/open_inwoner/cms/cases/tests/test_plugin_cases.py new file mode 100644 index 0000000000..acda5f4a52 --- /dev/null +++ b/src/open_inwoner/cms/cases/tests/test_plugin_cases.py @@ -0,0 +1,34 @@ +from unittest.mock import patch + +from django.test import TestCase, override_settings + +import requests_mock + +from open_inwoner.openzaak.tests.mixins import ZakenTestMixin +from open_inwoner.utils.test import ClearCachesMixin + +from ...tests import cms_tools +from ..cms_apps import CasesApphook +from ..cms_plugins import CasesPlugin + + +@requests_mock.Mocker() +@patch.object(CasesPlugin, "limit", 2) +@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") +class CasesPluginTest(ZakenTestMixin, ClearCachesMixin, TestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + cms_tools.create_apphook_page(CasesApphook) + + def test_cms_plugin_cases_are_rendered(self, m): + self._setUpMocks(m) + + html, context = cms_tools.render_plugin(CasesPlugin, user=self.user) + + cases = context["cases"] + + self.assertEqual(len(cases), 2) + + self.assertEqual(cases[0].omschrijving, "Coffee zaak 1") + self.assertEqual(cases[1].omschrijving, "Coffee zaak 2") diff --git a/src/open_inwoner/cms/products/cms_plugins.py b/src/open_inwoner/cms/products/cms_plugins.py index 9b423f0aa1..83685d6198 100644 --- a/src/open_inwoner/cms/products/cms_plugins.py +++ b/src/open_inwoner/cms/products/cms_plugins.py @@ -3,6 +3,7 @@ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool +from open_inwoner.openzaak.cases import fetch_cases from open_inwoner.pdc.forms import ProductFinderForm from open_inwoner.pdc.models import Category, ProductCondition, ProductLocation from open_inwoner.questionnaire.models import QuestionnaireStep @@ -14,7 +15,8 @@ class CategoriesPlugin(CMSActiveAppMixin, CMSPluginBase): module = _("PDC") name = _("Categories Plugin") - render_template = "cms/products/categories_plugin.html" + # render_template = "cms/products/categories_plugin.html" + render_template = "cms/cases/cases_plugin.html" app_hook = "ProductsApphook" cache = False @@ -40,6 +42,10 @@ def render(self, context, instance, placeholder): context["categories"] = categories + # from cms.models import Page + # pages = Page.objects.published().all() + # import pdb;pdb.set_trace() + return context diff --git a/src/open_inwoner/cms/utils/plugin_mixins.py b/src/open_inwoner/cms/utils/plugin_mixins.py index 608e1a524b..20788e368b 100644 --- a/src/open_inwoner/cms/utils/plugin_mixins.py +++ b/src/open_inwoner/cms/utils/plugin_mixins.py @@ -1,12 +1,18 @@ from cms.models import Page -from cms.plugin_base import CMSPluginBase class CMSActiveAppMixin: + """ + Render only plugins for active CMS apps + """ + app_hook = None @property - def render_plugin(self): + def render_plugin(self) -> bool: + """ + Return `True` if plugin should be rendered, `False` otherwise + """ if self.app_hook is None: raise ValueError(f"Apphook for plugin '{self.name}' is not defined") diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 17fadf7c0c..925a5988d5 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -535,6 +535,7 @@ "content": { "plugins": [ # "TextPlugin", + "CasesPlugin", "PicturePlugin", "VideoPlayerPlugin", "CategoriesPlugin", diff --git a/src/open_inwoner/openzaak/tests/mixins.py b/src/open_inwoner/openzaak/tests/mixins.py new file mode 100644 index 0000000000..e6e26e17b0 --- /dev/null +++ b/src/open_inwoner/openzaak/tests/mixins.py @@ -0,0 +1,202 @@ +from furl import furl +from zgw_consumers.api_models.constants import VertrouwelijkheidsAanduidingen +from zgw_consumers.constants import APITypes +from zgw_consumers.test import generate_oas_component, mock_service_oas_get + +from open_inwoner.accounts.choices import LoginTypeChoices +from open_inwoner.accounts.tests.factories import UserFactory +from open_inwoner.openzaak.models import OpenZaakConfig +from open_inwoner.openzaak.tests.factories import ServiceFactory +from open_inwoner.openzaak.tests.shared import CATALOGI_ROOT, ZAKEN_ROOT +from open_inwoner.utils.test import paginated_response + + +class ZakenTestMixin: + """ + Set up data required by tests that interact with the Zaken API: + - digid user + - services (zaken, catalogi) + - zaken, zaak types, status types + - request mocks + + Note: the inheriting class must be decorated with requests_mock.Mocker() to make + the call to _setUpMocks work + """ + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + cls.user = UserFactory( + login_type=LoginTypeChoices.digid, bsn="900222086", email="johm@smith.nl" + ) + # services + cls.zaak_service = ServiceFactory(api_root=ZAKEN_ROOT, api_type=APITypes.zrc) + cls.catalogi_service = ServiceFactory( + api_root=CATALOGI_ROOT, api_type=APITypes.ztc + ) + # openzaak config + cls.config = OpenZaakConfig.get_solo() + cls.config.zaak_service = cls.zaak_service + cls.config.catalogi_service = cls.catalogi_service + cls.config.zaak_max_confidentiality = ( + VertrouwelijkheidsAanduidingen.beperkt_openbaar + ) + cls.config.save() + + cls.zaaktype = generate_oas_component( + "ztc", + "schemas/ZaakType", + url=f"{CATALOGI_ROOT}zaaktypen/53340e34-7581-4b04-884f", + omschrijving="Coffee zaaktype", + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + indicatieInternOfExtern="extern", + ) + cls.zaak_type_intern = generate_oas_component( + "ztc", + "schemas/ZaakType", + url=f"{CATALOGI_ROOT}zaaktypen/53340e34-75a1-4b04-1234", + omschrijving="Intern zaaktype", + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + indicatieInternOfExtern="intern", + ) + cls.status_type1 = generate_oas_component( + "ztc", + "schemas/StatusType", + url=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-777yu878km09", + zaaktype=cls.zaaktype["url"], + omschrijving="Initial request", + volgnummer=1, + isEindstatus=False, + ) + cls.status_type2 = generate_oas_component( + "ztc", + "schemas/StatusType", + url=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-744516671fe4", + zaaktype=cls.zaaktype["url"], + omschrijving="Finish", + volgnummer=2, + isEindstatus=True, + ) + # open + cls.zaak1 = generate_oas_component( + "zrc", + "schemas/Zaak", + url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", + uuid="d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", + zaaktype=cls.zaaktype["url"], + identificatie="ZAAK-2022-0000000001", + omschrijving="Coffee zaak 1", + startdatum="2022-01-02", + einddatum=None, + status=f"{ZAKEN_ROOT}statussen/3da89990-c7fc-476a-ad13-c9023450083c", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + ) + cls.status1 = generate_oas_component( + "zrc", + "schemas/Status", + url=cls.zaak1["status"], + zaak=cls.zaak1["url"], + statustype=cls.status_type1["url"], + datumStatusGezet="2021-01-12", + statustoelichting="", + ) + cls.zaak2 = generate_oas_component( + "zrc", + "schemas/Zaak", + url=f"{ZAKEN_ROOT}zaken/e4d469b9-6666-4bdd-bf42-b53445298102", + uuid="e4d469b9-6666-4bdd-bf42-b53445298102", + zaaktype=cls.zaaktype["url"], + identificatie="ZAAK-2022-0008800002", + omschrijving="Coffee zaak 2", + startdatum="2022-01-12", + einddatum=None, + status=f"{ZAKEN_ROOT}statussen/3da81560-c7fc-476a-ad13-beu760sle929", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + ) + cls.status2 = generate_oas_component( + "zrc", + "schemas/Status", + url=cls.zaak2["status"], + zaak=cls.zaak2["url"], + statustype=cls.status_type1["url"], + datumStatusGezet="2021-03-12", + statustoelichting="", + ) + # closed + cls.zaak3 = generate_oas_component( + "zrc", + "schemas/Zaak", + url=f"{ZAKEN_ROOT}zaken/6f8de38f-85ea-42d3-978c-845a033335a7", + uuid="6f8de38f-85ea-42d3-978c-845a033335a7", + zaaktype=cls.zaaktype["url"], + identificatie="ZAAK-2022-0001000003", + omschrijving="Coffee zaak closed", + startdatum="2021-07-26", + einddatum="2022-01-16", + status=f"{ZAKEN_ROOT}statussen/98659876-bbb3-476a-ad13-n3nvcght758js", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + ) + cls.status3 = generate_oas_component( + "zrc", + "schemas/Status", + url=cls.zaak3["status"], + zaak=cls.zaak3["url"], + statustype=cls.status_type2["url"], + datumStatusGezet="2021-03-15", + statustoelichting="", + ) + # not visible + cls.zaak_intern = generate_oas_component( + "zrc", + "schemas/Zaak", + url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4ee0bf67d", + uuid="d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", + zaaktype=cls.zaak_type_intern["url"], + identificatie="ZAAK-2022-0000000009", + omschrijving="Intern zaak", + startdatum="2022-01-02", + einddatum=None, + status=f"{ZAKEN_ROOT}statussen/3da89990-c7fc-476a-ad13-c90234500333", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + ) + cls.status_intern = generate_oas_component( + "zrc", + "schemas/Status", + url=cls.zaak_intern["status"], + zaak=cls.zaak_intern["url"], + statustype=cls.status_type1["url"], + datumStatusGezet="2021-01-12", + statustoelichting="", + ) + + def _setUpMocks(self, m): + mock_service_oas_get(m, ZAKEN_ROOT, "zrc") + mock_service_oas_get(m, CATALOGI_ROOT, "ztc") + m.get( + furl(f"{ZAKEN_ROOT}zaken") + .add( + { + "rol__betrokkeneIdentificatie__natuurlijkPersoon__inpBsn": self.user.bsn, + "maximaleVertrouwelijkheidaanduiding": VertrouwelijkheidsAanduidingen.beperkt_openbaar, + } + ) + .url, + json=paginated_response( + [self.zaak1, self.zaak2, self.zaak3, self.zaak_intern] + ), + ) + for resource in [ + self.zaaktype, + self.status_type1, + self.status_type2, + self.status1, + self.status2, + self.status3, + self.zaak_intern, + self.status_intern, + self.zaak_type_intern, + ]: + m.get(resource["url"], json=resource) diff --git a/src/open_inwoner/templates/cms/cases/cases_plugin.html b/src/open_inwoner/templates/cms/cases/cases_plugin.html new file mode 100644 index 0000000000..6544e0cd79 --- /dev/null +++ b/src/open_inwoner/templates/cms/cases/cases_plugin.html @@ -0,0 +1,31 @@ +{% load i18n anchor_menu_tags grid_tags icon_tags list_tags pagination_tags utils %} + +
+ {% anchor_menu anchors desktop=True %} +
+ +

{{ page_title }} ({{ paginator.count }})

+{% render_grid %} + + {% for case in cases %} +
+
+

{{ title }}

+

{{ description }}

+ +
+
{% trans "Einddatum" %}:
+
{{ case }}
+
{% trans "Created by" %}:
+
{{ object }}
+
+ + + + {% icon icon="arrow_forward" icon_position="after" primary=True outlined=True %} + + +
+ + {% endfor %} +{% endrender_grid %} From 00979acbd55dcd742d5720ade88cbdf2c5fc2743 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Wed, 18 Oct 2023 16:24:53 +0200 Subject: [PATCH 2/2] [#1799] Add tests for cms cases-plugin --- src/open_inwoner/cms/cases/cms_plugins.py | 15 +- .../cms/cases/tests/test_plugin_cases.py | 56 ++- src/open_inwoner/cms/products/cms_plugins.py | 8 +- src/open_inwoner/cms/utils/auth.py | 22 + src/open_inwoner/openzaak/tests/mixins.py | 441 ++++++++++++++---- .../templates/cms/cases/cases_plugin.html | 81 ++-- 6 files changed, 482 insertions(+), 141 deletions(-) create mode 100644 src/open_inwoner/cms/utils/auth.py diff --git a/src/open_inwoner/cms/cases/cms_plugins.py b/src/open_inwoner/cms/cases/cms_plugins.py index 1d18eb40ab..be1abf39c0 100644 --- a/src/open_inwoner/cms/cases/cms_plugins.py +++ b/src/open_inwoner/cms/cases/cms_plugins.py @@ -7,6 +7,7 @@ from open_inwoner.openzaak.cases import fetch_cases from open_inwoner.openzaak.formapi import fetch_open_submissions +from ..utils.auth import check_user_access_rights, check_user_auth from ..utils.plugin_mixins import CMSActiveAppMixin @@ -23,10 +24,18 @@ class CasesPlugin(CMSActiveAppMixin, CMSPluginBase): def render(self, context, instance, placeholder): request = context["request"] + user = request.user + + if not check_user_auth(user, digid_required=True): + context["cases"] = None + return context + + raw_cases = [case for case in fetch_cases(user.bsn) if not case.einddatum] + + if not all(check_user_access_rights(user, case.url) for case in raw_cases): + context["cases"] = None + return context - raw_cases = [ - case for case in fetch_cases(request.user.bsn) if not case.einddatum - ] # TODO # preprocessed_cases = preprocess_data(raw_cases) diff --git a/src/open_inwoner/cms/cases/tests/test_plugin_cases.py b/src/open_inwoner/cms/cases/tests/test_plugin_cases.py index acda5f4a52..3d9093ec94 100644 --- a/src/open_inwoner/cms/cases/tests/test_plugin_cases.py +++ b/src/open_inwoner/cms/cases/tests/test_plugin_cases.py @@ -2,8 +2,12 @@ from django.test import TestCase, override_settings +import lxml import requests_mock +from pyquery import PyQuery as pq +from open_inwoner.accounts.choices import LoginTypeChoices +from open_inwoner.accounts.tests.factories import UserFactory from open_inwoner.openzaak.tests.mixins import ZakenTestMixin from open_inwoner.utils.test import ClearCachesMixin @@ -21,14 +25,62 @@ def setUpTestData(cls): super().setUpTestData() cms_tools.create_apphook_page(CasesApphook) - def test_cms_plugin_cases_are_rendered(self, m): - self._setUpMocks(m) + def test_cms_plugin_cases_not_rendered_for_anonymous_user(self, m): + self.setUpMocks(m) + # anonymous user + html, context = cms_tools.render_plugin(CasesPlugin) + + self.assertIsNone(context["cases"]) + + # check that html empty + with self.assertRaises(lxml.etree.ParserError) as ctx: + pq(html) + + self.assertEqual(str(ctx.exception), "Document is empty") + + def test_cms_plugin_cases_not_rendered_for_non_digid_user(self, m): + self.setUpMocks(m) + + user = UserFactory() + user.login_type = LoginTypeChoices.default + user.save() + + html, context = cms_tools.render_plugin(CasesPlugin, user=user) + + self.assertIsNone(context["cases"]) + + # check that html empty + with self.assertRaises(lxml.etree.ParserError) as ctx: + pq(html) + + self.assertEqual(str(ctx.exception), "Document is empty") + + def test_cms_plugin_cases_rendered(self, m): + self.setUpMocks(m) + self.setUpMocksExtra(m) # create additional zaken + + # the ZakenTestMixin user is a digid user html, context = cms_tools.render_plugin(CasesPlugin, user=self.user) cases = context["cases"] + # check that limiting display works (ZakenTestMixin creates 3 zaken) self.assertEqual(len(cases), 2) self.assertEqual(cases[0].omschrijving, "Coffee zaak 1") self.assertEqual(cases[1].omschrijving, "Coffee zaak 2") + + # check html + doc = pq(html) + + case_descriptions = doc.find(".h4").find("span") + + for case_description, case in zip(case_descriptions, cases): + self.assertEqual(case_description.text, case.omschrijving) + + case_link_paths = (case.url.rsplit("/", 1)[-1] for case in cases) + html_case_links = doc.find("a") + + for html_link, path in zip(html_case_links, case_link_paths): + self.assertEqual(html_link.attrib["href"], f"/cases/{path}/status/") diff --git a/src/open_inwoner/cms/products/cms_plugins.py b/src/open_inwoner/cms/products/cms_plugins.py index 83685d6198..9b423f0aa1 100644 --- a/src/open_inwoner/cms/products/cms_plugins.py +++ b/src/open_inwoner/cms/products/cms_plugins.py @@ -3,7 +3,6 @@ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool -from open_inwoner.openzaak.cases import fetch_cases from open_inwoner.pdc.forms import ProductFinderForm from open_inwoner.pdc.models import Category, ProductCondition, ProductLocation from open_inwoner.questionnaire.models import QuestionnaireStep @@ -15,8 +14,7 @@ class CategoriesPlugin(CMSActiveAppMixin, CMSPluginBase): module = _("PDC") name = _("Categories Plugin") - # render_template = "cms/products/categories_plugin.html" - render_template = "cms/cases/cases_plugin.html" + render_template = "cms/products/categories_plugin.html" app_hook = "ProductsApphook" cache = False @@ -42,10 +40,6 @@ def render(self, context, instance, placeholder): context["categories"] = categories - # from cms.models import Page - # pages = Page.objects.published().all() - # import pdb;pdb.set_trace() - return context diff --git a/src/open_inwoner/cms/utils/auth.py b/src/open_inwoner/cms/utils/auth.py new file mode 100644 index 0000000000..0f6c0c3e4b --- /dev/null +++ b/src/open_inwoner/cms/utils/auth.py @@ -0,0 +1,22 @@ +import logging + +from open_inwoner.openzaak.cases import fetch_roles_for_case_and_bsn + +logger = logging.getLogger(__name__) + + +def check_user_auth(user, digid_required: bool = False) -> bool: + if not user.is_authenticated: + logger.debug("Permission denied: user not authenticated") + return False + if digid_required and not getattr(user, "bsn", None): + logger.debug("Permission denied: user has no BSN") + return False + return True + + +def check_user_access_rights(user, case_url) -> bool: + if not fetch_roles_for_case_and_bsn(case_url, user.bsn): + f"Permission denied: no role for the case {case_url}" + return False + return True diff --git a/src/open_inwoner/openzaak/tests/mixins.py b/src/open_inwoner/openzaak/tests/mixins.py index e6e26e17b0..16675ce25d 100644 --- a/src/open_inwoner/openzaak/tests/mixins.py +++ b/src/open_inwoner/openzaak/tests/mixins.py @@ -1,26 +1,35 @@ -from furl import furl -from zgw_consumers.api_models.constants import VertrouwelijkheidsAanduidingen +from django.urls import reverse + +from zgw_consumers.api_models.constants import ( + RolOmschrijving, + RolTypes, + VertrouwelijkheidsAanduidingen, +) from zgw_consumers.constants import APITypes from zgw_consumers.test import generate_oas_component, mock_service_oas_get from open_inwoner.accounts.choices import LoginTypeChoices from open_inwoner.accounts.tests.factories import UserFactory +from open_inwoner.cms.cases.views.status import SimpleFile from open_inwoner.openzaak.models import OpenZaakConfig from open_inwoner.openzaak.tests.factories import ServiceFactory -from open_inwoner.openzaak.tests.shared import CATALOGI_ROOT, ZAKEN_ROOT +from open_inwoner.openzaak.tests.shared import ( + CATALOGI_ROOT, + DOCUMENTEN_ROOT, + ZAKEN_ROOT, +) from open_inwoner.utils.test import paginated_response class ZakenTestMixin: """ - Set up data required by tests that interact with the Zaken API: + Create data required for tests that interact with the Zaken API: - digid user - - services (zaken, catalogi) - - zaken, zaak types, status types - - request mocks + - services (catalogi, documenti) + - zaken (including zaak type, status, rol) - Note: the inheriting class must be decorated with requests_mock.Mocker() to make - the call to _setUpMocks work + Note: the inheriting class must be decorated with `requests_mock.Mocker()` + in order for the call to `setUpMocks` to work """ @classmethod @@ -35,80 +44,63 @@ def setUpTestData(cls): cls.catalogi_service = ServiceFactory( api_root=CATALOGI_ROOT, api_type=APITypes.ztc ) + cls.document_service = ServiceFactory( + api_root=DOCUMENTEN_ROOT, api_type=APITypes.drc + ) # openzaak config cls.config = OpenZaakConfig.get_solo() cls.config.zaak_service = cls.zaak_service cls.config.catalogi_service = cls.catalogi_service + cls.config.document_service = cls.document_service + cls.config.document_max_confidentiality = ( + VertrouwelijkheidsAanduidingen.beperkt_openbaar + ) cls.config.zaak_max_confidentiality = ( VertrouwelijkheidsAanduidingen.beperkt_openbaar ) cls.config.save() - cls.zaaktype = generate_oas_component( - "ztc", - "schemas/ZaakType", - url=f"{CATALOGI_ROOT}zaaktypen/53340e34-7581-4b04-884f", - omschrijving="Coffee zaaktype", - catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", - vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, - indicatieInternOfExtern="extern", - ) - cls.zaak_type_intern = generate_oas_component( - "ztc", - "schemas/ZaakType", - url=f"{CATALOGI_ROOT}zaaktypen/53340e34-75a1-4b04-1234", - omschrijving="Intern zaaktype", - catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", - vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, - indicatieInternOfExtern="intern", - ) - cls.status_type1 = generate_oas_component( - "ztc", - "schemas/StatusType", - url=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-777yu878km09", - zaaktype=cls.zaaktype["url"], - omschrijving="Initial request", - volgnummer=1, - isEindstatus=False, - ) - cls.status_type2 = generate_oas_component( - "ztc", - "schemas/StatusType", - url=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-744516671fe4", - zaaktype=cls.zaaktype["url"], - omschrijving="Finish", - volgnummer=2, - isEindstatus=True, + cls.case_detail_url = reverse( + "cases:case_detail_content", + kwargs={"object_id": "d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d"}, ) - # open - cls.zaak1 = generate_oas_component( + + # openzaak resources + cls.zaak = generate_oas_component( "zrc", "schemas/Zaak", - url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", uuid="d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", - zaaktype=cls.zaaktype["url"], - identificatie="ZAAK-2022-0000000001", + url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", + zaaktype=f"{CATALOGI_ROOT}zaaktypen/0caa29cb-0167-426f-8dc1-88bebd7c8804", + identificatie="ZAAK-2022-0000000024", omschrijving="Coffee zaak 1", startdatum="2022-01-02", - einddatum=None, + einddatumGepland="2022-01-04", + uiterlijkeEinddatumAfdoening="2022-01-05", status=f"{ZAKEN_ROOT}statussen/3da89990-c7fc-476a-ad13-c9023450083c", + resultaat=f"{ZAKEN_ROOT}resultaten/a44153aa-ad2c-6a07-be75-15add5113", vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, ) - cls.status1 = generate_oas_component( + cls.zaak_invisible = generate_oas_component( "zrc", - "schemas/Status", - url=cls.zaak1["status"], - zaak=cls.zaak1["url"], - statustype=cls.status_type1["url"], - datumStatusGezet="2021-01-12", - statustoelichting="", + "schemas/Zaak", + uuid="213b0a04-fcbc-4fee-8d11-cf950a0a0bbb", + url=f"{ZAKEN_ROOT}zaken/213b0a04-fcbc-4fee-8d11-cf950a0a0bbb", + zaaktype=cls.zaak["zaaktype"], + identificatie="ZAAK-2022-invisible", + omschrijving="Zaak invisible", + startdatum="2022-01-02", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.geheim, ) + + # additional zaken cls.zaak2 = generate_oas_component( "zrc", "schemas/Zaak", url=f"{ZAKEN_ROOT}zaken/e4d469b9-6666-4bdd-bf42-b53445298102", uuid="e4d469b9-6666-4bdd-bf42-b53445298102", - zaaktype=cls.zaaktype["url"], + zaaktype=f"{CATALOGI_ROOT}zaaktypen/0caa29cb-0167-426f-8dc1-88bebd7c8804", + # zaaktype=cls.zaaktype["url"], identificatie="ZAAK-2022-0008800002", omschrijving="Coffee zaak 2", startdatum="2022-01-12", @@ -116,22 +108,14 @@ def setUpTestData(cls): status=f"{ZAKEN_ROOT}statussen/3da81560-c7fc-476a-ad13-beu760sle929", vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, ) - cls.status2 = generate_oas_component( - "zrc", - "schemas/Status", - url=cls.zaak2["status"], - zaak=cls.zaak2["url"], - statustype=cls.status_type1["url"], - datumStatusGezet="2021-03-12", - statustoelichting="", - ) # closed cls.zaak3 = generate_oas_component( "zrc", "schemas/Zaak", url=f"{ZAKEN_ROOT}zaken/6f8de38f-85ea-42d3-978c-845a033335a7", uuid="6f8de38f-85ea-42d3-978c-845a033335a7", - zaaktype=cls.zaaktype["url"], + zaaktype=f"{CATALOGI_ROOT}zaaktypen/0caa29cb-0167-426f-8dc1-88bebd7c8804", + # zaaktype=cls.zaaktype["url"], identificatie="ZAAK-2022-0001000003", omschrijving="Coffee zaak closed", startdatum="2021-07-26", @@ -139,42 +123,207 @@ def setUpTestData(cls): status=f"{ZAKEN_ROOT}statussen/98659876-bbb3-476a-ad13-n3nvcght758js", vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, ) - cls.status3 = generate_oas_component( + + cls.zaaktype = generate_oas_component( + "ztc", + "schemas/ZaakType", + uuid="0caa29cb-0167-426f-8dc1-88bebd7c8804", + url=cls.zaak["zaaktype"], + identificatie="ZAAKTYPE-2020-0000000001", + omschrijving="Coffee zaaktype", + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + doel="Ask for coffee", + aanleiding="Coffee is essential", + indicatieInternOfExtern="extern", + handelingInitiator="Request", + onderwerp="Coffee", + handelingBehandelaar="Behandelen", + opschortingEnAanhoudingMogelijk=False, + verlengingMogelijk=False, + publicatieIndicatie=False, + besluittypen=[], + beginGeldigheid="2020-09-25", + versiedatum="2020-09-25", + ) + cls.status_new = generate_oas_component( "zrc", "schemas/Status", - url=cls.zaak3["status"], - zaak=cls.zaak3["url"], - statustype=cls.status_type2["url"], - datumStatusGezet="2021-03-15", + url=f"{ZAKEN_ROOT}statussen/3da81560-c7fc-476a-ad13-beu760sle929", + zaak=cls.zaak["url"], + statustype=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-777yu878km09", + datumStatusGezet="2021-01-12", statustoelichting="", ) - # not visible - cls.zaak_intern = generate_oas_component( + cls.status_finish = generate_oas_component( "zrc", - "schemas/Zaak", - url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4ee0bf67d", - uuid="d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d", - zaaktype=cls.zaak_type_intern["url"], - identificatie="ZAAK-2022-0000000009", - omschrijving="Intern zaak", - startdatum="2022-01-02", - einddatum=None, - status=f"{ZAKEN_ROOT}statussen/3da89990-c7fc-476a-ad13-c90234500333", + "schemas/Status", + url=f"{ZAKEN_ROOT}statussen/3da89990-c7fc-476a-ad13-c9023450083c", + zaak=cls.zaak["url"], + statustype=f"{CATALOGI_ROOT}statustypen/e3798107-ab27-4c3c-977d-744516671fe4", + datumStatusGezet="2021-03-12", + statustoelichting="", + ) + cls.status_type_new = generate_oas_component( + "ztc", + "schemas/StatusType", + url=cls.status_new["statustype"], + zaaktype=cls.zaaktype["url"], + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + omschrijving="Initial request", + omschrijvingGeneriek="some content", + statustekst="", + volgnummer=1, + isEindstatus=False, + ) + cls.status_type_finish = generate_oas_component( + "ztc", + "schemas/StatusType", + url=cls.status_finish["statustype"], + zaaktype=cls.zaaktype["url"], + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + omschrijving="Finish", + omschrijvingGeneriek="some content", + statustekst="", + volgnummer=2, + isEindstatus=False, + ) + cls.user_role = generate_oas_component( + "zrc", + "schemas/Rol", + url=f"{ZAKEN_ROOT}rollen/f33153aa-ad2c-4a07-ae75-15add5891", + omschrijvingGeneriek=RolOmschrijving.initiator, + betrokkeneType=RolTypes.natuurlijk_persoon, + betrokkeneIdentificatie={ + "inpBsn": "900222086", + "voornamen": "Foo Bar", + "voorvoegselGeslachtsnaam": "van der", + "geslachtsnaam": "Bazz", + }, + ) + cls.not_initiator_role = generate_oas_component( + "zrc", + "schemas/Rol", + url=f"{ZAKEN_ROOT}rollen/aa353aa-ad2c-4a07-ae75-15add5822", + omschrijvingGeneriek=RolOmschrijving.behandelaar, + betrokkeneType=RolTypes.natuurlijk_persoon, + betrokkeneIdentificatie={ + "inpBsn": "123456789", + "voornamen": "Somebody", + "geslachtsnaam": "Else", + }, + ) + cls.result = generate_oas_component( + "zrc", + "schemas/Resultaat", + uuid="a44153aa-ad2c-6a07-be75-15add5113", + url=cls.zaak["resultaat"], + resultaattype=f"{CATALOGI_ROOT}resultaattypen/b1a268dd-4322-47bb-a930-b83066b4a32c", + zaak=cls.zaak["url"], + toelichting="resultaat toelichting", + ) + cls.zaak_informatie_object = generate_oas_component( + "zrc", + "schemas/ZaakInformatieObject", + url=f"{ZAKEN_ROOT}zaakinformatieobjecten/e55153aa-ad2c-4a07-ae75-15add57d6", + informatieobject=f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/014c38fe-b010-4412-881c-3000032fb812", + zaak=cls.zaak["url"], + aardRelatieWeergave="some content", + titel="", + beschrijving="", + registratiedatum="2021-01-12", + ) + cls.informatie_object_type = generate_oas_component( + "ztc", + "schemas/InformatieObjectType", + url=f"{CATALOGI_ROOT}informatieobjecttype/014c38fe-b010-4412-881c-3000032fb321", + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + omschrijving="Some content", + ) + cls.zaaktype_informatie_object_type = generate_oas_component( + "ztc", + "schemas/ZaakTypeInformatieObjectType", + uuid="3fb03882-f6f9-4e0d-ad92-f810e24b9abb", + url=f"{CATALOGI_ROOT}zaaktype-informatieobjecttypen/93250e6d-ef92-4474-acca-a6dbdcd61b7e", + catalogus=f"{CATALOGI_ROOT}catalogussen/1b643db-81bb-d71bd5a2317a", + zaaktype=cls.zaaktype["url"], + informatieobjecttype=cls.informatie_object_type["url"], + volgnummer=1, + richting="inkomend", + statustype=cls.status_type_finish, + ) + cls.informatie_object = generate_oas_component( + "drc", + "schemas/EnkelvoudigInformatieObject", + uuid="014c38fe-b010-4412-881c-3000032fb812", + url=cls.zaak_informatie_object["informatieobject"], + inhoud=f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/014c38fe-b010-4412-881c-3000032fb812/download", + informatieobjecttype=cls.informatie_object_type["url"], + status="definitief", vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + bestandsnaam="uploaded_document.txt", + titel="uploaded_document_title.txt", + bestandsomvang=123, ) - cls.status_intern = generate_oas_component( + cls.uploaded_informatie_object = generate_oas_component( + "drc", + "schemas/EnkelvoudigInformatieObject", + uuid="85079ba3-554a-450f-b963-2ce20b176c90", + url=cls.zaak_informatie_object["informatieobject"], + inhoud=f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/85079ba3-554a-450f-b963-2ce20b176c90/download", + informatieobjecttype=cls.informatie_object_type["url"], + status="definitief", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.openbaar, + bestandsnaam="upload.txt", + bestandsomvang=123, + ) + + cls.zaak_informatie_object_invisible = generate_oas_component( "zrc", - "schemas/Status", - url=cls.zaak_intern["status"], - zaak=cls.zaak_intern["url"], - statustype=cls.status_type1["url"], - datumStatusGezet="2021-01-12", - statustoelichting="", + "schemas/ZaakInformatieObject", + url=f"{ZAKEN_ROOT}zaakinformatieobjecten/fa5153aa-ad2c-4a07-ae75-15add57ee", + informatieobject=f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/994c38fe-b010-4412-881c-3000032fb123", + zaak=cls.zaak_invisible["url"], + aardRelatieWeergave="some invisible content", + titel="", + beschrijving="", + registratiedatum="2021-01-12", + ) + cls.informatie_object_invisible = generate_oas_component( + "drc", + "schemas/EnkelvoudigInformatieObject", + uuid="994c38fe-b010-4412-881c-3000032fb123", + url=cls.zaak_informatie_object_invisible["informatieobject"], + inhoud=f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/994c38fe-b010-4412-881c-3000032fb123/download", + informatieobjecttype=cls.informatie_object_type["url"], + status="definitief", + vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduidingen.geheim, + bestandsnaam="geheim-document.txt", + bestandsomvang=123, ) - def _setUpMocks(self, m): + cls.informatie_object_file = SimpleFile( + name="uploaded_document_title.txt", + size=123, + url=reverse( + "cases:document_download", + kwargs={ + "object_id": cls.zaak["uuid"], + "info_id": cls.informatie_object["uuid"], + }, + ), + ) + + def _setUpOASMocks(self, m): mock_service_oas_get(m, ZAKEN_ROOT, "zrc") mock_service_oas_get(m, CATALOGI_ROOT, "ztc") + mock_service_oas_get(m, DOCUMENTEN_ROOT, "drc") + + def setUpMocks(self, m): + self._setUpOASMocks(m) + + from furl import furl + m.get( furl(f"{ZAKEN_ROOT}zaken") .add( @@ -184,19 +333,105 @@ def _setUpMocks(self, m): } ) .url, - json=paginated_response( - [self.zaak1, self.zaak2, self.zaak3, self.zaak_intern] - ), + json=paginated_response([self.zaak, self.zaak2]), ) + for resource in [ + self.zaak, + self.result, self.zaaktype, - self.status_type1, - self.status_type2, - self.status1, - self.status2, - self.status3, - self.zaak_intern, - self.status_intern, - self.zaak_type_intern, + self.informatie_object_type, + self.informatie_object, + self.informatie_object_invisible, + self.zaaktype_informatie_object_type, + self.status_type_new, + self.status_type_finish, ]: m.get(resource["url"], json=resource) + + m.post( + f"{ZAKEN_ROOT}zaakinformatieobjecten", + status_code=201, + json=self.zaak_informatie_object, + ) + + # Zaak #1 (info_object, status, rol) + m.get( + f"{ZAKEN_ROOT}zaakinformatieobjecten?zaak={self.zaak['url']}", + json=[self.zaak_informatie_object, self.zaak_informatie_object_invisible], + ) + m.get( + f"{ZAKEN_ROOT}statussen?zaak={self.zaak['url']}", + # Taiga #972 these have to be oldest-last (newest-first) and cannot be resorted on + json=paginated_response([self.status_finish, self.status_new]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak['url']}", + json=paginated_response([self.user_role, self.not_initiator_role]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak['url']}&omschrijvingGeneriek={RolOmschrijving.initiator}", + # Taiga #961 this is not an accurate OpenZaak response as it has a 'behandelaar' even when we filter on 'initiator' + # but eSuite doesn't filter the response in the API, so we use filtering in Python to remove the not-initiator + json=paginated_response([self.user_role, self.not_initiator_role]), + ) + + # other data + m.post( + f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten", + status_code=201, + json=self.uploaded_informatie_object, + ) + m.get( + f"{CATALOGI_ROOT}zaaktype-informatieobjecttypen?zaaktype={self.zaaktype['url']}&richting=inkomend", + json=paginated_response([self.zaaktype_informatie_object_type]), + ) + m.get( + f"{DOCUMENTEN_ROOT}enkelvoudiginformatieobjecten/014c38fe-b010-4412-881c-3000032fb812/download", + text="document content", + ) + + def setUpMocksExtra(self, m): + for resource in [ + self.zaak2, + self.zaak3, + ]: + m.get(resource["url"], json=resource) + + # Zaak #2 (info_object, status, rol) + m.get( + f"{ZAKEN_ROOT}zaakinformatieobjecten?zaak={self.zaak2['url']}", + json=[self.zaak_informatie_object, self.zaak_informatie_object_invisible], + ) + m.get( + f"{ZAKEN_ROOT}statussen?zaak={self.zaak2['url']}", + # Taiga #972 these have to be oldest-last (newest-first) and cannot be resorted on + json=paginated_response([self.status_finish, self.status_new]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak2['url']}", + json=paginated_response([self.user_role, self.not_initiator_role]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak2['url']}&omschrijvingGeneriek={RolOmschrijving.initiator}", + json=paginated_response([self.user_role, self.not_initiator_role]), + ) + + # Zaak #3 (info_object, status, rol) + m.get( + f"{ZAKEN_ROOT}zaakinformatieobjecten?zaak={self.zaak3['url']}", + json=[self.zaak_informatie_object, self.zaak_informatie_object_invisible], + ) + m.get( + f"{ZAKEN_ROOT}statussen?zaak={self.zaak3['url']}", + # Taiga #972 these have to be oldest-last (newest-first) and cannot be resorted on + json=paginated_response([self.status_finish, self.status_new]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak3['url']}", + json=paginated_response([self.user_role, self.not_initiator_role]), + ) + m.get( + f"{ZAKEN_ROOT}rollen?zaak={self.zaak3['url']}&omschrijvingGeneriek={RolOmschrijving.initiator}", + json=paginated_response([self.user_role, self.not_initiator_role]), + ) diff --git a/src/open_inwoner/templates/cms/cases/cases_plugin.html b/src/open_inwoner/templates/cms/cases/cases_plugin.html index 6544e0cd79..1d95c32818 100644 --- a/src/open_inwoner/templates/cms/cases/cases_plugin.html +++ b/src/open_inwoner/templates/cms/cases/cases_plugin.html @@ -1,31 +1,60 @@ -{% load i18n anchor_menu_tags grid_tags icon_tags list_tags pagination_tags utils %} +{% load i18n grid_tags icon_tags list_tags pagination_tags utils %} -
- {% anchor_menu anchors desktop=True %} -
+{% if cases %} +

{{ page_title }} ({{ paginator.count }})

+

{{ title_text }}

+ {% render_grid %} + + {% for case in cases %} + {% render_column start=forloop.counter_0|multiply:4 span=4 %} +
+
+ + {% if case.datum_laatste_wijziging %} + +

{{ case.naam }}

+ {% render_list %} + + {% list_item text="Openstaand aanvraag" caption=_("Status") compact=True strong=False %} + {% list_item text=case.datum_laatste_wijziging caption=_("Laatste wijziging") compact=True strong=False %} + {% with end_date=case.eind_datum_geldigheid|default:"Geen" %} + {% list_item text=end_date caption=_("Einddatum geldigheid") compact=True strong=False%} + {% endwith %} + + {% endrender_list %} -

{{ page_title }} ({{ paginator.count }})

-{% render_grid %} - - {% for case in cases %} -
+
+ {% endrender_column %} + {% endfor %} + {% endrender_grid %} +{% endif %}