finish reason: [stop] 2025-05-14 17:26:17.285 [info] [fetchCompletions] Request 0ff0a1c0-2aa1-4dd0-9e7b-f5bbfc502437 at finished with 200 status after 197.06833100004587ms 2025-05-14 17:26:17.287 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:20.989 [info] [fetchCompletions] Request bb3ee398-4563-4d3f-af1b-01140f870e87 at finished with 200 status after 163.17418399988674ms 2025-05-14 17:26:20.991 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:25.372 [info] [fetchCompletions] Request 2c080899-1a20-4c28-850c-a0b635c1614d at finished with 200 status after 386.2803240000503ms 2025-05-14 17:26:25.376 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:28.434 [info] [fetchCompletions] Request 983496cb-d0e0-442e-b25d-574859a7d5f6 at finished with 200 status after 421.34472399996594ms 2025-05-14 17:26:28.435 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:30.010 [info] [fetchCompletions] Request 2dc775bc-a870-4b17-846c-0920a672a8eb at finished with 200 status after 177.28202200005762ms 2025-05-14 17:26:30.012 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:31.343 [info] [fetchCompletions] Request ab3cc16f-879b-479a-bc46-15ec7548eb11 at finished with 200 status after 297.982405000017ms 2025-05-14 17:26:31.345 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:31.346 [info] [ghostText] Filtered out solution matching next line 2025-05-14 17:26:31.582 [info] [fetchCompletions] Request a9d6df6e-d9cd-4049-8e64-0b9372d37354 at finished with 200 status after 180.78697599994484ms 2025-05-14 17:26:31.584 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:34.652 [info] [fetchCompletions] Request 6ca0ff3d-308e-4cfa-8f7c-e20c04dbc781 at finished with 200 status after 229.29859899997246ms 2025-05-14 17:26:34.655 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:26:37.795 [info] [fetchCompletions] Request 2c331dfd-edd0-4019-81af-bc8e4d597a8b at finished with 200 status after 167.59584699990228ms 2025-05-14 17:26:37.796 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:27:08.159 [info] [fetchCompletions] Request 7813e51c-f5ad-42ec-a316-d222dc66e785 at finished with 200 status after 296.86452900001314ms 2025-05-14 17:27:08.161 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:27:11.475 [info] [fetchCompletions] Request fba2f3be-778c-456b-a1b0-7315862217af at finished with 200 status after 235.28302299999632ms 2025-05-14 17:27:11.477 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:29:51.790 [info] [fetchCompletions] Request c9e47708-e018-4224-908c-7bb6cda49234 at finished with 200 status after 420.54672999994364ms 2025-05-14 17:29:51.791 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-05-14 17:31:14.270 [info] [fetchCompletions] Request 554db126-33d4-4bde-b80b-84d1e666245e at 'https://donatello.aptuslegal.app/oauth/token/'.$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']; $cuentas[$id] = [ 'NumCta' => $c['account_code'], // el código real 'DesCta' => utf8_decode(substr($c['account_name'],11)) // descripción ]; } // 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 $dias = cal_days_in_month(CAL_GREGORIAN, intval($mesCata), intval($anioCat)); $config = urlencode(json_encode([ "criteria" => "\"Fecha de transaccion\" between '{$anioCat}-{$mesCata}-01' and '{$anioCat}-{$mesCata}-{$dias}'", "responseFormat"=> "json" ])); $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" ] ]); $j1 = json_decode(curl_exec($ch), true); curl_close($ch); $jobId = $j1['data']['jobId'] ?? ''; if (!$jobId) { echo json_encode(['code'=>'404','message'=>'No se obtuvo jobId Analytics']); 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['Transaccion'], 'Concepto' => $r['Concepto'], 'Debe' => (float)$r['Debe'], 'Haber' => (float)$r['Haber'] ]; } // 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,'.','')); } } // 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 ]);