# Sidebar de drill-down para Google Sheets

Panel lateral (estilo LiveFlow) que muestra, **sin salir del spreadsheet**, las
transacciones que componen el valor de la celda seleccionada.

## Cómo funciona

1. El export/sync de Sunbright Core escribe en cada celda numérica un hyperlink
   a `/reports/{type}/drilldown?company=..&section=..&accountId=..&from=..&to=..`
   (requiere `APP_URL` configurado en el `.env` del servidor).
2. Este script (bound al spreadsheet) hace polling de la celda seleccionada y
   lee su hyperlink. Como el link vive en el **formato de celda** de un número,
   `getRichTextValue()` no lo expone → se lee con el **servicio avanzado Sheets
   API** (`Sheets.Spreadsheets.get`, ver `readCellLink`).
3. Llama a `GET /api/drilldown` con la **identidad Google del usuario**
   (`ScriptApp.getIdentityToken()`, requiere el scope `openid`).
4. El servidor valida el ID token (firma + expiración) y exige que el email esté
   en la tabla `users` con `is_allowed=1`.
5. El panel muestra: cuenta, rango, transacciones (con link a QBO por
   transacción), total y conteo — el mismo dato que la página de drilldown.

El hyperlink de la celda sigue siendo clickeable: abre la página completa de
drill-down en el navegador (fallback universal en sheets sin este script).

## Archivos

- `Code.gs` — menú, lectura del link de la celda (vía Sheets API), llamada a la
  API con el ID token, y `authorize()` (utilidad de instalación).
- `Sidebar.html` — panel: polling de selección, render de txns + total + links QBO.
- `appsscript.json` — manifiesto con los scopes y el servicio Sheets.

Ajustá `APP_URL` en `Code.gs` para que coincida con el `APP_URL` del servidor.

## Instalación (manual, ~5 min por spreadsheet) — la hace el desarrollador

> Los pasos 4 y 6 son los que **no son obvios** y sin ellos el panel queda vacío
> o devuelve 401. Están aquí porque nos costó descubrirlos.

1. Abrir el spreadsheet → **Extensiones → Apps Script**.
2. Reemplazar el contenido de `Código.gs` con `Code.gs` de este directorio.
3. **+ (Archivos) → HTML**, nombrarlo `Sidebar` (sin `.html`), pegar `Sidebar.html`.
4. **Añadir el servicio Sheets**: panel izquierdo → **Servicios → +** → *Google
   Sheets API* (v4, identificador `Sheets`) → **Añadir**. Sin esto,
   `readCellLink` falla y el panel nunca encuentra el link de las celdas numéricas.
5. **Aplicar el manifiesto**: ⚙️ **Configuración del proyecto** → activar
   *"Mostrar el archivo de manifiesto appsscript.json en el editor"* → volver al
   editor → abrir `appsscript.json` → reemplazar con el de este directorio.
   Debe incluir el scope **`openid`** — sin él, `getIdentityToken()` devuelve un
   token inválido ("Wrong number of segments") y el servidor responde 401.
6. **Forzar el consentimiento**: seleccionar la función **`authorize`** en el
   selector de funciones (barra superior) → **Ejecutar**. Google pide autorizar
   (ver "Aviso de app no verificada" abajo). Esto concede *todos* los scopes de
   una — `onOpen` por sí solo no los dispara, así que el menú no aparece hasta
   haber autorizado.
7. Recargar el spreadsheet → aparece el menú **Sunbright → Abrir drill-down**.

### Aviso de "app no verificada"

Al autorizar (paso 6), como el proyecto no está verificado por Google aparece
**"Google no verificó esta app"** → **Configuración avanzada → Ir a … (no seguro)**
→ **Permitir**. Es una sola vez por usuario. (Inherente a cualquier script/add-on
no publicado; LiveFlow evita el aviso porque pasó la verificación de Marketplace.)

Los permisos que pide: ver/editar hojas de cálculo (Sheets API), hacer la llamada
a la app (external_request), mostrar el panel (container.ui) y tu email (openid).

## Actualización del código

Repetir los pasos 2-3 con el código nuevo del repo. Si cambian los scopes del
manifiesto, repetir 5-6 (reautorizar con `authorize`). El código canónico vive
acá, versionado en git.

## Seguridad

- El endpoint `/api/drilldown` **nunca** devuelve datos sin auth: sesión web o
  ID token de Google con email en el allowlist (`users.is_allowed=1`). El token
  ya viene firmado por Google, así que no se exige el claim `email_verified`
  (`getIdentityToken()` no lo incluye).
- El scope es `spreadsheets` (no `currentonly`) porque el servicio avanzado
  Sheets lo requiere. Aceptable para uso interno; el `fetch` siempre va a
  `APP_URL` con el Bearer, así que un sheet no puede desviar la consulta.
- Pinning opcional: setear `SIDEBAR_TOKEN_AUDIENCE` en el `.env` del servidor con
  el client id que aparece como `aud` en los tokens del script. Vacío = se acepta
  cualquier token de Google, siempre filtrado por el allowlist de emails.
- Diagnóstico: si el panel da "No autorizado", el motivo exacto queda en el log
  de PHP del servidor (`grep "drilldown auth" error.log`).

## Evolución futura

- **V2 — instalación automática**: botón "Instalar sidebar" en la app usando la
  Apps Script API (`script.googleapis.com`, scope `script.projects`). Prereq:
  cada usuario activa una vez el toggle "Google Apps Script API" en
  <https://script.google.com/home/usersettings>.
- **V3 — add-on privado de Marketplace**: instalación única que aparece en
  todos los spreadsheets (paridad LiveFlow). Requiere Google Workspace con
  dominio propio (publicación privada) o verificación pública de Google.
