Compare commits
2 Commits
d7132733fc
...
feat/seria
| Author | SHA1 | Date | |
|---|---|---|---|
| b088794716 | |||
| 177b4c01ca |
39
AGENTS.md
39
AGENTS.md
@@ -159,7 +159,44 @@ Nota: El volumen monta `tienda_ilusion/` en `/app/`, por lo que el path correcto
|
|||||||
## Tests
|
## Tests
|
||||||
- Framework: Django unittest
|
- Framework: Django unittest
|
||||||
- Directorio: don_confiao/tests/
|
- Directorio: don_confiao/tests/
|
||||||
- Ejecutar: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py test`
|
- Ejecutar: `docker compose -f docker-compose.dev.yml run --rm django python manage.py test`
|
||||||
|
|
||||||
|
## Workflow TDD (Semáforo Rojo/Verde)
|
||||||
|
|
||||||
|
Todo cambio o nueva funcionalidad debe seguir TDD. **Está prohibido asumir fallos de tests: todo fallo debe ser confirmado ejecutando los tests.**
|
||||||
|
|
||||||
|
### Regla de oro: confirmar antes de modificar
|
||||||
|
Antes de modificar tests (por cambios en comportamiento o por nuevas funcionalidades), **SIEMPRE ejecutar los tests primero** y confirmar que el fallo esperado realmente ocurre. Nunca asumir que un test falla: verificar el fallo con la ejecución real.
|
||||||
|
|
||||||
|
### Ciclo Rojo → Verde → Refactor
|
||||||
|
1. **ROJO (escribir el test y confirmar que falla)**:
|
||||||
|
- Escribir o modificar el test que describe el comportamiento esperado.
|
||||||
|
- Ejecutar los tests **antes de tocar el código de producción**.
|
||||||
|
- Confirmar que el test falla por el motivo esperado (assertion fallida por el comportamiento nuevo, no por error de setup o de importación).
|
||||||
|
- Copiar el output del fallo como evidencia del semáforo rojo.
|
||||||
|
|
||||||
|
2. **VERDE (implementar lo mínimo para que pase)**:
|
||||||
|
- Implementar el código de producción mínimo necesario para que el test pase.
|
||||||
|
- Volver a ejecutar los tests y confirmar que el semáforo queda en verde.
|
||||||
|
|
||||||
|
3. **REFACTOR (limpiar con los tests en verde)**:
|
||||||
|
- Refactorizar y limpiar el código sin cambiar comportamiento.
|
||||||
|
- Re-ejecutar los tests para confirmar que siguen en verde.
|
||||||
|
|
||||||
|
### Ejecutar tests
|
||||||
|
Siempre ejecutar dentro del contenedor de desarrollo:
|
||||||
|
```bash
|
||||||
|
# Test específico (durante el ciclo, preferible sobre el suite completo para ser rápido)
|
||||||
|
docker compose -f docker-compose.dev.yml run --rm django python manage.py test don_confiao.tests.<archivo>
|
||||||
|
|
||||||
|
# Suite completa (al finalizar, para confirmar que nada se rompió)
|
||||||
|
docker compose -f docker-compose.dev.yml run --rm django python manage.py test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Confirmar el fallo, no asumirlo
|
||||||
|
- Si un test falla o se espera que falle, **ejecutarlo y mostrar el resultado** antes de cambiar cualquier código o test.
|
||||||
|
- Verificar que el fallo corresponde a la razón esperada (leer el traceback), no a un problema de entorno, migraciones, importaciones o fixtures.
|
||||||
|
- No modificar tests para "hacerlos pasar" si el fallo no fue previamente confirmado.
|
||||||
|
|
||||||
## Comandos Útiles (dentro del contenedor)
|
## Comandos Útiles (dentro del contenedor)
|
||||||
- Migraciones: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations && docker-compose -f docker-compose.dev.yml run --rm django python manage.py migrate`
|
- Migraciones: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations && docker-compose -f docker-compose.dev.yml run --rm django python manage.py migrate`
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class StoreSettingsAdmin(admin.ModelAdmin):
|
|||||||
@admin.register(Customer)
|
@admin.register(Customer)
|
||||||
class CustomerAdmin(admin.ModelAdmin):
|
class CustomerAdmin(admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
"id",
|
|
||||||
"name",
|
"name",
|
||||||
"email",
|
"email",
|
||||||
"phone",
|
"phone",
|
||||||
@@ -73,7 +72,7 @@ class CatalogSaleLineAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Product)
|
@admin.register(Product)
|
||||||
class ProductAdmin(admin.ModelAdmin):
|
class ProductAdmin(admin.ModelAdmin):
|
||||||
list_display = ("id", "name", "price", "measuring_unit", "external_id")
|
list_display = ("name", "price", "measuring_unit", "external_id")
|
||||||
search_fields = ("id", "name",)
|
search_fields = ("id", "name",)
|
||||||
list_filter = ("name", "id")
|
list_filter = ("name", "id")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2026-08-10 03:51
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('don_confiao', '0050_catalogsale_code_sale_code'),
|
||||||
|
('don_confiao', '0051_storesettings_logo'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
]
|
||||||
@@ -36,6 +36,7 @@ class SaleSerializer(serializers.ModelSerializer):
|
|||||||
model = Sale
|
model = Sale
|
||||||
fields = [
|
fields = [
|
||||||
"id",
|
"id",
|
||||||
|
"code",
|
||||||
"customer",
|
"customer",
|
||||||
"date",
|
"date",
|
||||||
"saleline_set",
|
"saleline_set",
|
||||||
@@ -68,6 +69,7 @@ class CatalogSaleSerializer(serializers.ModelSerializer):
|
|||||||
model = CatalogSale
|
model = CatalogSale
|
||||||
fields = [
|
fields = [
|
||||||
"id",
|
"id",
|
||||||
|
"code",
|
||||||
"customer",
|
"customer",
|
||||||
"date",
|
"date",
|
||||||
"catalogsaleline_set",
|
"catalogsaleline_set",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from ..admin import CatalogSaleAdmin, SaleAdmin
|
|||||||
from ..models.customers import Customer
|
from ..models.customers import Customer
|
||||||
from ..models.products import Product
|
from ..models.products import Product
|
||||||
from ..models.sales import CatalogSale, Sale
|
from ..models.sales import CatalogSale, Sale
|
||||||
|
from ..serializers import CatalogSaleSerializer, SaleSerializer
|
||||||
from .Mixins import LoginMixin
|
from .Mixins import LoginMixin
|
||||||
|
|
||||||
|
|
||||||
@@ -191,3 +192,31 @@ class TestOrderSummaryPublic(APITestCase, LoginMixin):
|
|||||||
def test_admin_list_display_includes_code(self):
|
def test_admin_list_display_includes_code(self):
|
||||||
self.assertIn("code", SaleAdmin.list_display)
|
self.assertIn("code", SaleAdmin.list_display)
|
||||||
self.assertIn("code", CatalogSaleAdmin.list_display)
|
self.assertIn("code", CatalogSaleAdmin.list_display)
|
||||||
|
|
||||||
|
def test_sale_serializer_includes_code(self):
|
||||||
|
sale = self._create_sale()
|
||||||
|
data = SaleSerializer(sale).data
|
||||||
|
self.assertIn("code", data)
|
||||||
|
self.assertEqual(data["code"], sale.code)
|
||||||
|
|
||||||
|
def test_catalog_sale_serializer_includes_code(self):
|
||||||
|
catalog_sale = self._create_catalog_sale()
|
||||||
|
data = CatalogSaleSerializer(catalog_sale).data
|
||||||
|
self.assertIn("code", data)
|
||||||
|
self.assertEqual(data["code"], catalog_sale.code)
|
||||||
|
|
||||||
|
def test_sales_list_endpoint_includes_code(self):
|
||||||
|
self.login()
|
||||||
|
sale = self._create_sale()
|
||||||
|
response = self.client.get("/don_confiao/api/sales/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
item = next(i for i in response.json() if i["id"] == sale.id)
|
||||||
|
self.assertEqual(item["code"], sale.code)
|
||||||
|
|
||||||
|
def test_catalog_sales_list_endpoint_includes_code(self):
|
||||||
|
self.login()
|
||||||
|
catalog_sale = self._create_catalog_sale()
|
||||||
|
response = self.client.get("/don_confiao/api/catalog_sales/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
item = next(i for i in response.json() if i["id"] == catalog_sale.id)
|
||||||
|
self.assertEqual(item["code"], catalog_sale.code)
|
||||||
|
|||||||
Reference in New Issue
Block a user