Coverage for benefits/enrollment/views.py: 97%

87 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-15 00:14 +0000

1""" 

2The enrollment application: view definitions for the benefits enrollment flow. 

3""" 

4 

5from dataclasses import asdict, dataclass 

6import logging 

7 

8from django.template.defaultfilters import date 

9from django.template.response import TemplateResponse 

10from django.urls import reverse 

11from django.utils.decorators import decorator_from_middleware 

12from django.views.generic import RedirectView, TemplateView 

13 

14from benefits.core.context.agency import AgencySlug 

15from benefits.core.context.flow import SystemName 

16from benefits.core.context import formatted_gettext_lazy as _ 

17from benefits.routes import routes 

18from benefits.core import session 

19from benefits.core.mixins import ( 

20 AgencySessionRequiredMixin, 

21 EligibleSessionRequiredMixin, 

22 FlowSessionRequiredMixin, 

23 PageViewMixin, 

24) 

25from benefits.core.middleware import EligibleSessionRequired 

26from . import analytics 

27 

28TEMPLATE_RETRY = "enrollment/retry.html" 

29TEMPLATE_SYSTEM_ERROR = "enrollment/system_error.html" 

30 

31 

32logger = logging.getLogger(__name__) 

33 

34 

35class IndexView(AgencySessionRequiredMixin, EligibleSessionRequiredMixin, RedirectView): 

36 """CBV for the enrollment landing page.""" 

37 

38 route_origin = routes.ENROLLMENT_INDEX 

39 

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

41 route_name = self.agency.enrollment_index_route 

42 return reverse(route_name) 

43 

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

45 session.update(request, origin=reverse(self.route_origin)) 

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

47 

48 

49class ReenrollmentErrorView(FlowSessionRequiredMixin, EligibleSessionRequiredMixin, TemplateView): 

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

51 

52 template_name = "enrollment/reenrollment-error.html" 

53 

54 def get_context_data(self, **kwargs): 

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

56 

57 request = self.request 

58 

59 flow = self.flow 

60 expiry = session.enrollment_expiry(request) 

61 reenrollment = session.enrollment_reenrollment(request) 

62 

63 if flow.system_name == SystemName.CALFRESH: 63 ↛ 72line 63 didn't jump to line 72 because the condition on line 63 was always true

64 does_not_expire_until = _("Your CalFresh Cardholder transit benefit does not expire until") 

65 reenroll_on = _("You can re-enroll for this benefit beginning on") 

66 try_again = _("Please try again then.") 

67 

68 context["paragraphs"] = [ 

69 f"{does_not_expire_until} {date(expiry)}. {reenroll_on} {date(reenrollment)}. {try_again}" 

70 ] 

71 

72 return context 

73 

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

75 flow = self.flow 

76 

77 if session.logged_in(request) and flow.supports_sign_out: 

78 # overwrite origin for a logged in user 

79 # if they click the logout button, they are taken to the new route 

80 session.update(request, origin=reverse(routes.LOGGED_OUT)) 

81 

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

83 

84 

85@decorator_from_middleware(EligibleSessionRequired) 

86def retry(request): 

87 """View handler for a recoverable failure condition.""" 

88 flow = session.flow(request) 

89 agency = session.agency(request) 

90 analytics.returned_retry(request, enrollment_group=flow.group_id, transit_processor=agency.transit_processor) 

91 return TemplateResponse(request, TEMPLATE_RETRY) 

92 

93 

94@decorator_from_middleware(EligibleSessionRequired) 

95def system_error(request): 

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

97 

98 # overwrite origin so that CTA takes user to agency index 

99 agency = session.agency(request) 

100 session.update(request, origin=agency.index_url) 

101 

102 return TemplateResponse(request, TEMPLATE_SYSTEM_ERROR) 

103 

104 

105@dataclass 

106class EnrollmentSuccess: 

107 success_message: str 

108 thank_you_message: str 

109 

110 def dict(self): 

111 return asdict(self) 

112 

113 

114class DefaultEnrollmentSuccess(EnrollmentSuccess): 

