VWVC Pipeline
Limpieza automatizada de bases de datos — VW Vehículos Comerciales

1. Sube los archivos de Salesforce

📂

Arrastra aquí los archivos o haz clic para seleccionarlos

Se aceptan hasta 3 archivos: PDC P. FISICAS, PDC P. MORALES y LCV CEM VENTAS (NADIN)

Selecciona la fecha de los datos que estás limpiando
Procesando...

2. Resultados del proceso

Historial reciente

Cargando…

Historial de procesos

Cargando historial...

Bitácora de limpiezas

Cargando bitácora...

Bienvenido al pipeline

Vas a procesar los 3 reportes de Salesforce (PDC Físicas, PDC Morales, LCV CEM Ventas NADIN) en 4 etapas: Normalización → Base de Limpieza → Archivos MX → Concentrado. En cada paso podrás revisar la tabla resultante antes de continuar — nada se encadena automáticamente.

Concentrado Histórico

Base de datos local de todos los registros procesados. Consulta, filtra, exporta o importa archivos externos. Cada importación permite rollback durante 30 días.

Historial de ejecuciones

Carga el historial para ver las últimas ejecuciones.

Manual de Usuario y Documentación Técnica

Guía completa del pipeline de limpieza de datos VWVC. Documenta la lógica del sistema, cómo operarlo día a día, los archivos que produce y el esquema de la base de datos histórica.

1. Flujo del pipeline — paso a paso

El sistema procesa tres reportes de Salesforce (PDC Físicas, PDC Morales y LCV CEM Ventas NADIN) y los transforma en archivos limpios listos para entrega a Volkswagen. El flujo se ejecuta en 4 etapas observables vía la pestaña Pipeline. Lo que sigue describe en orden lo que el código realmente hace.

Paso 1 — Carga y lectura de archivos

Qué hace: abre cada Excel crudo de Salesforce con tolerancia a fallos: tipos mixtos, AutoFilters problemáticos y errores de XML interno. Salta las filas de metadata iniciales (skiprows) hasta encontrar los encabezados reales.

Por qué: los archivos exportados de Salesforce traen 2-4 filas de metadata (fecha de exportación, usuario, totales) antes del header real. Si se leyera "directo" pandas tomaría esas filas como encabezados y todo lo demás reventaría.

Archivo
backend/pipeline.py
Función
_leer_excel_robusto() + procesar_excel() paso 1
Ejemplo: el reporte de Físicas trae skiprows=3 porque las primeras 3 filas son: título del reporte, filtros aplicados, y "Total registros: 142".
Paso 2 — Normalización de nombres de columna

Qué hace: remueve acentos, reemplaza espacios y caracteres especiales por guiones bajos, deja todo en un formato canónico. "Cliente de Venta: Nombre" se vuelve Cliente_de_Venta_Nombre.

Por qué: Salesforce exporta encabezados con espacios, dos puntos, signos y acentos. Estos son difíciles de manejar en código (escapes, codificación) y rompen joins con el directorio oficial. La normalización vuelve las columnas referenciables sin ambigüedad.

Archivo
backend/pipeline.py
Función
limpiar_nombre_columna()
Ejemplo: "Vehículo de Inventario: VIN""Vehiculo_de_Inventario_VIN"
Paso 3 — Eliminación de la fila de totales

Qué hace: detecta la fila donde la columna centinela contiene la palabra "Total" y corta el DataFrame justo antes. Si no hay total, se queda con todo.

Por qué: Salesforce concatena una fila resumen al final de cada export ("Total: 142"). Si no se eliminara, sería procesada como un registro real y produciría estados inválidos en la cascada de validación.

Archivo
backend/pipeline.py · procesar_excel() paso 3
Param
col_total — columna centinela (varía por reporte)
Ejemplo: en el reporte de Ventas la columna centinela es Vehiculo_de_Inventario_VIN. La fila final tiene VIN = "Total" y se descarta.
Paso 4 — Limpieza de valores (mapeos, teléfonos, fechas)

