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

117 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +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 SelfOrderPeriod, Shift 

8from sales.services import is_manager 

9 

10 

11class SelfOrderPeriodInline(admin.TabularInline): 

12 model = SelfOrderPeriod 

13 ordering = ("start",) 

14 extra = 0 

15 fields = ( 

16 "start", 

17 "end", 

18 ) 

19 

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

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

22 return False 

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

24 return False 

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

26 

27 

28class OrderInline(admin.TabularInline): 

29 model = Order 

30 ordering = ("created_at",) 

31 extra = 0 

32 show_change_link = True 

33 can_delete = False 

34 

35 fields = ( 

36 "created_at", 

37 "id", 

38 "order_description", 

39 "discount", 

40 "total_amount", 

41 "paid", 

42 "payer", 

43 ) 

44 

45 readonly_fields = ( 

46 "created_at", 

47 "id", 

48 "order_description", 

49 "discount", 

50 "total_amount", 

51 "paid", 

52 "payer", 

53 ) 

54 

55 def has_add_permission(self, request, obj): 

56 return False 

57 

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

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

60 return False 

61 

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

63 return False 

64 

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

66 

67 def get_queryset(self, request): 

68 queryset = super().get_queryset(request) 

69 queryset = queryset.select_properties( 

70 "total_amount", "subtotal", "num_items", "age_restricted" 

71 ) 

72 queryset = queryset.prefetch_related("payment") 

73 queryset = queryset.prefetch_related("order_items__product__product") 

74 queryset = queryset.prefetch_related("payer") 

75 return queryset 

76 

77 def total_amount(self, obj): 

78 if obj.total_amount: 

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

80 return "-" 

81 

82 def discount(self, obj): 

83 if obj.discount: 

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

85 return "-" 

86 

87 def paid(self, obj): 

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

89 return None 

90 return obj.payment is not None 

91 

92 paid.boolean = True 

93 

94 

95@register(Shift) 

96class ShiftAdmin(admin.ModelAdmin): 

97 inlines = [ 

98 SelfOrderPeriodInline, 

99 OrderInline, 

100 ] 

101 search_fields = ( 

102 "id", 

103 "title", 

104 "start", 

105 "end", 

106 ) 

107 filter_horizontal = ("managers",) 

108 date_hierarchy = "start" 

109 list_display_links = ( 

110 "id", 

111 "title", 

112 ) 

113 list_display = ( 

114 "id", 

115 "title", 

116 "start", 

117 "end", 

118 "active", 

119 "locked", 

120 "product_list", 

121 "num_orders", 

122 "total_revenue", 

123 ) 

124 fields = ( 

125 "title", 

126 "start", 

127 "end", 

128 "active", 

129 "product_list", 

130 "managers", 

131 "product_sales", 

132 "payment_method_sales", 

133 "num_orders", 

134 "total_revenue", 

135 "locked", 

136 ) 

137 

138 readonly_fields = ( 

139 "active", 

140 "total_revenue", 

141 "num_orders", 

142 "product_sales", 

143 "payment_method_sales", 

144 ) 

145 

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

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

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

149 fields += ("locked",) 

150 return fields 

151 

152 def get_queryset(self, request): 

153 queryset = super().get_queryset(request) 

154 

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

156 queryset = queryset.none() 

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

158 queryset = queryset.filter( 

159 managers__in=request.member.get_member_groups() 

160 ).distinct() 

161 

162 queryset = queryset.select_properties( 

163 "active", 

164 "total_revenue", 

165 "total_revenue_paid", 

166 "num_orders", 

167 "num_orders_paid", 

168 ) 

169 queryset = queryset.prefetch_related("event") 

170 return queryset 

171 

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

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

174 return False 

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

176 

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

178 if obj and obj.locked: 

179 return False 

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

181 return False 

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

183 

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

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

186 return False 

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

188 return False 

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

190 

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

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

193 self.message_user( 

194 request, 

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

196 messages.WARNING, 

197 ) 

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

199 

200 def active(self, obj): 

201 return obj.active 

202 

203 active.boolean = True 

204 

205 def num_orders(self, obj): 

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

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

208 return obj.num_orders 

209 

210 def total_revenue(self, obj): 

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

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

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

214 

215 def product_sales(self, obj): 

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

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

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

219 return output 

220 

221 def payment_method_sales(self, obj): 

222 output = "\n".join( 

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

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

225 ) 

226 return output