diff --git a/requirements/base.txt b/requirements/base.txt
index 782c8f11a9..ae5f6cb3c3 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -161,7 +161,7 @@ django-csp==3.7
# via -r requirements/base.in
django-csp-reports==1.8.1
# via -r requirements/base.in
-django-digid-eherkenning==0.7.0
+django-digid-eherkenning==0.9.0
# via -r requirements/base.in
django-elasticsearch-dsl==7.2.1
# via -r requirements/base.in
diff --git a/requirements/ci.txt b/requirements/ci.txt
index c4a2185bad..2c0c11c304 100644
--- a/requirements/ci.txt
+++ b/requirements/ci.txt
@@ -249,7 +249,7 @@ django-csp-reports==1.8.1
# via
# -c requirements/base.txt
# -r requirements/base.txt
-django-digid-eherkenning==0.7.0
+django-digid-eherkenning==0.9.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 70c4d5322b..da46fb6976 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -276,7 +276,7 @@ django-csp-reports==1.8.1
# -r requirements/ci.txt
django-debug-toolbar==3.2.2
# via -r requirements/dev.in
-django-digid-eherkenning==0.7.0
+django-digid-eherkenning==0.9.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
diff --git a/src/open_inwoner/accounts/tests/test_auth.py b/src/open_inwoner/accounts/tests/test_auth.py
index 76823b2954..518b054f4f 100644
--- a/src/open_inwoner/accounts/tests/test_auth.py
+++ b/src/open_inwoner/accounts/tests/test_auth.py
@@ -1,4 +1,5 @@
from datetime import date
+from unittest.mock import patch
from urllib.parse import urlencode
from django.contrib.sites.models import Site
@@ -68,7 +69,11 @@ def test_registration_page_only_digid_with_invite(self):
furl(reverse("digid:login")).add({"next": necessary_url}).url,
)
- def test_digid_fail_without_invite_redirects_to_login_page(self):
+ @patch("digid_eherkenning.validators.Proef11ValidatorBase.__call__")
+ def test_digid_fail_without_invite_redirects_to_login_page(self, m):
+ # disable mock form validation to check redirect
+ m.return_value = True
+
self.assertNotIn("invite_url", self.client.session.keys())
url = reverse("digid-mock:password")
@@ -87,7 +92,11 @@ def test_digid_fail_without_invite_redirects_to_login_page(self):
self.assertRedirectsLogin(response, with_host=True)
- def test_digid_fail_without_invite_and_next_url_redirects_to_login_page(self):
+ @patch("digid_eherkenning.validators.Proef11ValidatorBase.__call__")
+ def test_digid_fail_without_invite_and_next_url_redirects_to_login_page(self, m):
+ # disable mock form validation to check redirect
+ m.return_value = True
+
self.assertNotIn("invite_url", self.client.session.keys())
url = reverse("digid-mock:password")
@@ -106,7 +115,10 @@ def test_digid_fail_without_invite_and_next_url_redirects_to_login_page(self):
self.assertRedirectsLogin(response, with_host=True)
- def test_digid_fail_with_invite_redirects_to_register_page(self):
+ @patch("digid_eherkenning.validators.Proef11ValidatorBase.__call__")
+ def test_digid_fail_with_invite_redirects_to_register_page(self, m):
+ # disable mock form validation to check redirect
+ m.return_value = True
invite = InviteFactory()
session = self.client.session
session[
@@ -149,7 +161,7 @@ def test_invite_url_not_in_session_after_successful_login(self):
url = f"{url}?{urlencode(params)}"
data = {
- "auth_name": "123456789",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
@@ -175,7 +187,7 @@ def test_user_can_modify_only_email_when_digid_and_brp(self, m):
"next": reverse("profile:registration_necessary"),
}
data = {
- "auth_name": "123456789",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
url = f"{url}?{urlencode(params)}"
@@ -223,7 +235,7 @@ def test_partial_response_from_haalcentraal_when_digid_and_brp(self, m):
"next": reverse("profile:registration_necessary"),
}
data = {
- "auth_name": "123456789",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
url = f"{url}?{urlencode(params)}"
@@ -260,7 +272,7 @@ def test_first_digid_login_updates_brp_fields(self, m):
url = f"{url}?{urlencode(params)}"
data = {
- "auth_name": "123456782",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
# post our password to the IDP
@@ -691,7 +703,7 @@ def test_digid_user_success(self):
"""Assert that digid users can register with duplicate emails"""
test_user = DigidUserFactory.create(
email="test@example.com",
- bsn="123456789",
+ bsn="648197724",
)
url = reverse("digid-mock:password")
@@ -703,7 +715,7 @@ def test_digid_user_success(self):
data = {
# different BSN
- "auth_name": "112083948",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
# post our password to the IDP
@@ -780,7 +792,7 @@ def test_digid_user_non_digid_duplicate_fail(self):
url = f"{url}?{urlencode(params)}"
data = {
- "auth_name": "123456789",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
# post our password to the IDP
@@ -816,7 +828,7 @@ def test_digid_user_can_edit_profile(self):
url = f"{url}?{urlencode(params)}"
data = {
- "auth_name": "123456782",
+ "auth_name": "533458225",
"auth_pass": "bar",
}
# post our password to the IDP
diff --git a/src/open_inwoner/cms/cases/tests/test_htmx.py b/src/open_inwoner/cms/cases/tests/test_htmx.py
index e7502953d3..6d873c94e2 100644
--- a/src/open_inwoner/cms/cases/tests/test_htmx.py
+++ b/src/open_inwoner/cms/cases/tests/test_htmx.py
@@ -411,15 +411,6 @@ def test_cases(self, m):
# check case is visible
expect(page.get_by_text(self.zaak["identificatie"])).to_be_visible()
- # out-of-band anchor menu
- menu_items = page.get_by_role(
- "complementary", name=_("Secundaire paginanavigatie")
- ).get_by_role("listitem")
-
- expect(menu_items.get_by_role("link", name=_("Gegevens"))).to_be_visible()
- expect(menu_items.get_by_role("link", name=_("Status"))).to_be_visible()
- expect(menu_items.get_by_role("link", name=_("Documenten"))).to_be_visible()
-
# check documents show
documents = page.locator(".file-list").get_by_role("listitem")
diff --git a/src/open_inwoner/components/templates/components/Dashboard/Dashboard.html b/src/open_inwoner/components/templates/components/Dashboard/Dashboard.html
index cfda1c7f3b..2e6c897efa 100644
--- a/src/open_inwoner/components/templates/components/Dashboard/Dashboard.html
+++ b/src/open_inwoner/components/templates/components/Dashboard/Dashboard.html
@@ -4,9 +4,7 @@
diff --git a/src/open_inwoner/components/templatetags/dashboard_tags.py b/src/open_inwoner/components/templatetags/dashboard_tags.py
index 5544c7f5d7..371edbde37 100644
--- a/src/open_inwoner/components/templatetags/dashboard_tags.py
+++ b/src/open_inwoner/components/templatetags/dashboard_tags.py
@@ -9,7 +9,6 @@
class Metric(TypedDict):
- icon: str
label: str
value: Optional[str]
@@ -27,7 +26,7 @@ def case_dashboard(case: dict, **kwargs) -> dict:
{% case_dashboard case %}
Variables:
- + case: dict | The case to be able to build the dashboard, fetching the documents and statusses of the case.
+ + case: dict | The case to be able to build the dashboard, fetching the documents and statuses of the case.
Extra context:
+ config: DashboardConfig | The configuration of the dashboard.
@@ -35,24 +34,16 @@ def case_dashboard(case: dict, **kwargs) -> dict:
config: DashboardConfig = {
"metrics": [
{
- "icon": "inventory_2",
- "label": _("Aanvraag"),
+ "label": _("Zaaknummer:"),
"value": case.get("identification"),
},
{
- "icon": "calendar_today",
- "label": _("Datum"),
+ "label": _("Aanvraag ingediend op:"),
"value": case.get("start_date"),
},
{
- "icon": "task_alt",
- "label": _("Status"),
- "value": case.get("current_status"),
- },
- {
- "icon": "description",
- "label": _("Documenten"),
- "value": len(case.get("documents")),
+ "label": _("Verwachte uitslag:"),
+ "value": case.get("end_date_legal"),
},
]
}
@@ -80,18 +71,15 @@ def contactmoment_dashboard(kcm: KCMDict, **kwargs) -> dict:
config: DashboardConfig = {
"metrics": [
{
- "icon": "calendar_today",
- "label": _("Ontvangstdatum"),
+ "label": _("Ontvangstdatum: "),
"value": kcm.get("registered_date"),
},
{
- "icon": "inventory_2",
- "label": _("Contactwijze"),
+ "label": _("Contactwijze: "),
"value": kcm.get("channel"),
},
{
- "icon": "task_alt",
- "label": _("Status"),
+ "label": _("Status: "),
"value": kcm.get("status"),
},
]
diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py
index 95e5336465..bab3b3cad3 100644
--- a/src/open_inwoner/openzaak/admin.py
+++ b/src/open_inwoner/openzaak/admin.py
@@ -19,6 +19,7 @@
UserCaseStatusNotification,
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
+ ZaakTypeResultaatTypeConfig,
ZaakTypeStatusTypeConfig,
)
from .resources.import_resource import StatusTranslationImportResource
@@ -202,11 +203,37 @@ def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
+class ZaakTypeResultaattypeConfigInline(admin.TabularInline):
+ model = ZaakTypeResultaatTypeConfig
+ fields = [
+ "omschrijving",
+ "resultaattype_url",
+ "zaaktype_uuids",
+ "description",
+ ]
+ readonly_fields = [
+ "omschrijving",
+ "resultaattype_url",
+ "zaaktype_uuids",
+ ]
+ ordering = (
+ "zaaktype_uuids",
+ "omschrijving",
+ )
+
+ def has_add_permission(self, request, obj):
+ return False
+
+ def has_delete_permission(self, request, obj=None):
+ return request.user.is_superuser
+
+
@admin.register(ZaakTypeConfig)
class ZaakTypeConfigAdmin(admin.ModelAdmin):
inlines = [
ZaakTypeInformatieObjectTypeConfigInline,
ZaakTypeStatusTypeConfigInline,
+ ZaakTypeResultaattypeConfigInline,
]
actions = [
"mark_as_notify_status_changes",
diff --git a/src/open_inwoner/openzaak/catalog.py b/src/open_inwoner/openzaak/catalog.py
index de40998b06..c0a13b23ed 100644
--- a/src/open_inwoner/openzaak/catalog.py
+++ b/src/open_inwoner/openzaak/catalog.py
@@ -83,6 +83,26 @@ def fetch_single_status_type(status_type_url: str) -> Optional[StatusType]:
return status_type
+@cache_result(
+ "resultaat_type:{resultaat_type_url}", timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT
+)
+def fetch_single_resultaat_type(resultaat_type_url: str) -> Optional[ResultaatType]:
+ client = build_client("catalogi")
+
+ if client is None:
+ return
+
+ try:
+ response = client.retrieve("resultaattype", url=resultaat_type_url)
+ except (RequestException, ClientError) as e:
+ logger.exception("exception while making request", exc_info=e)
+ return
+
+ resultaat_type = factory(ResultaatType, response)
+
+ return resultaat_type
+
+
# not cached because only used by tools,
# and because caching (stale) listings can break lookups
def fetch_zaaktypes_no_cache() -> List[ZaakType]:
diff --git a/src/open_inwoner/openzaak/management/commands/zgw_import_data.py b/src/open_inwoner/openzaak/management/commands/zgw_import_data.py
index d032c319a7..e2915b4ebb 100644
--- a/src/open_inwoner/openzaak/management/commands/zgw_import_data.py
+++ b/src/open_inwoner/openzaak/management/commands/zgw_import_data.py
@@ -6,6 +6,7 @@
import_catalog_configs,
import_zaaktype_configs,
import_zaaktype_informatieobjecttype_configs,
+ import_zaaktype_resultaattype_configs,
import_zaaktype_statustype_configs,
)
@@ -15,7 +16,38 @@
class Command(BaseCommand):
help = "Import ZGW catalog data"
+ def log_supplement_imports_to_stdout(
+ self, import_func: callable, config_type: str
+ ) -> None:
+ """
+ Convenience function for logging zaaktype config types to stdout
+
+ Example input:
+ import_func=import_zaaktype_informatieobjecttype_configs
+ config_type="informatiebjecttype"
+
+ Example output:
+ imported 3 new zaaktype-informatiebjecttype configs
+ AAA - zaaktype-aaa
+ info-aaa-1
+ info-aaa-2
+ BBB - zaaktype-bbb
+ info-bbb
+ """
+ imported = import_func()
+
+ count = sum(len(t[1]) for t in imported)
+ self.stdout.write(f"imported {count} new zaaktype-{config_type} configs")
+
+ for ztc, config_types in sorted(imported, key=lambda t: str(t[0])):
+ self.stdout.write(str(ztc))
+ for c in sorted(map(str, config_types)):
+ self.stdout.write(f" {c}")
+
+ self.stdout.write("")
+
def handle(self, *args, **options):
+ # catalogus config
imported = import_catalog_configs()
self.stdout.write(f"imported {len(imported)} new catalogus configs")
@@ -24,6 +56,7 @@ def handle(self, *args, **options):
self.stdout.write("")
+ # zaaktype config
imported = import_zaaktype_configs()
self.stdout.write(f"imported {len(imported)} new zaaktype configs")
@@ -32,24 +65,13 @@ def handle(self, *args, **options):
self.stdout.write("")
- imported = import_zaaktype_informatieobjecttype_configs()
-
- count = sum(len(t[1]) for t in imported)
-
- self.stdout.write(f"imported {count} new zaaktype-informatiebjecttype configs")
- for ztc, info_types in sorted(imported, key=lambda t: str(t[0])):
- self.stdout.write(str(ztc))
- for c in sorted(map(str, info_types)):
- self.stdout.write(f" {c}")
-
- self.stdout.write("")
-
- imported = import_zaaktype_statustype_configs()
-
- count = sum(len(t[1]) for t in imported)
-
- self.stdout.write(f"imported {count} new zaaktype-statustype configs")
- for ztc, status_types in sorted(imported, key=lambda t: str(t[0])):
- self.stdout.write(str(ztc))
- for c in sorted(map(str, status_types)):
- self.stdout.write(f" {c}")
+ # supplemental configs
+ self.log_supplement_imports_to_stdout(
+ import_zaaktype_informatieobjecttype_configs, "informatiebjecttype"
+ )
+ self.log_supplement_imports_to_stdout(
+ import_zaaktype_statustype_configs, "statustype"
+ )
+ self.log_supplement_imports_to_stdout(
+ import_zaaktype_resultaattype_configs, "resultaattype"
+ )
diff --git a/src/open_inwoner/openzaak/migrations/0027_zaaktype_resultaattype_config.py b/src/open_inwoner/openzaak/migrations/0027_zaaktype_resultaattype_config.py
index 840666039f..9b32306884 100644
--- a/src/open_inwoner/openzaak/migrations/0027_zaaktype_resultaattype_config.py
+++ b/src/open_inwoner/openzaak/migrations/0027_zaaktype_resultaattype_config.py
@@ -1,8 +1,7 @@
# Generated by Django 3.2.20 on 2023-10-26 10:05
-import django.db.models.deletion
from django.db import migrations, models
-
+import django.db.models.deletion
import django_better_admin_arrayfield.models.fields
diff --git a/src/open_inwoner/openzaak/models.py b/src/open_inwoner/openzaak/models.py
index 6d1e998c96..49995e1bba 100644
--- a/src/open_inwoner/openzaak/models.py
+++ b/src/open_inwoner/openzaak/models.py
@@ -377,6 +377,50 @@ def __str__(self):
return f"{self.zaaktype_config.identificatie} - {self.omschrijving}"
+class ZaakTypeResultaatTypeConfig(models.Model):
+ zaaktype_config = models.ForeignKey(
+ "openzaak.ZaakTypeConfig",
+ on_delete=models.CASCADE,
+ )
+ resultaattype_url = models.URLField(
+ verbose_name=_("Resultaattype URL"),
+ max_length=1000,
+ )
+ omschrijving = models.CharField(
+ verbose_name=_("Omschrijving"),
+ max_length=20,
+ )
+ zaaktype_uuids = ArrayField(
+ models.UUIDField(
+ verbose_name=_("Zaaktype UUID"),
+ ),
+ default=list,
+ )
+
+ # configuration
+ description = models.TextField(
+ blank=True,
+ default="",
+ verbose_name=_("Frontend description"),
+ help_text=_(
+ "Determines the text that will be shown to the user if a case is set to this result"
+ ),
+ )
+
+ class Meta:
+ verbose_name = _("Zaaktype Resultaattype Configuration")
+
+ constraints = [
+ UniqueConstraint(
+ name="unique_zaaktype_config_resultaattype_url",
+ fields=["zaaktype_config", "resultaattype_url"],
+ )
+ ]
+
+ def __str__(self):
+ return f"{self.zaaktype_config.identificatie} - {self.omschrijving}"
+
+
class UserCaseStatusNotificationBase(models.Model):
user = models.ForeignKey(
"accounts.User",
diff --git a/src/open_inwoner/openzaak/tests/test_case_detail.py b/src/open_inwoner/openzaak/tests/test_case_detail.py
index 3dedeabf24..bac41a4e45 100644
--- a/src/open_inwoner/openzaak/tests/test_case_detail.py
+++ b/src/open_inwoner/openzaak/tests/test_case_detail.py
@@ -496,8 +496,6 @@ def test_page_displays_expected_data(self, m):
self.assertContains(response, "ZAAK-2022-0000000024")
self.assertContains(response, "Coffee zaaktype")
self.assertContains(response, "uploaded_document_title")
- self.assertContains(response, "Foo Bar van der Bazz")
- self.assertContains(response, "resultaat toelichting")
def test_page_reformats_zaak_identificatie(self, m):
self._setUpMocks(m)
diff --git a/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py b/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py
index 88542d6a81..21b71a6e31 100644
--- a/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py
+++ b/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py
@@ -12,6 +12,7 @@
OpenZaakConfig,
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
+ ZaakTypeResultaatTypeConfig,
ZaakTypeStatusTypeConfig,
)
from open_inwoner.openzaak.tests.factories import ServiceFactory
@@ -49,6 +50,7 @@ def test_zgw_import_data_command(self, m):
self.assertEqual(ZaakTypeConfig.objects.count(), 2)
self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 3)
self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2)
+ self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2)
stdout = out.getvalue().strip()
@@ -73,6 +75,12 @@ def test_zgw_import_data_command(self, m):
AAA - zaaktype-aaa
AAA - status-aaa-1
AAA - status-aaa-2
+
+ imported 2 new zaaktype-resultaattype configs
+ AAA - zaaktype-aaa
+ AAA - test
+ BBB - zaaktype-bbb
+ BBB - test
"""
).strip()
@@ -87,6 +95,7 @@ def test_zgw_import_data_command(self, m):
self.assertEqual(ZaakTypeConfig.objects.count(), 2)
self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 3)
self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2)
+ self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2)
stdout = out.getvalue().strip()
@@ -99,6 +108,8 @@ def test_zgw_import_data_command(self, m):
imported 0 new zaaktype-informatiebjecttype configs
imported 0 new zaaktype-statustype configs
+
+ imported 0 new zaaktype-resultaattype configs
"""
).strip()
@@ -111,7 +122,7 @@ def test_zgw_import_data_command_without_catalog(self, m):
)
InformationObjectTypeMockData().install_mocks(m, with_catalog=False)
- # run it to import our data
+ # # run it to import our data
out = StringIO()
call_command("zgw_import_data", stdout=out)
@@ -119,6 +130,7 @@ def test_zgw_import_data_command_without_catalog(self, m):
self.assertEqual(ZaakTypeConfig.objects.count(), 2)
self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 3)
self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2)
+ self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2)
stdout = out.getvalue().strip()
@@ -141,6 +153,12 @@ def test_zgw_import_data_command_without_catalog(self, m):
AAA - zaaktype-aaa
AAA - status-aaa-1
AAA - status-aaa-2
+
+ imported 2 new zaaktype-resultaattype configs
+ AAA - zaaktype-aaa
+ AAA - test
+ BBB - zaaktype-bbb
+ BBB - test
"""
).strip()
@@ -155,6 +173,7 @@ def test_zgw_import_data_command_without_catalog(self, m):
self.assertEqual(ZaakTypeConfig.objects.count(), 2)
self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 3)
self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2)
+ self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2)
stdout = out.getvalue().strip()
@@ -167,6 +186,8 @@ def test_zgw_import_data_command_without_catalog(self, m):
imported 0 new zaaktype-informatiebjecttype configs
imported 0 new zaaktype-statustype configs
+
+ imported 0 new zaaktype-resultaattype configs
"""
).strip()
diff --git a/src/open_inwoner/openzaak/tests/test_zgw_imports_iotypes.py b/src/open_inwoner/openzaak/tests/test_zgw_imports_iotypes.py
index 1612fa80c4..79f3984017 100644
--- a/src/open_inwoner/openzaak/tests/test_zgw_imports_iotypes.py
+++ b/src/open_inwoner/openzaak/tests/test_zgw_imports_iotypes.py
@@ -88,6 +88,18 @@ def __init__(self):
statustypen=[
self.statustype_aaa_1["url"],
],
+ resultaattypen=[
+ f"{CATALOGI_ROOT}resultaatypen/b1a268dd-4322-47bb-a930-b83066b4a32c"
+ ],
+ )
+ self.resultaat_type_1 = generate_oas_component(
+ "ztc",
+ "schemas/ResultaatType",
+ url=f"{CATALOGI_ROOT}resultaatypen/b1a268dd-4322-47bb-a930-b83066b4a32c",
+ zaaktype=self.zaaktype_aaa_1,
+ omschrijving="test",
+ resultaattypeomschrijving="test1",
+ selectielijstklasse="ABC",
)
self.zaaktype_bbb = generate_oas_component(
"ztc",
@@ -103,6 +115,9 @@ def __init__(self):
self.info_type_bbb["url"],
],
statustypen=[],
+ resultaattypen=[
+ f"{CATALOGI_ROOT}resultaatypen/b1a268dd-4322-47bb-a930-b83066b4a32c"
+ ],
)
self.zaaktype_aaa_2 = generate_oas_component(
"ztc",
@@ -121,6 +136,9 @@ def __init__(self):
statustypen=[
self.statustype_aaa_2["url"],
],
+ resultaattypen=[
+ f"{CATALOGI_ROOT}resultaatypen/b1a268dd-4322-47bb-a930-b83066b4a32c",
+ ],
)
self.zaaktype_aaa_intern = generate_oas_component(
"ztc",
@@ -137,6 +155,7 @@ def __init__(self):
self.info_type_aaa_1["url"],
],
statustypen=[],
+ resultaattypen=[],
)
self.extra_zaaktype_aaa = generate_oas_component(
"ztc",
@@ -155,6 +174,9 @@ def __init__(self):
self.extra_info_type_aaa_3["url"],
],
statustypen=[],
+ resultaattypen=[
+ self.resultaat_type_1["url"],
+ ],
)
self.all_io_types = [
@@ -174,6 +196,9 @@ def __init__(self):
self.statustype_aaa_1,
self.statustype_aaa_2,
]
+ self.all_resultaat_types = [
+ self.resultaat_type_1,
+ ]
def setUpOASMocks(self, m):
mock_service_oas_get(m, CATALOGI_ROOT, "ztc")
@@ -188,6 +213,7 @@ def install_mocks(self, m, *, with_catalog=True) -> "InformationObjectTypeMockDa
self.extra_info_type_aaa_3,
self.statustype_aaa_1,
self.statustype_aaa_2,
+ self.resultaat_type_1,
]:
m.get(resource["url"], json=resource)
@@ -202,6 +228,14 @@ def install_mocks(self, m, *, with_catalog=True) -> "InformationObjectTypeMockDa
]
),
)
+ m.get(
+ f"{CATALOGI_ROOT}resultaattypen",
+ json=paginated_response(
+ [
+ self.resultaat_type_1,
+ ]
+ ),
+ )
if with_catalog:
cat_a = f"&catalogus={CATALOGI_ROOT}catalogussen/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
diff --git a/src/open_inwoner/openzaak/zgw_imports.py b/src/open_inwoner/openzaak/zgw_imports.py
index 38d6b3a276..038a5fcac1 100644
--- a/src/open_inwoner/openzaak/zgw_imports.py
+++ b/src/open_inwoner/openzaak/zgw_imports.py
@@ -3,13 +3,18 @@
from django.db import transaction
-from zgw_consumers.api_models.catalogi import InformatieObjectType, StatusType
+from zgw_consumers.api_models.catalogi import (
+ InformatieObjectType,
+ ResultaatType,
+ StatusType,
+)
from open_inwoner.openzaak.api_models import ZaakType
from open_inwoner.openzaak.catalog import (
fetch_case_types_by_identification_no_cache,
fetch_catalogs_no_cache,
fetch_single_information_object_type,
+ fetch_single_resultaat_type,
fetch_single_status_type,
fetch_zaaktypes_no_cache,
)
@@ -17,6 +22,7 @@
CatalogusConfig,
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
+ ZaakTypeResultaatTypeConfig,
ZaakTypeStatusTypeConfig,
)
@@ -148,6 +154,20 @@ def import_zaaktype_statustype_configs() -> List[Tuple[ZaakTypeConfig, StatusTyp
return created
+def import_zaaktype_resultaattype_configs() -> List[
+ Tuple[ZaakTypeConfig, ResultaatType]
+]:
+ """
+ generate ZaakTypeResultaatTypeConfigs for all ZaakTypeConfig
+ """
+ created = []
+ for ztc in ZaakTypeConfig.objects.all():
+ imported = import_resultaattype_configs_for_type(ztc)
+ if imported:
+ created.append((ztc, imported))
+ return created
+
+
def import_zaaktype_informatieobjecttype_configs_for_type(
ztc: ZaakTypeConfig,
) -> List[ZaakTypeInformatieObjectTypeConfig]:
@@ -243,7 +263,7 @@ def import_statustype_configs_for_type(
for zaaktype_statustype in ztc.zaaktypestatustypeconfig_set.all()
}
- # collect and implicitly de-duplicate informatieobjecttype url's and track which zaaktype used it
+ # collect and implicitly de-duplicate statustype url's and track which zaaktype used it
info_queue = defaultdict(list)
for zaak_type in zaak_types:
for url in zaak_type.statustypen:
@@ -282,3 +302,70 @@ def import_statustype_configs_for_type(
ZaakTypeStatusTypeConfig.objects.bulk_update(update, ["zaaktype_uuids"])
return create
+
+
+def import_resultaattype_configs_for_type(
+ ztc: ZaakTypeConfig,
+) -> List[ZaakTypeResultaatTypeConfig]:
+ """
+ generate ZaakTypeResultaatTypeConfigs for all ResultaatTypes used by each ZaakTypeConfigs source ZaakTypes
+
+ this is a bit complicated because one ZaakTypeConfig can represent multiple ZaakTypes
+ """
+
+ # grab actual ZaakTypes for this identificatie
+ zaak_types: List[ZaakType] = get_configurable_zaaktypes_by_identification(
+ ztc.identificatie, ztc.catalogus_url
+ )
+ if not zaak_types:
+ return []
+
+ create = []
+ update = []
+
+ with transaction.atomic():
+ # map existing config records by url
+
+ info_map = {
+ zaaktype_resultaattype.resultaattype_url: zaaktype_resultaattype
+ for zaaktype_resultaattype in ztc.zaaktyperesultaattypeconfig_set.all()
+ }
+
+ # collect and implicitly de-duplicate resultaattype url's and track which zaaktype used it
+ info_queue = defaultdict(list)
+ for zaak_type in zaak_types:
+ for url in zaak_type.resultaattypen:
+ info_queue[url].append(zaak_type)
+
+ if info_queue:
+ # load urls and update/create records
+ for resultaattype_url, using_zaak_types in info_queue.items():
+ resultaat_type = fetch_single_resultaat_type(resultaattype_url)
+
+ zaaktype_resultaattype = info_map.get(resultaat_type.url)
+ if zaaktype_resultaattype:
+ # we got a record for this, see if we got data to update
+ for using in using_zaak_types:
+ # track which zaaktype UUID's are interested in this resultaattype
+ if using.uuid not in zaaktype_resultaattype.zaaktype_uuids:
+ zaaktype_resultaattype.zaaktype_uuids.append(using.uuid)
+ if zaaktype_resultaattype not in create:
+ update.append(zaaktype_resultaattype)
+ else:
+ # new record
+ zaaktype_resultaattype = ZaakTypeResultaatTypeConfig(
+ zaaktype_config=ztc,
+ resultaattype_url=resultaat_type.url,
+ omschrijving=resultaat_type.omschrijving,
+ zaaktype_uuids=[zt.uuid for zt in using_zaak_types],
+ )
+ create.append(zaaktype_resultaattype)
+ # not strictly necessary but let's be accurate
+ info_map[resultaat_type.uuid] = zaaktype_resultaattype
+
+ if create:
+ ZaakTypeResultaatTypeConfig.objects.bulk_create(create)
+ if update:
+ ZaakTypeResultaatTypeConfig.objects.bulk_update(update, ["zaaktype_uuids"])
+
+ return create
diff --git a/src/open_inwoner/scss/components/Cases/Cases.scss b/src/open_inwoner/scss/components/Cases/Cases.scss
index 372613b974..38f6a718a4 100644
--- a/src/open_inwoner/scss/components/Cases/Cases.scss
+++ b/src/open_inwoner/scss/components/Cases/Cases.scss
@@ -1,6 +1,22 @@
.cases {
margin-top: var(--spacing-giant);
+ &__grid .grid {
+ grid-row-gap: var(--spacing-medium);
+ grid-column-gap: var(--spacing-extra-large);
+
+ // Tablets
+ @media (min-width: 768px) and (max-width: 900px) {
+ grid-template-columns: 1fr;
+ }
+
+ .column--start-4 {
+ @media (min-width: 768px) and (max-width: 900px) {
+ grid-column-start: 1;
+ }
+ }
+ }
+
/// cards on cases page
.card {
.cases__link {
diff --git a/src/open_inwoner/scss/components/Contactmomenten/Contactmomenten.scss b/src/open_inwoner/scss/components/Contactmomenten/Contactmomenten.scss
index 2aaa67d79e..fd6b11bdfa 100644
--- a/src/open_inwoner/scss/components/Contactmomenten/Contactmomenten.scss
+++ b/src/open_inwoner/scss/components/Contactmomenten/Contactmomenten.scss
@@ -8,3 +8,28 @@
}
}
}
+
+.contactmoment {
+ &__details {
+ // Set table width for word-break
+ table {
+ margin-top: 0;
+ overflow-x: auto;
+ table-layout: fixed;
+ width: 100%;
+ @media (min-width: 767px) {
+ width: var(--mobile-ms-width);
+ }
+ }
+ table th {
+ width: 100px;
+ white-space: nowrap;
+ }
+ table td {
+ width: 300px;
+ overflow: hidden;
+ word-break: break-word;
+ white-space: normal;
+ }
+ }
+}
diff --git a/src/open_inwoner/scss/components/Container/Container.scss b/src/open_inwoner/scss/components/Container/Container.scss
index 9a699cfdfe..b31155e94a 100644
--- a/src/open_inwoner/scss/components/Container/Container.scss
+++ b/src/open_inwoner/scss/components/Container/Container.scss
@@ -14,7 +14,9 @@
}
@media (min-width: 768px) {
- $hm: max(calc((100vw - var(--container-width)) / 2), var(--spacing-large));
+ // $hm has a minimum of var(--spacing-large), and a maximum of $hlargest
+ $hlargest: calc(((100vw - var(--container-width)) / 2) - 7px);
+ $hm: max($hlargest, var(--spacing-large));
margin: var(--spacing-extra-large) $hm calc(var(--row-height) * 2);
&--no-margin {
diff --git a/src/open_inwoner/scss/components/Dashboard/Dashboard.scss b/src/open_inwoner/scss/components/Dashboard/Dashboard.scss
index 4d495f9541..ead34f4832 100644
--- a/src/open_inwoner/scss/components/Dashboard/Dashboard.scss
+++ b/src/open_inwoner/scss/components/Dashboard/Dashboard.scss
@@ -1,28 +1,57 @@
.dashboard {
+ margin-bottom: var(--spacing-mega);
+
&__list {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: var(--spacing-large) var(--spacing-medium);
- justify-content: space-between;
+ display: block;
list-style: none;
margin: 0;
padding: 0;
+ border-bottom: var(--border-width-thin) solid var(--color-gray);
- @media (min-width: 768px) {
+ @media (min-width: 900px) {
display: flex;
- gap: var(--spacing-medium);
+ gap: 0;
+ border-top: var(--border-width-thin) solid var(--color-gray);
}
}
&__list-item {
+ padding: var(--spacing-large) 0;
+ color: var(--color-gray-dark);
white-space: nowrap;
- text-align: center;
+ border-top: var(--border-width-thin) solid var(--color-gray);
+ @media (min-width: 900px) {
+ border-top: none;
+ margin-left: var(--spacing-extra-large);
+ }
- @media (min-width: 768px) {
+ &:first-child {
+ margin-left: 0;
+ }
+
+ p::after {
+ color: var(--color-gray);
+ @media (min-width: 900px) {
+ content: '|';
+ margin-left: var(--spacing-extra-large);
+ }
+ }
+
+ &:last-child p::after {
+ @media (min-width: 900px) {
+ content: none;
+ }
+ }
+
+ @media (min-width: 900px) {
text-align: start;
}
}
+ &__item-label {
+ color: var(--color-gray-90);
+ }
+
.h4 {
@media (min-width: 768px) {
margin-top: var(--spacing-large);
diff --git a/src/open_inwoner/scss/components/Grid/Grid.scss b/src/open_inwoner/scss/components/Grid/Grid.scss
index c8b045be5b..a1bdfee348 100644
--- a/src/open_inwoner/scss/components/Grid/Grid.scss
+++ b/src/open_inwoner/scss/components/Grid/Grid.scss
@@ -18,7 +18,8 @@
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 1fr;
- gap: var(--gutter-width);
+ grid-row-gap: var(--spacing-extra-large);
+ grid-column-gap: var(--gutter-width);
}
/// Layout.
diff --git a/src/open_inwoner/scss/components/Spinner/Spinner.scss b/src/open_inwoner/scss/components/Spinner/Spinner.scss
index 3604b85c81..9bc0ba8192 100644
--- a/src/open_inwoner/scss/components/Spinner/Spinner.scss
+++ b/src/open_inwoner/scss/components/Spinner/Spinner.scss
@@ -20,6 +20,11 @@
align-items: flex-start;
height: 70vh;
margin: 100px 0 0 200px;
+
+ &.case__spinner-container {
+ justify-content: center;
+ margin: 100px 0 0 0;
+ }
}
.spinner {
diff --git a/src/open_inwoner/scss/views/App.scss b/src/open_inwoner/scss/views/App.scss
index db1aa713b7..1999e15287 100644
--- a/src/open_inwoner/scss/views/App.scss
+++ b/src/open_inwoner/scss/views/App.scss
@@ -25,6 +25,7 @@
--color-black: #000;
--color-blue: #1261a3;
--color-gray: #d2d2d2;
+ --color-gray-90: #676767;
--color-gray-dark: #4b4b4b;
--color-gray-lighter: #7a7a7a;
--color-gray-light: #d2d2d2;
@@ -246,6 +247,7 @@
--spacing-large: 16px;
--spacing-extra-large: 24px;
--spacing-giant: 32px;
+ --spacing-mega: 80px;
/// Common widths
--form-width: 500px;
diff --git a/src/open_inwoner/templates/pages/cases/status_inner.html b/src/open_inwoner/templates/pages/cases/status_inner.html
index 42b911fad2..c6b129d678 100644
--- a/src/open_inwoner/templates/pages/cases/status_inner.html
+++ b/src/open_inwoner/templates/pages/cases/status_inner.html
@@ -1,31 +1,22 @@
{% load i18n ssd_tags anchor_menu_tags card_tags dashboard_tags file_tags grid_tags table_tags solo_tags link_tags button_tags icon_tags notification_tags %}
-{# Anchor menu-mobile #}
-
- {% anchor_menu anchors=anchors desktop=False %}
-
-
{# Messages #}
{% notifications messages %}
-{# Anchor menu-desktop #}
-
- {% anchor_menu anchors desktop=True %}
-
-
{% get_solo 'openzaak.OpenZaakConfig' as openzaak_config %}
+
{% if case %}
{% render_grid %}
- {% render_column span=9 %}
-
+ {% render_column span=12 %}
{# Title/dashboard. #}
{{ case.description }}
{% case_dashboard case %}
- {% case_table case %}
+ {% endrender_column %}
+ {% render_column start=4 span=6 %}
{# Status history. #}
{% if case.statuses %}
{% trans 'Status' %}
@@ -151,3 +142,4 @@ {% trans "Document toevoegen" %}
{% else %}
{% trans 'There is no available data at the moment.' %}
{% endif %}
+
diff --git a/src/open_inwoner/templates/pages/cases/status_outer.html b/src/open_inwoner/templates/pages/cases/status_outer.html
index ed7b79a8da..30cfdcb081 100644
--- a/src/open_inwoner/templates/pages/cases/status_outer.html
+++ b/src/open_inwoner/templates/pages/cases/status_outer.html
@@ -1,22 +1,14 @@
{% extends 'master.html' %}
{% load i18n icon_tags %}
-{% block mobile_anchors %}
-
-{% endblock mobile_anchors %}
-
{% block notifications %}
{% endblock notifications %}
-{% block sidebar_content %}
-
-{% endblock sidebar_content %}
-
{% block content %}
-
+
{% icon icon="rotate_right" extra_classes="spinner-icon rotate" %}
{% trans "Gegevens laden..." %}
diff --git a/src/open_inwoner/templates/pages/contactmoment/detail.html b/src/open_inwoner/templates/pages/contactmoment/detail.html
index 9055fe43fc..d55d0fb0f8 100644
--- a/src/open_inwoner/templates/pages/contactmoment/detail.html
+++ b/src/open_inwoner/templates/pages/contactmoment/detail.html
@@ -1,18 +1,19 @@
{% extends 'master.html' %}
{% load i18n anchor_menu_tags card_tags dashboard_tags file_tags grid_tags table_tags solo_tags form_tags button_tags %}
-{% block sidebar_content %}
- {% if contactmoment %}
- {% anchor_menu anchors desktop=True %}
- {% endif %}
-{% endblock sidebar_content %}
-
{% block content %}
{% if contactmoment %}
{% render_grid %}
- {% render_column span=9 %}
+ {% render_column span=12 %}
+ {# Contactmoment/dashboard. #}
+
{{ page_title }}
{% contactmoment_dashboard contactmoment %}
+ {% endrender_column %}
+ {% render_column start=4 span=6 %}
+
+
{% contactmoment_table contactmoment %}
+
{% endrender_column %}
{% endrender_grid %}
{% else %}