Coverage for website/sales/admin/shift_admin.py: 80.71%

106 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2026-06-21 23:59 +0000

1from django.contrib import admin, messages 

2from django.contrib.admin import register 

3from django.utils.translation import gettext_lazy as _ 

4 

5from payments.models import Payment 

6from sales.models.order import Order 

7from sales.models.shift import Shift 

8from sales.services import is_manager 

9 

10 

11class OrderInline(admin.TabularInline): 

12 model = Order 

13 ordering = ("created_at",) 

14 extra = 0 

15 show_change_link = True 

16 can_delete = False 

17 

18 fields = ( 

19 "created_at", 

20 "id", 

21 "order_description", 

22 "discount", 

23 "total_amount", 

24 "paid", 

25 "payer", 

26 ) 

27 

28 readonly_fields = ( 

29 "created_at", 

30 "id", 

31 "order_description", 

32 "discount", 

33 "total_amount", 

34 "paid", 

35 "payer", 

36 ) 

37 

38 def has_add_permission(self, request, obj): 

39 return False 

40 

41 def has_change_permission(self, request, obj=None): 

42 if obj and obj.locked: 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true

43 return False 

44 

45 if obj and not is_manager(request.member, obj): 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true

46 return False 

47 

48 return super().has_change_permission(request, obj) 

49 

50 def get_queryset(self, request): 

51 queryset = super().get_queryset(request) 

52 queryset = queryset.select_properties( 

53 "total_amount", "subtotal", "num_items", "age_restricted" 

54 ) 

55 queryset = queryset.prefetch_related("payment") 

56 queryset = queryset.prefetch_related("order_items__product__product") 

57 queryset = queryset.prefetch_related("payer") 

58 return queryset 

59 

60 def total_amount(self, obj): 

61 if obj.total_amount: 

62 return f"{obj.total_amount:.2f}" 

63 return "-" 

64 

65 def discount(self, obj): 

66 if obj.discount: 

67 return f"{obj.discount:.2f}" 

68 return "-" 

69 

70 def paid(self, obj): 

71 if obj.total_amount is None or obj.total_amount == 0: 

72 return None 

73 return obj.payment is not None 

74 

75 paid.boolean = True 

76 

77 

78@register(Shift) 

79class ShiftAdmin(admin.ModelAdmin): 

80 inlines = [ 

81 OrderInline, 

82 ] 

83 search_fields = ( 

84 "id", 

85 "title", 

86 "start", 

87 "end", 

88 ) 

89 filter_horizontal = ("managers",) 

90 date_hierarchy = "start" 

91 list_display_links = ( 

92 "id", 

93 "title", 

94 ) 

95 list_display = ( 

96 "id", 

97 "title", 

98 "start", 

99 "end", 

100 "active", 

101 "locked", 

102 "product_list", 

103 "num_orders", 

104 "total_revenue", 

105 ) 

106 fields = ( 

107 "title", 

108 "start", 

109 "end", 

110 "selforder", 

111 "active", 

112 "event", 

113 "product_list", 

114 "managers", 

115 "product_sales", 

116 "payment_method_sales", 

117 "num_orders", 

118 "total_revenue", 

119 "locked", 

120 ) 

121 

122 readonly_fields = ( 

123 "active", 

124 "total_revenue", 

125 "num_orders", 

126 "product_sales", 

127 "payment_method_sales", 

128 ) 

129 

130 def get_readonly_fields(self, request, obj=None): 

131 fields = super().get_readonly_fields(request, obj) 

132 if not obj or obj.locked: 132 ↛ 133line 132 didn't jump to line 133 because the condition on line 132 was never true

133 fields += ("locked",) 

134 return fields 

135 

136 def get_queryset(self, request): 

137 queryset = super().get_queryset(request) 

138 

139 if not request.member: 139 ↛ 140line 139 didn't jump to line 140 because the condition on line 139 was never true

140 queryset = queryset.none() 

141 elif not request.member.has_perm("sales.override_manager"): 141 ↛ 142line 141 didn't jump to line 142 because the condition on line 141 was never true

142 queryset = queryset.filter( 

143 managers__in=request.member.get_member_groups() 

144 ).distinct() 

145 

146 queryset = queryset.select_properties( 

147 "active", 

148 "total_revenue", 

149 "total_revenue_paid", 

150 "num_orders", 

151 "num_orders_paid", 

152 ) 

153 queryset = queryset.prefetch_related("event") 

154 return queryset 

155 

156 def has_view_permission(self, request, obj=None): 

157 if obj and not is_manager(request.member, obj): 

158 return False 

159 return super().has_view_permission(request, obj) 

160 

161 def has_change_permission(self, request, obj=None): 

162 if obj and obj.locked: 

163 return False 

164 if obj and not is_manager(request.member, obj): 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true

165 return False 

166 return super().has_change_permission(request, obj) 

167 

168 def has_delete_permission(self, request, obj=None): 

169 if obj and obj.locked: 169 ↛ 170line 169 didn't jump to line 170 because the condition on line 169 was never true

170 return False 

171 if obj and not is_manager(request.member, obj): 

172 return False 

173 return super().has_delete_permission(request, obj) 

174 

175 def changelist_view(self, request, extra_context=None): 

176 if not (request.member and request.member.has_perm("sales.override_manager")): 176 ↛ 177line 176 didn't jump to line 177 because the condition on line 176 was never true

177 self.message_user( 

178 request, 

179 _("You are only seeing shifts that you are managing."), 

180 messages.WARNING, 

181 ) 

182 return super().changelist_view(request, extra_context) 

183 

184 def active(self, obj): 

185 return obj.active 

186 

187 active.boolean = True 

188 

189 def num_orders(self, obj): 

190 if obj.num_orders and obj.num_orders != obj.num_orders_paid: 190 ↛ 192line 190 didn't jump to line 192 because the condition on line 190 was always true

191 return f"{obj.num_orders} ({obj.num_orders - obj.num_orders_paid} {_('unpaid')})" 

192 return obj.num_orders 

193 

194 def total_revenue(self, obj): 

195 if obj.total_revenue and obj.total_revenue != obj.total_revenue_paid: 195 ↛ 197line 195 didn't jump to line 197 because the condition on line 195 was always true

196 return f"{obj.total_revenue:.2f} (€{obj.total_revenue-obj.total_revenue_paid:.2f} {_('unpaid')})" 

197 return f"{obj.total_revenue or 0:.2f}" 

198 

199 def product_sales(self, obj): 

200 output = "\n".join(f"- {k}: {v}x" for k, v in obj.product_sales.items()) 

201 if obj.num_orders != obj.num_orders_paid: 201 ↛ 203line 201 didn't jump to line 203 because the condition on line 201 was always true

202 return f"{output}\n{_('This includes some orders that are unpaid.')}" 

203 return output 

204 

205 def payment_method_sales(self, obj): 

206 output = "\n".join( 

207 f"- {dict(Payment.PAYMENT_TYPE)[k] if k else _('Unpaid')}: €{v or '':.2f}" 

208 for k, v in obj.payment_method_sales.items() 

209 ) 

210 return output