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:
@@ -37,12 +37,21 @@ class WhatsAppAnalyticsController extends Controller
|
||||
$query->where('created_at', '<=', $request->fecha_hasta . ' 23:59:59');
|
||||
}
|
||||
|
||||
$enviados = (clone $query)->where('tipo', 'enviado')->count();
|
||||
$fallidos = (clone $query)->where('tipo', 'error')->count();
|
||||
$optOuts = (clone $query)->where('tipo', 'opt_out')->count();
|
||||
$recibidos = (clone $query)->where('tipo', 'recibido')->count();
|
||||
// Métricas siempre sobre todos los tipos (sin filtro de tipo)
|
||||
$enviados = (clone $query)->where('tipo', 'enviado')->count();
|
||||
$fallidos = (clone $query)->where('tipo', 'error')->count();
|
||||
$optOuts = (clone $query)->where('tipo', 'opt_out')->count();
|
||||
$recibidos = (clone $query)->where('tipo', 'recibido')->count();
|
||||
$entregados = (clone $query)->where('tipo', 'status_delivered')->count();
|
||||
$leidos = (clone $query)->where('tipo', 'status_read')->count();
|
||||
|
||||
$logsRecientes = (clone $query)
|
||||
// El filtro de tipo solo aplica al listado de logs
|
||||
$logsQuery = clone $query;
|
||||
if ($request->filled('tipo')) {
|
||||
$logsQuery->where('tipo', $request->tipo);
|
||||
}
|
||||
|
||||
$logsRecientes = $logsQuery
|
||||
->with('alumno.alumnos')
|
||||
->latest('created_at')
|
||||
->paginate(20);
|
||||
@@ -52,7 +61,7 @@ class WhatsAppAnalyticsController extends Controller
|
||||
})->latest()->get();
|
||||
|
||||
return view('promocion.analytics', compact(
|
||||
'enviados', 'fallidos', 'optOuts', 'recibidos',
|
||||
'enviados', 'fallidos', 'optOuts', 'recibidos', 'entregados', 'leidos',
|
||||
'logsRecientes', 'planteles', 'promociones'
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user