fix: prevent forced intervention status transition to to_be_planned during unassignment if unauthorized

This commit is contained in:
kdeterme 2026-08-17 17:06:02 +02:00
parent aa6c0d0603
commit 6e02156034
2 changed files with 36 additions and 5 deletions

View file

@ -738,6 +738,31 @@ class ExternalManagerScheduleUpdateTests(TestCase):
self.assertIsNone(self.itv.planned_begin_time) self.assertIsNone(self.itv.planned_begin_time)
self.assertIsNone(self.itv.planned_end_time) self.assertIsNone(self.itv.planned_end_time)
def test_external_manager_unassign_without_to_be_planned_permission_keeps_current_status(self):
# Disable can_change_status_to on to_be_planned
UserContractStatusPermission.objects.filter(
user_contract=self.uca,
status='to_be_planned',
).update(can_change_status_to=False)
payload = {
'intervention_id': self.itv.id,
'assigned_member_id': None,
'assigned_team_id': None,
'unassign': True,
}
res = self.client.post(self.update_url, data=json.dumps(payload), content_type='application/json')
self.assertEqual(res.status_code, 200)
self.assertTrue(res.json().get('success'))
self.itv.refresh_from_db()
# Status remains to_be_processed since user cannot transition to to_be_planned
self.assertEqual(self.itv.status, 'to_be_processed')
self.assertIsNone(self.itv.assigned_member)
self.assertIsNone(self.itv.assigned_team)
self.assertIsNone(self.itv.planned_begin_time)
self.assertIsNone(self.itv.planned_end_time)
def test_external_manager_cannot_assign_member_of_different_company(self): def test_external_manager_cannot_assign_member_of_different_company(self):
new_begin = timezone.now() + timedelta(days=1) new_begin = timezone.now() + timedelta(days=1)
new_end = new_begin + timedelta(hours=2) new_end = new_begin + timedelta(hours=2)
@ -776,7 +801,9 @@ class ExternalManagerScheduleUpdateTests(TestCase):
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
itv_assigned.refresh_from_db() itv_assigned.refresh_from_db()
self.assertEqual(itv_assigned.status, 'to_be_planned') # Since 'assigned' -> 'to_be_planned' is not an allowed transition in ALLOWED_TRANSITIONS,
# the status remains 'assigned' while unassigning member/team/dates
self.assertEqual(itv_assigned.status, 'assigned')
self.assertIsNone(itv_assigned.assigned_member) self.assertIsNone(itv_assigned.assigned_member)
self.assertIsNone(itv_assigned.assigned_team) self.assertIsNone(itv_assigned.assigned_team)

View file

@ -560,7 +560,7 @@ def _interventions_events_by_members(
planned_begin = getattr(itv, 'planned_begin_time', None) planned_begin = getattr(itv, 'planned_begin_time', None)
return bool(planned_begin and planned_begin > timezone.now()) return bool(planned_begin and planned_begin > timezone.now())
backlog_allowed_statuses = {'in_preparation', 'to_be_approved', 'to_be_planned', 'to_be_processed'} backlog_allowed_statuses = {'in_preparation', 'to_be_approved', 'to_be_planned', 'to_be_processed', 'assigned'}
PRE_PROCESSING_STATUSES = {'in_preparation', 'to_be_approved', 'to_be_planned'} PRE_PROCESSING_STATUSES = {'in_preparation', 'to_be_approved', 'to_be_planned'}
backlog_allowed_types = {'corrective', 'preventive', 'ameliorative'} backlog_allowed_types = {'corrective', 'preventive', 'ameliorative'}
maintain_type_labels = dict(MAINTAIN_CHOICES) maintain_type_labels = dict(MAINTAIN_CHOICES)
@ -1010,7 +1010,7 @@ def _interventions_events_by_teams_daily(
}) })
# Compute backlog groups for the assignment sidebar # Compute backlog groups for the assignment sidebar
backlog_allowed_statuses = {'in_preparation', 'to_be_approved', 'to_be_planned', 'to_be_processed'} backlog_allowed_statuses = {'in_preparation', 'to_be_approved', 'to_be_planned', 'to_be_processed', 'assigned'}
backlog_allowed_types = {'corrective', 'preventive', 'ameliorative'} backlog_allowed_types = {'corrective', 'preventive', 'ameliorative'}
maintain_type_labels = dict(MAINTAIN_CHOICES) maintain_type_labels = dict(MAINTAIN_CHOICES)
contract_filter = None if allowed_contract_ids is None else set(allowed_contract_ids) contract_filter = None if allowed_contract_ids is None else set(allowed_contract_ids)
@ -1293,9 +1293,13 @@ def interventions_schedule_update(request):
# If intervention was "to_be_planned" and is now assigned -> change to "to_be_processed" # If intervention was "to_be_planned" and is now assigned -> change to "to_be_processed"
if prev_status == 'to_be_planned' and is_now_assigned: if prev_status == 'to_be_planned' and is_now_assigned:
new_status = 'to_be_processed' new_status = 'to_be_processed'
# If intervention is being unassigned (back to backlog) -> change to "to_be_planned" # If intervention is being unassigned (back to backlog) -> change to "to_be_planned" only if user has transition rights
elif is_unassigning and prev_status in ('to_be_processed', 'assigned'): elif is_unassigning and prev_status in ('to_be_processed', 'assigned'):
new_status = 'to_be_planned' allowed_transitions = user_allowed_transitions(request.user, prev_status, intervention=intervention)
if 'to_be_planned' in allowed_transitions or is_admin:
new_status = 'to_be_planned'
else:
new_status = prev_status
if new_status != prev_status: if new_status != prev_status:
intervention.status = new_status intervention.status = new_status