We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sales order payments are not always saved
### Describe the bug
When paying for a Thalia pay order (via the sales payment view, so the QR code flow), the payment is not always stored back to the order. The payment is created properly, but after payment, the foreign key to the payment in the order is not saved. 

### How to reproduce
I am not sure exactly when this happens, at least it happens for the current shift 2 on the current staging environment. It might be because the shift has already been ended.

### Expected behaviour
Store the payment properly

### Additional context
Might be related to https://github.com/svthalia/concrexit/blob/6d0866022afb7fdf3edab34709d4d99e28039d59/website/sales/models/order.py#L123

--- 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/models/order.py`
Content:
```
1 from decimal import Decimal
2 
3 from django.conf import settings
4 from django.core.exceptions import ValidationError
5 from django.core.validators import MinValueValidator
6 from django.db import models
7 from django.db.models import (
8     Sum,
9     Value,
10     F,
11     DecimalField,
12     Q,
13     IntegerField,
14     BooleanField,
15     Count,
16 )
17 from django.db.models.functions import Coalesce
18 from django.urls import reverse
19 from django.utils import timezone
20 from django.utils.translation import gettext_lazy as _
21 from queryable_properties.managers import QueryablePropertiesManager
22 from queryable_properties.properties import AnnotationProperty
23 
24 from members.models import uuid, Member
25 from payments.models import Payable, Payment
26 from sales.models.product import ProductListItem
27 from sales.models.shift import Shift
28 
29 
30 def default_order_shift():
31     return Shift.objects.filter(active=True).first()
32 
33 
34 class Order(models.Model, Payable):
35 
36     objects = QueryablePropertiesManager()
37 
38     class Meta:
39         verbose_name = _("order")
40         verbose_name_plural = _("orders")
41         permissions = [
42             ("custom_prices", _("Can use custom prices and discounts in orders")),
43         ]
44         ordering = ["created_at"]
45 
46     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
47 
48     created_at = models.DateTimeField(
49         verbose_name=_("created at"), default=timezone.now
50     )
51 
52     shift = models.ForeignKey(
53         Shift,
54         verbose_name=_("shift"),
55         related_name="orders",
56         default=default_order_shift,
57         null=False,
58         blank=False,
59         on_delete=models.PROTECT,
60     )
61 
62     items = models.ManyToManyField(
63         ProductListItem, through="OrderItem", verbose_name=_("items"),
64     )
65 
66     payment = models.OneToOneField(
67         Payment,
68         verbose_name=_("payment"),
69         related_name="sales_order",
70         on_delete=models.SET_NULL,
71         blank=True,
72         null=True,
73     )
74 
75     discount = models.DecimalField(
76         verbose_name=_("discount"),
77         max_digits=6,
78         decimal_places=2,
79         null=True,
80         blank=True,
81         validators=[MinValueValidator(Decimal("0.00"))],
82     )
83 
84     payer = models.ForeignKey(
85         Member,
86         models.SET_NULL,
87         verbose_name=_("payer"),
88         related_name="sales_order",
89         blank=True,
90         null=True,
91     )
92 
93     age_restricted = AnnotationProperty(
94         Count(
95             "order_items__pk",
96             filter=Q(order_items__product__product__age_restricted=True),
97             output_field=BooleanField(),
98         )
99     )
100 
101     subtotal = AnnotationProperty(
102         Coalesce(Sum("order_items__total"), Value(0.00), output_field=DecimalField())
103     )
104 
105     total_amount = AnnotationProperty(
106         Coalesce(Sum("order_items__total"), Value(0.00), output_field=DecimalField())
107         - Coalesce(F("discount"), Value(0.00), output_field=DecimalField())
108     )
109 
110     num_items = AnnotationProperty(
111         Coalesce(Sum("order_items__amount"), Value(0), output_field=IntegerField())
112     )
113 
114     def save(
115         self, force_insert=False, force_update=False, using=None, update_fields=None
116     ):
117         if self.shift.locked:
118             return
119         if self.shift.start > timezone.now():
120             return
121         if (
122             self.payment
123             and float(sum(self.order_items.values_list("total", flat=True)))
124             - (self.discount or 0)
125             != self.payment.amount
126         ):
127             return
128         if self.payment and not self.payer:
129             self.payer = self.payment.paid_by
130 
131         return super(Order, self).save(force_insert, force_update, using, update_fields)
132 
133     def clean(self):
134         super().clean()
135         errors = {}
136 
137         if self.shift.start > timezone.now():
138             errors.update({"shift": _("The shift hasn't started yet.")})
139 
140         if self.shift.locked:
141             errors.update({"shift": _("The shift this order belongs to is locked.")})
142 
143         if self.discount and self.discount > self.total_amount:
144             errors.update(
145                 {"discount": _("Discount cannot be higher than total amount.")}
146             )
147 
148         if errors:
149             raise ValidationError(errors)
150 
151     @property
152     def payment_amount(self):
153         return self.total_amount
154 
155     @property
156     def payment_topic(self):
157         return f"Sales at {self.shift}"
158 
159     @property
160     def order_description(self):
161         return ", ".join(str(x) for x in self.order_items.all())
162 
163     @property
164     def payment_notes(self):
165         return (
166             f"{self.order_description}. Ordered at {self.created_at.time()} ({self.id})"
167         )
168 
169     @property
170     def payment_payer(self):
171         return self.payer
172 
173     @property
174     def accept_payment_from_any_user(self):
175         return True
176 
177     @property
178     def payment_url(self):
179         return (
180             settings.BASE_URL + reverse("sales:order-pay", kwargs={"pk": self.pk})
181             if not self.payment
182             and (self.payment_amount is not None and self.payment_amount != 0)
183             else None
184         )
185 
186     def __str__(self):
187         return f"Order {self.id} ({self.shift})"
188 
189 
190 class OrderItem(models.Model):
191     class Meta:
192         verbose_name = "item"
193         verbose_name_plural = "items"
194         ordering = ["pk"]
195         indexes = [
196             models.Index(fields=["order"]),
197         ]
198 
199     product = models.ForeignKey(
200         ProductListItem,
201         verbose_name=_("product"),
202         null=False,
203         blank=False,
204         on_delete=models.PROTECT,
205     )
206     order = models.ForeignKey(
207         Order,
208         verbose_name=_("order"),
209         related_name="order_items",
210         null=False,
211         blank=False,
212         on_delete=models.CASCADE,
213     )
214     total = models.DecimalField(
215         verbose_name=_("total"),
216         max_digits=6,
217         decimal_places=2,
218         null=False,
219         blank=True,
220         validators=[MinValueValidator(Decimal("0.00"))],
221         help_text="Only when overriding the default",
222     )
223     amount = models.PositiveSmallIntegerField(
224         verbose_name=_("amount"), null=False, blank=False
225     )
226 
227     def save(
228         self, force_insert=False, force_update=False, using=None, update_fields=None
229     ):
230         if self.order.shift.locked:
231             return
232         if self.order.payment:
233             return
234 
235         if not self.total:
236             self.total = self.product.price * self.amount
237 
238         return super(OrderItem, self).save(
239             force_insert, force_update, using, update_fields
240         )
241 
242     def clean(self):
243         super().clean()
244         errors = {}
245 
246         if self.order.shift.locked:
247             errors.update({"order": _("The shift is locked.")})
248 
249         if self.product not in self.order.shift.product_list.product_items.all():
250             errors.update({"product": _("This product is not available.")})
251 
252         if errors:
253             raise ValidationError(errors)
254 
255     def __str__(self):
256         return f"{self.amount}x {self.product.product.name}"
257
```
--- 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/models/order.py b/website/sales/models/order.py
--- a/website/sales/models/order.py
+++ b/website/sales/models/order.py
@@ -115,16 +115,17 @@
         self, force_insert=False, force_update=False, using=None, update_fields=None
     ):
         if self.shift.locked:
-            return
+            raise ValueError("The shift this order belongs to is locked.")
         if self.shift.start > timezone.now():
-            return
+            raise ValueError("The shift hasn't started yet.")
         if (
             self.payment
-            and float(sum(self.order_items.values_list("total", flat=True)))
-            - (self.discount or 0)
-            != self.payment.amount
+            and self.subtotal - Decimal(self.discount or 0) != self.payment.amount
         ):
-            return
+            # We cannot use self.total_amount as it is a requires a database query and hence will not use any updated values
+            raise ValueError(
+                "The payment amount does not match the order total amount."
+            )
         if self.payment and not self.payer:
             self.payer = self.payment.paid_by
 
@@ -228,9 +229,9 @@
         self, force_insert=False, force_update=False, using=None, update_fields=None
     ):
         if self.order.shift.locked:
-            return
+            raise ValueError("The shift this order belongs to is locked.")
         if self.order.payment:
-            return
+            raise ValueError("This order has already been paid for.")
 
         if not self.total:
             self.total = self.product.price * self.amount

{"golden_diff": "diff --git a/website/sales/models/order.py b/website/sales/models/order.py\n--- a/website/sales/models/order.py\n+++ b/website/sales/models/order.py\n@@ -115,16 +115,17 @@\n         self, force_insert=False, force_update=False, using=None, update_fields=None\n     ):\n         if self.shift.locked:\n-            return\n+            raise ValueError(\"The shift this order belongs to is locked.\")\n         if self.shift.start > timezone.now():\n-            return\n+            raise ValueError(\"The shift hasn't started yet.\")\n         if (\n             self.payment\n-            and float(sum(self.order_items.values_list(\"total\", flat=True)))\n-            - (self.discount or 0)\n-            != self.payment.amount\n+            and self.subtotal - Decimal(self.discount or 0) != self.payment.amount\n         ):\n-            return\n+            # We cannot use self.total_amount as it is a requires a database query and hence will not use any updated values\n+            raise ValueError(\n+                \"The payment amount does not match the order total amount.\"\n+            )\n         if self.payment and not self.payer:\n             self.payer = self.payment.paid_by\n \n@@ -228,9 +229,9 @@\n         self, force_insert=False, force_update=False, using=None, update_fields=None\n     ):\n         if self.order.shift.locked:\n-            return\n+            raise ValueError(\"The shift this order belongs to is locked.\")\n         if self.order.payment:\n-            return\n+            raise ValueError(\"This order has already been paid for.\")\n \n         if not self.total:\n             self.total = self.product.price * self.amount\n", "issue": "Sales order payments are not always saved\n### Describe the bug\r\nWhen paying for a Thalia pay order (via the sales payment view, so the QR code flow), the payment is not always stored back to the order. The payment is created properly, but after payment, the foreign key to the payment in the order is not saved. \r\n\r\n### How to reproduce\r\nI am not sure exactly when this happens, at least it happens for the current shift 2 on the current staging environment. It might be because the shift has already been ended.\r\n\r\n### Expected behaviour\r\nStore the payment properly\r\n\r\n### Additional context\r\nMight be related to https://github.com/svthalia/concrexit/blob/6d0866022afb7fdf3edab34709d4d99e28039d59/website/sales/models/order.py#L123\n", "before_files": [{"content": "from decimal import Decimal\n\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.validators import MinValueValidator\nfrom django.db import models\nfrom django.db.models import (\n    Sum,\n    Value,\n    F,\n    DecimalField,\n    Q,\n    IntegerField,\n    BooleanField,\n    Count,\n)\nfrom django.db.models.functions import Coalesce\nfrom django.urls import reverse\nfrom django.utils import timezone\nfrom django.utils.translation import gettext_lazy as _\nfrom queryable_properties.managers import QueryablePropertiesManager\nfrom queryable_properties.properties import AnnotationProperty\n\nfrom members.models import uuid, Member\nfrom payments.models import Payable, Payment\nfrom sales.models.product import ProductListItem\nfrom sales.models.shift import Shift\n\n\ndef default_order_shift():\n    return Shift.objects.filter(active=True).first()\n\n\nclass Order(models.Model, Payable):\n\n    objects = QueryablePropertiesManager()\n\n    class Meta:\n        verbose_name = _(\"order\")\n        verbose_name_plural = _(\"orders\")\n        permissions = [\n            (\"custom_prices\", _(\"Can use custom prices and discounts in orders\")),\n        ]\n        ordering = [\"created_at\"]\n\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n\n    created_at = models.DateTimeField(\n        verbose_name=_(\"created at\"), default=timezone.now\n    )\n\n    shift = models.ForeignKey(\n        Shift,\n        verbose_name=_(\"shift\"),\n        related_name=\"orders\",\n        default=default_order_shift,\n        null=False,\n        blank=False,\n        on_delete=models.PROTECT,\n    )\n\n    items = models.ManyToManyField(\n        ProductListItem, through=\"OrderItem\", verbose_name=_(\"items\"),\n    )\n\n    payment = models.OneToOneField(\n        Payment,\n        verbose_name=_(\"payment\"),\n        related_name=\"sales_order\",\n        on_delete=models.SET_NULL,\n        blank=True,\n        null=True,\n    )\n\n    discount = models.DecimalField(\n        verbose_name=_(\"discount\"),\n        max_digits=6,\n        decimal_places=2,\n        null=True,\n        blank=True,\n        validators=[MinValueValidator(Decimal(\"0.00\"))],\n    )\n\n    payer = models.ForeignKey(\n        Member,\n        models.SET_NULL,\n        verbose_name=_(\"payer\"),\n        related_name=\"sales_order\",\n        blank=True,\n        null=True,\n    )\n\n    age_restricted = AnnotationProperty(\n        Count(\n            \"order_items__pk\",\n            filter=Q(order_items__product__product__age_restricted=True),\n            output_field=BooleanField(),\n        )\n    )\n\n    subtotal = AnnotationProperty(\n        Coalesce(Sum(\"order_items__total\"), Value(0.00), output_field=DecimalField())\n    )\n\n    total_amount = AnnotationProperty(\n        Coalesce(Sum(\"order_items__total\"), Value(0.00), output_field=DecimalField())\n        - Coalesce(F(\"discount\"), Value(0.00), output_field=DecimalField())\n    )\n\n    num_items = AnnotationProperty(\n        Coalesce(Sum(\"order_items__amount\"), Value(0), output_field=IntegerField())\n    )\n\n    def save(\n        self, force_insert=False, force_update=False, using=None, update_fields=None\n    ):\n        if self.shift.locked:\n            return\n        if self.shift.start > timezone.now():\n            return\n        if (\n            self.payment\n            and float(sum(self.order_items.values_list(\"total\", flat=True)))\n            - (self.discount or 0)\n            != self.payment.amount\n        ):\n            return\n        if self.payment and not self.payer:\n            self.payer = self.payment.paid_by\n\n        return super(Order, self).save(force_insert, force_update, using, update_fields)\n\n    def clean(self):\n        super().clean()\n        errors = {}\n\n        if self.shift.start > timezone.now():\n            errors.update({\"shift\": _(\"The shift hasn't started yet.\")})\n\n        if self.shift.locked:\n            errors.update({\"shift\": _(\"The shift this order belongs to is locked.\")})\n\n        if self.discount and self.discount > self.total_amount:\n            errors.update(\n                {\"discount\": _(\"Discount cannot be higher than total amount.\")}\n            )\n\n        if errors:\n            raise ValidationError(errors)\n\n    @property\n    def payment_amount(self):\n        return self.total_amount\n\n    @property\n    def payment_topic(self):\n        return f\"Sales at {self.shift}\"\n\n    @property\n    def order_description(self):\n        return \", \".join(str(x) for x in self.order_items.all())\n\n    @property\n    def payment_notes(self):\n        return (\n            f\"{self.order_description}. Ordered at {self.created_at.time()} ({self.id})\"\n        )\n\n    @property\n    def payment_payer(self):\n        return self.payer\n\n    @property\n    def accept_payment_from_any_user(self):\n        return True\n\n    @property\n    def payment_url(self):\n        return (\n            settings.BASE_URL + reverse(\"sales:order-pay\", kwargs={\"pk\": self.pk})\n            if not self.payment\n            and (self.payment_amount is not None and self.payment_amount != 0)\n            else None\n        )\n\n    def __str__(self):\n        return f\"Order {self.id} ({self.shift})\"\n\n\nclass OrderItem(models.Model):\n    class Meta:\n        verbose_name = \"item\"\n        verbose_name_plural = \"items\"\n        ordering = [\"pk\"]\n        indexes = [\n            models.Index(fields=[\"order\"]),\n        ]\n\n    product = models.ForeignKey(\n        ProductListItem,\n        verbose_name=_(\"product\"),\n        null=False,\n        blank=False,\n        on_delete=models.PROTECT,\n    )\n    order = models.ForeignKey(\n        Order,\n        verbose_name=_(\"order\"),\n        related_name=\"order_items\",\n        null=False,\n        blank=False,\n        on_delete=models.CASCADE,\n    )\n    total = models.DecimalField(\n        verbose_name=_(\"total\"),\n        max_digits=6,\n        decimal_places=2,\n        null=False,\n        blank=True,\n        validators=[MinValueValidator(Decimal(\"0.00\"))],\n        help_text=\"Only when overriding the default\",\n    )\n    amount = models.PositiveSmallIntegerField(\n        verbose_name=_(\"amount\"), null=False, blank=False\n    )\n\n    def save(\n        self, force_insert=False, force_update=False, using=None, update_fields=None\n    ):\n        if self.order.shift.locked:\n            return\n        if self.order.payment:\n            return\n\n        if not self.total:\n            self.total = self.product.price * self.amount\n\n        return super(OrderItem, self).save(\n            force_insert, force_update, using, update_fields\n        )\n\n    def clean(self):\n        super().clean()\n        errors = {}\n\n        if self.order.shift.locked:\n            errors.update({\"order\": _(\"The shift is locked.\")})\n\n        if self.product not in self.order.shift.product_list.product_items.all():\n            errors.update({\"product\": _(\"This product is not available.\")})\n\n        if errors:\n            raise ValidationError(errors)\n\n    def __str__(self):\n        return f\"{self.amount}x {self.product.product.name}\"\n", "path": "website/sales/models/order.py"}], "after_files": [{"content": "from decimal import Decimal\n\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.validators import MinValueValidator\nfrom django.db import models\nfrom django.db.models import (\n    Sum,\n    Value,\n    F,\n    DecimalField,\n    Q,\n    IntegerField,\n    BooleanField,\n    Count,\n)\nfrom django.db.models.functions import Coalesce\nfrom django.urls import reverse\nfrom django.utils import timezone\nfrom django.utils.translation import gettext_lazy as _\nfrom queryable_properties.managers import QueryablePropertiesManager\nfrom queryable_properties.properties import AnnotationProperty\n\nfrom members.models import uuid, Member\nfrom payments.models import Payable, Payment\nfrom sales.models.product import ProductListItem\nfrom sales.models.shift import Shift\n\n\ndef default_order_shift():\n    return Shift.objects.filter(active=True).first()\n\n\nclass Order(models.Model, Payable):\n\n    objects = QueryablePropertiesManager()\n\n    class Meta:\n        verbose_name = _(\"order\")\n        verbose_name_plural = _(\"orders\")\n        permissions = [\n            (\"custom_prices\", _(\"Can use custom prices and discounts in orders\")),\n        ]\n        ordering = [\"created_at\"]\n\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n\n    created_at = models.DateTimeField(\n        verbose_name=_(\"created at\"), default=timezone.now\n    )\n\n    shift = models.ForeignKey(\n        Shift,\n        verbose_name=_(\"shift\"),\n        related_name=\"orders\",\n        default=default_order_shift,\n        null=False,\n        blank=False,\n        on_delete=models.PROTECT,\n    )\n\n    items = models.ManyToManyField(\n        ProductListItem, through=\"OrderItem\", verbose_name=_(\"items\"),\n    )\n\n    payment = models.OneToOneField(\n        Payment,\n        verbose_name=_(\"payment\"),\n        related_name=\"sales_order\",\n        on_delete=models.SET_NULL,\n        blank=True,\n        null=True,\n    )\n\n    discount = models.DecimalField(\n        verbose_name=_(\"discount\"),\n        max_digits=6,\n        decimal_places=2,\n        null=True,\n        blank=True,\n        validators=[MinValueValidator(Decimal(\"0.00\"))],\n    )\n\n    payer = models.ForeignKey(\n        Member,\n        models.SET_NULL,\n        verbose_name=_(\"payer\"),\n        related_name=\"sales_order\",\n        blank=True,\n        null=True,\n    )\n\n    age_restricted = AnnotationProperty(\n        Count(\n            \"order_items__pk\",\n            filter=Q(order_items__product__product__age_restricted=True),\n            output_field=BooleanField(),\n        )\n    )\n\n    subtotal = AnnotationProperty(\n        Coalesce(Sum(\"order_items__total\"), Value(0.00), output_field=DecimalField())\n    )\n\n    total_amount = AnnotationProperty(\n        Coalesce(Sum(\"order_items__total\"), Value(0.00), output_field=DecimalField())\n        - Coalesce(F(\"discount\"), Value(0.00), output_field=DecimalField())\n    )\n\n    num_items = AnnotationProperty(\n        Coalesce(Sum(\"order_items__amount\"), Value(0), output_field=IntegerField())\n    )\n\n    def save(\n        self, force_insert=False, force_update=False, using=None, update_fields=None\n    ):\n        if self.shift.locked:\n            raise ValueError(\"The shift this order belongs to is locked.\")\n        if self.shift.start > timezone.now():\n            raise ValueError(\"The shift hasn't started yet.\")\n        if (\n            self.payment\n            and self.subtotal - Decimal(self.discount or 0) != self.payment.amount\n        ):\n            # We cannot use self.total_amount as it is a requires a database query and hence will not use any updated values\n            raise ValueError(\n                \"The payment amount does not match the order total amount.\"\n            )\n        if self.payment and not self.payer:\n            self.payer = self.payment.paid_by\n\n        return super(Order, self).save(force_insert, force_update, using, update_fields)\n\n    def clean(self):\n        super().clean()\n        errors = {}\n\n        if self.shift.start > timezone.now():\n            errors.update({\"shift\": _(\"The shift hasn't started yet.\")})\n\n        if self.shift.locked:\n            errors.update({\"shift\": _(\"The shift this order belongs to is locked.\")})\n\n        if self.discount and self.discount > self.total_amount:\n            errors.update(\n                {\"discount\": _(\"Discount cannot be higher than total amount.\")}\n            )\n\n        if errors:\n            raise ValidationError(errors)\n\n    @property\n    def payment_amount(self):\n        return self.total_amount\n\n    @property\n    def payment_topic(self):\n        return f\"Sales at {self.shift}\"\n\n    @property\n    def order_description(self):\n        return \", \".join(str(x) for x in self.order_items.all())\n\n    @property\n    def payment_notes(self):\n        return (\n            f\"{self.order_description}. Ordered at {self.created_at.time()} ({self.id})\"\n        )\n\n    @property\n    def payment_payer(self):\n        return self.payer\n\n    @property\n    def accept_payment_from_any_user(self):\n        return True\n\n    @property\n    def payment_url(self):\n        return (\n            settings.BASE_URL + reverse(\"sales:order-pay\", kwargs={\"pk\": self.pk})\n            if not self.payment\n            and (self.payment_amount is not None and self.payment_amount != 0)\n            else None\n        )\n\n    def __str__(self):\n        return f\"Order {self.id} ({self.shift})\"\n\n\nclass OrderItem(models.Model):\n    class Meta:\n        verbose_name = \"item\"\n        verbose_name_plural = \"items\"\n        ordering = [\"pk\"]\n        indexes = [\n            models.Index(fields=[\"order\"]),\n        ]\n\n    product = models.ForeignKey(\n        ProductListItem,\n        verbose_name=_(\"product\"),\n        null=False,\n        blank=False,\n        on_delete=models.PROTECT,\n    )\n    order = models.ForeignKey(\n        Order,\n        verbose_name=_(\"order\"),\n        related_name=\"order_items\",\n        null=False,\n        blank=False,\n        on_delete=models.CASCADE,\n    )\n    total = models.DecimalField(\n        verbose_name=_(\"total\"),\n        max_digits=6,\n        decimal_places=2,\n        null=False,\n        blank=True,\n        validators=[MinValueValidator(Decimal(\"0.00\"))],\n        help_text=\"Only when overriding the default\",\n    )\n    amount = models.PositiveSmallIntegerField(\n        verbose_name=_(\"amount\"), null=False, blank=False\n    )\n\n    def save(\n        self, force_insert=False, force_update=False, using=None, update_fields=None\n    ):\n        if self.order.shift.locked:\n            raise ValueError(\"The shift this order belongs to is locked.\")\n        if self.order.payment:\n            raise ValueError(\"This order has already been paid for.\")\n\n        if not self.total:\n            self.total = self.product.price * self.amount\n\n        return super(OrderItem, self).save(\n            force_insert, force_update, using, update_fields\n        )\n\n    def clean(self):\n        super().clean()\n        errors = {}\n\n        if self.order.shift.locked:\n            errors.update({\"order\": _(\"The shift is locked.\")})\n\n        if self.product not in self.order.shift.product_list.product_items.all():\n            errors.update({\"product\": _(\"This product is not available.\")})\n\n        if errors:\n            raise ValidationError(errors)\n\n    def __str__(self):\n        return f\"{self.amount}x {self.product.product.name}\"\n", "path": "website/sales/models/order.py"}]}