feat: added product batches and provider models

This commit is contained in:
Pablo Moreno
2024-05-13 01:25:56 +02:00
parent 236fd5c37b
commit 5bbb5c39b9
8 changed files with 262 additions and 44 deletions
+1 -15
View File
@@ -149,17 +149,6 @@ REST_FRAMEWORK = {
"PAGE_SIZE": PAGE_SIZE,
}
JWT_AUTH = {
"JWT_SECRET_KEY": SECRET_KEY,
"JWT_VERIFY": True,
"JWT_VERIFY_EXPIRATION": True,
"JWT_EXPIRATION_DELTA": datetime.timedelta(days=7),
"JWT_ALLOW_REFRESH": True,
"JWT_REFRESH_EXPIRATION_DELTA": datetime.timedelta(days=7),
"JWT_AUTH_HEADER_PREFIX": "JWT",
"JWT_AUTH_COOKIE": "jwt",
}
LOCALE_PATHS = [
BASE_DIR / "locale",
]
@@ -196,10 +185,7 @@ SIMPLE_JWT = {
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": (
"Bearer",
"JWT",
),
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
+4
View File
@@ -6,6 +6,8 @@ from shop.api.v1.viewsets import (
ProductPriceViewSet,
OrderViewSet,
OrderLineViewSet,
ProviderViewSet,
ProductBatchViewSet,
)
@@ -13,8 +15,10 @@ router = DefaultRouter()
router.register("taxes", TaxViewSet)
router.register("customers", CustomerViewSet)
router.register("providers", ProviderViewSet)
router.register("products", ProductViewSet)
router.register("product-prices", ProductPriceViewSet)
router.register("product-batches", ProductBatchViewSet)
router.register("orders", OrderViewSet)
router.register("orders/(?P<order_id>[^/.]+)/lines", OrderLineViewSet)
+59 -22
View File
@@ -1,28 +1,16 @@
from rest_framework import serializers
from files.api.v1.serializers import FileUploadSerializer
from shop.models import Product, ProductPrice, Customer, Tax, OrderLine, Order
class ProductSerializer(serializers.ModelSerializer):
images = FileUploadSerializer(many=True, read_only=True)
price = serializers.SerializerMethodField()
def get_price(self, product):
last_price = product.prices.last()
return ProductPriceSerializer(last_price).data
class Meta:
model = Product
fields = (
"name",
"description",
"stock",
"is_digital_asset",
"images",
"price",
)
from shop.models import (
Product,
ProductPrice,
Customer,
Tax,
OrderLine,
Order,
ProductBatch,
Provider,
)
class TaxSerializer(serializers.ModelSerializer):
@@ -47,6 +35,39 @@ class ProductPriceSerializer(serializers.ModelSerializer):
)
class ProductSerializer(serializers.ModelSerializer):
images = FileUploadSerializer(many=True, read_only=True)
price = ProductPriceSerializer()
def get_price(self, product):
last_price = product.prices.last()
return ProductPriceSerializer(last_price).data
class Meta:
model = Product
fields = (
"name",
"description",
"stock",
"is_digital_asset",
"images",
"price",
)
class ProductBatchSerializer(serializers.ModelSerializer):
class Meta:
model = ProductBatch
fields = (
"code",
"product",
"quantity",
"creation_date",
"expiration_date",
)
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
@@ -63,6 +84,22 @@ class CustomerSerializer(serializers.ModelSerializer):
)
class ProviderSerializer(serializers.ModelSerializer):
class Meta:
model = Provider
fields = (
"vat_id",
"name",
"email",
"phone",
"address",
"city",
"state",
"country",
"zip",
)
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
+22 -1
View File
@@ -7,8 +7,19 @@ from shop.api.v1.serializers import (
TaxSerializer,
OrderSerializer,
OrderLineSerializer,
ProviderSerializer,
ProductBatchSerializer,
)
from shop.models import (
Product,
ProductPrice,
Customer,
Tax,
Order,
OrderLine,
Provider,
ProductBatch,
)
from shop.models import Product, ProductPrice, Customer, Tax, Order, OrderLine
class ProductViewSet(ModelViewSet):
@@ -21,11 +32,21 @@ class ProductPriceViewSet(ModelViewSet):
queryset = ProductPrice.objects.all()
class ProductBatchViewSet(ModelViewSet):
serializer_class = ProductBatchSerializer
queryset = ProductBatch.objects.all()
class CustomerViewSet(ModelViewSet):
serializer_class = CustomerSerializer
queryset = Customer.objects.all()
class ProviderViewSet(ModelViewSet):
serializer_class = ProviderSerializer
queryset = Provider.objects.all()
class TaxViewSet(ModelViewSet):
serializer_class = TaxSerializer
queryset = Tax.objects.all()
@@ -0,0 +1,114 @@
# Generated by Django 5.0.3 on 2024-05-12 23:18
import django.db.models.deletion
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("shop", "0002_remove_product_unit_order_status_product_images_and_more"),
]
operations = [
migrations.CreateModel(
name="Provider",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"vat_id",
models.CharField(
max_length=32,
unique=True,
verbose_name="Documento de identidad",
),
),
("name", models.CharField(max_length=64, verbose_name="Nombre")),
(
"email",
models.EmailField(
max_length=254, verbose_name="E-mail de contacto"
),
),
(
"phone",
models.CharField(
blank=True,
default="",
max_length=16,
null=True,
verbose_name="Teléfono de contacto",
),
),
("address", models.CharField(max_length=255, verbose_name="Dirección")),
("city", models.CharField(max_length=64, verbose_name="Ciudad")),
("state", models.CharField(max_length=64, verbose_name="Región")),
("country", models.CharField(max_length=64, verbose_name="País")),
("zip", models.CharField(max_length=32, verbose_name="Código postal")),
],
options={
"verbose_name": "Proveedor",
"verbose_name_plural": "Proveedores",
},
),
migrations.CreateModel(
name="ProductBatch",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"code",
models.CharField(
db_index=True, max_length=32, unique=True, verbose_name="Código"
),
),
(
"quantity",
models.DecimalField(
decimal_places=4,
default=Decimal("1"),
max_digits=13,
verbose_name="Cantidad",
),
),
(
"creation_date",
models.DateTimeField(
auto_now_add=True, verbose_name="Fecha de creación"
),
),
(
"expiration_date",
models.DateField(
blank=True,
null=True,
verbose_name="Fecha de caducidad / consumo preferente",
),
),
(
"product",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="shop.product",
verbose_name="Producto",
),
),
],
),
]
+57 -1
View File
@@ -60,7 +60,7 @@ class ProductPrice(models.Model):
return f"{self.price} - {self.tax.code}"
@property
def price_with_tax(self):
def price_with_tax(self) -> Decimal:
tax_value = self.price * Decimal(self.tax.value / 100)
return round(self.price + tax_value, 2)
@@ -69,6 +69,30 @@ class ProductPrice(models.Model):
verbose_name_plural = _("Precio de producto")
class ProductBatch(models.Model):
code = models.CharField(
max_length=32, unique=True, db_index=True, verbose_name=_("Código")
)
product = models.ForeignKey(
"shop.Product", on_delete=models.PROTECT, verbose_name=_("Producto")
)
quantity = models.DecimalField(
max_digits=13,
decimal_places=4,
default=Decimal("1"),
verbose_name=_("Cantidad"),
)
creation_date = models.DateTimeField(
auto_now_add=True, verbose_name=_("Fecha de creación")
)
expiration_date = models.DateField(
blank=True, null=True, verbose_name=_("Fecha de caducidad / consumo preferente")
)
def __str__(self):
return f"{self.code} - {self.product.name} - {self.quantity}"
class Tax(models.Model):
code = models.CharField(
max_length=8, blank=False, unique=True, verbose_name=_("Código de impuesto")
@@ -112,6 +136,38 @@ class Customer(models.Model):
verbose_name_plural = _("Clientes")
class Provider(models.Model):
vat_id = models.CharField(
max_length=32,
blank=False,
unique=True,
verbose_name=_("Documento de identidad"),
)
name = models.CharField(max_length=64, blank=False, verbose_name=_("Nombre"))
email = models.EmailField(blank=False, verbose_name=_("E-mail de contacto"))
phone = models.CharField(
max_length=16,
blank=True,
null=True,
default="",
verbose_name=_("Teléfono de contacto"),
)
address = models.CharField(max_length=255, blank=False, verbose_name=_("Dirección"))
city = models.CharField(max_length=64, blank=False, verbose_name=_("Ciudad"))
state = models.CharField(max_length=64, blank=False, verbose_name=_("Región"))
country = models.CharField(max_length=64, blank=False, verbose_name=_("País"))
zip = models.CharField(max_length=32, blank=False, verbose_name=_("Código postal"))
def __str__(self):
return f"{self.vat_id} - {self.name}"
class Meta:
verbose_name = _("Proveedor")
verbose_name_plural = _("Proveedores")
class OrderLine(models.Model):
order = models.ForeignKey(
"shop.Order",
+4 -4
View File
@@ -27,7 +27,7 @@ class TestChangePassword(APITestCase):
assert response.status_code == status.HTTP_200_OK
jwt_token = response.data.get("access")
self.client.credentials(HTTP_AUTHORIZATION=f"JWT {jwt_token}")
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
response = self.client.put(
"/api/v1/auth/change-password/",
{
@@ -50,7 +50,7 @@ class TestChangePassword(APITestCase):
assert response.status_code == status.HTTP_200_OK
jwt_token = response.data.get("access")
self.client.credentials(HTTP_AUTHORIZATION=f"JWT {jwt_token}")
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
response = self.client.put(
"/api/v1/auth/change-password/",
{
@@ -73,7 +73,7 @@ class TestChangePassword(APITestCase):
assert response.status_code == status.HTTP_200_OK
jwt_token = response.data.get("access")
self.client.credentials(HTTP_AUTHORIZATION=f"JWT {jwt_token}")
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
response = self.client.put(
"/api/v1/auth/change-password/",
{
@@ -96,7 +96,7 @@ class TestChangePassword(APITestCase):
assert response.status_code == status.HTTP_200_OK
jwt_token = response.data.get("access")
self.client.credentials(HTTP_AUTHORIZATION=f"JWT {jwt_token}")
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
response = self.client.put(
"/api/v1/auth/change-password/",
{
+1 -1
View File
@@ -82,7 +82,7 @@ class TestLogin(APITestCase):
assert response.status_code == status.HTTP_200_OK
jwt_token = response.data.get("access")
self.client.credentials(HTTP_AUTHORIZATION=f"JWT {jwt_token}")
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
response = self.client.get("/api/v1/auth/me/")
assert response.status_code == status.HTTP_200_OK