procesar_importacion.php Controlador de importación CSV 1 líneas
1
<?php
// ============================================================
// SIDE - ARC 1.0
// Archivo: procesar_importacion.php
// ============================================================

require_once 'conexion.php';
require_once 'helpers.php';
requerir_admin();

// --- Solo POST ---
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: /apps/side-arc/index.php');
    exit;
}

// --- Validar CSRF ---
if (
    empty($_POST['csrf_token']) ||
    empty($_SESSION['csrf_token']) ||
    !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])
) {
    header('Location: /apps/side-arc/index.php');
    exit;
}
unset($_SESSION['csrf_token']);

// --- Validar parent_id ---
if (!isset($_POST['parent_id']) || !is_numeric($_POST['parent_id'])) {
    header('Location: /apps/side-arc/cuadro_clasificacion.php');
    exit;
}

$parent_id = (int) $_POST['parent_id'];

// --- Cargar nivel del padre ---
$stmt = mysqli_prepare($conexion,
    "SELECT nivel_descripcion FROM descripcion_isadg WHERE id = ? LIMIT 1");
mysqli_stmt_bind_param($stmt, 'i', $parent_id);
mysqli_stmt_execute($stmt);
$res   = mysqli_stmt_get_result($stmt);
$padre = mysqli_fetch_assoc($res);
mysqli_free_result($res);
mysqli_stmt_close($stmt);

if (!$padre) {
    header('Location: /apps/side-arc/cuadro_clasificacion.php');
    exit;
}

$nivel_padre   = $padre['nivel_descripcion'];
$niveles_validos = niveles_permitidos_bajo($nivel_padre);

// ============================================================
// VALIDAR ARCHIVO CSV
// ============================================================
$redir_base = '/apps/side-arc/importar.php?parent_id=' . $parent_id;

if (
    empty($_FILES['archivo_csv']) ||
    $_FILES['archivo_csv']['error'] !== UPLOAD_ERR_OK
) {
    $_SESSION['importar_reporte'] = [
        'total' => 0, 'insertadas' => 0, 'rechazadas' => 0,
        'errores' => [['fila'=>0,'titulo'=>'','motivo'=>'No se recibió el archivo CSV.']]
    ];
    header('Location: ' . $redir_base);
    exit;
}

$archivo  = $_FILES['archivo_csv'];
$tmp_path = $archivo['tmp_name'];
$tamanio  = $archivo['size'];
$ext      = strtolower(pathinfo($archivo['name'], PATHINFO_EXTENSION));

if ($ext !== 'csv' || $tamanio > 5 * 1024 * 1024) {
    $_SESSION['importar_reporte'] = [
        'total' => 0, 'insertadas' => 0, 'rechazadas' => 0,
        'errores' => [['fila'=>0,'titulo'=>'','motivo'=>
            'Archivo inválido. Solo CSV de hasta 5 MB.']]
    ];
    header('Location: ' . $redir_base);
    exit;
}

// ============================================================
// LEER Y PROCESAR EL CSV
// ============================================================
$handle = fopen($tmp_path, 'r');

// Eliminar BOM UTF-8 si existe
$bom = fread($handle, 3);
if ($bom !== "\xEF\xBB\xBF") {
    rewind($handle);
}

// Leer cabecera
$cabecera = fgetcsv($handle, 0, ',', '"');
if (!$cabecera) {
    fclose($handle);
    $_SESSION['importar_reporte'] = [
        'total' => 0, 'insertadas' => 0, 'rechazadas' => 0,
        'errores' => [['fila'=>1,'titulo'=>'','motivo'=>
            'No se pudo leer la cabecera del CSV.']]
    ];
    header('Location: ' . $redir_base);
    exit;
}

$cabecera = array_map(fn($c) => strtolower(trim($c)), $cabecera);

// Verificar columnas mínimas requeridas
foreach (['codigo_referencia', 'titulo', 'nivel_descripcion'] as $col_req) {
    if (!in_array($col_req, $cabecera, true)) {
        fclose($handle);
        $_SESSION['importar_reporte'] = [
            'total' => 0, 'insertadas' => 0, 'rechazadas' => 0,
            'errores' => [['fila'=>1,'titulo'=>'','motivo'=>
                "Falta columna requerida: '$col_req' en la cabecera del CSV."]]
        ];
        header('Location: ' . $redir_base);
        exit;
    }
}

// ============================================================
// PREPARAR INSERT
// ============================================================
$sql_insert = "INSERT INTO descripcion_isadg (
    parent_id, codigo_referencia, titulo, fechas, nivel_descripcion,
    volumen_soporte, nombre_productor, historia_institucional,
    alcance_contenido, valoracion_eliminacion, organizacion,
    condiciones_acceso, condiciones_reproduccion, lengua_escritura,
    caracteristicas_fisicas, unidades_relacionadas, notas,
    fecha_ingreso_registro, fechas_descripciones, estado,
    nombre_descriptor, nota_archivero, fecha_creacion_revision,
    reglas_normas, pa_nombre, pa_geografico, pam_materia,
    fecha_inicial_filtro, fecha_final_filtro
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

$stmt_ins = mysqli_prepare($conexion, $sql_insert);

// ============================================================
// HELPER: obtener valor de columna del CSV
// ============================================================
function col(array $fila, array $cabecera, string $nombre): ?string {
    $idx = array_search($nombre, $cabecera, true);
    if ($idx === false) return null;
    $val = trim($fila[$idx] ?? '');
    return $val === '' ? null : $val;
}

// ============================================================
// PROCESAR FILAS
// ============================================================
$reporte = [
    'total'      => 0,
    'insertadas' => 0,
    'rechazadas' => 0,
    'errores'    => [],
];

