From 14d302518215486d05302723b83ceb703ff83640 Mon Sep 17 00:00:00 2001 From: egutierrez Date: Tue, 23 Jun 2026 17:49:48 +0200 Subject: [PATCH] chore: auto-commit (1 archivos) - reports/0003-2026-06-17-dead-stock-dash851.md Co-Authored-By: Claude Opus 4.7 (1M context) --- reports/0003-2026-06-17-dead-stock-dash851.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 reports/0003-2026-06-17-dead-stock-dash851.md diff --git a/reports/0003-2026-06-17-dead-stock-dash851.md b/reports/0003-2026-06-17-dead-stock-dash851.md new file mode 100644 index 0000000..313fe7a --- /dev/null +++ b/reports/0003-2026-06-17-dead-stock-dash851.md @@ -0,0 +1,59 @@ +# 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.