293 lines
11 KiB
PHP
293 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\ChiamataPostIt;
|
|
use App\Models\RubricaUniversale;
|
|
use App\Support\PhoneNumber;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class PanasonicCstaController extends Controller
|
|
{
|
|
public function incoming(Request $request): JsonResponse
|
|
{
|
|
if (! $this->isAuthorized($request)) {
|
|
return response()->json(['ok' => false, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
$payload = $request->validate([
|
|
'phone' => ['required', 'string', 'max:64'],
|
|
'name' => ['nullable', 'string', 'max:255'],
|
|
'direction' => ['nullable', 'in:in_arrivo,in_uscita,persa'],
|
|
'outcome' => ['nullable', 'string', 'max:255'],
|
|
'event_id' => ['nullable', 'string', 'max:100'],
|
|
'called_extension' => ['nullable', 'string', 'max:30'],
|
|
'note' => ['nullable', 'string'],
|
|
'called_at' => ['nullable', 'date'],
|
|
]);
|
|
|
|
$normalized = PhoneNumber::normalizeForMatch((string) $payload['phone']);
|
|
$rubrica = $this->findRubricaByPhone($normalized);
|
|
|
|
$postIt = null;
|
|
if (Schema::hasTable('chiamate_post_it')) {
|
|
$postIt = ChiamataPostIt::query()->create([
|
|
'stabile_id' => null,
|
|
'rubrica_id' => $rubrica?->id,
|
|
'ticket_id' => null,
|
|
'creato_da_user_id' => null,
|
|
'telefono' => $normalized !== '' ? $normalized : (string) $payload['phone'],
|
|
'nome_chiamante' => $rubrica?->nome_completo ?: ($payload['name'] ?? null),
|
|
'direzione' => $payload['direction'] ?? 'in_arrivo',
|
|
'esito' => $payload['outcome'] ?? null,
|
|
'origine' => 'panasonic_ns1000',
|
|
'origine_id' => $payload['event_id'] ?? null,
|
|
'oggetto' => 'Chiamata ' . (($payload['direction'] ?? 'in_arrivo') === 'in_arrivo' ? 'in arrivo' : 'telefonica'),
|
|
'nota' => $this->buildNote($payload),
|
|
'priorita' => 'Media',
|
|
'stato' => 'post_it',
|
|
'chiamata_il' => $payload['called_at'] ?? now(),
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'source' => 'panasonic_ns1000',
|
|
'phone_normalized' => $normalized,
|
|
'matched' => (bool) $rubrica,
|
|
'rubrica' => $rubrica ? [
|
|
'id' => $rubrica->id,
|
|
'nome_completo' => $rubrica->nome_completo,
|
|
'categoria' => $rubrica->categoria,
|
|
] : null,
|
|
'post_it_id' => $postIt?->id,
|
|
'called_extension' => $payload['called_extension'] ?? null,
|
|
]);
|
|
}
|
|
|
|
public function lookup(Request $request): JsonResponse
|
|
{
|
|
if (! $this->isAuthorized($request)) {
|
|
return response()->json(['ok' => false, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
$data = $request->validate([
|
|
'phone' => ['required', 'string', 'max:64'],
|
|
]);
|
|
|
|
$normalized = PhoneNumber::normalizeForMatch((string) $data['phone']);
|
|
$rubrica = $this->findRubricaByPhone($normalized);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'phone_normalized' => $normalized,
|
|
'matched' => (bool) $rubrica,
|
|
'rubrica' => $rubrica ? [
|
|
'id' => $rubrica->id,
|
|
'nome_completo' => $rubrica->nome_completo,
|
|
'telefono_ufficio' => $rubrica->telefono_ufficio,
|
|
'telefono_cellulare' => $rubrica->telefono_cellulare,
|
|
'telefono_casa' => $rubrica->telefono_casa,
|
|
'categoria' => $rubrica->categoria,
|
|
] : null,
|
|
]);
|
|
}
|
|
|
|
public function callEnded(Request $request): JsonResponse
|
|
{
|
|
if (! $this->isAuthorized($request)) {
|
|
return response()->json(['ok' => false, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
$payload = $request->validate([
|
|
'phone' => ['nullable', 'string', 'max:64', 'required_without:event_id'],
|
|
'event_id' => ['nullable', 'string', 'max:100', 'required_without:phone'],
|
|
'outcome' => ['nullable', 'string', 'max:255'],
|
|
'duration_seconds' => ['nullable', 'integer', 'min:0', 'max:86400'],
|
|
'ended_at' => ['nullable', 'date'],
|
|
'direction' => ['nullable', 'in:in_arrivo,in_uscita,persa'],
|
|
'called_extension' => ['nullable', 'string', 'max:30'],
|
|
'note' => ['nullable', 'string'],
|
|
]);
|
|
|
|
$normalized = PhoneNumber::normalizeForMatch((string) ($payload['phone'] ?? ''));
|
|
$postIt = $this->findOpenPostIt(
|
|
(string) ($payload['event_id'] ?? ''),
|
|
$normalized !== '' ? $normalized : (string) ($payload['phone'] ?? '')
|
|
);
|
|
|
|
$created = false;
|
|
if (! $postIt && Schema::hasTable('chiamate_post_it')) {
|
|
$created = true;
|
|
$rubrica = $this->findRubricaByPhone($normalized);
|
|
|
|
$postIt = ChiamataPostIt::query()->create([
|
|
'stabile_id' => null,
|
|
'rubrica_id' => $rubrica?->id,
|
|
'ticket_id' => null,
|
|
'creato_da_user_id' => null,
|
|
'telefono' => $normalized !== '' ? $normalized : (string) ($payload['phone'] ?? ''),
|
|
'nome_chiamante' => $rubrica?->nome_completo,
|
|
'direzione' => $payload['direction'] ?? 'in_arrivo',
|
|
'esito' => $payload['outcome'] ?? null,
|
|
'origine' => 'panasonic_ns1000',
|
|
'origine_id' => $payload['event_id'] ?? null,
|
|
'oggetto' => 'Chiamata terminata (evento senza incoming)',
|
|
'nota' => $this->buildNote($payload),
|
|
'priorita' => 'Media',
|
|
'stato' => 'post_it',
|
|
'chiamata_il' => $payload['ended_at'] ?? now(),
|
|
]);
|
|
}
|
|
|
|
if (! $postIt) {
|
|
return response()->json([
|
|
'ok' => false,
|
|
'message' => 'No matching Post-it and table unavailable',
|
|
], 404);
|
|
}
|
|
|
|
$postIt->esito = $payload['outcome'] ?? $postIt->esito;
|
|
$postIt->stato = 'chiusa';
|
|
|
|
if (Schema::hasColumn('chiamate_post_it', 'durata_secondi')) {
|
|
$postIt->durata_secondi = $payload['duration_seconds'] ?? $postIt->durata_secondi;
|
|
}
|
|
|
|
if (Schema::hasColumn('chiamate_post_it', 'chiusa_il')) {
|
|
$postIt->chiusa_il = $payload['ended_at'] ?? now();
|
|
}
|
|
|
|
if (! empty($payload['note'])) {
|
|
$existing = trim((string) ($postIt->nota ?? ''));
|
|
$suffix = 'Note chiusura PBX: ' . $payload['note'];
|
|
$postIt->nota = $existing !== '' ? ($existing . "\n" . $suffix) : $suffix;
|
|
}
|
|
|
|
$postIt->save();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'source' => 'panasonic_ns1000',
|
|
'closed' => true,
|
|
'created_new' => $created,
|
|
'post_it_id' => $postIt->id,
|
|
'stato' => $postIt->stato,
|
|
'esito' => $postIt->esito,
|
|
'durata_secondi' => Schema::hasColumn('chiamate_post_it', 'durata_secondi') ? $postIt->durata_secondi : null,
|
|
]);
|
|
}
|
|
|
|
private function isAuthorized(Request $request): bool
|
|
{
|
|
$configured = (string) env('NETGESCON_CTI_SHARED_TOKEN', '');
|
|
if ($configured === '') {
|
|
$configured = (string) env('CTI_SHARED_SECRET', '');
|
|
}
|
|
|
|
if ($configured === '') {
|
|
$configured = (string) env('CTI_SHARED_TOKEN', '');
|
|
}
|
|
|
|
if ($configured === '') {
|
|
$configured = (string) env('CTI_TOKEN', '');
|
|
}
|
|
|
|
if ($configured === '') {
|
|
$configured = (string) env('CTI_PANASONIC_TOKEN', '');
|
|
}
|
|
|
|
if ($configured === '') {
|
|
return false;
|
|
}
|
|
|
|
$token = (string) ($request->header('X-CTI-Token') ?: $request->input('token', ''));
|
|
|
|
return hash_equals($configured, $token);
|
|
}
|
|
|
|
private function findRubricaByPhone(string $phone): ?RubricaUniversale
|
|
{
|
|
$digits = PhoneNumber::normalizeDigits($phone);
|
|
if ($digits === '') {
|
|
return null;
|
|
}
|
|
|
|
$tail = substr($digits, -7);
|
|
if ($tail === false || $tail === '') {
|
|
return null;
|
|
}
|
|
|
|
return RubricaUniversale::query()
|
|
->where(function ($q) use ($tail) {
|
|
$q->orWhere('telefono_cellulare', 'like', '%' . $tail . '%')
|
|
->orWhere('telefono_ufficio', 'like', '%' . $tail . '%')
|
|
->orWhere('telefono_casa', 'like', '%' . $tail . '%')
|
|
->orWhere('telefono_cellulare_nazionale', 'like', '%' . $tail . '%');
|
|
})
|
|
->orderByDesc('updated_at')
|
|
->first();
|
|
}
|
|
|
|
private function findOpenPostIt(string $eventId, string $phone): ?ChiamataPostIt
|
|
{
|
|
if (! Schema::hasTable('chiamate_post_it')) {
|
|
return null;
|
|
}
|
|
|
|
if ($eventId !== '') {
|
|
$byEvent = ChiamataPostIt::query()
|
|
->where('origine', 'panasonic_ns1000')
|
|
->where('origine_id', $eventId)
|
|
->where('stato', '!=', 'chiusa')
|
|
->orderByDesc('id')
|
|
->first();
|
|
|
|
if ($byEvent) {
|
|
return $byEvent;
|
|
}
|
|
}
|
|
|
|
$digits = PhoneNumber::normalizeDigits($phone);
|
|
if ($digits === '') {
|
|
return null;
|
|
}
|
|
|
|
$tail = substr($digits, -7);
|
|
if ($tail === false || $tail === '') {
|
|
return null;
|
|
}
|
|
|
|
return ChiamataPostIt::query()
|
|
->where('stato', '!=', 'chiusa')
|
|
->where('origine', 'panasonic_ns1000')
|
|
->where('telefono', 'like', '%' . $tail . '%')
|
|
->orderByDesc('chiamata_il')
|
|
->first();
|
|
}
|
|
|
|
private function buildNote(array $payload): string
|
|
{
|
|
$lines = [
|
|
'Origine: Panasonic NS1000 (CSTA)',
|
|
];
|
|
|
|
if (! empty($payload['called_extension'])) {
|
|
$lines[] = 'Interno chiamato: ' . $payload['called_extension'];
|
|
}
|
|
|
|
if (! empty($payload['event_id'])) {
|
|
$lines[] = 'Event ID: ' . $payload['event_id'];
|
|
}
|
|
|
|
if (! empty($payload['note'])) {
|
|
$lines[] = 'Note PBX: ' . $payload['note'];
|
|
}
|
|
|
|
return implode("\n", $lines);
|
|
}
|
|
}
|