chore: initial import

This commit is contained in:
2025-11-09 00:26:00 -06:00
commit 67fb60e6ca
76 changed files with 3925 additions and 0 deletions

1
api/apps/__init__.py Normal file
View File

@@ -0,0 +1 @@
# Package marker for Django apps.

View File

@@ -0,0 +1,2 @@
# Default app configuration
default_app_config = "apps.commerce.apps.CommerceConfig"

View File

@@ -0,0 +1,87 @@
from django.contrib import admin
from . import models
@admin.register(models.ProductCategory)
class ProductCategoryAdmin(admin.ModelAdmin):
list_display = ("name", "slug", "display_order", "is_active")
list_filter = ("is_active",)
search_fields = ("name", "slug")
ordering = ("display_order", "name")
prepopulated_fields = {"slug": ("name",)}
@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin):
list_display = (
"name",
"category",
"price",
"inventory_quantity",
"max_per_order",
"is_active",
"is_featured",
)
list_filter = ("category", "is_active", "is_featured")
search_fields = ("name", "slug")
prepopulated_fields = {"slug": ("name",)}
autocomplete_fields = ("category",)
@admin.register(models.DeliveryZone)
class DeliveryZoneAdmin(admin.ModelAdmin):
list_display = ("label", "postal_code", "is_active")
list_filter = ("is_active",)
search_fields = ("label", "postal_code")
@admin.register(models.DeliveryOverrideCode)
class DeliveryOverrideCodeAdmin(admin.ModelAdmin):
list_display = ("code", "label", "max_uses", "usage_count", "is_active")
list_filter = ("is_active",)
search_fields = ("code", "label")
class OrderItemInline(admin.TabularInline):
model = models.OrderItem
extra = 0
readonly_fields = (
"product",
"product_name",
"product_slug",
"unit_price",
"quantity",
"line_subtotal",
)
@admin.register(models.Order)
class OrderAdmin(admin.ModelAdmin):
list_display = (
"id",
"customer_name",
"fulfillment_method",
"status",
"scheduled_date",
"total_amount",
"created_at",
)
list_filter = (
"fulfillment_method",
"status",
"scheduled_date",
"delivery_postal_code",
)
search_fields = ("id", "customer_name", "customer_email")
date_hierarchy = "scheduled_date"
inlines = [OrderItemInline]
readonly_fields = (
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"created_at",
"updated_at",
)

View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class CommerceConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.commerce"
verbose_name = "Commerce"

View File