$num_fila = 1;

while (($fila = fgetcsv($handle, 0, ',', '"')) !== false) {
    $num_fila++;
    $reporte['total']++;

    if (count(array_filter($fila, fn($v) => trim($v) !== '')) === 0) {
        $reporte['rechazadas']++;
        $reporte['errores'][] = ['fila' => $num_fila, 'titulo' => '', 'motivo' => 'Fila vacía, omitida.'];
        continue;
    }

    $titulo     = col($fila, $cabecera, 'titulo');
    $codigo_ref = col($fila, $cabecera, 'codigo_referencia');
    $nivel      = col($fila, $cabecera, 'nivel_descripcion');

    if (empty($titulo)) {
        $reporte['rechazadas']++;
        $reporte['errores'][] = ['fila' => $num_fila, 'titulo' => '', 'motivo' => 'Falta el campo "titulo".'];
        continue;
    }

    if (empty($codigo_ref)) {
        $reporte['rechazadas']++;
        $reporte['errores'][] = ['fila' => $num_fila, 'titulo' => $titulo, 'motivo' => 'Falta el campo "codigo_referencia".'];
        continue;
    }

    if (empty($nivel)) {
        $reporte['rechazadas']++;
        $reporte['errores'][] = ['fila' => $num_fila, 'titulo' => $titulo, 'motivo' => 'Falta el campo "nivel_descripcion".'];
        continue;
    }

    if (!in_array($nivel, $niveles_validos, true)) {
        $reporte['rechazadas']++;
        $reporte['errores'][] = ['fila' => $num_fila, 'titulo' => $titulo, 'motivo' => "Nivel \"$nivel\" no permitido para un hijo de \"$nivel_padre\"."];
        continue;
    }

    $fechas                  = col($fila, $cabecera, 'fechas');
    $volumen_soporte         = col($fila, $cabecera, 'volumen_soporte');
    $nombre_productor        = col($fila, $cabecera, 'nombre_productor');
    $historia_institucional  = col($fila, $cabecera, 'historia_institucional');
    $alcance_contenido       = col($fila, $cabecera, 'alcance_contenido');
    $valoracion_eliminacion  = col($fila, $cabecera, 'valoracion_eliminacion');
    $organizacion            = col($fila, $cabecera, 'organizacion');
    $condiciones_acceso      = col($fila, $cabecera, 'condiciones_acceso');
    $condiciones_reproduccion= col($fila, $cabecera, 'condiciones_reproduccion');
    $lengua_escritura        = col($fila, $cabecera, 'lengua_escritura');
    $caracteristicas_fisicas = col($fila, $cabecera, 'caracteristicas_fisicas');
    $unidades_relacionadas   = col($fila, $cabecera, 'unidades_relacionadas');
    $notas                   = col($fila, $cabecera, 'notas');
    $fechas_descripciones    = col($fila, $cabecera, 'fechas_descripciones');
    $estado                  = col($fila, $cabecera, 'estado');
    $nombre_descriptor       = col($fila, $cabecera, 'nombre_descriptor');
    $nota_archivero          = col($fila, $cabecera, 'nota_archivero');
    $fecha_creacion_revision = col($fila, $cabecera, 'fecha_creacion_revision');
    $reglas_normas           = col($fila, $cabecera, 'reglas_normas');
    $pa_nombre               = col($fila, $cabecera, 'pa_nombre');
    $pa_geografico           = col($fila, $cabecera, 'pa_geografico');
    $pam_materia             = col($fila, $cabecera, 'pam_materia');

    $fecha_ingreso = col($fila, $cabecera, 'fecha_ingreso_registro');
    if ($fecha_ingreso !== null &&
        !preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha_ingreso)) {
        $fecha_ingreso = null;
    }

    $anio_ini_raw         = col($fila, $cabecera, 'fecha_inicial_filtro');
    $anio_fin_raw         = col($fila, $cabecera, 'fecha_final_filtro');
    $fecha_inicial_filtro = ($anio_ini_raw !== null && is_numeric($anio_ini_raw)) ? (int) $anio_ini_raw : null;
    $fecha_final_filtro   = ($anio_fin_raw !== null && is_numeric($anio_fin_raw)) ? (int) $anio_fin_raw : null;

    mysqli_stmt_bind_param(
        $stmt_ins,
        'issssssssssssssssssssssssssii',
        $parent_id,
        $codigo_ref,
        $titulo,
        $fechas,
        $nivel,
        $volumen_soporte,
        $nombre_productor,
        $historia_institucional,
        $alcance_contenido,
        $valoracion_eliminacion,
        $organizacion,
        $condiciones_acceso,
        $condiciones_reproduccion,
        $lengua_escritura,
        $caracteristicas_fisicas,
        $unidades_relacionadas,
        $notas,
        $fecha_ingreso,
        $fechas_descripciones,
        $estado,
        $nombre_descriptor,
        $nota_archivero,
        $fecha_creacion_revision,
        $reglas_normas,
        $pa_nombre,
        $pa_geografico,
        $pam_materia,
        $fecha_inicial_filtro,
        $fecha_final_filtro
    );

    if (mysqli_stmt_execute($stmt_ins)) {
        $reporte['insertadas']++;
    } else {
        $reporte['rechazadas']++;
        $reporte['errores'][] = [
            'fila'   => $num_fila,
            'titulo' => $titulo,
            'motivo' => 'Error SQL: ' . mysqli_stmt_error($stmt_ins),
        ];
    }
}

fclose($handle);
mysqli_stmt_close($stmt_ins);
mysqli_close($conexion);

$_SESSION['importar_reporte'] = $reporte;
header('Location: ' . $redir_base);
exit;