# Ubicador de sepulturas — contexto para Claude Code

App web para ubicar sepulturas en el Parque Jardín las Flores (Santa Ana, El Salvador). El usuario es Adiel (ingeniero industrial); los trabajadores la usan en teléfono y en campo. Todo en español.

## Cómo está armada

- `app/plantilla.html` es la fuente de la app. `scripts/armar_app.py` genera dos salidas; ninguna se edita a mano:
  - `vista_local/index.html`: modo incrustado, con el plano adentro. Solo para revisar en la computadora o en claude.ai. **Nunca se sube al hosting.**
  - `servidor/index.html`: modo servidor. Lee y guarda todo en `servidor/api.php` (PHP 8.2 + MariaDB, el mismo stack de Control de Planta). Es la versión de producción; ver `servidor/DESPLIEGUE.md`.
- El modo lo decide `window.UB.api`. En modo servidor, `apiDB()` imita la interfaz `doc().set()` / `onSnapshot()` de la base de datos de claude.ai, así que el editor de sectores, los vendedores y la calibración GPS usan el mismo código en los dos modos.
- Base de datos: tablas `ub_*` (`servidor/esquema.sql`). Un fallecido apunta a `ub_parcela.id`. El importador (`servidor/lib.php`, `ub_importar_version`) conserva ese id entre versiones del DWG reconociendo cada parcela por posición (0.8 m).
- Mapa: Leaflet 1.9.4 con `CRS.Simple`, en metros del plano. El origen es (X0=8840, Y1=6935) del DWG; y es negativa hacia el sur. Las líneas y los textos se dibujan en un canvas propio (`DwgLayer`).
- Datos (`plano/versiones/<id>/data.json`): `P` polilíneas en cm con deltas, `T` textos `[txt, x, y, alto mm, rot×10, alineación, ancho%]`, `S` sectores, `L` parcelas `[índice de texto, sector, x, y, contorno]`.
- Una parcela se identifica por su **posición** (centro, en decímetros), nunca por su número: hay números repetidos dentro de un mismo sector.

## Reglas que ya aprendimos

- Expandir siempre los bloques del DWG (`INSERT`, recursivo). Hay unos 60 bloques con miles de números de parcela adentro.
- La posición de un texto depende de su alineación (`halign`/`valign`): con alineación distinta de izquierda-base, usar `endPoint`.
- El contorno de una parcela se obtiene trazando rayos desde su número hasta las líneas más cercanas (`cells.py`), no con un tamaño fijo.
- El contorno de un sector es la unión de las parcelas que le pertenecen, no los linderos del dibujo (muchos están abiertos).
- Para saber qué DWG es más nuevo se usa la fecha interna `TDUPDATE`, no el nombre del archivo.
- El GPS no funciona dentro de claude.ai; solo en https.
- Rótulos del plano (`scripts/rotulos.py` → `rotulos.json`): cada ficha de monumento se apila, en el sentido del texto, como monumento / cliente(s) / código de tipo / cuenta SOF / vendedor. El vendedor es la línea justo debajo de la cuenta. Una línea con una cadena de líneas que baja hasta una cuenta es cliente, aunque coincida con el nombre de un vendedor ("Ana María / Flores de Ramírez / SOF-9970").
- Registro de inhumados de SOF: el PDF "Alfabético de inhumados" se lee con `pdftotext -raw` (en `-layout` las columnas se desfasan y parece que cada registro trae dos fechas). El Excel `INHUMADOS.XLS` (SpreadsheetML) corta la letra del sector ("008A" sale "008") y el PDF corta nombres largos: el PDF manda en sector/jardín/cliente y el Excel solo aporta nombre completo, lado e ID. Sectores y fracciones se comparan sin ceros a la izquierda ni guiones ("007B" = "7B" = "7-B").
- Georreferencia (`GEO`): similitud plano↔lat/lng (`toLocal`/`toGeo`). La foto satelital se dibuja en `SatLayer` (GridLayer en CRS.Simple que gira y escala mosaicos Web Mercator); el mapa sigue en metros del plano. El ajuste sobre satélite (pares de puntos, mínimos cuadrados) y la calibración GPS guardan la misma `config/geo`.
- Los vendedores no deben aparecer en búsquedas ni en el mapa por defecto: capa `v` apagada. La lista de vendedores se corrige en la app (Capas y vendedores) o en `plano/vendedores.json`; `armar_app.py --sin-vendedores` los borra del todo.

## Tareas frecuentes

- Esta computadora (Windows) no tiene Python: usar `node scripts/armar_app.mjs` (equivalente a `armar_app.py`, sin regenerar rótulos). `pdftotext` viene con Git para Windows.
- Inhumados: `node scripts/inhumados.mjs --pdf <alfabético.pdf> --xls INHUMADOS.XLS` → `plano/inhumados/` (csv, json, reporte.md). Al servidor: `php servidor/importar_fallecidos.php plano/inhumados/inhumados.json --borrar-ejemplos`. Ver los reales en la vista local: `node scripts/armar_app.mjs --inhumados`.
- DWG nuevo: `python3 scripts/actualizar_plano.py <archivo.dwg> --publicar` (procesa, compara y lo sube a la base del servidor) y seguir `ACTUALIZAR_PLANO.md`.
- Claves: `token_lectura` (personal), `token_edicion` (supervisores), `token_importar` (solo el script). Van en `servidor/config.php`; la del script, en `plano/publicar.json`. Ninguno de los dos archivos va a Git.
- Cambio de pantalla: editar `app/plantilla.html` → `python3 scripts/armar_app.py` → probar los dos modos (abrir `vista_local/index.html`, y `servidor/` con `php -S`).
- Probar el modo servidor en local: MariaDB, `servidor/config.php` apuntando a ella, `php servidor/importar.php --esquema plano/versiones/<id>`, luego `cd servidor && php -S 127.0.0.1:8099`.
- API en Hostwinds: llamadas en secuencia, no en paralelo (`Promise.all` da ERR_HTTP2_PROTOCOL_ERROR en ese hosting).
- Marcar o desmarcar vendedores para siempre: exportar `vendedores.json` desde la app, guardarlo en `plano/vendedores.json` y ejecutar `python3 scripts/armar_app.py`.
- Entregas: el usuario prefiere ZIP de parche con solo los archivos modificados.

## Seguridad (requisito de Adiel)

Nadie debe poder descargar la página con el mapa y usarla sin el servidor: todo se pide a la API, con sesión. El plan detallado está en `PARA_CLAUDE_CODE.md`, tarea 1: login con Google, búsqueda en el servidor y plano por zonas.

## Próximo paso pendiente

Ver el plan en orden en `PARA_CLAUDE_CODE.md`. Resumen:

1. Seguridad: login con Google de Control de Planta, búsqueda de fallecidos en el servidor y plano por zonas.
2. Cargar los fallecidos reales desde el CSV del registro (sector + lote → `parcela_id`, con reporte de ambiguos).
3. Pantalla de alta y edición de fallecidos.
4. Despliegue en cPanel (`servidor/DESPLIEGUE.md`).
5. Fase 2: memorial digital (foto, muro, audios, QR).
