" en repli.
*
* Corrige suite a l'audit du 20/09/2026 : le detail technique des
* exceptions n'est plus affiche a l'ecran ; il est journalise via
* error_log().
*
* Note : le controle par role (require_admin_role) est volontairement
* absent de cette version, la separation des privileges etant reportee
* a une migration dediee.
*/
declare(strict_types=1);
require_once __DIR__ . '/admin_auth.php';
require_once __DIR__ . '/../lib/db.php';
require_once __DIR__ . '/_nav.php';
require_admin_auth();
$db = pdo();
$feedback = null;
$feedbackType = 'success';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_csrf_token();
$action = $_POST['action'] ?? '';
$confirmed = isset($_POST['confirm']) && $_POST['confirm'] === '1';
if (!$confirmed) {
$feedback = 'Action refusee : la confirmation explicite est obligatoire.';
$feedbackType = 'error';
} elseif ($action === 'delete_cell') {
$cellId = filter_input(INPUT_POST, 'cell_id', FILTER_VALIDATE_INT);
if ($cellId === false || $cellId === null) {
$feedback = 'Identifiant de cellule invalide.';
$feedbackType = 'error';
} else {
try {
$stmt = $db->prepare('DELETE FROM cells WHERE id = ? LIMIT 1');
$stmt->execute([$cellId]);
$feedback = $stmt->rowCount() > 0
? sprintf('Cellule #%d supprimee.', $cellId)
: 'Aucune cellule correspondante trouvee (deja supprimee ?).';
} catch (Throwable $e) {
error_log('[duplicates] delete_cell: ' . $e->getMessage());
$feedback = 'Une erreur est survenue lors de la suppression.';
$feedbackType = 'error';
}
}
} elseif ($action === 'merge_sites') {
$keepId = filter_input(INPUT_POST, 'keep_site_id', FILTER_VALIDATE_INT);
$dropId = filter_input(INPUT_POST, 'drop_site_id', FILTER_VALIDATE_INT);
if (!$keepId || !$dropId || $keepId === $dropId) {
$feedback = 'Selection de sites invalide pour la fusion.';
$feedbackType = 'error';
} else {
try {
$db->beginTransaction();
$stmt = $db->prepare('UPDATE cells SET site_id = ? WHERE site_id = ?');
$stmt->execute([$keepId, $dropId]);
$moved = $stmt->rowCount();
$stmt = $db->prepare('DELETE FROM sites WHERE id = ? LIMIT 1');
$stmt->execute([$dropId]);
$db->commit();
$feedback = sprintf(
'Fusion effectuee : %d cellule(s) rattachee(s) au site #%d, site #%d supprime.',
$moved,
$keepId,
$dropId
);
} catch (Throwable $e) {
$db->rollBack();
error_log('[duplicates] merge_sites: ' . $e->getMessage());
$feedback = 'Une erreur est survenue lors de la fusion. Aucune donnee n\'a ete modifiee.';
$feedbackType = 'error';
}
}
}
}
/**
* Collisions eNB/Cell ID : deux cellules distinctes qui partagent le
* meme couple (sub_id2, cell_id) sur la meme technologie sont des
* candidates a la fusion ou a la correction manuelle. area_code est
* volontairement exclu de ce regroupement (contrairement a uniq_cell)
* pour repérer aussi les cas ou le TAC differe par erreur de saisie
* entre deux lignes du meme eNB.
*/
$cellDuplicates = $db->query(
"SELECT c.sub_id2, c.cell_id, c.tech, COUNT(*) AS occurrences,
GROUP_CONCAT(c.id ORDER BY c.id SEPARATOR ',') AS cell_ids,
GROUP_CONCAT(
DISTINCT COALESCE(s.address_desc, s.location_desc, CONCAT('Site #', s.id))
ORDER BY s.id SEPARATOR ' | '
) AS site_names
FROM cells c
LEFT JOIN sites s ON s.id = c.site_id
WHERE c.sub_id2 IS NOT NULL AND c.cell_id IS NOT NULL
GROUP BY c.sub_id2, c.cell_id, c.tech
HAVING occurrences > 1
ORDER BY occurrences DESC
LIMIT 200"
)->fetchAll(PDO::FETCH_ASSOC);
$siteDuplicates = $db->query(
"SELECT ROUND(s.lat, 5) AS lat_r, ROUND(s.lon, 5) AS lon_r,
COUNT(*) AS occurrences,
GROUP_CONCAT(s.id ORDER BY s.id SEPARATOR ',') AS site_ids,
GROUP_CONCAT(
COALESCE(s.address_desc, s.location_desc, CONCAT('Site #', s.id))
ORDER BY s.id SEPARATOR ' | '
) AS names
FROM sites s
WHERE s.lat IS NOT NULL AND s.lon IS NOT NULL
AND NOT (s.lat = 0 AND s.lon = 0)
GROUP BY lat_r, lon_r
HAVING occurrences > 1
ORDER BY occurrences DESC
LIMIT 200"
)->fetchAll(PDO::FETCH_ASSOC);
$user = current_admin_username();
?>
Doublons & collisions — NetMonster Admin
Doublons & collisions eNB / Cell ID
Cette page detecte les collisions potentielles sans jamais modifier la base automatiquement. Toute fusion ou suppression exige une confirmation explicite.