Skip to content

Commit

Permalink
Merge pull request #514 from maykinmedia/feature/498-pre-populate-sel…
Browse files Browse the repository at this point in the history
…ection

[#498] Pre populate selection after processing review response
  • Loading branch information
SilviaAmAm authored Nov 22, 2024
2 parents 57e691a + ada5fe3 commit d9ad329
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 5 deletions.
12 changes: 10 additions & 2 deletions backend/src/openarchiefbeheer/destruction/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from .exceptions import DeletionProcessingError
from .models import DestructionList, DestructionListItem, ReviewResponse
from .signals import deletion_failure
from .utils import notify_assignees_successful_deletion
from .utils import (
notify_assignees_successful_deletion,
prepopulate_selection_after_review_response,
)

logger = logging.getLogger(__name__)

Expand All @@ -22,7 +25,9 @@ def process_review_response(pk: int) -> None:
"review", "review__destruction_list"
).get(pk=pk)
items_review_responses = review_response.items_responses.select_related(
"review_item", "review_item__destruction_list_item"
"review_item",
"review_item__destruction_list_item",
"review_item__destruction_list_item__zaak",
)

for item_response in items_review_responses:
Expand All @@ -38,6 +43,9 @@ def process_review_response(pk: int) -> None:
item_response.save()
return

prepopulate_selection_after_review_response(
review_response.review.destruction_list, items_review_responses
)
review_response.review.destruction_list.assign_next()


Expand Down
76 changes: 76 additions & 0 deletions backend/src/openarchiefbeheer/destruction/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from openarchiefbeheer.accounts.tests.factories import UserFactory
from openarchiefbeheer.emails.models import EmailConfig
from openarchiefbeheer.selection.models import SelectionItem
from openarchiefbeheer.zaken.models import Zaak
from openarchiefbeheer.zaken.tests.factories import ZaakFactory

Expand All @@ -24,6 +25,7 @@
ListItemStatus,
ListRole,
ListStatus,
ReviewDecisionChoices,
ZaakActionType,
)
from ..tasks import (
Expand All @@ -33,10 +35,13 @@
process_review_response,
queue_destruction_lists_for_deletion,
)
from ..utils import get_selection_key_for_review
from .factories import (
DestructionListAssigneeFactory,
DestructionListFactory,
DestructionListItemFactory,
DestructionListItemReviewFactory,
DestructionListReviewFactory,
ReviewItemResponseFactory,
ReviewResponseFactory,
)
Expand Down Expand Up @@ -199,6 +204,77 @@ def test_reject_suggestion_does_not_change_zaak(self, m):
zaak.archiefactiedatum.isoformat(), "2025-01-01"
) # NOT changed!!

def test_prepopulating_selection(self, m):
destruction_list = DestructionListFactory.create(
status=ListStatus.changes_requested
)
reviewer = DestructionListAssigneeFactory.create(
destruction_list=destruction_list, post__can_review_destruction=True
)
items = DestructionListItemFactory.create_batch(
3, destruction_list=destruction_list, with_zaak=True
)

review = DestructionListReviewFactory.create(
destruction_list=destruction_list,
decision=ReviewDecisionChoices.rejected,
author=reviewer.user,
)
review_item1 = DestructionListItemReviewFactory.create(
destruction_list=destruction_list,
review=review,
destruction_list_item=items[0],
)
review_item2 = DestructionListItemReviewFactory.create(
destruction_list=destruction_list,
review=review,
destruction_list_item=items[1],
)

review_response = ReviewResponseFactory.create(review=review)
# Reviewer suggestion on zaak1 ignored
ReviewItemResponseFactory.create(
review_item=review_item1,
action_item=DestructionListItemAction.keep,
)
# Reviewer suggestion implemented, zaak2 is removed from the list
ReviewItemResponseFactory.create(
review_item=review_item2,
action_item=DestructionListItemAction.remove,
action_zaak_type=ZaakActionType.bewaartermijn,
action_zaak={
"archiefactiedatum": "2026-01-01",
},
)
# The third zaak was accepted

# Now we process the review and we expect the selection to contain zaak1 NOT selected and zaak3 selected.
# Zaak2 should not be present because it was removed from the list
ServiceFactory.create(
api_type=APITypes.zrc,
api_root="http://zaken-api.nl/",
)
m.patch(items[1].zaak.url, json={"archiefactiedatum": "2026-01-01"})
process_review_response(review_response.pk)

selection_key = get_selection_key_for_review(destruction_list, "review")

selection_items = SelectionItem.objects.filter(key=selection_key).order_by(
"selection_data__selected"
)

self.assertEqual(selection_items.count(), 2)
self.assertEqual(selection_items[0].zaak_url, items[0].zaak.url)
self.assertEqual(selection_items[1].zaak_url, items[2].zaak.url)