Qué hace: aplica mapeos globales de valores categóricos (sexo M/F → 1/2, permisos Sí/No → 1/0, etc.), limpia teléfonos eliminando notación científica y caracteres no numéricos, normaliza fechas a formato MX YYYYMMDD.

Por qué: Excel convierte teléfonos largos a notación científica (5.5512e+9) o a float (5551234567.0) si la celda no está en formato texto. La limpieza preserva los 10 dígitos como string y la celda final en el Excel queda con formato '@' (texto) para que VW pueda usarla tal cual.

Archivo
backend/pipeline.py · procesar_excel() pasos 6, 13b, 14
Constante
MAPEOS_GLOBALES (valores categóricos)
Ejemplo: Salesforce exporta el teléfono como 5551234567.0. El pipeline lo deja como "5551234567" (sin .0) y la celda en Excel queda con formato Texto.
Paso 5 — Identificación y normalización de modelo (incluyendo inferencia por VIN)

Qué hace: primero intenta usar el valor de la columna Modelo directamente. Si está vacío, busca el modelo en un catálogo VIN→modelo precalculado (data/concentrado_modelos.csv, ~10 000 entradas). Si encuentra coincidencia, completa el modelo automáticamente. Después normaliza el resultado a uno de los 5 modelos válidos.

Por qué: el campo "Modelo" en Salesforce viene vacío en ~5-8% de los registros, pero el VIN siempre está. El catálogo histórico permite recuperar la información sin perder al registro en la cascada de validación. Modelos no comerciales (Jetta, Polo, etc.) se filtran fuera.

Archivos
backend/pipeline.py (validar_y_completar_modelo(), normalizar_modelo())
Modelos válidos
AMAROK, CADDY, CRAFTER, EUROVAN, TRANSPORTER
Catálogo
backend/vin_model_dict.json + data/concentrado_modelos.csv
Ejemplo: VIN WV1ZZZ70ZNH012345 con modelo vacío. El catálogo contiene WV1ZZZ70 → CRAFTER, así que el modelo se completa automáticamente. La excepción "E" (typo histórico) se normaliza a CRAFTER también.
Paso 6 — Validación con cascada de penalizaciones

Qué hace: aplica 11 reglas de evaluación en orden estricto. La primera que matche "gana" — un registro solo recibe un motivo de penalización, no se acumulan.

  1. Red no oficial — dealer fuera del directorio VW vigente
  2. Fecha no válida (no penalizado) — fuera del rango de la ola
  3. Órdenes internas (no penalizado) — tipos 3,5,7,8,9,10,11,12 en postventa
  4. Chasis repetido (no penalizado) — VIN duplicado intra-reporte
  5. Otros modelos (no penalizado) — Jetta, Polo, etc.
  6. Sin permiso (penalizado) — cliente no autorizó contacto
  7. Sin modelo (penalizado) — modelo vacío + VIN no rescatable por catálogo
  8. Sin nombre (penalizado) — nombre vacío, basura o "Volkswagen"/"SAPI de CV"/etc.
  9. Sin email (penalizado) — ninguna de las 4 columnas alternativas de correo tiene un email válido
  10. Cargado — pasa todas las reglas, se entrega a la encuesta
  11. Cliente repetido (no penalizado) — mismo cliente ya cargado en este lote

Por qué cascada y no flags independientes: el SP original en SQL Server tenía esta lógica de exclusión mutua. Si un registro no tiene modelo y tampoco nombre, queremos reportarlo UNA sola razón (la primera detectada) — replicar el comportamiento original es clave para no romper auditoría histórica.

Archivo
scripts/validador.py
Función
validar_plantilla() — líneas 716-806
Origen
migración de stored procedures SQL Server (SP_ins_upd_*)
Ejemplo: registro con red oficial OK, fecha OK, modelo "AMAROK", VIN único, permiso=1, nombre="ANA TORRES", email="ana@gmail.com" → Cargado → Status = CARGADO, Observaciones = Cargado.
Paso 7 — Asignación de Status y Observaciones

