diff --git a/manifest.json b/manifest.json index a3f1871b..7f8075b6 100755 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "displayName": "SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp", "description": "Este plugin sincroniza los clientes del sistema UISP CRM con los contactos de WhatsApp en CallBell, además procesa pagos de Stripe como las trasferencias bancarias y genera referencias de pago vía OXXO, además envía comprobantes de pago en formato imagen PNG o texto vía Whatsapp a los clientes", "url": "https://siip.mx/", - "version": "5.0.0", + "version": "4.5.0", "unmsVersionCompliancy": { "min": "2.1.0", "max": null diff --git a/public.php b/public.php index ec01e3b9..3b512286 100644 --- a/public.php +++ b/public.php @@ -1734,7 +1734,7 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador Menú Principal - -
-
- -

SIIP Notifications

-

Inicia sesión con tu cuenta de UISP

- -
- - -
- - - -
- - -
- - -
- - -
- - -
- -
-
- -
- -

Portal Administrativo de Pagos de STRIPE y Notificaciones WhatsApp

-

Administración de Notificaciones vía WhatsApp, Intenciones de pago con Stripe y Fichas de OXXO Pay

-
-
-
- - - - - - - -
-
- - - - - - - - - -
-
-
-

- Pagos OXXO -

-

🏪 Genera fichas de pago OXXO para que tus clientes paguen en tiendas de conveniencia

-
- - -
-
- -
- - - - - -
-
-
-
- - -
- 🏪 -

Selecciona un cliente para generar una ficha OXXO Pay

-
-
- - - - - - -
-
- - - - - - - - - - - -
- - - - - - \ No newline at end of file diff --git a/siip_full_structure/src/Facade/AbstractMessageNotifierFacade.php b/siip_full_structure/src/Facade/AbstractMessageNotifierFacade.php deleted file mode 100644 index 9734de8f..00000000 --- a/siip_full_structure/src/Facade/AbstractMessageNotifierFacade.php +++ /dev/null @@ -1,531 +0,0 @@ -logger = $logger; - $this->messageTextFactory = $messageTextFactory; - $this->clientPhoneNumber = $clientPhoneNumber; - - $config = PluginConfigManager::create()->loadConfig(); - $ipServer = $config['ipserver'] ?? 'localhost'; - $apiUrl = "https://$ipServer/crm/api/v1.0/"; - - $client = new Client([ - 'base_uri' => $apiUrl, - 'verify' => false, - ]); - - $this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? ''); - } - - 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']); - - $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); - - // 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, false); - } - - // 3. Gestión del Título / Prefijos - if ($isPending) { - if ($clientNotified || $reprogramming || $changeInstaller) { - $newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '', $title); - $this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]); - } else if (!$hasClientWhatsApp) { - $newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '[CLIENTE-SIN-WHATSAPP]', $title); - $this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]); - } - } else if ($isNoWhatsApp && ($clientNotified || $reprogramming || $changeInstaller)) { - $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); - if ($contact) $api->patchWhatsapp($contact, $notificationData); - } - } else { - if ($api->sendPaymentNotificationWhatsApp($phone, $notificationData)) { - $contact = json_decode($api->getContactWhatsapp($phone), true); - if ($contact) $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 - { - $this->logger->debug("onlyUpdate: Iniciando actualización para teléfono: $phoneToUpdate"); - $config = PluginConfigManager::create()->loadConfig(); - $api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']); - $phone = $this->validarNumeroTelefono($phoneToUpdate); - $this->logger->debug("onlyUpdate: Teléfono validado: $phone"); - $contact = json_decode($api->getContactWhatsapp($phone), true); - $this->logger->debug("onlyUpdate: Contacto obtenido de CallBell: " . json_encode($contact)); - if ($contact) { - $this->logger->info("onlyUpdate: Ejecutando patchWhatsapp para teléfono: $phone"); - $api->patchWhatsapp($contact, $notificationData); - } else { - $this->logger->warning("onlyUpdate: No se encontró contacto en CallBell para teléfono: $phone"); - } - } - - 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(); - $ipServer = $config['ipserver'] ?? ''; - $crm = new Client(['base_uri' => "https://{$ipServer}/crm/api/v1.0/", 'verify' => false]); - - try { - // OPT: Lazy Check - Si ya tiene pass válido en CRM, no hace falta procesar nada - $respClient = $crm->get("clients/$clientId", ['headers' => ['X-Auth-Token' => $config['apitoken']]]); - $clientData = json_decode($respClient->getBody()->getContents(), true); - $passCRM = ''; - if (isset($clientData['attributes'])) { - foreach ($clientData['attributes'] as $attr) { - if ($attr['key'] === 'passwordAntenaCliente') { - $passCRM = $attr['value'] ?? ''; - break; - } - } - } - - // Si el campo no está vacío y no tiene advertencias, usamos el actual para ahorrar recursos - if (!empty($passCRM) && strpos($passCRM, '⚠️') === false) { - return $passCRM; - } - - // 1. Obtener los servicios del cliente - $respSvc = $crm->get('clients/services?clientId=' . $clientId, [ - 'headers' => ['X-Auth-Token' => $config['apitoken']] - ]); - $svcs = json_decode($respSvc->getBody()->getContents(), true); - - if (empty($svcs)) { - $msg = '⚠️ Cliente sin servicios/antenas'; - $this->syncPasswordWithCrm((int)$clientId, $msg); - return $msg; - } - - $unms = new Client(['base_uri' => "https://{$ipServer}/nms/api/v2.1/", 'verify' => false]); - $allServicePasswords = []; - $isTestEnv = ($ipServer === '172.16.5.134' || $ipServer === 'pruebas.internet.mx' || $ipServer === 'venus.siip.mx'); - - $numServices = count($svcs); - foreach ($svcs as $index => $svc) { - $label = ($numServices > 1) ? "Servicio " . ($index + 1) . ":" : ""; - $siteId = $svc['unmsClientSiteId'] ?? null; - $passwordValue = ""; - - if (!$siteId) { - $passwordValue = "⚠️ Sin sitio"; - } else { - if ($isTestEnv) { - // Lógica de bypass: intentar recuperar de la cadena existente - $foundInCRM = false; - if (!empty($passCRM)) { - if ($numServices > 1) { - if (preg_match('/Servicio ' . ($index + 1) . ':\s*([^⚠️\s]+)/', $passCRM, $matches)) { - $passwordValue = $matches[1]; - $foundInCRM = true; - } - } else { - // Caso de un solo servicio: si no tiene advertencias ni etiquetas, la tomamos a secas - if (strpos($passCRM, '⚠️') === false && strpos($passCRM, 'Servicio') === false) { - $passwordValue = trim($passCRM); - $foundInCRM = true; - } - } - } - - if (!$foundInCRM) { - $passwordValue = $this->generateStrongPassword(16); - } - } else { - // Lógica de producción - try { - $respDev = $unms->get("devices?siteId=$siteId", [ - 'headers' => ['X-Auth-Token' => $config['unmsApiToken']] - ]); - $devs = json_decode($respDev->getBody()->getContents(), true); - - if (empty($devs)) { - $passwordValue = "⚠️ Sin antena"; - } else { - $passVault = null; - $firstDeviceId = null; - foreach ($devs as $dev) { - $deviceId = $dev['identification']['id'] ?? null; - if (!$deviceId) continue; - if (!$firstDeviceId) $firstDeviceId = $deviceId; - try { - $respVault = $unms->get("vault/$deviceId/credentials", [ - 'headers' => ['X-Auth-Token' => $config['unmsApiToken']] - ]); - $vault = json_decode($respVault->getBody()->getContents(), true); - if (isset($vault['credentials'][0]['password'])) { - $passVault = $vault['credentials'][0]['password']; - break; - } - } catch (\Exception $e) { - continue; - } - } - - if ($passVault) { - $passwordValue = $passVault; - } else if ($firstDeviceId) { - // Regenerar - $newPass = $this->generateStrongPassword(16); - try { - $unms->post("vault/$firstDeviceId/credentials/regenerate", [ - 'headers' => ['X-Auth-Token' => $config['unmsApiToken']], - 'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]] - ]); - $passwordValue = $newPass; - } catch (\Exception $e) { - $passwordValue = $newPass; - } - } else { - $passwordValue = "⚠️ Sin antena"; - } - } - } catch (\Exception $e) { - $passwordValue = "⚠️ Error API"; - } - } - } - $allServicePasswords[] = trim("$label $passwordValue"); - } - - $finalValue = implode(' ', $allServicePasswords); - - // Evitar sincronización redundante - if ($finalValue === $passCRM) { - return $finalValue; - } - - $this->syncPasswordWithCrm((int)$clientId, $finalValue); - return $finalValue; - } catch (\Exception $e) { - $this->logger->error("Error en getVaultCredentialsByClientId: " . $e->getMessage()); - return 'Error: ' . $e->getMessage(); - } - } - - private function syncPasswordWithCrm(int $clientId, string $passVault): void - { - $config = PluginConfigManager::create()->loadConfig(); - $crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]); - try { - $respClient = $crm->get("clients/$clientId", [ - 'headers' => ['X-Auth-Token' => $config['apitoken']] - ]); - $clientData = json_decode($respClient->getBody()->getContents(), true); - $passCRM = ''; - $attributeId = 17; - if (isset($clientData['attributes'])) { - foreach ($clientData['attributes'] as $attr) { - if ($attr['key'] === 'passwordAntenaCliente') { - $passCRM = $attr['value'] ?? ''; - $attributeId = $attr['customAttributeId']; - break; - } - } - } - if (empty($passCRM) || $passCRM !== $passVault) { - $this->logger->info("Sincronizando pass CRM cliente $clientId."); - $this->patchClientCustomAttribute($clientId, (int)$attributeId, $passVault); - } - } catch (\Exception $e) { - $this->logger->warning("Fallo sincronización pass CRM: " . $e->getMessage()); - } - } - - protected function generateStrongPassword(int $length = 16): string - { - $lower = 'abcdefghijkmnopqrstuvwxyz'; // Eliminamos 'l' - $upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Eliminamos 'I', 'O' - $digits = '23456789'; // Eliminamos '1', '0' - $symbols = '@#'; // Solo símbolos amigables para impresoras térmicas - - $all = $lower . $upper . $digits . $symbols; - $pwChars = []; - - // Asegurar que tenga al menos uno de cada tipo si es posible - $pwChars[] = $lower[random_int(0, strlen($lower) - 1)]; - $pwChars[] = $upper[random_int(0, strlen($upper) - 1)]; - $pwChars[] = $digits[random_int(0, strlen($digits) - 1)]; - $pwChars[] = $symbols[random_int(0, strlen($symbols) - 1)]; - - for ($i = count($pwChars); $i < $length; $i++) { - $pwChars[] = $all[random_int(0, strlen($all) - 1)]; - } - - // Mezclar Fisher-Yates - $n = count($pwChars); - for ($i = $n - 1; $i > 0; $i--) { - $j = random_int(0, $i); - $tmp = $pwChars[$i]; - $pwChars[$i] = $pwChars[$j]; - $pwChars[$j] = $tmp; - } - - return implode('', $pwChars); - } - - protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool - { - $config = PluginConfigManager::create()->loadConfig(); - $crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]); - try { - $crm->patch("clients/$clientId", [ - 'headers' => ['X-Auth-Token' => $config['apitoken']], - 'json' => [ - "attributes" => [['value' => $value, 'customAttributeId' => $attributeId]] - ] - ]); - return true; - } catch (\Exception $e) { - $this->logger->error("Error patching custom attribute for client $clientId: " . $e->getMessage()); - return false; - } - } - - protected function comparePasswords(?string $crm, ?string $vault): string - { - if ($vault && strpos($vault, 'Error') !== 0) return $vault; - if ($crm && strpos($crm, 'Error') !== 0) return $crm; - 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; -} diff --git a/siip_full_structure/src/Facade/ClientCallBellAPI.php b/siip_full_structure/src/Facade/ClientCallBellAPI.php deleted file mode 100644 index 140751ac..00000000 --- a/siip_full_structure/src/Facade/ClientCallBellAPI.php +++ /dev/null @@ -1,1460 +0,0 @@ -UCRMAPIToken = $UCRMAPIToken; - $this->IPServer = $IPServer; - $this->CallBellAPIToken = $CallBellAPIToken; - } - - public function updateContact($client_uuid) {} - - public function printPrueba($clientWhatsAppNumber, $notificationData) - { - $log = PluginLogManager::create(); //Initialize Logger - $log->appendLog("Ejecutando metodo print prueba: " . $clientWhatsAppNumber); - // $logger = new Logger(); - // $logger->debug('Ejecutando metodo print prueba con Logger'); - } - - public function sendMessageWhatsApp($clientWhatsAppNumber, $notificationData) - { - - $log = PluginLogManager::create(); //Initialize Logger - $log->appendLog("Entrando al metodo sendMessageWhatsApp" . PHP_EOL); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer g8thcZkXGd3xBj2g3TtYNYFMH1fuesbJ.b6a940ea7d78cf6c9e42f067b21c8ddf96e9fa2a9e307bfd0c7c7c4d7fa38f79', - 'Content-Type: application/json', - ]); - - // $jsonString = json_encode($notificationData); - // $log->appendLog($jsonString); - - $campo1 = sprintf("😀 *%s %s*", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - $log->appendLog("Valor del campo1: " . $campo1); - $estado_service = ($notificationData->clientData['hasSuspendedService']) ? 'Suspendido 🔴' : 'Activo 🟢'; - $log->appendLog("Valor del estado_service: " . $estado_service); - $domicilio = ($notificationData->clientData['fullAddress'] == null) ? 'Sin domicilio' : $notificationData->clientData['fullAddress']; - $log->appendLog("Valor del domicilio: " . $domicilio); - $campo2 = sprintf("📡 Su servicio está: %s 📍Su dirección es: *%s* ", $estado_service, $domicilio); - $log->appendLog("Valor del campo2 " . $campo2); - - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - curl_close($ch); - } - - public function sendJobNotificationWhatsAppToClient($clientWhatsAppNumber, $jobNotificationData, $reprogramming, $changeInstaller): bool - { - - $log = PluginLogManager::create(); //Initialize Logger - $log->appendLog("Enviando mensaje de trabajo para el cliente" . PHP_EOL); - $jsonJobNotificationData = json_encode($jobNotificationData, true); - $log->appendLog("Datos de la notificación de trabajo: " . $jsonJobNotificationData . PHP_EOL); - - // --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! --- - $reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - $changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - // ----------------------------------------------------------- - - // Puedes volver a loggear los valores para confirmar la conversión (opcional) - $log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - $campo1 = '👤 ' . sprintf(' *%s*', $jobNotificationData['clientFullName']); - if ($changeInstaller && !$reprogramming) { - $campo2 = sprintf('se ha hecho un cambio de técnico 👷🏻‍♂️🔄 para su visita con el folio *#️⃣%s*', $jobNotificationData['jobId']); - } else { - $campo2 = sprintf('*#️⃣%s*', $jobNotificationData['jobId']); - } - $campo3 = '🗓️ ' . sprintf('%s', $jobNotificationData['date']); - if ($changeInstaller && $reprogramming) { - $campo4 = 'Además se ha hecho un cambio de técnico por el siguiente 👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']); - } else { - $campo4 = '👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']); - } - - $campo5 = "asegúrese de que alguien esté presente en el domicilio 🏠 para permitir el acceso y confirme su disponibilidad."; - - $log->appendLog("DEBUG: Valores antes de la estructura IF - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); - - if ($reprogramming && !$changeInstaller) { - //{{1}}, se ha reprogramado su visita técnica con el folio {{2}} - - $campo3 = '🗓️➡️ ' . sprintf('%s', $jobNotificationData['date']); - $campo1_combinado = "Estimado cliente $campo1 se ha reprogramado su visita técnica con folio $campo2"; - - // Case: true, false - //Enviar notificación de reprogramación al cliente - $log->appendLog("Enviando notificación de reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"715eed9d6f2d4d90853f25e296202e00\",\n \"optin_contact\": true\n }"; - } else if (!$reprogramming && $changeInstaller) { - // Case: false, true - //Enviar notificación de cambio de instalador - $log->appendLog("Enviando notificación de cambio de instalador al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"d684e6fa2ba24593a86c98be1815831d\",\n \"optin_contact\": true\n }"; - } else if (!$reprogramming && !$changeInstaller) { // <--- Ahora este else if está correctamente encadenado - // Case: false, false - //Enviar notificación normal de visita técnica al cliente - $log->appendLog("Enviando notificación normal de visita técnica al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"07cfbc6e394044608485b530a27177d0\",\n \"optin_contact\": true\n }"; - } else if ($reprogramming && $changeInstaller) { // <--- Ahora este else if está correctamente encadenado - // Case: true, true - //Enviar notificación de cambio de instalador y reprogramación al cliente - $log->appendLog("Enviando notificación de cambio de instalador y reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"145885d15323414f978f1e3f249c2ae1\",\n \"optin_contact\": true\n }"; - } else { - // Case: true, true (la única combinación restante con booleanos) - $log->appendLog("No se encontró una opción válida para enviar la notificación (reprogramming y changeInstaller son true)." . PHP_EOL); - // Decide qué hacer aquí, ¿Quizás devolver false? ¿O manejar esta combinación? - return false; // O el manejo adecuado para true/true - } - - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - curl_close($ch); - - // Validar la respuesta de Callbell - $jsonResponse = json_decode($response, true); - - if (json_last_error() === JSON_ERROR_NONE) { - // Evaluar si contiene "message" con "status": "enqueued" - if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { - $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); - return true; - } - - // Evaluar si contiene "error" - if (isset($jsonResponse['error'])) { - $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); - return false; - } - } else { - // Manejo de error en caso de que la respuesta no sea un JSON válido - $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); - return false; - } - - // Valor de retorno predeterminado en caso de que ninguna condición se cumpla - $log->appendLog("Ruta no prevista en la función." . PHP_EOL); - return false; - } - - public function sendJobNotificationWhatsAppToInstaller($installerWhatsAppNumber, $jobInstallerNotificationData, $reprogramming, $changeInstaller): bool - { - - $log = PluginLogManager::create(); //Initialize Logger - $log->appendLog("Enviando mensaje de tarea al instalador" . PHP_EOL); - $jsonJobNotificationData = json_encode($jobInstallerNotificationData, true); - $log->appendLog("Datos de la notificación de tarea: " . $jsonJobNotificationData . PHP_EOL); - - // --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! --- - $reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - $changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - // ----------------------------------------------------------- - - // Puedes volver a loggear los valores para confirmar la conversión (opcional) - $log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - - if (!$reprogramming && $changeInstaller) { - $campo1 = $jobInstallerNotificationData['installerName']; - $campo2 = $jobInstallerNotificationData['subjectOfChange']; - $campo3 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']); - $campo4 = sprintf('*👤%s* ', $jobInstallerNotificationData['clientFullName']); - $campo5 = $jobInstallerNotificationData['additionalChangeData']; - } else { - $campo1 = '👷🏻‍♂️ ' . $jobInstallerNotificationData['installerName']; - $campo2 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']); - $campo3 = '👤 *' . $jobInstallerNotificationData['clientFullName'] . '*'; - // $campo4 = $jobInstallerNotificationData['clientAddress']; - $campo4 = '☎️ ' . $jobInstallerNotificationData['clientWhatsApp']; - $campo5 = '🗓️ ' . $jobInstallerNotificationData['date']; - // $campo6 = '🛠️ ' . $jobInstallerNotificationData['jobDescription']; - // $campo7 = '📌 ' . $jobInstallerNotificationData['gmapsLocation']; - // $campo8 = '🔐 ' . $passwordAntenaCliente; - } - - if ($reprogramming && !$changeInstaller) { - $jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']); - $clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']); - $date = sprintf("🗓️➡️ %s", $jobInstallerNotificationData['date']); - $installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']); - - $campo1_combinado = "$installerName se reprogramó una tarea con el folio $jobId para el cliente $clientFullName, la nueva fecha será el $date"; - $campo2 = $jobInstallerNotificationData['clientWhatsApp']; - $campo3 = $jobInstallerNotificationData['gmapsLocation']; - $campo4 = $jobInstallerNotificationData['passwordAntenaCliente']; - - //Enviar notificación de reprogramación - $log->appendLog("Enviando notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }"; - } else if (!$reprogramming && $changeInstaller) { - //Enviar notificación de cambio de instalador - $log->appendLog("Enviando notificación de cambio de instalador al instalador anterior (desasignación), valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }"; - } else if ($reprogramming && $changeInstaller) { - //Enviar notificación de cambio de instalador - $log->appendLog("Enviando notificación de cambio de instalador al instalador y notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }"; - } else if (!$reprogramming && !$changeInstaller) { - - //combinar el campo3, campo4, campo5, campo6, campo7 y campo8 en un solo campo con saltos de línea - $jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']); - $clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']); - $date = sprintf("🗓️ %s", $jobInstallerNotificationData['date']); - $installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']); - - $campo1_combinado = "$installerName se te ha asignado una tarea con folio $jobId, del cliente $clientFullName, para el $date"; - $campo2 = $jobInstallerNotificationData['clientWhatsApp']; - $campo3 = $jobInstallerNotificationData['gmapsLocation']; - $campo4 = $jobInstallerNotificationData['passwordAntenaCliente']; - - $log->appendLog("Enviando notificación normal de tarea al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); - $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }"; - } - - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - curl_close($ch); - - - - - // Validar la respuesta de Callbell - $jsonResponse = json_decode($response, true); - - if (json_last_error() === JSON_ERROR_NONE) { - // Evaluar si contiene "message" con "status": "enqueued" - if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { - $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); - return true; - } - - // Evaluar si contiene "error" - if (isset($jsonResponse['error'])) { - $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); - return false; - } - } else { - // Manejo de error en caso de que la respuesta no sea un JSON válido - $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); - return false; - } - - // Valor de retorno predeterminado en caso de que ninguna condición se cumpla - $log->appendLog("Ruta no prevista en la función." . PHP_EOL); - return false; - } - - public function sendPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool - { - - $log = PluginLogManager::create(); //Initialize Logger - - $log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL); - - // URL base de la API - $baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; - // Token de autenticación - $token = $this->UCRMAPIToken; - //Path base del comprobante de pago - $pdf_payment_path = ''; - - $config = PluginConfigManager::create()->loadConfig(); - $gClient = new Client(['base_uri' => "https://{$this->IPServer}/crm/api/v1.0/", 'verify' => false]); - $this->ucrmApi = new UcrmApi($gClient, $this->UCRMAPIToken ?? ''); - $payment_id = $notificationData->paymentData['id']; - $payment_amount = '$' . $notificationData->paymentData['amount']; - - // We already have the payment data in $notificationData, no need to fetch 'payments/' again to get the ID. - // However, if we need the 'note' field for the overlay which might not be in notificationData (depending on richness), - // we should try to use what we have or fetch SPECIFICALLY this payment. - - // Let's verify if 'note' is in paymentData. Usually UCRM webhook payload has it. - // But to be safe and consistent with previous logic, if we need 'note', we can fetch THIS payment. - - // $payments = $this->ucrmApi->get('payments/'.$payment_id); // This would be better if we need details. - - // The previous code did: - // $payments = $this->ucrmApi->get('payments/', ['clientId' => ..., 'limit' => 1 ...]); - // $payment_id = $payments[0]['id']; <-- THIS WAS THE BUG. Always getting latest. - - // Fix: Use the ID passed in notificationData. - - // If we need the NOTE for the Overlay (OXXO/Transfer check later in code), we should ensure we have it. - // $notificationData->paymentData usually contains 'note'. - $note = $notificationData->paymentData['note'] ?? ''; - - // Let's keep $payments array structure if downstream code expects it, OR refactor downstream. - // Downstream uses $payments[0]['note']. - - // Let's just mock $payments[0] with our data OR fetch the correct single payment. - // Fetching single payment is safer to ensure we have the Note. - try { - $fetchedPayment = $this->ucrmApi->get('payments/' . $payment_id); - $payments = [$fetchedPayment]; - } catch (\Exception $e) { - // Fallback if fetch fails (unlikely if ID is valid) - $payments = [$notificationData->paymentData]; - } - - // $payment_id is already set above. - - $payment_amount = '$' . $payments[0]['amount']; - //$saldo = '$' . $notificationData->clientData['accountBalance']; - $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - - $accountBalance = $notificationData->clientData['accountBalance']; - $saldoTexto = ''; - - if ($accountBalance < 0) { - // Si el saldo es negativo, quitar el signo y poner "Pendientes" - $saldoTexto = '$' . abs($accountBalance) . ' pendientes'; - } elseif ($accountBalance > 0) { - // Si el saldo es positivo, poner "a favor" - $saldoTexto = '$' . $accountBalance . ' a favor'; - } else { - // Si el saldo es 0, poner "está al corriente" - $saldoTexto = '$' . $accountBalance . ' está al corriente'; - } - - // Configuración del cliente GuzzleHttp - $clientGuzzleHttp = new Client([ - 'base_uri' => $baseUri, - 'headers' => [ - 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización - 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON - ], - 'verify' => false, - ]); - - try { - // Hacer la solicitud GET para obtener el PDF - $response = $clientGuzzleHttp->request('GET', "payments/$payment_id/pdf"); - - // Leer el contenido del PDF de la respuesta - $contenidoArchivo = $response->getBody()->getContents(); - - // Construir el nombre del archivo PDF basado en el cliente - $unwanted_array = array( 'Š'=>'S', 'š'=>'s', 'Ž'=>'Z', 'ž'=>'z', 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E', - 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', - 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', - 'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', - 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ü'=>'u', 'ý'=>'y', 'þ'=>'b', 'ÿ'=>'y' ); - $clean_name = strtr($nombre_cliente, $unwanted_array); - $clean_name = preg_replace('/[^A-Za-z0-9_\-]/', '', str_replace(' ', '_', $clean_name)); - - $fileNameComprobante = 'Comprobante_' . $clean_name . '.pdf'; - $rutaArchivo = __DIR__ . '/../../comprobantes/' . $fileNameComprobante; - - // Guardar el contenido del PDF en un archivo local - if (file_put_contents($rutaArchivo, $contenidoArchivo) !== false) { - $log->appendLog("El archivo PDF se ha descargado y guardado correctamente en: $rutaArchivo" . PHP_EOL); - } else { - $log->appendLog("Error al guardar el archivo PDF en la ruta: $rutaArchivo" . PHP_EOL); - return false; - } - - if (!file_exists($rutaArchivo)) { - $log->appendLog("El archivo PDF no existe: $rutaArchivo" . PHP_EOL); - return false; - } - - if (filesize($rutaArchivo) === 0) { - $log->appendLog("El archivo PDF está vacío: $rutaArchivo" . PHP_EOL); - return false; - } - - $log->appendLog("El archivo PDF es válido y tiene contenido: $rutaArchivo" . PHP_EOL); - $rutaImagen = __DIR__ . '/../../comprobantes/' . str_replace('.pdf', '.png', $fileNameComprobante); - } catch (\Exception $e) { - $log->appendLog("Error al manejar el comprobante de pago: " . $e->getMessage() . PHP_EOL); - return false; - } - - // --- NUEVO FLUJO: Microservicio PDF->IMG + MinIO --- - $url_file = ''; - $rutaImagen = str_replace('.pdf', '.jpg', $rutaArchivo); // Usamos .jpg para la salida del microservicio - - try { - // 1. Instanciar Logger y MinioService - $loggerService = new Logger(); // Servicio de Logging propio del namespace - $minioService = new MinioStorageService($loggerService); - - // 2. Configurar Microservicio - $ipMicroservice = $config['ipMicroservice'] ?? 'pdf-cropper-service'; - $portMicroservice = $config['portMicroservice'] ?? '8000'; - $microserviceUrl = "http://{$ipMicroservice}:{$portMicroservice}/process"; - - $log->appendLog("Procesando PDF con microservicio: $microserviceUrl" . PHP_EOL); - - // 2. DETECTAR METODO DE PAGO (Overlay) - $overlayText = ''; - $note = $payments[0]['note'] ?? ''; - - // Si la nota dice OXXO o Transferencia, preparamos el reemplazo - if (stripos($note, 'OXXO') !== false) { - $overlayText = 'OXXO Pay'; - } elseif (stripos($note, 'Transferencia') !== false) { - $overlayText = 'Transferencia Bancaria'; - } - - // Coordenadas APROXIMADAS para el template default de UCRM (Ajustar si es necesario) - // Asumiendo PDF a 200dpi (ancho ~1600px). - // "Método" suele estar alineado a la izquierda, valor a la derecha. - // X=500, Y=650 (ejemplo). W=800, H=40. - $overlayX = 450; - $overlayY = 665; // Ajustar probando - $overlayW = 1000; - $overlayH = 50; - - // 3. Llamar al Microservicio (PDF -> JPG) - $multipartData = [ - [ - 'name' => 'file', - 'contents' => fopen($rutaArchivo, 'r'), - 'filename' => basename($rutaArchivo) - ], - [ - 'name' => 'mode', - 'contents' => 'full' - ] - ]; - - if ($overlayText) { - $log->appendLog("Aplicando Overlay: $overlayText en ($overlayX, $overlayY)" . PHP_EOL); - $multipartData[] = ['name' => 'overlay_text', 'contents' => $overlayText]; - $multipartData[] = ['name' => 'overlay_x', 'contents' => (string)$overlayX]; - $multipartData[] = ['name' => 'overlay_y', 'contents' => (string)$overlayY]; - $multipartData[] = ['name' => 'overlay_w', 'contents' => (string)$overlayW]; - $multipartData[] = ['name' => 'overlay_h', 'contents' => (string)$overlayH]; - } - - $guzzleParams = [ - 'multipart' => $multipartData, - 'timeout' => 30 - ]; - - $httpClient = new Client(); - $responseMs = $httpClient->post($microserviceUrl, $guzzleParams); - - if ($responseMs->getStatusCode() === 200) { - $jpgContent = $responseMs->getBody()->getContents(); - if (file_put_contents($rutaImagen, $jpgContent) !== false) { - $log->appendLog("Imagen generada por microservicio guardada en: $rutaImagen" . PHP_EOL); - - // 4. Subir a MinIO - $fileNameComprobanteImage = str_replace('.jpg', '_' . time() . '.jpg', basename($rutaImagen)); - $url_file = $minioService->uploadFile($rutaImagen, $fileNameComprobanteImage); - - if ($url_file) { - $log->appendLog("Imagen subida a MinIO: $url_file" . PHP_EOL); - - // 5. LIMPIEZA AUTOMÁTICA (Consistencia con req. de usuario) - if (file_exists($rutaArchivo)) { - unlink($rutaArchivo); - //$log->appendLog("Archivo PDF temporal eliminado." . PHP_EOL); - } - if (file_exists($rutaImagen)) { - unlink($rutaImagen); - //$log->appendLog("Archivo JPG temporal eliminado." . PHP_EOL); - } - $log->appendLog("Archivos temporales (PDF/JPG) eliminados tras subida exitosa." . PHP_EOL); - } else { - $log->appendLog("Error: Falló la subida a MinIO." . PHP_EOL); - return false; - } - } else { - $log->appendLog("Error guardando imagen del microservicio." . PHP_EOL); - return false; - } - } else { - $log->appendLog("Error microservicio PDF: HTTP " . $responseMs->getStatusCode() . PHP_EOL); - return false; - } - } catch (\Exception $e) { - $log->appendLog("Excepción en flujo Microservicio/MinIO: " . $e->getMessage() . PHP_EOL); - return false; - } - - // Legacy FTP eliminado - // $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobanteImage); - - $log->appendLog("Proceso de obtención de imagen finalizado." . PHP_EOL); - - //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldoTexto\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - //Enviar notificación adicional si es transferencia - // if($transferNotify){ - // curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_uuid\": \"41783c020cd84881bc27cb9e7e1da348\",\n \"optin_contact\": true\n }"); - // $response2 = curl_exec($ch); - // $log->appendLog("Response del CallBell aviso adicional: " . $response2); - // } - - curl_close($ch); - $this->deleteFilesExceptLastHundred(); - //return $fileNameComprobante; - - // Validar la respuesta de Callbell - $jsonResponse = json_decode($response, true); - - if (json_last_error() === JSON_ERROR_NONE) { - // Evaluar si contiene "message" con "status": "enqueued" - if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { - $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); - return true; - } - - // Evaluar si contiene "error" - if (isset($jsonResponse['error'])) { - $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); - return false; - } - } else { - // Manejo de error en caso de que la respuesta no sea un JSON válido - $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); - return false; - } - - // Valor de retorno predeterminado en caso de que ninguna condición se cumpla - $log->appendLog("Ruta no prevista en la función." . PHP_EOL); - return false; - } - public function sendTextPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool - { - - $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - $folio = $notificationData->paymentData['id']; - $client_id = $notificationData->clientData['id']; - $fecha_pago = ''; - $cantidad_pagada = $notificationData->paymentData['amount']; - $metodo_pago = ''; - //$invoice_id= null; - $total_factura = null; - $pago_factura = $notificationData->paymentData['amount']; - // $saldoTexto= null; - $creditoPorPago = $notificationData->paymentData['creditAmount']; - $texto_credito = null; - $creditoClienteBase = $notificationData->clientData['accountCredit']; - - switch ($notificationData->paymentData['methodId']) { - case '11721cdf-a498-48be-903e-daa67552e4f6': - $metodo_pago = 'Cheque 📄'; - break; - case '6efe0fa8-36b2-4dd1-b049-427bffc7d369': - $metodo_pago = 'Efectivo 💵'; - break; - case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb': - $metodo_pago = 'Transferencia bancaria 📱🏦'; - break; - case '78e84000-9b5b-44a4-8367-da43df86ce34': - $metodo_pago = 'PayPal 🌐💳'; - break; - case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d': - $metodo_pago = 'Tarjeta de crédito PayPal 💳'; - break; - case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae': - $metodo_pago = 'Tarjeta de crédito Stripe 💳'; - break; - case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290': - $metodo_pago = 'Suscripción de Stripe (tarjeta de crédito) 🌐💳'; - break; - case '939f7701-00b7-4676-9b1e-17afb268c8ba': - $metodo_pago = 'Suscripción de PayPal 🌐💳'; - break; - case '1c963e35-df24-444d-95d2-12592d5107e8': - $metodo_pago = 'MercadoPago 🌐💳'; - break; - case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3': - $metodo_pago = 'Personalizado 📝💸'; - break; - default: - $metodo_pago = 'Desconocido, revisar metodos de pago no contemplados'; - break; - } - - $log = PluginLogManager::create(); //Initialize Logger - - $log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL); - - // URL base de la API - $baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; - // Token de autenticación - $token = $this->UCRMAPIToken; - //Path base del comprobante de pago - $pdf_payment_path = ''; - - $config = PluginConfigManager::create()->loadConfig(); - $gClient = new Client(['base_uri' => "https://{$this->IPServer}/crm/api/v1.0/", 'verify' => false]); - $this->ucrmApi = new UcrmApi($gClient, $this->UCRMAPIToken ?? ''); - $payments = $this->ucrmApi->get( - 'payments/', - [ - 'clientId' => $notificationData->clientData['id'], - 'limit' => 1, - 'direction' => 'DESC', - - ] - ); - - //$datos_payment_array = json_decode($response->getBody()->getContents(), true); - $datos_paymentJsonText = json_encode($payments, true); - $log->appendLog("Datos traidos con payment api: " . $datos_paymentJsonText . PHP_EOL); - - // $log->appendLog("Check 1" . PHP_EOL); - $paymentDate = new DateTime($fecha_pago); - - // Restar 6 horas - $paymentDate->modify('-6 hours'); - - // Formatear la fecha como "d/m/Y g:ia" (día/mes/año hora:minutos am/pm) - $fecha_pago = $paymentDate->format('d/m/Y g:ia'); - - $accountBalance = $notificationData->clientData['accountBalance']; - $saldoTexto = ''; - - if ($accountBalance < 0) { - // Si el saldo es negativo, quitar el signo y poner "Pendientes" - $saldoTexto = '$' . abs($accountBalance) . ' pendientes'; - } elseif ($accountBalance > 0) { - // Si el saldo es positivo, poner "a favor" - $saldoTexto = '$' . $accountBalance . ' a favor'; - } else { - // Si el saldo es 0, poner "está al corriente" - $saldoTexto = '$' . $accountBalance . ' está al corriente'; - } - - $log->appendLog("Datos client data credit amount: " . $notificationData->paymentData['creditAmount'] . PHP_EOL); - - $cantidad_pagadaFormateada = '$' . number_format($cantidad_pagada, 2, ',', '.') . ' MXN'; - $creditoPorPagoFormateado = '$' . number_format($creditoPorPago, 2, ',', '.') . ' MXN'; - - if ($creditoClienteBase > 0 && empty($notificationData->paymentData['paymentCovers'])) { - - $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; - $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); - - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"4ac9dc060cf746b6ad7f2e8acad355e0\",\n \"optin_contact\": true\n }"; - } else { - - if ($creditoPorPago > 0) { - $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; - $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); - } else { - $texto_credito = '_________________________'; - } - - // Configuración del cliente GuzzleHttp - $clientGuzzleHttp = new Client([ - 'base_uri' => $baseUri, - 'headers' => [ - 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización - 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON - ], - 'verify' => false, - ]); - - $log->appendLog("Verificar paymentCovers " . PHP_EOL); - - $log->appendLog("payment covers" . json_encode($notificationData->paymentData['paymentCovers']) . PHP_EOL); - - if (!empty($notificationData->paymentData['paymentCovers'])) { - $log->appendLog('Datos del payment covers:' . PHP_EOL); - - $invoiceIds = ''; // Variable para almacenar los invoiceId - $amounts = ''; // Variable para almacenar los amounts formateados - - foreach ($notificationData->paymentData['paymentCovers'] as $paymentCover) { - $log->appendLog('Invoice ID pagado: ' . $paymentCover['invoiceId'] . ' de esta cantidad: ' . $paymentCover['amount'] . PHP_EOL); - - // Concatenar cada invoiceId seguido de una coma - $invoiceIds .= $paymentCover['invoiceId'] . ', '; - - // Formatear el amount con punto para miles y coma para decimales - $formattedAmount = number_format($paymentCover['amount'], 2, ',', '.'); - - // Concatenar el amount formateado seguido de una coma - $amounts .= $formattedAmount . ', '; - } - - // Eliminar la última coma y el espacio extra usando substr - $invoiceIds = substr($invoiceIds, 0, -2); // Elimina los últimos dos caracteres (coma y espacio) - $amounts = substr($amounts, 0, -2); // Elimina los últimos dos caracteres (coma y espacio) - - // Mostrar las cadenas finales - $log->appendLog('Todos los Invoice IDs: ' . $invoiceIds . PHP_EOL); - $log->appendLog('Todas las cantidades: ' . $amounts . PHP_EOL); - - try { - - // Hacer la solicitud GET - $response = $clientGuzzleHttp->request('GET', "invoices/$invoiceIds"); - - // Realizar la solicitud a la API y guardar el contenido en un archivo local - $responseInvoices = $response->getBody()->getContents(); - $responseInvoicesJSON = json_decode($responseInvoices, true); - $log->appendLog('Numero de factura: ' . $responseInvoicesJSON['number'] . PHP_EOL); - $log->appendLog('TOTAL de factura: ' . $responseInvoicesJSON['total'] . PHP_EOL); - $total_factura = $responseInvoicesJSON['total']; - } catch (\Exception $e) { - $log->appendLog("Error con un problema al obtener alguna factura del cliente: " . $e->getMessage() . PHP_EOL); - return false; - } - } else { - $log->appendLog("no hay datos en payment covers" . PHP_EOL); - $invoiceIds = $notificationData->paymentData['id']; - $amounts = $notificationData->paymentData['amount']; - $total_factura = 0; - } - //$cantidad_pagadaFormateada = '$' . number_format($cantidad_pagada, 2, ',', '.') . ' MXN'; - $log->appendLog("Se terminó de verificar payment covers" . PHP_EOL); - $total_facturaFormateada = '$' . number_format($total_factura, 2, ',', '.') . ' MXN'; - $pagoFacturaFormateado = '$' . $amounts . ' MXN'; - - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$invoiceIds\", \"$total_facturaFormateada\", \"$pagoFacturaFormateado\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"c1396a6ad3cb4192916d4ac2bfb782a5\",\n \"optin_contact\": true\n }"; - } - - //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; - - // $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - //curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - //Enviar notificación adicional si es transferencia - // if($transferNotify){ - // curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_uuid\": \"41783c020cd84881bc27cb9e7e1da348\",\n \"optin_contact\": true\n }"); - // $response2 = curl_exec($ch); - // $log->appendLog("Response del CallBell aviso adicional: " . $response2); - // } - - curl_close($ch); - //$this->deleteFilesExceptLastFifty(); - //return $fileNameComprobante; - // Validar la respuesta de Callbell - $jsonResponse = json_decode($response, true); - - if (json_last_error() === JSON_ERROR_NONE) { - // Evaluar si contiene "message" con "status": "enqueued" - if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { - $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); - return true; - } - - // Evaluar si contiene "error" - if (isset($jsonResponse['error'])) { - $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); - return false; - } - } else { - // Manejo de error en caso de que la respuesta no sea un JSON válido - $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); - return false; - } - - // Valor de retorno predeterminado en caso de que ninguna condición se cumpla - $log->appendLog("Ruta no prevista en la función." . PHP_EOL); - return false; - } - - public function sendOverdueNotificationWhatsApp($clientWhatsAppNumber, $notificationData): void - { - - $log = PluginLogManager::create(); //Initialize Logger - - // URL base de la API - //$baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; - // Token de autenticación - //$token = $this->UCRMAPIToken; - //Path base del comprobante de pago - //$pdf_payment_path = ''; - - // $this->ucrmApi = UcrmApi::create(); - // $invoices = $this->ucrmApi->get( - // 'invoices/', - // [ - // 'clientId' => $notificationData->clientData['id'], - // 'limit' => 1, - // 'direction' => 'DESC' - - // ] - // ); - - //$datos_payment_array = json_decode($payments,true); - //$datos_invoices = json_encode($invoices); - - //$log->appendLog("Datos traidos con invoices api: " . $datos_invoices . PHP_EOL); - //$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL); - // $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL); - - // $invoice_id = $invoices[0]['id']; - // $invoices_amount = '$' . $invoices[0]['amount']; - $saldo = '$' . $notificationData->clientData['accountBalance']; - $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - - // Configuración del cliente GuzzleHttp - // $clientGuzzleHttp = new Client([ - // 'base_uri' => $baseUri, - // 'headers' => [ - // 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización - // 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON - // ], - // 'verify' => false, - // ]); - - // if (empty($notificationData->clientData['contacts'][0]['email'])) { - // $log->appendLog("El cliente no tiene correo" . PHP_EOL); - // } else { - // // $log->appendLog("El cliente SI tiene correo y es: ".$notificationData->clientData['contacts'][0]['email'] . PHP_EOL); - // // Hacer la solicitud PATCH para enviar correo electronico con el comprobante de pago - // $response = $clientGuzzleHttp->request('PATCH', "invoices/$invoice_id/send"); - - // } - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - // Verificar si la ruta es válida - // if ($realPath = realpath('/data/ucrm/data/plugins/siip-whatsapp-notifications/src/Facade/Comprobante.pdf')) { - // $log->appendLog("La ruta es válida: $realPath" . PHP_EOL); - // $log->appendLog(var_export($realPath,true) . PHP_EOL); - - // } else { - // $log->appendLog("La ruta no es válida o no existe" . PHP_EOL); - // } - - $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"SIIP INTERNET\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\",\n },\n \"template_values\": [\"$nombre_cliente\", \"$saldo\"],\n \"template_uuid\": \"9e7024c0a61a4c49b382150d26888dc2\",\n \"optin_contact\": true\n }"; - //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; - //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; - - // $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; - $log->appendLog("La cadena CURL que se envia es: " . $curl_string); - curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); - - $response = curl_exec($ch); - $log->appendLog("Response del CallBell: " . $response); - - curl_close($ch); - } - - public function getContactWhatsapp($cellphone_number): string - { - // URL de la API REST - $apiUrl = 'https://api.callbell.eu/v1/contacts/phone/' . $cellphone_number; - - // Inicializar cURL - $ch = curl_init(); - - // Configurar las opciones de cURL - curl_setopt($ch, CURLOPT_URL, $apiUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - // Ejecutar la solicitud y obtener la respuesta - $response = curl_exec($ch); - - // Verificar si hay errores en la solicitud cURL - if (curl_errno($ch)) { - return 'Error al realizar la solicitud cURL: ' . curl_error($ch); - } else { - // Cerrar la sesión cURL - curl_close($ch); - return $response; - } - } - - public function patchWhatsapp($response_getContactCallBell, $notificationData, $clabeInterbancaria = null) - { - - $log = PluginLogManager::create(); //Initialize Logger - //imprimir notificacionData - $log->appendLog("Notificacion data: " . json_encode($notificationData) . PHP_EOL); - //ejemplo de $notificationData: {"uuid":"cad4fa25-176e-4823-9f4c-1421d05d2a31","changeType":"edit","entity":"client","entityId":2,"message":null,"clientId":2,"eventName":"client.edit","clientData":{"id":2,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"31 Chiapas","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Chiapas 31, Centro, 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":"Cliente espacial, el m\u00e1s chido","sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2024-01-25T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":true,"firstName":"Daniel Humberto","lastName":"Soto Villegas Pollerias2","username":"danydhsv","contacts":[{"id":2,"clientId":2,"email":"dhsv.141089@gmail.com","phone":"5214181878106","name":"Personal","isBilling":false,"isContact":false,"types":[{"id":1000,"name":"WhatsApp"}]},{"id":170,"clientId":2,"email":null,"phone":"5214181817609","name":"Bussiness","isBilling":false,"isContact":false,"types":[]}],"attributes":[{"id":112,"clientId":2,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_PetN1dhr4rx0kX","clientZoneVisible":true},{"id":113,"clientId":2,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"0021804341999569810","clientZoneVisible":true},{"id":178,"clientId":2,"customAttributeId":15,"name":"Site","key":"site","value":"0LOCS","clientZoneVisible":false},{"id":179,"clientId":2,"customAttributeId":16,"name":"Antena\/Sectorial","key":"antenaSectorial","value":"Sectorial-4b 172.16.13.16\/24","clientZoneVisible":false}],"accountBalance":1553.33,"accountCredit":1553.33,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[{"id":2,"name":"NS EXENTO","colorBackground":"#42a3df","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1564209,"addressGpsLon":-100.9384185,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":true,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"serviceData":null,"invoiceData":null,"paymentData":null} - - //buscar el attribute con el key site y antenaSectorial - $attributes = $notificationData->clientData['attributes'] ?? []; //Obtener los atributos del cliente - $site = ''; - $antenaSectorial = ''; - $passAntenaUCRM = ''; - $clabeAttr = ''; - - // Iterar sobre los atributos - foreach ($attributes as $attribute) { - // Verificar si el key es "site" - if ($attribute['key'] === 'site') { - $site = $attribute['value'] ?? ''; - } - // Verificar si el key es "antenaSectorial" - if ($attribute['key'] === 'antenaSectorial') { - $antenaSectorial = $attribute['value'] ?? ''; - } - if ($attribute['key'] === 'passwordAntenaCliente') { - $passAntenaUCRM = $attribute['value'] ?? ''; - } - if ($attribute['key'] === 'clabeInterbancaria') { - $clabeAttr = $attribute['value'] ?? ''; - } - } - - if (empty($clabeInterbancaria)) { - $clabeInterbancaria = $clabeAttr; - } - - // Parsear contraseñas multi-servicio a JSON - $passAntenaJSON = []; - if (!empty($passAntenaUCRM)) { - if (strpos($passAntenaUCRM, 'Servicio') !== false) { - // Buscamos patrones "Servicio X: " - // Usamos una expresión más robusta para capturar hasta el siguiente "Servicio" o el final de la cadena - preg_match_all('/Servicio (\d+):\s*(.*?)(?=\s*Servicio \d+:|$)/', $passAntenaUCRM, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $numServicio = "Servicio " . $match[1]; - $passAntenaJSON[$numServicio] = trim($match[2]); - } - } else { - // Un solo servicio sin etiqueta (o con advertencia ⚠️) - $passAntenaJSON["Servicio 1"] = trim($passAntenaUCRM); - } - } - $passAntenaFinal = !empty($passAntenaJSON) ? json_encode($passAntenaJSON) : ""; - - $log->appendLog("Dentro del proceso del patch: " . PHP_EOL); - $config = PluginConfigManager::create()->loadConfig(); - $gClient = new Client(['base_uri' => "https://{$this->IPServer}/crm/api/v1.0/", 'verify' => false]); - $this->ucrmApi = new UcrmApi($gClient, $this->UCRMAPIToken ?? ''); - $payments = $this->ucrmApi->get( - 'payments/', - [ - 'clientId' => $notificationData->clientData['id'], - 'limit' => 1, - 'direction' => 'DESC', - - ] - ); - - //$datos_payment_array = json_decode($payments,true); - $datos_payment = json_encode($payments); - - $log->appendLog("Datos traidos con payment api: " . $datos_payment . PHP_EOL); - //$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL); - // $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL); - - $uuid = $response_getContactCallBell['contact']['uuid']; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - - $UrlChatCallBell = 'https://api.callbell.eu/v1/contacts/' . $uuid; - curl_setopt($ch, CURLOPT_URL, $UrlChatCallBell); - - $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - $log->appendLog("Nombre del cliente que se va a actualizar: " . $nombre_cliente . PHP_EOL); - $log->appendLog("UUID: " . $uuid . PHP_EOL); - - $payment_method_id = $payments[0]['methodId']; - $payment_method = ''; - - switch ($payment_method_id) { - case '11721cdf-a498-48be-903e-daa67552e4f6': - $payment_method = 'Cheque 📄'; - break; - case '6efe0fa8-36b2-4dd1-b049-427bffc7d369': - $payment_method = 'Efectivo 💵'; - break; - case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb': - $payment_method = 'Transferencia bancaria 📱🏦'; - break; - case '78e84000-9b5b-44a4-8367-da43df86ce34': - $payment_method = 'PayPal 🌐💳'; - break; - case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d': - $payment_method = 'Tarjeta de crédito PayPal 💳'; - break; - case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae': - $payment_method = 'Tarjeta de crédito Stripe 💳'; - break; - case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290': - $payment_method = 'Suscripción de Stripe (tarjeta de crédito) 🌐💳'; - break; - case '939f7701-00b7-4676-9b1e-17afb268c8ba': - $payment_method = 'Suscripción de PayPal 🌐💳'; - break; - case '1c963e35-df24-444d-95d2-12592d5107e8': - $payment_method = 'MercadoPago 🌐💳'; - break; - case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3': - $payment_method = 'Personalizado 📝💸'; - break; - default: - $payment_method = 'Desconocido, revisar metodos de pago no contemplados'; - break; - } - - $fecha_actual = new DateTime(); - $fecha_actual->modify('-6 hours'); - $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); - - $fecha_ultimoPago = new DateTime($payments[0]['createdDate']); - //$fecha_ultimoPago = $fecha_ultimoPago->modify('-6 hours'); - $fecha_ultimoPago_ajustada = $fecha_ultimoPago->format("d/m/Y H:i"); - - $accountBalance = $notificationData->clientData['accountBalance']; - $saldoTexto = ''; - - if ($accountBalance < 0) { - // Si el saldo es negativo, quitar el signo y poner "Pendientes" - $saldoTexto = '💲' . abs($accountBalance) . ' pendientes'; - } elseif ($accountBalance > 0) { - // Si el saldo es positivo, poner "a favor" - $saldoTexto = '💲' . $accountBalance . ' a favor'; - } else { - // Si el saldo es 0, poner "está al corriente" - $saldoTexto = '💲' . $accountBalance . ' está al corriente'; - } - - $resumenClienteJSON = '{' . - '"Cliente": "' . $notificationData->clientData['id'] . '",' . - '"Nombre": "' . sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']) . '",' . - '"URL": "https://sistema.siip.mx/crm/client/' . $notificationData->clientId . '",' . - '"Saldo Actual": "' . $saldoTexto . '",' . - '"Monto Ultimo Pago": "$ ' . $payments[0]['amount'] . '",' . - '"Estado": "' . - (($notificationData->clientData['hasSuspendedService']) ? 'Suspendido' : 'Activo') . '",' . - '"Fecha Ultimo Pago": " ' . $fecha_ultimoPago_ajustada . ' con ' . $payment_method . '",' . - '"Fecha Ultima Actualizacion": "' . $fecha_actual_ajustada . '",' . - '"Clabe Interbancaria": "' . $clabeInterbancaria . '",' . - '"Site": "' . $site . '",' . - '"Antena/Sectorial": "' . $antenaSectorial . '",' . - '"Password Antena": ' . (empty($passAntenaFinal) ? '""' : $passAntenaFinal) . - '}'; - - $data_CRM = [ - "name" => sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), - - "custom_fields" => [ - "Site" => $site, - "Estado" => ($notificationData->clientData['hasSuspendedService']) ? '🟠 Suspendido' : '🟢 Activo ', - "URL" => '🌐 https://sistema.siip.mx/crm/client/' . $notificationData->clientId, - "Nombre" => sprintf("👤 %s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), - "Cliente" => $notificationData->clientData['id'], - "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada, - "Saldo Actual" => $saldoTexto, - "Monto Ultimo Pago" => '💲 ' . $payments[0]['amount'], - "Clabe Interbancaria" => $clabeInterbancaria, - "Fecha Ultimo Pago" => '📆💸 ' . $fecha_ultimoPago_ajustada . ' con ' . $payment_method, - "Antena/Sectorial" => $antenaSectorial, - "Domicilio" => ($notificationData->clientData['fullAddress'] == null) ? '📍❌ Sin domicilio' : '📍 ' . $notificationData->clientData['fullAddress'], - "Resumen" => $resumenClienteJSON, - "password-antena" => $passAntenaFinal, - ], - ]; - - $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); - - // OPT: Comparación inteligente para evitar patches innecesarios - // La respuesta de getContactWhatsapp viene envuelta en 'contact' y usa 'customFields' - $contact = $response_getContactCallBell['contact'] ?? []; - $contactCustomFields = $contact['customFields'] ?? []; - - $log->appendLog("DEBUG COMPARACIÓN - CallBell Saldo Actual: '" . ($contactCustomFields['Saldo Actual'] ?? 'NULL') . "'" . PHP_EOL); - $log->appendLog("DEBUG COMPARACIÓN - UCRM Saldo Actual: '" . $data_CRM['custom_fields']['Saldo Actual'] . "'" . PHP_EOL); - $log->appendLog("DEBUG COMPARACIÓN - CallBell Estado: '" . ($contactCustomFields['Estado'] ?? 'NULL') . "'" . PHP_EOL); - $log->appendLog("DEBUG COMPARACIÓN - UCRM Estado: '" . $data_CRM['custom_fields']['Estado'] . "'" . PHP_EOL); - - $hasChanges = false; - if (($contact['name'] ?? '') != $data_CRM['name']) { - $log->appendLog("DEBUG COMPARACIÓN - Diferencia en name. CallBell: '" . ($contact['name'] ?? 'NULL') . "', UCRM: '" . $data_CRM['name'] . "'" . PHP_EOL); - $hasChanges = true; - } else { - foreach ($data_CRM['custom_fields'] as $key => $value) { - if ($key === 'Fecha Ultima Actualizacion') { - continue; // Skip this as it always changes and shouldn't trigger a patch on its own - } - - $callBellValue = $contactCustomFields[$key] ?? ''; - if ((string)$callBellValue !== (string)$value) { - $log->appendLog("DEBUG COMPARACIÓN - Diferencia encontrada en $key. CallBell: '$callBellValue', UCRM: '$value'" . PHP_EOL); - $hasChanges = true; - break; - } - } - } - - if ($hasChanges) { - $log->appendLog("EJECUTANDO PATCH - Se detectaron cambios" . PHP_EOL); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data_CRM)); - $response = curl_exec($ch); - $log->appendLog("Response Patch CallBell: " . $response . PHP_EOL); - curl_close($ch); - } else { - $log->appendLog("NO SE EJECUTA PATCH - No hay cambios que actualizar" . PHP_EOL); - curl_close($ch); - } - } - - public function patchServiceStatusWhatsApp($response_getContactCallBell, $notificationData) - { - - $log = PluginLogManager::create(); //Initialize Logger - - $log->appendLog("Actualizando estado del servicio " . PHP_EOL); - - $uuid = $response_getContactCallBell['contact']['uuid']; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Authorization: Bearer ' . $this->CallBellAPIToken, - 'Content-Type: application/json', - ]); - $UrlChatCallBell = 'https://api.callbell.eu/v1/contacts/' . $uuid; - curl_setopt($ch, CURLOPT_URL, $UrlChatCallBell); - $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); - $log->appendLog("Nombre del cliente al que se va a actualizar el estado del servicio: " . $nombre_cliente . PHP_EOL); - $log->appendLog("UUID: " . $uuid . PHP_EOL); - - $fecha_actual = new DateTime(); - $fecha_actual->modify('-6 hours'); - $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); - - $data_CRM = [ - "name" => $nombre_cliente, - "custom_fields" => [ - "Estado" => ( - isset($notificationData->serviceData['status']) - && $notificationData->serviceData['status'] == 3 - ) ? '🟠 Suspendido' : ( - (isset($notificationData->serviceData['status']) - && $notificationData->serviceData['status'] == 1) - ? '🟢 Activo' : '🚫 Finalizado'), - "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada, - ], - ]; - - $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); - - $contact = $response_getContactCallBell['contact'] ?? []; - $contactCustomFields = $contact['customFields'] ?? []; - - if (($contactCustomFields['Estado'] ?? '') != $data_CRM['custom_fields']['Estado']) { - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data_CRM)); - $response = curl_exec($ch); - $log->appendLog("Response Patch CallBell: " . $response . PHP_EOL); - curl_close($ch); - } else { - $log->appendLog("No hay cambios en el estado del servicio que actualizar " . PHP_EOL); - } - } - - function UploadReceiptToWordpressByImageFileName($imageFileName): string - { - - $log = PluginLogManager::create(); //Initialize Logger - $configManager = PluginConfigManager::create(); - $config = $configManager->loadConfig(); - - // Configuración de conexión FTP - $ftp_server = $config['hostServerFTP']; - $ftp_username = $config['usernameServerFTP']; - $ftp_password = $config['passServerFTP']; - $remote_folder = "/public_html/wp/wp-content/uploads/img/"; - - $log->appendLog("Subiendo comprobante a worpdpress " . PHP_EOL); - // Configuración de conexión FTP - // $ftp_server = "siip.mx"; - // $ftp_username = "siip0001"; - // $ftp_password = '$spGiT,[wa)n'; - $remote_file = "/public_html/wp/wp-content/uploads/img/" . $imageFileName; - $file_to_upload = __DIR__ . '/../../comprobantes/' . $imageFileName; - $url = 'https://siip.mx/wp/wp-content/uploads/img/' . $imageFileName; - - $log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL); - - // Conexión FTP - $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); - $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); - ftp_pasv($ftp_conn, true); - // Verificar conexión y login - if ($ftp_conn && $login) { - $log->appendLog("Conexión FTP exitosa" . PHP_EOL); - - // Cargar archivo - if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { - $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); - $log->appendLog("La URL es: " . $url . PHP_EOL); - - $this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_file); - // Cerrar conexión FTP - ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO - return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO - - } else { - $log->appendLog("Error al cargar el archivo " . PHP_EOL); - ftp_close($ftp_conn); - return ''; - } - - //SI SE DECIDE VOLVER A ELIMINAR LOS COMPROBANTES ENTONCES DESCOMENTAR ESTA PARTE DE ABAJO Y COMENTAR LA SECCIÓN DE ARRIBA - - // Obtener lista de archivos en la carpeta - // $files = ftp_nlist($ftp_conn, $remote_folder); - // if (is_array($files)) { - // // Eliminar la ruta del directorio de los archivos - // $files = array_map(function ($file) use ($remote_folder) { - // return str_replace($remote_folder, '', $file); - // }, $files); - - // // Obtener fechas de modificación - // $filesWithTime = []; - // foreach ($files as $file) { - // $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); - // if ($modifiedTime != -1) { - // $filesWithTime[$file] = $modifiedTime; - // } - // } - - // // Ordenar archivos por fecha de modificación, más recientes primero - // arsort($filesWithTime); - - // // Obtener los archivos a eliminar (todos menos los 50 más recientes) - // $filesToDelete = array_slice(array_keys($filesWithTime), 50); - - // // Eliminar archivos antiguos - // foreach ($filesToDelete as $file) { - // if (ftp_delete($ftp_conn, $remote_folder . $file)) { - // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); - // } else { - // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); - // } - // } - // $log->appendLog("Archivos eliminados" . PHP_EOL); - // ftp_close($ftp_conn); - // return $url; - // } else { - // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); - // ftp_close($ftp_conn); - // return $url; - // } - - } else { - $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); - return ''; - } - } - - function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool - { - - // Obtener lista de archivos en la carpeta - $files = ftp_nlist($ftp_conn, $remote_folder); - if (is_array($files)) { - // Eliminar la ruta del directorio de los archivos - $files = array_map(function ($file) use ($remote_folder) { - return str_replace($remote_folder, '', $file); - }, $files); - - // Obtener fechas de modificación - $filesWithTime = []; - foreach ($files as $file) { - $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); - if ($modifiedTime != -1) { - $filesWithTime[$file] = $modifiedTime; - } - } - - // Ordenar archivos por fecha de modificación, más recientes primero - arsort($filesWithTime); - - // Obtener los archivos a eliminar (todos menos los 50 más recientes) - $filesToDelete = array_slice(array_keys($filesWithTime), 50); - - // Eliminar archivos antiguos - foreach ($filesToDelete as $file) { - if (ftp_delete($ftp_conn, $remote_folder . $file)) { - $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); - } else { - $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); - } - } - $log->appendLog("Archivos eliminados" . PHP_EOL); - ftp_close($ftp_conn); - return true; - } else { - $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); - ftp_close($ftp_conn); - return false; - } - } - - /** - * /Elimina los archivos del directorio de comprobantes de pago a excepción de los últimos 100 para evitar errores de envío - * @return void - */ - function deleteFilesExceptLastHundred() - { - $log = PluginLogManager::create(); //Initialize Logger - $directory = __DIR__ . '/../../comprobantes/'; - // Verificar si el directorio existe - if (!is_dir($directory)) { - $log->appendLog('El directorio no existe.'); - die("El directorio no existe."); - } - - // Obtener la lista de archivos en el directorio - $files = glob($directory . '/*'); - - // Ordenar los archivos por fecha de modificación, de más reciente a más antiguo - usort($files, function ($a, $b) { - return filemtime($b) - filemtime($a); - }); - - // Verificar que haya más de 100 archivos - if (count($files) > 100) { - // Obtener los archivos que deben ser eliminados (todos menos los 100 más recientes) - $filesToDelete = array_slice($files, 100); - - // Eliminar los archivos - foreach ($filesToDelete as $file) { - if (is_file($file)) { - unlink($file); - $log->appendLog("Archivo eliminado: $file" . PHP_EOL); - } - } - } else { - $log->appendLog("Hay menos de 100 archivos en el directorio. No se eliminarán archivos." . PHP_EOL); - } - } -}