Coverage for website/sales/api/v2/admin/serializers/order.py: 90.91%
117 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from django.core.exceptions import ObjectDoesNotExist
2from django.utils.encoding import smart_str
4from rest_framework import serializers
5from rest_framework.exceptions import ValidationError
6from rest_framework.settings import api_settings
8from members.api.v2.serializers.member import MemberSerializer
9from payments.api.v2.serializers import PaymentSerializer
10from payments.api.v2.serializers.payment_amount import PaymentAmountSerializer
11from sales.models.order import Order, OrderItem
12from sales.models.product import ProductListItem
15class ProductNameRelatedField(serializers.SlugRelatedField):
16 def get_queryset(self):
17 shift = self.root.context.get("shift", None)
18 if shift is None:
19 shift = self.root.instance.shift
20 return ProductListItem.objects.filter(product_list=shift.product_list)
22 def to_internal_value(self, data):
23 if isinstance(data, ProductListItem): 23 ↛ 24line 23 didn't jump to line 24 because the condition on line 23 was never true
24 return data
26 queryset = self.get_queryset()
27 try:
28 return queryset.get(product__name=data)
29 except ObjectDoesNotExist:
30 self.fail(
31 "does_not_exist", slug_name=self.slug_field, value=smart_str(data)
32 )
33 except (TypeError, ValueError):
34 self.fail("invalid")
35 return None
37 def to_representation(self, obj):
38 return obj.product.name
41class OrderItemSerializer(serializers.ModelSerializer):
42 """Serializer for order items."""
44 class Meta:
45 model = OrderItem
46 fields = ("product", "amount", "total")
47 read_only_fields = ("total",)
49 product = ProductNameRelatedField("product")
51 total = PaymentAmountSerializer(read_only=True)
53 def get_fields(self):
54 fields = super().get_fields()
55 request = self.context.get("request", None)
56 if request and request.user and request.user.has_perm("sales.custom_prices"):
57 fields["total"].read_only = False
58 if request and request.method == "GET":
59 fields["product"] = serializers.CharField(source="product_name")
60 return fields
62 def create(self, validated_data, **kwargs):
63 order = self.context["order"]
64 item = OrderItem.objects.create(order=order, **validated_data)
65 return item
67 def update(self, instance, validated_data, **kwargs):
68 order = self.context["order"]
69 instance.order = order
70 instance.total = None # Always recalculate the total amount if updating using API (note the difference from the model that only recalculates if the total is None, to deal with historic data and allow for special discounts)
71 try:
72 super().update(instance, validated_data)
73 except ValueError as e:
74 raise ValidationError({api_settings.NON_FIELD_ERRORS_KEY: [e]}) from e
75 return instance
78class OrderSerializer(serializers.ModelSerializer):
79 """Serializer for orders."""
81 class Meta:
82 model = Order
83 fields = (
84 "pk",
85 "shift",
86 "created_at",
87 "order_items",
88 "order_description",
89 "age_restricted",
90 "subtotal",
91 "discount",
92 "total_amount",
93 "num_items",
94 "payment",
95 "payer",
96 "payment_url",
97 )
98 read_only_fields = (
99 "pk",
100 "created_at",
101 "payment",
102 "num_items",
103 "order_description",
104 )
106 shift = serializers.PrimaryKeyRelatedField(read_only=True)
108 age_restricted = serializers.BooleanField(read_only=True)
110 order_item_serializer_class = OrderItemSerializer
112 order_items = order_item_serializer_class(many=True, required=False)
114 subtotal = PaymentAmountSerializer(read_only=True)
116 discount = PaymentAmountSerializer(read_only=True)
118 total_amount = PaymentAmountSerializer(read_only=True)
120 payment = PaymentSerializer(read_only=True)
122 payer = MemberSerializer(read_only=True, detailed=False)
124 payment_url = serializers.URLField(read_only=True)
126 def get_fields(self):
127 fields = super().get_fields()
128 request = self.context.get("request", None)
129 if request and request.user and request.user.has_perm("sales.custom_prices"):
130 try:
131 fields["discount"].read_only = False
132 except KeyError:
133 pass
134 return fields
136 def create(self, validated_data):
137 shift = self.context["shift"]
138 if "order_items" in validated_data:
139 items_data = validated_data.pop("order_items")
140 order = Order.objects.create(shift=shift, **validated_data)
141 for item_data in items_data:
142 OrderItem.objects.create(order=order, **item_data)
144 order.refresh_from_db()
145 if order.num_items == 0: 145 ↛ 146line 145 didn't jump to line 146 because the condition on line 145 was never true
146 order.delete() # Delete if the order has no products anymore
147 raise ValidationError("You cannot order 0 products")
149 else:
150 order = Order.objects.create(shift=shift, **validated_data)
151 self.is_valid(raise_exception=True)
152 return order
154 def update(self, instance, validated_data):
155 # Update the order items for an order
156 if "order_items" in validated_data:
157 items_data = validated_data.pop("order_items")
158 current_items = list(instance.order_items.all())
160 # Overwrite all existing order items by the newly provided ones
161 for item_data in items_data:
162 if len(current_items) > 0:
163 item = current_items.pop(0)
164 self.order_item_serializer_class(
165 item, context={"order": instance}
166 ).update(item, item_data)
167 else:
168 # Create new order items if required
169 self.order_item_serializer_class(
170 context={"order": instance}
171 ).create(validated_data=item_data)
173 # Delete all order items that we have not updated
174 for i in current_items:
175 i.delete()
177 # Update other fields of the order as default
178 instance = super().update(instance, validated_data)
179 instance = Order.objects.get(
180 pk=instance.pk
181 ) # refresh from database to update queryable properties
183 if instance.num_items == 0: 183 ↛ 184line 183 didn't jump to line 184 because the condition on line 183 was never true
184 instance.delete() # Delete if the order has no products anymore
185 raise ValidationError("You cannot order 0 products")
187 return instance
190class OrderListSerializer(OrderSerializer):
191 class Meta:
192 model = Order
193 fields = (
194 "pk",
195 "created_at",
196 "total_amount",
197 "num_items",
198 )
199 read_only_fields = (
200 "pk",
201 "created_at",
202 "total_amount",
203 "num_items",
204 )