- Implementación de Dashboard profesional en public.php con CRUD de instaladores. - Sincronización con la API de UCRM para selección y validación automática de administradores. - Soporte para creación selectiva de clientes en Stripe (Etiqueta "CREAR CLIENTE STRIPE" vs "CREAR CLABE STRIPE"). - Mejora en la lógica de notificaciones de tareas (nuevo prefijo [CLIENTE-SIN-WHATSAPP]). - Refactorización integral de fachadas y saneamiento de código muerto. - Sistema de Modo Oscuro persistente con UI refinada.
290 lines
15 KiB
PHP
Executable File
290 lines
15 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace SmsNotifier\Facade;
|
|
|
|
use GuzzleHttp\Client;
|
|
use SmsNotifier\Data\NotificationData;
|
|
use SmsNotifier\Facade\ClientCallBellAPI;
|
|
use SmsNotifier\Factory\MessageTextFactory;
|
|
use SmsNotifier\Service\Logger;
|
|
use SmsNotifier\Service\SmsNumberProvider;
|
|
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
|
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
|
use \DateTime;
|
|
|
|
abstract class AbstractMessageNotifierFacade
|
|
{
|
|
protected $logger;
|
|
protected $messageTextFactory;
|
|
protected $clientPhoneNumber;
|
|
protected $ucrmApi;
|
|
|
|
const SUBJECT_OF_INSTALLER_CHANGE = ["se ha cancelado una tarea que tenías asignada con el folio ", "se te ha desasignado❌ la tarea con el folio "];
|
|
const ADDITIONAL_CHANGE_DATA = ["Ya no es necesario realizar la visita técnica.", "En tu lugar asistirá el técnico 👷🏻♂️➡️ "];
|
|
|
|
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber) {
|
|
$this->logger = $logger;
|
|
$this->messageTextFactory = $messageTextFactory;
|
|
$this->clientPhoneNumber = $clientPhoneNumber;
|
|
$this->ucrmApi = UcrmApi::create();
|
|
}
|
|
|
|
public function verifyPaymentActionToDo(NotificationData $notificationData): void {
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
switch ($type) {
|
|
case 'whatsapp': $this->notifyAndUpdate($notificationData, $phone); break;
|
|
case 'whatsnotifica': $this->notify($notificationData, $phone); break;
|
|
case 'whatsactualiza': $this->onlyUpdate($notificationData, $phone); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyClientActionToDo(NotificationData $notificationData): void {
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdate($notificationData, $phone);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyServiceActionToDo(NotificationData $notificationData): void {
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdateService($notificationData, $phone);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyJobActionToDo($jsonNotificationData, $reprogramming = null, $changeInstaller = null): void {
|
|
$this->logger->info('Iniciando verifyJobActionToDo');
|
|
$clientId = $jsonNotificationData['extraData']['entity']['clientId'];
|
|
$installerId = $jsonNotificationData['extraData']['entity']['assignedUserId'];
|
|
$jobId = $jsonNotificationData['entityId'];
|
|
|
|
$dateString = $jsonNotificationData['extraData']['entity']['date'] ?? null;
|
|
$formattedDate = $dateString ? sprintf("*%s*", (new DateTime($dateString))->format('d/m/Y')) : '';
|
|
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$admin = $this->ucrmApi->get("users/admins/$installerId", []);
|
|
$installerName = trim(($admin['firstName'] ?? '') . ' ' . ($admin['lastName'] ?? ''));
|
|
|
|
$installerWhatsApp = '';
|
|
$installers = json_decode($config['installersDataWhatsApp'] ?? '{"instaladores":[]}', true);
|
|
foreach ($installers['instaladores'] as $inst) {
|
|
if ($inst['id'] == $installerId) { $installerWhatsApp = $inst['whatsapp']; break; }
|
|
}
|
|
|
|
if (empty($installerWhatsApp)) {
|
|
$this->logger->warning("No se encontró número de WhatsApp para el instalador ID: $installerId");
|
|
}
|
|
|
|
$clientCRM = $this->ucrmApi->get("clients/$clientId", []);
|
|
$clientName = trim(($clientCRM['firstName'] ?? '') . ' ' . ($clientCRM['lastName'] ?? ''));
|
|
$passCRM = '';
|
|
foreach ($clientCRM['attributes'] as $attr) {
|
|
if ($attr['key'] === 'passwordAntenaCliente') { $passCRM = $attr['value']; break; }
|
|
}
|
|
|
|
$allPhones = $this->clientPhoneNumber->getAllUcrmClientNumbers($clientCRM);
|
|
$phonesStr = implode(', ', array_map(fn($n) => $this->validarNumeroTelefono($n), $allPhones));
|
|
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
|
|
// --- Nueva Lógica de Prefijos ---
|
|
$title = $jsonNotificationData['extraData']['entity']['title'] ?? '';
|
|
$isPending = (stripos($title, '[NOTIFICACION-PENDIENTE]') !== false);
|
|
$isNoWhatsApp = (stripos($title, '[CLIENTE-SIN-WHATSAPP]') !== false);
|
|
|
|
$reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN);
|
|
$changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN);
|
|
|
|
$clientPhones = $this->clientPhoneNumber->getUcrmClientNumbers(null, $clientCRM);
|
|
$hasClientWhatsApp = false;
|
|
foreach ($clientPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (($type === 'whatsapp' || $type === 'whatsnotifica') && !empty($phones)) {
|
|
$hasClientWhatsApp = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$shouldNotifyTech = ($isPending || $reprogramming || $changeInstaller);
|
|
$shouldNotifyClient = ($isPending || $isNoWhatsApp || $reprogramming || $changeInstaller);
|
|
|
|
$this->logger->debug("Estado de notificación - Pending: " . ($isPending?'SI':'NO') . ", NoWhatsApp: " . ($isNoWhatsApp?'SI':'NO') . ", HasWA: " . ($hasClientWhatsApp?'SI':'NO'));
|
|
|
|
// 1. Notificar al Instalador Anterior (Desasignación)
|
|
if ($changeInstaller) {
|
|
$prevId = $jsonNotificationData['extraData']['entityBeforeEdit']['assignedUserId'];
|
|
$prevAdmin = $this->ucrmApi->get("users/admins/$prevId", []);
|
|
$prevWhatsApp = '';
|
|
foreach ($installers['instaladores'] as $inst) {
|
|
if ($inst['id'] == $prevId) { $prevWhatsApp = $inst['whatsapp']; break; }
|
|
}
|
|
if ($prevWhatsApp) {
|
|
$api->sendJobNotificationWhatsAppToInstaller($this->validarNumeroTelefono($prevWhatsApp), [
|
|
"installerName" => "👷🏻♂️" . trim(($prevAdmin['firstName'] ?? '') . ' ' . ($prevAdmin['lastName'] ?? '')),
|
|
"subjectOfChange" => self::SUBJECT_OF_INSTALLER_CHANGE[1],
|
|
"jobId" => $jobId,
|
|
"clientFullName" => "[$clientId] $clientName",
|
|
"additionalChangeData" => self::ADDITIONAL_CHANGE_DATA[1] . ' *' . $installerName . '*',
|
|
], $reprogramming, $changeInstaller);
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
// 2. Notificar al Cliente (si aplica)
|
|
$clientNotified = false;
|
|
if ($shouldNotifyClient && $hasClientWhatsApp) {
|
|
foreach ($clientPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones) || ($type !== 'whatsapp' && $type !== 'whatsnotifica')) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($api->sendJobNotificationWhatsAppToClient($this->validarNumeroTelefono($phone), ["clientFullName" => $clientName, "jobId" => $jobId, "date" => $formattedDate, "installerName" => $installerName], $reprogramming, $changeInstaller)) {
|
|
$clientNotified = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Notificar al Técnico (si aplica)
|
|
if ($shouldNotifyTech) {
|
|
$passVault = $this->getVaultCredentialsByClientId($clientId);
|
|
$api->sendJobNotificationWhatsAppToInstaller($this->validarNumeroTelefono($installerWhatsApp), [
|
|
"installerName" => $installerName,
|
|
"clientFullName" => "$clientName [ID:$clientId]",
|
|
"jobId" => $jobId,
|
|
"clientAddress" => $clientCRM['fullAddress'] ?? 'N/A',
|
|
"clientWhatsApp" => !empty($phonesStr) ? $phonesStr : 'Sin WhatsApp',
|
|
"date" => $formattedDate,
|
|
"jobDescription" => $jsonNotificationData['extraData']['entity']['description'] ?? 'S/D',
|
|
"gmapsLocation" => ($clientCRM['addressGpsLat'] && $clientCRM['addressGpsLon']) ? "https://www.google.com/maps?q={$clientCRM['addressGpsLat']},{$clientCRM['addressGpsLon']}" : 'N/A',
|
|
"passwordAntenaCliente" => $this->comparePasswords($passCRM, $passVault)
|
|
], $reprogramming, $changeInstaller);
|
|
}
|
|
|
|
// 3. Gestión del Título / Prefijos
|
|
if ($isPending) {
|
|
if ($clientNotified || $reprogramming || $changeInstaller) {
|
|
// Si el cliente fue notificado o es un cambio, quitamos el prefijo
|
|
$newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
} else if (!$hasClientWhatsApp) {
|
|
// Si no tiene WhatsApp, cambiamos a estado "SIN-WHATSAPP" para no volver a notificar al técnico
|
|
$newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '[CLIENTE-SIN-WHATSAPP]', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
}
|
|
} else if ($isNoWhatsApp && ($clientNotified || $reprogramming || $changeInstaller)) {
|
|
// Si estaba marcado como sin whatsapp y ahora sí pudimos notificarlo
|
|
$newTitle = str_ireplace('[CLIENTE-SIN-WHATSAPP]', '', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
}
|
|
}
|
|
|
|
public function verifyInvoiceActionToDo(NotificationData $notificationData): void {
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdate($notificationData, $phone, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function notify(NotificationData $notificationData, $phoneToNotify = null): void {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToNotify);
|
|
if (!$phone) return;
|
|
if ($config['notificationTypeText'] ?? false) $api->sendTextPaymentNotificationWhatsApp($phone, $notificationData);
|
|
else $api->sendPaymentNotificationWhatsApp($phone, $notificationData);
|
|
}
|
|
|
|
public function notifyAndUpdate(NotificationData $notificationData, $phoneToNotifyAndUpdate = null): void {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToNotifyAndUpdate);
|
|
if (!$phone) return;
|
|
if ($config['notificationTypeText'] ?? false) {
|
|
if ($api->sendTextPaymentNotificationWhatsApp($phone, $notificationData)) {
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
$api->patchWhatsapp($contact, $notificationData);
|
|
}
|
|
} else {
|
|
if ($api->sendPaymentNotificationWhatsApp($phone, $notificationData)) {
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
$api->patchWhatsapp($contact, $notificationData);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function notifyOverDue(NotificationData $notificationData): void {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->clientPhoneNumber->getUcrmClientNumber($notificationData);
|
|
if ($phone) $api->sendOverdueNotificationWhatsApp($phone, $notificationData);
|
|
}
|
|
|
|
public function onlyUpdate(NotificationData $notificationData, $phoneToUpdate): void {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
if ($contact) $api->patchWhatsapp($contact, $notificationData);
|
|
}
|
|
|
|
public function onlyUpdateService(NotificationData $notificationData, $phoneToUpdate): void {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
if ($contact) $api->patchServiceStatusWhatsApp($contact, $notificationData);
|
|
}
|
|
|
|
protected function getVaultCredentialsByClientId($clientId): string {
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
if ($config['ipserver'] === '172.16.5.134') return 'gYAIEK:Be}SK*01z5+/V';
|
|
$unms = new Client(['base_uri' => "https://{$config['ipserver']}/nms/api/v2.1/", 'verify' => false]);
|
|
$crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]);
|
|
try {
|
|
$respSvc = $crm->get('clients/services?clientId=' . $clientId, ['headers' => ['X-Auth-Token' => $config['apitoken']]]);
|
|
$svcs = json_decode($respSvc->getBody()->getContents(), true);
|
|
if (!isset($svcs[0]['unmsClientSiteId'])) return 'Error: Sin sitio';
|
|
$respDev = $unms->get("devices?siteId={$svcs[0]['unmsClientSiteId']}", ['headers' => ['X-Auth-Token' => $config['unmsApiToken']]]);
|
|
$devs = json_decode($respDev->getBody()->getContents(), true);
|
|
if (!isset($devs[0]['identification']['id'])) return 'Error: Sin equipo';
|
|
$respVault = $unms->get("vault/{$devs[0]['identification']['id']}/credentials", ['headers' => ['X-Auth-Token' => $config['unmsApiToken']]]);
|
|
$vault = json_decode($respVault->getBody()->getContents(), true);
|
|
return $vault['credentials'][0]['password'] ?? 'Error: Sin pass';
|
|
} catch (\Exception $e) { return 'Error: ' . $e->getMessage(); }
|
|
}
|
|
|
|
protected function comparePasswords(?string $crm, ?string $vault): string {
|
|
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
|
if ($vault && strpos($vault, 'Error') !== 0) return $vault;
|
|
return '⚠️ Probar pass conocida.';
|
|
}
|
|
|
|
protected function validarNumeroTelefono($n): string {
|
|
if (!$n) return '';
|
|
$n = preg_replace('/\D/', '', (string)$n);
|
|
return (strlen($n) === 10) ? '52' . $n : $n;
|
|
}
|
|
|
|
abstract protected function sendWhatsApp(NotificationData $notificationData, string $clientSmsNumber): void;
|
|
}
|