Skip to content

Commit b9e5cbd

Browse files
♻️ - refactor: refactor DestructionListWriteSerializer items by add and adjust frontend
1 parent 2fb0fa0 commit b9e5cbd

File tree

12 files changed

+103
-89
lines changed

12 files changed

+103
-89
lines changed

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

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
from django.db.models import Case, QuerySet, Value, When
22
from django.utils.translation import gettext_lazy as _
33

4-
from django_filters import FilterSet, NumberFilter, OrderingFilter, UUIDFilter
4+
from django_filters import (
5+
BooleanFilter,
6+
FilterSet,
7+
NumberFilter,
8+
OrderingFilter,
9+
UUIDFilter,
10+
)
511

612
from ..constants import InternalStatus
713
from ..models import (
@@ -23,13 +29,34 @@ class DestructionListItemFilterset(FilterSet):
2329
),
2430
)
2531

32+
# FIXME: Due to the implementation of the dit page, the zake endpoint and the destruction list items endpoint
33+
# MUST return values in EXACTLY THE SAME order, untill we fix this properly, we use a special param
34+
# "item-order_match_zaken" to instruct the filter to order on the "zaak_pk" which is the (default) ordering of the
35+
# zaken endpoint.
36+
order_match_zaken = BooleanFilter(
37+
field_name="ordering",
38+
method="filter_order_match_zaken",
39+
help_text=_(
40+
"If edit mode is active, ordering should match ordering on the zaken endpoint, use the item-ordering param "
41+
"to define the exact same ordering as the zaak endpoint"
42+
),
43+
)
44+
2645
class Meta:
2746
model = DestructionListItem
28-
fields = ("destruction_list", "status", "processing_status")
47+
fields = (
48+
"destruction_list",
49+
"status",
50+
"processing_status",
51+
"order_match_zaken",
52+
)
2953

