We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Order.DoesNotExist: Order matching query does not exist.
This error should be caught (I think it happens in very specific cases when orders are being deleted while you're loading the admin)


Sentry Issue: [CONCREXIT-86](https://sentry.io/organizations/thalia/issues/2542195680/?referrer=github_integration)

```
Order.DoesNotExist: Order matching query does not exist.
(6 additional frame(s) were not displayed)
...
  File "sales/admin/order_admin.py", line 342, in change_view
    return super().change_view(request, object_id, form_url, extra_context)
  File "django/contrib/admin/options.py", line 1660, in change_view
    return self.changeform_view(request, object_id, form_url, extra_context)
  File "sales/admin/order_admin.py", line 227, in changeform_view
    obj = self.model.objects.get(pk=object_id)
  File "django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "django/db/models/query.py", line 435, in get
    raise self.model.DoesNotExist(
```

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

{"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@@ -347,7 +347,23 @@\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+        if object_id:\n+            try:\n+                obj = self.model.objects.get(pk=object_id)\n+                if (\n+                    obj.age_restricted\n+                    and obj.payer\n+                    and not services.is_adult(obj.payer)\n+                ):\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+            except self.model.DoesNotExist:\n+                pass\n         return super().change_view(request, object_id, form_url, extra_context)\n \n     def order_description(self, obj):\n", "issue": "Order.DoesNotExist: Order matching query does not exist.\nThis error should be caught (I think it happens in very specific cases when orders are being deleted while you're loading the admin)\n\n\nSentry Issue: [CONCREXIT-86](https://sentry.io/organizations/thalia/issues/2542195680/?referrer=github_integration)\n\n```\nOrder.DoesNotExist: Order matching query does not exist.\n(6 additional frame(s) were not displayed)\n...\n  File \"sales/admin/order_admin.py\", line 342, in change_view\n    return super().change_view(request, object_id, form_url, extra_context)\n  File \"django/contrib/admin/options.py\", line 1660, in change_view\n    return self.changeform_view(request, object_id, form_url, extra_context)\n  File \"sales/admin/order_admin.py\", line 227, in changeform_view\n    obj = self.model.objects.get(pk=object_id)\n  File \"django/db/models/manager.py\", line 85, in manager_method\n    return getattr(self.get_queryset(), name)(*args, **kwargs)\n  File \"django/db/models/query.py\", line 435, in get\n    raise self.model.DoesNotExist(\n```\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 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\", \"product_name\", \"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        \"id\",\n        \"shift\",\n        \"created_at\",\n        \"created_by\",\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        \"id\",\n        \"created_at\",\n        \"created_by\",\n        \"order_description\",\n        \"num_items\",\n        \"subtotal\",\n        \"total_amount\",\n        \"payment\",\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 save_model(self, request, obj, form, change):\n        obj.created_by = request.user\n        obj.save()\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"}], "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\", \"product_name\", \"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        \"id\",\n        \"shift\",\n        \"created_at\",\n        \"created_by\",\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        \"id\",\n        \"created_at\",\n        \"created_by\",\n        \"order_description\",\n        \"num_items\",\n        \"subtotal\",\n        \"total_amount\",\n        \"payment\",\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 save_model(self, request, obj, form, change):\n        obj.created_by = request.user\n        obj.save()\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        if object_id:\n            try:\n                obj = self.model.objects.get(pk=object_id)\n                if (\n                    obj.age_restricted\n                    and obj.payer\n                    and not services.is_adult(obj.payer)\n                ):\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            except self.model.DoesNotExist:\n                pass\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"}]}