Coverage for website/reimbursements/admin.py: 40.59%
73 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 django import forms
2from django.contrib import admin
3from django.contrib.admin.widgets import AdminDateWidget
4from django.core.exceptions import ValidationError
5from django.db import transaction
6from django.shortcuts import redirect
7from django.template.response import TemplateResponse
8from django.utils import timezone
10from moneybirdsynchronization.tasks import synchronize_moneybird_reimbursement
11from reimbursements.emails import send_verdict_email
13from . import models
16class ReimbursementForm(forms.ModelForm):
17 class Meta:
18 model = models.Reimbursement
19 fields = ["amount", "date_incurred", "description", "receipt", "confirm_iban"]
21 confirm_iban = forms.BooleanField(required=True)
22 date_incurred = forms.DateField(required=True, widget=AdminDateWidget())
25@admin.register(models.Reimbursement)
26class ReimbursementsAdmin(admin.ModelAdmin):
27 list_display = (
28 "owner",
29 "created",
30 "date_incurred",
31 "amount",
32 "verdict",
33 "evaluated_by",
34 )
35 list_filter = ("verdict", "created", "date_incurred")
36 search_fields = (
37 "date_incurred",
38 "owner__first_name",
39 "owner__last_name",
40 "description",
41 )
43 def save_model(self, request, obj, form, change):
44 if obj.verdict is not None and change:
45 obj.evaluated_by = request.user
46 obj.evaluated_at = timezone.now()
48 if obj.verdict == obj.Verdict.APPROVED or obj.verdict == obj.Verdict.DENIED:
49 if not obj.evaluated_by:
50 raise ValidationError("You must provide the evaluator.")
51 if not obj.evaluated_at:
52 raise ValidationError("You must provide the evaluation date.")
54 super().save_model(request, obj, form, change)
56 if obj.verdict is not None and obj.verdict != form.initial.get("verdict"):
57 send_verdict_email(obj)
59 if obj.verdict == obj.Verdict.APPROVED:
60 transaction.on_commit(
61 lambda: synchronize_moneybird_reimbursement.delay(obj.id)
62 )
64 def get_readonly_fields(self, request, obj=None):
65 readonly = [
66 "created",
67 "owner",
68 ]
69 if obj:
70 readonly += [
71 "description",
72 "amount",
73 "date_incurred",
74 "receipt",
75 ]
77 return readonly
79 def get_queryset(self, request):
80 if request.user.has_perm("reimbursements.change_reimbursement"):
81 return super().get_queryset(request)
82 return models.Reimbursement.objects.filter(owner=request.user)
84 def has_view_permission(self, request, obj=None) -> bool:
85 if obj and request.member and obj.owner == request.member:
86 return True
87 return super().has_view_permission(request, obj)
89 def has_change_permission(self, request, obj=None):
90 if obj and obj.verdict is not None: 90 ↛ 91line 90 didn't jump to line 91 because the condition on line 90 was never true
91 return False
92 return super().has_change_permission(request, obj)
94 def add_view(self, request, **kwargs):
95 """View for setting study status.
97 This is implemented within the ModelAdmin because the admin templates require
98 complicated logic that is not easily implemented in a separate FormView.
99 """
100 app_label = self.opts.app_label
102 if not request.user.has_perm("reimbursements.add_reimbursement"):
103 raise PermissionError
105 bank_account = request.member.bank_accounts.last()
107 if not bank_account:
108 self.message_user(
109 request,
110 "Please add a bank account to create a reimbursement.",
111 level="ERROR",
112 )
113 return redirect("payments:bankaccount-list")
115 if request.POST:
116 form = ReimbursementForm(
117 request.POST,
118 request.FILES,
119 initial={"owner": request.user},
120 )
122 form[
123 "confirm_iban"
124 ].help_text = f"I confirm that this is my IBAN: {bank_account.iban}"
125 if form.is_valid():
126 form.instance.owner = request.user
127 form.save()
128 self.message_user(request, "Successfully created a reimbursement.")
129 return redirect("admin:reimbursements_reimbursement_changelist")
130 else:
131 form = ReimbursementForm()
132 form[
133 "confirm_iban"
134 ].help_text = f"I confirm that this is my IBAN: {bank_account.iban}"
136 context = {
137 **self.admin_site.each_context(request),
138 "module_name": "Reimbursements",
139 "title": "Create a reimbursement",
140 "subtitle": None,
141 "opts": self.opts,
142 "app_label": app_label,
143 "media": self.media,
144 "form": form,
145 "add": True,
146 "change": False,
147 "save_as": False,
148 "has_delete_permission": self.has_delete_permission(request),
149 "has_change_permission": self.has_change_permission(request),
150 "has_view_permission": self.has_view_permission(request),
151 "has_add_permission": self.has_add_permission(request),
152 "has_editable_inline_admin_formsets": False,
153 "has_file_field": True,
154 }
156 return TemplateResponse(
157 request,
158 "admin/reimbursements/reimbursement/add.html",
159 context,
160 )