Coverage for benefits/in_person/views.py: 98%

122 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-12 23:32 +0000

1import logging 

2 

3from django.contrib.admin import site as admin_site 

4from django.shortcuts import redirect 

5from django.template.response import TemplateResponse 

6from django.urls import reverse 

7from django.views.generic import FormView 

8 

9from benefits.core.models.transit import TransitAgency 

10from benefits.core import models, session 

11from benefits.eligibility import analytics as eligibility_analytics 

12from benefits.enrollment import analytics as enrollment_analytics 

13from benefits.enrollment.views import IndexView 

14from benefits.enrollment_littlepay.session import Session as LittlepaySession 

15from benefits.enrollment_littlepay.views import TokenView, IndexView as LittlepayIndexView 

16from benefits.enrollment_switchio.session import Session as SwitchioSession 

17from benefits.enrollment_switchio.views import GatewayUrlView, IndexView as SwitchioIndexView 

18 

19from benefits.in_person import forms 

20from benefits.routes import routes 

21 

22logger = logging.getLogger(__name__) 

23 

24 

25class EligibilityView(FormView): 

26 """CBV for the in-person eligibility flow selection form.""" 

27 

28 template_name = "in_person/eligibility.html" 

29 form_class = forms.InPersonEligibilityForm 

30 

31 def dispatch(self, request, *args, **kwargs): 

32 """Initialize session state before handling the request.""" 

33 

34 LittlepaySession(request, reset=True) 

35 SwitchioSession(request, reset=True) 

36 

37 agency = session.agency(request) 

38 if not agency: 

39 agency = TransitAgency.for_user(request.user) 

40 session.update(request, agency=agency) 

41 self.agency = agency 

42 return super().dispatch(request, *args, **kwargs) 

43 

44 def get_context_data(self, **kwargs): 

45 """Add in-person specific context data.""" 

46 

47 context = super().get_context_data(**kwargs) 

48 context.update( 

49 { 

50 **admin_site.each_context(self.request), 

51 "title": f"{self.agency.long_name} | In-person enrollment | {admin_site.site_title}", 

52 } 

53 ) 

54 return context 

55 

56 def get_form_kwargs(self): 

57 """Return the keyword arguments for instantiating the form.""" 

58 

59 kwargs = super().get_form_kwargs() 

60 kwargs["agency"] = self.agency 

61 return kwargs 

62 

63 def form_valid(self, form): 

64 """If the form is valid, set enrollment flow, eligible session, and redirect.""" 

65 

66 flow_id = form.cleaned_data.get("flow") 

67 flow = models.EnrollmentFlow.objects.get(id=flow_id) 

68 session.update(self.request, flow=flow, eligible=True) 

69 eligibility_analytics.selected_flow(self.request, flow, enrollment_method=models.EnrollmentMethods.IN_PERSON) 

70 eligibility_analytics.started_eligibility(self.request, flow, enrollment_method=models.EnrollmentMethods.IN_PERSON) 

71 eligibility_analytics.returned_success(self.request, flow, enrollment_method=models.EnrollmentMethods.IN_PERSON) 

72 return redirect(routes.IN_PERSON_ENROLLMENT) 

73 

74 

75class LittlepayTokenView(TokenView): 

76 """View handler for the enrollment auth token.""" 

77 

78 enrollment_method = models.EnrollmentMethods.IN_PERSON 

79 route_system_error = routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR 

80 route_server_error = routes.IN_PERSON_SERVER_ERROR 

81 

82 

83class EnrollmentView(IndexView): 

84 

85 route_origin = routes.IN_PERSON_ENROLLMENT 

86 

87 def get_redirect_url(self, *args, **kwargs): 

88 route_name = self.agency.in_person_enrollment_index_route 

89 return reverse(route_name) 

90 

91 

92class LittlepayEnrollmentView(LittlepayIndexView): 

93 """View handler for the in-person enrollment page.""" 

94 

95 enrollment_method = models.EnrollmentMethods.IN_PERSON 

96 route_enrollment_success = routes.IN_PERSON_ENROLLMENT_SUCCESS 

97 route_enrollment_retry = routes.IN_PERSON_ENROLLMENT_RETRY 

98 route_reenrollment_error = routes.IN_PERSON_ENROLLMENT_REENROLLMENT_ERROR 

99 route_server_error = routes.IN_PERSON_SERVER_ERROR 

100 route_system_error = routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR 

101 route_tokenize_success = routes.IN_PERSON_ENROLLMENT_LITTLEPAY_INDEX 

102 template_name = "in_person/enrollment/index_littlepay.html" 

103 

104 def _get_verified_by(self): 

105 return f"{self.request.user.first_name} {self.request.user.last_name}" 

106 

107 def get_context_data(self, **kwargs): 

108 """Add in-person specific context data.""" 

