feat: added creation endpoints
This commit is contained in:
+10
-1
@@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from shop.models import Product, ProductPrice, OrderLine, Order, Tax
|
||||
from shop.models import Product, ProductPrice, OrderLine, Order, Tax, Customer
|
||||
from unfold.admin import ModelAdmin
|
||||
|
||||
|
||||
@@ -35,6 +35,15 @@ class TaxAdmin(ModelAdmin):
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Customer)
|
||||
class CustomerAdmin(ModelAdmin):
|
||||
list_display = (
|
||||
"vat_id",
|
||||
"first_name",
|
||||
"last_name",
|
||||
)
|
||||
|
||||
|
||||
@admin.register(OrderLine)
|
||||
class OrderLineAdmin(ModelAdmin):
|
||||
autocomplete_fields = ("product",)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from shop.api.v1.viewsets import (
|
||||
TaxViewSet,
|
||||
CustomerViewSet,
|
||||
ProductViewSet,
|
||||
ProductPriceViewSet,
|
||||
OrderViewSet,
|
||||
OrderLineViewSet,
|
||||
)
|
||||
|
||||
|
||||
router = DefaultRouter()
|
||||
|
||||
router.register("taxes", TaxViewSet)
|
||||
router.register("customers", CustomerViewSet)
|
||||
router.register("product", ProductViewSet)
|
||||
router.register("product-prices", ProductPriceViewSet)
|
||||
router.register("orders", OrderViewSet)
|
||||
router.register("orders/(?P<order_id>[^/.]+)/lines", OrderLineViewSet)
|
||||
|
||||
|
||||
urlpatterns = router.urls
|
||||
@@ -0,0 +1,99 @@
|
||||
from rest_framework import serializers
|
||||
from shop.models import Product, ProductPrice, Customer, Tax, OrderLine, Order
|
||||
|
||||
|
||||
class ProductSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Product
|
||||
fields = (
|
||||
"name",
|
||||
"description",
|
||||
"stock",
|
||||
"unit",
|
||||
)
|
||||
|
||||
|
||||
class ProductPriceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ProductPrice
|
||||
fields = (
|
||||
"price",
|
||||
"date",
|
||||
"product",
|
||||
)
|
||||
|
||||
|
||||
class CustomerSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = (
|
||||
"vat_id",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"address",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"zip",
|
||||
)
|
||||
|
||||
|
||||
class TaxSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Tax
|
||||
fields = (
|
||||
"code",
|
||||
"value",
|
||||
)
|
||||
|
||||
|
||||
class OrderSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = (
|
||||
"uuid",
|
||||
"creation_date",
|
||||
"last_modification_date",
|
||||
"base_total",
|
||||
"total",
|
||||
"customer",
|
||||
"billing_address",
|
||||
"billing_city",
|
||||
"billing_state",
|
||||
"billing_country",
|
||||
"billing_zip",
|
||||
"shipping_address",
|
||||
"shipping_city",
|
||||
"shipping_state",
|
||||
"shipping_country",
|
||||
"shipping_zip",
|
||||
"contact_phone",
|
||||
)
|
||||
read_only_fields = (
|
||||
"uuid",
|
||||
"base_total",
|
||||
"total",
|
||||
)
|
||||
|
||||
|
||||
class OrderLineSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = OrderLine
|
||||
fields = (
|
||||
"product",
|
||||
"quantity",
|
||||
"order",
|
||||
"price",
|
||||
"base_total",
|
||||
"total",
|
||||
"taxes",
|
||||
"tax_value",
|
||||
)
|
||||
read_only_fields = (
|
||||
"price",
|
||||
"base_total",
|
||||
"total",
|
||||
"taxes",
|
||||
"tax_value",
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from shop.api.v1.serializers import (
|
||||
ProductSerializer,
|
||||
ProductPriceSerializer,
|
||||
CustomerSerializer,
|
||||
TaxSerializer,
|
||||
OrderSerializer,
|
||||
OrderLineSerializer,
|
||||
)
|
||||
from shop.models import Product, ProductPrice, Customer, Tax, Order, OrderLine
|
||||
|
||||
|
||||
class ProductViewSet(ModelViewSet):
|
||||
serializer_class = ProductSerializer
|
||||
queryset = Product.objects.all()
|
||||
|
||||
|
||||
class ProductPriceViewSet(ModelViewSet):
|
||||
serializer_class = ProductPriceSerializer
|
||||
queryset = ProductPrice.objects.all()
|
||||
|
||||
|
||||
class CustomerViewSet(ModelViewSet):
|
||||
serializer_class = CustomerSerializer
|
||||
queryset = Customer.objects.all()
|
||||
|
||||
|
||||
class TaxViewSet(ModelViewSet):
|
||||
serializer_class = TaxSerializer
|
||||
queryset = Tax.objects.all()
|
||||
|
||||
|
||||
class OrderViewSet(ModelViewSet):
|
||||
serializer_class = OrderSerializer
|
||||
queryset = Order.objects.all()
|
||||
lookup_field = "uuid"
|
||||
|
||||
|
||||
class OrderLineViewSet(ModelViewSet):
|
||||
serializer_class = OrderLineSerializer
|
||||
queryset = OrderLine.objects.all()
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(order_id=self.kwargs.get("order_id"))
|
||||
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 5.0.3 on 2024-03-24 18:23
|
||||
# Generated by Django 5.0.3 on 2024-03-24 22:50
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -50,64 +51,6 @@ class Migration(migrations.Migration):
|
||||
"verbose_name_plural": "Clientes",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="OrderLine",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"quantity",
|
||||
models.DecimalField(
|
||||
decimal_places=4,
|
||||
default=Decimal("1"),
|
||||
max_digits=13,
|
||||
verbose_name="Cantidad",
|
||||
),
|
||||
),
|
||||
(
|
||||
"price",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Precio"
|
||||
),
|
||||
),
|
||||
(
|
||||
"base_total",
|
||||
models.DecimalField(
|
||||
decimal_places=4,
|
||||
default=Decimal("0"),
|
||||
max_digits=13,
|
||||
verbose_name="Total sin impuestos",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tax_value",
|
||||
models.PositiveIntegerField(verbose_name="Valor de impuestos"),
|
||||
),
|
||||
(
|
||||
"taxes",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Impuestos"
|
||||
),
|
||||
),
|
||||
(
|
||||
"total",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Total"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Línea de pedido",
|
||||
"verbose_name_plural": "Líneas de pedido",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Product",
|
||||
fields=[
|
||||
@@ -196,6 +139,7 @@ class Migration(migrations.Migration):
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("uuid", models.UUIDField(default=uuid.uuid4, verbose_name="UUID")),
|
||||
(
|
||||
"creation_date",
|
||||
models.DateTimeField(
|
||||
@@ -288,24 +232,86 @@ class Migration(migrations.Migration):
|
||||
verbose_name="Cliente",
|
||||
),
|
||||
),
|
||||
(
|
||||
"lines",
|
||||
models.ManyToManyField(to="shop.orderline", verbose_name="Líneas"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Pedido",
|
||||
"verbose_name_plural": "Pedidos",
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="orderline",
|
||||
name="product",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="shop.product",
|
||||
verbose_name="Producto",
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="OrderLine",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"quantity",
|
||||
models.DecimalField(
|
||||
decimal_places=4,
|
||||
default=Decimal("1"),
|
||||
max_digits=13,
|
||||
verbose_name="Cantidad",
|
||||
),
|
||||
),
|
||||
(
|
||||
"price",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Precio"
|
||||
),
|
||||
),
|
||||
(
|
||||
"base_total",
|
||||
models.DecimalField(
|
||||
decimal_places=4,
|
||||
default=Decimal("0"),
|
||||
max_digits=13,
|
||||
verbose_name="Total sin impuestos",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tax_value",
|
||||
models.PositiveIntegerField(verbose_name="Valor de impuestos"),
|
||||
),
|
||||
(
|
||||
"taxes",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Impuestos"
|
||||
),
|
||||
),
|
||||
(
|
||||
"total",
|
||||
models.DecimalField(
|
||||
decimal_places=4, max_digits=13, verbose_name="Total"
|
||||
),
|
||||
),
|
||||
(
|
||||
"order",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="lines",
|
||||
to="shop.order",
|
||||
verbose_name="Pedido",
|
||||
),
|
||||
),
|
||||
(
|
||||
"product",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="shop.product",
|
||||
verbose_name="Producto",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Línea de pedido",
|
||||
"verbose_name_plural": "Líneas de pedido",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ProductPrice",
|
||||
|
||||
+8
-1
@@ -1,4 +1,5 @@
|
||||
from decimal import Decimal
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
@@ -108,6 +109,12 @@ class Customer(models.Model):
|
||||
|
||||
|
||||
class OrderLine(models.Model):
|
||||
order = models.ForeignKey(
|
||||
"shop.Order",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="lines",
|
||||
verbose_name=_("Pedido"),
|
||||
)
|
||||
product = models.ForeignKey(
|
||||
"shop.Product",
|
||||
on_delete=models.CASCADE,
|
||||
@@ -163,7 +170,7 @@ class OrderLine(models.Model):
|
||||
|
||||
|
||||
class Order(models.Model):
|
||||
lines = models.ManyToManyField("shop.OrderLine", verbose_name=_("Líneas"))
|
||||
uuid = models.UUIDField(default=uuid4, verbose_name=_("UUID"), db_index=True)
|
||||
creation_date = models.DateTimeField(
|
||||
auto_now_add=True, verbose_name=_("Fecha de creación")
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from decimal import Decimal
|
||||
from rest_framework.test import APITestCase as TestCase
|
||||
from shop.models import Product, ProductPrice, OrderLine, Tax, Customer
|
||||
from shop.models import Product, ProductPrice, Tax, Customer
|
||||
from shop.utils import create_order_line_for_product, create_order
|
||||
|
||||
|
||||
@@ -45,25 +45,8 @@ class ShopModelsTest(TestCase):
|
||||
country="Tatooine",
|
||||
)
|
||||
|
||||
l1 = create_order_line_for_product(
|
||||
self.potatoes,
|
||||
quantity=Decimal("1.5"),
|
||||
tax=self.tax,
|
||||
)
|
||||
l2 = create_order_line_for_product(
|
||||
self.gasoline,
|
||||
quantity=Decimal("40"),
|
||||
tax=self.tax,
|
||||
)
|
||||
l3 = create_order_line_for_product(
|
||||
self.usb_c,
|
||||
quantity=Decimal("1.00"),
|
||||
tax=self.tax,
|
||||
)
|
||||
|
||||
order = create_order(
|
||||
customer=self.customer,
|
||||
lines=OrderLine.objects.all(),
|
||||
billing_address=self.customer.address,
|
||||
billing_city=self.customer.city,
|
||||
billing_state=self.customer.state,
|
||||
@@ -71,5 +54,25 @@ class ShopModelsTest(TestCase):
|
||||
billing_country=self.customer.country,
|
||||
)
|
||||
|
||||
l1 = create_order_line_for_product(
|
||||
self.potatoes,
|
||||
quantity=Decimal("1.5"),
|
||||
tax=self.tax,
|
||||
order=order,
|
||||
)
|
||||
l2 = create_order_line_for_product(
|
||||
self.gasoline,
|
||||
quantity=Decimal("40"),
|
||||
tax=self.tax,
|
||||
order=order,
|
||||
)
|
||||
l3 = create_order_line_for_product(
|
||||
self.usb_c,
|
||||
quantity=Decimal("1.00"),
|
||||
tax=self.tax,
|
||||
order=order,
|
||||
)
|
||||
order.calculate_total_from_lines()
|
||||
|
||||
assert order.total == l1.total + l2.total + l3.total
|
||||
assert order.base_total == l1.base_total + l2.base_total + l3.base_total
|
||||
|
||||
+4
-6
@@ -5,12 +5,15 @@ from shop.models import OrderLine, Tax, Product, Order, Customer
|
||||
from django.db.models import QuerySet
|
||||
|
||||
|
||||
def create_order_line_for_product(product: Product, quantity: Decimal, tax: Tax):
|
||||
def create_order_line_for_product(
|
||||
product: Product, quantity: Decimal, tax: Tax, order: Order
|
||||
):
|
||||
price = product.prices.last().price
|
||||
base_total = round(price * quantity, 2)
|
||||
taxes = round(base_total * (tax.value / Decimal("100")), 2)
|
||||
|
||||
return OrderLine.objects.create(
|
||||
order=order,
|
||||
product=product,
|
||||
quantity=quantity,
|
||||
price=price,
|
||||
@@ -23,7 +26,6 @@ def create_order_line_for_product(product: Product, quantity: Decimal, tax: Tax)
|
||||
|
||||
def create_order(
|
||||
customer: Customer,
|
||||
lines: Iterable,
|
||||
billing_address: str,
|
||||
billing_city: str,
|
||||
billing_state: str,
|
||||
@@ -61,8 +63,4 @@ def create_order(
|
||||
shipping_zip=shipping_zip,
|
||||
)
|
||||
|
||||
for line in lines:
|
||||
order.lines.add(line)
|
||||
|
||||
order.calculate_total_from_lines()
|
||||
return order
|
||||
|
||||
Reference in New Issue
Block a user