Overview
El Portal de Proveedores tiene un único punto de entrada para que cada proveedor cargue sus parámetros ambientales: la página Mis Envíos. Se entra desde el sidebar y aterriza ahí por defecto al loguearse.
El formulario que se abre adapta sus campos al tipo de proveedor:
- Fibra (Textil San Cristóbal): 12 campos sobre cultivo, riego, fertilizantes, energía, transporte.
- Tela (Textil San Ramón): 13 campos sobre proceso (hilado, tejido, teñido, acabado), químicos REACH, gramaje y transporte.
- Lavandería (Industrial Tintotex): 11 campos sobre lavado por kg, temperatura, encogimiento, REACH y transporte ida/vuelta.
Cada envío queda registrado. El proveedor puede re-enviar (corregir o actualizar) cuantas veces sea necesario; el histórico se mantiene completo. Al iniciar un nuevo envío, los campos se pre-llenan con los valores del último envío para minimizar el trabajo.
Cómo lo ve el proveedor
El recorrido completo, capturado paso a paso por el test automatizado:
Flujo: proveedor de Tela (Textil San Ramón)
Flujo: Lavandería (Industrial Tintotex)
Flujo: Fibra (Textil San Cristóbal)
proveedor@sancristobal.pe (tipo=fibra / tier=TIER_3) ve el mismo card único PEF — el discriminador es el formulario.
Casos de borde
¿Qué pasa si el proveedor no tiene órdenes asignadas?
No bloquea el flujo. Mis Envíos PEF es org-level (independiente de OPs). Los proveedores que aún no recibieron órdenes pueden cargar sus parámetros ambientales igual.
¿Y si los campos del spec cambian para un tipo de proveedor?
El seeder seed_supplier_form_configs.py centraliza la definición.
Cambiar los FIBRA_FIELDS, TELA_FIELDS o
LAVANDERIA_FIELDS y re-correrlo incrementa la version
y deja la nueva config activa. Submissions previos no se ven afectados (siguen
siendo válidos contra la versión con la que fueron enviados).
¿Y los datos de envíos viejos cuando cambia el spec?
Quedan inmutables. La vista read-only los muestra con el formato existente al
momento del envío (schema_version en cada submission).
Resumen técnico
El cambio incluye una migration de schema, una refactorización del seeder de form_configs, un endpoint nuevo (con fallback al legacy), tres métodos nuevos de repo (active draft / histórico / latest), y dos páginas Astro adaptadas. El test E2E con Playwright cubre los dos flujos vivos (tela y lavandería) con screenshots por paso.
Decisión clave: el discriminador de campos pasa de supplier_tier
(numérico/genérico) a proveedor.tipo (semántico). El tier se
mantiene por compat — el lookup nuevo prefiere tipo y cae a tier si no encuentra.
Componentes tecnológicos
Columna supplier_tipo
Migration 058 agrega supplier_tipo nullable + index a supplier_form_configs.
- apps/backend/src/domain/models/supplier_form_config.py
- apps/backend/src/migrations/versions/o1p58d058_*.py
3 specs × 36 campos
Inserta o actualiza las configs fibra / tela / lavanderia con descriptions del spec.
- apps/backend/src/seeds/seed_supplier_form_configs.py
Endpoint /me/{form_key}
Resuelve proveedor.tipo del user y devuelve la config correcta. Cae a /tier/form_key si no encuentra.
- apps/backend/src/api/routers/supplier_portal/form_configs.py
Histórico de submissions
Métodos para draft activo, histórico, último submitted. POST idempotente. GET /history nuevo.
- apps/backend/src/infrastructure/repositories/supplier_env_submission_repo.py
- apps/backend/src/api/routers/supplier_portal/submissions.py
Card único + tabla histórico
WFD comentado. Card PEF muestra estado del draft. Tabla "Envíos anteriores" debajo con fechas y "Ver detalle".
- apps/app-astro-dashboard/src/pages/supplier-portal/mis-envios.astro
Render dinámico + read-only + pre-fill
Llama a /form-configs/me/pef, soporta ?id=... read-only, pre-fill desde el último submitted, redirige tras enviar.
- apps/app-astro-dashboard/src/pages/supplier-portal/formulario-pef.astro
API client
Métodos getMyFormConfig y listSubmissionHistory. FieldDef agrega description.
- apps/app-astro-dashboard/src/lib/supplier-portal.ts
3 filas en supplier_form_configs
Seedeadas vía script. Las viejas (supplier_tipo IS NULL) quedan como legacy ignoradas por el endpoint nuevo.
- tipo=fibra · tier=TIER_3 · 12 campos
- tipo=tela · tier=TIER_2 · 13 campos
- tipo=lavanderia · tier=TIER_2 · 11 campos
Test E2E + screenshots
Login, navegación, validación de campos por tipo, llenado, envío, verificación de histórico y read-only.
- project management/proveedores-revision/revisor/tests/supplier-portal.spec.ts
Flujo técnico
1 — El navegador entra a Mis Envíos
SSR de Astro renderiza la página y dispara el script de cliente.
El script llama a listSubmissions('pef') para el estado del draft
y listSubmissionHistory('pef', 50) para el histórico.
Ambas llamadas viajan con la cookie ftl_access_token al backend.
2 — Click "Iniciar" → /supplier-portal/formulario-pef
La página formulario-pef llama a GET /api/v1/supplier-portal/form-configs/me/pef.
El backend resuelve proveedor.tipo a partir del org_id del user logueado y
devuelve la config con los campos correspondientes (12, 13 o 11).
GET /api/v1/supplier-portal/form-configs/me/pef
Cookie: ftl_access_token=<jwt>
3 — El frontend busca draft activo y/o histórico para pre-fill
Si existe un draft con production_order_id IS NULL AND status='draft',
se usa. Si no, se consulta listSubmissionHistory(..., limit=1) y se
copia su data_json al form como pre-fill, mostrando un banner aclaratorio.
4 — Envío: POST /submissions + /submit
Si no hay draft, POST /submissions con production_order_id=null
crea uno (idempotente: si existe, devuelve el existente). Luego
PATCH /submissions/<id> guarda los datos. Finalmente
POST /submissions/<id>/submit valida campos requeridos y marca como
submitted. Tras 1.2s, redirect a Mis Envíos.
5 — Histórico via /submissions/history
El endpoint GET /api/v1/supplier-portal/submissions/history?form_key=pef&limit=50
devuelve sólo las submissions con status='submitted' AND production_order_id IS NULL,
ordenadas por submitted_at DESC.
6 — Read-only: /formulario-pef?id=<sub_id>
La página detecta el query param y carga el submission por id (cualquier estado). Renderiza los campos en formato read-only (label + value + unit + description) sin botones.
Spec de campos por tipo
Origen: Requerimientos_datos_proveedores_pef_dpp_v01. Sintetizado:
Fibra (12 campos · TIER_3)
| Campo | Unidad |
|---|---|
| Tipo de fibra de algodón | texto · GOTS/BCI/OCS |
| País y región de cultivo | texto |
| Rendimiento del cultivo | kg fibra / hectárea |
| Consumo de agua de riego | m³ / kg fibra |
| Uso de fertilizantes | kg / hectárea |
| Uso de pesticidas / herbicidas | kg / hectárea |
| Consumo energético | kWh / kg fibra |
| Fuente de energía | texto |
| Emisiones directas de campo | kgCO₂eq / kg fibra |
| Distancia transporte a hilandería | km |
| Modo de transporte | texto |
| Peso del lote despachado | kg |
Tela (13 campos · TIER_2)
| Campo | Unidad |
|---|---|
| Consumo eléctrico total por proceso | kWh / kg tela |
| Fuente de energía eléctrica | texto |
| Consumo de gas natural | m³ / kg tela |
| Consumo de agua por proceso | m³ / kg tela |
| Productos químicos utilizados | kg / kg tela |
| Declaración REACH | texto · listado SVHC |
| Tasa de pérdida / merma | % por sub-proceso |
| Composición de la tela producida | % por fibra |
| Peso de la tela por metro | g/m² |
| Tipo de tejido | texto |
| Distancia a Trento | km |
| Modo de transporte | texto |
| Peso del lote despachado | kg |
Lavandería (11 campos · TIER_2)
| Campo | Unidad |
|---|---|
| Consumo eléctrico por kg de tela lavada | kWh / kg |
| Consumo de gas / vapor por kg lavada | m³ / kg |
| Consumo de agua por kg lavada | m³ / kg |
| Temperatura de lavado | °C |
| Productos químicos utilizados | kg / kg prenda |
| Declaración REACH | texto · listado SVHC |
| Tasa de pérdida / encogimiento | % (PEFCR 1.2%) |
| Fuente de energía | texto |
| Distancia Trento → Lavandería → Trento | km (ida + vuelta) |
| Modo de transporte | texto |
| Peso del lote procesado | kg |
Configuración
Migración
cd apps/backend
set -a && source ../../.env && set +a
uv run alembic upgrade head
Seeding de form_configs
uv run python -m src.seeds.seed_supplier_form_configs
Idempotente. Devuelve cantidad de filas creadas o actualizadas. Re-ejecutarlo tras cambiar el spec para subir la versión y dejar activa la nueva config.
Usuarios de prueba
| Tipo | Tier | |
|---|---|---|
proveedor@sancristobal.pe | fibra | TIER_3 |
proveedor@sanramon.pe | tela | TIER_2 |
proveedor@tintotex.com.pe | lavanderia | TIER_2 |
Sus passwords se sincronizan con el de Jenny (Tr3nt0.2025!) vía
uv run python -m scripts.sync_supplier_test_users.
Test E2E (Playwright)
cd "project management/proveedores-revision/revisor"
npm install
npm run install:browsers
cp .env.example .env # completar DASHBOARD_PASSWORD
npm test # corre los 2 escenarios y graba screenshots
Salidas:
test-results/screenshots/<NN-name>.png— capturas por paso, numeradas.playwright-report/index.html— reporte detallado con steps y traces.
Para refrescar el reporte ejecutivo, copiar el directorio
screenshots/ a ../report/screenshots/.
Issues conocidos / Próximos pasos
Endpoint legacy /form-configs/{tier}/{form_key} sigue disponible
Mantengo el endpoint viejo por compat (otros consumidores podrían usarlo).
Una vez confirmado que nada lo necesita, conviene marcar deprecated en
su decorator y eventualmente retirarlo.
Filas legacy en supplier_form_configs (supplier_tipo IS NULL)
Hay 2 filas viejas (TIER_2/None/pef y TIER_2/None/wfd) que ya no usa el endpoint /me. Pueden borrarse en un próximo cleanup; por ahora quedan intocadas para evitar romper algún consumidor del legacy.
Card de WFD oculto, no eliminado
El bloque del card de Gestión de Residuos quedó comentado en
mis-envios.astro. Para reactivarlo cuando vuelva al alcance,
quitar los comentadores HTML y ajustar el grid a 2 columnas.
Spec de campos en código, no en BD
Los campos del formulario viven en el seeder Python, no en una UI admin. Cambios al spec requieren un commit + re-seed. Una "Admin: editar form-config" quedaría pendiente para una iteración futura si es necesario.