feat: WhatsApp promociones — fechas en español, status webhook y analytics inspector

- Corrige formato de fechas a español con Carbon isoFormat('D [de] MMMM')
- Webhook procesa statuses de Meta (delivered/read/failed) y actualiza pivot alumno_promocion
- Analytics: nuevas métricas de entregados/leídos, embudo de entrega, filtro por tipo
- Inspector de payload raw por log con modal y botón copiar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 15:42:26 -06:00
parent 19a236818e
commit 61b18df8e8
6 changed files with 273 additions and 73 deletions
@@ -7,6 +7,7 @@ use App\Models\Alumno;
use App\Models\WhatsappLog;
use App\Services\WhatsAppService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class WhatsAppWebhookController extends Controller
@@ -37,6 +38,13 @@ class WhatsAppWebhookController extends Controller
$value = $changes['value'] ?? null;
$message = $value['messages'][0] ?? null;
// Procesar status updates (entregado/leído/fallido) de Meta
if (!empty($value['statuses'])) {
foreach ($value['statuses'] as $status) {
$this->procesarStatus($status, $payload);
}
}
if (!$message) {
return response()->json(['status' => 'ok']);
}
@@ -113,4 +121,34 @@ class WhatsAppWebhookController extends Controller
return false;
}
private function procesarStatus(array $status, array $payload): void
{
$mensajeId = $status['id'] ?? null;
$estadoMeta = $status['status'] ?? null; // sent, delivered, read, failed
$telefono = $status['recipient_id'] ?? null;
$alumno = $this->alumnoPorTelefono($telefono);
WhatsappLog::create([
'alumno_id' => $alumno?->id,
'telefono' => $telefono,
'tipo' => 'status_' . $estadoMeta,
'mensaje' => "Acuse de {$estadoMeta} para mensaje {$mensajeId}",
'payload' => $payload,
]);
// Actualizar pivot alumno_promocion cuando hay confirmación de entrega o lectura
if ($mensajeId && in_array($estadoMeta, ['delivered', 'read', 'failed'])) {
$nuevoStatus = match($estadoMeta) {
'delivered' => 'entregado',
'read' => 'leido',
'failed' => 'fallido',
default => $estadoMeta,
};
DB::table('alumno_promocion')
->where('whatsapp_mensaje_id', $mensajeId)
->update(['status_envio' => $nuevoStatus]);
}
}
}