feat: added tags

This commit is contained in:
Pablo Moreno
2024-05-15 22:21:20 +02:00
parent 2043440499
commit 84bd7b1951
9 changed files with 219 additions and 2 deletions
+28 -1
View File
@@ -1,5 +1,5 @@
from django.contrib import admin
from shop.models import Product, ProductPrice, OrderLine, Order, Tax, Customer
from shop.models import Product, ProductPrice, OrderLine, Order, Tax, Customer, Provider, Tag, ProductBatch
from unfold.admin import ModelAdmin
@@ -58,3 +58,30 @@ class OrderLineAdmin(ModelAdmin):
@admin.register(Order)
class OrderAdmin(ModelAdmin):
list_display = ("id",)
@admin.register(Provider)
class ProviderAdmin(ModelAdmin):
list_display = (
"vat_id",
"name",
)
@admin.register(Tag)
class TagAdmin(ModelAdmin):
list_display = (
"id",
"name",
)
@admin.register(ProductBatch)
class ProductBatchAdmin(ModelAdmin):
list_display = (
"id",
"product",
"quantity",
"provider",
)
+2
View File
@@ -8,6 +8,7 @@ from shop.api.v1.viewsets import (
OrderLineViewSet,
ProviderViewSet,
ProductBatchViewSet,
TagViewSet,
)
@@ -17,6 +18,7 @@ app_name = "shop"
router = DefaultRouter()
router.register("taxes", TaxViewSet)
router.register("tags", TagViewSet)
router.register("customers", CustomerViewSet)
router.register("providers", ProviderViewSet)
router.register("products", ProductViewSet)
+31
View File
@@ -11,6 +11,7 @@ from shop.models import (
Order,
ProductBatch,
Provider,
Tag,
)
@@ -24,6 +25,15 @@ class TaxSerializer(serializers.ModelSerializer):
)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = (
"id",
"name",
)
class ProductPriceSerializer(serializers.ModelSerializer):
tax = TaxSerializer()
@@ -60,6 +70,23 @@ class ProductSerializer(serializers.ModelSerializer):
"is_digital_asset",
"images",
"price",
"tags",
)
class ListProductSerializer(serializers.ModelSerializer):
price = ProductPriceSerializer()
tags = TagSerializer(many=True)
class Meta:
model = Product
fields = (
"name",
"description",
"stock",
"is_digital_asset",
"price",
"tags",
)
@@ -98,6 +125,8 @@ class CreateProductSerializer(serializers.ModelSerializer):
"url",
"price",
"tax",
"tags",
"images",
)
@@ -197,3 +226,5 @@ class OrderLineSerializer(serializers.ModelSerializer):
"taxes",
"tax_value",
)
+10
View File
@@ -20,6 +20,8 @@ from shop.api.v1.serializers import (
ProductBatchSerializer,
CreateProductSerializer,
CreateProductPriceSerializer,
TagSerializer,
ListProductSerializer,
)
from shop.models import (
Product,
@@ -30,6 +32,7 @@ from shop.models import (
OrderLine,
Provider,
ProductBatch,
Tag,
)
from shop.api.v1.permissions import ProductPermissions, ProductPricePermissions
@@ -46,6 +49,8 @@ class ProductViewSet(ModelViewSet):
def get_serializer_class(self):
if self.action == "create":
return CreateProductSerializer
elif self.action == "list":
return ListProductSerializer
return self.serializer_class
def perform_destroy(self, instance):
@@ -145,3 +150,8 @@ class OrderLineViewSet(ModelViewSet):
.select_related("order")
.filter(order__uuid=self.kwargs.get("order_id"))
)
class TagViewSet(ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
@@ -0,0 +1,41 @@
# Generated by Django 5.0.6 on 2024-05-14 16:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("files", "0001_initial"),
("shop", "0004_productbatch_provider"),
]
operations = [
migrations.CreateModel(
name="Tag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=16)),
],
),
migrations.AlterField(
model_name="product",
name="images",
field=models.ManyToManyField(
to="files.fileupload", verbose_name="Imágenes"
),
),
migrations.AddField(
model_name="product",
name="tags",
field=models.ManyToManyField(to="shop.tag", verbose_name="Etiquetas"),
),
]
+16
View File
@@ -6,6 +6,17 @@ from django.utils import timezone
from django.utils.text import gettext_lazy as _
class Tag(models.Model):
name = models.CharField(max_length=16)
def __str__(self):
return self.name
class Meta:
verbose_name = _("Etiqueta")
verbose_name_plural = _("Etiquetas")
class Product(models.Model):
name = models.CharField(
max_length=96,
@@ -30,6 +41,7 @@ class Product(models.Model):
images = models.ManyToManyField(
"files.FileUpload", blank=True, verbose_name=_("Imágenes")
)
tags = models.ManyToManyField("shop.Tag", blank=True, verbose_name=_("Etiquetas"))
def __str__(self):
return self.name
@@ -103,6 +115,10 @@ class ProductBatch(models.Model):
def __str__(self):
return f"{self.code} - {self.product.name} - {self.quantity}"
class Meta:
verbose_name = _("Remesa de producto")
verbose_name_plural = _("Remesas de productos")
class Tax(models.Model):
code = models.CharField(
+38 -1
View File
@@ -5,7 +5,7 @@ from rest_framework.test import APITestCase
from django.utils import timezone
from config.tests.mixins import TestUserAuthenticationMixin
from shop.models import Product, ProductPrice, Tax, ProductBatch
from shop.models import Product, ProductPrice, Tax, ProductBatch, Tag
class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
@@ -17,6 +17,7 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
code="IVA",
value=21,
)
self.tag = Tag.objects.create(name="Alimentos")
def create_products(self):
self.product = Product.objects.create(
@@ -58,6 +59,7 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20",
"stock": "100",
"tax": self.tax.pk,
"tags": [self.tag.pk],
},
)
assert response.status_code == status.HTTP_201_CREATED
@@ -78,6 +80,9 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20",
"stock": "100",
"tax": self.tax.pk,
"tags": [
self.tag.pk,
],
},
)
assert response.status_code == status.HTTP_201_CREATED
@@ -105,6 +110,9 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20",
"stock": "100",
"tax": self.tax.pk,
"tags": [
self.tag.pk,
],
},
)
assert response.status_code == status.HTTP_201_CREATED
@@ -127,3 +135,32 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
response = self.client.delete(f"/api/v1/shop/products/{self.product.pk}/")
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_update_product_tags(self):
self.login()
tag = Tag.objects.create(name="Alimentación")
response = self.client.post(
"/api/v1/shop/products/",
{
"name": "Papafritas",
"description": "Las mejores papafritas",
"is_digital_asset": False,
"url": "",
"price": "1.20",
"stock": "100",
"tax": self.tax.pk,
"tags": [
self.tag.pk,
],
},
)
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/products/{pk}/")
assert response.status_code == status.HTTP_200_OK
response = self.client.patch(f"/api/v1/shop/products/{pk}/", {"tags": [tag.pk]})
assert response.status_code == status.HTTP_200_OK
response = self.client.get(f"/api/v1/shop/products/{pk}/")
assert response.data.get("tags", [])[0] == tag.pk
+50
View File
@@ -0,0 +1,50 @@
from rest_framework import status
from rest_framework.test import APITestCase
from config.tests.mixins import TestUserAuthenticationMixin
from shop.models import Tag
class TestTagsAPI(APITestCase, TestUserAuthenticationMixin):
model_name = "tag"
def setUp(self):
self.create_user()
def create_tags(self):
Tag.objects.create(name="Deportes")
def test_fetch_taxes(self):
self.create_tags()
self.login()
response = self.client.get("/api/v1/shop/tags/")
assert response.status_code == status.HTTP_200_OK
assert len(response.data.get("results")) == 1
def test_create_retrieve_tax(self):
self.login()
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
def test_create_update_tax(self):
self.login()
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
response = self.client.patch(
f"/api/v1/shop/tags/{pk}/", {"name": "Juegos de mesa"}
)
assert response.status_code == status.HTTP_200_OK
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
assert response.data.get("name") == "Juegos de mesa"