Qué hace: traduce el _estatus interno a 2 columnas user-facing:

  • Status = CARGADO si _estatus="Cargado", sino NO CARGADO.
  • Observaciones = el texto exacto del _estatus (e.g. "Sin email (penalizado)").

Por qué: el operador ve dos columnas claras: una para filtrar (Status binario) y otra para auditar el motivo (texto descriptivo). VW solo recibe los registros con Status=CARGADO en el MX final, pero la Base de Limpieza conserva los NO CARGADO con su razón para análisis.

Archivo
scripts/validador.py líneas 808-812
Paso 8 — Generación de la Base de Limpieza

Qué hace: genera dos archivos Excel para el operador interno con TODOS los registros (CARGADO + NO CARGADO) más hojas auxiliares:

  • Base_limpieza_ventas_YYYYMMDD.xlsx: Hoja1 (datos), NADIN (DWH), ARCO, td
  • Base_limpieza_postventa_YYYYMMDD.xlsx: Hoja1 (físicas + morales unificadas), ARCO, TD

Por qué: es el entregable que revisa el operador antes de mandar a VW. La hoja ARCO traza los derechos de cliente (Aplicación de derechos ARCO según LFPDPPP), la hoja TD/td lista las redes territoriales, NADIN es referencia del DWH histórico.

Archivo
scripts/generar_bases_limpieza.py
Función
generar_bases_limpieza()
Paso 9 — Generación de los archivos MX (entregables a VW)

Qué hace: filtra SOLO los registros con Status=CARGADO y los proyecta a la estructura exacta que VW espera para la encuesta CEM:

  • MX_CV_SA_YYYY_YYYYMMDD.xlsx — Ventas (63 columnas)
  • MX_CV_AS_YYYY_YYYYMMDD.xlsx — Postventa (Físicas + Morales consolidadas, 64 columnas)

El nombre incluye el número de "ola" (001 si día ≤ 15, 002 si día ≥ 16) y un consecutivo que se calcula leyendo el último MX previo del directorio.

Por qué solo CARGADO: los NO CARGADO no van a encuesta — solo los registros elegibles que pasaron todas las reglas. El consecutivo y el wave son requeridos por VW para correlacionar con su lado.

Archivos
scripts/generar_mx_ventas.py, scripts/generar_mx_pv.py
Ejemplo: 257 registros tras normalización → 174 con Status=CARGADO → MX_CV_SA_2026_20260520.xlsx con 174 filas y 63 columnas.
Paso 10 — Carga al Concentrado Histórico (SQLite)

Qué hace: inserta los registros (TODOS, no solo CARGADO) en la BD local SQLite db/concentrado.db, creando una fila padre en ejecuciones y filas hijas en ventas y postventa. Registra un rollback disponible por 30 días.

Por qué: sin esta etapa cada ejecución se perdería. El concentrado permite consultar cualquier registro procesado en el pasado, exportar rangos de fechas, detectar VINs duplicados entre ejecuciones, y hacer rollback de una carga errónea. El rollback de 30 días balancea seguridad operativa vs. crecimiento de tabla.

Archivo
backend/db_concentrado.py
Funciones
insertar_ejecucion(), insertar_ventas(), insertar_postventa(), registrar_rollback()

2. Guía de operación diaria

1. Obtener los tres reportes de Salesforce

  1. Entra a Salesforce → reportes guardados.
  2. Ejecuta los tres reportes del día:
    • PDC P. FISICAS VW 2025 — postventa de personas físicas
    • PDC P. MORALES VW 2025 — postventa de personas morales
    • LCV CEM VENTAS NADIN FISICAS & Base 10 — ventas
  3. Exporta cada uno como Excel (.xlsx), sin modificar el nombre del archivo (el sistema reconoce por palabras clave: FISICAS, MORALES, NADIN/LCV).

