tion 0 returned. finish reason: [stop] 2025-07-09 17:37:53.027 [info] [fetchCompletions] Request 2a545aae-3546-49a6-a2da-58da832ccf72 at finished with 200 status after 159.0848990008235ms 2025-07-09 17:37:53.029 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:38:16.041 [info] [fetchCompletions] Request 12f37ab7-9566-4dd4-8688-0186e57c5d42 at finished with 200 status after 222.4067439995706ms 2025-07-09 17:38:16.043 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:38:16.243 [info] [fetchCompletions] Request 482e01d4-d0ef-4da6-baca-6d842e8ed1dc at finished with 200 status after 190.65755400061607ms 2025-07-09 17:38:16.245 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:48:50.018 [info] [fetchCompletions] Request f293ce7e-7f20-4c60-90f0-25878bfbc37f at finished with 200 status after 387.17405699938536ms 2025-07-09 17:48:50.019 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:48:50.527 [info] [fetchCompletions] Request 1a8665b3-2894-4547-96b9-5141b656b936 at finished with 200 status after 311.3818620033562ms 2025-07-09 17:48:50.529 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:48:50.798 [info] [fetchCompletions] Request f9a38b28-9220-4913-8020-c664e5dab8a3 at finished with 200 status after 258.7578850015998ms 2025-07-09 17:48:50.799 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:48:51.650 [info] [fetchCompletions] Request ba429c27-1f30-4b42-b837-59d30c4ed743 at finished with 200 status after 1585.0553099997342ms 2025-07-09 17:48:52.925 [info] [streamChoices] solution 2 returned. finish reason: [stop] 2025-07-09 17:48:52.940 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:48:52.942 [info] [streamChoices] solution 1 returned. finish reason: [stop] 2025-07-09 17:52:07.638 [info] [fetchCompletions] Request f5397103-b02b-4dea-b471-ae8970f9cc99 at finished with 200 status after 336.90095200017095ms 2025-07-09 17:52:07.641 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 17:52:07.838 [info] [fetchCompletions] Request eec6a392-7682-48e9-bcbd-64167bec5498 at finished with 200 status after 186.54435700178146ms 2025-07-09 17:52:07.839 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 18:07:53.457 [info] [fetcher] Using Helix fetcher, Electron fetcher is not available. 2025-07-09 18:07:53.457 [info] [code-referencing] Public code references are enabled. 2025-07-09 18:07:53.523 [info] [fetcher] Using Helix fetcher, Electron fetcher is not available. 2025-07-09 18:07:54.171 [info] [fetchCompletions] Request 9ce0a939-a686-406a-a296-ba1dced8f9eb at finished with 200 status after 648.2668719999492ms 2025-07-09 18:07:54.173 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 18:07:54.695 [info] [fetchCompletions] Request bde324db-cc4a-4aa6-a6e9-0474c8fd794e at finished with 200 status after 510.06343200057745ms 2025-07-09 18:07:54.696 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-09 18:12:14.516 [info] [fetchCompletions]format('d-M-Y'); // e.g. "01-Jun-2025" $end = $mes->modify('last day of this month')->format('d-M-Y'); // "30-Jun-2025 // Valores por defecto $opts = array_merge([ 'criteria' => "(fechaFactura >= '{$start}' && fechaFactura <= '{$end}')", 'field_config' => 'quick_view', 'fields' => [], 'max_records' => 200, 'environment' => null, 'demo_user_name'=> null, ], $options); do { // Construir URL y query string $query = [ 'max_records' => $opts['max_records'], 'field_config' => $opts['field_config'], ]; if ($opts['criteria']) { $query['criteria'] = $opts['criteria']; } if ($opts['field_config'] === 'custom' && $opts['fields']) { $query['fields'] = implode(',', $opts['fields']); } $url = "https://{$baseUrl}/creator/v2.1/data/{$owner}/{$appLink}/report/{$reportLink}" . '?' . http_build_query($query); // Inicializar cURL $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, // No usar CURLOPT_HEADER = true, sino un HEADERFUNCTION para capturar solo headers CURLOPT_HTTPHEADER => array_filter([ "Authorization: Zoho-oauthtoken {$accessToken}", "accept: application/json", $opts['environment'] ? "environment: {$opts['environment']}" : null, $opts['demo_user_name']? "demo_user_name: {$opts['demo_user_name']}" : null, $cursor ? "record_cursor: {$cursor}" : null, ]), // Capturar encabezados CURLOPT_HEADERFUNCTION => function($curl, $headerLine) use (&$responseHeaders) { $len = strlen($headerLine); if (strpos($headerLine, ':') !== false) { list($name, $value) = explode(':', trim($headerLine), 2); $responseHeaders[strtolower($name)] = trim($value); } return $len; }, ]); $body = curl_exec($ch); if ($body === false) { throw new Exception('cURL error: ' . curl_error($ch)); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { $err = json_decode($body, true); $msg = $err['message'] ?? $body; throw new Exception("Error HTTP {$httpCode}: {$msg}"); } // Decodificar y acumular registros $data = json_decode($body, true); if (!isset($data['data']) || !is_array($data['data'])) { throw new Exception('Respuesta inesperada: falta la clave "data".'); } $allRecords = array_merge($allRecords, $data['data']); // Verificar paginación $nextCursor = $responseHeaders['record_cursor'] ?? null; $cursor = $nextCursor ?: null; // limpiar los headers previos $responseHeaders = []; } while ($cursor); return $allRecords; } try { // Antes de la primera llamada: $responseHeaders = []; $records = fetchZohoCreatorReport( '1000.d6d5a3d221f0a8246e68b2033041bffb.c42304b5f57adaacb1252795f7b36417', 'www.zohoapis.com', 'arochiylindner.aptus', 'cfdi', 'cfdi_I_query', [ 'field_config' => 'all', 'max_records' => 200, ] ); echo json_encode([ 'error' => false, 'total' => count($records), 'records' => $records ], JSON_PRETTY_PRINT); } catch (Exception $e) { http_response_code(500); echo json_encode([ 'error' => true, 'message' => $e->getMessage() ]); exit; }