@@ -0,0 +1,267 @@
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="DeliveryOverrideCode",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("code", models.CharField(max_length=32, unique=True)),
("label", models.CharField(max_length=120)),
("message_to_customer", models.CharField(blank=True, max_length=240)),
(
"max_uses",
models.PositiveIntegerField(default=0, help_text="0 = unlimited"),
),
("usage_count", models.PositiveIntegerField(default=0)),
("is_active", models.BooleanField(default=True)),
],
options={"ordering": ["code"]},
),
migrations.CreateModel(
name="DeliveryZone",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("label", models.CharField(max_length=120)),
("postal_code", models.CharField(db_index=True, max_length=10)),
("is_active", models.BooleanField(default=True)),
],
options={"ordering": ["postal_code"], "unique_together": {("postal_code", "label")}},
),
migrations.CreateModel(
name="Order",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("customer_name", models.CharField(max_length=160)),
("customer_email", models.EmailField(max_length=254)),
("customer_phone", models.CharField(blank=True, max_length=32)),
(
"fulfillment_method",
models.CharField(
choices=[("pickup", "Pickup"), ("delivery", "Delivery")],
db_index=True,
max_length=16,
),
),
(
"status",
models.CharField(
choices=[
("draft", "Draft"),
("pending", "Pending"),
("confirmed", "Confirmed"),
("in_production", "In Production"),
("ready", "Ready"),
("completed", "Completed"),
("cancelled", "Cancelled"),
],
db_index=True,
default="pending",
max_length=16,
),
),
("scheduled_date", models.DateField(blank=True, null=True)),
("scheduled_time_slot", models.CharField(blank=True, max_length=64)),
("delivery_address_line1", models.CharField(blank=True, max_length=160)),
("delivery_address_line2", models.CharField(blank=True, max_length=160)),
("delivery_city", models.CharField(blank=True, max_length=80)),
("delivery_state", models.CharField(blank=True, max_length=32)),
("delivery_postal_code", models.CharField(blank=True, max_length=16)),
(
"subtotal_amount",
models.DecimalField(
decimal_places=2,
default=0,
max_digits=8,
validators=[django.core.validators.MinValueValidator(0)],
),
),
(
"tax_amount",
models.DecimalField(
decimal_places=2,
default=0,
max_digits=8,
validators=[django.core.validators.MinValueValidator(0)],
),
),
(
"delivery_fee_amount",
models.DecimalField(
decimal_places=2,
default=0,
max_digits=8,
validators=[django.core.validators.MinValueValidator(0)],
),
),
(
"discount_amount",
models.DecimalField(
decimal_places=2,
default=0,
max_digits=8,
validators=[django.core.validators.MinValueValidator(0)],
),
),
(
"total_amount",
models.DecimalField(
decimal_places=2,
default=0,
max_digits=8,
validators=[django.core.validators.MinValueValidator(0)],
),
),
("notes", models.TextField(blank=True)),
(
"delivery_override_code",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="orders",
to="commerce.deliveryoverridecode",
),
),
],
options={"ordering": ["-scheduled_date", "-created_at"]},
),
migrations.CreateModel(
name="ProductCategory",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=120, unique=True)),
("slug", models.SlugField(max_length=140, unique=True)),
("description", models.TextField(blank=True)),
("display_order", models.PositiveIntegerField(default=0)),
("is_active", models.BooleanField(default=True)),
],
options={
"ordering": ["display_order", "name"],
"verbose_name_plural": "Product categories",
},
),
migrations.CreateModel(
name="Product",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=160)),
("slug", models.SlugField(max_length=180, unique=True)),
("short_description", models.CharField(blank=True, max_length=280)),
("description", models.TextField(blank=True)),
("ingredients", models.TextField(blank=True)),
(
"allergens",
models.CharField(
blank=True,
help_text="Comma-separated allergen labels.",
max_length=280,
),
),
("price", models.DecimalField(decimal_places=2, max_digits=8)),
("inventory_quantity", models.PositiveIntegerField(default=0)),
("max_per_order", models.PositiveIntegerField(default=6)),
("is_active", models.BooleanField(default=True)),
("is_featured", models.BooleanField(default=False)),
("hero_image", models.ImageField(blank=True, upload_to="products/hero/")),
(
"category",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="products",
to="commerce.productcategory",
),
),
],
options={"ordering": ["name"]},
),
migrations.CreateModel(
name="OrderItem",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("product_name", models.CharField(max_length=160)),
("product_slug", models.SlugField(max_length=180)),
("unit_price", models.DecimalField(decimal_places=2, max_digits=8)),
(
"quantity",
models.PositiveIntegerField(
default=1, validators=[django.core.validators.MinValueValidator(1)]
),
),
(
"line_subtotal",
models.DecimalField(decimal_places=2, default=0, max_digits=8),
),
(
"order",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="items",
to="commerce.order",
),
),
(
"product",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="order_items",
to="commerce.product",
),
),
],
options={"ordering": ["product_name"]},
),
]

View File

@@ -0,0 +1 @@
# Package marker for migrations.

198
api/apps/commerce/models.py Normal file
View File

