siip-whatsapp-notifications.../src/Plugin.php

653 lines
42 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace SmsNotifier;
use Psr\Log\LogLevel;
use SmsNotifier\Facade\PluginNotifierFacade;
use SmsNotifier\Facade\PluginOxxoNotifierFacade;
use SmsNotifier\Facade\TwilioNotifierFacade;
use SmsNotifier\Factory\NotificationDataFactory;
use SmsNotifier\Service\Logger;
use SmsNotifier\Service\OptionsManager;
use SmsNotifier\Service\PluginDataValidator;
use Twilio\Exceptions\TwilioException;
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
class Plugin
{
/**
* @var Logger
*/
private $logger;
/**
* @var OptionsManager
*/
private $optionsManager;
/**
* @var PluginDataValidator
*/
private $pluginDataValidator;
/**
* @var PluginNotifierFacade
*/
private $pluginNotifierFacade;
/**
* @var PluginOxxoNotifierFacade
*/
private $pluginOxxoNotifierFacade;
/**
* @var TwilioNotifierFacade
*/
private $notifierFacade;
/**
* @var NotificationDataFactory
*/
private $notificationDataFactory;
/**
* @var UcrmApi
*/
protected $ucrmApi;
public function __construct(
Logger $logger,
OptionsManager $optionsManager,
PluginDataValidator $pluginDataValidator,
TwilioNotifierFacade $notifierFacade,
PluginNotifierFacade $pluginNotifierFacade,
PluginOxxoNotifierFacade $pluginOxxoNotifierFacade,
NotificationDataFactory $notificationDataFactory
) {
$this->logger = $logger;
$this->optionsManager = $optionsManager;
$this->pluginDataValidator = $pluginDataValidator;
$this->notifierFacade = $notifierFacade;
$this->pluginNotifierFacade = $pluginNotifierFacade;
$this->pluginOxxoNotifierFacade = $pluginOxxoNotifierFacade;
$this->notificationDataFactory = $notificationDataFactory;
$config = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create()->loadConfig();
$ipServer = $config['ipserver'] ?? 'localhost';
$apiUrl = "https://$ipServer/crm/api/v1.0/";
$client = new \GuzzleHttp\Client([
'base_uri' => $apiUrl,
'verify' => false,
]);
$this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? '');
}
public function run(): void
{
if (PHP_SAPI === 'fpm-fcgi' || PHP_SAPI === 'cgi-fcgi' || PHP_SAPI === 'apache2handler') {
$this->processHttpRequest();
} elseif (PHP_SAPI === 'cli') {
$this->processCli();
} else {
$this->logger->error('SAPI desconocido: ' . PHP_SAPI . '. Intentando procesar como HTTP.');
$this->processHttpRequest();
}
}
private function processCli(): void
{
if ($this->pluginDataValidator->validate()) {
$this->logger->info('Validating config');
$this->optionsManager->load();
}
}
private function processHttpRequest(): void
{
$pluginData = $this->optionsManager->load();
if ($pluginData->logging_level || $pluginData->debugMode) {
$this->logger->setLogLevelThreshold(LogLevel::DEBUG);
}
$userInput = file_get_contents('php://input');
// DEBUG TEMPORAL
$this->logger->debug('Payload recibido: ' . $userInput . PHP_EOL);
if (! $userInput) {
$this->logger->warning('No se recibió input en la petición HTTP.');
return;
}
$jsonData = @json_decode((string)$userInput, true, 50);
if (! isset($jsonData['uuid'])) {
$this->logger->info('No UUID found in the webhook data');
//$this->logger->error('JSON error: ' . json_last_error_msg());
//return;
// Maneja el evento del webhook externo
if ($jsonData) {
switch ($jsonData['type']) {
case 'customer_cash_balance_transaction.created':
if ($jsonData['data']['object']['type'] === 'funded') {
$this->logger->info('Evento de transferencia de un cliente recibido: ' . json_encode($jsonData) . PHP_EOL);
$this->pluginNotifierFacade->createPaymentIntent($jsonData);
}
if ($jsonData['data']['object']['type'] === 'applied_to_payment') {
$this->logger->info('Se aplicó el saldo en Stripe de un pago: ' . json_encode($jsonData) . PHP_EOL);
$this->pluginNotifierFacade->registerPaymentFromWebhook($jsonData);
} elseif ($jsonData['data']['object']['type'] === 'unapplied_from_payment') {
//ejemplo de json para transferencia de dinero cancelada: {"id":"evt_1RlEGgEFY1WEUtgR6Bp2DzDP","object":"event","api_version":"2023-10-16","created":1752606717,"data":{"object":{"id":"ccsbtxn_1RlEGfEFY1WEUtgRv8jAUGmE","object":"customer_cash_balance_transaction","created":1752606717,"currency":"mxn","customer":"cus_PetN1dhr4rx0kX","ending_balance":18000,"livemode":false,"net_amount":18000,"type":"unapplied_from_payment","unapplied_from_payment":{"payment_intent":"pi_3RlDPdEFY1WEUtgR1JBgNhTQ"}}},"livemode":false,"pending_webhooks":2,"request":{"id":"req_954mskVBfAI0jn","idempotency_key":"749518f6-baa0-4ae9-99e4-8029a35719aa"},"type":"customer_cash_balance_transaction.created"}
$paymentIntentId = $jsonData['data']['object']['unapplied_from_payment']['payment_intent'];
//Se canceló una transferencia de dinero, imprimir que se canceló y además el monto neto
$this->logger->warning('Evento de transferencia cancelada para el pago: ' . $paymentIntentId . PHP_EOL);
$this->logger->warning('Monto neto de la transferencia cancelada: ' . $jsonData['data']['object']['net_amount'] . PHP_EOL);
}
break;
case 'payout.failed':
$this->logger->info('Evento de transferencia fallida encontrado: ' . json_encode($jsonData) . PHP_EOL);
//imprimir detalles del fallo
$this->logger->info('Detalles del fallo: ' . json_encode($jsonData));
break;
case 'payment_intent.partially_funded':
$this->logger->info('Evento de pago parcialmente financiado encontrado: ' . json_encode($jsonData) . PHP_EOL);
//imprimir detalles del evento o pago
$this->logger->info('Detalles del evento: ' . json_encode($jsonData));
break;
case 'inbound_payment.payment_attempt':
//$this->logger->info('Evento de Pago de OXXO recibido: ' . json_encode($jsonData) . PHP_EOL);
break;
case 'cash_balance.funds_available':
$this->logger->info('Evento de Pago de fondos disponibles recibido: ' . json_encode($jsonData) . PHP_EOL);
break;
case 'payment_intent.succeeded':
$this->logger->info('Evento de pago exitoso (Stripe PI) recibido.');
if (isset($jsonData['data']['object'])) {
$this->pluginNotifierFacade->registerPaymentFromIntent($jsonData['data']['object']);
}
break;
case 'oxxo.retrieve':
$this->logger->info('Evento de recuperación de orden OXXO recibido');
$orderId = $jsonData['order_id'] ?? null;
if ($orderId) {
$status = $this->pluginOxxoNotifierFacade->getOxxoOrderStatus($orderId);
$this->logger->debug("Estado recuperado para Orden #$orderId: " . json_encode($status));
header('Content-Type: application/json');
echo json_encode($status);
} else {
$this->logger->warning('Solicitud de oxxo.retrieve sin order_id');
echo json_encode(['error' => 'missing_order_id']);
}
return; // Terminar ejecución aquí
break;
case 'oxxo.request':
$this->logger->info('Evento de solicitud de referencia de OXXO recibido (Async Flow)');
// 1. Obtener datos de Stripe (OXXO Reference, URL, etc.)
$stripeResult = $this->pluginOxxoNotifierFacade->createStripeReference($jsonData, $jsonData['amount'] ?? null);
if ($stripeResult['hasError'] ?? false) {
$this->logger->error('Error generando referencia Stripe: ' . json_encode($stripeResult));
header('Content-Type: application/json');
echo json_encode([
'oxxo_reference' => '',
'url' => '',
'error' => $stripeResult['data']['error'] ?? 'stripe_error',
'status' => 'failed'
]);
exit;
}
$oxxoData = $stripeResult['data'];
// 2. Crear Orden (Sync) para obtener ID
// Ahora pasamos $oxxoData que SI tiene clientID, clientFullName, etc.
$responseOxxo = $this->pluginOxxoNotifierFacade->createOxxoOrder($oxxoData);
$orderId = $responseOxxo['order_id'] ?? null;
// 3. Preparar respuesta inmediata (Pending)
$responseArray = [
'oxxo_reference' => $responseOxxo['oxxo_reference'] ?? '',
'url' => '', // No hay URL de imagen todavía
'stripe_url' => $responseOxxo['url'] ?? '',
'voucher_image_url' => '',
'order_id' => $orderId,
'clientFullName' => $responseOxxo['clientFullName'] ?? '',
'clientID' => $responseOxxo['clientID'] ?? '',
'amount' => $responseOxxo['amount'] ?? '',
'error' => $responseOxxo['error'] ?? '',
'status' => 'generating' // Estado esperado por el bot para iniciar polling
];
// 4. Enviar Respuesta y CERRAR Conexión
// Limpiamos buffers
while (ob_get_level() > 0) {
ob_end_clean();
}
header('Content-Type: application/json');
header('Connection: close');
ignore_user_abort(true); // Permitir que el script siga corriendo
echo json_encode($responseArray);
// Forzar envío al cliente
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} else {
// Fallback para servidores no FastCGI
header('Content-Length: ' . strlen(json_encode($responseArray)));
flush();
}
// 5. Trabajo en Pesado (Background)
if ($orderId) {
$this->logger->info("Iniciando generación background para orden #$orderId");
set_time_limit(180); // 3 minutos para Puppeteer
sleep(1); // Pequeña pausa para asegurar liberación del socket
$this->pluginOxxoNotifierFacade->generateOxxoVoucher($responseOxxo, true);
$this->logger->info("Generación background finalizada para orden #$orderId");
} else {
$this->logger->error("No se pudo iniciar background job: Falta order_id");
}
// Terminar proceso hijo
exit;
break;
}
}
return;
}
$notification = $this->notificationDataFactory->getObject($jsonData);
$this->logger->debug('Evento recibido: ' . $notification->eventName);
if ($notification->changeType === 'test') {
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
$config = $configManager->loadConfig();
$this->logger->info('Webhook test successful.');
return;
} else if ($notification->changeType === 'paperless.update') {
//imprimir el webhook json
$this->logger->info('Webhook de paperless update: ' . json_encode($jsonData) . PHP_EOL);
}
// if (!$notification->clientId) {
// $this->logger->warning('No client specified, cannot notify them.');
// return;
// }
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
$config = $configManager->loadConfig();
// the "exportFormat" key must be defined in plugin's manifest file, see the link above
try {
if ($notification->eventName === 'payment.add') {
$result = json_encode($notification);
$this->logger->debug('Notification encodificado en JSON:' . $result . PHP_EOL);
// [MOVED] Attempt to patch method ID only if it comes as "Stripe Credit Card" (catch-all)
//$this->pluginNotifierFacade->ensureStripePaymentAttribute($notification); (Removed from top)
$payment_method_id = $notification->paymentData['methodId'];
$payment_method = '';
switch ($payment_method_id) {
case '11721cdf-a498-48be-903e-daa67552e4f6':
$payment_method = 'Cheque';
if ($config['checkPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '6efe0fa8-36b2-4dd1-b049-427bffc7d369':
$payment_method = 'Efectivo';
if ($config['cashPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb':
$payment_method = 'Transferencia bancaria';
if ($config['bankTransferPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '78e84000-9b5b-44a4-8367-da43df86ce34':
$payment_method = 'PayPal';
if ($config['paypalPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d':
$payment_method = 'Tarjeta de crédito PayPal';
if ($config['creditCardPaypalPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae':
// [NEW] Logic to patch method ID based on metadata
$this->pluginNotifierFacade->ensureStripePaymentAttribute($notification);
// Check if Method ID was updated in memory to OXXO or Transfer
$patchedMethodId = $notification->paymentData['methodId'];
if ($patchedMethodId === 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb') {
// It is OXXO Pay
$payment_method = 'OXXO Pay';
if ($config['oxxoPayPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
} elseif ($patchedMethodId === '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb') {
// It is Bank Transfer
$payment_method = 'Transferencia bancaria';
if ($config['bankTransferPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
} else {
// Default: Credit Card Stripe
$payment_method = 'Tarjeta de crédito Stripe';
if ($config['creditCardStripePaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
}
break;
case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290':
$payment_method = 'Suscripción de Stripe (tarjeta de crédito)';
if ($config['stripeSubscriptionCreditCardPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '939f7701-00b7-4676-9b1e-17afb268c8ba':
$payment_method = 'Suscripción de PayPal';
if ($config['paypalSubscriptionPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '1c963e35-df24-444d-95d2-12592d5107e8':
$payment_method = 'MercadoPago';
if ($config['mercadopagoPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3':
$payment_method = 'Personalizado';
if ($config['customPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '72271b72-5c0a-45e2-94d1-cdf4d7cf10e2':
$payment_method = 'Cortesía';
if ($config['courtesyPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb':
$payment_method = 'OXXO Pay';
if ($config['oxxoPayPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb':
$payment_method = 'Transferencia bancaria';
if ($config['bankTransferPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
case '93814765-66a1-4c7d-a777-05c18fd6aab3':
$payment_method = 'Tarjeta de crédito/débito';
if ($config['creditDebitCardPaymentMethodId'] ?? false) {
$this->notifierFacade->verifyPaymentActionToDo($notification);
}
break;
default:
$payment_method = 'Desconocido';
break;
}
} else if ($notification->eventName === 'client.edit') {
$this->logger->info('Procesando evento client.edit para entityId: ' . ($jsonData['entityId'] ?? 'unknown'));
$this->logger->debug('Payload completo client.edit: ' . json_encode($jsonData));
try {
$clientID = $jsonData['entityId'];
if (!$this->pluginNotifierFacade) {
$this->logger->error('Falla crítica: pluginNotifierFacade no está inicializado.');
} else {
$this->logger->info('Llamando a updatePasswordAntenaIfNeeded para cliente: ' . $clientID);
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded((int)$clientID, $jsonData);
// $this->lcdf4d7cf10e2ogger->info('Llamada finalizada exitosamente.');
}
} catch (\Throwable $e) {
$this->logger->error('ERROR FATAL procesando client.edit: ' . $e->getMessage());
$this->logger->error('Trace: ' . $e->getTraceAsString());
}
//ejemplo de json_data: {"uuid":"17e043a7-03b5-4312-ab81-a7818124a77e","changeType":"edit","entity":"client","entityId":"158","eventName":"client.edit","extraData":{"entity":{"id":158,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":"2025-02-09T03:15:49-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":158,"userIdent":null,"previousIsp":null,"isLead":true,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}}
if (isset($jsonData['extraData']['entityBeforeEdit'], $jsonData['extraData']['entity'])) {
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'];
$entity = $jsonData['extraData']['entity'];
if (isset($entityBeforeEdit['isLead'], $entity['isLead'])) {
$isLeadBefore = $entityBeforeEdit['isLead'];
$isLeadAfter = $entity['isLead'];
if ($isLeadBefore === true && $isLeadAfter === false) {
$this->logger->info("El cliente $clientID cambió de potencial a cliente. Iniciando creación automática en Stripe...");
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLIENTE STRIPE', false);
}
}
if (isset($entity['tags'], $entityBeforeEdit['tags'])) {
$tags = $entity['tags'];
$tagsBefore = $entityBeforeEdit['tags'];
$clabeTagExistsBefore = false;
$stripeTagExistsBefore = false;
$passwordAntenaTagExistsBefore = false;
$clabeTagExists = false;
$stripeTagExists = false;
$passwordAntenaTagExists = false;
foreach ($tagsBefore as $tag) {
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExistsBefore = true;
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExistsBefore = true;
if ($tag['name'] === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExistsBefore = true;
}
foreach ($tags as $tag) {
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExists = true;
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExists = true;
if ($tag['name'] === 'OBTENER PASSWORD ANTENA') $passwordAntenaTagExists = true;
}
if ($clabeTagExists && !$clabeTagExistsBefore) {
$this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregó al cliente');
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLABE STRIPE', true);
}
if ($stripeTagExists && !$stripeTagExistsBefore) {
$this->logger->debug('La etiqueta CREAR CLIENTE STRIPE se agregó al cliente');
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLIENTE STRIPE', false);
}
if ($passwordAntenaTagExists && !$passwordAntenaTagExistsBefore) {
$this->logger->debug('La etiqueta OBTENER PASSWORD ANTENA se agregó al cliente');
$this->pluginNotifierFacade->processClientPasswordAntenna($clientID, $entity);
}
}
// Automatización: Sincronizar cambios de Nombre o Email con Stripe
$nameBefore = trim(($entityBeforeEdit['firstName'] ?? '') . ' ' . ($entityBeforeEdit['lastName'] ?? ''));
if (empty($nameBefore)) $nameBefore = $entityBeforeEdit['companyName'] ?? '';
$nameAfter = trim(($entity['firstName'] ?? '') . ' ' . ($entity['lastName'] ?? ''));
if (empty($nameAfter)) $nameAfter = $entity['companyName'] ?? '';
$emailBefore = null;
foreach ($entityBeforeEdit['contacts'] ?? [] as $contact) {
if ($contact['isBilling'] || $contact['isContact']) {
$emailBefore = $contact['email'] ?? null;
if ($emailBefore) break;
}
}
$emailAfter = null;
foreach ($entity['contacts'] ?? [] as $contact) {
if ($contact['isBilling'] || $contact['isContact']) {
$emailAfter = $contact['email'] ?? null;
if ($emailAfter) break;
}
}
if ($nameBefore !== $nameAfter || $emailBefore !== $emailAfter) {
$this->logger->info("Detectado cambio en datos básicos del cliente $clientID. Sincronizando con Stripe...");
$this->pluginNotifierFacade->syncStripeCustomerData((int)$clientID, $nameAfter, $emailAfter);
}
} else {
$this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData');
}
$this->notifierFacade->verifyClientActionToDo($notification);
} else if ($notification->eventName === 'client.add') {
$this->logger->debug('Se agregó un nuevo cliente');
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
} else if ($notification->eventName === 'service.edit') {
$this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification);
//ejemplo de json_data: {"uuid":"06d281ca-d78e-4f0a-a282-3a6b77d25da0","changeType":"edit","entity":"service","entityId":"155","eventName":"service.edit","extraData":{"entity":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]},"entityBeforeEdit":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]}}}
$clientID = $jsonData['extraData']['entity']['clientId'];
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
} else if ($notification->eventName === 'service.suspend') {
$this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification);
} else if ($notification->eventName === 'service.suspend_cancel') {
$this->logger->debug('Se reactivó el servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification);
} else if ($notification->eventName === 'service.postpone') {
$this->logger->debug('Se pospuso la suspención del servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification);
} else if ($notification->eventName === 'invoice.near_due') {
$this->logger->debug('Factura casi por vencer' . PHP_EOL);
$this->notifierFacade->notifyOverDue($notification);
} else if ($notification->eventName === 'invoice.overdue') {
$this->logger->debug('Factura vencida' . PHP_EOL);
$result = json_encode($notification);
$this->logger->debug('datos del notification para el invoice overdue:' . $result . PHP_EOL);
$this->notifierFacade->notifyOverDue($notification);
} else if ($notification->eventName === 'invoice.add') {
$this->logger->debug('Adición de Factura' . PHP_EOL);
$result = json_encode($notification);
$this->logger->debug('datos del notification para el invoice add:' . $result . PHP_EOL);
$accountBalance = $notification->clientData['accountBalance'];
//$invoiceAmountPaid = $notification->invoiceData['amountPaid'];
$this->logger->debug("Account Balance: " . $accountBalance . PHP_EOL);
// $this->logger->debug("Pago hecho con la factura: " . $invoiceAmountPaid . PHP_EOL);
$this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'invoice.edit') {
$this->logger->debug('Edición de Factura' . PHP_EOL);
$this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'invoice.add_draft') {
$this->logger->debug('Adición de borrador de Factura' . PHP_EOL);
$this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'invoice.draft_approved') {
$this->logger->debug('Aprobación de Factura' . PHP_EOL);
$this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'invoice.delete') {
$this->logger->debug('Eliminación de Factura' . PHP_EOL);
$this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'job.add') {
$this->logger->debug('Se ha agregado un nuevo trabajo');
$title = $jsonData['extraData']['entity']['title'] ?? '';
$title = '[NOTIFICACION-PENDIENTE]' . $title;
$responsePatch = $this->ucrmApi->patch('scheduling/jobs/' . $jsonData['entityId'], [
'title' => $title,
]);
$this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch));
} else if ($notification->eventName === 'job.edit') {
$this->logger->debug('Se actualiza un trabajo' . PHP_EOL);
// $this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
// Validar que 'extraData' existe y contiene las claves necesarias
if (
isset($jsonData['extraData']['entityBeforeEdit']) &&
isset($jsonData['extraData']['entity'])
) {
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'];
$entity = $jsonData['extraData']['entity'];
$this->logger->debug('Validando claves dentro de entityBeforeEdit y entity');
// Validar que 'assignedUserId' existe en ambas entidades
if (array_key_exists('assignedUserId', $entityBeforeEdit) && array_key_exists('assignedUserId', $entity)) {
// $this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo assignedUserId');
$assignedUserIdBefore = $entityBeforeEdit['assignedUserId'];
$assignedUserIdAfter = $entity['assignedUserId'];
$dateBefore = $entityBeforeEdit['date'];
$dateAfter = $entity['date'];
$statusBefore = $entityBeforeEdit['status'];
$statusAfter = $entity['status'];
$title = $entityBeforeEdit['title'];
$pendingPrefix = '[NOTIFICACION-PENDIENTE]'; // Prefijo para trabajos pendientes de notificación
$currentTitle = $entity['title'] ?? ''; // Obtener el título actual
//Valores de status y su significado: 0 = abierto, 1= En curso, 2 = Cerrado
if ($statusAfter == 1) {
$isNewActivation = ($statusBefore == 0 && $statusAfter == 1);
$isTechChange = ($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore && $dateBefore === $dateAfter);
$isReprogramming = ($assignedUserIdBefore != null && $assignedUserIdBefore === $assignedUserIdAfter && $dateBefore != $dateAfter);
$isBothChange = ($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore && $dateBefore != $dateAfter);
$isDeassignment = ($assignedUserIdBefore != null && $assignedUserIdAfter === null);
if ($isNewActivation) {
$this->logger->debug('Trabajo iniciado o asignado: Notificando...');
$this->notifierFacade->verifyJobActionToDo($jsonData, false, false);
} else if ($isTechChange) {
$this->logger->debug('Cambio de técnico sin reprogramación');
$this->notifierFacade->verifyJobActionToDo($jsonData, false, true);
} else if ($isReprogramming) {
$this->logger->debug('Reprogramación sin cambio de técnico');
$this->notifierFacade->verifyJobActionToDo($jsonData, true, false);
} else if ($isBothChange) {
$this->logger->debug('Reprogramación y cambio de técnico');
$this->notifierFacade->verifyJobActionToDo($jsonData, true, true);
} else if ($isDeassignment) {
$this->logger->debug('Técnico desasignado');
$this->notifierFacade->verifyJobActionToDo($jsonData, null, true);
} else {
$this->logger->debug('Edición de trabajo "En curso" sin cambios de fecha o técnico relevantes para notificación');
}
}
} else {
$this->logger->warning('El campo assignedUserId no existe en entityBeforeEdit o entity');
}
} else {
$this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData');
}
}
//$this->notifierFacade->update($notification);
} catch (TwilioException $exception) {
$this->logger->error($exception->getMessage());
} catch (\Exception $ex) {
$this->logger->error($ex->getMessage());
$this->logger->info($ex->getTraceAsString());
}
}
}