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

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 

9 

10from moneybirdsynchronization.tasks import synchronize_moneybird_reimbursement 

11from reimbursements.emails import send_verdict_email 

12 

13from . import models 

14 

15 

16class ReimbursementForm(forms.ModelForm): 

17 class Meta: 

18 model = models.Reimbursement 

19 fields = ["amount", "date_incurred", "description", "receipt", "confirm_iban"] 

20 

21 confirm_iban = forms.BooleanField(required=True) 

22 date_incurred = forms.DateField(required=True, widget=AdminDateWidget()) 

23 

24 

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 ) 

42 

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() 

47 

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.") 

53 

54 super().save_model(request, obj, form, change) 

55 

56 if obj.verdict is not None and obj.verdict != form.initial.get("verdict"): 

57 send_verdict_email(obj) 

58 

59 if obj.verdict == obj.Verdict.APPROVED: 

60 transaction.on_commit( 

61 lambda: synchronize_moneybird_reimbursement.delay(obj.id) 

62 ) 

63 

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 ] 

76 

77 return readonly 

78 

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) 

83 

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) 

88 

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) 

93 

94 def add_view(self, request, **kwargs): 

95 """View for setting study status. 

96 

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 

101 

102 if not request.user.has_perm("reimbursements.add_reimbursement"): 

103 raise PermissionError 

104 

105 bank_account = request.member.bank_accounts.last() 

106 

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") 

114 

115 if request.POST: 

116 form = ReimbursementForm( 

117 request.POST, 

118 request.FILES, 

119 initial={"owner": request.user}, 

120 ) 

121 

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}" 

135 

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 } 

155 

156 return TemplateResponse( 

157 request, 

158 "admin/reimbursements/reimbursement/add.html", 

159 context, 

160 )