Coverage for website/registrations/admin.py: 100.00%
85 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from functools import partial
3from django.contrib import admin
4from django.forms import Field
5from django.utils.translation import gettext_lazy as _
7from payments.widgets import PaymentWidget
8from registrations.services import (
9 accept_registration,
10 accept_renewal,
11 reject_registration,
12 reject_renewal,
13)
15from .forms import RegistrationAdminForm
16from .models import Entry, Reference, Registration, Renewal
19class ReferenceInline(admin.StackedInline):
20 model = Reference
21 extra = 0
24@admin.register(Registration)
25class RegistrationAdmin(admin.ModelAdmin):
26 """Manage the registrations."""
28 list_display = (
29 "name",
30 "email",
31 "status",
32 "membership_type",
33 "contribution",
34 "created_at",
35 "payment",
36 "no_references",
37 "reference_count",
38 )
39 list_filter = (
40 "status",
41 "programme",
42 "membership_type",
43 "no_references",
44 "payment__type",
45 "contribution",
46 )
47 inlines = (ReferenceInline,)
48 search_fields = (
49 "first_name",
50 "last_name",
51 "email",
52 "phone_number",
53 "student_number",
54 )
55 date_hierarchy = "created_at"
56 fieldsets = (
57 (
58 _("Application information"),
59 {
60 "fields": (
61 "created_at",
62 "updated_at",
63 "username",
64 "length",
65 "contribution",
66 "membership_type",
67 "status",
68 "payment",
69 "remarks",
70 )
71 },
72 ),
73 (
74 _("Personal information"),
75 {
76 "fields": (
77 "first_name",
78 "last_name",
79 "birthday",
80 "optin_birthday",
81 "email",
82 "optin_mailinglist",
83 "phone_number",
84 )
85 },
86 ),
87 (
88 _("Address"),
89 {
90 "fields": (
91 "address_street",
92 "address_street2",
93 "address_postal_code",
94 "address_city",
95 "address_country",
96 )
97 },
98 ),
99 (
100 _("Financial"),
101 {
102 "fields": (
103 "direct_debit",
104 "initials",
105 "iban",
106 "bic",
107 "signature",
108 )
109 },
110 ),
111 (
112 _("University information"),
113 {
114 "fields": (
115 "student_number",
116 "programme",
117 "starting_year",
118 )
119 },
120 ),
121 )
123 form = RegistrationAdminForm
125 actions = ["accept_registrations", "reject_registrations"]
127 def get_actions(self, request):
128 actions = super().get_actions(request)
130 if not request.user.has_perm("registrations.review_entries"):
131 if "accept_registrations" in actions:
132 del actions["accept_registrations"]
133 if "reject_registrations" in actions:
134 del actions["reject_registrations"]
136 return actions
138 @admin.action(description="Accept selected registrations")
139 def accept_registrations(self, request, queryset): # pragma: no cover
140 if queryset.exclude(status=Registration.STATUS_REVIEW).exists():
141 self.message_user(
142 request, "Only registrations in review can be accepted", "error"
143 )
144 return
146 count = 0
147 for registration in queryset:
148 try:
149 accept_registration(registration, actor=request.user)
150 count += 1
151 except ValueError as e:
152 self.message_user(
153 request, f"Error accepting {registration}: {e.message}", "error"
154 )
156 self.message_user(request, f"Accepted {count} registrations", "success")
158 @admin.action(description="Reject selected registrations")
159 def reject_registrations(self, request, queryset): # pragma: no cover
160 if queryset.exclude(status=Registration.STATUS_REVIEW).exists():
161 self.message_user(
162 request, "Only registrations in review can be rejected", "error"
163 )
164 return
166 count = queryset.count()
167 for registration in queryset:
168 reject_registration(registration, actor=request.user)
170 self.message_user(request, f"Rejected {count} registrations", "success")
172 def reference_count(self, obj):
173 return obj.reference_set.count()
175 reference_count.short_description = _("references")
177 def get_form(self, request, obj=None, **kwargs):
178 return super().get_form(
179 request,
180 obj,
181 formfield_callback=partial(
182 self.formfield_for_dbfield, request=request, obj=obj
183 ),
184 **kwargs,
185 )
187 def formfield_for_dbfield(self, db_field, request, obj=None, **kwargs):
188 field = super().formfield_for_dbfield(db_field, request, **kwargs)
189 if db_field.name == "payment":
190 return Field(
191 widget=PaymentWidget(obj=obj), initial=field.initial, required=False
192 )
193 return field
195 def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
196 """Render the change formview.
198 Only allow when the entry has not been processed yet
199 """
200 obj = None
201 can_review = False
202 can_resend = False
203 can_revert = False
204 if object_id is not None and request.user.has_perm(
205 "registrations.review_entries"
206 ): # pragma: no cover
207 obj = self.get_object(request, object_id)
208 if obj is None:
209 return self._get_obj_does_not_exist_redirect(
210 request, self.opts, object_id
211 )
212 can_review = obj.status == Entry.STATUS_REVIEW
213 can_revert = obj.status in [Entry.STATUS_ACCEPTED, Entry.STATUS_REJECTED]
214 can_resend = obj.status == Entry.STATUS_CONFIRM and isinstance(
215 obj, Registration
216 )
218 return super().changeform_view(
219 request,
220 object_id,
221 form_url,
222 {
223 "entry": obj,
224 "can_review": can_review,
225 "can_resend": can_resend,
226 "can_revert": can_revert,
227 },
228 )
230 def get_readonly_fields(self, request, obj=None):
231 if obj is None or obj.status not in (
232 Entry.STATUS_REJECTED,
233 Entry.STATUS_ACCEPTED,
234 Entry.STATUS_COMPLETED,
235 ):
236 return ["status", "created_at", "updated_at", "payment"]
237 return [
238 field.name
239 for field in self.model._meta.get_fields()
240 if field.name not in ["payment", "no_references"] and field.editable
241 ]
243 @staticmethod
244 def name(obj):
245 return obj.get_full_name()
247 def has_change_permission(self, request, obj=None):
248 """Completed registrations are read-only."""
249 return (
250 False
251 if obj and obj.status == Entry.STATUS_COMPLETED
252 else super().has_change_permission(request, obj)
253 )
255 def has_add_permission(self, request):
256 return False
258 def save_model(self, request, obj, form, change):
259 if obj.status not in (
260 Entry.STATUS_REJECTED,
261 Entry.STATUS_ACCEPTED,
262 Entry.STATUS_COMPLETED,
263 ):
264 super().save_model(request, obj, form, change)
267@admin.register(Renewal)
268class RenewalAdmin(RegistrationAdmin):
269 """Manage the renewals."""
271 list_display = (
272 "name",
273 "email",
274 "status",
275 "membership_type",
276 "contribution",
277 "created_at",
278 "payment",
279 "no_references",
280 "reference_count",
281 )
282 list_filter = (
283 "status",
284 "membership_type",
285 "no_references",
286 "payment__type",
287 "contribution",
288 )
289 search_fields = (
290 "member__first_name",
291 "member__last_name",
292 "member__email",
293 "member__profile__phone_number",
294 "member__profile__student_number",
295 )
296 date_hierarchy = "created_at"
297 fieldsets = (
298 (
299 _("Application information"),
300 {
301 "fields": (
302 "created_at",
303 "updated_at",
304 "length",
305 "contribution",
306 "membership_type",
307 "status",
308 "payment",
309 "remarks",
310 "member",
311 )
312 },
313 ),
314 )
316 actions = ["accept_renewals", "reject_renewals"]
318 def get_actions(self, request):
319 actions = super().get_actions(request)
321 if not request.user.has_perm("registrations.review_entries"):
322 if "accept_renewals" in actions: # pragma: no cover
323 del actions["accept_renewals"]
324 if "reject_renewals" in actions: # pragma: no cover
325 del actions["reject_renewals"]
327 return actions
329 @admin.action(description="Accept selected renewals")
330 def accept_renewals(self, request, queryset): # pragma: no cover
331 if queryset.exclude(status=Renewal.STATUS_REVIEW).exists():
332 self.message_user(
333 request, "Only renewals in review can be accepted", "error"
334 )
335 return
337 count = queryset.count()
338 for renewal in queryset:
339 accept_renewal(renewal, actor=request.user)
340 count += 1
342 self.message_user(request, f"Accepted {count} renewals", "success")
344 @admin.action(description="Reject selected renewals")
345 def reject_renewals(self, request, queryset): # pragma: no cover
346 if queryset.exclude(status=Renewal.STATUS_REVIEW).exists():
347 self.message_user(
348 request, "Only renewals in review can be rejected", "error"
349 )
350 return
352 count = queryset.count()
353 for renewal in queryset:
354 reject_renewal(renewal, actor=request.user)
356 self.message_user(request, f"Rejected {count} renewals", "success")
358 def get_readonly_fields(self, request, obj=None):
359 """Make all fields read-only and add member if needed."""
360 fields = super().get_readonly_fields(request, obj)
361 if "member" not in fields and obj is not None:
362 return fields + ["member"]
363 return fields
365 def has_add_permission(self, request):
366 return False
368 @staticmethod
369 def name(obj):
370 return obj.member.get_full_name()
372 name.short_description = _("name")
374 @staticmethod
375 def email(obj):
376 return obj.member.email