diff --git a/src/open_inwoner/accounts/tests/test_auth.py b/src/open_inwoner/accounts/tests/test_auth.py
index 5c60b5213c..76823b2954 100644
--- a/src/open_inwoner/accounts/tests/test_auth.py
+++ b/src/open_inwoner/accounts/tests/test_auth.py
@@ -955,7 +955,7 @@ def test_non_digid_user_can_edit_profile(self):
form = edit_page.forms["profile-edit"]
form["first_name"] = "changed_first"
form["last_name"] = "changed_last"
- response = form.submit()
+ form.submit()
user = User.objects.get(id=test_user.id)
@@ -994,7 +994,7 @@ def test_any_page_for_digid_user_redirect_to_necessary_fields(self):
urls = [
reverse("pages-root"),
reverse("products:category_list"),
- reverse("cases:open_cases"),
+ reverse("cases:index"),
reverse("profile:detail"),
reverse("profile:data"),
reverse("collaborate:plan_list"),
diff --git a/src/open_inwoner/cms/cases/tests/test_htmx.py b/src/open_inwoner/cms/cases/tests/test_htmx.py
index 61adb728dc..f8d41b789e 100644
--- a/src/open_inwoner/cms/cases/tests/test_htmx.py
+++ b/src/open_inwoner/cms/cases/tests/test_htmx.py
@@ -358,22 +358,7 @@ def test_cases(self, m):
context = self.browser.new_context(storage_state=self.user_login_state)
page = context.new_page()
- page.goto(self.live_reverse("cases:open_cases"))
-
- # expected anchors
- menu_items = page.get_by_role(
- "complementary", name=_("Secundaire paginanavigatie")
- ).get_by_role("listitem")
-
- expect(
- menu_items.get_by_role("link", name=_("Openstaande aanvragen"))
- ).to_be_visible()
- expect(
- menu_items.get_by_role("link", name=_("Lopende aanvragen"))
- ).to_be_visible()
- expect(
- menu_items.get_by_role("link", name=_("Afgeronde aanvragen"))
- ).to_be_visible()
+ page.goto(self.live_reverse("cases:index"))
# case title
case_title = page.get_by_role("link", name=self.zaaktype["omschrijving"])
diff --git a/src/open_inwoner/cms/cases/urls.py b/src/open_inwoner/cms/cases/urls.py
index 41030809d6..b1c283d37f 100644
--- a/src/open_inwoner/cms/cases/urls.py
+++ b/src/open_inwoner/cms/cases/urls.py
@@ -1,5 +1,4 @@
from django.urls import path
-from django.views.generic import RedirectView
from open_inwoner.accounts.views.contactmoments import (
KlantContactMomentDetailView,
@@ -11,30 +10,14 @@
CaseDocumentDownloadView,
CaseDocumentUploadFormView,
InnerCaseDetailView,
- InnerClosedCaseListView,
- InnerOpenCaseListView,
- InnerOpenSubmissionListView,
+ InnerCaseListView,
OuterCaseDetailView,
- OuterClosedCaseListView,
- OuterOpenCaseListView,
- OuterOpenSubmissionListView,
+ OuterCaseListView,
)
app_name = "cases"
urlpatterns = [
- path(
- "closed/content/",
- InnerClosedCaseListView.as_view(),
- name="closed_cases_content",
- ),
- path("closed/", OuterClosedCaseListView.as_view(), name="closed_cases"),
- path("forms/", OuterOpenSubmissionListView.as_view(), name="open_submissions"),
- path(
- "forms/content/",
- InnerOpenSubmissionListView.as_view(),
- name="open_submissions_content",
- ),
path(
"contactmomenten/",
KlantContactMomentListView.as_view(),
@@ -70,7 +53,6 @@
CaseDocumentUploadFormView.as_view(),
name="case_detail_document_form",
),
- path("open/", RedirectView.as_view(pattern_name="cases:open_cases"), name="index"),
- path("content/", InnerOpenCaseListView.as_view(), name="open_cases_content"),
- path("", OuterOpenCaseListView.as_view(), name="open_cases"),
+ path("content/", InnerCaseListView.as_view(), name="cases_content"),
+ path("", OuterCaseListView.as_view(), name="index"),
]
diff --git a/src/open_inwoner/cms/cases/views/__init__.py b/src/open_inwoner/cms/cases/views/__init__.py
index 05d357737a..7afdddfb80 100644
--- a/src/open_inwoner/cms/cases/views/__init__.py
+++ b/src/open_inwoner/cms/cases/views/__init__.py
@@ -1,9 +1,4 @@
-from .cases import (
- InnerClosedCaseListView,
- InnerOpenCaseListView,
- OuterClosedCaseListView,
- OuterOpenCaseListView,
-)
+from .cases import InnerCaseListView, OuterCaseListView
from .status import (
CaseContactFormView,
CaseDocumentDownloadView,
@@ -11,4 +6,3 @@
InnerCaseDetailView,
OuterCaseDetailView,
)
-from .submissions import InnerOpenSubmissionListView, OuterOpenSubmissionListView
diff --git a/src/open_inwoner/cms/cases/views/cases.py b/src/open_inwoner/cms/cases/views/cases.py
index 60deea403f..19e3c82f46 100644
--- a/src/open_inwoner/cms/cases/views/cases.py
+++ b/src/open_inwoner/cms/cases/views/cases.py
@@ -1,121 +1,76 @@
from django.urls import reverse
-from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView
-from view_breadcrumbs import BaseBreadcrumbMixin
-
from open_inwoner.htmx.mixins import RequiresHtmxMixin
+from open_inwoner.openzaak.cases import fetch_cases, preprocess_data
+from open_inwoner.openzaak.formapi import fetch_open_submissions
+from open_inwoner.openzaak.models import OpenZaakConfig
+from open_inwoner.openzaak.types import UniformCase
+from open_inwoner.utils.mixins import PaginationMixin
from open_inwoner.utils.views import CommonPageMixin
-from .mixins import CaseAccessMixin, CaseListMixin, OuterCaseAccessMixin
+from .mixins import CaseAccessMixin, CaseLogMixin, OuterCaseAccessMixin
-class OuterOpenCaseListView(
- OuterCaseAccessMixin, CommonPageMixin, BaseBreadcrumbMixin, TemplateView
-):
- template_name = "pages/cases/list_outer.html"
+class OuterCaseListView(OuterCaseAccessMixin, CommonPageMixin, TemplateView):
+ """View on the case list while content is loaded via htmx"""
- @cached_property
- def crumbs(self):
- return [(_("Mijn aanvragen"), reverse("cases:open_cases"))]
+ template_name = "pages/cases/list_outer.html"
def page_title(self):
- return _("Lopende aanvragen")
-
- def get_anchors(self) -> list:
- return [
- (reverse("cases:open_submissions"), _("Openstaande aanvragen")),
- ("#cases", _("Lopende aanvragen")),
- (reverse("cases:closed_cases"), _("Afgeronde aanvragen")),
- ]
+ return _("Mijn aanvragen")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- # anchors are needed here as well for rendering the mobile ones
- context["anchors"] = self.get_anchors()
- context["hxget"] = reverse("cases:open_cases_content")
+ context["hxget"] = reverse("cases:cases_content")
return context
-class InnerOpenCaseListView(
- RequiresHtmxMixin, CommonPageMixin, CaseAccessMixin, CaseListMixin, TemplateView
+class InnerCaseListView(
+ RequiresHtmxMixin,
+ CommonPageMixin,
+ CaseAccessMixin,
+ CaseLogMixin,
+ PaginationMixin,
+ TemplateView,
):
template_name = "pages/cases/list_inner.html"
+ paginate_by = 9
def page_title(self):
- return _("Lopende aanvragen")
+ return _("Mijn aanvragen")
def get_cases(self):
- all_cases = super().get_cases()
-
- cases = [case for case in all_cases if not case.einddatum]
- cases.sort(key=lambda case: case.startdatum, reverse=True)
- return cases
-
- def get_anchors(self) -> list:
- return [
- (reverse("cases:open_submissions"), _("Openstaande aanvragen")),
- ("#cases", _("Lopende aanvragen")),
- (reverse("cases:closed_cases"), _("Afgeronde aanvragen")),
- ]
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["hxget"] = reverse("cases:open_cases_content")
- return context
-
-
-class OuterClosedCaseListView(
- OuterCaseAccessMixin, CommonPageMixin, BaseBreadcrumbMixin, TemplateView
-):
- template_name = "pages/cases/list_outer.html"
-
- @cached_property
- def crumbs(self):
- return [(_("Mijn aanvragen"), reverse("cases:closed_cases"))]
-
- def page_title(self):
- return _("Afgeronde aanvragen")
+ raw_cases = fetch_cases(self.request.user.bsn)
+ preprocessed_cases = preprocess_data(raw_cases)
+ preprocessed_cases.sort(key=lambda case: case.startdatum, reverse=True)
+ return preprocessed_cases
- def get_anchors(self) -> list:
- return [
- (reverse("cases:open_submissions"), _("Openstaande aanvragen")),
- (reverse("cases:open_cases"), _("Lopende aanvragen")),
- ("#cases", _("Afgeronde aanvragen")),
- ]
+ def get_submissions(self):
+ subs = fetch_open_submissions(self.request.user.bsn)
+ subs.sort(key=lambda sub: sub.datum_laatste_wijziging, reverse=True)
+ return subs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context["hxget"] = reverse("cases:closed_cases_content")
- context["anchors"] = self.get_anchors()
- return context
-
-
-class InnerClosedCaseListView(
- RequiresHtmxMixin, CommonPageMixin, CaseAccessMixin, CaseListMixin, TemplateView
-):
- template_name = "pages/cases/list_inner.html"
+ config = OpenZaakConfig.get_solo()
- def page_title(self):
- return _("Afgeronde aanvragen")
+ # update ctx with submissions + cases
+ open_submissions: list[UniformCase] = self.get_submissions()
+ preprocessed_cases: list[UniformCase] = self.get_cases()
+ paginator_dict = self.paginate_with_context(
+ [*open_submissions, *preprocessed_cases]
+ )
+ case_dicts = [case.process_data() for case in paginator_dict["object_list"]]
- def get_cases(self):
- all_cases = super().get_cases()
+ context["cases"] = case_dicts
+ context.update(paginator_dict)
- cases = [case for case in all_cases if case.einddatum]
- cases.sort(key=lambda case: case.einddatum, reverse=True)
- return cases
+ self.log_access_cases(case_dicts)
- def get_anchors(self) -> list:
- return [
- (reverse("cases:open_submissions"), _("Openstaande aanvragen")),
- (reverse("cases:open_cases"), _("Lopende aanvragen")),
- ("#cases", _("Afgeronde aanvragen")),
- ]
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["hxget"] = reverse("cases:closed_cases_content")
+ # other data
+ context["hxget"] = reverse("cases:cases_content")
+ context["title_text"] = config.title_text
return context
diff --git a/src/open_inwoner/cms/cases/views/mixins.py b/src/open_inwoner/cms/cases/views/mixins.py
index 977ab7b583..67ecd8905a 100644
--- a/src/open_inwoner/cms/cases/views/mixins.py
+++ b/src/open_inwoner/cms/cases/views/mixins.py
@@ -1,41 +1,38 @@
import logging
-from collections import defaultdict
-from typing import List, Optional
+from typing import Optional
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
from django.template.response import TemplateResponse
from django.utils.translation import gettext_lazy as _
-from glom import glom
-
from open_inwoner.openzaak.api_models import Zaak
-from open_inwoner.openzaak.cases import (
- fetch_cases,
- fetch_roles_for_case_and_bsn,
- fetch_single_case,
- fetch_single_status,
-)
-from open_inwoner.openzaak.catalog import (
- fetch_single_case_type,
- fetch_single_status_type,
-)
-from open_inwoner.openzaak.models import (
- OpenZaakConfig,
- StatusTranslation,
- ZaakTypeStatusTypeConfig,
-)
-from open_inwoner.openzaak.utils import format_zaak_identificatie, is_zaak_visible
-from open_inwoner.utils.mixins import PaginationMixin
+from open_inwoner.openzaak.cases import fetch_roles_for_case_and_bsn, fetch_single_case
+from open_inwoner.openzaak.catalog import fetch_single_case_type
+from open_inwoner.openzaak.types import UniformCase
+from open_inwoner.openzaak.utils import is_zaak_visible
from open_inwoner.utils.views import LogMixin
logger = logging.getLogger(__name__)
class CaseLogMixin(LogMixin):
- def log_case_access(self, case_identificatie: str):
+ def log_access_cases(self, cases: list[dict]):
+ """
+ Log access to cases on the list view
+
+ Creates a single log for all cases
+ """
+ case_ids = (case["identification"] for case in cases)
+
+ self.log_user_action(
+ self.request.user,
+ _("Zaken bekeken: {cases}").format(cases=", ".join(case_ids)),
+ )
+
+ def log_access_case_detail(self, case: UniformCase):
self.log_user_action(
self.request.user,
- _("Zaak bekeken: {case}").format(case=case_identificatie),
+ _("Zaak bekeken: {case}").format(case=case.identification),
)
@@ -52,8 +49,6 @@ class CaseAccessMixin(AccessMixin):
- case confidentiality is not higher than globally configured
"""
- case: Zaak = None
-
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
logger.debug("CaseAccessMixin - permission denied: user not authenticated")
@@ -105,91 +100,6 @@ def get_case(self, kwargs) -> Optional[Zaak]:
return fetch_single_case(case_uuid)
-class CaseListMixin(CaseLogMixin, PaginationMixin):
- paginate_by = 9
-
- def get_cases(self) -> List[Zaak]:
- cases = fetch_cases(self.request.user.bsn)
-
- case_types = {}
- case_types_set = {case.zaaktype for case in cases}
-
- mapping = {
- zaaktype_statustype.statustype_url: zaaktype_statustype
- for zaaktype_statustype in ZaakTypeStatusTypeConfig.objects.all()
- }
-
- # fetch unique case types
- for case_type_url in case_types_set:
- # todo parallel
- case_types[case_type_url] = fetch_single_case_type(case_type_url)
-
- # set resolved case types
- for case in cases:
- case.zaaktype = case_types[case.zaaktype]
-
- # filter visibility
- cases = [case for case in cases if is_zaak_visible(case)]
-
- # fetch case status resources and attach resolved to case
- status_types = defaultdict(list)
- for case in cases:
- if case.status:
- # todo parallel
- case.status = fetch_single_status(case.status)
- status_types[case.status.statustype].append(case)
-
- case.statustype_config = mapping.get(case.status.statustype)
-
- for status_type_url, _cases in status_types.items():
- # todo parallel
- status_type = fetch_single_status_type(status_type_url)
- for case in _cases:
- case.status.statustype = status_type
-
- return cases
-
- def process_cases(self, cases: List[Zaak]) -> List[dict]:
- # Prepare data for frontend
- config = OpenZaakConfig.get_solo()
- status_translate = StatusTranslation.objects.get_lookup()
-
- updated_cases = []
- for case in cases:
- case_dict = {
- "identificatie": format_zaak_identificatie(case.identificatie, config),
- "uuid": str(case.uuid),
- "start_date": case.startdatum,
- "end_date": getattr(case, "einddatum", None),
- "description": case.zaaktype.omschrijving,
- "current_status": status_translate.from_glom(
- case, "status.statustype.omschrijving", default=""
- ),
- "statustype_config": getattr(case, "statustype_config", None),
- }
- updated_cases.append(case_dict)
- return updated_cases
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
-
- raw_cases = self.get_cases()
- paginator_dict = self.paginate_with_context(raw_cases)
- cases = self.process_cases(paginator_dict["object_list"])
-
- context["cases"] = cases
- context.update(paginator_dict)
-
- for case in cases:
- self.log_case_access(case["identificatie"])
-
- context["anchors"] = self.get_anchors()
- return context
-
- def get_anchors(self) -> list:
- return []
-
-
class OuterCaseAccessMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated and not request.user.bsn:
diff --git a/src/open_inwoner/cms/cases/views/status.py b/src/open_inwoner/cms/cases/views/status.py
index 66c11330cc..d87bec77e0 100644
--- a/src/open_inwoner/cms/cases/views/status.py
+++ b/src/open_inwoner/cms/cases/views/status.py
@@ -44,11 +44,7 @@
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
)
-from open_inwoner.openzaak.utils import (
- format_zaak_identificatie,
- get_role_name_display,
- is_info_object_visible,
-)
+from open_inwoner.openzaak.utils import get_role_name_display, is_info_object_visible
from open_inwoner.utils.translate import TranslationLookup
from open_inwoner.utils.views import CommonPageMixin, LogMixin
@@ -71,7 +67,7 @@ class OuterCaseDetailView(
@cached_property
def crumbs(self):
return [
- (_("Mijn aanvragen"), reverse("cases:open_cases")),
+ (_("Mijn aanvragen"), reverse("cases:index")),
(
_("Status"),
reverse("cases:case_detail", kwargs=self.kwargs),
@@ -91,11 +87,12 @@ class InnerCaseDetailView(
template_name = "pages/cases/status_inner.html"
form_class = CaseUploadForm
contact_form_class = CaseContactForm
+ case: Zaak = None
@cached_property
def crumbs(self):
return [
- (_("Mijn aanvragen"), reverse("cases:open_cases")),
+ (_("Mijn aanvragen"), reverse("cases:index")),
(
_("Status"),
reverse("cases:case_detail", kwargs=self.kwargs),
@@ -108,8 +105,10 @@ def page_title(self):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
+ # case is retrieved via CaseAccessMixin
if self.case:
- self.log_case_access(self.case.identificatie)
+ self.log_access_case_detail(self.case)
+
config = OpenZaakConfig.get_solo()
status_translate = StatusTranslation.objects.get_lookup()
@@ -137,9 +136,7 @@ def get_context_data(self, **kwargs):
context["case"] = {
"id": str(self.case.uuid),
- "identification": format_zaak_identificatie(
- self.case.identificatie, config
- ),
+ "identification": self.case.identification,
"initiator": self.get_initiator_display(self.case),
"result": self.get_result_display(self.case),
"start_date": self.case.startdatum,
@@ -354,7 +351,7 @@ def handle_no_permission(self):
raise PermissionDenied()
-class CaseDocumentUploadFormView(CaseAccessMixin, CaseLogMixin, FormView):
+class CaseDocumentUploadFormView(CaseAccessMixin, LogMixin, FormView):
template_name = "pages/cases/document_form.html"
form_class = CaseUploadForm
@@ -437,7 +434,7 @@ def get_context_data(self, **kwargs):
return context
-class CaseContactFormView(CaseAccessMixin, CaseLogMixin, FormView):
+class CaseContactFormView(CaseAccessMixin, LogMixin, FormView):
template_name = "pages/cases/contact_form.html"
form_class = CaseContactForm
diff --git a/src/open_inwoner/cms/cases/views/submissions.py b/src/open_inwoner/cms/cases/views/submissions.py
deleted file mode 100644
index 951b1d51c0..0000000000
--- a/src/open_inwoner/cms/cases/views/submissions.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from django.urls import reverse
-from django.utils.functional import cached_property
-from django.utils.translation import gettext_lazy as _
-from django.views.generic import TemplateView
-
-from view_breadcrumbs import BaseBreadcrumbMixin
-
-from open_inwoner.htmx.mixins import RequiresHtmxMixin
-from open_inwoner.openzaak.formapi import fetch_open_submissions
-from open_inwoner.utils.views import CommonPageMixin
-
-from .mixins import CaseAccessMixin, OuterCaseAccessMixin
-
-
-class OuterOpenSubmissionListView(
- OuterCaseAccessMixin, CommonPageMixin, BaseBreadcrumbMixin, TemplateView
-):
- template_name = "pages/cases/submissions_outer.html"
-
- @cached_property
- def crumbs(self):
- return [(_("Mijn aanvragen"), reverse("cases:open_submissions"))]
-
- def page_title(self):
- return _("Openstaande aanvragen")
-
- def get_anchors(self) -> list:
- return [
- ("#submissions", _("Openstaande aanvragen")),
- (reverse("cases:open_cases"), _("Lopende aanvragen")),
- (reverse("cases:closed_cases"), _("Afgeronde aanvragen")),
- ]
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["hxget"] = reverse("cases:open_submissions_content")
- context["anchors"] = self.get_anchors()
- return context
-
-
-class InnerOpenSubmissionListView(
- RequiresHtmxMixin,
- CommonPageMixin,
- BaseBreadcrumbMixin,
- CaseAccessMixin,
- TemplateView,
-):
- template_name = "pages/cases/submissions_inner.html"
-
- @cached_property
- def crumbs(self):
- return [(_("Mijn aanvragen"), reverse("cases:open_submissions"))]
-
- def page_title(self):
- return _("Openstaande aanvragen")
-
- def get_submissions(self):
- submissions = fetch_open_submissions(self.request.user.bsn)
- return submissions
-
- def get_anchors(self) -> list:
- return [
- ("#submissions", _("Openstaande aanvragen")),
- (reverse("cases:open_cases"), _("Lopende aanvragen")),
- (reverse("cases:closed_cases"), _("Afgeronde aanvragen")),
- ]
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["submissions"] = self.get_submissions()
- context["anchors"] = self.get_anchors()
- return context
diff --git a/src/open_inwoner/components/templates/components/Notification/Notification.html b/src/open_inwoner/components/templates/components/Notification/Notification.html
index 8305b92862..b6c7f87ad1 100644
--- a/src/open_inwoner/components/templates/components/Notification/Notification.html
+++ b/src/open_inwoner/components/templates/components/Notification/Notification.html
@@ -1,5 +1,5 @@
{% load i18n button_tags icon_tags button_tags icon_tags %}
-
+
{% if not icon == False %}
{% icon icon %}
diff --git a/src/open_inwoner/components/templatetags/notification_tags.py b/src/open_inwoner/components/templatetags/notification_tags.py
index 799147c5e4..ea9aa01db6 100644
--- a/src/open_inwoner/components/templatetags/notification_tags.py
+++ b/src/open_inwoner/components/templatetags/notification_tags.py
@@ -31,7 +31,7 @@ def notification(type, message, **kwargs):
Add a notification to the screen. These will be places inline.
Usage:
- {% notification type="success" message="this is the message" closable=True %}
+ {% notification type="success" message="this is the message" closable=True ctx="cases" %}
{% notification type="warning" title="title" message="this is the message" action="#" action_text="Verzoek opsturen" %}
Variables:
@@ -43,6 +43,8 @@ def notification(type, message, **kwargs):
- action_text: string | The text of the button.
- closable: bool | If a close button should be shown.
- compact: boolean | Whether to use compact styling or not.
+ - ctx: string | The context in which the tag is rendered; used to add
+ classes to HTML element and override CSS
"""
message_types = {
"debug": "bug_report",
diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py
index 54b4e35b3d..95e5336465 100644
--- a/src/open_inwoner/openzaak/admin.py
+++ b/src/open_inwoner/openzaak/admin.py
@@ -47,6 +47,7 @@ class OpenZaakConfigAdmin(SingletonModelAdmin):
"document_max_confidentiality",
"max_upload_size",
"allowed_file_extensions",
+ "title_text",
],
},
),
diff --git a/src/open_inwoner/openzaak/api_models.py b/src/open_inwoner/openzaak/api_models.py
index 2e2c0eca76..9ed56f4331 100644
--- a/src/open_inwoner/openzaak/api_models.py
+++ b/src/open_inwoner/openzaak/api_models.py
@@ -1,3 +1,5 @@
+import logging
+import re
from dataclasses import dataclass, field
from datetime import date, datetime
from typing import Dict, Optional, Union
@@ -6,6 +8,9 @@
from zgw_consumers.api_models.base import Model, ZGWModel
from zgw_consumers.api_models.constants import RolOmschrijving, RolTypes
+logger = logging.getLogger(__name__)
+
+
"""
Modified ZGWModel's to work with both OpenZaak and e-Suite implementations,
because there is an issue where e-Suite doesn't return all JSON fields the official API and dataclasses expect
@@ -32,6 +37,55 @@ class Zaak(ZGWModel):
# relevante_andere_zaken: list
# zaakgeometrie: dict
+ @staticmethod
+ def _reformat_esuite_zaak_identificatie(identificatie: str) -> str:
+ """
+ 0014ESUITE66392022 -> 6639-2022
+
+ Static utility function; only used in connection with `Zaak` instances
+ """
+ exp = r"^\d+ESUITE(?P
\d+?)(?P\d{4})$"
+ m = re.match(exp, identificatie)
+ if not m:
+ return identificatie
+ num = m.group("num")
+ year = m.group("year")
+ return f"{num}-{year}"
+
+ def _format_zaak_identificatie(self) -> str:
+ from open_inwoner.openzaak.models import OpenZaakConfig
+
+ zaak_config = OpenZaakConfig.get_solo()
+
+ if zaak_config.reformat_esuite_zaak_identificatie:
+ return self._reformat_esuite_zaak_identificatie(self.identificatie)
+ return self.identificatie
+
+ @property
+ def identification(self) -> str:
+ return self._format_zaak_identificatie()
+
+ def process_data(self) -> dict:
+ """
+ Prepare data for template
+ """
+ from open_inwoner.openzaak.models import StatusTranslation
+
+ status_translate = StatusTranslation.objects.get_lookup()
+
+ return {
+ "identification": self.identification,
+ "uuid": str(self.uuid),
+ "start_date": self.startdatum,
+ "end_date": getattr(self, "einddatum", None),
+ "description": self.zaaktype.omschrijving,
+ "current_status": status_translate.from_glom(
+ self, "status.statustype.omschrijving", default=""
+ ),
+ "statustype_config": getattr(self, "statustype_config", None),
+ "case_type": "Zaak",
+ }
+
@dataclass
class ZaakType(ZGWModel):
diff --git a/src/open_inwoner/openzaak/cases.py b/src/open_inwoner/openzaak/cases.py
index 64c68ae1ca..9dc7cb5f1a 100644
--- a/src/open_inwoner/openzaak/cases.py
+++ b/src/open_inwoner/openzaak/cases.py
@@ -1,3 +1,4 @@
+import copy
import logging
from typing import List, Optional
@@ -9,10 +10,12 @@
from zgw_consumers.api_models.constants import RolOmschrijving, RolTypes
from zgw_consumers.service import get_paginated_results
+from ..utils.decorators import cache as cache_result
from .api_models import Resultaat, Rol, Status, Zaak, ZaakInformatieObject
+from .catalog import fetch_single_case_type, fetch_single_status_type
from .clients import build_client
-from .models import OpenZaakConfig
-from .utils import cache as cache_result
+from .models import OpenZaakConfig, ZaakTypeStatusTypeConfig
+from .utils import is_zaak_visible
logger = logging.getLogger(__name__)
@@ -288,3 +291,64 @@ def connect_case_with_document(case_url: str, document_url: str) -> Optional[dic
return
return response
+
+
+def resolve_zaak_type(case: Zaak) -> None:
+ """
+ Resolve `case.zaaktype` (`str`) to a `ZaakType(ZGWModel)` object
+
+ Note: the result of `fetch_single_case_type` is cached, hence a request
+ is only made for new case type urls
+ """
+ case_type_url = case.zaaktype
+ case_type = fetch_single_case_type(case_type_url)
+ case.zaaktype = case_type
+
+
+def resolve_status(case: Zaak) -> None:
+ """
+ Resolve `case.status` (`str`) to a `Status(ZGWModel)` object
+ """
+ case.status = fetch_single_status(case.status)
+
+
+def resolve_status_type(case: Zaak) -> None:
+ """
+ Resolve `case.statustype` (`str`) to a `StatusType(ZGWModel)` object
+ """
+ statustype_url = case.status.statustype
+ case.status.statustype = fetch_single_status_type(statustype_url)
+
+
+def add_status_type_config(case: Zaak) -> None:
+ """
+ Add `ZaakTypeStatusTypeConfig` corresponding to the status type url of the case
+
+ Note: must be called after `resolve_status_type` since we're getting the
+ status type url from `case.status.statustype`
+ """
+ try:
+ case.statustype_config = ZaakTypeStatusTypeConfig.objects.get(
+ statustype_url=case.status.statustype.url
+ )
+ except ZaakTypeStatusTypeConfig.DoesNotExist:
+ pass
+
+
+def filter_visible(cases: list[Zaak]) -> list[Zaak]:
+ return [case for case in cases if is_zaak_visible(case)]
+
+
+def preprocess_data(cases: list[Zaak]) -> list[Zaak]:
+ """
+ Resolve zaaktype and statustype, add status type config, filter for visibility
+ """
+ for case in cases:
+ resolve_zaak_type(case)
+
+ if case.status:
+ resolve_status(case)
+ resolve_status_type(case)
+ add_status_type_config(case)
+
+ return filter_visible(cases)
diff --git a/src/open_inwoner/openzaak/catalog.py b/src/open_inwoner/openzaak/catalog.py
index 0685fc984e..de40998b06 100644
--- a/src/open_inwoner/openzaak/catalog.py
+++ b/src/open_inwoner/openzaak/catalog.py
@@ -9,9 +9,10 @@
from zgw_consumers.api_models.catalogi import Catalogus
from zgw_consumers.service import get_paginated_results
+from ..utils.decorators import cache as cache_result
from .api_models import InformatieObjectType, ResultaatType, StatusType, ZaakType
from .clients import build_client
-from .utils import cache as cache_result, get_retrieve_resource_by_uuid_url
+from .utils import get_retrieve_resource_by_uuid_url
logger = logging.getLogger(__name__)
diff --git a/src/open_inwoner/openzaak/documents.py b/src/open_inwoner/openzaak/documents.py
index 7e5e6c5c9a..b234b64508 100644
--- a/src/open_inwoner/openzaak/documents.py
+++ b/src/open_inwoner/openzaak/documents.py
@@ -16,7 +16,7 @@
from open_inwoner.openzaak.clients import build_client
from open_inwoner.openzaak.models import OpenZaakConfig
-from .utils import cache as cache_result
+from ..utils.decorators import cache as cache_result
logger = logging.getLogger(__name__)
diff --git a/src/open_inwoner/openzaak/formapi.py b/src/open_inwoner/openzaak/formapi.py
index 755249de8a..a027c86399 100644
--- a/src/open_inwoner/openzaak/formapi.py
+++ b/src/open_inwoner/openzaak/formapi.py
@@ -22,6 +22,25 @@ class OpenSubmission(Model):
vervolg_link: Optional[str] = None
eind_datum_geldigheid: Optional[datetime] = None
+ @property
+ def identification(self) -> str:
+ return f"{self.naam}: {self.uuid}"
+
+ def process_data(self) -> dict:
+ """
+ Prepare data for template
+ """
+ return {
+ "identification": self.identification,
+ "url": self.url,
+ "uuid": self.uuid,
+ "naam": self.naam,
+ "vervolg_link": self.vervolg_link,
+ "datum_laatste_wijziging": self.datum_laatste_wijziging,
+ "eind_datum_geldigheid": self.eind_datum_geldigheid or "Geen",
+ "case_type": "OpenSubmission",
+ }
+
def fetch_open_submissions(bsn: str) -> List[OpenSubmission]:
if not bsn:
diff --git a/src/open_inwoner/openzaak/migrations/0025_openzaakconfig_title_text.py b/src/open_inwoner/openzaak/migrations/0025_openzaakconfig_title_text.py
new file mode 100644
index 0000000000..ad6a75cbab
--- /dev/null
+++ b/src/open_inwoner/openzaak/migrations/0025_openzaakconfig_title_text.py
@@ -0,0 +1,22 @@
+# Generated by Django 3.2.20 on 2023-10-11 07:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("openzaak", "0024_zaaktypeconfig_contact_subject_code"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="openzaakconfig",
+ name="title_text",
+ field=models.TextField(
+ default="Hier vindt u een overzicht van al uw lopende an afgeronde aanvragen.",
+ help_text="The title/introductory text shown on the list view of 'Mijn aanvragen'.",
+ verbose_name="Title text",
+ ),
+ ),
+ ]
diff --git a/src/open_inwoner/openzaak/migrations/0026_merge_20231024_0949.py b/src/open_inwoner/openzaak/migrations/0026_merge_20231024_0949.py
new file mode 100644
index 0000000000..c4268d0751
--- /dev/null
+++ b/src/open_inwoner/openzaak/migrations/0026_merge_20231024_0949.py
@@ -0,0 +1,13 @@
+# Generated by Django 3.2.20 on 2023-10-24 07:49
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("openzaak", "0025_auto_20231016_0957"),
+ ("openzaak", "0025_openzaakconfig_title_text"),
+ ]
+
+ operations = []
diff --git a/src/open_inwoner/openzaak/models.py b/src/open_inwoner/openzaak/models.py
index 3dab53dde6..727398dcb3 100644
--- a/src/open_inwoner/openzaak/models.py
+++ b/src/open_inwoner/openzaak/models.py
@@ -131,6 +131,16 @@ class OpenZaakConfig(SingletonModel):
default=False,
)
+ title_text = models.TextField(
+ verbose_name=_("Title text"),
+ help_text=_(
+ "The title/introductory text shown on the list view of 'Mijn aanvragen'."
+ ),
+ default=_(
+ "Hier vindt u een overzicht van al uw lopende an afgeronde aanvragen."
+ ),
+ )
+
class Meta:
verbose_name = _("Open Zaak configuration")
diff --git a/src/open_inwoner/openzaak/notifications.py b/src/open_inwoner/openzaak/notifications.py
index 05e4a159d2..45b221bfd8 100644
--- a/src/open_inwoner/openzaak/notifications.py
+++ b/src/open_inwoner/openzaak/notifications.py
@@ -35,7 +35,6 @@
UserCaseStatusNotification,
)
from open_inwoner.openzaak.utils import (
- format_zaak_identificatie,
get_zaak_type_config,
get_zaak_type_info_object_type_config,
is_info_object_visible,
@@ -364,7 +363,7 @@ def send_case_update_email(user: User, case: Zaak):
template = find_template("case_notification")
context = {
- "identification": format_zaak_identificatie(case.identificatie),
+ "identification": case.identification,
"type_description": case.zaaktype.omschrijving,
"start_date": case.startdatum,
"case_link": case_detail_url,
diff --git a/src/open_inwoner/openzaak/tests/factories.py b/src/open_inwoner/openzaak/tests/factories.py
index 06df72472b..1195cf2292 100644
--- a/src/open_inwoner/openzaak/tests/factories.py
+++ b/src/open_inwoner/openzaak/tests/factories.py
@@ -3,13 +3,12 @@
from simple_certmanager.constants import CertificateTypes
from simple_certmanager.models import Certificate
from zgw_consumers.api_models.base import factory as zwg_factory
-from zgw_consumers.api_models.catalogi import InformatieObjectType
from zgw_consumers.api_models.constants import RolOmschrijving
from zgw_consumers.models import Service
from zgw_consumers.test import generate_oas_component
from open_inwoner.accounts.tests.factories import UserFactory
-from open_inwoner.openzaak.api_models import Notification, Rol, ZaakType
+from open_inwoner.openzaak.api_models import Notification, Rol
from open_inwoner.openzaak.models import (
CatalogusConfig,
StatusTranslation,
diff --git a/src/open_inwoner/openzaak/tests/mocks.py b/src/open_inwoner/openzaak/tests/mocks.py
new file mode 100644
index 0000000000..0d573488b0
--- /dev/null
+++ b/src/open_inwoner/openzaak/tests/mocks.py
@@ -0,0 +1,42 @@
+from zgw_consumers.test import mock_service_oas_get
+
+from open_inwoner.openzaak.tests.shared import FORMS_ROOT
+
+
+class ESuiteData:
+ def __init__(self):
+ self.submission_1 = {
+ "url": "https://dmidoffice2.esuite-development.net/formulieren-provider/api/v1/8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
+ "uuid": "8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
+ "naam": "Melding openbare ruimte",
+ "vervolgLink": "https://dloket2.esuite-development.net/formulieren-nieuw/formulier/start/8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
+ "datumLaatsteWijziging": "2023-02-13T14:02:00.999+01:00",
+ "eindDatumGeldigheid": "2023-05-14T14:02:00.999+02:00",
+ }
+ self.submission_2 = {
+ "url": "https://dmidoffice2.esuite-development.net/formulieren-provider/api/v1/d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
+ "uuid": "d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
+ "naam": "Indienen bezwaarschrift",
+ "vervolgLink": "https://dloket2.esuite-development.net/formulieren-nieuw/formulier/start/d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
+ "datumLaatsteWijziging": "2023-02-13T14:10:26.197000+0100",
+ "eindDatumGeldigheid": "2023-05-14T14:10:26.197+02:00",
+ }
+ # note this is a weird esuite response without pagination links
+ self.response = {
+ "count": 2,
+ "results": [
+ self.submission_1,
+ self.submission_2,
+ ],
+ }
+
+ def setUpOASMocks(self, m):
+ mock_service_oas_get(m, FORMS_ROOT, "submissions-esuite")
+
+ def install_mocks(self, m):
+ self.setUpOASMocks(m)
+ m.get(
+ f"{FORMS_ROOT}openstaande-inzendingen",
+ json=self.response,
+ )
+ return self
diff --git a/src/open_inwoner/openzaak/tests/test_case_detail.py b/src/open_inwoner/openzaak/tests/test_case_detail.py
index 72b1c9e5e8..531524fc89 100644
--- a/src/open_inwoner/openzaak/tests/test_case_detail.py
+++ b/src/open_inwoner/openzaak/tests/test_case_detail.py
@@ -34,7 +34,6 @@
from ...utils.tests.helpers import AssertRedirectsMixin
from ..api_models import Status, StatusType
from ..models import OpenZaakConfig
-from ..utils import format_zaak_identificatie
from .factories import CatalogusConfigFactory, ServiceFactory
from .shared import CATALOGI_ROOT, DOCUMENTEN_ROOT, ZAKEN_ROOT
@@ -414,12 +413,13 @@ def test_page_reformats_zaak_identificatie(self, m):
self._setUpMocks(m)
with patch(
- "open_inwoner.cms.cases.views.status.format_zaak_identificatie",
- wraps=format_zaak_identificatie,
+ "open_inwoner.openzaak.api_models.Zaak._format_zaak_identificatie",
) as spy_format:
self.app.get(self.case_detail_url, user=self.user)
- spy_format.assert_called_once()
+ # _format_zaak_identificatie is called twice on requesting DetailVew:
+ # once for the log, once for adding case to context
+ spy_format.assert_called
def test_page_translates_statuses(self, m):
st1 = StatusTranslationFactory(
diff --git a/src/open_inwoner/openzaak/tests/test_cases.py b/src/open_inwoner/openzaak/tests/test_cases.py
index 6a936dc9b0..7078014e37 100644
--- a/src/open_inwoner/openzaak/tests/test_cases.py
+++ b/src/open_inwoner/openzaak/tests/test_cases.py
@@ -15,31 +15,28 @@
from open_inwoner.accounts.choices import LoginTypeChoices
from open_inwoner.accounts.tests.factories import UserFactory
-from open_inwoner.cms.cases.views.mixins import CaseListMixin
+from open_inwoner.cms.cases.views.cases import InnerCaseListView
+from open_inwoner.openzaak.tests.shared import FORMS_ROOT
from open_inwoner.utils.test import ClearCachesMixin, paginated_response
+from open_inwoner.utils.tests.helpers import AssertTimelineLogMixin, Lookups
from ...utils.tests.helpers import AssertRedirectsMixin
+from ..api_models import Zaak
from ..constants import StatusIndicators
-from ..models import OpenZaakConfig, ZaakTypeStatusTypeConfig
-from ..utils import format_zaak_identificatie
+from ..models import OpenZaakConfig
from .factories import (
ServiceFactory,
StatusTranslationFactory,
ZaakTypeStatusTypeConfigFactory,
)
+from .mocks import ESuiteData
from .shared import CATALOGI_ROOT, ZAKEN_ROOT
@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
class CaseListAccessTests(AssertRedirectsMixin, ClearCachesMixin, WebTest):
- outer_urls = [
- reverse_lazy("cases:open_cases"),
- reverse_lazy("cases:closed_cases"),
- ]
- inner_urls = [
- reverse_lazy("cases:open_cases_content"),
- reverse_lazy("cases:closed_cases_content"),
- ]
+ outer_url = reverse_lazy("cases:index")
+ inner_url = reverse_lazy("cases:cases_content")
@classmethod
def setUpTestData(cls):
@@ -63,18 +60,14 @@ def test_user_access_is_forbidden_when_not_logged_in_via_digid(self):
last_name="",
login_type=LoginTypeChoices.default,
)
- for url in self.outer_urls:
- with self.subTest(url):
- self.app.get(url, user=user, status=403)
+ self.app.get(self.outer_url, user=user, status=403)
def test_anonymous_user_has_no_access_to_cases_page(self):
user = AnonymousUser()
- for url in self.outer_urls:
- with self.subTest(url):
- response = self.app.get(url, user=user)
+ response = self.app.get(self.outer_url, user=user)
- self.assertRedirectsLogin(response, next=url)
+ self.assertRedirectsLogin(response, next=self.outer_url)
def test_bad_request_when_no_htmx_in_inner_urls(self):
user = UserFactory(
@@ -83,9 +76,7 @@ def test_bad_request_when_no_htmx_in_inner_urls(self):
login_type=LoginTypeChoices.default,
)
- for url in self.inner_urls:
- with self.subTest(url):
- self.app.get(url, user=user, status=400)
+ self.app.get(self.inner_url, user=user, status=400)
def test_missing_zaak_client_returns_empty_list(self):
user = UserFactory(
@@ -94,11 +85,11 @@ def test_missing_zaak_client_returns_empty_list(self):
self.config.zaak_service = None
self.config.save()
- for url in self.inner_urls:
- with self.subTest(url):
- response = self.app.get(url, user=user, headers={"HX-Request": "true"})
+ response = self.app.get(
+ self.inner_url, user=user, headers={"HX-Request": "true"}
+ )
- self.assertListEqual(response.context.get("cases"), [])
+ self.assertListEqual(response.context.get("cases"), [])
@requests_mock.Mocker()
def test_no_cases_are_retrieved_when_http_404(self, m):
@@ -111,11 +102,11 @@ def test_no_cases_are_retrieved_when_http_404(self, m):
status_code=404,
)
- for url in self.inner_urls:
- with self.subTest(url):
- response = self.app.get(url, user=user, headers={"HX-Request": "true"})
+ response = self.app.get(
+ self.inner_url, user=user, headers={"HX-Request": "true"}
+ )
- self.assertListEqual(response.context.get("cases"), [])
+ self.assertListEqual(response.context.get("cases"), [])
@requests_mock.Mocker()
def test_no_cases_are_retrieved_when_http_500(self, m):
@@ -128,18 +119,17 @@ def test_no_cases_are_retrieved_when_http_500(self, m):
status_code=500,
)
- for url in self.inner_urls:
- with self.subTest(url):
- response = self.app.get(url, user=user, headers={"HX-Request": "true"})
+ response = self.app.get(
+ self.inner_url, user=user, headers={"HX-Request": "true"}
+ )
- self.assertListEqual(response.context.get("cases"), [])
+ self.assertListEqual(response.context.get("cases"), [])
@requests_mock.Mocker()
@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
-class CaseListViewTests(ClearCachesMixin, WebTest):
- inner_url_open = reverse_lazy("cases:open_cases_content")
- inner_url_closed = reverse_lazy("cases:closed_cases_content")
+class CaseListViewTests(AssertTimelineLogMixin, ClearCachesMixin, WebTest):
+ inner_url = reverse_lazy("cases:cases_content")
@classmethod
def setUpTestData(cls):
@@ -234,7 +224,7 @@ def setUpTestData(cls):
url=f"{ZAKEN_ROOT}zaken/e4d469b9-6666-4bdd-bf42-b53445298102",
uuid="e4d469b9-6666-4bdd-bf42-b53445298102",
zaaktype=cls.zaaktype["url"],
- identificatie="ZAAK-2022-0008800002",
+ identificatie="0014ESUITE66392022",
omschrijving="Coffee zaak 2",
startdatum="2022-01-12",
einddatum=None,
@@ -296,6 +286,14 @@ def setUpTestData(cls):
datumStatusGezet="2021-01-12",
statustoelichting="",
)
+ cls.submission = generate_oas_component(
+ "zrc",
+ "schemas/Zaak",
+ url=f"{ZAKEN_ROOT}zaken/d8bbdeb7-770f-4ca9-b1ea-77b4ee0bf67d",
+ uuid="c8yudeb7-490f-2cw9-h8wa-44h9830bf67d",
+ naam="mysub",
+ datum_laatste_wijziging="2023-10-10",
+ )
def _setUpMocks(self, m):
mock_service_oas_get(m, ZAKEN_ROOT, "zrc")
@@ -326,11 +324,11 @@ def _setUpMocks(self, m):
]:
m.get(resource["url"], json=resource)
- def test_list_open_cases(self, m):
+ def test_list_cases(self, m):
self._setUpMocks(m)
response = self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
)
self.assertListEqual(
@@ -340,33 +338,37 @@ def test_list_open_cases(self, m):
"uuid": self.zaak2["uuid"],
"start_date": datetime.date.fromisoformat(self.zaak2["startdatum"]),
"end_date": None,
- "identificatie": self.zaak2["identificatie"],
+ "identification": self.zaak2["identificatie"],
"description": self.zaaktype["omschrijving"],
"current_status": self.status_type1["omschrijving"],
"statustype_config": self.zt_statustype_config1,
+ "case_type": "Zaak",
},
{
"uuid": self.zaak1["uuid"],
"start_date": datetime.date.fromisoformat(self.zaak1["startdatum"]),
"end_date": None,
- "identificatie": self.zaak1["identificatie"],
+ "identification": self.zaak1["identificatie"],
"description": self.zaaktype["omschrijving"],
"current_status": self.status_type1["omschrijving"],
"statustype_config": self.zt_statustype_config1,
+ "case_type": "Zaak",
+ },
+ {
+ "uuid": self.zaak3["uuid"],
+ "start_date": datetime.date.fromisoformat(self.zaak3["startdatum"]),
+ "end_date": datetime.date.fromisoformat(self.zaak3["einddatum"]),
+ "identification": self.zaak3["identificatie"],
+ "description": self.zaaktype["omschrijving"],
+ "current_status": self.status_type2["omschrijving"],
+ "statustype_config": None,
+ "case_type": "Zaak",
},
],
)
- # don't show closed cases
- self.assertNotContains(response, self.zaak3["identificatie"])
- self.assertNotContains(response, self.zaak3["omschrijving"])
- self.assertNotContains(response, self.zaak_intern["identificatie"])
+ # don't show internal cases
self.assertNotContains(response, self.zaak_intern["omschrijving"])
-
- zaken_cards = response.html.find_all("div", {"class": "card"})
-
- self.assertEqual(len(zaken_cards), 2)
- self.assertTrue("U moet documenten toevoegen" in zaken_cards[0].text)
- self.assertTrue("U moet documenten toevoegen" in zaken_cards[1].text)
+ self.assertNotContains(response, self.zaak_intern["identificatie"])
# check zaken request query parameters
list_zaken_req = [
@@ -387,38 +389,76 @@ def test_list_open_cases(self, m):
},
)
- def test_list_open_cases_reformats_zaak_identificatie(self, m):
+ def test_format_zaak_identificatie(self, m):
+ config = OpenZaakConfig.get_solo()
self._setUpMocks(m)
- with patch(
- "open_inwoner.cms.cases.views.mixins.format_zaak_identificatie",
- wraps=format_zaak_identificatie,
- ) as spy_format:
- self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
+ with self.subTest("formatting enabled"):
+ config.reformat_esuite_zaak_identificatie = True
+ config.save()
+
+ response = self.app.get(
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
+ )
+
+ e_suite_case = next(
+ (
+ case
+ for case in response.context["cases"]
+ if case["uuid"] == self.zaak2["uuid"]
+ )
)
- spy_format.assert_called()
- self.assertEqual(spy_format.call_count, 2)
+ self.assertEqual(e_suite_case["identification"], "6639-2022")
+
+ with self.subTest("formatting disabled"):
+ config.reformat_esuite_zaak_identificatie = False
+ config.save()
- def test_list_open_cases_translates_status(self, m):
+ response = self.app.get(
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
+ )
+
+ e_suite_case = next(
+ (
+ case
+ for case in response.context["cases"]
+ if case["uuid"] == self.zaak2["uuid"]
+ )
+ )
+
+ self.assertEqual(e_suite_case["identification"], "0014ESUITE66392022")
+
+ def test_reformat_esuite_zaak_identificatie(self, m):
+ tests = [
+ ("0014ESUITE66392022", "6639-2022"),
+ ("4321ESUITE00011991", "0001-1991"),
+ ("4321ESUITE123456781991", "12345678-1991"),
+ ("12345678", "12345678"),
+ ("aaaaaa1234", "aaaaaa1234"),
+ ]
+
+ for value, expected in tests:
+ with self.subTest(value=value, expected=expected):
+ actual = Zaak._reformat_esuite_zaak_identificatie(value)
+ self.assertEqual(actual, expected)
+
+ def test_list_cases_translates_status(self, m):
st1 = StatusTranslationFactory(
status=self.status_type1["omschrijving"],
translation="Translated Status Type",
)
self._setUpMocks(m)
response = self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
)
self.assertNotContains(response, st1.status)
self.assertContains(response, st1.translation)
- def test_list_open_cases_logs_displayed_case_ids(self, m):
+ def test_list_cases_logs_displayed_case_ids(self, m):
self._setUpMocks(m)
- self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
- )
+ self.app.get(self.inner_url, user=self.user, headers={"HX-Request": "true"})
# check access logs for displayed cases
logs = list(TimelineLog.objects.all())
@@ -437,104 +477,13 @@ def test_list_open_cases_logs_displayed_case_ids(self, m):
self.assertEqual(self.user, case_log[0].user)
self.assertEqual(self.user, case_log[0].content_object)
- # no logs for non-displayed cases
+ # no logs for internal, hence non-displayed cases
for log in logs:
- self.assertNotIn(self.zaak3["identificatie"], log.extra_data["message"])
-
- def test_list_closed_cases(self, m):
- self._setUpMocks(m)
-
- response = self.app.get(
- self.inner_url_closed, user=self.user, headers={"HX-Request": "true"}
- )
-
- self.assertListEqual(
- response.context.get("cases"),
- [
- {
- "uuid": self.zaak3["uuid"],
- "start_date": datetime.date.fromisoformat(self.zaak3["startdatum"]),
- "end_date": datetime.date.fromisoformat(self.zaak3["einddatum"]),
- "identificatie": self.zaak3["identificatie"],
- "description": self.zaaktype["omschrijving"],
- "current_status": self.status_type2["omschrijving"],
- "statustype_config": None,
- },
- ],
- )
- # don't show closed cases
- for open_zaak in [self.zaak1, self.zaak2]:
- self.assertNotContains(response, open_zaak["identificatie"])
- self.assertNotContains(response, open_zaak["omschrijving"])
-
- # check zaken request query parameters
- list_zaken_req = [
- req
- for req in m.request_history
- if req.hostname == "zaken.nl" and req.path == "/api/v1/zaken"
- ][0]
- self.assertEqual(len(list_zaken_req.qs), 2)
- self.assertEqual(
- list_zaken_req.qs,
- {
- "rol__betrokkeneidentificatie__natuurlijkpersoon__inpbsn": [
- self.user.bsn
- ],
- "maximalevertrouwelijkheidaanduiding": [
- VertrouwelijkheidsAanduidingen.beperkt_openbaar
- ],
- },
- )
-
- def test_list_closed_cases_reformats_zaak_identificatie(self, m):
- self._setUpMocks(m)
-
- with patch(
- "open_inwoner.cms.cases.views.mixins.format_zaak_identificatie",
- wraps=format_zaak_identificatie,
- ) as spy_format:
- self.app.get(
- self.inner_url_closed, user=self.user, headers={"HX-Request": "true"}
+ self.assertNotIn(
+ self.zaak_intern["identificatie"], log.extra_data["message"]
)
- spy_format.assert_called()
- self.assertEqual(spy_format.call_count, 1)
-
- def test_list_closed_cases_translates_status(self, m):
- st1 = StatusTranslationFactory(
- status=self.status_type2["omschrijving"],
- translation="Translated Status Type",
- )
- self._setUpMocks(m)
- response = self.app.get(
- self.inner_url_closed, user=self.user, headers={"HX-Request": "true"}
- )
- self.assertNotContains(response, st1.status)
- self.assertContains(response, st1.translation)
-
- def test_list_closed_cases_logs_displayed_case_ids(self, m):
- self._setUpMocks(m)
-
- self.app.get(
- self.inner_url_closed, user=self.user, headers={"HX-Request": "true"}
- )
-
- # check access logs for displayed cases
- logs = list(TimelineLog.objects.all())
-
- case_log = [
- l for l in logs if self.zaak3["identificatie"] in l.extra_data["message"]
- ]
- self.assertEqual(len(case_log), 1)
- self.assertEqual(self.user, case_log[0].user)
- self.assertEqual(self.user, case_log[0].content_object)
-
- # no logs for non-displayed cases
- for log in logs:
- self.assertNotIn(self.zaak1["identificatie"], log.extra_data["message"])
- self.assertNotIn(self.zaak2["identificatie"], log.extra_data["message"])
-
- @patch.object(CaseListMixin, "paginate_by", 1)
+ @patch.object(InnerCaseListView, "paginate_by", 1)
def test_list_cases_paginated(self, m):
"""
show only one case and url to the next page
@@ -543,7 +492,7 @@ def test_list_cases_paginated(self, m):
# 1. test first page
response_1 = self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
)
self.assertListEqual(
@@ -553,10 +502,11 @@ def test_list_cases_paginated(self, m):
"uuid": self.zaak2["uuid"],
"start_date": datetime.date.fromisoformat(self.zaak2["startdatum"]),
"end_date": None,
- "identificatie": self.zaak2["identificatie"],
+ "identification": self.zaak2["identificatie"],
"description": self.zaaktype["omschrijving"],
"current_status": self.status_type1["omschrijving"],
"statustype_config": self.zt_statustype_config1,
+ "case_type": "Zaak",
},
],
)
@@ -564,7 +514,7 @@ def test_list_cases_paginated(self, m):
self.assertContains(response_1, "?page=2")
# 2. test next page
- next_page = f"{self.inner_url_open}?page=2"
+ next_page = f"{self.inner_url}?page=2"
response_2 = self.app.get(
next_page, user=self.user, headers={"HX-Request": "true"}
)
@@ -576,59 +526,97 @@ def test_list_cases_paginated(self, m):
"uuid": self.zaak1["uuid"],
"start_date": datetime.date.fromisoformat(self.zaak1["startdatum"]),
"end_date": None,
- "identificatie": self.zaak1["identificatie"],
+ "identification": self.zaak1["identificatie"],
"description": self.zaaktype["omschrijving"],
"current_status": self.status_type1["omschrijving"],
"statustype_config": self.zt_statustype_config1,
+ "case_type": "Zaak",
},
],
)
self.assertNotContains(response_2, self.zaak2["identificatie"])
self.assertContains(response_2, "?page=1")
- @patch.object(CaseListMixin, "paginate_by", 1)
+ @patch.object(InnerCaseListView, "paginate_by", 1)
def test_list_cases_paginated_logs_displayed_case_ids(self, m):
self._setUpMocks(m)
- # 1. test first page
- response = self.app.get(
- self.inner_url_open, user=self.user, headers={"HX-Request": "true"}
- )
- self.assertEqual(response.context.get("cases")[0]["uuid"], self.zaak2["uuid"])
-
- # check access logs for displayed cases
- logs = list(TimelineLog.objects.all())
+ with self.subTest("first page"):
+ response = self.app.get(
+ self.inner_url, user=self.user, headers={"HX-Request": "true"}
+ )
+ self.assertEqual(
+ response.context.get("cases")[0]["uuid"], self.zaak2["uuid"]
+ )
- case_log = [
- l for l in logs if self.zaak2["identificatie"] in l.extra_data["message"]
- ]
- self.assertEqual(len(case_log), 1)
- self.assertEqual(self.user, case_log[0].user)
- self.assertEqual(self.user, case_log[0].content_object)
+ self.assertTimelineLog(f"Zaken bekeken: {self.zaak2['identificatie']}")
- # no logs for non-displayed cases
- for log in logs:
- self.assertNotIn(self.zaak1["identificatie"], log.extra_data["message"])
- self.assertNotIn(self.zaak3["identificatie"], log.extra_data["message"])
+ with self.assertRaises(AssertionError):
+ self.assertTimelineLog(
+ self.zaak1["identificatie"], lookup=Lookups.icontains
+ )
+ self.assertTimelineLog(
+ self.zaak3["identificatie"], lookup=Lookups.icontains
+ )
- # clear logs for testing
TimelineLog.objects.all().delete()
- # 2. test next page
- next_page = f"{self.inner_url_open}?page=2"
+ with self.subTest("next page"):
+ next_page = f"{self.inner_url}?page=2"
+ response = self.app.get(
+ next_page, user=self.user, headers={"HX-Request": "true"}
+ )
+ self.assertEqual(
+ response.context.get("cases")[0]["uuid"], self.zaak1["uuid"]
+ )
+
+ self.assertTimelineLog(f"Zaken bekeken: {self.zaak1['identificatie']}")
+
+ with self.assertRaises(AssertionError):
+ self.assertTimelineLog(
+ self.zaak2["identificatie"], lookup=Lookups.icontains
+ )
+ self.assertTimelineLog(
+ self.zaak3["identificatie"], lookup=Lookups.icontains
+ )
+
+
+@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
+class CaseSubmissionTest(WebTest):
+ inner_url = reverse_lazy("cases:cases_content")
+
+ @classmethod
+ def setUpTestData(cls):
+ super().setUpTestData()
+
+ cls.config = OpenZaakConfig.get_solo()
+ cls.config.form_service = ServiceFactory(
+ api_root=FORMS_ROOT, api_type=APITypes.orc
+ )
+ cls.config.save()
+
+ @requests_mock.Mocker()
+ def test_case_submission(self, m):
+ user = UserFactory(
+ login_type=LoginTypeChoices.digid, bsn="900222086", email="john@smith.nl"
+ )
+
+ data = ESuiteData().install_mocks(m)
+
response = self.app.get(
- next_page, user=self.user, headers={"HX-Request": "true"}
+ self.inner_url, user=user, headers={"HX-Request": "true"}
)
- self.assertEqual(response.context.get("cases")[0]["uuid"], self.zaak1["uuid"])
- # check access logs for displayed cases
- logs = list(TimelineLog.objects.all())
- case_log = [
- l for l in logs if self.zaak1["identificatie"] in l.extra_data["message"]
- ]
- self.assertEqual(len(case_log), 1)
+ cases = response.context["cases"]
- # no logs for non-displayed cases (after we cleared just above)
- for log in logs:
- self.assertNotIn(self.zaak2["identificatie"], log.extra_data["message"])
- self.assertNotIn(self.zaak3["identificatie"], log.extra_data["message"])
+ self.assertEqual(len(cases), 2)
+
+ # submission cases are sorted in reverse by `last modified`
+ self.assertEqual(cases[0]["url"], data.submission_2["url"])
+ self.assertEqual(cases[0]["uuid"], data.submission_2["uuid"])
+ self.assertEqual(cases[0]["naam"], data.submission_2["naam"])
+ self.assertEqual(cases[0]["vervolg_link"], data.submission_2["vervolgLink"])
+ self.assertEqual(
+ cases[0]["datum_laatste_wijziging"].strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
+ data.submission_2["datumLaatsteWijziging"],
+ )
diff --git a/src/open_inwoner/openzaak/tests/test_cases_cache.py b/src/open_inwoner/openzaak/tests/test_cases_cache.py
index 6ee80ad122..c8ff0d45bf 100644
--- a/src/open_inwoner/openzaak/tests/test_cases_cache.py
+++ b/src/open_inwoner/openzaak/tests/test_cases_cache.py
@@ -24,7 +24,7 @@
@requests_mock.Mocker()
@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
class OpenCaseListCacheTests(ClearCachesMixin, WebTest):
- inner_url = reverse_lazy("cases:open_cases_content")
+ inner_url = reverse_lazy("cases:cases_content")
@classmethod
def setUpTestData(cls):
@@ -301,18 +301,3 @@ def test_cached_statuses_in_combination_with_new_ones(self, m):
self.assertIsNotNone(cache.get(f"status:{self.new_status['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status2['url']}"))
-
-
-class ClosedCaseListCacheTests(OpenCaseListCacheTests):
- """
- run the same tests as for open cases
- """
-
- inner_url = reverse_lazy("cases:closed_cases_content")
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
-
- for zaak in [cls.zaak1, cls.zaak2, cls.new_zaak]:
- zaak["einddatum"] = "2022-01-16"
diff --git a/src/open_inwoner/openzaak/tests/test_formapi.py b/src/open_inwoner/openzaak/tests/test_formapi.py
deleted file mode 100644
index fda1f53eec..0000000000
--- a/src/open_inwoner/openzaak/tests/test_formapi.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from django.test.utils import override_settings
-from django.urls import reverse
-from django.utils.translation import gettext_lazy as _
-
-import requests_mock
-from django_webtest import WebTest
-from zgw_consumers.constants import APITypes
-from zgw_consumers.test import mock_service_oas_get
-
-from open_inwoner.accounts.tests.factories import UserFactory
-from open_inwoner.openzaak.formapi import fetch_open_submissions
-from open_inwoner.openzaak.models import OpenZaakConfig
-from open_inwoner.openzaak.tests.factories import ServiceFactory
-from open_inwoner.openzaak.tests.shared import FORMS_ROOT
-from open_inwoner.utils.test import ClearCachesMixin
-from open_inwoner.utils.tests.helpers import AssertRedirectsMixin
-
-
-class ESuiteData:
- def __init__(self):
- self.submission_1 = {
- "url": "https://dmidoffice2.esuite-development.net/formulieren-provider/api/v1/8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
- "uuid": "8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
- "naam": "Melding openbare ruimte",
- "vervolgLink": "https://dloket2.esuite-development.net/formulieren-nieuw/formulier/start/8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
- "datumLaatsteWijziging": "2023-02-13T14:02:00.999+01:00",
- "eindDatumGeldigheid": "2023-05-14T14:02:00.999+02:00",
- }
- self.submission_2 = {
- "url": "https://dmidoffice2.esuite-development.net/formulieren-provider/api/v1/d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
- "uuid": "d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
- "naam": "Indienen bezwaarschrift",
- "vervolgLink": "https://dloket2.esuite-development.net/formulieren-nieuw/formulier/start/d14658b0-dcb4-4d3c-a61c-fd7d0c78f296",
- "datumLaatsteWijziging": "2023-02-13T14:10:26.197+01:00",
- "eindDatumGeldigheid": "2023-05-14T14:10:26.197+02:00",
- }
- # note this is a weird esuite response without pagination links
- self.response = {
- "count": 2,
- "results": [
- self.submission_1,
- self.submission_2,
- ],
- }
-
- def setUpOASMocks(self, m):
- mock_service_oas_get(m, FORMS_ROOT, "submissions-esuite")
-
- def install_mocks(self, m):
- self.setUpOASMocks(m)
- m.get(
- f"{FORMS_ROOT}openstaande-inzendingen",
- json=self.response,
- )
- return self
-
-
-@requests_mock.Mocker()
-@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
-class FormAPITest(AssertRedirectsMixin, ClearCachesMixin, WebTest):
- config: OpenZaakConfig
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
-
- cls.config = OpenZaakConfig.get_solo()
- cls.config.form_service = ServiceFactory(
- api_root=FORMS_ROOT, api_type=APITypes.orc
- )
- cls.config.save()
-
- cls.user = UserFactory(bsn="900222086")
- cls.outer_submissions_url = reverse("cases:open_submissions")
- cls.inner_submissions_url = reverse("cases:open_submissions_content")
-
- def test_api_fetch(self, m):
- data = ESuiteData().install_mocks(m)
-
- res = fetch_open_submissions(self.user.bsn)
-
- self.assertEqual(len(res), 2)
- self.assertEqual(res[0].url, data.submission_1["url"])
-
- def test_page_shows_open_submissions(self, m):
- data = ESuiteData().install_mocks(m)
-
- response = self.app.get(
- self.inner_submissions_url, user=self.user, headers={"HX-Request": "true"}
- )
-
- self.assertContains(response, data.submission_1["naam"])
- self.assertContains(response, data.submission_1["vervolgLink"])
-
- self.assertContains(response, data.submission_2["naam"])
- self.assertContains(response, data.submission_2["vervolgLink"])
-
- def test_page_shows_zero_submissions(self, m):
- data = ESuiteData()
- data.response["results"] = []
- data.response["count"] = 0
- data.install_mocks(m)
-
- response = self.app.get(
- self.inner_submissions_url, user=self.user, headers={"HX-Request": "true"}
- )
-
- self.assertContains(response, _("Er zijn geen open aanvragen."))
-
- def test_requires_auth(self, m):
- response = self.app.get(self.outer_submissions_url)
- self.assertRedirectsLogin(response, next=self.outer_submissions_url)
-
- def test_requires_bsn(self, m):
- self.app.get(self.outer_submissions_url, user=UserFactory(bsn=""), status=403)
diff --git a/src/open_inwoner/openzaak/tests/test_notification_utils.py b/src/open_inwoner/openzaak/tests/test_notification_utils.py
index b8ef621259..f628f32642 100644
--- a/src/open_inwoner/openzaak/tests/test_notification_utils.py
+++ b/src/open_inwoner/openzaak/tests/test_notification_utils.py
@@ -1,4 +1,4 @@
-from unittest.mock import patch
+from unittest import mock
from django.core import mail
from django.test import TestCase
@@ -19,17 +19,12 @@
from open_inwoner.openzaak.tests.factories import generate_rol
from ..api_models import Zaak, ZaakType
-from ..utils import format_zaak_identificatie
from .test_notification_data import MockAPIData
@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
class NotificationHandlerUtilsTestCase(TestCase):
- @patch(
- "open_inwoner.openzaak.notifications.format_zaak_identificatie",
- wraps=format_zaak_identificatie,
- )
- def test_send_case_update_email(self, spy_format):
+ def test_send_case_update_email(self):
config = SiteConfiguration.get_solo()
data = MockAPIData()
@@ -40,9 +35,16 @@ def test_send_case_update_email(self, spy_format):
case_url = reverse("cases:case_detail", kwargs={"object_id": str(case.uuid)})
- send_case_update_email(user, case)
+ # mock `_format_zaak_identificatie`, but then continue with result of actual call
+ # (test redirect for invalid BSN that passes pattern validation)
+ ret_val = case._format_zaak_identificatie()
+ with mock.patch.object(
+ Zaak, "_format_zaak_identificatie"
+ ) as format_identificatie:
+ format_identificatie.return_value = ret_val
+ send_case_update_email(user, case)
- spy_format.assert_called_once()
+ format_identificatie.assert_called_once()
self.assertEqual(len(mail.outbox), 1)
email = mail.outbox[0]
diff --git a/src/open_inwoner/openzaak/tests/test_utils.py b/src/open_inwoner/openzaak/tests/test_utils.py
index 22bbf6397f..96274edc56 100644
--- a/src/open_inwoner/openzaak/tests/test_utils.py
+++ b/src/open_inwoner/openzaak/tests/test_utils.py
@@ -8,11 +8,9 @@
from open_inwoner.openzaak.models import OpenZaakConfig
from open_inwoner.openzaak.tests.factories import generate_rol
from open_inwoner.openzaak.utils import (
- format_zaak_identificatie,
get_role_name_display,
is_info_object_visible,
is_zaak_visible,
- reformat_esuite_zaak_identificatie,
)
from ...utils.test import ClearCachesMixin
@@ -259,37 +257,6 @@ def test_get_role_name_display(self):
expected = "Bazz, Foo van der"
self.assertEqual(expected, get_role_name_display(role))
- def test_format_zaak_identificatie(self):
- config = OpenZaakConfig.get_solo()
- value = "0014ESUITE66392022"
-
- with self.subTest("enabled"):
- config.reformat_esuite_zaak_identificatie = True
- config.save()
- actual = format_zaak_identificatie(value, config)
- self.assertEqual(actual, "6639-2022")
-
- with self.subTest("disabled"):
- config.reformat_esuite_zaak_identificatie = False
- config.save()
- actual = format_zaak_identificatie(value, config)
- # no change
- self.assertEqual(actual, value)
-
- def test_reformat_esuite_zaak_identificatie(self):
- tests = [
- ("0014ESUITE66392022", "6639-2022"),
- ("4321ESUITE00011991", "0001-1991"),
- ("4321ESUITE123456781991", "12345678-1991"),
- ("12345678", "12345678"),
- ("aaaaaa1234", "aaaaaa1234"),
- ]
-
- for value, expected in tests:
- with self.subTest(value=value, expected=expected):
- actual = reformat_esuite_zaak_identificatie(value)
- self.assertEqual(actual, expected)
-
class TestHelpers(TestCase):
def test_copy_with_new_uuid(self):
diff --git a/src/open_inwoner/openzaak/types.py b/src/open_inwoner/openzaak/types.py
new file mode 100644
index 0000000000..ccecb11a7a
--- /dev/null
+++ b/src/open_inwoner/openzaak/types.py
@@ -0,0 +1,24 @@
+from typing import Protocol
+
+
+class UniformCase(Protocol):
+ """
+ Zaken and open submissions are classified as "cases" if they have an
+ `identification` property and a method `process_data` to prepare data
+ for the template
+ """
+
+ @property
+ def identification(self) -> str:
+ ...
+
+ def process_data(self) -> dict:
+ """
+ Prepare data for template
+
+ Should include (at least) the following:
+ - identification (str)
+ - uuid (str)
+ - case_type (str)
+ """
+ ...
diff --git a/src/open_inwoner/openzaak/utils.py b/src/open_inwoner/openzaak/utils.py
index 9bd3c4f83b..5b3626ca85 100644
--- a/src/open_inwoner/openzaak/utils.py
+++ b/src/open_inwoner/openzaak/utils.py
@@ -1,12 +1,7 @@
-import inspect
import logging
-import re
-from functools import wraps
-from typing import Callable, Optional, TypeVar, Union
+from typing import Optional, Union
from uuid import UUID
-from django.core.cache import caches
-
from zds_client import get_operation_url
from zgw_consumers.api_models.constants import RolTypes, VertrouwelijkheidsAanduidingen
@@ -113,82 +108,6 @@ def join(*values):
return display
-RT = TypeVar("RT")
-
-
-def cache(key: str, alias: str = "default", **set_options):
- """
- Function-decorator for updating the django low-level cache.
-
- It determines if the key exists in cache and skips it by calling the decorated function
- or creates it if doesn't exist.
-
- We can pass a keyword argument for the time we want the cache the data in
- seconds (timeout=60).
- """
-
- def decorator(func: Callable[..., RT]) -> Callable[..., RT]:
- argspec = inspect.getfullargspec(func)
-
- if argspec.defaults:
- positional_count = len(argspec.args) - len(argspec.defaults)
- defaults = dict(zip(argspec.args[positional_count:], argspec.defaults))
- else:
- defaults = {}
-
- @wraps(func)
- def wrapped(*args, **kwargs) -> RT:
- skip_cache = kwargs.pop("skip_cache", False)
- if skip_cache:
- return func(*args, **kwargs)
-
- key_kwargs = defaults.copy()
- named_args = dict(zip(argspec.args, args), **kwargs)
- key_kwargs.update(**named_args)
-
- if argspec.varkw:
- var_kwargs = {
- key: value
- for key, value in named_args.items()
- if key not in argspec.args
- }
- key_kwargs[argspec.varkw] = var_kwargs
-
- cache_key = key.format(**key_kwargs)
-
- _cache = caches[alias]
- result = _cache.get(cache_key)
-
- # The key exists in cache so we return the already cached data
- if result is not None:
- logger.debug("Cache key '%s' hit", cache_key)
- return result
-
- # The key does not exist so we call the decorated function and set the cache
- result = func(*args, **kwargs)
- _cache.set(cache_key, result, **set_options)
-
- return result
-
- return wrapped
-
- return decorator
-
-
-def get_retrieve_resource_by_uuid_url(
- client, resource: str, uuid: Union[str, UUID]
-) -> str:
- op_suffix = client.operation_suffix_mapping["retrieve"]
- operation_id = f"{resource}{op_suffix}"
- path_kwargs = {
- "uuid": uuid,
- }
- url = get_operation_url(
- client.schema, operation_id, base_url=client.base_url, **path_kwargs
- )
- return url
-
-
def get_zaak_type_config(case_type: ZaakType) -> Optional[ZaakTypeConfig]:
try:
return ZaakTypeConfig.objects.filter_case_type(case_type).get()
@@ -209,24 +128,15 @@ def get_zaak_type_info_object_type_config(
return None
-def format_zaak_identificatie(
- identificatie: str, config: Optional[OpenZaakConfig] = None
-):
- config = config or OpenZaakConfig.get_solo()
- if config.reformat_esuite_zaak_identificatie:
- return reformat_esuite_zaak_identificatie(identificatie)
- else:
- return identificatie
-
-
-def reformat_esuite_zaak_identificatie(identificatie: str):
- """
- 0014ESUITE66392022 -> 6639-2022
- """
- exp = r"^\d+ESUITE(?P\d+?)(?P\d{4})$"
- m = re.match(exp, identificatie)
- if not m:
- return identificatie
- num = m.group("num")
- year = m.group("year")
- return f"{num}-{year}"
+def get_retrieve_resource_by_uuid_url(
+ client, resource: str, uuid: Union[str, UUID]
+) -> str:
+ op_suffix = client.operation_suffix_mapping["retrieve"]
+ operation_id = f"{resource}{op_suffix}"
+ path_kwargs = {
+ "uuid": uuid,
+ }
+ url = get_operation_url(
+ client.schema, operation_id, base_url=client.base_url, **path_kwargs
+ )
+ return url
diff --git a/src/open_inwoner/scss/components/Cases/Cases.scss b/src/open_inwoner/scss/components/Cases/Cases.scss
index 1c6c777830..9b87137998 100644
--- a/src/open_inwoner/scss/components/Cases/Cases.scss
+++ b/src/open_inwoner/scss/components/Cases/Cases.scss
@@ -11,6 +11,11 @@
a:hover {
cursor: pointer;
}
+
+ &__title_text {
+ padding-top: var(--spacing-medium);
+ padding-bottom: var(--spacing-extra-large);
+ }
}
#document-upload {
diff --git a/src/open_inwoner/scss/components/Notification/_Notification.scss b/src/open_inwoner/scss/components/Notification/_Notification.scss
index f77ab4fac9..b492c42f74 100644
--- a/src/open_inwoner/scss/components/Notification/_Notification.scss
+++ b/src/open_inwoner/scss/components/Notification/_Notification.scss
@@ -49,10 +49,6 @@
width: 24px;
}
- &__content {
- width: 100%;
- }
-
&__close {
position: absolute;
top: var(--spacing-small);
@@ -78,4 +74,22 @@
@media (min-width: 768px) {
margin: var(--spacing-tiny) 0;
}
+
+ // Overrides based on context
+ &--cases {
+ padding: var(--spacing-small);
+ margin-bottom: var(--spacing-medium);
+ align-items: center;
+
+ .notification__icon {
+ display: flex;
+ padding-left: var(--spacing-medium);
+ align-items: center;
+ }
+
+ .notification__close {
+ top: 0;
+ position: inherit;
+ }
+ }
}
diff --git a/src/open_inwoner/templates/pages/cases/list_inner.html b/src/open_inwoner/templates/pages/cases/list_inner.html
index 5178cd1932..a1ff3a9d77 100644
--- a/src/open_inwoner/templates/pages/cases/list_inner.html
+++ b/src/open_inwoner/templates/pages/cases/list_inner.html
@@ -1,10 +1,9 @@
-{% load i18n anchor_menu_tags grid_tags icon_tags list_tags pagination_tags utils %}
-
-
- {% anchor_menu anchors desktop=True %}
-
+{% load link_tags button_tags i18n grid_tags icon_tags list_tags pagination_tags utils %}
{% trans "Mijn aanvragen" %}
+{{ page_title }} ({{ paginator.count }})
+{{ title_text }}
+
{% render_grid %}
{% if not cases %}
{% trans "U heeft op dit moment nog geen lopende aanvragen." %}
@@ -15,23 +14,45 @@ {% trans "Mijn aanvragen" %}
{% include "components/StatusIndicator/StatusIndicator.html" with status_indicator=case.statustype_config.status_indicator status_indicator_text=case.statustype_config.status_indicator_text %}
{% endrender_column %}
diff --git a/src/open_inwoner/templates/pages/cases/list_outer.html b/src/open_inwoner/templates/pages/cases/list_outer.html
index 631a56ec41..df470dd08b 100644
--- a/src/open_inwoner/templates/pages/cases/list_outer.html
+++ b/src/open_inwoner/templates/pages/cases/list_outer.html
@@ -1,10 +1,6 @@
{% extends 'master.html' %}
{% load i18n icon_tags %}
-{% block sidebar_content %}
-
-{% endblock sidebar_content %}
-
{% block content %}
diff --git a/src/open_inwoner/templates/pages/cases/submissions_inner.html b/src/open_inwoner/templates/pages/cases/submissions_inner.html
deleted file mode 100644
index 6ca42d5fa7..0000000000
--- a/src/open_inwoner/templates/pages/cases/submissions_inner.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% load i18n anchor_menu_tags button_tags link_tags %}
-
-
- {% anchor_menu anchors=anchors desktop=False %}
-
-
-
- {% anchor_menu anchors desktop=True %}
-
-
-{{ page_title }} ({{ submissions|length }})
-
-
-
-
- {% trans "Formulier" %} |
- {% trans "Laatste wijziging" %} |
- {% trans "Einde geldigheid" %} |
- |
-
-
-
- {% for submission in submissions %}
-
-
- {{submission.datum_laatste_wijziging|date:"d-m-Y"}} |
- {{submission.eind_datum_geldigheid|date:"d-m-Y"}} |
- {% button text=submission.naam hide_text=True href=submission.vervolg_link icon="arrow_forward" secondary=True icon_outlined=True transparent=True %} |
-
- {% empty %}
-
- {% trans "Er zijn geen open aanvragen." %} |
-
- {% endfor %}
-
-
diff --git a/src/open_inwoner/templates/pages/cases/submissions_outer.html b/src/open_inwoner/templates/pages/cases/submissions_outer.html
deleted file mode 100644
index 5e1598de74..0000000000
--- a/src/open_inwoner/templates/pages/cases/submissions_outer.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends 'master.html' %}
-{% load i18n icon_tags %}
-
-
-
-{% block sidebar_content %}
-
-{% endblock sidebar_content %}
-
-{% block content %}
- {% trans "Mijn aanvragen" %}
-
-
-
-
-
- {% icon icon="rotate_right" extra_classes="spinner-icon rotate" %}
-
{% trans "Gegevens laden..." %}
-
-
-{% endblock content %}
diff --git a/src/open_inwoner/urls.py b/src/open_inwoner/urls.py
index 6397bde269..f8fda5df8c 100644
--- a/src/open_inwoner/urls.py
+++ b/src/open_inwoner/urls.py
@@ -110,7 +110,6 @@
path("apimock/", include("open_inwoner.apimock.urls")),
# TODO move search to products cms app?
path("", include("open_inwoner.search.urls", namespace="search")),
- # path("uitkeringen/", include("open_inwoner.cms.ssd.urls", namespace="ssd")),
re_path(r"^", include("cms.urls")),
]
diff --git a/src/open_inwoner/utils/decorators.py b/src/open_inwoner/utils/decorators.py
new file mode 100644
index 0000000000..83717ef9cb
--- /dev/null
+++ b/src/open_inwoner/utils/decorators.py
@@ -0,0 +1,70 @@
+import inspect
+import logging
+from functools import wraps
+from typing import Callable, TypeVar
+
+from django.core.cache import caches
+
+logger = logging.getLogger(__name__)
+
+
+RT = TypeVar("RT")
+
+
+def cache(key: str, alias: str = "default", **set_options):
+ """
+ Decorator factory for updating the django low-level cache.
+
+ It determines if the key exists in cache and skips it by calling the decorated function
+ or creates it if doesn't exist.
+
+ We can pass a keyword argument for the time we want the cache the data in
+ seconds (timeout=60).
+ """
+
+ def decorator(func: Callable[..., RT]) -> Callable[..., RT]:
+ argspec = inspect.getfullargspec(func)
+
+ if argspec.defaults:
+ positional_count = len(argspec.args) - len(argspec.defaults)
+ defaults = dict(zip(argspec.args[positional_count:], argspec.defaults))
+ else:
+ defaults = {}
+
+ @wraps(func)
+ def wrapped(*args, **kwargs) -> RT:
+ skip_cache = kwargs.pop("skip_cache", False)
+ if skip_cache:
+ return func(*args, **kwargs)
+
+ key_kwargs = defaults.copy()
+ named_args = dict(zip(argspec.args, args), **kwargs)
+ key_kwargs.update(**named_args)
+
+ if argspec.varkw:
+ var_kwargs = {
+ key: value
+ for key, value in named_args.items()
+ if key not in argspec.args
+ }
+ key_kwargs[argspec.varkw] = var_kwargs
+
+ cache_key = key.format(**key_kwargs)
+
+ _cache = caches[alias]
+ result = _cache.get(cache_key)
+
+ # The key exists in cache so we return the already cached data
+ if result is not None:
+ logger.debug("Cache key '%s' hit", cache_key)
+ return result
+
+ # The key does not exist so we call the decorated function and set the cache
+ result = func(*args, **kwargs)
+ _cache.set(cache_key, result, **set_options)
+
+ return result
+
+ return wrapped
+
+ return decorator
diff --git a/src/open_inwoner/utils/views.py b/src/open_inwoner/utils/views.py
index 4cebb559bb..e090b6e815 100644
--- a/src/open_inwoner/utils/views.py
+++ b/src/open_inwoner/utils/views.py
@@ -88,8 +88,7 @@ def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
class LogMixin(object):
"""
- Class based views mixin that adds simple wrappers to
- the functions above.
+ CBV mixin that adds simple wrappers to logging functions
"""
def log_addition(self, instance, message=""):