@@ -0,0 +1,198 @@
import uuid
from django.core.validators import MinValueValidator
from django.db import models
class TimeStampedModel(models.Model):
"""Abstract base with created/updated timestamps."""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class ProductCategory(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=120, unique=True)
slug = models.SlugField(max_length=140, unique=True)
description = models.TextField(blank=True)
display_order = models.PositiveIntegerField(default=0)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["display_order", "name"]
verbose_name_plural = "Product categories"
def __str__(self) -> str:
return self.name
class Product(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
category = models.ForeignKey(
ProductCategory,
related_name="products",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
name = models.CharField(max_length=160)
slug = models.SlugField(max_length=180, unique=True)
short_description = models.CharField(max_length=280, blank=True)
description = models.TextField(blank=True)
ingredients = models.TextField(blank=True)
allergens = models.CharField(
max_length=280, blank=True, help_text="Comma-separated allergen labels."
)
price = models.DecimalField(max_digits=8, decimal_places=2)
inventory_quantity = models.PositiveIntegerField(default=0)
max_per_order = models.PositiveIntegerField(default=6)
is_active = models.BooleanField(default=True)
is_featured = models.BooleanField(default=False)
hero_image = models.ImageField(upload_to="products/hero/", blank=True)
class Meta:
ordering = ["name"]
def __str__(self) -> str:
return self.name
@property
def is_available(self) -> bool:
return self.is_active and self.inventory_quantity > 0
class DeliveryZone(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
label = models.CharField(max_length=120)
postal_code = models.CharField(max_length=10, db_index=True)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["postal_code"]
unique_together = ("postal_code", "label")
def __str__(self) -> str:
return f"{self.label} ({self.postal_code})"
class DeliveryOverrideCode(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
code = models.CharField(max_length=32, unique=True)
label = models.CharField(max_length=120)
message_to_customer = models.CharField(max_length=240, blank=True)
max_uses = models.PositiveIntegerField(default=0, help_text="0 = unlimited")
usage_count = models.PositiveIntegerField(default=0)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["code"]
def __str__(self) -> str:
return self.code
def can_use(self) -> bool:
if not self.is_active:
return False
if self.max_uses and self.usage_count >= self.max_uses:
return False
return True
class Order(TimeStampedModel):
class FulfillmentMethod(models.TextChoices):
PICKUP = "pickup", "Pickup"
DELIVERY = "delivery", "Delivery"
class Status(models.TextChoices):
DRAFT = "draft", "Draft"
PENDING = "pending", "Pending"
CONFIRMED = "confirmed", "Confirmed"
IN_PRODUCTION = "in_production", "In Production"
READY = "ready", "Ready"
COMPLETED = "completed", "Completed"
CANCELLED = "cancelled", "Cancelled"
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
customer_name = models.CharField(max_length=160)
customer_email = models.EmailField()
customer_phone = models.CharField(max_length=32, blank=True)
fulfillment_method = models.CharField(
max_length=16, choices=FulfillmentMethod.choices, db_index=True
)
status = models.CharField(
max_length=16, choices=Status.choices, default=Status.PENDING, db_index=True
)
scheduled_date = models.DateField(null=True, blank=True)
scheduled_time_slot = models.CharField(max_length=64, blank=True)
delivery_address_line1 = models.CharField(max_length=160, blank=True)
delivery_address_line2 = models.CharField(max_length=160, blank=True)
delivery_city = models.CharField(max_length=80, blank=True)
delivery_state = models.CharField(max_length=32, blank=True)
delivery_postal_code = models.CharField(max_length=16, blank=True)
delivery_override_code = models.ForeignKey(
DeliveryOverrideCode,
related_name="orders",
null=True,
blank=True,
on_delete=models.SET_NULL,
)
subtotal_amount = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
validators=[MinValueValidator(0)],
)
tax_amount = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
validators=[MinValueValidator(0)],
)
delivery_fee_amount = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
validators=[MinValueValidator(0)],
)
discount_amount = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
validators=[MinValueValidator(0)],
)
total_amount = models.DecimalField(
max_digits=8,
decimal_places=2,
default=0,
validators=[MinValueValidator(0)],
)
notes = models.TextField(blank=True)
class Meta:
ordering = ["-scheduled_date", "-created_at"]
def __str__(self) -> str:
return f"Order {self.id}"
class OrderItem(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
order = models.ForeignKey(Order, related_name="items", on_delete=models.CASCADE)
product = models.ForeignKey(
Product, related_name="order_items", on_delete=models.SET_NULL, null=True
)
product_name = models.CharField(max_length=160)
product_slug = models.SlugField(max_length=180)
unit_price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)])
line_subtotal = models.DecimalField(max_digits=8, decimal_places=2, default=0)
class Meta:
ordering = ["product_name"]
def __str__(self) -> str:
return f"{self.product_name} x{self.quantity}"

View File

