Files
aurgi/reports/0003-2026-06-17-dead-stock-dash851.md
T
egutierrez 14d3025182 chore: auto-commit (1 archivos)
- reports/0003-2026-06-17-dead-stock-dash851.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-23 17:49:48 +02:00

60 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
1. **Sentinela `1999-01-01`** = "sin venta registrada", no venta real → `NULLIF(ultimo_mov_venta,'1999-01-01')`. Afecta 18,3M €.
2. **`Almacen Central` no vende** (distribución a tiendas; 0% combos con venta, 15,7M €) → excluir de la deadness; su `ultimo_mov_venta` es siempre sentinela.
3. **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 (filtra `ultimo_mov_venta` crudo, sin normalizar sentinela ni excluir central); la card usa su variable propia `meses_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.