true]; if ($msg) $r['message'] = $msg; echo json_encode(array_merge($r, $data), JSON_UNESCAPED_UNICODE); exit; } function pubErr(string $msg, int $code = 400): void { http_response_code($code); echo json_encode(['success' => false, 'error' => $msg], JSON_UNESCAPED_UNICODE); exit; } try { $form = new Formulario(); // GET: cargar datos del formulario por token if ($_SERVER['REQUEST_METHOD'] === 'GET') { $token = $_GET['t'] ?? ''; if (!$token) pubErr('Token requerido'); $envio = $form->obtenerPorToken($token); if (!$envio) pubErr('Link inválido o expirado', 404); // Config de diseño: primero override del formulario, luego config global $db2 = Database::getInstance(); $cfgRows = $db2->fetchAll('SELECT clave, valor FROM lab_config ORDER BY clave'); $gCfg = []; foreach ($cfgRows as $r) { $gCfg[$r['clave']] = $r['valor']; } $docConfig = [ 'doc_color' => $envio['doc_color'] ?: ($gCfg['doc_color'] ?? '#1565c0'), 'doc_logo_base64' => $envio['doc_logo_base64'] ?: ($gCfg['doc_logo_base64'] ?? null), 'doc_encabezado' => $envio['doc_encabezado'] ?: ($gCfg['empresa_nombre'] ?? null), 'doc_subtitulo' => $envio['doc_subtitulo'] ?: ($gCfg['empresa_subtitulo'] ?? null), 'doc_pie_pagina' => $envio['doc_pie_pagina'] ?: ($gCfg['doc_pie_pagina'] ?? null), ]; // Estructura que espera form_cliente.php pubOk([ 'formulario' => [ 'nombre' => $envio['form_nombre'], 'descripcion' => $envio['form_descripcion'] ?? '', 'esquema_decoded'=> $envio['esquema_decoded'], 'permite_firma' => (bool)($envio['permite_firma'] ?? false), 'requiere_firma' => (bool)($envio['requiere_firma'] ?? false), ], 'envio' => [ 'id' => $envio['id'], 'estado' => $envio['estado'], 'firma_svg' => $envio['firma_svg'] ?? null, 'hash_verificacion'=> $envio['hash_verificacion'] ?? null, 'completado_en' => $envio['completado_en'] ?? null, ], 'prefilled' => $envio['prefilled_decoded'] ?? [], 'config' => $docConfig, ]); } // POST: guardar respuesta $raw = file_get_contents('php://input'); $datos = json_decode($raw, true) ?? []; $token = $datos['token'] ?? ''; if (!$token) pubErr('Token requerido'); $envio = $form->obtenerPorToken($token); if (!$envio) pubErr('Link inválido o expirado', 404); if (in_array($envio['estado'], ['completado', 'firmado'])) { // El formulario ya fue guardado (posible reintento tras error de red). // Devolvemos éxito con los datos existentes para que el cliente no quede bloqueado. $db = Database::getInstance(); $saved = $db->fetch('SELECT id, hash_verificacion, firma_svg FROM lab_form_envios WHERE token = ?', [$token]); pubOk([ 'firmado' => !empty($saved['firma_svg']), 'hash' => $saved['hash_verificacion'] ?? null, 'envio_id' => $saved['id'] ?? null, 'ya_enviado'=> true, ], 'Formulario enviado correctamente. ¡Gracias!'); } $datosCliente = $datos['datos_cliente'] ?? []; $firmaSvg = $datos['firma_svg'] ?? null; $firmaFoto = $datos['firma_foto'] ?? null; // Guardar foto de firma dentro de datos_cliente para no requerir nueva columna if ($firmaFoto) { $datosCliente['__firma_foto'] = $firmaFoto; } // Validar que si requiere firma, venga if ($envio['requiere_firma'] && !$firmaSvg) { pubErr('La firma es obligatoria para este formulario'); } $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? ''; $ua = $_SERVER['HTTP_USER_AGENT'] ?? ''; $ok = $form->guardarRespuesta($token, $datosCliente, $firmaSvg, $ip, $ua); if (!$ok) pubErr('No se pudo guardar la respuesta', 500); // Recuperar hash e id para la respuesta $db = Database::getInstance(); $saved = $db->fetch('SELECT id, hash_verificacion, turnero_consentimiento_id FROM lab_form_envios WHERE token = ?', [$token]); // Si este envío está vinculado a un consentimiento del turnero, marcarlo como firmado if (!empty($saved['turnero_consentimiento_id'])) { $pdo2 = $db->getConnection(); $tc = $pdo2->prepare( "SELECT tc.id, t.sesion_id FROM turnero_consentimientos tc JOIN turnero_turnos t ON t.id = tc.turno_id WHERE tc.id = ? AND tc.estado != 'firmado'" ); $tc->execute([(int)$saved['turnero_consentimiento_id']]); $tcRow = $tc->fetch(PDO::FETCH_ASSOC); if ($tcRow) { $datosRespuestasJson = !empty($datosCliente) ? json_encode($datosCliente, JSON_UNESCAPED_UNICODE) : null; $pdo2->prepare( "UPDATE turnero_consentimientos SET estado='firmado', firmado_at=NOW(), firma_svg=?, ip_firma=?, ua_firma=?, datos_respuestas=? WHERE id=?" )->execute([$firmaSvg, $ip, substr($ua, 0, 500), $datosRespuestasJson, (int)$tcRow['id']]); // Notificar via SSE para actualizar lugar.php en tiempo real if ($tcRow['sesion_id']) { require_once __DIR__ . '/../../modules/turnero/api/_helpers.php'; notificarSSE((int)$tcRow['sesion_id']); } } } pubOk([ 'firmado' => (bool)$firmaSvg, 'hash' => $saved['hash_verificacion'] ?? null, 'envio_id' => $saved['id'] ?? null, ], 'Formulario enviado correctamente. ¡Gracias!'); } catch (Exception $e) { pubErr($e->getMessage(), 500); }