Resumen de Mejoras en Manejo de Errores de QuickBooks
Problema Original
El error en intuit_sync_errors era truncado y poco claro:
{
"error": "API Error [http_error]: Error HTTP 400: ... (truncated...)",
"step": 3
}
Solución Implementada
1. Captura Completa de Errores HTTP
Ubicación: app/Traits/UpdateIntuitOrdersTrait.php → registerInQuickBooks()
Antes:
return [
'success' => false,
'error' => "Error HTTP {$statusCode}: " . $e->getMessage(),
'error_type' => 'http_error',
'status_code' => $statusCode,
'response_body' => $responseBody
];
Ahora:
// Decodificar JSON completo
$responseData = json_decode($responseBody, true);
// Extraer información detallada
$detailedError = $responseData['error'] ?? null;
if (isset($responseData['details'])) {
$detailedError .= ' | Detalles: ' . json_encode($responseData['details']);
}
return [
'success' => false,
'error' => "Error HTTP {$statusCode}: " . $e->getMessage() . " | API Response: {$detailedError}",
'error_type' => 'http_error',
'status_code' => $statusCode,
'response_body' => $responseBody,
'response_data' => $responseData, // NUEVO
'detailed_error' => $detailedError // NUEVO
];
2. Nuevo Método Helper: formatIntuitError()
Ubicación: app/Traits/UpdateIntuitOrdersTrait.php
protected function formatIntuitError(
array $errorResponse,
string $context = '',
string|null $identifier = null
): string
Ejemplo de salida:
Contexto: Registro de Factura |
ID/Número: FE0000006238 |
Tipo: http_error |
HTTP Status: 400 |
Error: Error HTTP 400: Client error |
Detalles API: Request failed with status code 400 - Missing field CustomerRef |
Response Data: {"success":false,"error":"..."} |
Timestamp: 2026-01-23T15:30:45.123Z
3. Actualización de Jobs
UpdateIntuitOrdersJob
// ANTES
throw new \Exception("API Error [{$errorType}]: {$errorMessage} - Invoice: {$invoiceNumber}");
// AHORA
$formattedError = $this->formatIntuitError(
$resultUpdateInvoice,
'Registro de Factura',
$invoiceNumber
);
throw new \Exception($formattedError);
UploadSalesIntuitJob
// ANTES
Log::error("Error registrando factura", [
'error_message' => $errorMessage,
'api_response' => $resultUpdateInvoice
]);
// AHORA
$formattedError = $this->formatIntuitError(
$resultUpdateInvoice,
'Registro de Factura',
$invoiceNumber
);
Log::error("Error registrando factura", [
'formatted_error' => $formattedError,
'api_response' => $resultUpdateInvoice
]);
SendSaleToQuickBooksJob
// ANTES
throw new \Exception("API Error [{$errorType}]: {$errorMessage} - Sale: {$this->sale->getKey()}");
// AHORA
$formattedError = $this->formatIntuitError(
$qbResponse,
'Registro de Factura',
$invoiceNumber
);
throw new \Exception($formattedError);
UpdateIntuitFEJob
// ANTES
$this->handleRateLimitError($resultUpdateInvoice, 'feStepRegisterInvoice', $fe->id);
// AHORA
$formattedError = $this->formatIntuitError(
$resultUpdateInvoice,
'Registro de Factura FE',
$invoiceNumber
);
$this->handleRateLimitError($resultUpdateInvoice, 'feStepRegisterInvoice', $fe->id, $formattedError);
4. Mejoras en handleSyncError()
Ubicación: app/Jobs/Intuit/UpdateIntuitOrdersJob.php
Antes:
$salesHeader->update([
'intuit_sync_status' => 'failed',
'intuit_sync_errors' => json_encode([
'error' => $errorMessage,
'step' => $salesHeader->intuit_sync_step,
'trace' => $e->getTraceAsString()
])
]);
Ahora:
$errorData = [
'error' => $errorMessage,
'step' => $salesHeader->intuit_sync_step,
'timestamp' => now()->toISOString(),
'invoice_number' => $salesHeader->InvoiceNumber ?? 'N/A'
];
// Trace limitado solo en debug mode
if (config('app.debug')) {
$errorData['trace'] = substr($e->getTraceAsString(), 0, 2000);
}
$salesHeader->update([
'intuit_sync_status' => 'failed',
'intuit_sync_errors' => json_encode($errorData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
]);
// Log mejorado
Log::error("Intuit Sync Error", [
'organization_id' => $this->connectionModel->organization->id,
'invoice_number' => $salesHeader->InvoiceNumber,
'step_name' => $this->getStepName($salesHeader->intuit_sync_step),
'has_intuit_customer_id' => !empty($salesHeader->intuit_customer_id)
]);
Nuevo método agregado:
protected function getStepName($step): string
{
$steps = [
0 => 'Validación de datos',
1 => 'Registro de cliente',
2 => 'Registro de items',
3 => 'Registro de factura',
4 => 'Registro de pagos',
5 => 'Finalización'
];
return $steps[$step] ?? "Paso desconocido ({$step})";
}
Comparación de Errores
ANTES (Truncado y poco útil):
{
"error": "API Error [http_error]: Error HTTP 400: Client error: `POST https://...` resulted in a `400 Bad Request` response:\n{\"success\":false,\"error\":\"Request failed with status code 400\",\"data\":null,\"timestamp\":\"2026-01-23T02:05:27.478Z\",\"detai (truncated...)\n - Invoice: FE0000006238",
"step": 3,
"timestamp": "2026-01-23T02:05:27.496161Z"
}
AHORA (Completo y estructurado):
{
"error": "Contexto: Registro de Factura | ID/Número: FE0000006238 | Tipo: http_error | HTTP Status: 400 | Error: Error HTTP 400: Client error: POST https://us-central1-zoho-books-edocs-integracion.cloudfunctions.net/aciv2/create_invoice_quickbooks resulted in a 400 Bad Request response | Detalles API: Request failed with status code 400 - Missing required field: CustomerRef in request payload | Response Data: {\"success\":false,\"error\":\"Request failed with status code 400\",\"data\":null,\"timestamp\":\"2026-01-23T02:05:27.478Z\",\"details\":{\"field\":\"CustomerRef\",\"message\":\"Required field missing\"}} | Timestamp: 2026-01-23T02:05:27.496Z",
"step": 3,
"timestamp": "2026-01-23T02:05:27.496161Z",
"invoice_number": "FE0000006238"
}
Archivos Modificados
- app/Traits/UpdateIntuitOrdersTrait.php
- Mejorada captura de errores HTTP con decodificación JSON completa
-
Agregado método
formatIntuitError() -
app/Jobs/Intuit/UpdateIntuitOrdersJob.php
- Uso de
formatIntuitError()enstepRegisterCustomer() - Uso de
formatIntuitError()enstepRegisterInvoice() - Uso de
formatIntuitError()enstepRegisterPayments() - Mejorado
handleSyncError()con más contexto -
Agregado
getStepName()para nombres descriptivos -
app/Jobs/Invupos/UploadSalesIntuitJob.php
-
Uso de
formatIntuitError()para items, facturas y pagos -
app/Jobs/SendSaleToQuickBooksJob.php
-
Uso de
formatIntuitError()en registro de facturas -
app/Jobs/Intuit/UpdateIntuitFEJob.php
- Uso de
formatIntuitError()enfeStepRegisterInvoice() - Uso de
formatIntuitError()enfeStepRegisterPayments() - Mejorado
handleRateLimitError()para aceptar errores formateados
Beneficios Clave
✅ Errores completos: No más mensajes truncados
✅ Información estructurada: Contexto, tipo, status, detalles
✅ Mejor diagnóstico: Identificar problema en segundos
✅ Logs mejorados: Más contexto para debugging
✅ Compatibilidad: No requiere cambios en BD
✅ Performance: No afecta velocidad de jobs
Testing
# Ver errores en tiempo real
docker exec -it docucenter-app-1 tail -f storage/logs/laravel.log | grep "Intuit"
# Consultar últimos errores en DB
docker exec -it docucenter-app-1 php artisan tinker
>>> $sale = \App\Models\SalesHeaderImp::whereNotNull('intuit_sync_errors')->latest()->first();
>>> echo $sale->intuit_sync_errors;
Documentación
📄 Guía completa: docs/optimizations/quickbooks-error-handling-improvements.md