# Migración v1.0 → v2.0

Script: [`upgrade_v1_to_v2.sh`](upgrade_v1_to_v2.sh)

Migración no incremental (DROP + recrear estructura desde [`admin/v2.0/database.sql`](../admin/v2.0/database.sql) y [`application/v2.0/database.sql`](../application/v2.0/database.sql) + reimport de datos). Pensada para promocionar dev → prod ejecutando el mismo script en cada entorno.

---

## Estrategia (9 fases)
II
1. **Verificaciones previas** — root, schemas v2.0 disponibles, `mysql`/`mysqldump` instalados.
2. **Credenciales root** — pedidas interactivamente; password vacío usa autenticación por socket.
3. **Descubrir BDs** — `INFORMATION_SCHEMA.SCHEMATA` filtrando `^limpioo$` y `^limpioo_userdb_%$`.
4. **Crear directorio de backups** — `database/backups/v1_to_v2_<YYYYMMDD_HHMMSS>/`. Comprueba >500 MB libres. Redirige stdout/stderr a `upgrade.log`.
5. **Backup por BD** — dos `mysqldump` por base:
   - `full_<DB>.sql` — `--single-transaction --routines --triggers --events --hex-blob` (estructura + datos, para rollback manual completo).
   - `data_<DB>.sql` — `--no-create-info --skip-triggers --complete-insert --single-transaction --hex-blob` (solo datos, para reimport).
   Verifica tamaño > 0; aborta si fallo.
6. **DROP DATABASE** — de cada BD descubierta.
7. **Recrear estructura desde v2.0** — `mysql < admin/v2.0/database.sql` (master, ya hace `CREATE DATABASE limpioo`); para cada tenant: `CREATE DATABASE limpioo_userdb_N` + `mysql limpioo_userdb_N < application/v2.0/database.sql`.
8. **Reimport datos + resync `AUTO_INCREMENT`** — `SET FOREIGN_KEY_CHECKS=0; SOURCE data_<DB>.sql; SET FOREIGN_KEY_CHECKS=1;`. Luego, por cada tabla con auto-increment: `ALTER TABLE t AUTO_INCREMENT = MAX(pk)+1`. Conteo post y comparación con `manifest.txt`.
9. **Pre-poblar caché UUID → tenant** — `INSERT INTO limpioo.user_uuid_databases (uuid, db_name) SELECT uuid, '<tenant>' FROM <tenant>.users ON DUPLICATE KEY UPDATE last_used = CURRENT_TIMESTAMP` por cada tenant. Evita que las primeras consultas caigan en el fallback de auto-descubrimiento. Omitible con `--skip-prewarm`.

---

## Uso

```bash
# Revisar plan sin ejecutar nada destructivo
sudo bash database/migrations/upgrade_v1_to_v2.sh --dry-run

# Ejecutar (pide confirmación con "SI")
sudo bash database/migrations/upgrade_v1_to_v2.sh

# Ejecutar sin confirmación
sudo bash database/migrations/upgrade_v1_to_v2.sh --yes

# Migrar solo una BD (útil para reintentos focalizados)
sudo bash database/migrations/upgrade_v1_to_v2.sh --only-db=limpioo_userdb_1
```

### Flags

| Flag | Descripción |
|------|-------------|
| `--dry-run` | Imprime todos los comandos pero NO ejecuta nada destructivo. |
| `--yes`, `-y` | Omite la confirmación interactiva (`SI`). |
| `--only-db=NAME` | Procesa solo esa BD (útil para tests). |
| `--skip-prewarm` | Omite la fase 9 (pre-poblar caché UUID→tenant). |
| `-h`, `--help` | Muestra la cabecera del script. |

### Requisitos

- Ejecutar como `root` (necesario para `DROP/CREATE DATABASE`).
- MariaDB/MySQL activo, herramientas `mysql` y `mysqldump` en el `PATH`.
- Schemas v2.0 presentes en [`admin/v2.0/database.sql`](../admin/v2.0/database.sql) y [`application/v2.0/database.sql`](../application/v2.0/database.sql).
- ≥ 500 MB libres en el filesystem que contiene `database/`.

---

## Salida generada

Cada ejecución produce el directorio `database/backups/v1_to_v2_<TIMESTAMP>/`:

```
v1_to_v2_<YYYYMMDD_HHMMSS>/
├── manifest.txt                  # conteos PRE y POST por tabla, lista de BDs
├── upgrade.log                   # log completo (stdout + stderr)
├── full_limpioo.sql              # backup completo (rollback)
├── data_limpioo.sql              # solo datos (reimport)
├── full_limpioo_userdb_1.sql
├── data_limpioo_userdb_1.sql
└── reimport_<DB>.errors.log      # solo si hubo errores en el reimport
```

