diff --git a/apps/wf/tests.py b/apps/wf/tests.py index 7ce503c2..2c8dd210 100755 --- a/apps/wf/tests.py +++ b/apps/wf/tests.py @@ -1,3 +1,60 @@ -from django.test import TestCase +from types import SimpleNamespace +from unittest.mock import MagicMock, patch -# Create your tests here. +from django.test import SimpleTestCase +from rest_framework.test import APIRequestFactory + +from apps.wf.views import TicketViewSet + + +class TicketAvailableActionsTests(SimpleTestCase): + def setUp(self): + self.request = APIRequestFactory().get('/api/wf/ticket/1/available_actions/') + self.request.user = SimpleNamespace(id='user-1') + self.view = TicketViewSet() + self.ticket = SimpleNamespace(in_add_node=False) + self.view.get_object = MagicMock(return_value=self.ticket) + + @patch('apps.wf.views.TransitionSerializer') + @patch('apps.wf.views.WfService.get_ticket_transitions') + @patch('apps.wf.views.WfService.ticket_handle_permission_check') + def test_returns_transitions_when_user_can_handle( + self, permission_check, get_transitions, serializer): + permission_check.return_value = { + 'permission': True, + 'need_accept': False, + 'msg': '', + } + transitions = MagicMock() + transitions.select_related.return_value = transitions + get_transitions.return_value = transitions + serializer.return_value.data = [{'id': 'transition-1', 'name': '同意'}] + + response = self.view.available_actions(self.request) + + self.assertTrue(response.data['permission']) + self.assertFalse(response.data['need_accept']) + self.assertEqual( + response.data['transitions'], + [{'id': 'transition-1', 'name': '同意'}], + ) + + @patch('apps.wf.views.TransitionSerializer') + @patch('apps.wf.views.WfService.get_ticket_transitions') + @patch('apps.wf.views.WfService.ticket_handle_permission_check') + def test_hides_transitions_and_requests_accept_for_active_distribution( + self, permission_check, get_transitions, serializer): + permission_check.return_value = { + 'permission': False, + 'need_accept': True, + 'msg': '需要先接单再处理', + } + + response = self.view.available_actions(self.request) + + self.assertFalse(response.data['permission']) + self.assertTrue(response.data['need_accept']) + self.assertEqual(response.data['message'], '需要先接单再处理') + self.assertEqual(response.data['transitions'], []) + get_transitions.assert_not_called() + serializer.assert_not_called() diff --git a/apps/wf/views.py b/apps/wf/views.py index 0f0dcd63..afd07b28 100755 --- a/apps/wf/views.py +++ b/apps/wf/views.py @@ -363,6 +363,34 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R transitions = WfService.get_ticket_transitions(ticket) return Response(TransitionSerializer(instance=transitions.select_related('source_state', 'destination_state'), many=True).data) + @action(methods=['get'], detail=True, perms_map={'get': '*'}) + def available_actions(self, request, pk=None): + """ + 获取当前用户对工单可执行的操作及接单状态。 + + 前端应使用该接口决定是否展示处理按钮,避免自行解释 participant + 后与后端实际处理权限不一致。原 transitions 接口保留用于兼容。 + """ + ticket = self.get_object() + permission_result = WfService.ticket_handle_permission_check( + ticket, request.user + ) + transitions = [] + if permission_result.get('permission', False): + transition_queryset = WfService.get_ticket_transitions( + ticket + ).select_related('source_state', 'destination_state') + transitions = TransitionSerializer( + instance=transition_queryset, many=True + ).data + return Response({ + 'permission': permission_result.get('permission', False), + 'need_accept': permission_result.get('need_accept', False), + 'message': permission_result.get('msg', ''), + 'in_add_node': ticket.in_add_node, + 'transitions': transitions, + }) + @action(methods=['post'], detail=True, perms_map={'post': '*'}) def accpet(self, request, pk=None): """