@@ -0,0 +1,253 @@
from decimal import Decimal
from django.db import transaction
from rest_framework import serializers
from . import models
class ProductCategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.ProductCategory
fields = (
"id",
"name",
"slug",
"description",
"display_order",
"is_active",
)
class ProductSerializer(serializers.ModelSerializer):
category = ProductCategorySerializer(read_only=True)
category_id = serializers.PrimaryKeyRelatedField(
source="category",
queryset=models.ProductCategory.objects.all(),
required=False,
allow_null=True,
write_only=True,
)
class Meta:
model = models.Product
fields = (
"id",
"name",
"slug",
"short_description",
"description",
"ingredients",
"allergens",
"price",
"inventory_quantity",
"max_per_order",
"is_active",
"is_featured",
"hero_image",
"category",
"category_id",
)
class DeliveryZoneSerializer(serializers.ModelSerializer):
is_active = serializers.BooleanField(required=False)
class Meta:
model = models.DeliveryZone
fields = ("id", "label", "postal_code", "is_active")
class DeliveryOverrideCodeSerializer(serializers.ModelSerializer):
can_use = serializers.SerializerMethodField()
class Meta:
model = models.DeliveryOverrideCode
fields = (
"id",
"code",
"label",
"message_to_customer",
"max_uses",
"usage_count",
"is_active",
"can_use",
)
read_only_fields = ("usage_count", "can_use")
def get_can_use(self, obj: models.DeliveryOverrideCode) -> bool:
return obj.can_use()
class OrderItemSerializer(serializers.ModelSerializer):
line_subtotal = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
class Meta:
model = models.OrderItem
fields = (
"id",
"product",
"product_name",
"product_slug",
"unit_price",
"quantity",
"line_subtotal",
)
read_only_fields = ("product_name", "product_slug", "unit_price", "line_subtotal")
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
subtotal_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
tax_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
delivery_fee_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
discount_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
total_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
class Meta:
model = models.Order
fields = (
"id",
"customer_name",
"customer_email",
"customer_phone",
"fulfillment_method",
"status",
"scheduled_date",
"scheduled_time_slot",
"delivery_address_line1",
"delivery_address_line2",
"delivery_city",
"delivery_state",
"delivery_postal_code",
"delivery_override_code",
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"notes",
"items",
"created_at",
"updated_at",
)
read_only_fields = ("status", "created_at", "updated_at")
def create(self, validated_data):
items_data = validated_data.pop("items", [])
delivery_fee_amount = Decimal(validated_data.pop("delivery_fee_amount", Decimal("0")))
discount_amount = Decimal(validated_data.pop("discount_amount", Decimal("0")))
tax_amount = Decimal(validated_data.pop("tax_amount", Decimal("0")))
if not items_data:
raise serializers.ValidationError("Order requires at least one item.")
with transaction.atomic():
subtotal = Decimal("0")
order_items = []
for item_data in items_data:
product = item_data.get("product")
if product is None:
raise serializers.ValidationError("Each order item must reference a product.")
quantity = item_data.get("quantity", 1)
if quantity > product.inventory_quantity:
raise serializers.ValidationError(
f"Insufficient inventory for {product.name}. Requested {quantity}, "
f"available {product.inventory_quantity}."
)
line_subtotal = product.price * quantity
subtotal += line_subtotal
order_items.append(
{
"product": product,
"product_name": product.name,
"product_slug": product.slug,
"unit_price": product.price,
"quantity": quantity,
"line_subtotal": line_subtotal,
}
)
product.inventory_quantity -= quantity
product.save(update_fields=["inventory_quantity"])
total_amount = subtotal + tax_amount + delivery_fee_amount - discount_amount
order = models.Order.objects.create(
subtotal_amount=subtotal,
tax_amount=tax_amount,
delivery_fee_amount=delivery_fee_amount,
discount_amount=discount_amount,
total_amount=total_amount,
**validated_data,
)
models.OrderItem.objects.bulk_create(
[models.OrderItem(order=order, **item) for item in order_items]
)
return order
class OrderAdminSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True, read_only=True)
subtotal_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
tax_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
delivery_fee_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
discount_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
total_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
class Meta:
model = models.Order
fields = (
"id",
"customer_name",
"customer_email",
"customer_phone",
"fulfillment_method",
"status",
"scheduled_date",
"scheduled_time_slot",
"delivery_address_line1",
"delivery_address_line2",
"delivery_city",
"delivery_state",
"delivery_postal_code",
"delivery_override_code",
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"notes",
"items",
"created_at",
"updated_at",
)
read_only_fields = (
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"created_at",
"updated_at",
)

