We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Display warning in admin for age restricted orders for under age members
https://github.com/svthalia/concrexit/blob/8244e4bd50db6e64a63aa1605756acc2fb413094/website/sales/admin/order_admin.py#L334

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `website/sales/admin/order_admin.py`
Content:
```
1 from functools import partial
2 
3 from admin_auto_filters.filters import AutocompleteFilter
4 from django.contrib import admin, messages
5 from django.contrib.admin import register, SimpleListFilter
6 from django.forms import Field
7 from django.http import HttpRequest
8 from django.urls import resolve
9 from django.utils import timezone
10 
11 from django.utils.translation import gettext_lazy as _
12 
13 from payments.widgets import PaymentWidget
14 from sales.models.order import Order, OrderItem
15 from sales.models.shift import Shift
16 from sales.services import is_manager
17 
18 
19 class OrderItemInline(admin.TabularInline):
20     model = OrderItem
21     extra = 0
22 
23     fields = ("product", "amount", "total")
24 
25     def get_readonly_fields(self, request: HttpRequest, obj: Order = None):
26         default_fields = self.readonly_fields
27 
28         if not (request.member and request.member.has_perm("sales.custom_prices")):
29             default_fields += ("total",)
30 
31         return default_fields
32 
33     def get_queryset(self, request):
34         queryset = super().get_queryset(request)
35         queryset = queryset.prefetch_related("product", "product__product")
36         return queryset
37 
38     def has_add_permission(self, request, obj):
39         if obj and obj.shift.locked:
40             return False
41 
42         if obj and obj.payment:
43             return False
44 
45         parent = self.get_parent_object_from_request(request)
46         if not parent:
47             return False
48 
49         return super().has_add_permission(request, obj)
50 
51     def has_change_permission(self, request, obj=None):
52         if obj and obj.payment:
53             return False
54         if obj and obj.shift.locked:
55             return False
56         if obj and not is_manager(request.member, obj.shift):
57             return False
58         return True
59 
60     def has_delete_permission(self, request, obj=None):
61         if obj and obj.payment:
62             return False
63         if obj and obj.shift.locked:
64             return False
65         if obj and not is_manager(request.member, obj.shift):
66             return False
67         return True
68 
69     def get_parent_object_from_request(self, request):
70         """Get parent object to determine product list."""
71         resolved = resolve(request.path_info)
72         if resolved.kwargs:
73             parent = self.parent_model.objects.get(pk=resolved.kwargs["object_id"])
74             return parent
75         return None
76 
77     def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
78         """Limit product list items to items of order's shift."""
79         field = super().formfield_for_foreignkey(db_field, request, **kwargs)
80 
81         if db_field.name == "product":
82             if request is not None:
83                 parent = self.get_parent_object_from_request(request)
84                 if parent:
85                     field.queryset = parent.shift.product_list.product_items
86             else:
87                 field.queryset = field.queryset.none()
88 
89         return field
90 
91 
92 class OrderShiftFilter(AutocompleteFilter):
93     title = _("shift")
94     field_name = "shift"
95     rel_model = Order
96 
97     def queryset(self, request, queryset):
98         if self.value():
99             return queryset.filter(shift=self.value())
100         return queryset
101 
102 
103 class OrderMemberFilter(AutocompleteFilter):
104     title = _("member")
105     field_name = "payer"
106     rel_model = Order
107 
108     def queryset(self, request, queryset):
109         if self.value():
110             return queryset.filter(payer=self.value())
111         return queryset
112 
113 
114 class OrderPaymentFilter(SimpleListFilter):
115     title = _("payment")
116     parameter_name = "payment"
117 
118     def lookups(self, request, model_admin):
119         return (
120             ("not_required", _("No payment required")),
121             ("paid", _("Paid")),
122             ("unpaid", _("Unpaid")),
123         )
124 
125     def queryset(self, request, queryset):
126         if self.value() is None:
127             return queryset
128         if self.value() == "paid":
129             return queryset.filter(payment__isnull=False)
130         if self.value() == "unpaid":
131             return queryset.filter(payment__isnull=True, total_amount__gt=0)
132         return queryset.filter(total_amount__exact=0)
133 
134 
135 class OrderProductFilter(SimpleListFilter):
136     title = _("product")
137     parameter_name = "product"
138 
139     def lookups(self, request, model_admin):
140         qs = model_admin.get_queryset(request)
141         types = qs.filter(order_items__product__product__isnull=False).values_list(
142             "order_items__product__product__id", "order_items__product__product__name"
143         )
144         return list(types.order_by("order_items__product__product__id").distinct())
145 
146     def queryset(self, request, queryset):
147         if self.value() is None:
148             return queryset
149         return queryset.filter(order_items__product__product__id__contains=self.value())
150 
151 
152 @register(Order)
153 class OrderAdmin(admin.ModelAdmin):
154     class Media:
155         pass
156 
157     inlines = [
158         OrderItemInline,
159     ]
160     ordering = ("-created_at",)
161     date_hierarchy = "created_at"
162     search_fields = (
163         "id",
164         "payer__username",
165         "payer__first_name",
166         "payer__last_name",
167         "payer__profile__nickname",
168     )
169 
170     list_display = (
171         "id",
172         "shift",
173         "created_at",
174         "order_description",
175         "num_items",
176         "discount",
177         "total_amount",
178         "paid",
179         "payer",
180     )
181     list_filter = [
182         OrderShiftFilter,
183         OrderMemberFilter,
184         OrderPaymentFilter,
185         OrderProductFilter,
186     ]
187 
188     fields = (
189         "shift",
190         "created_at",
191         "order_description",
192         "num_items",
193         "age_restricted",
194         "subtotal",
195         "discount",
196         "total_amount",
197         "payer",
198         "payment",
199         "payment_url",
200     )
201 
202     readonly_fields = (
203         "created_at",
204         "order_description",
205         "num_items",
206         "subtotal",
207         "total_amount",
208         "age_restricted",
209         "payment_url",
210     )
211 
212     def get_readonly_fields(self, request: HttpRequest, obj: Order = None):
213         """Disallow changing shift when selected."""
214         default_fields = self.readonly_fields
215 
216         if not (request.member and request.member.has_perm("sales.custom_prices")):
217             default_fields += ("discount",)
218 
219         if obj and obj.shift:
220             default_fields += ("shift",)
221 
222         return default_fields
223 
224     def get_queryset(self, request):
225         queryset = super().get_queryset(request)
226 
227         if not request.member:
228             queryset = queryset.none()
229         elif not request.member.has_perm("sales.override_manager"):
230             queryset = queryset.filter(
231                 shift__managers__in=request.member.get_member_groups()
232             ).distinct()
233 
234         queryset = queryset.select_properties(
235             "total_amount", "subtotal", "num_items", "age_restricted"
236         )
237         queryset = queryset.prefetch_related(
238             "shift", "shift__event", "shift__product_list"
239         )
240         queryset = queryset.prefetch_related(
241             "order_items", "order_items__product", "order_items__product__product"
242         )
243         queryset = queryset.prefetch_related("payment")
244         queryset = queryset.prefetch_related("payer")
245         return queryset
246 
247     def has_add_permission(self, request):
248         if not request.member:
249             return False
250         elif not request.member.has_perm("sales.override_manager"):
251             if (
252                 Shift.objects.filter(
253                     start__lte=timezone.now(),
254                     locked=False,
255                     managers__in=request.member.get_member_groups(),
256                 ).count()
257                 == 0
258             ):
259                 return False
260         return super().has_view_permission(request)
261 
262     def has_view_permission(self, request, obj=None):
263         if obj and not is_manager(request.member, obj.shift):
264             return False
265         return super().has_view_permission(request, obj)
266 
267     def has_change_permission(self, request, obj=None):
268         if obj and obj.shift.locked:
269             return False
270         if obj and obj.payment:
271             return False
272 
273         if obj and not is_manager(request.member, obj.shift):
274             return False
275 
276         return super().has_change_permission(request, obj)
277 
278     def has_delete_permission(self, request, obj=None):
279         if obj and obj.shift.locked:
280             return False
281         if obj and obj.payment:
282             return False
283 
284         if obj and not is_manager(request.member, obj.shift):
285             return False
286 
287         return super().has_delete_permission(request, obj)
288 
289     def get_form(self, request, obj=None, **kwargs):
290         """Override get form to use payment widget."""
291         return super().get_form(
292             request,
293             obj,
294             formfield_callback=partial(
295                 self.formfield_for_dbfield, request=request, obj=obj
296             ),
297             **kwargs,
298         )
299 
300     def formfield_for_dbfield(self, db_field, request, obj=None, **kwargs):
301         """Use payment widget for payments."""
302         field = super().formfield_for_dbfield(db_field, request, **kwargs)
303         if db_field.name == "payment":
304             return Field(
305                 widget=PaymentWidget(obj=obj), initial=field.initial, required=False
306             )
307         if db_field.name == "shift":
308             field.queryset = Shift.objects.filter(locked=False)
309             if not request.member:
310                 field.queryset = field.queryset.none()
311             elif not request.member.has_perm("sales.override_manager"):
312                 field.queryset = field.queryset.filter(
313                     managers__in=request.member.get_member_groups()
314                 )
315         return field
316 
317     def changelist_view(self, request, extra_context=None):
318         if not (request.member and request.member.has_perm("sales.override_manager")):
319             self.message_user(
320                 request,
321                 _("You are only seeing orders that are relevant to you."),
322                 messages.WARNING,
323             )
324         return super().changelist_view(request, extra_context)
325 
326     def change_view(self, request, object_id, form_url="", extra_context=None):
327         object_id
328         return super().change_view(request, object_id, form_url, extra_context)
329 
330     def order_description(self, obj):
331         if obj.order_description:
332             return obj.order_description
333         return "-"
334 
335     def num_items(self, obj):
336         return obj.num_items
337 
338     def subtotal(self, obj):
339         if obj.subtotal:
340             return f"€{obj.subtotal:.2f}"
341         return "-"
342 
343     def discount(self, obj):
344         if obj.discount:
345             return f"€{obj.discount:.2f}"
346         return "-"
347 
348     def total_amount(self, obj):
349         if obj.total_amount:
350             return f"€{obj.total_amount:.2f}"
351         return "-"
352 
353     def paid(self, obj):
354         if obj.total_amount is None or obj.total_amount == 0:
355             return None
356         return obj.payment is not None
357 
358     paid.boolean = True
359 
360     def age_restricted(self, obj):
361         return bool(obj.age_restricted) if obj else None
362 
363     age_restricted.boolean = True
364
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/website/sales/admin/order_admin.py b/website/sales/admin/order_admin.py
--- a/website/sales/admin/order_admin.py
+++ b/website/sales/admin/order_admin.py
@@ -11,6 +11,7 @@
 from django.utils.translation import gettext_lazy as _
 
 from payments.widgets import PaymentWidget
+from sales import services
 from sales.models.order import Order, OrderItem
 from sales.models.shift import Shift
 from sales.services import is_manager
@@ -221,6 +222,19 @@
 
         return default_fields
 
+    def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
+        if object_id:
+            obj = self.model.objects.get(pk=object_id)
+            if obj.age_restricted and obj.payer and not services.is_adult(obj.payer):
+                self.message_user(
+                    request,
+                    _(
+                        "The payer for this order is under-age while the order is age restricted!"
+                    ),
+                    messages.WARNING,
+                )
+        return super().changeform_view(request, object_id, form_url, extra_context)
+
     def get_queryset(self, request):
         queryset = super().get_queryset(request)

{"golden_diff": "diff --git a/website/sales/admin/order_admin.py b/website/sales/admin/order_admin.py\n--- a/website/sales/admin/order_admin.py\n+++ b/website/sales/admin/order_admin.py\n@@ -11,6 +11,7 @@\n from django.utils.translation import gettext_lazy as _\n \n from payments.widgets import PaymentWidget\n+from sales import services\n from sales.models.order import Order, OrderItem\n from sales.models.shift import Shift\n from sales.services import is_manager\n@@ -221,6 +222,19 @@\n \n         return default_fields\n \n+    def changeform_view(self, request, object_id=None, form_url=\"\", extra_context=None):\n+        if object_id:\n+            obj = self.model.objects.get(pk=object_id)\n+            if obj.age_restricted and obj.payer and not services.is_adult(obj.payer):\n+                self.message_user(\n+                    request,\n+                    _(\n+                        \"The payer for this order is under-age while the order is age restricted!\"\n+                    ),\n+                    messages.WARNING,\n+                )\n+        return super().changeform_view(request, object_id, form_url, extra_context)\n+\n     def get_queryset(self, request):\n         queryset = super().get_queryset(request)\n", "issue": "Display warning in admin for age restricted orders for under age members\nhttps://github.com/svthalia/concrexit/blob/8244e4bd50db6e64a63aa1605756acc2fb413094/website/sales/admin/order_admin.py#L334\n", "before_files": [{"content": "from functools import partial\n\nfrom admin_auto_filters.filters import AutocompleteFilter\nfrom django.contrib import admin, messages\nfrom django.contrib.admin import register, SimpleListFilter\nfrom django.forms import Field\nfrom django.http import HttpRequest\nfrom django.urls import resolve\nfrom django.utils import timezone\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom payments.widgets import PaymentWidget\nfrom sales.models.order import Order, OrderItem\nfrom sales.models.shift import Shift\nfrom sales.services import is_manager\n\n\nclass OrderItemInline(admin.TabularInline):\n    model = OrderItem\n    extra = 0\n\n    fields = (\"product\", \"amount\", \"total\")\n\n    def get_readonly_fields(self, request: HttpRequest, obj: Order = None):\n        default_fields = self.readonly_fields\n\n        if not (request.member and request.member.has_perm(\"sales.custom_prices\")):\n            default_fields += (\"total\",)\n\n        return default_fields\n\n    def get_queryset(self, request):\n        queryset = super().get_queryset(request)\n        queryset = queryset.prefetch_related(\"product\", \"product__product\")\n        return queryset\n\n    def has_add_permission(self, request, obj):\n        if obj and obj.shift.locked:\n            return False\n\n        if obj and obj.payment:\n            return False\n\n        parent = self.get_parent_object_from_request(request)\n        if not parent:\n            return False\n\n        return super().has_add_permission(request, obj)\n\n    def has_change_permission(self, request, obj=None):\n        if obj and obj.payment:\n            return False\n        if obj and obj.shift.locked:\n            return False\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return True\n\n    def has_delete_permission(self, request, obj=None):\n        if obj and obj.payment:\n            return False\n        if obj and obj.shift.locked:\n            return False\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return True\n\n    def get_parent_object_from_request(self, request):\n        \"\"\"Get parent object to determine product list.\"\"\"\n        resolved = resolve(request.path_info)\n        if resolved.kwargs:\n            parent = self.parent_model.objects.get(pk=resolved.kwargs[\"object_id\"])\n            return parent\n        return None\n\n    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):\n        \"\"\"Limit product list items to items of order's shift.\"\"\"\n        field = super().formfield_for_foreignkey(db_field, request, **kwargs)\n\n        if db_field.name == \"product\":\n            if request is not None:\n                parent = self.get_parent_object_from_request(request)\n                if parent:\n                    field.queryset = parent.shift.product_list.product_items\n            else:\n                field.queryset = field.queryset.none()\n\n        return field\n\n\nclass OrderShiftFilter(AutocompleteFilter):\n    title = _(\"shift\")\n    field_name = \"shift\"\n    rel_model = Order\n\n    def queryset(self, request, queryset):\n        if self.value():\n            return queryset.filter(shift=self.value())\n        return queryset\n\n\nclass OrderMemberFilter(AutocompleteFilter):\n    title = _(\"member\")\n    field_name = \"payer\"\n    rel_model = Order\n\n    def queryset(self, request, queryset):\n        if self.value():\n            return queryset.filter(payer=self.value())\n        return queryset\n\n\nclass OrderPaymentFilter(SimpleListFilter):\n    title = _(\"payment\")\n    parameter_name = \"payment\"\n\n    def lookups(self, request, model_admin):\n        return (\n            (\"not_required\", _(\"No payment required\")),\n            (\"paid\", _(\"Paid\")),\n            (\"unpaid\", _(\"Unpaid\")),\n        )\n\n    def queryset(self, request, queryset):\n        if self.value() is None:\n            return queryset\n        if self.value() == \"paid\":\n            return queryset.filter(payment__isnull=False)\n        if self.value() == \"unpaid\":\n            return queryset.filter(payment__isnull=True, total_amount__gt=0)\n        return queryset.filter(total_amount__exact=0)\n\n\nclass OrderProductFilter(SimpleListFilter):\n    title = _(\"product\")\n    parameter_name = \"product\"\n\n    def lookups(self, request, model_admin):\n        qs = model_admin.get_queryset(request)\n        types = qs.filter(order_items__product__product__isnull=False).values_list(\n            \"order_items__product__product__id\", \"order_items__product__product__name\"\n        )\n        return list(types.order_by(\"order_items__product__product__id\").distinct())\n\n    def queryset(self, request, queryset):\n        if self.value() is None:\n            return queryset\n        return queryset.filter(order_items__product__product__id__contains=self.value())\n\n\n@register(Order)\nclass OrderAdmin(admin.ModelAdmin):\n    class Media:\n        pass\n\n    inlines = [\n        OrderItemInline,\n    ]\n    ordering = (\"-created_at\",)\n    date_hierarchy = \"created_at\"\n    search_fields = (\n        \"id\",\n        \"payer__username\",\n        \"payer__first_name\",\n        \"payer__last_name\",\n        \"payer__profile__nickname\",\n    )\n\n    list_display = (\n        \"id\",\n        \"shift\",\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"discount\",\n        \"total_amount\",\n        \"paid\",\n        \"payer\",\n    )\n    list_filter = [\n        OrderShiftFilter,\n        OrderMemberFilter,\n        OrderPaymentFilter,\n        OrderProductFilter,\n    ]\n\n    fields = (\n        \"shift\",\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"age_restricted\",\n        \"subtotal\",\n        \"discount\",\n        \"total_amount\",\n        \"payer\",\n        \"payment\",\n        \"payment_url\",\n    )\n\n    readonly_fields = (\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"subtotal\",\n        \"total_amount\",\n        \"age_restricted\",\n        \"payment_url\",\n    )\n\n    def get_readonly_fields(self, request: HttpRequest, obj: Order = None):\n        \"\"\"Disallow changing shift when selected.\"\"\"\n        default_fields = self.readonly_fields\n\n        if not (request.member and request.member.has_perm(\"sales.custom_prices\")):\n            default_fields += (\"discount\",)\n\n        if obj and obj.shift:\n            default_fields += (\"shift\",)\n\n        return default_fields\n\n    def get_queryset(self, request):\n        queryset = super().get_queryset(request)\n\n        if not request.member:\n            queryset = queryset.none()\n        elif not request.member.has_perm(\"sales.override_manager\"):\n            queryset = queryset.filter(\n                shift__managers__in=request.member.get_member_groups()\n            ).distinct()\n\n        queryset = queryset.select_properties(\n            \"total_amount\", \"subtotal\", \"num_items\", \"age_restricted\"\n        )\n        queryset = queryset.prefetch_related(\n            \"shift\", \"shift__event\", \"shift__product_list\"\n        )\n        queryset = queryset.prefetch_related(\n            \"order_items\", \"order_items__product\", \"order_items__product__product\"\n        )\n        queryset = queryset.prefetch_related(\"payment\")\n        queryset = queryset.prefetch_related(\"payer\")\n        return queryset\n\n    def has_add_permission(self, request):\n        if not request.member:\n            return False\n        elif not request.member.has_perm(\"sales.override_manager\"):\n            if (\n                Shift.objects.filter(\n                    start__lte=timezone.now(),\n                    locked=False,\n                    managers__in=request.member.get_member_groups(),\n                ).count()\n                == 0\n            ):\n                return False\n        return super().has_view_permission(request)\n\n    def has_view_permission(self, request, obj=None):\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return super().has_view_permission(request, obj)\n\n    def has_change_permission(self, request, obj=None):\n        if obj and obj.shift.locked:\n            return False\n        if obj and obj.payment:\n            return False\n\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n\n        return super().has_change_permission(request, obj)\n\n    def has_delete_permission(self, request, obj=None):\n        if obj and obj.shift.locked:\n            return False\n        if obj and obj.payment:\n            return False\n\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n\n        return super().has_delete_permission(request, obj)\n\n    def get_form(self, request, obj=None, **kwargs):\n        \"\"\"Override get form to use payment widget.\"\"\"\n        return super().get_form(\n            request,\n            obj,\n            formfield_callback=partial(\n                self.formfield_for_dbfield, request=request, obj=obj\n            ),\n            **kwargs,\n        )\n\n    def formfield_for_dbfield(self, db_field, request, obj=None, **kwargs):\n        \"\"\"Use payment widget for payments.\"\"\"\n        field = super().formfield_for_dbfield(db_field, request, **kwargs)\n        if db_field.name == \"payment\":\n            return Field(\n                widget=PaymentWidget(obj=obj), initial=field.initial, required=False\n            )\n        if db_field.name == \"shift\":\n            field.queryset = Shift.objects.filter(locked=False)\n            if not request.member:\n                field.queryset = field.queryset.none()\n            elif not request.member.has_perm(\"sales.override_manager\"):\n                field.queryset = field.queryset.filter(\n                    managers__in=request.member.get_member_groups()\n                )\n        return field\n\n    def changelist_view(self, request, extra_context=None):\n        if not (request.member and request.member.has_perm(\"sales.override_manager\")):\n            self.message_user(\n                request,\n                _(\"You are only seeing orders that are relevant to you.\"),\n                messages.WARNING,\n            )\n        return super().changelist_view(request, extra_context)\n\n    def change_view(self, request, object_id, form_url=\"\", extra_context=None):\n        object_id\n        return super().change_view(request, object_id, form_url, extra_context)\n\n    def order_description(self, obj):\n        if obj.order_description:\n            return obj.order_description\n        return \"-\"\n\n    def num_items(self, obj):\n        return obj.num_items\n\n    def subtotal(self, obj):\n        if obj.subtotal:\n            return f\"\u20ac{obj.subtotal:.2f}\"\n        return \"-\"\n\n    def discount(self, obj):\n        if obj.discount:\n            return f\"\u20ac{obj.discount:.2f}\"\n        return \"-\"\n\n    def total_amount(self, obj):\n        if obj.total_amount:\n            return f\"\u20ac{obj.total_amount:.2f}\"\n        return \"-\"\n\n    def paid(self, obj):\n        if obj.total_amount is None or obj.total_amount == 0:\n            return None\n        return obj.payment is not None\n\n    paid.boolean = True\n\n    def age_restricted(self, obj):\n        return bool(obj.age_restricted) if obj else None\n\n    age_restricted.boolean = True\n", "path": "website/sales/admin/order_admin.py"}], "after_files": [{"content": "from functools import partial\n\nfrom admin_auto_filters.filters import AutocompleteFilter\nfrom django.contrib import admin, messages\nfrom django.contrib.admin import register, SimpleListFilter\nfrom django.forms import Field\nfrom django.http import HttpRequest\nfrom django.urls import resolve\nfrom django.utils import timezone\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom payments.widgets import PaymentWidget\nfrom sales import services\nfrom sales.models.order import Order, OrderItem\nfrom sales.models.shift import Shift\nfrom sales.services import is_manager\n\n\nclass OrderItemInline(admin.TabularInline):\n    model = OrderItem\n    extra = 0\n\n    fields = (\"product\", \"amount\", \"total\")\n\n    def get_readonly_fields(self, request: HttpRequest, obj: Order = None):\n        default_fields = self.readonly_fields\n\n        if not (request.member and request.member.has_perm(\"sales.custom_prices\")):\n            default_fields += (\"total\",)\n\n        return default_fields\n\n    def get_queryset(self, request):\n        queryset = super().get_queryset(request)\n        queryset = queryset.prefetch_related(\"product\", \"product__product\")\n        return queryset\n\n    def has_add_permission(self, request, obj):\n        if obj and obj.shift.locked:\n            return False\n\n        if obj and obj.payment:\n            return False\n\n        parent = self.get_parent_object_from_request(request)\n        if not parent:\n            return False\n\n        return super().has_add_permission(request, obj)\n\n    def has_change_permission(self, request, obj=None):\n        if obj and obj.payment:\n            return False\n        if obj and obj.shift.locked:\n            return False\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return True\n\n    def has_delete_permission(self, request, obj=None):\n        if obj and obj.payment:\n            return False\n        if obj and obj.shift.locked:\n            return False\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return True\n\n    def get_parent_object_from_request(self, request):\n        \"\"\"Get parent object to determine product list.\"\"\"\n        resolved = resolve(request.path_info)\n        if resolved.kwargs:\n            parent = self.parent_model.objects.get(pk=resolved.kwargs[\"object_id\"])\n            return parent\n        return None\n\n    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):\n        \"\"\"Limit product list items to items of order's shift.\"\"\"\n        field = super().formfield_for_foreignkey(db_field, request, **kwargs)\n\n        if db_field.name == \"product\":\n            if request is not None:\n                parent = self.get_parent_object_from_request(request)\n                if parent:\n                    field.queryset = parent.shift.product_list.product_items\n            else:\n                field.queryset = field.queryset.none()\n\n        return field\n\n\nclass OrderShiftFilter(AutocompleteFilter):\n    title = _(\"shift\")\n    field_name = \"shift\"\n    rel_model = Order\n\n    def queryset(self, request, queryset):\n        if self.value():\n            return queryset.filter(shift=self.value())\n        return queryset\n\n\nclass OrderMemberFilter(AutocompleteFilter):\n    title = _(\"member\")\n    field_name = \"payer\"\n    rel_model = Order\n\n    def queryset(self, request, queryset):\n        if self.value():\n            return queryset.filter(payer=self.value())\n        return queryset\n\n\nclass OrderPaymentFilter(SimpleListFilter):\n    title = _(\"payment\")\n    parameter_name = \"payment\"\n\n    def lookups(self, request, model_admin):\n        return (\n            (\"not_required\", _(\"No payment required\")),\n            (\"paid\", _(\"Paid\")),\n            (\"unpaid\", _(\"Unpaid\")),\n        )\n\n    def queryset(self, request, queryset):\n        if self.value() is None:\n            return queryset\n        if self.value() == \"paid\":\n            return queryset.filter(payment__isnull=False)\n        if self.value() == \"unpaid\":\n            return queryset.filter(payment__isnull=True, total_amount__gt=0)\n        return queryset.filter(total_amount__exact=0)\n\n\nclass OrderProductFilter(SimpleListFilter):\n    title = _(\"product\")\n    parameter_name = \"product\"\n\n    def lookups(self, request, model_admin):\n        qs = model_admin.get_queryset(request)\n        types = qs.filter(order_items__product__product__isnull=False).values_list(\n            \"order_items__product__product__id\", \"order_items__product__product__name\"\n        )\n        return list(types.order_by(\"order_items__product__product__id\").distinct())\n\n    def queryset(self, request, queryset):\n        if self.value() is None:\n            return queryset\n        return queryset.filter(order_items__product__product__id__contains=self.value())\n\n\n@register(Order)\nclass OrderAdmin(admin.ModelAdmin):\n    class Media:\n        pass\n\n    inlines = [\n        OrderItemInline,\n    ]\n    ordering = (\"-created_at\",)\n    date_hierarchy = \"created_at\"\n    search_fields = (\n        \"id\",\n        \"payer__username\",\n        \"payer__first_name\",\n        \"payer__last_name\",\n        \"payer__profile__nickname\",\n    )\n\n    list_display = (\n        \"id\",\n        \"shift\",\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"discount\",\n        \"total_amount\",\n        \"paid\",\n        \"payer\",\n    )\n    list_filter = [\n        OrderShiftFilter,\n        OrderMemberFilter,\n        OrderPaymentFilter,\n        OrderProductFilter,\n    ]\n\n    fields = (\n        \"shift\",\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"age_restricted\",\n        \"subtotal\",\n        \"discount\",\n        \"total_amount\",\n        \"payer\",\n        \"payment\",\n        \"payment_url\",\n    )\n\n    readonly_fields = (\n        \"created_at\",\n        \"order_description\",\n        \"num_items\",\n        \"subtotal\",\n        \"total_amount\",\n        \"age_restricted\",\n        \"payment_url\",\n    )\n\n    def get_readonly_fields(self, request: HttpRequest, obj: Order = None):\n        \"\"\"Disallow changing shift when selected.\"\"\"\n        default_fields = self.readonly_fields\n\n        if not (request.member and request.member.has_perm(\"sales.custom_prices\")):\n            default_fields += (\"discount\",)\n\n        if obj and obj.shift:\n            default_fields += (\"shift\",)\n\n        return default_fields\n\n    def changeform_view(self, request, object_id=None, form_url=\"\", extra_context=None):\n        if object_id:\n            obj = self.model.objects.get(pk=object_id)\n            if obj.age_restricted and obj.payer and not services.is_adult(obj.payer):\n                self.message_user(\n                    request,\n                    _(\n                        \"The payer for this order is under-age while the order is age restricted!\"\n                    ),\n                    messages.WARNING,\n                )\n        return super().changeform_view(request, object_id, form_url, extra_context)\n\n    def get_queryset(self, request):\n        queryset = super().get_queryset(request)\n\n        if not request.member:\n            queryset = queryset.none()\n        elif not request.member.has_perm(\"sales.override_manager\"):\n            queryset = queryset.filter(\n                shift__managers__in=request.member.get_member_groups()\n            ).distinct()\n\n        queryset = queryset.select_properties(\n            \"total_amount\", \"subtotal\", \"num_items\", \"age_restricted\"\n        )\n        queryset = queryset.prefetch_related(\n            \"shift\", \"shift__event\", \"shift__product_list\"\n        )\n        queryset = queryset.prefetch_related(\n            \"order_items\", \"order_items__product\", \"order_items__product__product\"\n        )\n        queryset = queryset.prefetch_related(\"payment\")\n        queryset = queryset.prefetch_related(\"payer\")\n        return queryset\n\n    def has_add_permission(self, request):\n        if not request.member:\n            return False\n        elif not request.member.has_perm(\"sales.override_manager\"):\n            if (\n                Shift.objects.filter(\n                    start__lte=timezone.now(),\n                    locked=False,\n                    managers__in=request.member.get_member_groups(),\n                ).count()\n                == 0\n            ):\n                return False\n        return super().has_view_permission(request)\n\n    def has_view_permission(self, request, obj=None):\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n        return super().has_view_permission(request, obj)\n\n    def has_change_permission(self, request, obj=None):\n        if obj and obj.shift.locked:\n            return False\n        if obj and obj.payment:\n            return False\n\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n\n        return super().has_change_permission(request, obj)\n\n    def has_delete_permission(self, request, obj=None):\n        if obj and obj.shift.locked:\n            return False\n        if obj and obj.payment:\n            return False\n\n        if obj and not is_manager(request.member, obj.shift):\n            return False\n\n        return super().has_delete_permission(request, obj)\n\n    def get_form(self, request, obj=None, **kwargs):\n        \"\"\"Override get form to use payment widget.\"\"\"\n        return super().get_form(\n            request,\n            obj,\n            formfield_callback=partial(\n                self.formfield_for_dbfield, request=request, obj=obj\n            ),\n            **kwargs,\n        )\n\n    def formfield_for_dbfield(self, db_field, request, obj=None, **kwargs):\n        \"\"\"Use payment widget for payments.\"\"\"\n        field = super().formfield_for_dbfield(db_field, request, **kwargs)\n        if db_field.name == \"payment\":\n            return Field(\n                widget=PaymentWidget(obj=obj), initial=field.initial, required=False\n            )\n        if db_field.name == \"shift\":\n            field.queryset = Shift.objects.filter(locked=False)\n            if not request.member:\n                field.queryset = field.queryset.none()\n            elif not request.member.has_perm(\"sales.override_manager\"):\n                field.queryset = field.queryset.filter(\n                    managers__in=request.member.get_member_groups()\n                )\n        return field\n\n    def changelist_view(self, request, extra_context=None):\n        if not (request.member and request.member.has_perm(\"sales.override_manager\")):\n            self.message_user(\n                request,\n                _(\"You are only seeing orders that are relevant to you.\"),\n                messages.WARNING,\n            )\n        return super().changelist_view(request, extra_context)\n\n    def change_view(self, request, object_id, form_url=\"\", extra_context=None):\n        object_id\n        return super().change_view(request, object_id, form_url, extra_context)\n\n    def order_description(self, obj):\n        if obj.order_description:\n            return obj.order_description\n        return \"-\"\n\n    def num_items(self, obj):\n        return obj.num_items\n\n    def subtotal(self, obj):\n        if obj.subtotal:\n            return f\"\u20ac{obj.subtotal:.2f}\"\n        return \"-\"\n\n    def discount(self, obj):\n        if obj.discount:\n            return f\"\u20ac{obj.discount:.2f}\"\n        return \"-\"\n\n    def total_amount(self, obj):\n        if obj.total_amount:\n            return f\"\u20ac{obj.total_amount:.2f}\"\n        return \"-\"\n\n    def paid(self, obj):\n        if obj.total_amount is None or obj.total_amount == 0:\n            return None\n        return obj.payment is not None\n\n    paid.boolean = True\n\n    def age_restricted(self, obj):\n        return bool(obj.age_restricted) if obj else None\n\n    age_restricted.boolean = True\n", "path": "website/sales/admin/order_admin.py"}]}