diff --git a/essenza/essenza/urls.py b/essenza/essenza/urls.py index 790c3d4..2390f45 100644 --- a/essenza/essenza/urls.py +++ b/essenza/essenza/urls.py @@ -4,6 +4,7 @@ from django.urls import include, path from info.views import info_view from product.views import DashboardView +from product.views import CatalogView, CatalogDetailView urlpatterns = [ path("info/", info_view, name="info-home"), @@ -11,6 +12,8 @@ path("admin/", admin.site.urls), path("product/", include("product.urls")), path("", DashboardView.as_view(), name="dashboard"), + path("catalogo/", CatalogView.as_view(), name="catalog"), + path("catalogo//", CatalogDetailView.as_view(), name="catalog_detail"), ] if settings.DEBUG: diff --git a/essenza/product/tests.py b/essenza/product/tests.py index b558ece..2f03894 100644 --- a/essenza/product/tests.py +++ b/essenza/product/tests.py @@ -1,9 +1,9 @@ from django.test import TestCase from django.urls import reverse from django.contrib.auth import get_user_model -from product.models import Product +from product.models import Product, Category from django.core.files.uploadedfile import SimpleUploadedFile - +from decimal import Decimal User = get_user_model() @@ -111,3 +111,104 @@ def test_admin_can_delete_product(self): self.assertFalse(Product.objects.filter(pk=self.product.pk).exists()) + +class CatalogViewTests(TestCase): + @classmethod + def setUpTestData(cls): + # Producto visible en el catálogo (is_active = True) + cls.active_product = Product.objects.create( + name="Producto Activo", + description="Descripción producto activo", + category=Category.MAQUILLAJE, + brand="Marca A", + price=Decimal("19.99"), + stock=10, + is_active=True, + ) + + # Producto NO visible en el catálogo (is_active = False) + cls.inactive_product = Product.objects.create( + name="Producto Inactivo", + description="Descripción producto inactivo", + category=Category.TRATAMIENTO, + brand="Marca B", + price=Decimal("9.99"), + stock=5, + is_active=False, + ) + + def test_catalog_url_status_code(self): + """La URL del catálogo responde con 200.""" + url = reverse("catalog") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + def test_catalog_uses_correct_template(self): + """El catálogo usa la plantilla correcta.""" + url = reverse("catalog") + response = self.client.get(url) + self.assertTemplateUsed(response, "product/catalog.html") + + def test_catalog_shows_only_active_products(self): + """ + En el catálogo solo aparecen productos activos + (is_active=True). + """ + url = reverse("catalog") + response = self.client.get(url) + + products = response.context["products"] + + self.assertIn(self.active_product, products) + self.assertNotIn(self.inactive_product, products) + + +class CatalogDetailViewTests(TestCase): + @classmethod + def setUpTestData(cls): + cls.active_product = Product.objects.create( + name="Detalle Activo", + description="Descripción detalle activo", + category=Category.CABELLO, + brand="Marca C", + price=Decimal("29.99"), + stock=20, + is_active=True, + ) + + cls.inactive_product = Product.objects.create( + name="Detalle Inactivo", + description="Descripción detalle inactivo", + category=Category.PERFUME, + brand="Marca D", + price=Decimal("39.99"), + stock=0, + is_active=False, + ) + + def test_catalog_detail_status_code_and_template(self): + """ + El detalle de un producto activo devuelve 200 y usa + la plantilla de detalle para usuario. + """ + url = reverse("catalog_detail", args=[self.active_product.pk]) + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "product/detail_user.html") + self.assertContains(response, self.active_product.name) + + def test_catalog_detail_returns_404_for_inactive_product(self): + """ + Si el producto está inactivo, el detalle del catálogo + debe devolver 404. + """ + url = reverse("catalog_detail", args=[self.inactive_product.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + + def test_catalog_detail_returns_404_for_nonexistent_product(self): + """Si el producto no existe, también 404.""" + url = reverse("catalog_detail", args=[9999]) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) diff --git a/essenza/product/views.py b/essenza/product/views.py index ca0a94f..0550e2a 100644 --- a/essenza/product/views.py +++ b/essenza/product/views.py @@ -6,6 +6,7 @@ from .forms import ProductForm from order.models import OrderProduct from .models import Product +from django.shortcuts import render, get_object_or_404 class DashboardView(View): template_name = "product/dashboard.html" @@ -141,3 +142,18 @@ def post(self, request, pk): product = get_object_or_404(Product, pk=pk) product.delete() return redirect('product_list') + +class CatalogView(View): + template_name = "product/catalog.html" + + def get(self, request): + products = Product.objects.filter(is_active=True) + return render(request, self.template_name, {"products": products}) + + +class CatalogDetailView(View): + template_name = "product/detail_user.html" + + def get(self, request, pk): + product = get_object_or_404(Product, pk=pk, is_active=True) + return render(request, self.template_name, {"product": product}) diff --git a/essenza/templates/product/catalog.html b/essenza/templates/product/catalog.html new file mode 100644 index 0000000..2bf34da --- /dev/null +++ b/essenza/templates/product/catalog.html @@ -0,0 +1,291 @@ +{% load static %} +{% load humanize %} + + + + + + Catálogo · Essenza + + + + + + + +
+ i +
ESSENZA
+ + + +
+ + +
+
+ + +
+

Catálogo Essenza

+

Explora nuestra selección de productos mejor valorados

+
+ + +
+ + + + + +
+ + +
+ {% for product in products %} +
+ {% if product.photo %} + {{ product.name }} + {% else %} + {{ product.name }} + {% endif %} + + +

{{ product.name }}

+

{{ product.price }} €

+ {{ product.get_category_display }} +
+ {% endfor %} +
+ + + + + diff --git a/essenza/templates/product/detail_user.html b/essenza/templates/product/detail_user.html new file mode 100644 index 0000000..49db3f2 --- /dev/null +++ b/essenza/templates/product/detail_user.html @@ -0,0 +1,326 @@ +{% load static %} + + + + + + {{ product.name }} - Essenza + + + + + + +
+
+ + +
+ +
+ {% if product.photo %} + {{ product.name }} + {% else %} + {{ product.name }} + {% endif %} + + +
+ +
+

{{ product.name }}

+
{{ product.brand }}
+
€ {{ product.price }}
+ + {% if product.get_categoria_display %} + {{ product.get_categoria_display }} + {% endif %} + +
+ Stock: {{ product.stock }} unidades +
+
+
+ + +
+ Descripción: +

{{ product.description }}

+
+ + +
+
+
Estado
+
+ {% if product.is_active %} + ✓ Activo + {% else %} + ✗ Inactivo + {% endif %} +
+
+
+
ID Producto
+
#{{ product.id }}
+
+
+ + +
+ + + + + ← Volver +
+ +
+
+ + + + + +