# Migración de Datos: Sistema Viejo (MySQL) → Sistema Nuevo (MSSQL)

**Última actualización:** 2026-02-23  
**Estado:** MIGRACIÓN DE DATOS COMPLETADA — pendientes post-migración

---

## 1. Contexto

Migración de tarjetas/lotes del sistema viejo (PHP + MySQL) al sistema nuevo (Node.js + Angular + MSSQL).  
Se usó el **linked server MYSQL_QA** en MSSQL para consultar MySQL directamente (sin exportar CSVs).

### Bases de datos

| Sistema | Motor | Linked Server | BD |
|---------|-------|---------------|----|
| Viejo (PHP) | MySQL | MYSQL_QA | lamundial_bk_19_02_2026 |
| Nuevo (Node) | MSSQL | — | Sis2000 (env: NAME_BD) |

### Resultado de la migración (2026-02-23)

| Tabla destino MSSQL | Registros migrados | Skip (ya existían) | Errores |
|----------------------|--------------------|---------------------|---------|
| `tatarlot`           | 2,089              | 5                   | 0       |
| `tatarjeta`          | 117,113            | 18                  | 0       |

**Totales finales:** tatarlot=2,094 | tatarjeta=117,131  
**IDENTITY reseed:** tatarlot→2100, tatarjeta→117162  
**Log completo:** `migration/migration_log_2026-02-23.txt`

---

## 2. Lo que YA está hecho

- [x] Tablas `tatarlot`, `tatarjeta`, `tafactura`, `tafactura_d`, `taproceso_log` creadas
- [x] Stored Procedures desplegados: `sp_RegistrarLoteTarjetas`, `sp_ConsultarDetalleLote`, `sp_ActivarTarjeta`, `sp_ActualizarEstadoTarjeta`, `sp_GestionarClienteAPI`, `sp_RegistrarLogProceso`
- [x] Vista `mavcanal` creada
- [x] UI rediseñada (cards.component, card-detail.component)
- [x] Backend API funcional (`/api/v1/cards/*`)
- [x] Migración de datos ejecutada vía linked server (migrate.cjs)
- [x] Variable `NAME_DB_PHP=MYSQL_QA` agregada al `.env`

---

## 3. PENDIENTES — Acciones desde el Frontend / Post-migración

### 3.1 CRITICO: Mapeo de Productos (`cproducto`)

**Problema:** Todos los lotes migrados tienen `cproducto = NULL` porque los nombres de insurances en MySQL no coinciden con los productos en MSSQL.

**Productos disponibles en MSSQL (maproductos):**

| cproducto | xdescripcion_l |
|-----------|----------------|
| 3         | Póliza de Vida |
| 5         | 4 en 1 |
| 11        | Seguro Funerario |
| 14        | Salud Individual |
| 15        | Salud Familiar |
| 56        | RCV |

**Insurances en MySQL (79 registros) — ejemplos principales:**

| id_insurance | name_insurance | cproducto sugerido |
|--------------|----------------|---------------------|
| 3  | Póliza de Seguro de Vida Temporal | 3 |
| 5  | Seguro combinado de personas 4en1 | 5 |
| 9  | Seguro combinado de personas 3en1 | 5 (?) |
| 10 | Seguro Masivo de RCV | 56 |
| 11 | Seguro de Servicios Funerarios para Grupo Familiar | 11 |
| 13 | Póliza de Seguro Temporal de Vida | 3 |
| 14 | Póliza de Seguro de Salud Individual | 14 |
| 15 | Póliza de Seguros de Salud para Grupo Familiar | 15 |
| 16 | Póliza de Seguro de RCV Vehículos | 56 |
| 22 | Póliza de Seguros RCV Moto | 56 |
| 58 | Seguro de Servicios Funerarios | 11 |
| ... | (79 en total — ver explore --explore para lista completa) | |

**Acción requerida:**

1. Crear el mapeo completo `id_insurance → cproducto` en `insurance_mapping.json`
2. Ejecutar UPDATE masivo en MSSQL:

```sql
-- Ejemplo: actualizar lotes que vinieron del insurance 3 (Vida Temporal) → cproducto 3
UPDATE L SET L.cproducto = '3'
FROM tatarlot L
INNER JOIN MYSQL_QA.lamundial_bk_19_02_2026..cards C ON C.id_cards_lot = L.ctarlot
WHERE C.id_insurance = 3;

-- Repetir para cada insurance → cproducto
```

**Alternativa:** Editar lotes manualmente desde el frontend (endpoint `PUT /api/v1/cards/edit-lot`).

### 3.2 Canales sin `ccanalalt` (lotes sin canal)

Algunos clientes MySQL no tenían `ccanalalt_sisip` registrado. Esos lotes aparecerán sin canal en el frontend.

**Clientes MySQL sin ccanalalt_sisip:**

| id_client_card | name_client | ccanalalt |
|----------------|-------------|-----------|
| 1  | Hospital Clínico del Este | NULL |
| 12 | Canal5 | NULL |
| 98 | CANAL 33-FUNERARIO | NULL |

**Acción:** Registrar estos canales en MSSQL con `sp_GestionarClienteAPI` o asignar manualmente un `ccanalalt` desde el frontend.

### 3.3 Verificar frontend Angular

