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()); } } }