2. Subir los archivos a la interfaz web

  1. Abre https://bivwvc.base10.mx/ e inicia sesión.
  2. Ve a la pestaña Pipeline.
  3. Arrastra los 3 archivos al área de carga (o haz clic para seleccionarlos).
  4. Verifica que aparezcan los tres con su tipo correctamente clasificado.
  5. Selecciona el modo:
    • Automático — corre las 4 etapas seguidas
    • Manual — avanza una etapa a la vez (recomendado para diagnóstico)
  6. Pulsa Iniciar pipeline.

3. Interpretar el progreso de las etapas

  • Las 4 etapas se muestran como cards con ícono de estado: ⏱ (pendiente), spinner (en proceso), ✓ (completado), ✗ (error).
  • Haz clic en cualquier etapa para ver sus logs en tiempo real en el panel inferior (polling cada 2 s mientras la etapa esté en proceso).
  • Cada etapa muestra: registros procesados, OK, errores, duración, y archivo de salida cuando aplica.

4. Descargar los archivos generados

  1. Al terminar el proceso, ve a la pestaña Historial de Procesos.
  2. Localiza tu proceso por ID o fecha.
  3. Descarga: Plantillas intermedias, Bases de Limpieza, y archivos MX (los que VW recibe).

5. Consultar el Concentrado Histórico

  1. Ve a la pestaña Concentrado Histórico.
  2. Por defecto muestra el mes actual. Cambia el filtro de fechas si necesitas otro rango.
  3. Alterna entre Ventas y Postventa.
  4. Búsqueda libre por nombre, VIN, correo (campo de texto arriba de la tabla).
  5. Filtros: Fecha inicio/fin, Status, Dealer ID, Modelo, Tipo persona (solo postventa).
  6. Personaliza columnas visibles con el botón Columnas (se recuerda en localStorage).

6. Exportar datos del concentrado

  1. Aplica los filtros que quieras (mes, status, dealer, etc.).
  2. Pulsa Exportar Excel o Exportar CSV.
  3. El archivo descargado tiene TODOS los registros que matchean los filtros (sin paginación).

7. Importar archivos externos

  1. Pulsa Importar datos en la pestaña Concentrado.
  2. Selecciona archivo .xlsx o .csv (máx 50 MB).
  3. Elige el tipo (Ventas o Postventa).
  4. Pulsa Validar: el sistema lee el archivo y muestra preview + errores.
  5. Revisa errores críticos (que bloquean la importación) vs advertencias (duplicados históricos).
  6. Si todo OK, pulsa Confirmar importación. Se crea una ejecución con rollback disponible por 30 días.

8. Ejecutar un rollback

  1. En la pestaña Concentrado, sección Historial de ejecuciones.
  2. Localiza la ejecución a revertir (debe tener rollback disponible, no haber pasado 30 días).
  3. Pulsa el botón Rollback de esa fila.
  4. Confirma. Se eliminan todos los registros de ventas y postventa de esa ejecución y se marca el rollback como ejecutado (no se puede deshacer).

3. Archivos de salida

Archivo Hojas Cols Contenido Para quién
Plantilla_ventas_YYYYMMDD.xlsx
Plantilla_p_fisicas_YYYYMMDD.xlsx
Plantilla_p_morales_YYYYMMDD.xlsx
1 variable Datos normalizados (post limpieza, pre validación). Incluye Status y Observaciones. Uso interno — paso intermedio.
Base_limpieza_ventas_YYYYMMDD.xlsx 4 (Hoja1, NADIN DWH, ARCO, td) 51+ TODOS los registros de ventas (CARGADO + NO CARGADO) + hojas auxiliares legales. Operador / auditoría. Revisión antes de envío.
Base_limpieza_postventa_YYYYMMDD.xlsx 3 (Hoja1, ARCO, TD) 58+ Postventa consolidada (físicas + morales). Todos los registros. Operador / auditoría.
MX_CV_SA_YYYY_YYYYMMDD.xlsx 1 63 SOLO registros CARGADO de ventas, estructura exacta esperada por VW. Entregable a VW.
MX_CV_AS_YYYY_YYYYMMDD.xlsx 1 64 SOLO registros CARGADO de postventa (físicas + morales). Entregable a VW.
Nota sobre ola y consecutivo: el nombre MX_CV_SA_2026_20260520 se descompone como CV (Comerciales) · SA (sales / ventas) · 2026 (año en curso) · 20260520 (fecha de generación). El wave 001/002 se decide por día: ≤15 = 001, ≥16 = 002.

