---
name: metabase_smartscalar_kpi_sql
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: pure
signature: "def metabase_smartscalar_kpi_sql(*, act_expr: str, n1_expr: str, body_sql: str, date_expr: str = 'MIN(fecha)') -> str"
description: "Envuelve agregaciones actual+n-1 en el patron de 2 filas (periodo, valor) que el display smartscalar de Metabase v0.59 requiere para mostrar comparacion vs ano anterior sin pedir breakout temporal. Genera SQL nativo BigQuery con UNION ALL d_min/d_min-52w."
tags: [metabase, smartscalar, kpi, sql, bigquery, pure, builder, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: act_expr
    desc: "Expresion SQL de agregado para el periodo actual, ej. 'ROUND(SUM(v.venta_n), 2)' o 'SAFE_DIVIDE(SUM(a), NULLIF(SUM(b),0))'"
  - name: n1_expr
    desc: "Expresion SQL para el mismo agregado del ano anterior, ej. 'ROUND(SUM(v.venta_n1), 2)'"
  - name: body_sql
    desc: "Cuerpo SQL desde 'FROM' que define las tablas/CTEs y filtros con template-tags Metabase ([[AND {{tag}}]])"
  - name: date_expr
    desc: "Expresion para extraer la fecha minima del periodo; usada como periodo en la fila actual y ancla para DATE_SUB de la fila n-1. Default 'MIN(fecha)'"
output: "string con SQL nativo BigQuery que devuelve 2 filas con columnas periodo (DATE) y valor (numero); fila 1 = periodo n-1, fila 2 = periodo actual"
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/metabase/smartscalar.py"
---

## Por que existe

El display ``smartscalar`` de Metabase v0.59.4 con ``previousValue`` requiere
una serie temporal para mostrar comparacion. Si el query devuelve un solo
numero el frontend muestra el error "Agrupa solo por un campo de tiempo para
ver como ha cambiado con el tiempo".

El truco probado en el dashboard ``Informe Lean`` (cards 9340-9373) es
producir 2 filas sinteticas: una para el periodo actual (``d_min``, ``act``)
y otra para n-1 (``d_min - 52 weeks``, ``n1``). Smartscalar lo interpreta como
serie de 2 puntos y compara naturalmente.

Esta funcion automatiza ese patron: el caller solo provee el cuerpo SQL con
sus filtros y las dos expresiones de agregado.

## Ejemplo

```python
body = """
FROM `proj.ds.base_margenes_aa` `proj.ds.base_margenes_aa` v
LEFT JOIN `proj.ds.Objeto_productos` p ON p.nav_id = v.prod_nav_id
WHERE 1=1
  [[AND {{fecha}}]]
  [[AND {{categoria}}]]
  [[AND {{tipo}}]]
"""

sql = metabase_smartscalar_kpi_sql(
    act_expr="ROUND(SUM(v.venta_n), 2)",
    n1_expr="ROUND(SUM(v.venta_n1), 2)",
    body_sql=body,
    date_expr="MIN(v.fecha)",
)
# sql tiene UNION ALL con periodo y valor
```

## Notas

- Por convencion los template-tags estan envueltos en ``[[ ]]`` para que sean
  opcionales: si el dashboard no aplica el filtro la clausula desaparece.
- Para que los field-filters de Metabase resuelvan correctamente las tablas
  dentro de CTEs, alias cada tabla con la cadena ``schema.table`` exacta que
  Metabase usa al expandir el template-tag (ej. ``\`proj.ds.tabla\` \`ds.tabla\``).
- El SQL generado usa ``DATE_SUB(d_min, INTERVAL 52 WEEK)`` (BigQuery dialect).
  Para Postgres adaptar a ``d_min - INTERVAL '52 weeks'``.
