@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
// migrate_categories_order.php
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CONFIGURATION DE CONNEXION (À ADAPTER)
|
||||||
|
// ============================================================================
|
||||||
|
require __DIR__ . '/includes/db.php';
|
||||||
|
|
||||||
|
|
||||||
|
echo "<h2>🚀 Migration : Ajout de l'ordre des catégories</h2>";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connexion au serveur MySQL (sans spécifier de base de données particulière)
|
||||||
|
$pdo = new PDO("mysql:host=$host;charset=utf8mb4", $user, $pass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
// 1. Trouver toutes les bases de données de la famille HouseHub
|
||||||
|
$stmt = $pdo->query("SHOW DATABASES LIKE 'househub_f%'");
|
||||||
|
$databases = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
if (empty($databases)) {
|
||||||
|
echo "<p style='color: #ef4444;'>❌ Aucune base de données correspondant à 'househub_f*' n'a été trouvée.</p>";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<p>🔍 <strong>" . count($databases) . "</strong> base(s) de données trouvée(s).</p>";
|
||||||
|
|
||||||
|
// 2. Boucler sur chaque base de données
|
||||||
|
foreach ($databases as $db) {
|
||||||
|
echo "<hr><div style='padding: 10px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 10px;'>";
|
||||||
|
echo "<h4 style='margin-top: 0;'>📂 Traitement de la base : <strong>$db</strong></h4>";
|
||||||
|
|
||||||
|
// Sélectionner la base de données active
|
||||||
|
$pdo->exec("USE `$db`");
|
||||||
|
|
||||||
|
// Vérifier si la table pf_budget_categories existe dans cette DB
|
||||||
|
$tableExists = $pdo->query("SHOW TABLES LIKE 'pf_budget_categories'")->rowCount() > 0;
|
||||||
|
|
||||||
|
if ($tableExists) {
|
||||||
|
// Vérifier si la colonne sort_order existe déjà (sécurité)
|
||||||
|
$columnExists = $pdo->query("SHOW COLUMNS FROM pf_budget_categories LIKE 'sort_order'")->rowCount() > 0;
|
||||||
|
|
||||||
|
if (!$columnExists) {
|
||||||
|
// La colonne n'existe pas, on l'ajoute !
|
||||||
|
$pdo->exec("ALTER TABLE pf_budget_categories ADD sort_order INT DEFAULT 0");
|
||||||
|
echo "<span style='color: #10b981;'>✅ Colonne 'sort_order' ajoutée avec succès !</span>";
|
||||||
|
} else {
|
||||||
|
// La colonne est déjà là
|
||||||
|
echo "<span style='color: #f59e0b;'>ℹ️ La colonne 'sort_order' existe déjà (Ignoré).</span>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "<span style='color: #ef4444;'>⚠️ La table 'pf_budget_categories' n'existe pas ici.</span>";
|
||||||
|
}
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<h3>🎉 Migration terminée avec succès !</h3>";
|
||||||
|
echo "<p style='color: #64748b;'><em>Tu peux maintenant supprimer ce fichier par mesure de sécurité.</em></p>";
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "<h3 style='color: #ef4444;'>❌ Erreur fatale MySQL :</h3>";
|
||||||
|
echo "<pre style='background: #fee2e2; padding: 15px; border-radius: 8px;'>" . $e->getMessage() . "</pre>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -168,6 +168,19 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAUVEGARDE DU NOUVEL ORDRE DES CATÉGORIES
|
||||||
|
if (isset($_POST['action']) && $_POST['action'] === 'save_categories_order') {
|
||||||
|
$ids = json_decode($_POST['order'], true);
|
||||||
|
if (is_array($ids)) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE pf_budget_categories SET sort_order = ? WHERE id = ?");
|
||||||
|
foreach ($ids as $index => $id) {
|
||||||
|
$stmt->execute([$index, (int)$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
throw new Exception("Action API inconnue.");
|
throw new Exception("Action API inconnue.");
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|||||||
@@ -305,16 +305,25 @@ function renderCategories(categories) {
|
|||||||
container.innerHTML = `<em class="text-muted">${tr('bs_empty_categories')}</em>`;
|
container.innerHTML = `<em class="text-muted">${tr('bs_empty_categories')}</em>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tri JS pour respecter l'ordre au chargement de la modale
|
||||||
|
categories.sort((a, b) => {
|
||||||
|
if (a.type !== b.type) return a.type === 'Income' ? -1 : 1;
|
||||||
|
return (a.sort_order || 0) - (b.sort_order || 0);
|
||||||
|
});
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
categories.forEach(cat => {
|
categories.forEach(cat => {
|
||||||
let typeLabel = cat.type === 'Income' ? tr('bs_type_income') : cat.type === 'Expense' ? tr('bs_type_expense') : tr('bs_type_savings_cat');
|
let typeLabel = cat.type === 'Income' ? tr('bs_type_income') : cat.type === 'Expense' ? tr('bs_type_expense') : tr('bs_type_savings_cat');
|
||||||
html += `
|
html += `
|
||||||
<div class="bs-list-item" style="border-left: 4px solid ${cat.color || '#ccc'};">
|
<div class="bs-list-item" data-id="${cat.id}" style="border-left: 4px solid ${cat.color || '#ccc'};">
|
||||||
<div>
|
<div>
|
||||||
<span style="font-size:1.2rem; margin-right:8px;">${cat.icon || '📌'}</span>
|
<span style="font-size:1.2rem; margin-right:8px;">${cat.icon || '📌'}</span>
|
||||||
<strong>${cat.label}</strong> <small class="text-muted">(${cat.code} — ${typeLabel})</small>
|
<strong>${cat.label}</strong> <small class="text-muted">(${cat.code} — ${typeLabel})</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-flex-gap-8">
|
<div class="pf-flex-gap-8">
|
||||||
|
<button type="button" class="btn-icon-action" onclick="moveCatUp(this)" title="Monter">⬆️</button>
|
||||||
|
<button type="button" class="btn-icon-action" onclick="moveCatDown(this)" title="Descendre">⬇️</button>
|
||||||
<button type="button" class="btn-icon-action delete" onclick="deleteCategory(${cat.id})" title="${tr('btn_delete')}">🗑️</button>
|
<button type="button" class="btn-icon-action delete" onclick="deleteCategory(${cat.id})" title="${tr('btn_delete')}">🗑️</button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -322,6 +331,35 @@ function renderCategories(categories) {
|
|||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nouvelles fonctions pour réordonner visuellement et sauvegarder
|
||||||
|
function moveCatUp(btn) {
|
||||||
|
let row = btn.closest('.bs-list-item');
|
||||||
|
if (row.previousElementSibling) {
|
||||||
|
row.parentNode.insertBefore(row, row.previousElementSibling);
|
||||||
|
saveCategoriesOrder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveCatDown(btn) {
|
||||||
|
let row = btn.closest('.bs-list-item');
|
||||||
|
if (row.nextElementSibling) {
|
||||||
|
row.parentNode.insertBefore(row.nextElementSibling, row);
|
||||||
|
saveCategoriesOrder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCategoriesOrder() {
|
||||||
|
let ids = Array.from(document.querySelectorAll('#bs-list-categories .bs-list-item')).map(el => el.dataset.id);
|
||||||
|
let fd = new FormData();
|
||||||
|
fd.append('action', 'save_categories_order');
|
||||||
|
fd.append('order', JSON.stringify(ids));
|
||||||
|
try {
|
||||||
|
await pachaFetch('/modules/budget/includes/api/settings.php', { method: 'POST', body: fd });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur de sauvegarde de l'ordre :", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. ONGLET : RÈGLES D'IMPORT
|
// 3. ONGLET : RÈGLES D'IMPORT
|
||||||
function renderRules(rules) {
|
function renderRules(rules) {
|
||||||
const container = document.getElementById('bs-list-rules');
|
const container = document.getElementById('bs-list-rules');
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// --- LECTURE DYNAMIQUE DES CATEGORIES ---
|
// --- LECTURE DYNAMIQUE DES CATEGORIES ---
|
||||||
$stmtCats = $pdo->query("SELECT * FROM pf_budget_categories ORDER BY type DESC, label ASC");
|
$stmtCats = $pdo->query("SELECT * FROM pf_budget_categories ORDER BY type DESC, sort_order ASC, label ASC");
|
||||||
$dbCategories = $stmtCats->fetchAll(PDO::FETCH_ASSOC);
|
$dbCategories = $stmtCats->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$categoriesConfig = [];
|
$categoriesConfig = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user