4. Preguntas frecuentes / casos de error

¿Qué pasa si un VIN no tiene modelo en Salesforce?

El pipeline consulta el catálogo histórico data/concentrado_modelos.csv (VIN → modelo) y completa el campo automáticamente. Si el VIN tampoco está en el catálogo, el registro se marca como "Sin modelo (penalizado)" en la cascada de validación y su Status queda en NO CARGADO.

¿Por qué un registro queda como NO CARGADO?

Cualquiera de estas razones, en orden de prioridad:

  • Red no oficial — el dealer no aparece en el directorio VW del periodo.
  • Fecha no válida — fuera del rango de la ola.
  • Órdenes internas — tipos administrativos de servicio (solo postventa).
  • Chasis repetido — el mismo VIN aparece más de una vez en el archivo.
  • Otros modelos — no es AMAROK/CADDY/CRAFTER/EUROVAN/TRANSPORTER.
  • Sin permiso — el cliente no autorizó contacto (penaliza al dealer).
  • Sin modelo — modelo vacío y VIN no rescatable.
  • Sin nombre — nombre vacío, basura o lista negra (Volkswagen, SAPI, etc.).
  • Sin email — ninguna de las 4 columnas alternativas tiene un email válido.
  • Cliente repetido — mismo cliente ya cargado en este lote (solo si pasó todo lo anterior).

La columna Observaciones indica el motivo exacto.

¿Qué significa cada valor en la columna Observaciones?

Los valores literales que pueden aparecer son:

  • Cargado — pasó todas las reglas, va a encuesta.
  • Red no oficial
  • Fecha no válida (no penalizado)
  • Órdenes internas (no penalizado)
  • Chasis repetido (no penalizado)
  • Otros modelos (no penalizado)
  • Sin permiso (penalizado)
  • Sin modelo (penalizado)
  • Sin nombre (penalizado)
  • Sin email (penalizado)
  • Cliente repetido (no penalizado)

El sufijo (penalizado) indica que el dealer responsable recibe puntos en contra en el índice de calidad. Las marcas (no penalizado) son filtros operativos sin impacto en el KPI del dealer.

¿Qué hacer si el pipeline falla en una etapa?
  1. Abre la etapa con error en la pestaña Pipeline.
  2. Revisa el panel de logs — la última línea típicamente describe el error (e.g. [ERROR] Plantilla intermedia no encontrada).
  3. Causas comunes:
    • Archivo corrupto — el Excel exportado de Salesforce quedó truncado; re-exportarlo.
    • Columna esperada faltante — Salesforce cambió el nombre de una columna; revisar los mapeos en backend/pipeline.py (config_fisicas, etc.).
    • Catálogo no encontradodata/concentrado_modelos.csv o Directorio_Febrero_2026.xlsx ausentes.
  4. Si necesitas re-correr solo desde una etapa, sube los archivos de nuevo en modo manual y avanza etapa por etapa.
  5. Si el error persiste, contacta al equipo de plataforma con el ID del proceso (visible en la tarjeta del proceso).
¿Cómo recuperar un error de importación al concentrado?
  1. Si la importación se completó pero los datos están mal: ejecuta Rollback desde el historial de ejecuciones (disponible los primeros 30 días).
  2. El rollback elimina todos los registros de ventas y postventa de esa ejecución; la fila padre en ejecuciones se conserva pero queda marcada como eliminada en rollbacks.
  3. Si pasaron más de 30 días: el rollback no está disponible. Hay que limpiar manualmente con SQL en db/concentrado.db (requiere acceso al servidor).
  4. Si la importación falló a media inserción: el sistema usa transacciones, así que NO debería quedar estado parcial. Revisa los logs de la ejecución para confirmar.

