antes de la implementación del reenvio de notificaciones a instaladores
This commit is contained in:
parent
8d4580e138
commit
d0430dd891
@ -1,5 +1,13 @@
|
||||
# CHANGELOG - SIIP WhatsApp Notifications Plugin
|
||||
|
||||
## VERSIÓN 4.3.1 - 10-03-2026
|
||||
|
||||
### 🐛 Correcciones (Bug Fixes)
|
||||
|
||||
1️⃣ **Fix Notificación de Cambio de Instalador**: Corregido bug crítico en `AbstractMessageNotifierFacade.php` donde al cambiar de técnico en una tarea **"En curso"**, el **nuevo** instalador recibía el mensaje de **desasignación** (❌ "se te ha desasignado la tarea...") en lugar de un mensaje de **asignación** con los datos del cliente.
|
||||
|
||||
**Causa raíz**: La bandera `$changeInstaller=true` se pasaba tanto a la notificación del técnico anterior como a la del nuevo, causando que ambos recibieran la plantilla de desasignación. El fix envía `false` al nuevo técnico para que use la plantilla de asignación normal.
|
||||
|
||||
## VERSIÓN 4.3.0 - 23-02-2026
|
||||
|
||||
### 🔐 Seguridad y Acceso (Login)
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
# SIIP - WhatsApp Notifications & Integrated Payment Portal
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Este plugin es una solución integral que transforma tu UCRM en un **Portal Administrativo de Última Generación**. No solo automatiza la comunicación por WhatsApp, sino que integra un Dashboard completo para la gestión de pagos online (Stripe/OXXO), visualización de comprobantes y coordinación de equipos técnicos.
|
||||
|
||||
## 🐛 Hotfix v4.3.1 (Installer Notification Fix)
|
||||
|
||||
- **🔧 Fix Cambio de Instalador**: Corregido bug donde el nuevo técnico recibía mensaje de desasignación en vez de asignación al cambiar instalador en una tarea "En curso".
|
||||
|
||||
## 🔐 Novedades v4.3.0 (Security & Premium UI)
|
||||
|
||||
- **🛡️ Sistema de Acceso Seguro**: Implementada validación híbrida (Server + Client). El plugin ahora protege las URLs públicas mediante una pantalla de inicio de sesión que requiere credenciales de Administrador de UCRM o autenticación 2FA.
|
||||
|
||||
1256
data/plugin.log
1256
data/plugin.log
File diff suppressed because it is too large
Load Diff
@ -5,13 +5,18 @@
|
||||
"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": "4.3.0",
|
||||
"version": "4.3.1",
|
||||
"unmsVersionCompliancy": {
|
||||
"min": "2.1.0",
|
||||
"max": null
|
||||
},
|
||||
"author": "SIIP INTERNET",
|
||||
"changelog": [
|
||||
{
|
||||
"version": "4.3.1",
|
||||
"date": "2026-03-10",
|
||||
"changes": "Hotfix: Corregido bug donde al cambiar de instalador en una tarea En Curso, el nuevo técnico recibía mensaje de desasignación en vez de asignación."
|
||||
},
|
||||
{
|
||||
"version": "4.3.0",
|
||||
"date": "2026-02-23",
|
||||
|
||||
338
public.php
Executable file → Normal file
338
public.php
Executable file → Normal file
@ -7,10 +7,8 @@ chdir(__DIR__);
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmSecurity;
|
||||
use SmsNotifier\Service\PaymentIntentService;
|
||||
|
||||
// Carga manual del servicio
|
||||
require_once __DIR__ . '/src/Service/PaymentIntentService.php';
|
||||
// Eliminado: PaymentIntentService ya no se usa aquí
|
||||
|
||||
if (!file_exists(__DIR__ . '/data/config.json')) {
|
||||
die('Acceso denegado o configuración no encontrada.');
|
||||
@ -18,7 +16,6 @@ if (!file_exists(__DIR__ . '/data/config.json')) {
|
||||
|
||||
$configManager = PluginConfigManager::create();
|
||||
$config = $configManager->loadConfig();
|
||||
$logger = new \SmsNotifier\Service\Logger();
|
||||
|
||||
// LOG DE EMERGENCIA
|
||||
$debugLogPath = __DIR__ . '/data/debug_public.log';
|
||||
@ -50,11 +47,7 @@ $httpClient = new \GuzzleHttp\Client([
|
||||
|
||||
$ucrmApi = new UcrmApi($httpClient, $config['apitoken'] ?? '');
|
||||
|
||||
$stripeService = new PaymentIntentService(
|
||||
$ucrmApi,
|
||||
$config['tokenstripe'] ?? '',
|
||||
$logger
|
||||
);
|
||||
$ucrmApi = new UcrmApi($httpClient, $config['apitoken'] ?? '');
|
||||
|
||||
// Admins Logic
|
||||
$admins = [];
|
||||
@ -110,19 +103,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['action']) && $_GET['ac
|
||||
$authToken = $resp->getHeaderLine('x-auth-token');
|
||||
|
||||
if ($statusCode === 200 && $authToken) {
|
||||
$logger->info("NMS Login OK for user: {$username}");
|
||||
echo json_encode(['success' => true, 'token' => $authToken, 'user' => $body]);
|
||||
} elseif ($statusCode === 201) {
|
||||
$logger->info("NMS Login requires 2FA for user: {$username}");
|
||||
http_response_code(201);
|
||||
echo json_encode(['requires2FA' => true, 'twoFactorToken' => $body]);
|
||||
} else {
|
||||
$logger->warning("NMS Login failed for user: {$username} (HTTP {$statusCode})");
|
||||
http_response_code($statusCode ?: 401);
|
||||
echo json_encode(['error' => $body['message'] ?? 'Credenciales inválidas.', 'statusCode' => $statusCode]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logger->error("NMS Login exception: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error de conexión con el servidor UISP.']);
|
||||
}
|
||||
@ -146,7 +135,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['action']) && $_GET['ac
|
||||
$authToken = $resp->getHeaderLine('x-auth-token');
|
||||
|
||||
if ($statusCode === 200 && $authToken) {
|
||||
$logger->info("NMS 2FA Login OK");
|
||||
echo json_encode(['success' => true, 'token' => $authToken, 'user' => $body]);
|
||||
} else {
|
||||
http_response_code($statusCode ?: 401);
|
||||
@ -222,6 +210,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||
$selfUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
|
||||
|
||||
$debugLogPath = __DIR__ . '/data/debug_public.log';
|
||||
file_put_contents($debugLogPath, "[" . date('Y-m-d H:i:s') . "] RESEND PAYMENT. URL: $selfUrl" . PHP_EOL, FILE_APPEND);
|
||||
|
||||
$ch = curl_init($selfUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
@ -229,10 +220,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_POSTREDIR, 3); // Mantener POST y payload en redirects 301/302
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
$res = curl_exec($ch);
|
||||
$err = curl_error($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
file_put_contents($debugLogPath, "[" . date('Y-m-d H:i:s') . "] RESEND CURL RESULT - Code: $code - Error: $err - Body: $res" . PHP_EOL, FILE_APPEND);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Notificación disparada.']);
|
||||
} catch (\Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
@ -271,60 +268,6 @@ if (isset($_GET['action'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_GET['action'] === 'get_stripe_history') {
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$stripeCustomerId = $_GET['stripeCustomerId'] ?? '';
|
||||
if (!$stripeCustomerId) {
|
||||
echo json_encode(['error' => 'Missing stripeCustomerId']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$history = $stripeService->getLastPayments($stripeCustomerId);
|
||||
$balance = $stripeService->getCustomerCashBalance($stripeCustomerId);
|
||||
|
||||
echo json_encode([
|
||||
'history' => $history,
|
||||
'cashBalance' => $balance
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_GET['action'] === 'get_oxxo_history') {
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$stripeCustomerId = $_GET['stripeCustomerId'] ?? '';
|
||||
if (!$stripeCustomerId) {
|
||||
echo json_encode(['error' => 'Missing stripeCustomerId']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$history = $stripeService->getLastOxxoPayments($stripeCustomerId);
|
||||
echo json_encode(['history' => $history]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_GET['action'] === 'search_stripe') {
|
||||
$q = $_GET['q'] ?? '';
|
||||
echo json_encode($stripeService->searchClients($q));
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_GET['action'] === 'get_stripe_details') {
|
||||
echo json_encode($stripeService->getClientDetails($_GET['id'] ?? null));
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_GET['action'] === 'image' || $_GET['action'] === 'get_image') {
|
||||
// Image Handler
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
@ -355,14 +298,6 @@ if (isset($_GET['action'])) {
|
||||
// POST Actions for Intents
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
header('Content-Type: application/json');
|
||||
if ($_POST['action'] === 'create_intent') {
|
||||
$clientId = $_POST['clientId'] ?? null;
|
||||
$amount = $_POST['amount'] ?? 0;
|
||||
$stripeCustomerId = $_POST['stripeCustomerId'] ?? null;
|
||||
$adminId = $_POST['adminId'] ?? null;
|
||||
echo json_encode($stripeService->createPaymentIntent($clientId, $amount, $stripeCustomerId, $adminId));
|
||||
exit;
|
||||
}
|
||||
if ($_POST['action'] === 'create_oxxo_intent') {
|
||||
try {
|
||||
$builder = new \DI\ContainerBuilder();
|
||||
@ -451,6 +386,46 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 12px;
|
||||
background: white;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-main);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* MENU DASHBOARD */
|
||||
@ -806,6 +781,45 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* STRIPE CASH BALANCE BADGE */
|
||||
.balance-badge {
|
||||
align-items: center;
|
||||
background-color: #f1f5f9;
|
||||
color: var(--text-main);
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9em;
|
||||
font-weight: 700;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .balance-badge {
|
||||
background-color: #bfdbfe;
|
||||
/* Light blue background for contrast */
|
||||
color: #1e3a8a;
|
||||
/* Navy blue text */
|
||||
border-color: #93c5fd;
|
||||
}
|
||||
|
||||
/* ENHANCED DARK MODE CONTRAST FOR SEARCH BOXES */
|
||||
[data-theme="dark"] .config-container {
|
||||
background: #1e293b;
|
||||
border: 1px solid #475569;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-wrapper .form-control {
|
||||
background: #0f172a;
|
||||
border: 1px solid #475569;
|
||||
color: #f1f5f9;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-wrapper .form-control:focus {
|
||||
border-color: var(--primary-hover);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
/* THEME TOGGLE (Premium) */
|
||||
.theme-toggle {
|
||||
background: var(--bg-card);
|
||||
@ -879,14 +893,56 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 12px;
|
||||
thead {
|
||||
background-color: var(--primary-surface);
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 16px 12px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: var(--text-main);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 14px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
vertical-align: middle;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tbody tr:hover td {
|
||||
background-color: rgba(37, 99, 235, 0.04);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
[data-theme="dark"] tbody tr:hover td {
|
||||
background-color: rgba(96, 165, 250, 0.08);
|
||||
}
|
||||
|
||||
[data-theme="dark"] thead {
|
||||
background-color: rgba(30, 41, 59, 0.8);
|
||||
}
|
||||
|
||||
[data-theme="dark"] th {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
@ -1037,9 +1093,75 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1.5rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 10px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 10px 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.config-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.actions-row {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actions-row .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
min-height: 200px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
@ -1468,15 +1590,15 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<div class="container">
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<img src="?action=image&file=logo-empresa.png" style="width:80px; height:80px; object-fit: contain; border-radius: 12px; background: white; padding: 5px; border: 1px solid var(--border);">
|
||||
<div class="header-left">
|
||||
<img src="?action=image&file=logo-empresa.png" class="header-logo">
|
||||
<div>
|
||||
<!-- RESTORED HEADER TEXT -->
|
||||
<h1 style="margin: 0; font-size: 24px;">Portal Administrativo de Pagos de STRIPE y Notificaciones WhatsApp</h1>
|
||||
<p style="margin: 0; color: #666;">Administración de Notificaciones vía WhatsApp, Intenciones de pago con Stripe y Fichas de OXXO Pay</p>
|
||||
<h1 class="header-title">Portal Administrativo de Pagos de STRIPE y Notificaciones WhatsApp</h1>
|
||||
<p class="header-subtitle">Administración de Notificaciones vía WhatsApp, Intenciones de pago con Stripe y Fichas de OXXO Pay</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-secondary hidden" id="btnBackToMenu" onclick="showDashboard()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2-2H5a2 2 0 0 1-2-2z" />
|
||||
@ -1484,14 +1606,16 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
</svg>
|
||||
Menú Principal
|
||||
</button>
|
||||
<button id="logoutBtn" class="btn btn-secondary" style="color: #ef4444; border-color: rgba(239, 68, 68, 0.3); background: rgba(239, 68, 68, 0.05);" onclick="handleLogout()">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align:middle; margin-right:6px;">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Cerrar Sesión
|
||||
</button>
|
||||
<?php if (strpos($_SERVER['REQUEST_URI'], '_plugins') !== false): ?>
|
||||
<button class="btn btn-secondary" style="color: #ef4444; border-color: rgba(239, 68, 68, 0.3); background: rgba(239, 68, 68, 0.05);" onclick="handleLogout()">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align:middle; margin-right:6px;">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Cerrar Sesión
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<!-- RESTORED THEME BUTTON -->
|
||||
<!-- PREMIUM THEME BUTTON -->
|
||||
<button class="theme-toggle" id="themeBtn" onclick="toggleTheme()">
|
||||
@ -1766,7 +1890,7 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<div id="stripeHistoryContainer" style="display:none; margin-top: 2rem; border-top: 1px solid var(--border); padding-top: 1.5rem;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
||||
<h4 class="section-title" style="margin-bottom: 0;">Historial de Pagos (Últimos 10)</h4>
|
||||
<div id="stripeCashBalanceBadge" style="display:none; align-items: center; background-color: #f1f5f9; color: var(--text-main); padding: 5px 12px; border-radius: 8px; font-size: 0.9em; font-weight: 600; border: 1px solid transparent;">
|
||||
<div id="stripeCashBalanceBadge" class="balance-badge" style="display:none;">
|
||||
<img src="?action=image&file=account-balance.webp" style="width: 32px; height: 32px; margin-right: 8px;">
|
||||
<span id="stripeCashBalanceText">Saldo Stripe: $0.00 MXN</span>
|
||||
</div>
|
||||
@ -1989,12 +2113,7 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
window.handleLogout = () => {
|
||||
sessionStorage.removeItem('nms_auth_token');
|
||||
sessionStorage.removeItem('nms_user');
|
||||
|
||||
if (!NEEDS_LOGIN) {
|
||||
window.top.location.href = '/crm/logout';
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
|
||||
@ -2220,7 +2339,6 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
<td>${new Date(p.createdDate).toLocaleString()}</td>
|
||||
<td>$${p.amount} ${p.currencyCode}</td>
|
||||
<td>${p.methodName}</td>
|
||||
<td>${p.methodName}</td>
|
||||
<td>
|
||||
<button class="btn btn-whatsapp" onclick="resendPayment(${p.id})">
|
||||
<img src="?action=image&file=whatsapp-logo-button.png" class="icon-btn" style="margin-right:5px;filter: brightness(0) invert(1);"> Re-enviar Notificación
|
||||
@ -2348,7 +2466,7 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
|
||||
const date = new Date(p.created * 1000).toLocaleString();
|
||||
const voucherBtn = p.voucherUrl ?
|
||||
`<a href="${p.voucherUrl}" target="_blank" class="btn btn-sm btn-outline-primary" style="padding: 2px 8px; font-size: 0.8rem;">Ver Ficha</a>` :
|
||||
`<a href="${p.voucherUrl}" target="_blank" class="btn btn-uniform" style="padding: 4px 12px; font-size: 0.8rem;">Ver Ficha</a>` :
|
||||
'-';
|
||||
|
||||
return `<tr>
|
||||
@ -2655,9 +2773,9 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
balanceBadge.style.color = '#15803d';
|
||||
balanceBadge.style.border = '1px solid #bbf7d0';
|
||||
} else {
|
||||
balanceBadge.style.backgroundColor = '#f1f5f9'; // Gray
|
||||
balanceBadge.style.color = 'var(--text-main)';
|
||||
balanceBadge.style.border = '1px solid transparent';
|
||||
balanceBadge.style.backgroundColor = '';
|
||||
balanceBadge.style.color = '';
|
||||
balanceBadge.style.border = '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -2903,16 +3021,6 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
||||
});
|
||||
|
||||
// Inicialización: decidir si mostrar login o portal
|
||||
sessionStorage.removeItem('nms_force_local_login'); // Cleanup from previous versions
|
||||
|
||||
// Ocultar botón de Cerrar Sesión si estamos dentro del iframe del CRM
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (window.top !== window.self) {
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
if (logoutBtn) logoutBtn.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
if (NEEDS_LOGIN) {
|
||||
// No hay sesión UCRM, intentar con token almacenado
|
||||
verifyStoredSession().then(valid => {
|
||||
|
||||
17
scripts-uisp/audit_client_passwords.php
Executable file → Normal file
17
scripts-uisp/audit_client_passwords.php
Executable file → Normal file
@ -438,12 +438,17 @@ function resolveAttributeIds($ucrmApi, $customAttributeKey, $siteAttributeKey, $
|
||||
try {
|
||||
$attributes = $ucrmApi->get('custom-attributes', ['attributeType' => 'client']);
|
||||
foreach ($attributes as $attr) {
|
||||
match ($attr['key']) {
|
||||
$customAttributeKey => $customAttributeId = $attr['id'],
|
||||
$siteAttributeKey => $siteAttributeId = $attr['id'],
|
||||
$antenaSectorialAttributeKey => $antenaSectorialAttributeId = $attr['id'],
|
||||
default => null,
|
||||
};
|
||||
switch ($attr['key']) {
|
||||
case $customAttributeKey:
|
||||
$customAttributeId = $attr['id'];
|
||||
break;
|
||||
case $siteAttributeKey:
|
||||
$siteAttributeId = $attr['id'];
|
||||
break;
|
||||
case $antenaSectorialAttributeKey:
|
||||
$antenaSectorialAttributeId = $attr['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
logMessage("Error fetching attributes: " . $e->getMessage());
|
||||
|
||||
0
scripts-uisp/debug_clients.php
Executable file → Normal file
0
scripts-uisp/debug_clients.php
Executable file → Normal file
0
scripts-uisp/ejemplo_script_actualizador.php
Executable file → Normal file
0
scripts-uisp/ejemplo_script_actualizador.php
Executable file → Normal file
0
scripts-uisp/generate_invalid_password_list.php
Executable file → Normal file
0
scripts-uisp/generate_invalid_password_list.php
Executable file → Normal file
@ -1 +0,0 @@
|
||||
10
|
||||
|
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
0
src/Data/NotificationData.php
Executable file → Normal file
0
src/Data/NotificationData.php
Executable file → Normal file
0
src/Data/PluginData.php
Executable file → Normal file
0
src/Data/PluginData.php
Executable file → Normal file
0
src/Data/UcrmData.php
Executable file → Normal file
0
src/Data/UcrmData.php
Executable file → Normal file
0
src/Exception/CurlException.php
Executable file → Normal file
0
src/Exception/CurlException.php
Executable file → Normal file
95
src/Facade/AbstractMessageNotifierFacade.php
Executable file → Normal file
95
src/Facade/AbstractMessageNotifierFacade.php
Executable file → Normal file
@ -24,7 +24,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
const SUBJECT_OF_INSTALLER_CHANGE = ["se ha cancelado una tarea que tenías asignada con el folio ", "se te ha desasignado❌ la tarea con el folio "];
|
||||
const ADDITIONAL_CHANGE_DATA = ["Ya no es necesario realizar la visita técnica.", "En tu lugar asistirá el técnico 👷🏻♂️➡️ "];
|
||||
|
||||
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber) {
|
||||
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->messageTextFactory = $messageTextFactory;
|
||||
$this->clientPhoneNumber = $clientPhoneNumber;
|
||||
@ -41,22 +42,30 @@ abstract class AbstractMessageNotifierFacade
|
||||
$this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? '');
|
||||
}
|
||||
|
||||
public function verifyPaymentActionToDo(NotificationData $notificationData): void {
|
||||
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;
|
||||
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 {
|
||||
public function verifyClientActionToDo(NotificationData $notificationData): void
|
||||
{
|
||||
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
||||
foreach ($arrayPhones as $type => $phones) {
|
||||
$type = trim(strtolower($type));
|
||||
@ -67,7 +76,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyServiceActionToDo(NotificationData $notificationData): void {
|
||||
public function verifyServiceActionToDo(NotificationData $notificationData): void
|
||||
{
|
||||
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
||||
foreach ($arrayPhones as $type => $phones) {
|
||||
$type = trim(strtolower($type));
|
||||
@ -78,7 +88,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyJobActionToDo($jsonNotificationData, $reprogramming = null, $changeInstaller = null): void {
|
||||
public function verifyJobActionToDo($jsonNotificationData, $reprogramming = null, $changeInstaller = null): void
|
||||
{
|
||||
$this->logger->info('Iniciando verifyJobActionToDo');
|
||||
$clientId = $jsonNotificationData['extraData']['entity']['clientId'];
|
||||
$installerId = $jsonNotificationData['extraData']['entity']['assignedUserId'];
|
||||
@ -94,7 +105,10 @@ abstract class AbstractMessageNotifierFacade
|
||||
$installerWhatsApp = '';
|
||||
$installers = json_decode($config['installersDataWhatsApp'] ?? '{"instaladores":[]}', true);
|
||||
foreach ($installers['instaladores'] as $inst) {
|
||||
if ($inst['id'] == $installerId) { $installerWhatsApp = $inst['whatsapp']; break; }
|
||||
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");
|
||||
@ -103,7 +117,10 @@ abstract class AbstractMessageNotifierFacade
|
||||
$clientName = trim(($clientCRM['firstName'] ?? '') . ' ' . ($clientCRM['lastName'] ?? ''));
|
||||
$passCRM = '';
|
||||
foreach ($clientCRM['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'passwordAntenaCliente') { $passCRM = $attr['value']; break; }
|
||||
if ($attr['key'] === 'passwordAntenaCliente') {
|
||||
$passCRM = $attr['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$allPhones = $this->clientPhoneNumber->getAllUcrmClientNumbers($clientCRM);
|
||||
@ -137,7 +154,10 @@ abstract class AbstractMessageNotifierFacade
|
||||
$prevAdmin = $this->ucrmApi->get("users/admins/$prevId", []);
|
||||
$prevWhatsApp = '';
|
||||
foreach ($installers['instaladores'] as $inst) {
|
||||
if ($inst['id'] == $prevId) { $prevWhatsApp = $inst['whatsapp']; break; }
|
||||
if ($inst['id'] == $prevId) {
|
||||
$prevWhatsApp = $inst['whatsapp'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($prevWhatsApp) {
|
||||
$api->sendJobNotificationWhatsAppToInstaller($this->validarNumeroTelefono($prevWhatsApp), [
|
||||
@ -178,7 +198,7 @@ abstract class AbstractMessageNotifierFacade
|
||||
"jobDescription" => $jsonNotificationData['extraData']['entity']['description'] ?? 'S/D',
|
||||
"gmapsLocation" => ($clientCRM['addressGpsLat'] && $clientCRM['addressGpsLon']) ? "https://www.google.com/maps?q={$clientCRM['addressGpsLat']},{$clientCRM['addressGpsLon']}" : 'N/A',
|
||||
"passwordAntenaCliente" => $this->comparePasswords($passCRM, $passVault)
|
||||
], $reprogramming, $changeInstaller);
|
||||
], $reprogramming, false);
|
||||
}
|
||||
|
||||
// 3. Gestión del Título / Prefijos
|
||||
@ -196,7 +216,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyInvoiceActionToDo(NotificationData $notificationData): void {
|
||||
public function verifyInvoiceActionToDo(NotificationData $notificationData): void
|
||||
{
|
||||
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
||||
foreach ($arrayPhones as $type => $phones) {
|
||||
$type = trim(strtolower($type));
|
||||
@ -207,7 +228,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function notify(NotificationData $notificationData, $phoneToNotify = null): void {
|
||||
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);
|
||||
@ -216,7 +238,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
else $api->sendPaymentNotificationWhatsApp($phone, $notificationData);
|
||||
}
|
||||
|
||||
public function notifyAndUpdate(NotificationData $notificationData, $phoneToNotifyAndUpdate = null): void {
|
||||
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);
|
||||
@ -234,14 +257,16 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function notifyOverDue(NotificationData $notificationData): void {
|
||||
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 {
|
||||
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']);
|
||||
@ -257,7 +282,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
public function onlyUpdateService(NotificationData $notificationData, $phoneToUpdate): void {
|
||||
public function onlyUpdateService(NotificationData $notificationData, $phoneToUpdate): void
|
||||
{
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
||||
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
||||
@ -265,7 +291,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
if ($contact) $api->patchServiceStatusWhatsApp($contact, $notificationData);
|
||||
}
|
||||
|
||||
protected function getVaultCredentialsByClientId($clientId): string {
|
||||
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]);
|
||||
@ -361,7 +388,9 @@ abstract class AbstractMessageNotifierFacade
|
||||
$passVault = $vault['credentials'][0]['password'];
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) { continue; }
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($passVault) {
|
||||
@ -375,7 +404,9 @@ abstract class AbstractMessageNotifierFacade
|
||||
'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]]
|
||||
]);
|
||||
$passwordValue = $newPass;
|
||||
} catch (\Exception $e) { $passwordValue = $newPass; }
|
||||
} catch (\Exception $e) {
|
||||
$passwordValue = $newPass;
|
||||
}
|
||||
} else {
|
||||
$passwordValue = "⚠️ Sin antena";
|
||||
}
|
||||
@ -392,19 +423,19 @@ abstract class AbstractMessageNotifierFacade
|
||||
|
||||
// Evitar sincronización redundante
|
||||
if ($finalValue === $passCRM) {
|
||||
return $finalValue;
|
||||
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 {
|
||||
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 {
|
||||
@ -427,10 +458,13 @@ abstract class AbstractMessageNotifierFacade
|
||||
$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()); }
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->warning("Fallo sincronización pass CRM: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateStrongPassword(int $length = 16): string {
|
||||
protected function generateStrongPassword(int $length = 16): string
|
||||
{
|
||||
$lower = 'abcdefghijkmnopqrstuvwxyz'; // Eliminamos 'l'
|
||||
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Eliminamos 'I', 'O'
|
||||
$digits = '23456789'; // Eliminamos '1', '0'
|
||||
@ -461,7 +495,8 @@ abstract class AbstractMessageNotifierFacade
|
||||
return implode('', $pwChars);
|
||||
}
|
||||
|
||||
protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool {
|
||||
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 {
|
||||
@ -478,13 +513,15 @@ abstract class AbstractMessageNotifierFacade
|
||||
}
|
||||
}
|
||||
|
||||
protected function comparePasswords(?string $crm, ?string $vault): string {
|
||||
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 {
|
||||
protected function validarNumeroTelefono($n): string
|
||||
{
|
||||
if (!$n) return '';
|
||||
$n = preg_replace('/\D/', '', (string)$n);
|
||||
return (strlen($n) === 10) ? '52' . $n : $n;
|
||||
|
||||
0
src/Facade/AbstractOxxoOperationsFacade.php
Executable file → Normal file
0
src/Facade/AbstractOxxoOperationsFacade.php
Executable file → Normal file
27
src/Facade/AbstractStripeOperationsFacade.php
Executable file → Normal file
27
src/Facade/AbstractStripeOperationsFacade.php
Executable file → Normal file
@ -283,21 +283,7 @@ abstract class AbstractStripeOperationsFacade
|
||||
try {
|
||||
$clientCRM = $this->ucrmApi->get("clients/$clientId", []);
|
||||
|
||||
// Si intenta crear CLABE pero NO tiene stripeCustomerId, cancelamos
|
||||
if ($tagName === 'CREAR CLABE STRIPE') {
|
||||
$hasStripeId = false;
|
||||
foreach ($clientCRM['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'stripeCustomerId' && !empty($attr['value'])) {
|
||||
$hasStripeId = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasStripeId) {
|
||||
$this->logger->warning("Cliente $clientId no tiene stripeCustomerId. No se puede crear CLABE.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Automaticamente creará el el Stripe Customer si no existe
|
||||
|
||||
$customer = $this->createCustomerStripe($stripe, $clientCRM, $generateSpei);
|
||||
|
||||
@ -358,10 +344,11 @@ abstract class AbstractStripeOperationsFacade
|
||||
|
||||
$customer = $stripe->customers->create($params);
|
||||
$this->logger->info("Nuevo Cliente Stripe creado para ID: $clientId. CID: {$customer->id}");
|
||||
// Guardar CID en UCRM
|
||||
$this->patchClientCustomAttribute($clientId, (int)$cidAttrId, $customer->id);
|
||||
}
|
||||
|
||||
// Guardar CID en UCRM siempre, por si no estaba sincronizado
|
||||
$this->patchClientCustomAttribute($clientId, (int)$cidAttrId, $customer->id);
|
||||
|
||||
// Si se requiere SPEI, generamos las instrucciones de fondeo para obtener la CLABE
|
||||
if ($generateSpei) {
|
||||
try {
|
||||
@ -687,6 +674,7 @@ abstract class AbstractStripeOperationsFacade
|
||||
try {
|
||||
$client = $this->ucrmApi->get("clients/$clientId");
|
||||
$targetTagId = null;
|
||||
$remainingTags = [];
|
||||
foreach ($client['tags'] as $tag) {
|
||||
if ($tag['name'] === $tagName) {
|
||||
$targetTagId = $tag['id'];
|
||||
@ -695,8 +683,9 @@ abstract class AbstractStripeOperationsFacade
|
||||
}
|
||||
|
||||
if ($targetTagId) {
|
||||
$this->ucrmApi->patch("clients/$clientId/remove-tag/$targetTagId", []);
|
||||
$this->logger->info("Etiqueta '$tagName' (ID: $targetTagId) removida del cliente $clientId via endpoint especializado.");
|
||||
// The proper UCRM endpoint to remove a tag from a client is PATCH /clients/{id}/remove-tag/{tagId}
|
||||
$this->ucrmApi->patch("clients/$clientId/remove-tag/$targetTagId");
|
||||
$this->logger->info("Etiqueta '$tagName' (ID: $targetTagId) removida del cliente $clientId.");
|
||||
} else {
|
||||
$this->logger->debug("Etiqueta '$tagName' no encontrada en el cliente $clientId, nada que remover.");
|
||||
}
|
||||
|
||||
0
src/Facade/AbstractUpdateClientFacade.php
Executable file → Normal file
0
src/Facade/AbstractUpdateClientFacade.php
Executable file → Normal file
14
src/Facade/ClientCallBellAPI.php
Executable file → Normal file
14
src/Facade/ClientCallBellAPI.php
Executable file → Normal file
@ -429,7 +429,15 @@ class ClientCallBellAPI
|
||||
$contenidoArchivo = $response->getBody()->getContents();
|
||||
|
||||
// Construir el nombre del archivo PDF basado en el cliente
|
||||
$fileNameComprobante = 'Comprobante_' . str_replace(' ', '_', $nombre_cliente) . '.pdf';
|
||||
$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
|
||||
@ -467,8 +475,8 @@ class ClientCallBellAPI
|
||||
$minioService = new MinioStorageService($loggerService);
|
||||
|
||||
// 2. Configurar Microservicio
|
||||
$ipMicroservice = $config['ipPuppeteer'] ?? 'localhost'; // Reutilizamos IP de Puppeteer
|
||||
$portMicroservice = '8050'; // Puerto definido en docker-compose
|
||||
$ipMicroservice = $config['ipMicroservice'] ?? 'pdf-cropper-service';
|
||||
$portMicroservice = $config['portMicroservice'] ?? '8000';
|
||||
$microserviceUrl = "http://{$ipMicroservice}:{$portMicroservice}/process";
|
||||
|
||||
$log->appendLog("Procesando PDF con microservicio: $microserviceUrl" . PHP_EOL);
|
||||
|
||||
0
src/Facade/PluginNotifierFacade.php
Executable file → Normal file
0
src/Facade/PluginNotifierFacade.php
Executable file → Normal file
0
src/Facade/PluginOxxoNotifierFacade.php
Executable file → Normal file
0
src/Facade/PluginOxxoNotifierFacade.php
Executable file → Normal file
0
src/Facade/TwilioNotifierFacade.php
Executable file → Normal file
0
src/Facade/TwilioNotifierFacade.php
Executable file → Normal file
0
src/Facade/pruebas_ucrm_api.php
Executable file → Normal file
0
src/Facade/pruebas_ucrm_api.php
Executable file → Normal file
0
src/Factory/MessageTextFactory.php
Executable file → Normal file
0
src/Factory/MessageTextFactory.php
Executable file → Normal file
0
src/Factory/NotificationDataFactory.php
Executable file → Normal file
0
src/Factory/NotificationDataFactory.php
Executable file → Normal file
0
src/Plugin.php
Executable file → Normal file
0
src/Plugin.php
Executable file → Normal file
0
src/Service/CurlExecutor.php
Executable file → Normal file
0
src/Service/CurlExecutor.php
Executable file → Normal file
0
src/Service/LogCleaner.php
Executable file → Normal file
0
src/Service/LogCleaner.php
Executable file → Normal file
0
src/Service/Logger.php
Executable file → Normal file
0
src/Service/Logger.php
Executable file → Normal file
0
src/Service/MinioStorageService.php
Executable file → Normal file
0
src/Service/MinioStorageService.php
Executable file → Normal file
0
src/Service/OptionsManager.php
Executable file → Normal file
0
src/Service/OptionsManager.php
Executable file → Normal file
0
src/Service/PaymentIntentService.php
Executable file → Normal file
0
src/Service/PaymentIntentService.php
Executable file → Normal file
0
src/Service/PluginDataValidator.php
Executable file → Normal file
0
src/Service/PluginDataValidator.php
Executable file → Normal file
0
src/Service/SmsNumberProvider.php
Executable file → Normal file
0
src/Service/SmsNumberProvider.php
Executable file → Normal file
0
src/Service/UcrmApi.php
Executable file → Normal file
0
src/Service/UcrmApi.php
Executable file → Normal file
13
test_facade_crash.php
Normal file
13
test_facade_crash.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/src/Facade/PluginNotifierFacade.php';
|
||||
echo "Included successfully\n";
|
||||
|
||||
if (!defined('INCLUDED_AS_LIBRARY')) {
|
||||
define('INCLUDED_AS_LIBRARY', true);
|
||||
}
|
||||
require_once __DIR__ . '/scripts-uisp/audit_client_passwords.php';
|
||||
echo "Audit included successfully\n";
|
||||
Loading…
Reference in New Issue
Block a user