025-07-01 10:23:09.582 [info] Missing or invalid credentials. Skip silent fetch commands Missing or invalid credentials. Skip silent fetch commands remote: Repository not found. fatal: Authentication failed for 'https://github.com/aptuslegal/aptusND.git/' 2025-07-01 10:23:09.592 [info] > git config --get commit.template [3ms] 2025-07-01 10:23:09.600 [info] > git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/master refs/remotes/master [1ms] 2025-07-01 10:23:09.611 [info] > git status -z -uall [6ms] 2025-07-01 10:23:09.613 [info] > git for-each-ref --sort -committerdate --format %(refname)%00%(objectname)%00%(*objectname) [2ms] 2025-07-01 10:23:13.729 [info] > git rev-parse --show-toplevel [3ms] 2025-07-01 10:23:13.729 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:30:05.015 [info] > git rev-parse --show-toplevel [1ms] 2025-07-01 10:30:05.015 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:36:37.576 [info] > git rev-parse --show-toplevel [2ms] 2025-07-01 10:36:37.576 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:44:36.964 [info] > git config --get commit.template [6ms] 2025-07-01 10:44:36.967 [info] > git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/master refs/remotes/master [3ms] 2025-07-01 10:44:36.981 [info] > git status -z -uall [7ms] 2025-07-01 10:44:36.981 [info] > git for-each-ref --sort -committerdate --format %(refname)%00%(objectname)%00%(*objectname) [1ms] 2025-07-01 10:44:41.996 [info] > git config --get commit.template [6ms] 2025-07-01 10:44:41.996 [info] > git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/master refs/remotes/master [1ms] 2025-07-01 10:44:42.007 [info] > git status -z -uall [6ms] 2025-07-01 10:44:42.007 [info] > git for-each-ref --sort -committerdate --format %(refname)%00%(objectname)%00%(*objectname) [1ms] 2025-07-01 10:44:49.950 [info] > git rev-parse --show-toplevel [3ms] 2025-07-01 10:44:49.950 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:45:00.358 [info] > git rev-parse --show-toplevel [1ms] 2025-07-01 10:45:00.358 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:45:03.454 [info] > git rev-parse --show-toplevel [2ms] 2025-07-01 10:45:03.454 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:50:31.859 [info] > git rev-parse --show-toplevel [2ms] 2025-07-01 10:50:31.859 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:54:13.397 [info] > git rev-parse --show-toplevel [2ms] 2025-07-01 10:54:13.398 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:54:33.955 [info] > git rev-parse --show-toplevel [3ms] 2025-07-01 10:54:33.955 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 10:56:00.333 [info] > git rev-parse --show-toplevel [4ms] 2025-07-01 10:56:00.333 [info] fatal: not a git repository (or any of the parent directories): .git 2025-07-01 13:30:07.212 [info] > git config --get commit.template [6ms] 2025-07-01 13:30:07.213 [info] > git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/master refs/remotes/master [2ms] 2025-07-01 13:30:07.224 [info] > git status -z -uall [7ms] 2025-07-01 13:30:07.224 [info] > git for-each-ref --sort -committerdate --format %(refname)%00%(objectname)%00%(*objectname) [1ms] 2025-07-01 13:30:52.670 [info] > git config --get commit.template [2ms] 2025-07-01 13:30:52.677 [info] > git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00% '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 ]; } // Ordenar las cuentas por NumCta ascendente uasort($cuentas, function($a, $b) { return strcmp($a['NumCta'], $b['NumCta']); }); // 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['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; // --- 1) Calcular totales de los movimientos para esta cuenta $totalDebe = 0.0; $totalHaber = 0.0; foreach ($txPorCta[$acctId] as $t) { $totalDebe += $t['Debe']; $totalHaber += $t['Haber']; } // --- 2) Crear nodo con los saldos correctos $nC = $xml->createElement('AuxiliarCtas:Cuenta'); $root->appendChild($nC); $nC->setAttribute('NumCta', $info['NumCta']); $nC->setAttribute('DesCta', $info['DesCta']); // Usamos number_format para dejar dos decimales $nC->setAttribute('SaldoIni', number_format($totalDebe, 2, '.', '')); $nC->setAttribute('SaldoFin', number_format($totalHaber, 2, '.', '')); 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 ]);