@@ -1222,6 +1229,29 @@ function showTab(name) {
const initTab = new URLSearchParams(location.search).get('tab') || 'general';
showTab(initTab);
+async function syncPhones(cid) {
+ const btn = document.getElementById('syncBtn');
+ const msg = document.getElementById('syncMsg');
+ btn.disabled = true;
+ btn.innerHTML = '
Sincronizando...';
+ msg.innerHTML = '';
+ const fd = new FormData(); fd.append('company_id', cid);
+ try {
+ const r = await fetch('/admin/company/phones/sync', {method:'POST', body:fd});
+ const j = await r.json();
+ if (j.ok) {
+ msg.innerHTML = '
' + j.message + '
';
+ setTimeout(() => location.reload(), 1200);
+ } else {
+ msg.innerHTML = '
' + j.error + (j.detail ? '
' + j.detail + '' : '') + '
';
+ }
+ } catch(e) {
+ msg.innerHTML = '
Error de red: ' + e.message + '
';
+ }
+ btn.disabled = false;
+ btn.innerHTML = '
Sincronizar desde ERP';
+}
+
async function addPhone(cid) {
const num = document.getElementById('newPhone').value.trim().replace(/\D/g,'');
const lbl = document.getElementById('newPhoneLabel').value.trim();
@@ -1290,6 +1320,111 @@ HTML;
echo Layout::close();
}
+ // ─── POST /admin/company/phones/sync ─────────────────────────────────────
+
+ public static function companyPhonesSync(): void
+ {
+ SessionAuth::require();
+ header('Content-Type: application/json; charset=utf-8');
+
+ $companyId = (int)($_POST['company_id'] ?? 0);
+ if ($companyId <= 0) {
+ echo json_encode(['ok' => false, 'error' => 'ID de empresa inválido']);
+ exit;
+ }
+
+ $company = CompanyRepository::findById($companyId);
+ if (!$company) {
+ echo json_encode(['ok' => false, 'error' => 'Empresa no encontrada']);
+ exit;
+ }
+
+ // Buscar endpoint numeros_dn configurado para esta empresa
+ $epRow = db()->prepare("SELECT url, method FROM company_endpoints WHERE company_id=? AND endpoint_key='numeros_dn' LIMIT 1");
+ $epRow->execute([$companyId]);
+ $ep = $epRow->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$ep || empty($ep['url'])) {
+ echo json_encode(['ok' => false, 'error' => 'El endpoint "Informe de números activos" no está configurado en la pestaña Endpoints API.']);
+ exit;
+ }
+
+ // Llamar al endpoint
+ $apiKey = $company['api_key'] ?? '';
+ $ch = curl_init($ep['url']);
+ $opts = [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_TIMEOUT => 15,
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_HTTPHEADER => ['Accept: application/json'],
+ ];
+ if ($apiKey !== '') $opts[CURLOPT_HTTPHEADER][] = 'Authorization: Bearer ' . $apiKey;
+ if (strtoupper($ep['method'] ?? 'GET') === 'POST') {
+ $opts[CURLOPT_POST] = true;
+ $opts[CURLOPT_POSTFIELDS] = '{}';
+ $opts[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
+ }
+ curl_setopt_array($ch, $opts);
+ $resp = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $curlErr = curl_error($ch);
+ curl_close($ch);
+
+ if ($curlErr) {
+ echo json_encode(['ok' => false, 'error' => 'No se pudo conectar al ERP.', 'detail' => $curlErr]);
+ exit;
+ }
+ if ($httpCode !== 200) {
+ echo json_encode(['ok' => false, 'error' => "El ERP respondió HTTP {$httpCode}.", 'detail' => substr((string)$resp, 0, 300)]);
+ exit;
+ }
+
+ $data = json_decode($resp, true);
+ if (!is_array($data)) {
+ echo json_encode(['ok' => false, 'error' => 'La respuesta del ERP no es JSON válido.', 'detail' => substr((string)$resp, 0, 300)]);
+ exit;
+ }
+
+ // Aceptar root array o {numeros:[...]}
+ $numeros = isset($data['numeros']) ? $data['numeros'] : (array_values($data) && is_array($data[0] ?? null) ? $data : []);
+ if (empty($numeros)) {
+ echo json_encode(['ok' => false, 'error' => 'El JSON no contiene números. Espera un array en "numeros" o un array raíz.', 'detail' => substr($resp, 0, 300)]);
+ exit;
+ }
+
+ $inserted = 0; $updated = 0; $skipped = 0;
+ $pTypeMap = ['1' => 1, '2' => 2, '3' => 3, 1 => 1, 2 => 2, 3 => 3];
+
+ $stmt = db()->prepare("
+ INSERT INTO company_phones (company_id, wa_number, label, permission_type)
+ VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE label=VALUES(label), permission_type=VALUES(permission_type), is_active=1
+ ");
+
+ foreach ($numeros as $n) {
+ $waNumber = preg_replace('/\D/', '', (string)($n['wa_number'] ?? $n['numero'] ?? $n['phone'] ?? ''));
+ if (strlen($waNumber) < 7) { $skipped++; continue; }
+
+ $label = trim((string)($n['nombre'] ?? $n['name'] ?? $n['label'] ?? ''));
+ $permRaw = $n['permiso'] ?? $n['permission_type'] ?? $n['tipo'] ?? 3;
+ $permission = $pTypeMap[(int)$permRaw] ?? 3;
+
+ try {
+ $before = db()->prepare("SELECT id FROM company_phones WHERE company_id=? AND wa_number=?");
+ $before->execute([$companyId, $waNumber]);
+ $exists = $before->fetch();
+ $stmt->execute([$companyId, $waNumber, $label, $permission]);
+ $exists ? $updated++ : $inserted++;
+ } catch (\PDOException $e) { $skipped++; }
+ }
+
+ echo json_encode([
+ 'ok' => true,
+ 'message' => "Sincronización completa: {$inserted} nuevos, {$updated} actualizados, {$skipped} omitidos.",
+ ]);
+ exit;
+ }
+
// ─── POST /admin/company/phone/save ──────────────────────────────────────
public static function companyPhoneSave(): void
diff --git a/public/index.php b/public/index.php
index cd6137d..f330e6b 100644
--- a/public/index.php
+++ b/public/index.php
@@ -268,6 +268,7 @@ $routes = [
// ─── Números WhatsApp por empresa ──────────────────────────────────────
['POST', '/admin/company/phone/save', fn() => DashboardController::companyPhoneSave()],
['POST', '/admin/company/phone/delete', fn() => DashboardController::companyPhoneDelete()],
+ ['POST', '/admin/company/phones/sync', fn() => DashboardController::companyPhonesSync()],
// ─── Endpoints API por empresa ─────────────────────────────────────────
['POST', '/admin/company/endpoint/save', fn() => DashboardController::companyEndpointSave()],