query("SELECT name, amount, type, category, is_estimate FROM pf_budget_items"); $budgetItems = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($budgetItems as $item) { $rawAmount = (float)$item['amount']; $amt = $rawAmount; // Montant lissé pour les totaux globaux $name = trim($item['name']); $type = $item['type']; // 'Mensuel' ou 'Annuel' $cat = $item['category']; // 'expense' ou 'income' $is_est = (int)$item['is_estimate']; // 0 ou 1 // Gestion du lissage pour le calcul des totaux globaux (Reste à vivre) if ($type === 'Annuel') { $amt = $rawAmount / 12; } // 1. Calcul des totaux globaux (Entrées / Sorties) if ($cat === 'income') { $total_income += $amt; } else { $total_expenses += $amt; } // 2. Mappage des ESTIMATIONS spécifiques (FMCG, School, Essence) // Ces lignes ont généralement is_estimate = 1 dans ta base if ($name === 'Estimacio F&B & beauty') { $budget_fmcg = $amt; } elseif ($name === 'Estimacio escola') { $budget_school = $amt; } elseif ($name === 'Estimation gasolina') { $budget_essence = $amt; } // 3. CALCUL DU BUDGET CHARGES FIXES (Selon tes critères stricts) // - Pas une estimation // - Type Mensuel // - Catégorie Dépense if ($is_est === 0 && $type === 'Mensuel' && $cat === 'expense') { $budget_frais += $rawAmount; } } // Calcul du "Reste à vivre" pour la catégorie Autres // Formule : Revenus Totaux - Toutes les dépenses lissées $budget_autres = $total_income - $total_expenses; if ($budget_autres < 0) $budget_autres = 0; // ============================================================================ // 2. CONFIGURATION DES CATÉGORIES // ============================================================================ $categoriesConfig = [ 'FMCG' => [ 'label' => 'Courses (FMCG)', 'budget' => $budget_fmcg, 'color' => '#3b82f6', // Bleu 'suggestions' => ['Action', 'Boucher', 'Boulangerie', 'Carrefour', 'Grand Frais', 'Lidl', 'Zooplus', 'Pharmacie', 'Decathlon'] ], 'Essence' => [ 'label' => 'Essence', 'budget' => $budget_essence, 'color' => '#f59e0b', // Orange 'suggestions' => ['Audi', 'Polo'] ], 'School' => [ 'label' => 'École / Garde', 'budget' => $budget_school, 'color' => '#10b981', // Vert 'suggestions' => ['Pep', 'Pol'] ], 'Frais' => [ 'label' => 'Charges / Frais Fixes', 'budget' => $budget_frais, // Calculé ci-dessus (Somme des fixes mensuels) 'color' => '#ef4444', // Rouge 'suggestions' => ['Prêt', 'Assurance', 'Banque', 'Netflix', 'Spotify', 'Verisure', 'Internet', 'Mobile', 'Eau', 'Elec', 'Cantine'] ], 'Autres' => [ 'label' => 'Autres / Imprévus', 'budget' => $budget_autres, // Reste à vivre 'color' => '#64748b', // Gris 'suggestions' => ['Restaurant', 'Cadeau', 'Maison', 'Sortie', 'Santé non remboursée'] ], 'LivretA' => [ 'label' => 'Epargne / Livret A', 'budget' => 0, 'color' => '#8b5cf6', // Violet 'suggestions' => ['Virement mensuel'] ] ]; // ============================================================================ // 3. LOGIQUE D'IMPORTATION CSV (Avec Mémoire) // ============================================================================ $csvData = []; $showPreview = false; if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) { $file = $_FILES['csv_file']['tmp_name']; $handle = fopen($file, "r"); // Chargement de la mémoire $rules = []; try { $rulesStmt = $pdo->query("SELECT keyword, category FROM pf_import_rules"); $rules = $rulesStmt->fetchAll(PDO::FETCH_KEY_PAIR); } catch (Exception $e) { /* Ignore */ } // Sauter l'en-tête fgetcsv($handle, 1000, ";"); while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) { // Mapping (Date;Libelle;...;Debit) $rawDebit = $data[8] ?? ''; if (!empty($rawDebit)) { $amount = abs((float)str_replace(',', '.', str_replace(' ', '', $rawDebit))); $dateParts = explode('/', $data[0]); if (count($dateParts) == 3) { $dateSql = $dateParts[2] . '-' . $dateParts[1] . '-' . $dateParts[0]; } else { $dateSql = date('Y-m-d'); } $label = trim($data[1]); if(empty($label)) $label = trim($data[2]); // Intelligence $suggestedCat = ''; foreach ($rules as $keyword => $cat) { if (stripos($label, $keyword) !== false) { $suggestedCat = $cat; break; } } $csvData[] = [ 'date' => $dateSql, 'label' => $label, 'amount' => $amount, 'cat' => $suggestedCat ]; } } fclose($handle); $showPreview = true; } // ============================================================================ // 4. TRAITEMENT DES ACTIONS (Sauvegardes) // ============================================================================ // A. SAUVEGARDE IMPORT CSV if (isset($_POST['action']) && $_POST['action'] === 'save_import') { $count = 0; $stmtExp = $pdo->prepare("INSERT INTO pf_expenses (date_exp, category, label, amount) VALUES (?, ?, ?, ?)"); $stmtRule = $pdo->prepare("INSERT INTO pf_import_rules (keyword, category) VALUES (?, ?) ON DUPLICATE KEY UPDATE category = VALUES(category)"); if (isset($_POST['lines']) && is_array($_POST['lines'])) { foreach ($_POST['lines'] as $line) { if (!empty($line['cat']) && isset($line['import_check'])) { $stmtExp->execute([$line['date'], $line['cat'], $line['label'], $line['amount']]); $stmtRule->execute([$line['label'], $line['cat']]); $count++; } } } header("Location: ?tab=suivi&msg=imported_$count"); exit; } // B. AJOUT MANUEL if (isset($_POST['action']) && $_POST['action'] === 'add_expense') { $cat = $_POST['category']; $label = trim($_POST['label']); $amount = floatval($_POST['amount']); $date = $_POST['date']; if ($label && $amount > 0 && isset($categoriesConfig[$cat])) { $stmt = $pdo->prepare("INSERT INTO pf_expenses (date_exp, category, label, amount) VALUES (?, ?, ?, ?)"); $stmt->execute([$date, $cat, $label, $amount]); header("Location: ?tab=suivi"); exit; } } // C. SUPPRESSION if (isset($_GET['delete_expense'])) { $id = (int)$_GET['delete_expense']; $pdo->prepare("DELETE FROM pf_expenses WHERE id = ?")->execute([$id]); header("Location: ?tab=suivi"); exit; } // ============================================================================ // 5. RÉCUPÉRATION DES DONNÉES D'AFFICHAGE DU MOIS // ============================================================================ $currentMonth = date('m'); $currentYear = date('Y'); $stmt = $pdo->prepare(" SELECT * FROM pf_expenses WHERE MONTH(date_exp) = ? AND YEAR(date_exp) = ? ORDER BY date_exp DESC, id DESC "); $stmt->execute([$currentMonth, $currentYear]); $allExpenses = $stmt->fetchAll(PDO::FETCH_ASSOC); // Calcul des totaux par catégorie $totals = array_fill_keys(array_keys($categoriesConfig), 0); $expensesByCategory = array_fill_keys(array_keys($categoriesConfig), []); foreach ($allExpenses as $exp) { $cat = $exp['category']; if (isset($totals[$cat])) { $totals[$cat] += $exp['amount']; $expensesByCategory[$cat][] = $exp; } } $globalSpent = array_sum($totals); $globalBudget = array_sum(array_column($categoriesConfig, 'budget')); ?>

Suivi Mensuel :

Total Dépensé / € Prévu
$conf): $spent = $totals[$key]; $budget = $conf['budget']; if ($budget > 0) { $percent = ($spent / $budget) * 100; } else { $percent = ($spent > 0) ? 100 : 0; } $barColor = ($spent > $budget && $budget > 0) ? '#ef4444' : $conf['color']; $cssPercent = min(100, $percent); ?>
/
$budget && $budget > 0): ?>
+

Importer un relevé bancaire (CSV)

Le fichier doit utiliser des points-virgules (;) comme séparateur.

Valider l'importation

Vérifiez les catégories. L'application apprendra de vos choix.

$row): ?>
Date Libellé Montant Catégorie
-
Annuler
$conf): ?>

Aucune dépense.

- ×