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
« 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 _
5from payments.models import Payment
6from sales.models.order import Order
7from sales.models.shift import Shift
8from sales.services import is_manager
11class OrderInline(admin.TabularInline):
12 model = Order
13 ordering = ("created_at",)
14 extra = 0
15 show_change_link = True
16 can_delete = False
18 fields = (
19 "created_at",
20 "id",
21 "order_description",
22 "discount",
23 "total_amount",
24 "paid",
25 "payer",
26 )
28 readonly_fields = (
29 "created_at",
30 "id",
31 "order_description",
32 "discount",
33 "total_amount",
34 "paid",
35 "payer",
36 )
38 def has_add_permission(self, request, obj):
39 return False
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
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
48 return super().has_change_permission(request, obj)
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
60 def total_amount(self, obj):
61 if obj.total_amount:
62 return f"€{obj.total_amount:.2f}"
63 return "-"
65 def discount(self, obj):
66 if obj.discount:
67 return f"€{obj.discount:.2f}"
68 return "-"
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
75 paid.boolean = True
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 )
122 readonly_fields = (
123 "active",
124 "total_revenue",
125 "num_orders",
126 "product_sales",
127 "payment_method_sales",
128 )
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
136 def get_queryset(self, request):
137 queryset = super().get_queryset(request)
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()
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
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)
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)
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)
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)
184 def active(self, obj):
185 return obj.active
187 active.boolean = True
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
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}"
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
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