115 def __init__(self, transportation_type): 

116 super().__init__( 

117 success_message=_( 

118 "You were not charged anything today. When boarding {transportation_type}, tap your contactless card and you " 

119 "will be charged a reduced fare. You will need to re-enroll if you choose to change the card you use to " 

120 "pay for transit service.", 

121 transportation_type=transportation_type, 

122 ), 

123 thank_you_message=_("Thank you for using Cal-ITP Benefits!"), 

124 ) 

125 

126 

127class AgencyCardEnrollmentSuccess(EnrollmentSuccess): 

128 def __init__(self, transit_benefit, transportation_type): 

129 super().__init__( 

130 success_message=_( 

131 "Your contactless card is now enrolled in {transit_benefit}. When boarding {transportation_type}, tap this " 

132 "card and you will be charged a reduced fare. You will need to re-enroll if you choose to change the card you " 

133 "use to pay for transit service.", 

134 transit_benefit=transit_benefit, 

135 transportation_type=transportation_type, 

136 ), 

137 thank_you_message=_("You were not charged anything today. Thank you for using Cal-ITP Benefits!"), 

138 ) 

139 

140 

141class SuccessView(PageViewMixin, FlowSessionRequiredMixin, EligibleSessionRequiredMixin, TemplateView): 

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

143 

144 template_name = "enrollment/success.html" 

145 

146 def get_context_data(self, **kwargs): 

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

148 

149 request = self.request 

150 flow = self.flow 

151 

152 context = {"redirect_to": request.path} 

153 copy = { 

154 AgencySlug.CST.value: DefaultEnrollmentSuccess( 

155 transportation_type=_("a CST bus"), 

156 ), 

157 SystemName.AGENCY_CARD.value: AgencyCardEnrollmentSuccess( 

158 transit_benefit=_("a CST Agency Card transit benefit"), transportation_type=_("a CST bus") 

159 ), 

160 AgencySlug.EDCTA.value: DefaultEnrollmentSuccess(transportation_type=_("an EDCTA bus")), 

161 AgencySlug.MST.value: DefaultEnrollmentSuccess(transportation_type=_("an MST bus")), 

162 SystemName.COURTESY_CARD.value: AgencyCardEnrollmentSuccess( 

163 transit_benefit=_("an MST Courtesy Card transit benefit"), transportation_type="an MST bus" 

164 ), 

165 AgencySlug.NEVCO.value: DefaultEnrollmentSuccess(transportation_type=_("a Nevada County Connects bus")), 

166 AgencySlug.RABA.value: DefaultEnrollmentSuccess(transportation_type=_("a RABA bus")), 

167 AgencySlug.SACRT.value: DefaultEnrollmentSuccess(transportation_type=_("a SacRT bus")), 

168 AgencySlug.SBMTD.value: DefaultEnrollmentSuccess(transportation_type=_("an SBMTD bus")), 

169 SystemName.REDUCED_FARE_MOBILITY_ID.value: AgencyCardEnrollmentSuccess( 

170 transit_benefit=_("an SBMTD Reduced Fare Mobility ID transit benefit"), transportation_type=_("an SBMTD bus") 

171 ), 

172 AgencySlug.VCTC.value: DefaultEnrollmentSuccess( 

173 transportation_type=_("a Ventura County Transportation Commission bus") 

174 ), 

175 } 

176 

177 if flow.uses_api_verification: 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true

178 copy_context = copy[flow.system_name].dict() 

179 else: 

180 copy_context = copy[flow.transit_agency.slug].dict() 

181 

182 context.update(copy_context) 

183 

184 return context 

185 

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

187 session.update(request, origin=reverse(routes.ENROLLMENT_SUCCESS)) 

188 

189 flow = self.flow 

190 

191 if session.logged_in(request) and flow.supports_sign_out: 

192 # overwrite origin for a logged in user 

193 # if they click the logout button, they are taken to the new route 

194 session.update(request, origin=reverse(routes.LOGGED_OUT)) 

195 

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