3054
def filter_in_destruction_list(
3155
self, queryset: QuerySet[DestructionListItem], name: str, value: str
3256
) -> QuerySet[DestructionListItem]:
57+
"""
58+
Fixme: see filter field.
59+
"""
3360
return (
3461
queryset.filter(destruction_list__uuid=value)
3562
.annotate(
@@ -45,6 +72,11 @@ def filter_in_destruction_list(
4572
.order_by("processing_status_index")
4673
)
4774

75+
def filter_order_match_zaken(
76+
self, queryset: QuerySet[DestructionListItem], name: str, value: str
77+
) -> QuerySet[DestructionListItem]:
78+
return queryset.order_by("zaak__pk")
79+
4880

4981
class DestructionListFilterset(FilterSet):
5082
assignee = NumberFilter(

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

+13-17
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ class Meta:
151151
class DestructionListWriteSerializer(serializers.ModelSerializer):
152152
add = DestructionListItemWriteSerializer(many=True, required=False)
153153
remove = DestructionListItemWriteSerializer(many=True, required=False)
154-
items = DestructionListItemWriteSerializer(many=True, required=False)
155154
reviewer = ReviewerAssigneeSerializer(required=False)
156155
author = UserSerializer(read_only=True)
157156
select_all = serializers.BooleanField(
@@ -172,7 +171,6 @@ class Meta:
172171
fields = (
173172
"add",
174173
"remove",
175-
"items",
176174
"uuid",
177175
"name",
178176
"author",
@@ -204,9 +202,15 @@ def validate_zaak_filters(self, value: Any) -> dict:
204202
return value
205203

206204
def validate(self, attrs: dict) -> dict:
207-
if not self.instance and not attrs.get("items") and not attrs.get("select_all"):
205+
if (attrs.get("add") or attrs.get("remove")) and attrs.get("select_all"):
208206
raise ValidationError(
209-
"Neither the 'items' nor the 'select_all' field have been specified.",
207+
"'add' or 'remove' cannot be combined with 'select_all'",
208+
code="invalid",
209+
)
210+
211+
if not self.instance and not attrs.get("add") and not attrs.get("select_all"):
212+
raise ValidationError(
213+
"Neither the 'add' nor the 'select_all' field have been specified.",
210214
code="invalid",
211215
)
212216

@@ -234,7 +238,7 @@ def _get_zaken(
234238

235239
def create(self, validated_data: dict) -> DestructionList:
236240
reviewer_data = validated_data.pop("reviewer")
237-
items = validated_data.pop("items", None)
241+
add = validated_data.pop("add", [])
238242
bulk_select = validated_data.pop("select_all", False)
239243
zaak_filters = validated_data.pop("zaak_filters", {})
240244

@@ -244,7 +248,7 @@ def create(self, validated_data: dict) -> DestructionList:
244248
validated_data["status"] = ListStatus.new
245249
destruction_list = DestructionList.objects.create(**validated_data)
246250

247-
zaken = self._get_zaken(zaak_filters, items, bulk_select)
251+
zaken = self._get_zaken(zaak_filters, add, bulk_select)
248252
destruction_list.add_items(zaken)
249253

250254
# Create an assignee also for the author
@@ -269,7 +273,6 @@ def update(
269273
validated_data.pop("reviewer", None)
270274
add_data = validated_data.pop("add", [])
271275
remove_data = validated_data.pop("remove", [])
272-
items_data = validated_data.pop("items", [])
273276
instance.contains_sensitive_info = validated_data.pop(
274277
"contains_sensitive_info", instance.contains_sensitive_info
275278
)
@@ -278,19 +281,12 @@ def update(
278281

279282
instance.name = validated_data.pop("name", instance.name)
280283

281-
if items_data or bulk_select:
282-
instance.items.all().delete()
283-
284-
zaken = self._get_zaken(zaak_filters, items_data, bulk_select)
285-
286-
instance.add_items(zaken)
287-
288-
if add_data:
284+
if add_data or bulk_select:
289285
zaken = self._get_zaken(zaak_filters, add_data, bulk_select)
290-
self.instance.add_items(zaken)
286+
self.instance.add_items(zaken, True)
291287

292288
if remove_data:
293-
zaken = self._get_zaken(zaak_filters, remove_data or [], bulk_select)
289+
zaken = [item["zaak"] for item in remove_data]
294290
self.instance.remove_items(zaken)
295291

296292
instance.save()

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

+1-19
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"user": 2,
8989
},
9090
],
91-
"items": [
91+
"add": [
9292
{
9393
"zaak": "http://some-zaken-api.nl/zaken/api/v1/zaken/111-111-111",
9494
"extraZaakData": {},
@@ -131,24 +131,6 @@
131131
],
132132
},
133133
),
134-
OpenApiExample(
135-
name="Replace items",
136-
request_only=True,
137-
value={
138-
"name": "An example updated list",
139-
"containsSensitiveInfo": False,
140-
"items": [
141-
{
142-
"zaak": "http://some-zaken-api.nl/zaken/api/v1/zaken/111-111-111",
143-
"extraZaakData": {},
144-
},
145-
{
146-
"zaak": "http://some-zaken-api.nl/zaken/api/v1/zaken/222-222-222",
147-
"extraZaakData": {},
148-
},
149-
],
150-
},
151-
),
152134
],
153135
),
154136
partial_update=extend_schema(

backend/src/openarchiefbeheer/destruction/models.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,17 @@ def set_status(self, status: str) -> None:
147147
self.status_changed = timezone.now()
148148
self.save()
149149

150-
def add_items(self, zaken: Iterable["Zaak"]) -> list["DestructionListItem"]:
150+
def add_items(
151+
self, zaken: Iterable["Zaak"], ignore_conflicts: bool = False
152+
) -> list["DestructionListItem"]:
151153
return DestructionListItem.objects.bulk_create(
152154
[
153155
DestructionListItem(
154156
destruction_list=self, zaak=zaak, _zaak_url=zaak.url
155157
)
156158
for zaak in zaken
157-
]
159+
],
160+
ignore_conflicts=ignore_conflicts,
158161
)
159162

160163
def remove_items(self, zaken: Iterable["Zaak"]) -> tuple[int, dict[str, int]]:

backend/src/openarchiefbeheer/destruction/tests/test_endpoints.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def test_create_destruction_list(self):
7979
"name": "A test list",
8080
"contains_sensitive_info": True,
8181
"reviewer": {"user": reviewer.pk},
82-
"items": [
82+
"add": [
8383
{
8484
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
8585
"extra_zaak_data": {},
@@ -161,7 +161,7 @@ def test_zaak_already_in_another_destruction_list(self):
161161
"assignees": [
162162
{"user": reviewer.pk},
163163
],
164-
"items": [
164+
"add": [
165165
{
166166
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
167167
"extra_zaak_data": {},
@@ -182,7 +182,7 @@ def test_zaak_already_in_another_destruction_list(self):
182182
self.assertEqual(
183183
data,
184184
{
185-
"items": [
185+
"add": [
186186
{
187187
"zaak": [
188188
_(
@@ -228,7 +228,7 @@ def test_update_destruction_list(self):
228228
data = {
229229
"name": "An updated test list",
230230
"contains_sensitive_info": False,
231-
"items": [
231+
"add": [
232232
{
233233
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
234234
"extra_zaak_data": {"key": "value"},
@@ -251,7 +251,7 @@ def test_update_destruction_list(self):
251251
destruction_list.refresh_from_db()
252252

253253
self.assertEqual(destruction_list.name, "An updated test list")
254-
self.assertEqual(destruction_list.items.all().count(), 1)
254+
self.assertEqual(destruction_list.items.all().count(), 3)
255255

256256
def test_cannot_update_destruction_list_if_not_new(self):
257257
record_manager = UserFactory.create(post__can_start_destruction=True)

backend/src/openarchiefbeheer/destruction/tests/test_serializers.py

+21-14
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_create_destruction_list(self):
5555
"name": "A test list",
5656
"contains_sensitive_info": True,
5757
"reviewer": {"user": reviewer.pk},
58-
"items": [
58+
"add": [
5959
{
6060
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
6161
"extra_zaak_data": {},
@@ -139,7 +139,7 @@ def test_zaak_already_included_in_other_list(self):
139139
"assignees": [
140140
{"user": reviewer.pk, "order": 0},
141141
],
142-
"items": [
142+
"add": [
143143
{
144144
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
145145
"extra_zaak_data": {},
@@ -157,7 +157,7 @@ def test_zaak_already_included_in_other_list(self):
157157

158158
self.assertFalse(serializer.is_valid())
159159
self.assertEqual(
160-
serializer.errors["items"][0]["zaak"],
160+
serializer.errors["add"][0]["zaak"],
161161
[
162162
_(
163163
"This case was already included in another destruction list and was not exempt during the review process."
@@ -189,7 +189,7 @@ def test_zaak_already_included_in_other_list_but_exempt(self):
189189
"name": "A test list",
190190
"contains_sensitive_info": True,
191191
"reviewer": {"user": reviewer.pk},
192-
"items": [
192+
"add": [
193193
{
194194
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
195195
"extra_zaak_data": {},
@@ -226,7 +226,7 @@ def test_full_list_update(self):
226226
data = {
227227
"name": "An updated test list",
228228
"contains_sensitive_info": False,
229-
"items": [
229+
"add": [
230230
{
231231
"zaak": "http://localhost:8003/zaken/api/v1/zaken/111-111-111",
232232
},
@@ -249,7 +249,7 @@ def test_full_list_update(self):
249249

250250
items = destruction_list.items.all()
251251

252-
self.assertEqual(items.count(), 1)
252+
self.assertEqual(items.count(), 3)
253253

254254
logs = TimelineLog.objects.filter(
255255
template="logging/destruction_list_updated.txt"
@@ -401,10 +401,12 @@ def test_partial_update_with_zaken(self):
401401
status=ListItemStatus.suggested,
402402
with_zaak=True,
403403
)
404+
zaak = ZaakFactory.create()
404405

405406
# We are removing 2 zaken from the destruction list
406407
data = {
407-
"items": [{"zaak": items[0].zaak.url}, {"zaak": items[1].zaak.url}],
408+
"add": [{"zaak": zaak.url}],
409+
"remove": [{"zaak": items[0].zaak.url}, {"zaak": items[1].zaak.url}],
408410
}
409411

410412
record_manager = UserFactory.create(post__can_start_destruction=True)
@@ -417,17 +419,22 @@ def test_partial_update_with_zaken(self):
417419
partial=True,
418420
context={"request": request},
419421
)
420-
421422
self.assertTrue(serializer.is_valid())
422423

423424
serializer.save()
424425

425-
items = DestructionListItem.objects.filter(destruction_list=destruction_list)
426-
items_in_list = items.values_list("zaak__url", flat=True)
426+
destruction_list_items = DestructionListItem.objects.filter(
427+
destruction_list=destruction_list
428+
)
429+
items_in_list = destruction_list_items.values_list("zaak__url", flat=True)
427430

428-
self.assertEqual(items_in_list.count(), 2)
429-
self.assertIn(data["items"][0]["zaak"], items_in_list)
430-
self.assertIn(data["items"][1]["zaak"], items_in_list)
431+
self.assertEqual(items_in_list.count(), 3)
432+
self.assertNotIn(items, items_in_list)
433+
self.assertIn(items[2].zaak.url, items_in_list)
434+
self.assertIn(items[3].zaak.url, items_in_list)
435+
self.assertIn(data["add"][0]["zaak"], items_in_list)
436+
self.assertNotIn(data["remove"][0]["zaak"], items_in_list)
437+
self.assertNotIn(data["remove"][1]["zaak"], items_in_list)
431438

432439
@tag("gh-122")
433440
def test_assign_author_as_reviewer(self):
@@ -603,7 +610,7 @@ def test_no_bulk_select_and_no_items(self):
603610
self.assertFalse(is_valid)
604611
self.assertEqual(
605612
serializer.errors["non_field_errors"][0],
606-
"Neither the 'items' nor the 'select_all' field have been specified.",
613+
"Neither the 'add' nor the 'select_all' field have been specified.",
607614
)
608615

609616
def test_zaak_filters_validation(self):

frontend/src/lib/api/destructionLists.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export type DestructionListUpdateData = {
5252
assignees?: DestructionListAssigneeUpdate[];
5353
add?: DestructionListItemUpdate[];
5454
remove?: DestructionListItemUpdate[];
55-
items?: DestructionListItemUpdate[];
5655
comment?: string;
5756
};
5857

@@ -74,7 +73,7 @@ export type DestructionListMarkAsFinalData = {
7473
* Create a new destruction list.
7574
* @param name
7675
* @param zaken
77-
* @param assignees
76+
* @param assigneeId
7877
* @param zaakFilters
7978
* @param allZakenSelected
8079
*/
@@ -90,7 +89,7 @@ export async function createDestructionList(
9089
const destructionList = {
9190
name,
9291
reviewer: { user: JSON.parse(assigneeId) },
93-
items: urls.map((url) => ({ zaak: url })),
92+
add: urls.map((url) => ({ zaak: url })),
9493
selectAll: allZakenSelected,
9594
zaakFilters: JSON.parse(zaakFilters),
9695
};

frontend/src/pages/destructionlist/abstract/BaseListView.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ export function BaseListView({
119119
getSelectionDetail,
120120
);
121121

122-
// Merge `selectedZakenOnPage`.
123122
const selectedZakenOnPage = useMemo(() => {
124123
const deselectedZaakUrls = deSelectedZakenOnPage.map(
125124
(z) => z.url as string,
@@ -200,7 +199,11 @@ export function BaseListView({
200199
page,
201200
sort: sortable && sort,
202201
selected: selectable
203-
? (selectedZakenOnPage as unknown as AttributeData[])
202+
? ([
203+
...new Map(
204+
selectedZakenOnPage.map((zaak) => [zaak["uuid"], zaak]),
205+
).values(),
206+
] as unknown as AttributeData[])
204207
: [],
205208
selectionActions: getSelectionActions(),
206209

0 commit comments

Comments
 (0)