# This is the zaak which the reviewer already had approved (zaak3)
self.assertTrue(selection_items[1].selection_data["detail"]["approved"])
self.assertTrue(selection_items[1].selection_data["selected"])

# This is the zaak for which the feedback was ignored (zaak1)
self.assertNotIn("detail", selection_items[0].selection_data)
self.assertFalse(selection_items[0].selection_data["selected"])


@temp_private_root()
class ProcessDeletingZakenTests(TestCase):
Expand Down
78 changes: 75 additions & 3 deletions backend/src/openarchiefbeheer/destruction/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.conf import settings
from django.core.mail import send_mail
from django.db import transaction
from django.db.models import OuterRef, Q, Subquery
from django.db.models import OuterRef, Q, QuerySet, Subquery
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

Expand All @@ -16,11 +16,23 @@
from openarchiefbeheer.config.models import ArchiveConfig
from openarchiefbeheer.emails.models import EmailConfig
from openarchiefbeheer.emails.render_backend import get_sandboxed_backend
from openarchiefbeheer.selection.models import SelectionItem
from openarchiefbeheer.utils.results_store import ResultStore
from openarchiefbeheer.zaken.models import Zaak

from .constants import InternalStatus, ListRole
from .models import DestructionList, DestructionListAssignee, DestructionListItem
from .constants import (
DestructionListItemAction,
InternalStatus,
ListItemStatus,
ListRole,
ListStatus,
)
from .models import (
DestructionList,
DestructionListAssignee,
DestructionListItem,
ReviewItemResponse,
)


def notify(subject: str, body: str, context: dict, recipients: list[str]) -> None:
Expand Down Expand Up @@ -245,3 +257,63 @@ def attach_report_to_zaak(
)
response.raise_for_status()
store.add_created_resource("zaakinformatieobjecten", response.json()["url"])


def get_selection_key_for_review(
destruction_list: "DestructionList", context: str
) -> str:
"""
Warning! This key needs to remain in sync with the key created by the frontend,
since the frontend need to be able to retrieve the pre-populates selection
in the review page. See github issue #498
"""
return f"destruction-list-{context}-{destruction_list.uuid}-{ListStatus.ready_to_review}"


def prepopulate_selection_after_review_response(
destruction_list: "DestructionList",
review_response_items: QuerySet[ReviewItemResponse],
) -> None:
selection_key = get_selection_key_for_review(destruction_list, "review")

ignored_advice_responses = review_response_items.filter(
action_item=DestructionListItemAction.keep
)

selection_items_advice_ignored_to_create = []
for response in ignored_advice_responses:
selection_items_advice_ignored_to_create.append(
SelectionItem(
key=selection_key,
selection_data={"selected": False},
zaak_url=response.review_item.destruction_list_item.zaak.url,
)
)

# Make sure that the selection is clean
SelectionItem.objects.filter(key=selection_key).delete()

# Create the selection items for the items where review advice
# was ignored (they will appear NOT selected)
SelectionItem.objects.bulk_create(selection_items_advice_ignored_to_create)

# Create the selection items for the items that were already
# approved (they will appear as selected and approved)
other_selection_items_to_create = []
for item in destruction_list.items.filter(
~Q(
zaak__in=ignored_advice_responses.values_list(
"review_item__destruction_list_item__zaak"
)
),
status=ListItemStatus.suggested,
):
other_selection_items_to_create.append(
SelectionItem(
key=selection_key,
selection_data={"selected": True, "detail": {"approved": True}},
zaak_url=item.zaak.url,
)
)

SelectionItem.objects.bulk_create(other_selection_items_to_create)
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export async function destructionListApproveListAction({
try {
await Promise.all([
await createDestructionListReview(data),
// The selection is cleared to prevent clashes, since the backend
// can pre-populate it for future reviews (see github issue #498).
await clearZaakSelection(
getDestructionListReviewKey(destructionList, status),
RestBackend,
Expand Down Expand Up @@ -110,6 +112,8 @@ export async function destructionListRejectListAction({
try {
await Promise.all([
createDestructionListReview(data),
// The selection is cleared to prevent clashes, since the backend
// can pre-populate it for future reviews (see github issue #498).
clearZaakSelection(
getDestructionListReviewKey(destructionList, status),
RestBackend,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ import { ReviewDestructionListAction } from "./DestructionListReview.action";
import "./DestructionListReview.css";
import { DestructionListReviewContext } from "./DestructionListReview.loader";

/**
* Warning! This key needs to remain in sync with the key created by the backend,
* since the backend can pre-populate the selection after a review is processed by
* the record manager. See github issue #498
*/
export const getDestructionListReviewKey = (id: string, status: string) =>
`destruction-list-review-${id}-${status}`;

Expand Down

0 comments on commit d9ad329

Please sign in to comment.