Skip to content

Commit c29d146

Browse files
authored
Merge pull request #389 from maykinmedia/fix/386-blijven-bewaren
[#386] Checks for selectielijstklasse waardering "blijvend bewaren" [Backend]
2 parents 554acf1 + c5ae7d1 commit c29d146

File tree

9 files changed

+400
-58
lines changed

9 files changed

+400
-58
lines changed

backend/docs/developers/logic.rst

+14-2
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,17 @@ For each zaak that has been "rejected" by the reviewer, the record manager can:
1010
- Keep the zaak in the destruction list (go against the suggestion of the reviewer). In this case, no details of the zaak can be updated.
1111
- Remove the zaak from the destruction list. When removing the zaak, the record manager should either:
1212

13-
- Update the selectielijstklasse of the zaak. This should automatically update the archiefactiedatum of the zaak (this is currently done by the frontend). This is for example desirable in the case that the zaak has the wrong selectielijstklasse by accident.
14-
- Update the archiefactiedatum of the zaak. In this case the selectielijstklasse is correct, but for whatever reason the zaak needs to be kept for longer.
13+
- Update the selectielijstklasse of the zaak.
14+
15+
This should automatically update the archiefactiedatum of the zaak (this is currently done by the frontend).
16+
*Example*: case where the zaak has the wrong selectielijstklasse by accident.
17+
18+
The record manager needs to still be able to update the archiefactiedatum manually.
19+
20+
In the case where the selectielijstklasse has "waardering" equal to "blijven_bewaren",
21+
then the archiefactiedatum must be empty and the record manager cannot change it.
22+
23+
- Update the archiefactiedatum of the zaak.
24+
25+
In this case the selectielijstklasse is correct, but for whatever reason the zaak needs to be kept for longer.
26+
Changing the archiefactiedatum for zaken with "waardering" equal to "blijven_bewaren" should not be possible.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ServiceNotConfigured(Exception):
2+
pass

backend/src/openarchiefbeheer/destruction/api/serializers.py

+87-24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.utils.translation import gettext_lazy as _
66

77
from drf_spectacular.utils import extend_schema_field
8+
from requests.exceptions import HTTPError
89
from rest_framework import serializers
910
from rest_framework.exceptions import ValidationError
1011
from rest_framework.relations import SlugRelatedField
@@ -16,6 +17,7 @@
1617
from openarchiefbeheer.zaken.api.filtersets import ZaakFilter
1718
from openarchiefbeheer.zaken.api.serializers import ZaakSerializer
1819
from openarchiefbeheer.zaken.models import Zaak
20+
from openarchiefbeheer.zaken.utils import retrieve_selectielijstklasse_resultaat
1921

2022
from ..constants import (
2123
DestructionListItemAction,
@@ -497,6 +499,11 @@ def to_internal_value(self, data: dict) -> dict:
497499

498500

499501
class ReviewItemResponseSerializer(serializers.ModelSerializer):
502+
review_item = serializers.PrimaryKeyRelatedField(
503+
queryset=DestructionListItemReview.objects.all().select_related(
504+
"destruction_list_item__zaak"
505+
),
506+
)
500507
action_zaak = ActionZaakSerializer(required=False)
501508

502509
class Meta:
@@ -511,8 +518,79 @@ class Meta:
511518
"comment",
512519
)
513520

521+
def _get_selectielijst_resultaat(self, resultaat_url: str) -> dict:
522+
try:
523+
resultaat = retrieve_selectielijstklasse_resultaat(resultaat_url)
524+
except HTTPError:
525+
raise ValidationError(
526+
_(
527+
"Could not validate the selectielijstklasse waardering "
528+
"due to an unexpected response from the Selectielijst API."
529+
)
530+
)
531+
532+
return resultaat
533+
534+
def _validate_action_zaak_with_type(
535+
self,
536+
action_zaak: dict,
537+
action_zaak_type: str,
538+
review_item: DestructionListItemReview,
539+
) -> None:
540+
# TODO this could be refactored using a polymorphic serializer
541+
selectielijstklasse = action_zaak.get("selectielijstklasse")
542+
archiefactiedatum = action_zaak.get("archiefactiedatum")
543+
544+
match action_zaak_type:
545+
case ZaakActionType.selectielijstklasse_and_bewaartermijn:
546+
if not selectielijstklasse:
547+
raise ValidationError(
548+
{
549+
"selectielijstklasse": _(
550+
"The selectielijstklasse is required for action type "
551+
"is 'selectielijstklasse_and_bewaartermijn'."
552+
)
553+
}
554+
)
555+
556+
resultaat = self._get_selectielijst_resultaat(selectielijstklasse)
557+
if resultaat["waardering"] == "blijvend_bewaren" and archiefactiedatum:
558+
raise ValidationError(
559+
{
560+
"selectielijstklasse": _(
561+
"The selectielijstklasse has waardering 'blijvend_bewaren', "
562+
"so the archiefactiedatum should be null."
563+
)
564+
}
565+
)
566+
567+
case ZaakActionType.bewaartermijn:
568+
if selectielijstklasse:
569+
raise ValidationError(
570+
{
571+
"action_zaak_type": _(
572+
"The selectielijstklasse cannot be changed if the case action type is 'bewaartermijn'."
573+
)
574+
}
575+
)
576+
577+
resultaat = self._get_selectielijst_resultaat(
578+
review_item.destruction_list_item.zaak.selectielijstklasse
579+
)
580+
if resultaat["waardering"] == "blijvend_bewaren":
581+
raise ValidationError(
582+
{
583+
"archiefactiedatum": _(
584+
"The selectielijstklasse has waardering 'blijvend_bewaren', "
585+
"so an archiefactiedatum cannot be set."
586+
)
587+
}
588+
)
589+
514590
def validate(self, attrs: dict) -> dict:
515591
action_zaak = attrs.get("action_zaak", {})
592+
action_zaak_type = attrs.get("action_zaak_type", {})
593+
516594
if attrs["action_item"] == DestructionListItemAction.keep and action_zaak:
517595
raise ValidationError(
518596
{
@@ -522,32 +600,17 @@ def validate(self, attrs: dict) -> dict:
522600
}
523601
)
524602

525-
zaak_action_type = attrs.get("action_zaak_type")
526-
selectielijstklasse = action_zaak.get("selectielijstklasse")
527-
if (
528-
attrs["action_item"] == DestructionListItemAction.remove
529-
and zaak_action_type == ZaakActionType.bewaartermijn
530-
and selectielijstklasse
531-
):
532-
raise ValidationError(
533-
{
534-
"action_zaak_type": _(
535-
"The selectielijstklasse cannot be changed if the case action type is 'bewaartermijn'."
603+
if attrs["action_item"] == DestructionListItemAction.remove:
604+
if not action_zaak_type or not action_zaak:
605+
raise ValidationError(
606+
_(
607+
"When removing an item from a destruction list, "
608+
"the fields action_zaak_type and action_zaak are required."
536609
)
537-
}
538-
)
610+
)
539611

540-
if (
541-
attrs["action_item"] == DestructionListItemAction.remove
542-
and zaak_action_type == ZaakActionType.selectielijstklasse_and_bewaartermijn
543-
and not selectielijstklasse
544-
):
545-
raise ValidationError(
546-
{
547-
"selectielijstklasse": _(
548-
"The selectielijstklasse is required for action type is 'selectielijstklasse_and_bewaartermijn'."
549-
)
550-
}
612+
self._validate_action_zaak_with_type(
613+
action_zaak, action_zaak_type, attrs["review_item"]
551614
)
552615

553616
return attrs

backend/src/openarchiefbeheer/destruction/tests/e2e/features/test_feature_list_process_review.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# fmt: off
2+
from unittest.mock import patch
3+
24
from django.test import tag
35

46
from asgiref.sync import sync_to_async
@@ -24,6 +26,13 @@
2426
@tag("e2e")
2527
class FeatureProcessReviewTests(GherkinLikeTestCase):
2628
async def test_scenario_record_manager_process_review(self):
29+
patcher = patch(
30+
"openarchiefbeheer.destruction.api.serializers.retrieve_selectielijstklasse_resultaat",
31+
return_value={"waardering": "vernietigen"}
32+
)
33+
patcher.start()
34+
self.addCleanup(patcher.stop)
35+
2736
async with browser_page() as page:
2837
await self.given.selectielijstklasse_choices_are_available(page)
2938
record_manager = await self.given.record_manager_exists()

backend/src/openarchiefbeheer/destruction/tests/endpoints/test_reviewresponse.py

+40-32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from unittest.mock import patch
2+
13
from django.utils.translation import gettext_lazy as _
24

35
import freezegun
@@ -61,46 +63,52 @@ def test_create_review_response(self):
6163
items_reviews = DestructionListItemReviewFactory.create_batch(
6264
3,
6365
destruction_list_item__destruction_list=review.destruction_list,
66+
destruction_list_item__with_zaak=True,
67+
destruction_list_item__zaak__selectielijstklasse="http://some-url.nl",
6468
review=review,
6569
)
6670

6771
endpoint = reverse("api:review-responses-list")
6872
self.client.force_authenticate(user=record_manager)
6973

70-
response = self.client.post(
71-
endpoint,
72-
data={
73-
"review": review.pk,
74-
"comment": "A comment about the review.",
75-
"itemsResponses": [
76-
{
77-
"reviewItem": items_reviews[0].pk,
78-
"actionItem": DestructionListItemAction.keep,
79-
"comment": "This zaak needs to stay in the list.",
80-
},
81-
{
82-
"reviewItem": items_reviews[1].pk,
83-
"actionItem": DestructionListItemAction.remove,
84-
"actionZaak": {
85-
"action_type": ZaakActionType.selectielijstklasse_and_bewaartermijn,
86-
"selectielijstklasse": "http://some-url.nl",
87-
"archiefactiedatum": "2030-01-01",
74+
with patch(
75+
"openarchiefbeheer.destruction.api.serializers.retrieve_selectielijstklasse_resultaat",
76+
return_value={"waardering": "vernietigen"},
77+
):
78+
response = self.client.post(
79+
endpoint,
80+
data={
81+
"review": review.pk,
82+
"comment": "A comment about the review.",
83+
"itemsResponses": [
84+
{
85+
"reviewItem": items_reviews[0].pk,
86+
"actionItem": DestructionListItemAction.keep,
87+
"comment": "This zaak needs to stay in the list.",
8888
},
89-
"comment": "Changed the selectielijstklasse and removed from the list.",
90-
},
91-
{
92-
"reviewItem": items_reviews[2].pk,
93-
"actionItem": DestructionListItemAction.remove,
94-
"actionZaak": {
95-
"action_type": ZaakActionType.bewaartermijn,
96-
"archiefactiedatum": "2030-01-01",
89+
{
90+
"reviewItem": items_reviews[1].pk,
91+
"actionItem": DestructionListItemAction.remove,
92+
"actionZaakType": ZaakActionType.selectielijstklasse_and_bewaartermijn,
93+
"actionZaak": {
94+
"selectielijstklasse": "http://some-url.nl",
95+
"archiefactiedatum": "2030-01-01",
96+
},
97+
"comment": "Changed the selectielijstklasse and removed from the list.",
9798
},
98-
"comment": "Changed the archiefactiedatum and removed from the list.",
99-
},
100-
],
101-
},
102-
format="json",
103-
)
99+
{
100+
"reviewItem": items_reviews[2].pk,
101+
"actionItem": DestructionListItemAction.remove,
102+
"actionZaakType": ZaakActionType.bewaartermijn,
103+
"actionZaak": {
104+
"archiefactiedatum": "2030-01-01",
105+
},
106+
"comment": "Changed the archiefactiedatum and removed from the list.",
107+
},
108+
],
109+
},
110+
format="json",
111+
)
104112

105113
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
106114
self.assertEqual(ReviewResponse.objects.filter(review=review).count(), 1)

backend/src/openarchiefbeheer/destruction/tests/serializers/test_reviewresponse.py

+57
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
from unittest.mock import patch
2+
13
from django.test import TestCase
24
from django.utils.translation import gettext_lazy as _
35

6+
from requests.exceptions import HTTPError
7+
from rest_framework.test import APIRequestFactory
8+
49
from openarchiefbeheer.accounts.tests.factories import UserFactory
510

611
from ...api.serializers import ReviewResponseSerializer
712
from ...constants import DestructionListItemAction, ListStatus, ZaakActionType
813
from ..factories import DestructionListItemReviewFactory, DestructionListReviewFactory
914

15+
factory = APIRequestFactory()
16+
1017

1118
class ReviewResponseSerializerTests(TestCase):
1219
def test_rejecting_suggestion_cannot_have_zaak_action(self):
@@ -121,3 +128,53 @@ def test_selectielijst_zaak_action_requires_selectielijst(self):
121128
"The selectielijstklasse is required for action type is 'selectielijstklasse_and_bewaartermijn'."
122129
),
123130
)
131+
132+
def test_update_archiefactiedatum_selectielijst_api_error(self):
133+
record_manager = UserFactory.create(role__can_start_destruction=True)
134+
review = DestructionListReviewFactory.create(
135+
destruction_list__author=record_manager,
136+
destruction_list__status=ListStatus.changes_requested,
137+
destruction_list__assignee=record_manager,
138+
)
139+
items_review = DestructionListItemReviewFactory.create(
140+
destruction_list_item__with_zaak=True,
141+
destruction_list_item__zaak__selectielijstklasse="https://selectielijst.openzaak.nl/api/v1/resultaten/8320ab7d-3a8d-4c8b-b94a-14b4fa374d0a",
142+
destruction_list_item__destruction_list=review.destruction_list,
143+
review=review,
144+
)
145+
146+
data = {
147+
"review": review.pk,
148+
"comment": "A response about the review.",
149+
"items_responses": [
150+
{
151+
"review_item": items_review.pk,
152+
"action_item": DestructionListItemAction.remove,
153+
"action_zaak_type": ZaakActionType.bewaartermijn,
154+
"action_zaak": {
155+
"archiefactiedatum": "2030-01-01",
156+
},
157+
"comment": "This zaak needs to kept.",
158+
},
159+
],
160+
}
161+
162+
request = factory.get("/foo")
163+
request.user = record_manager
164+
165+
with patch(
166+
"openarchiefbeheer.destruction.api.serializers.retrieve_selectielijstklasse_resultaat",
167+
side_effect=HTTPError,
168+
):
169+
serializer = ReviewResponseSerializer(
170+
data=data, context={"request": request}
171+
)
172+
173+
self.assertFalse(serializer.is_valid())
174+
175+
self.assertEqual(
176+
serializer.errors["items_responses"][0]["non_field_errors"][0],
177+
_(
178+
"Could not validate the selectielijstklasse waardering due to an unexpected response from the Selectielijst API."
179+
),
180+
)

0 commit comments

Comments
 (0)