diff --git a/modules/budget/views/suivi.php b/modules/budget/views/suivi.php index 9d6a660..73ac0bc 100644 --- a/modules/budget/views/suivi.php +++ b/modules/budget/views/suivi.php @@ -58,7 +58,6 @@ if (isset($_POST['action']) && $_POST['action'] === 'save_import') { if (isset($line['import_check'])) { $cat = $line['cat']; $is_credit = isset($line['is_credit']) ? (int)$line['is_credit'] : 0; - // Récupération ID (Charge fixe OU Revenu) $budgetItemId = !empty($line['budget_item_id']) ? (int)$line['budget_item_id'] : null; if ($is_credit && empty($cat)) continue; @@ -81,8 +80,9 @@ if (isset($_POST['action']) && $_POST['action'] === 'save_import') { header("Location: ?tab=suivi&msg=imported_$count"); exit; } -// E. AJOUT DÉPENSE MANUELLE (AVEC BUDGET_ITEM_ID) -if (isset($_POST['action']) && $_POST['action'] === 'add_expense') { +// E. AJOUT OU MODIFICATION DÉPENSE (MANUELLE) +if (isset($_POST['action']) && $_POST['action'] === 'save_expense_manual') { + $id = !empty($_POST['expense_id']) ? (int)$_POST['expense_id'] : null; $cat = $_POST['category']; $amount = floatval($_POST['amount']); $date = $_POST['date']; @@ -93,21 +93,25 @@ if (isset($_POST['action']) && $_POST['action'] === 'add_expense') { if ($cat === 'School' && !empty($_POST['label_select'])) { $label = trim($_POST['label_select']); } - // GESTION ID POUR FRAIS ET REVENUS elseif (($cat === 'Frais' || $cat === 'Income') && !empty($_POST['budget_item_id'])) { $budgetItemId = (int)$_POST['budget_item_id']; } if ($label && $amount > 0) { - $uniqueRef = "MANUAL_" . uniqid(); - // Si c'est un revenu (Income), on s'assure que le montant est enregistré en négatif (Crédit) dans pf_expenses - // Sauf si l'utilisateur a déjà mis un moins, mais on part du principe qu'il saisit un montant positif - if ($cat === 'Income') { - $amount = -abs($amount); - } + // Gestion du signe (Revenu = Négatif en BDD, Dépense = Positif) + // Note: L'utilisateur saisit toujours du positif dans le formulaire + $finalAmount = ($cat === 'Income') ? -abs($amount) : abs($amount); - $pdo->prepare("INSERT INTO pf_expenses (date_exp, category, label, amount, import_ref, budget_item_id) VALUES (?, ?, ?, ?, ?, ?)") - ->execute([$date, $cat, $label, $amount, $uniqueRef, $budgetItemId]); + if ($id) { + // UPDATE + $pdo->prepare("UPDATE pf_expenses SET date_exp=?, category=?, label=?, amount=?, budget_item_id=? WHERE id=?") + ->execute([$date, $cat, $label, $finalAmount, $budgetItemId, $id]); + } else { + // INSERT + $uniqueRef = "MANUAL_" . uniqid(); + $pdo->prepare("INSERT INTO pf_expenses (date_exp, category, label, amount, import_ref, budget_item_id) VALUES (?, ?, ?, ?, ?, ?)") + ->execute([$date, $cat, $label, $finalAmount, $uniqueRef, $budgetItemId]); + } header("Location: ?tab=suivi"); exit; } } @@ -127,25 +131,22 @@ $total_income = 0; $total_expenses_prevues = 0; $reste_a_venir = 0; $today_day = (int)date('j'); -// Listes pour les selecteurs $fixedChargesList = []; $incomeList = []; -// --- A. RÉCUPÉRATION DONNÉES RÉELLES POUR CALCUL "Reste à venir" --- $currentMonth = date('m'); $currentYear = date('Y'); -// 1. Récupération des IDs payés ce mois-ci +// 1. Récupération des IDs payés $stmtIds = $pdo->prepare("SELECT DISTINCT budget_item_id FROM pf_expenses WHERE MONTH(date_exp) = ? AND YEAR(date_exp) = ? AND budget_item_id IS NOT NULL"); $stmtIds->execute([$currentMonth, $currentYear]); $paidItemIds = $stmtIds->fetchAll(PDO::FETCH_COLUMN); -// 2. Récupération des libellés (pour le fallback mots-clés) +// 2. Récupération libellés $stmtLabels = $pdo->prepare("SELECT label FROM pf_expenses WHERE MONTH(date_exp) = ? AND YEAR(date_exp) = ?"); $stmtLabels->execute([$currentMonth, $currentYear]); $realExpensesLabels = $stmtLabels->fetchAll(PDO::FETCH_COLUMN); - -// --- B. SNAPSHOT & SOLDE THÉORIQUE --- +// Snapshot $snapshot = ['date' => date('Y-m-d'), 'amount' => 0]; $solde_theorique = 0; try { @@ -164,7 +165,7 @@ if (!empty($snapshot['date'])) { } catch (Exception $e) {} } -// --- C. LECTURE BUDGET ET CALCULS --- +// Lecture Budget $stmt = $pdo->query("SELECT id, name, amount, type, category, is_estimate, payment_day, is_checked, mapping_keywords FROM pf_budget_items ORDER BY name ASC"); while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { $rawAmount = (float)$item['amount']; @@ -173,11 +174,9 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { $pDay = (int)$item['payment_day']; $isChecked = (int)$item['is_checked']; - // Remplissage liste Charges Fixes (Selecteur) if ($item['category'] === 'expense' && $item['type'] === 'Mensuel' && (int)$item['is_estimate'] === 0) { $fixedChargesList[] = $item; } - // Remplissage liste Revenus (Selecteur) if ($item['category'] === 'income') { $incomeList[] = $item; $total_income += $amt; @@ -185,20 +184,11 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { } else { $total_expenses_prevues += $amt; - // --- CALCUL INTELLIGENT DU "RESTE A VENIR" --- + // Calcul intelligent Reste à venir if ($item['category'] === 'expense' && $item['type'] === 'Mensuel' && (int)$item['is_estimate'] === 0) { - $isPaid = false; - - // 1. Est-ce coché manuellement ? - if ($isChecked === 1) { - $isPaid = true; - } - // 2. Est-ce lié par ID (Import CSV ou Manuel) ? - elseif (in_array($item['id'], $paidItemIds)) { - $isPaid = true; - } - // 3. Est-ce détecté par mot-clé ? + if ($isChecked === 1) $isPaid = true; + elseif (in_array($item['id'], $paidItemIds)) $isPaid = true; elseif (!empty($item['mapping_keywords'])) { $keywords = array_map('trim', explode(',', $item['mapping_keywords'])); foreach ($keywords as $kw) { @@ -211,14 +201,9 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { } } } - - // Si ce n'est PAS payé/trouvé, on l'ajoute au reste à venir - if (!$isPaid) { - $reste_a_venir += $rawAmount; - } + if (!$isPaid) $reste_a_venir += $rawAmount; } - // Remplissage des jauges if ($name === 'Estimacio F&B & beauty') $budget_fmcg = $amt; elseif ($name === 'Estimacio escola') $budget_school = $amt; elseif ($name === 'Estimation gasolina') $budget_essence = $amt; @@ -228,9 +213,8 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { } } -// --- D. CATÉGORIES TEMPORAIRES (Le bloc manquant !) --- -$tempCats = []; -$total_temp_budget = 0; +// Catégories Temporaires +$tempCats = []; $total_temp_budget = 0; try { $stmt = $pdo->prepare("SELECT * FROM pf_monthly_categories WHERE month_year = ?"); $stmt->execute([$currentMonthKey]); @@ -242,7 +226,6 @@ try { } } catch (Exception $e) {} -// --- E. CALCUL DU BUDGET "AUTRES" --- $budget_autres = $total_income - ($total_expenses_prevues + $total_temp_budget); if ($budget_autres < 0) $budget_autres = 0; @@ -251,9 +234,7 @@ if ($budget_autres < 0) $budget_autres = 0; // ============================================================================ $categoriesConfig = [ - // NOUVEAU : Catégorie Revenus 'Income' => ['type'=>'credit', 'label'=>'Revenus', 'budget'=>$budget_income_prevu, 'color'=>'#10b981', 'suggestions'=>[]], - 'FMCG' => ['type'=>'debit', 'label'=>'Courses (FMCG)', 'budget'=>$budget_fmcg, 'color'=>'#3b82f6', 'suggestions'=>['Action', 'Carrefour', 'Lidl']], 'Essence' => ['type'=>'debit', 'label'=>'Essence', 'budget'=>$budget_essence, 'color'=>'#f59e0b', 'suggestions'=>['Audi', 'Polo']], 'School' => ['type'=>'debit', 'label'=>'École / Garde', 'budget'=>$budget_school, 'color'=>'#10b981', 'suggestions'=>[]], @@ -305,7 +286,6 @@ if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) { } // DÉPENSES EN BDD -$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"); $stmt->execute([$currentMonth, $currentYear]); $allExpenses = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -317,14 +297,9 @@ foreach ($allExpenses as $exp) { $cat = $exp['category']; if (!isset($totals[$cat])) $cat = 'Autres'; - // Si la dépense est un crédit (montant < 0), pour Income on l'ajoute au total "reçu" - // Pour les autres catégories (réserves), ça augmente le budget if ($exp['amount'] < 0) { - if ($cat === 'Income') { - $totals[$cat] += abs($exp['amount']); // On somme les revenus - } else { - $categoriesConfig[$cat]['budget'] += abs($exp['amount']); // Réserve - } + if ($cat === 'Income') $totals[$cat] += abs($exp['amount']); + else $categoriesConfig[$cat]['budget'] += abs($exp['amount']); } else { $totals[$cat] += $exp['amount']; } @@ -337,12 +312,7 @@ $globalBudget = array_sum(array_column($categoriesConfig, 'budget')); function getDisplayLogic($spent, $bg, $type) { if ($type === 'credit') { - // Pour Income et Réserves - // Income : 2200 / 4000 (Reçu / Attendu) - // Réserve : 50 / 400 (Restant / Initial) -> Cas particulier géré plus haut si besoin - // Ici on simplifie : - $remaining = $bg - $spent; - $pct = ($bg > 0) ? max(0, min(100, ($spent / $bg) * 100)) : 0; // % Reçu + $pct = ($bg > 0) ? max(0, min(100, ($spent / $bg) * 100)) : 0; $isOver = false; $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; } else { @@ -394,7 +364,6 @@ function getDisplayLogic($spent, $bg, $type) {