Este directorio está listado en `.gitignore` y **no se borra automáticamente**. Bórralo manualmente tras validar:

```bash
rm -rf database/backups/v1_to_v2_<TIMESTAMP>
```

---

## Probar la migración con datos reales de producción

Para validar el procedimiento antes de tocar prod, replica los datos de prod en dev:

### 1. En PROD — generar el paquete

Script no destructivo, solo lectura:

```bash
# Genera dumps comprimidos + checksums en database/backups/prod_<host>_<TS>/
bash database/migrations/backup_production.sh --gzip
```

Salida:

```
database/backups/prod_<HOST>_<TIMESTAMP>/
├── manifest.txt              # versión MySQL, conteos por tabla
├── full_limpioo.sql.gz
├── full_limpioo_userdb_1.sql.gz
├── …
└── SHA256SUMS
```

### 2. Transferir a DEV

```bash
# Desde PROD (o desde DEV con pull)
rsync -avz --progress \
  database/backups/prod_<HOST>_<TS>/ \
  user@dev-host:/var/www/html/limpioo.console/database/backups/prod_<HOST>_<TS>/
```

### 3. En DEV — restaurar

**DESTRUCTIVO en local**: hace `DROP DATABASE` de las BDs `limpioo*` locales antes de cargar las de prod.

```bash
# Verifica SHA256SUMS, pide confirmación, restaura cada dump
sudo bash database/migrations/restore_from_prod.sh \
  --backup-dir=prod_<HOST>_<TS>
```

### 4. En DEV — probar la migración v1.0 → v2.0

```bash
sudo bash database/migrations/upgrade_v1_to_v2.sh --dry-run    # revisar
sudo bash database/migrations/upgrade_v1_to_v2.sh              # ejecutar
```

Validar smoke test del panel + conteos pre/post en `manifest.txt`.

### 5. En PROD — aplicar la migración real

Una vez validado en dev, ejecutar exactamente lo mismo en prod:

```bash
sudo bash database/migrations/upgrade_v1_to_v2.sh --dry-run
sudo bash database/migrations/upgrade_v1_to_v2.sh
```

> El `upgrade_v1_to_v2.sh` ya hace su propio backup `full_*.sql` antes de tocar nada (fase 5), por lo que tienes **dos** redes de seguridad: el paquete `prod_<HOST>_<TS>/` original y el nuevo `v1_to_v2_<TS>/full_*.sql` generado por la migración.

---

## Diferencias de schema v1.0 → v2.0

### Master (`limpioo`)

Tablas nuevas (vacías tras la migración):

- `fcm_tokens` — tokens FCM cross-tenant para notificaciones globales.
- `user_uuid_databases` — mapeo UUID de cliente final → BD tenant.

### Tenant (`limpioo_userdb_%`)

**`log`** — columnas añadidas:

| Columna | Tipo | Default |
|---------|------|---------|
| `LICENSE_PLATE` | `VARCHAR(20)` | `NULL` |
| `QUANTITY` | `INT(11) NOT NULL` | `1` |

**`subscriptions`** — columnas añadidas y reorganización de claves:

| Columna | Tipo | Default |
|---------|------|---------|
| `extra_member_price` | `INT(11) NOT NULL` | `0` |
| `max_members` | `INT(11) NOT NULL` | `0` |
| `payment_gateway` | `TEXT` | `NULL` |
| `lpr_opted_out` | `TINYINT(1) NOT NULL` | `0` |

- 🔄 Sustituye `UNIQUE KEY uuid (uuid(64), item_id, license_plate)` por `UNIQUE KEY uq_uuid_subscription_id (uuid, subscription_id)`.
- ➕ Añade `KEY idx_uuid_item_plate_thing (uuid, item_id, license_plate, THING(100))`.

**`users`** — añade `UNIQUE KEY idx_uuid (uuid)`.

**Tablas nuevas (vacías tras la migración):**

- `subscription_members` — miembros familiares de una suscripción (FK a `subscriptions.id`).
- `fcm_tokens` — tokens FCM por dispositivo (versión por tenant).

---

## Por qué es seguro reimportar datos v1.0 sobre estructura v2.0