5. Esquema de la base de datos histórica

Toda la persistencia vive en un solo archivo SQLite: /opt/vwvc-pipeline/db/concentrado.db. Cuatro tablas, relación 1:N de ejecuciones hacia ventas/postventa, y 1:1 hacia rollbacks. Sin ORM — sqlite3 estándar.

ejecuciones PK: id

registro maestro de cada corrida
Una fila por cada ejecución del pipeline (automático) o por cada importación manual. Contiene metadatos del proceso: usuario, tipo de carga, resultado, totales. Logs en JSON.
CampoTipoDescripciónEjemplo
🔑 idINTEGER PK AUTOINCREMENTIdentificador interno autoincremental142
proceso_idTEXT NOT NULL UNIQUEUUID4 del proceso (mismo que en estado_pipeline)f52e796c-087d-...
fecha_cargaTEXT NOT NULLISO8601 timestamp2026-05-20T14:23:11
usuarioTEXTEmail del operadorjl@base10.mx
tipo_cargaTEXT NOT NULL'automatico' (pipeline) o 'manual' (importación)automatico
resultadoTEXT NOT NULL'exitoso', 'error' o 'parcial'exitoso
logsTEXTJSON array de líneas de log["...", "..."]
estadoTEXT NOT NULL DEFAULT 'completado'Estado del registro (completado/eliminado)completado
total_ventasINTEGER DEFAULT 0Conteo de filas insertadas en ventas174
total_pvINTEGER DEFAULT 0Conteo de filas insertadas en postventa83

ventas PK: id · FK: ejecucion_id → ejecuciones.id

19 campos · 5 índices
Un registro por cada fila procesada del reporte de Ventas, incluyendo NO CARGADO. Es la fuente de verdad histórica para auditoría y reporting.
CampoTipoDescripciónEjemplo
🔑 idINTEGER PK AUTOINCREMENTIdentificador interno87432
FK ejecucion_idINTEGER NOT NULL → ejecuciones.idA qué ejecución pertenece el registro142
vinTEXT (idx)VIN del vehículo (17 chars)WV1ZZZ70ZNH012345
dealer_idTEXT (idx)Construido por validador desde No.Concesionaria + directorioMX1234
modeloTEXTAMAROK / CADDY / CRAFTER / EUROVAN / TRANSPORTERAMAROK
nombre_facturaTEXTRazón social en facturaJUAN PEREZ LOPEZ
fecha_facturacionTEXTFecha de factura (texto MX)20260315
fechaTEXT (idx)Fecha canónica del evento (FECHA_MX)20260315
nombre_clienteTEXTNombre del clienteJUAN
apellido_paternoTEXTApellido paternoPEREZ
apellido_maternoTEXTApellido maternoLOPEZ
nombre_completoTEXTConcatenación normalizadaJUAN PEREZ LOPEZ
correoTEXTEmail rescatado por validador (4 columnas, typo-corregido)juan@gmail.com
telefonoTEXTTeléfono principal limpio (sin notación científica)5551234567
permiso_transferenciaTEXT1/0 — permiso transferencia a marca1
permiso_marketingTEXT1/0 — permiso contacto marketing0
statusTEXT (idx)CARGADO / NO CARGADOCARGADO
observacionesTEXTMotivo de la cascada (e.g. "Sin email (penalizado)")Cargado
olaTEXT"1Q", "2Q", "1a", "2a"1Q
fecha_cargaTEXT NOT NULLCuándo se insertó este registro2026-05-20T14:23:11
Índices: idx_ventas_vin, idx_ventas_status, idx_ventas_fecha, idx_ventas_dealer, idx_ventas_ejecucion — optimizados para las búsquedas más frecuentes (VIN, filtros por status/fecha/dealer y joins por ejecución).