109 

110 context = super().get_context_data(**kwargs) 

111 context.update( 

112 { 

113 "title": f"{self.agency.long_name} | In-person enrollment | {admin_site.site_title}", 

114 } 

115 ) 

116 return context 

117 

118 

119def reenrollment_error(request): 

120 """View handler for a re-enrollment attempt that is not yet within the re-enrollment window.""" 

121 

122 agency = session.agency(request) 

123 context = { 

124 **admin_site.each_context(request), 

125 "title": f"{agency.long_name} | In-person enrollment | {admin_site.site_title}", 

126 } 

127 

128 flow = session.flow(request) 

129 context["flow_label"] = flow.label 

130 

131 return TemplateResponse(request, "in_person/enrollment/reenrollment_error.html", context) 

132 

133 

134def retry(request): 

135 """View handler for card verification failure.""" 

136 # enforce POST-only route for sending analytics 

137 if request.method == "POST": 

138 enrollment_analytics.returned_retry(request, enrollment_method=models.EnrollmentMethods.IN_PERSON) 

139 

140 agency = session.agency(request) 

141 context = { 

142 **admin_site.each_context(request), 

143 "title": f"{agency.long_name} | In-person enrollment | {admin_site.site_title}", 

144 } 

145 

146 return TemplateResponse(request, "in_person/enrollment/retry.html", context) 

147 

148 

149def system_error(request): 

150 """View handler for an enrollment system error.""" 

151 agency = session.agency(request) 

152 context = { 

153 **admin_site.each_context(request), 

154 "title": f"{agency.long_name} | In-person enrollment | {admin_site.site_title}", 

155 } 

156 

157 return TemplateResponse(request, "in_person/enrollment/system_error.html", context) 

158 

159 

160def server_error(request): 

161 """View handler for errors caused by a misconfiguration or bad request.""" 

162 agency = session.agency(request) 

163 context = { 

164 **admin_site.each_context(request), 

165 "title": f"{agency.long_name} | In-person enrollment | {admin_site.site_title}", 

166 } 

167 

168 return TemplateResponse(request, "in_person/enrollment/server_error.html", context) 

169 

170 

171def success(request): 

172 """View handler for the final success page.""" 

173 agency = session.agency(request) 

174 context = { 

175 **admin_site.each_context(request), 

176 "title": f"{agency.long_name} | In-person enrollment | {admin_site.site_title}", 

177 } 

178 

179 return TemplateResponse(request, "in_person/enrollment/success.html", context) 

180 

181 

182class SwitchioGatewayUrlView(GatewayUrlView): 

183 enrollment_method = models.EnrollmentMethods.IN_PERSON 

184 route_redirect = routes.IN_PERSON_ENROLLMENT_SWITCHIO_INDEX 

185 route_server_error = routes.IN_PERSON_SERVER_ERROR 

186 route_system_error = routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR 

187 

188 

189class SwitchioEnrollmentIndexView(SwitchioIndexView): 

190 enrollment_method = models.EnrollmentMethods.IN_PERSON 

191 form_class = forms.CardTokenizeSuccessForm 

192 route_enrollment_success = routes.IN_PERSON_ENROLLMENT_SUCCESS 

193 route_reenrollment_error = routes.IN_PERSON_ENROLLMENT_REENROLLMENT_ERROR 

194 route_retry = routes.IN_PERSON_ENROLLMENT_RETRY 

195 route_server_error = routes.IN_PERSON_SERVER_ERROR 

196 route_system_error = routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR 

197 route_tokenize_success = routes.IN_PERSON_ENROLLMENT_SWITCHIO_INDEX 

198 template_name = "in_person/enrollment/index_switchio.html" 

199 

200 def _get_verified_by(self): 

201 return f"{self.request.user.first_name} {self.request.user.last_name}" 

202 

203 def get_context_data(self, **kwargs): 

204 """Add in-person specific context data.""" 

205 context = super().get_context_data(**kwargs) 

206 

207 if self.request.GET.get("state") == "tokenize": 

208 message = "Registering this contactless card for reduced fares..." 

209 else: 

210 message = "Connecting with payment processor..." 

211 

212 context.update( 

213 { 

214 "loading_message": message, 

215 "title": f"{self.agency.long_name} | In-person enrollment | {admin_site.site_title}", 

216 } 

217 ) 

218 return context 

219 

220 def get(self, request, *args, **kwargs): 

221 if request.GET.get("error") == "canceled": 221 ↛ 226line 221 didn't jump to line 226 because the condition on line 221 was always true

222 # the user clicked the "Back" button on the Switchio tokenization gateway 

223 # send them back to the Admin index, similar to the Littlepay cancel button 

224 return redirect(routes.ADMIN_INDEX) 

225 

226 return super().get(request, *args, **kwargs)