34
api/apps/commerce/urls.py Normal file
View File

@@ -0,0 +1,34 @@
from django.urls import include, path
from rest_framework import routers
from . import views
public_router = routers.DefaultRouter()
public_router.register(
"categories", views.ProductCategoryPublicViewSet, basename="category"
)
public_router.register("products", views.ProductPublicViewSet, basename="product")
public_router.register(
"delivery-zones", views.DeliveryZonePublicViewSet, basename="delivery-zone"
)
public_router.register("orders", views.OrderPublicViewSet, basename="order")
admin_router = routers.DefaultRouter()
admin_router.register(
"categories", views.ProductCategoryAdminViewSet, basename="admin-category"
)
admin_router.register("products", views.ProductAdminViewSet, basename="admin-product")
admin_router.register(
"delivery-zones", views.DeliveryZoneAdminViewSet, basename="admin-delivery-zone"
)
admin_router.register(
"delivery-overrides",
views.DeliveryOverrideCodeAdminViewSet,
basename="admin-delivery-override",
)
admin_router.register("orders", views.OrderAdminViewSet, basename="admin-order")
urlpatterns = [
path("", include(public_router.urls)),
path("admin/", include(admin_router.urls)),
]

View File

@@ -0,0 +1,82 @@
from rest_framework import mixins, permissions, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from . import models, serializers
class ProductPublicViewSet(viewsets.ReadOnlyModelViewSet):
"""Public storefront products."""
queryset = models.Product.objects.filter(is_active=True).select_related("category")
serializer_class = serializers.ProductSerializer
permission_classes = [permissions.AllowAny]
lookup_field = "slug"
class ProductAdminViewSet(viewsets.ModelViewSet):
queryset = models.Product.objects.select_related("category").all()
serializer_class = serializers.ProductSerializer
permission_classes = [permissions.IsAuthenticated]
class ProductCategoryPublicViewSet(viewsets.ReadOnlyModelViewSet):
queryset = models.ProductCategory.objects.filter(is_active=True)
serializer_class = serializers.ProductCategorySerializer
permission_classes = [permissions.AllowAny]
lookup_field = "slug"
class ProductCategoryAdminViewSet(viewsets.ModelViewSet):
queryset = models.ProductCategory.objects.all()
serializer_class = serializers.ProductCategorySerializer
permission_classes = [permissions.IsAuthenticated]
class DeliveryZonePublicViewSet(viewsets.ReadOnlyModelViewSet):
queryset = models.DeliveryZone.objects.filter(is_active=True)
serializer_class = serializers.DeliveryZoneSerializer
permission_classes = [permissions.AllowAny]
class DeliveryZoneAdminViewSet(viewsets.ModelViewSet):
queryset = models.DeliveryZone.objects.all()
serializer_class = serializers.DeliveryZoneSerializer
permission_classes = [permissions.IsAuthenticated]
class DeliveryOverrideCodeAdminViewSet(viewsets.ModelViewSet):
queryset = models.DeliveryOverrideCode.objects.all()
serializer_class = serializers.DeliveryOverrideCodeSerializer
permission_classes = [permissions.IsAuthenticated]
lookup_field = "code"
@action(methods=["post"], detail=True, permission_classes=[permissions.IsAuthenticated])
def activate(self, request, code=None):
override_code = self.get_object()
override_code.is_active = True
override_code.save(update_fields=["is_active"])
return Response(self.get_serializer(override_code).data)
@action(methods=["post"], detail=True, permission_classes=[permissions.IsAuthenticated])
def deactivate(self, request, code=None):
override_code = self.get_object()
override_code.is_active = False
override_code.save(update_fields=["is_active"])
return Response(self.get_serializer(override_code).data)
class OrderPublicViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
queryset = models.Order.objects.all()
serializer_class = serializers.OrderSerializer
permission_classes = [permissions.AllowAny]
class OrderAdminViewSet(viewsets.ModelViewSet):
queryset = (
models.Order.objects.select_related("delivery_override_code")
.prefetch_related("items")
.all()
)
serializer_class = serializers.OrderAdminSerializer
permission_classes = [permissions.IsAuthenticated]