postventa PK: id · FK: ejecucion_id → ejecuciones.id

16 campos · 4 índices
Postventa consolidada (físicas + morales). El discriminador tipo_persona diferencia el origen.
CampoTipoDescripciónEjemplo
🔑 idINTEGER PK AUTOINCREMENTIdentificador interno52891
FK ejecucion_idINTEGER NOT NULL → ejecuciones.idEjecución a la que pertenece142
vinTEXT (idx)VIN del vehículoWV1ZZZ70ZNH012345
dealer_idTEXTConstruido por validadorMX1234
modeloTEXTModelo válidoCRAFTER
fechaTEXT (idx)FECHA_MX o Fecha_Salida20260315
nombreTEXTNombre del clienteANA
apellidoTEXTApellido principalTORRES
nombre_completoTEXTConcatenadoANA TORRES
correoTEXTEmail rescatadoana@empresa.mx
telefonoTEXTTeléfono limpio5559876543
legal_consent_onlineTEXT1/0 — permiso transferencia marca1
statusTEXT (idx)CARGADO / NO CARGADOCARGADO
observacionesTEXTMotivo cascadaCargado
olaTEXTOla CEM (si aplica)1a
tipo_personaTEXT'fisica' o 'moral' — discriminador de origenfisica
fecha_cargaTEXT NOT NULLTimestamp de inserción2026-05-20T14:23:11
Índices: idx_pv_vin, idx_pv_status, idx_pv_fecha, idx_pv_ejecucion.

rollbacks PK: id · FK: ejecucion_id → ejecuciones.id (UNIQUE)

ventana de reversión por ejecución
Una fila por ejecución, con la fecha máxima hasta la cual se puede revertir (30 días desde fecha_carga). Si eliminado_por es NOT NULL, el rollback ya se ejecutó y no puede repetirse.
CampoTipoDescripciónEjemplo
🔑 idINTEGER PK AUTOINCREMENTIdentificador142
FK ejecucion_idINTEGER NOT NULL UNIQUE → ejecuciones.idEjecución reversable (1:1)142
disponible_hastaTEXT NOT NULLFecha límite del rollback (ISO8601)2026-06-19T14:23:11
eliminado_porTEXT (NULL si disponible)Email del que ejecutó el rollbackjl@base10.mx
eliminado_enTEXTTimestamp del rollback ejecutado2026-06-01T09:15:00

6. Diagrama del modelo de datos (ER)

Relaciones entre las 4 tablas. 🔑 marca llave primaria, FK marca llave foránea.

ejecuciones maestro 🔑 id proceso_id (UNIQUE) fecha_carga usuario tipo_carga resultado logs (JSON) estado total_ventas total_pv ventas N por ejecución 🔑 id ↑ ejecucion_id vin (idx) dealer_id (idx) modelo nombre_factura fecha_facturacion fecha (idx) nombre_cliente apellido_paterno apellido_materno nombre_completo correo telefono permiso_transferencia permiso_marketing status (idx) observaciones ola fecha_carga postventa N por ejecución 🔑 id ↑ ejecucion_id vin (idx) dealer_id modelo fecha (idx) nombre apellido nombre_completo correo telefono legal_consent_online status (idx) observaciones ola tipo_persona fecha_carga rollbacks 1:1 🔑 id ↑ ejecucion_id (UNIQUE) disponible_hasta eliminado_por eliminado_en 1 ── N 1 ── N 1 ── 1 Leyenda 🔑 PK · ↑ FK · sólida = 1:N · punteada = 1:1
Cómo se lee:
  • Una ejecución produce N filas de ventas y N de postventa.
  • Una ejecución tiene como máximo 1 rollback (UNIQUE en ejecucion_id).
  • El borrado en cascada NO está activado por integridad histórica — un rollback solo elimina las filas de ventas/postventa, conserva la fila padre en ejecuciones y marca el rollback como ejecutado.