1. **Listar lotes** — `GET /api/v1/cards/lots`
   - Verifica que carguen los 2,094 lotes
   - El query hace JOIN con `macanalalt` y `maproductos`, asi que los que tengan NULL mostrarán vacío en Canal/Producto
   - Si tarda mucho, considerar paginación (actualmente no tiene)

2. **Detalle de lote** — `GET /api/v1/cards/lot-detail/:ctarlot`
   - Usa `sp_ConsultarDetalleLote` que busca en `maclient_api`, `maclient`, `maclient_correo`
   - Si el canal no está registrado en `maclient_api`, no mostrará email/RIF/intermediario
   - Probar con un lote que SÍ tenga canal: ej. ctarlot con ccanalalt=8 (Farmatodo)

3. **Descargar CSV** — `GET /api/v1/cards/download-csv/:id_cards_lot`
   - Verifica que el CSV incluya las tarjetas del lote migrado

4. **Crear lote nuevo** — `POST /api/v1/cards/register-lot`
   - Verifica que nuevos lotes se creen con ctarlot > 2100 (después del reseed)
   - Requiere `ccanalalt` y `cproducto` obligatorios

### 3.4 (Opcional) Registrar canal Farmago

`ccanalalt=42` (Farmago) necesita registro con `sp_GestionarClienteAPI` para que el detalle de lote muestre email/intermediario.

### 3.5 Performance: Paginación

Con 2,094 lotes y 117K tarjetas, el query `queryCardsLots` podría ser lento.
**Acción sugerida:** agregar paginación server-side al endpoint `/api/v1/cards/lots`.

---

## 4. Mapeo de Tablas MySQL → MSSQL (referencia)

### `cards_lot` → `tatarlot`

| MySQL | MSSQL | Notas |
|-------|-------|-------|
| `id_cards_lot` | `ctarlot` | PK, IDENTITY_INSERT |
| `name_lot` | `xnombre_lote` | |
| `create_user` | `cusuario` | Numérico convertido a string |
| `create_date` | `fingreso` | |
| — | `ccanalalt` | Derivado de cards→api_clients_cards.ccanalalt_sisip |
| — | `cproducto` | **NULL** (mapeo pendiente) |

### `cards` → `tatarjeta`

| MySQL | MSSQL | Notas |
|-------|-------|-------|
| `id_card` | `ctarjeta` | PK, IDENTITY_INSERT |
| `nro_card` | `nro_tarjeta` | varchar(8) |
| `code_card` | `xcodigo_unico` | varchar(8) |
| `id_cards_lot` | `ctarlot` | FK a tatarlot |
| `id_status` | `iestado` | 1=Activo |
| `cpoliza_sisip` | `cpoliza` | |
| `cnpoliza_sisip` | `cnpoliza` | |
| `activate_date` | `factivacion` | |
| `create_date` | `fingreso` | |
| `create_user` | `cusuario` | |

---

## 5. Archivos del Proyecto

| Archivo | Descripción |
|---------|-------------|
| `lotes_tarjetas_bd/migration/migrate.cjs` | Script de migración (linked server) |
| `lotes_tarjetas_bd/migration/migration_log_2026-02-23.txt` | Log de la migración ejecutada |
| `lotes_tarjetas_bd/migration/insurance_mapping.json` | Mapeo insurance→producto (por completar) |
| `lotes_tarjetas_bd/stored_procedures/*.sql` | Todos los SPs y tablas |
| `lotes_tarjetas_bd/vistas/view_mavcanal.sql` | Vista de canales |
| `src/v1/routes/cardRoutes.js` | Rutas API |
| `src/controllers/cardController.js` | Controller |
| `src/service/cardService.js` | Servicio |
| `src/db/CardModel.js` | Modelo BD (queries y SPs) |

---

## 6. Endpoints API disponibles

| Método | Endpoint | Descripción |
|--------|----------|-------------|
| GET | `/api/v1/cards/lots` | Listar todos los lotes |
| GET | `/api/v1/cards/lot-detail/:ctarlot` | Detalle de un lote (usa SP) |
| GET | `/api/v1/cards/list/:id_cards_lot?` | Listar tarjetas (opcional: por lote) |
| GET | `/api/v1/cards/download-csv/:id_cards_lot` | Descargar CSV de tarjetas de un lote |
| POST | `/api/v1/cards/register-lot` | Crear nuevo lote con tarjetas |
| POST | `/api/v1/cards/edit-lot` | Editar lote (nombre, canal, producto) |
| POST | `/api/v1/cards/edit-card` | Editar tarjeta |
| POST | `/api/v1/cards/update-status` | Cambiar estado de tarjeta |
| POST | `/api/v1/cards/activate` | Activar tarjeta (asignar póliza) |
| DELETE | `/api/v1/cards/delete-lot/:ctarlot` | Eliminar lote y sus tarjetas |
| PUT | `/api/v1/cards/anular-card/:ctarjeta` | Anular tarjeta |

---

## 7. Notas Técnicas

- **Linked server:** `MYSQL_QA` (env: `NAME_DB_PHP=MYSQL_QA`)
- **Extensión `.cjs`** obligatoria para scripts CommonJS (package.json tiene `"type": "module"`)
- **Batches de 1000** para inserts masivos (evitar timeouts)
- El campo `cards.id_client_card` vincula tarjetas con canales; `cards_lot` NO tiene ese campo directamente
- `create_user` en MySQL es INT, `cusuario` en MSSQL es VARCHAR(50) — se convierte con `String()`
