5-05-29 16:59:12.833 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 16:59:12.834 [info] [ghostText] Filtered out solution matching next line 2025-05-29 16:59:13.380 [info] [fetchCompletions] Request 5b7bc8ac-549b-4bed-be9b-1bef6948d8f1 at finished with 200 status after 188.6761259995401ms 2025-05-29 16:59:13.382 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 16:59:17.566 [info] [fetchCompletions] Request c45c9ebd-005e-4429-964a-943061029310 at finished with 200 status after 182.8298269994557ms 2025-05-29 16:59:17.567 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 16:59:25.457 [info] [fetchCompletions] Request 313abbbe-9774-42c4-8d8c-e868730e5451 at finished with 200 status after 222.4068650007248ms 2025-05-29 16:59:25.677 [info] [fetchCompletions] Request 9c75afb7-2303-4d27-99c9-c795c6a14451 at finished with 200 status after 215.9658490009606ms 2025-05-29 16:59:25.680 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 16:59:26.541 [info] [fetchCompletions] Request cee4d6bd-d9b6-414b-bf45-555b7f9de3cd at finished with 200 status after 176.85317799821496ms 2025-05-29 16:59:26.543 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:00:42.521 [info] [fetchCompletions] Request 3f278f9b-7073-4263-a8b1-d97be7ea333c at finished with 200 status after 259.5130800008774ms 2025-05-29 17:00:42.524 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:00:47.867 [info] [fetchCompletions] Request ea997b14-222b-4298-96a3-dd2e39a8c37b at finished with 200 status after 284.9464859999716ms 2025-05-29 17:00:47.869 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:01:05.300 [info] [fetchCompletions] Request 3c372638-f19a-4dac-9771-efc69334ec66 at finished with 200 status after 569.6437299996614ms 2025-05-29 17:01:05.303 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:01:07.707 [info] [fetchCompletions] Request 9d8776b7-8cc9-4330-891f-982ff2d72027 at finished with 200 status after 367.8407780006528ms 2025-05-29 17:01:07.709 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:01:17.470 [info] [fetchCompletions] Request dd15d96e-249d-4c5d-bbc2-fa76f09cc338 at finished with 200 status after 817.8302819989622ms 2025-05-29 17:01:17.475 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:01:21.970 [info] [fetchCompletions] Request 6e6f808f-410d-4150-aed8-f07fec956801 at finished with 200 status after 263.392868001014ms 2025-05-29 17:01:21.972 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:01:22.304 [info] [fetchCompletions] Request d4c6dff3-b9da-4922-aca6-81396f0bcf74 at finished with 200 status after 218.33050300180912ms 2025-05-29 17:01:22.306 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-29 17:38:44.584 [info] [fetcher] Using Helix fetcher, Electron fetcher is not available. 2025-05-29 17:38:44.584 [info] [code-referencing] Pub 'https://donatello.aptuslegal.app/oauth/token/f/'.$organi_id_Analytics, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c' ), )); $response = curl_exec($curl); curl_close($curl); // Decodificamos la respuesta JSON $data = json_decode($response, true); // Accedemos al valor de 'access_token de donatello' $access_token = $data['access_token']; return $access_token; } // === Lógica principal === // 1. Parámetros $organiZB = trim($_REQUEST['pOrganiZB'] ?? ''); $anioCat = trim($_REQUEST['anio'] ?? ''); $mesCata = trim($_REQUEST['mes'] ?? ''); $numOrden = trim($_REQUEST['txbNumeroOrden'] ?? ''); $tipoSolicitud = trim($_REQUEST['TipoSolicitud'] ?? 'AF'); // 2. Validación if (!$organiZB || !$anioCat || !$mesCata || !$numOrden) { echo json_encode([ 'code'=>'400', 'message'=>'Faltan parámetros: pOrganiZB, anio, mes o txbNumeroOrden' ]); exit; } // 3. Token Zoho Books $zbToken = GetToken($organiZB); if (!$zbToken) { echo json_encode(['code'=>'401','message'=>'No se obtuvo token Zoho Books']); exit; } // 4. Descargar catálogo de cuentas SIN filtro extra (verás todo) $urlCtas = "https://books.zoho.com/api/v3/chartofaccounts" . "?organization_id={$organiZB}"; $ch = curl_init($urlCtas); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Zoho-oauthtoken {$zbToken}"], CURLOPT_VERBOSE => true, // ← activa verbose ]); $resp = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $err = curl_error($ch); curl_close($ch); $data = json_decode($resp, true); $catalogo = $data['chartofaccounts'] ?? []; // Debug: muestra información cruda si algo falla if ($httpCode !== 200) { echo json_encode([ 'code' => '402', 'message' => "Zoho Books devolvió HTTP {$httpCode}", 'curl_err' => $err, 'body' => $resp ]); exit; } $data = json_decode($resp, true); if (json_last_error() !== JSON_ERROR_NONE) { echo json_encode([ 'code' => '403', 'message' => 'JSON inválido de Zoho Books: '.json_last_error_msg(), 'body' => $resp ]); exit; } $catalogo = $data['chartofaccounts'] ?? []; if (empty($catalogo)) { echo json_encode([ 'code' => '404', 'message' => 'chartofaccounts está vacío', 'body' => $resp ]); exit; } // RE-Mapear el catálogo usando account_id como clave: $cuentas = []; foreach ($catalogo as $c) { $id = (string)$c['account_id']; // Número de cuenta en UTF-8 $numCta = ensure_utf8($c['account_code']); // Descripción: usa mb_substr y luego normaliza $rawName = mb_substr($c['account_name'], 11); $desCta = ensure_utf8($rawName); $cuentas[$id] = [ 'NumCta' => $numCta, 'DesCta' => $desCta ]; } // 5. Token Zoho Analytics (Donatello) $analyticsOrg = '722699151'; $workspace = '2296733000000004013'; $view = '2296733000030848002'; $anToken = GetToken($analyticsOrg); if (!$anToken) { echo json_encode(['code'=>'403','message'=>'Error al obtener token Analytics']); exit; } // 6. Bulk export Analytics // 1. Calcular días del mes $dias = cal_days_in_month(CAL_GREGORIAN, intval($mesCata), intval($anioCat)); // 2. Construir el criterio manteniendo comillas dobles para el campo y comillas simples para las fechas $criteria = "\"Fecha de transaccion\" between '{$anioCat}-{$mesCata}-01' and '{$anioCat}-{$mesCata}-{$dias}'"; // 3. Configurar CONFIG con responseFormat primero (solo 'csv','json','xml','xls','pdf','html' o 'image') $configArray = [ 'responseFormat' => 'json', // debe ir primero y en minúsculas :contentReference[oaicite:0]{index=0} 'criteria' => $criteria ]; // 4. Serializar y codificar para URL $configJson = json_encode($configArray, JSON_UNESCAPED_SLASHES); $config = rawurlencode($configJson); // 5. Preparar URL y ejecutar cURL $bulkUrl = "https://analyticsapi.zoho.com/restapi/v2/bulk" . "/workspaces/{$workspace}/views/{$view}/data?CONFIG={$config}"; $ch = curl_init($bulkUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "ZANALYTICS-ORGID: {$analyticsOrg}", "Authorization: Zoho-oauthtoken {$anToken}", "Content-Type: application/json" ], ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); // 6. Parsear y validar que venga jobId $j1 = json_decode($response, true); $jobId = $j1['data']['jobId'] ?? ''; if ($httpCode !== 200 || !$jobId) { echo json_encode([ 'code' => '404', 'message' => 'No se obtuvo jobId Analytics', 'httpStatus' => $httpCode, 'curlError' => $curlError, 'response' => $j1 ]); exit; } // Polling do { sleep(5); $cj = curl_init("https://analyticsapi.zoho.com/restapi/v2/bulk" . "/workspaces/{$workspace}/exportjobs/{$jobId}"); curl_setopt_array($cj,[ CURLOPT_RETURNTRANSFER=>true, CURLOPT_HTTPHEADER =>[ "ZANALYTICS-ORGID: {$analyticsOrg}", "Authorization: Zoho-oauthtoken {$anToken}" ] ]); $js = json_decode(curl_exec($cj), true); curl_close($cj); $status = $js['data']['jobStatus'] ?? 'ERROR'; if ($status==='ERROR') { echo json_encode(['code'=>'405','message'=>'Analytics job error']); exit; } } while ($status!=='JOB COMPLETED'); $downloadUrl = $js['data']['downloadUrl'] ?? ''; if (!$downloadUrl) { echo json_encode(['code'=>'406','message'=>'No downloadUrl Analytics']); exit; } // Descargar datos $ch = curl_init($downloadUrl); curl_setopt_array($ch,[ CURLOPT_RETURNTRANSFER=>true, CURLOPT_HTTPHEADER =>[ "ZANALYTICS-ORGID: {$analyticsOrg}", "Authorization: Zoho-oauthtoken {$anToken}" ] ]); $rows = json_decode(curl_exec($ch), true)['data'] ?? []; curl_close($ch); // 7. Agrupar transacciones $txPorCta = []; foreach ($rows as $r) { $acctId = (string)($r['ID de la cuenta'] ?? ''); if (!$acctId) continue; $txPorCta[$acctId][] = [ 'Fecha' => str_replace('.', '-', $r['Fecha de transaccion']), 'NumUnIdenPol' => $r['ID'], 'Concepto' => $r['Concepto'], 'Debe' => (float)$r['Debe'], 'Haber' => (float)$r['Haber'] ]; } // 7.1 Crear archivo CSV $csvDir = __DIR__.'/archs_csv/'; $fnCsv = "{$rfc}{$anioCat}{$mesCata}AC.csv"; $csvPath = $csvDir . $fnCsv; if (!file_exists($csvPath)) unlink($csvPath); $csv = fopen($csvPath, 'w'); chmod($csvPath, 0777); // Escribir encabezado fputcsv($csv, ['NumCta', 'DesCta', 'Fecha', 'NumUnIdenPol', 'Concepto', 'Debe', 'Haber']); $totalTransacciones = 0; // 8. Construir el XML $xml = new DOMDocument('1.0','UTF-8'); $xml->formatOutput = true; $root = $xml->createElementNS( 'http://www.sat.gob.mx/esquemas/ContabilidadE/1_3/AuxiliarCtas', 'AuxiliarCtas:AuxiliarCtas' ); $xml->appendChild($root); // Obtener RFC $conn = mysqli_connect("127.0.0.1","aptuslegal","#Aptus2021#","vendorbills"); $row = mysqli_fetch_assoc($conn->query( "SELECT rfc FROM conndetails WHERE organization_id=".intval($organiZB) )); mysqli_close($conn); $rfc = $row['rfc'] ?? ''; // Encabezado $root->setAttribute('Version','1.3'); $root->setAttribute('RFC',$rfc); $root->setAttribute('Mes',str_pad($mesCata,2,'0',STR_PAD_LEFT)); $root->setAttribute('Anio',$anioCat); $root->setAttribute('TipoSolicitud',$tipoSolicitud); $root->setAttribute('NumOrden',$numOrden); // Cuentas + Detalles foreach ($cuentas as $acctId => $info) { if (empty($txPorCta[$acctId])) continue; $nC = $xml->createElement('AuxiliarCtas:Cuenta'); $root->appendChild($nC); $nC->setAttribute('NumCta', $info['NumCta']); $nC->setAttribute('DesCta', $info['DesCta']); $nC->setAttribute('SaldoIni','0.00'); $nC->setAttribute('SaldoFin','0.00'); foreach ($txPorCta[$acctId] as $t) { $nD = $xml->createElement('AuxiliarCtas:DetalleAux'); $nC->appendChild($nD); $nD->setAttribute('Fecha', $t['Fecha']); $nD->setAttribute('NumUnIdenPol',$t['NumUnIdenPol']); $nD->setAttribute('Concepto', $t['Concepto']); $nD->setAttribute('Debe', number_format($t['Debe'],2,'.','')); $nD->setAttribute('Haber', number_format($t['Haber'],2,'.','')); fputcsv($csv, [ $info['NumCta'], $info['DesCta'], $t['Fecha'], $t['NumUnIdenPol'], $t['Concepto'], number_format($t['Debe'],2,'.',''), number_format($t['Haber'],2,'.','') ]); $totalTransacciones++; } } fclose($csv); // 9. Guardar XML y generar ZIP $xmlDir = __DIR__.'/archs_xml/'; $zipDir = __DIR__.'/archs_zip/'; $fnXml = "{$rfc}{$anioCat}{$mesCata}AC.xml"; $fnZip = "{$rfc}{$anioCat}{$mesCata}AC.zip"; $xml->save($xmlDir.$fnXml); chmod($xmlDir.$fnXml,0777); $zip = new ZipArchive(); $zip->open($zipDir.$fnZip, ZipArchive::CREATE|ZipArchive::OVERWRITE); $zip->addFile($xmlDir.$fnXml,$fnXml); $zip->close(); // 10. Responder JSON echo json_encode([ 'code' => '200', 'message' => 'Auxiliar generado', 'xml_file' => $fnXml, 'zip_file' => $fnZip, 'csv_file' => $fnCsv, 'total_registros' => $totalTransacciones ]);