- **Columnas nuevas con `DEFAULT`**: los `INSERT` v1.0 generados con `--complete-insert` listan solo las columnas que existían en v1.0, así que las nuevas (`LICENSE_PLATE`, `QUANTITY`, `extra_member_price`, `max_members`, `payment_gateway`, `lpr_opted_out`) toman su valor por defecto.
- **`qr_items.quantity_remaining`** es `GENERATED ALWAYS AS (...) STORED` en v2.0. `mysqldump --no-create-info --complete-insert` la omite y MariaDB la calcula automáticamente al insertar.
- **`subscriptions UNIQUE (uuid, subscription_id)`**: los datos v1.0 con `subscription_id NULL` no colisionan porque `NULL ≠ NULL` en `UNIQUE` de MariaDB.
- **`users UNIQUE idx_uuid`**: la v1.0 ya garantizaba unicidad por `email`; los UUIDs son MD5 derivados de `email` en signup, por lo que no se esperan duplicados. Si un dataset legacy tuviese duplicados, el `SOURCE` fallaría visiblemente y se registraría en `reimport_<DB>.errors.log`.
- **Tablas nuevas**: empiezan vacías y no tienen datos v1.0 que migrar.
- **`AUTO_INCREMENT`**: el dump preserva los IDs (PK explícitos en cada `INSERT`); el script ajusta el contador a `MAX(pk)+1` para evitar colisiones futuras.

---

## Verificación post-migración

```bash
# Comparar conteos PRE vs POST (deben coincidir para tablas preexistentes)
cat database/backups/v1_to_v2_<TS>/manifest.txt

# Verificar nuevas columnas en la tabla log
mysql -u root limpioo_userdb_1 -e "SHOW COLUMNS FROM log LIKE '%QUANT%'; SHOW COLUMNS FROM log LIKE '%LICENSE%';"

# Verificar nuevas columnas e índice en subscriptions
mysql -u root limpioo_userdb_1 -e "SHOW COLUMNS FROM subscriptions LIKE 'extra_member_price'; SHOW COLUMNS FROM subscriptions LIKE 'lpr_opted_out'; SHOW INDEX FROM subscriptions WHERE Key_name='uq_uuid_subscription_id';"

# Verificar tablas nuevas en tenant
mysql -u root limpioo_userdb_1 -e "SHOW TABLES LIKE 'subscription_members'; SHOW TABLES LIKE 'fcm_tokens';"

# Verificar tablas nuevas en master
mysql -u root limpioo -e "SHOW TABLES LIKE 'fcm_tokens'; SHOW TABLES LIKE 'user_uuid_databases';"

# Smoke test: cargar el panel y revisar errores SQL
: > /var/log/apache2/error.log
# … abrir https://<host>/console/ y navegar al dashboard …
tail -50 /var/log/apache2/error.log    # debe estar vacío de errores SQL
```

---

## Rollback

Cada BD tiene su backup completo (`full_<DB>.sql`) con estructura + datos en formato v1.0. Para revertir:

```bash
mysql -u root -e "DROP DATABASE IF EXISTS limpioo_userdb_1"
mysql -u root < database/backups/v1_to_v2_<TS>/full_limpioo_userdb_1.sql
```

(El dump contiene `CREATE DATABASE` solo si así se generó — si no, créala antes y selecciónala con `mysql -u root limpioo_userdb_1 < full_limpioo_userdb_1.sql`.)

---

## Despliegue a producción

El mismo script es portable. Procedimiento recomendado:

1. **Validar en dev** — ejecutar, smoke-test del panel, revisar `manifest.txt` y `/var/log/apache2/error.log`.
2. **Sincronizar código** a prod (incluyendo `database/admin/v2.0/`, `database/application/v2.0/` y `database/migrations/`).
3. **Ventana de mantenimiento** corta (la migración recrea las BDs).
4. **Ejecutar en prod**:
   ```bash
   sudo bash database/migrations/upgrade_v1_to_v2.sh --dry-run   # revisar
   sudo bash database/migrations/upgrade_v1_to_v2.sh             # ejecutar
   ```
5. **Validar** con las comprobaciones del bloque anterior.
6. **Borrar backups** tras varios días de uso normal: `rm -rf database/backups/v1_to_v2_<TS>`.

---

## Resultado de la primera ejecución (dev, 2026-04-23)

```
[3/8] Bases de datos detectadas:
   • limpioo
   • limpioo_userdb_1

[5/8] Backups generados:
   • limpioo            — 4 filas pre-migración   (8.0K full + 4.0K data)
   • limpioo_userdb_1   — 14 filas pre-migración  (28K full + 12K data)

[8/8] Reimport:
   ✅ limpioo:          pre=4  → post=4
   ✅ limpioo_userdb_1: pre=14 → post=14

✅ Migración v1.0 → v2.0 completada con éxito.
```

Sin errores en `reimport_*.errors.log`, sin pérdida de filas, schema v2.0 verificado.
