- reports/0003-2026-06-17-dead-stock-dash851.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.9 KiB
Report 0003 — Dead stock (productos en stock sin venta) — dashboard 851
- Fecha: 17/06/2026
- Autor: Claude (sesión egutierrez)
- Ámbito: Aurgi · Metabase reports.autingo.es · dashboard 851 "Seguimiento Stock y Compras" · BigQuery db 6
- Estado: parcial (card standalone creada y validada; falta engancharla al dashboard a confirmación del usuario)
Resumen
Se identifican los productos con stock disponible cuya última venta es anterior a un corte configurable (default 12 meses). Resultado depurado: ≈ 1,46M € de dead stock en ~10.900 productos (corte 12 meses, snapshot 2026-06-17). Se creó la card standalone 11801 "Productos en stock sin venta (dead stock)" (https://reports.autingo.es/question/11801) con variable {{meses_sin_venta}}. La métrica naïve daba ~20M € falsos; tras corregir tres errores de datos (sentinela de fecha, almacén de distribución, grano producto×centro) baja a 1,46M € reales.
Cambios
| Artefacto | Acción | Detalle |
|---|---|---|
| Card Metabase 11801 | Creada + actualizada | SQL nativo BigQuery sobre anjana_bi_amg.stock_actual, display tabla, variable meses_sin_venta (default 12), formato € en valor_stock. |
Memoria project_aurgi_dash851_dead_stock.md |
Creada | Tabla/campos correctos + gotchas de la métrica. |
temp/stock_*.py |
Exploración | Probes de tablas 4592 (stock_actual) y 2233 (Stock_diario_mas_precio). |
Modelo de la métrica
Fuente: anjana_bi_amg.stock_actual (Metabase table 4592) — es la única tabla con última venta estricta (ultimo_mov_venta, field 288864). Stock_diario_mas_precio (table 2233) solo tiene fecha_ultimo_movimiento genérico, no sirve.
stock_actual es un ledger (~48M filas) con columnas denormalizadas constantes por producto×centro: stock_hoy (stock actual), ultimo_mov_venta, ultimo_mov_compra, coste_actual; atributos en structs product.* y center.*. Se deduplica con GROUP BY item_no_, location_code + ANY_VALUE/MAX (sumar stock_hoy sobre el ledger crudo multiplica por nº de movimientos).
Definición de "dead stock" (a nivel producto): producto con stock>0 cuya última venta en cualquier centro es anterior al corte; los nunca-vendidos cuentan solo si su última compra también supera el corte (excluye novedades).
Tres correcciones imprescindibles (sin ellas: ~20M € falsos)
- Sentinela
1999-01-01= "sin venta registrada", no venta real →NULLIF(ultimo_mov_venta,'1999-01-01'). Afecta 18,3M €. Almacen Centralno vende (distribución a tiendas; 0% combos con venta, 15,7M €) → excluir de la deadness; suultimo_mov_ventaes siempre sentinela.- Grano producto, no producto×centro: un producto activo en tiendas con stock parado en un hub logístico daba falso positivo (balizas V16, neumáticos 205/55). La deadness se calcula con la última venta global del producto.
Verificación
Snapshot MAX(Fecha)=2026-06-17. Ejecución de la card (corte default 12 meses), agregado por estado:
estado prods líneas uds valor €
Vendido (parado) 6073 16884 42295 942.504
Nunca vendido (parado) 2208 3155 7802 357.367
Sin actividad 2624 3263 9010 163.219
---------
TOTAL DEAD STOCK 1.463.090
Validación cruzada del impacto de cada corrección (Productos, stock>0):
- Naïve (todo, sin corregir): ~20,4M € (dominado por Almacen Central + sentinela 1999).
- Solo excluyendo central + sentinela, grano producto×centro: 3,82M € (incluye stock parado de productos que sí venden en otro centro → útil para redistribución).
- Grano producto (definición final): 1,46M € (referencias que no venden en ningún centro → candidatas a liquidación).
Top de la card tras corrección: neumático Bridgestone DUELER (última venta 2022-09, 45 meses), máquinas/kits nunca vendidos. Desaparecieron los falsos positivos (baliza V16, neumáticos de rotación alta).
Gaps / pendientes
- Enganche al dashboard 851 (tab 317 "Ultimo movimiento de producto"): pendiente de confirmación del usuario. Plan: añadir scalars (valor total + nº productos dead) + la tabla 11801 vía
metabase_dashboard_append_row. El param existente "Fecha ultimo mov" NO se reutiliza tal cual (filtraultimo_mov_ventacrudo, sin normalizar sentinela ni excluir central); la card usa su variable propiameses_sin_venta. - Decisión de negocio abierta: nivel-producto (1,46M €, liquidación) vs producto×centro (3,82M €, redistribución). La card está en nivel-producto; cambiar a combo es un ajuste menor del SQL.
Sin actividad(0,16M €): productos sin venta NI compra registrada — posible falta de datos vs stock realmente congelado. No desglosado.- Cap de 2000 filas en la tabla (orden por valor desc); el conteo real es ~23k líneas. Para el listado completo, exportar la card.