From 4c5646766a232c4c500be2f8e5d9cc7912f83b3f Mon Sep 17 00:00:00 2001 From: "fefe.clochette" Date: Fri, 20 Mar 2026 12:16:58 +0100 Subject: [PATCH] suivi --- modules/budget/views/suivi.php | 190 +++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 79 deletions(-) diff --git a/modules/budget/views/suivi.php b/modules/budget/views/suivi.php index 10b1fe5..6f611b6 100644 --- a/modules/budget/views/suivi.php +++ b/modules/budget/views/suivi.php @@ -20,7 +20,6 @@ $prevLink = "?tab=suivi&m=$prevM&y=$prevY"; $nextLink = "?tab=suivi&m=$nextM&y=$nextY"; $todayLink = "?tab=suivi"; - // A. AJOUT CATÉGORIE TEMPORAIRE MANUELLE if (isset($_POST['action']) && $_POST['action'] === 'add_temp_cat') { $name = trim($_POST['cat_name']); @@ -52,12 +51,9 @@ if (isset($_POST['action']) && $_POST['action'] === 'save_snapshot') { // D. SAUVEGARDE IMPORT CSV (LIGNE PAR LIGNE AVEC CHECKBOX) if (isset($_POST['action']) && $_POST['action'] === 'save_import') { $count = 0; - - // On récupère le mois affiché sur l'écran lors de l'import $viewMonth = $_POST['view_month'] ?? $currentMonth; $viewYear = $_POST['view_year'] ?? $currentYear; - // 1. Catégories temporaires $tempCatMapping = []; if (!empty($_POST['new_temp_cats'])) { $stmtTemp = $pdo->prepare("INSERT INTO pf_monthly_categories (month_year, name, type, budget) VALUES (?, ?, ?, 0)"); @@ -87,15 +83,12 @@ if (isset($_POST['action']) && $_POST['action'] === 'save_import') { $finalAmount = $is_credit ? -abs($line['amount']) : abs($line['amount']); - // NOUVEAU : GESTION DU DÉCALAGE SI LA COCHE EST SÉLECTIONNÉE $dateToSave = $line['date']; if (!empty($line['force_current'])) { $day = date('d', strtotime($dateToSave)); - // On vérifie que la date existe (ex: pour éviter le 31 Février) if (checkdate((int)$viewMonth, (int)$day, (int)$viewYear)) { $dateToSave = "$viewYear-$viewMonth-$day"; } else { - // Sinon, on met au dernier jour du mois visé $dateToSave = date('Y-m-t', strtotime("$viewYear-$viewMonth-01")); } } @@ -167,32 +160,35 @@ $today_day = (int)date('j'); $fixedChargesList = []; $incomeList = []; -// 1. Récupération des IDs de charges fixes déjà payées (Dépenses uniquement) $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 AND amount > 0"); $stmtIds->execute([$currentMonth, $currentYear]); $paidItemIds = $stmtIds->fetchAll(PDO::FETCH_COLUMN); -// 2. Récupération libellés (Uniquement les dépenses pour éviter qu'un revenu ne valide une charge) $stmtLabels = $pdo->prepare("SELECT label FROM pf_expenses WHERE MONTH(date_exp) = ? AND YEAR(date_exp) = ? AND amount > 0"); $stmtLabels->execute([$currentMonth, $currentYear]); $realExpensesLabels = $stmtLabels->fetchAll(PDO::FETCH_COLUMN); // Snapshot $snapshot = ['date' => date('Y-m-d'), 'amount' => 0]; -$solde_theorique = 0; try { $snapStmt = $pdo->query("SELECT * FROM pf_bank_snapshots ORDER BY id DESC LIMIT 1"); if ($s = $snapStmt->fetch(PDO::FETCH_ASSOC)) { $snapshot = ['date' => $s['snapshot_date'], 'amount' => (float)$s['amount']]; } } catch (Exception $e) {} -$solde_theorique = $snapshot['amount']; + +$solde_actuel = $snapshot['amount']; +$solde_theorique = $solde_actuel; // On part du solde actuel +$today_day = (int)date('j'); + if (!empty($snapshot['date'])) { try { $stmtCalc = $pdo->prepare("SELECT SUM(amount) as total_diff FROM pf_expenses WHERE date_exp > ?"); $stmtCalc->execute([$snapshot['date']]); $resDiff = $stmtCalc->fetch(PDO::FETCH_ASSOC); - if ($resDiff && $resDiff['total_diff'] !== null) $solde_theorique -= (float)$resDiff['total_diff']; + if ($resDiff && $resDiff['total_diff'] !== null) { + $solde_theorique -= (float)$resDiff['total_diff']; + } } catch (Exception $e) {} } @@ -215,7 +211,6 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { } else { $total_expenses_prevues += $amt; - // Calcul intelligent Reste à venir if ($item['category'] === 'expense' && $item['type'] === 'Mensuel' && (int)$item['is_estimate'] === 0) { $isPaid = false; if ($isChecked === 1) $isPaid = true; @@ -232,7 +227,13 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { } } } - if (!$isPaid) $reste_a_venir += $rawAmount; + + if (!$isPaid) { + $reste_a_venir += $rawAmount; + if ($pDay > 0 && $pDay <= $today_day) { + $solde_theorique -= $rawAmount; + } + } } if ($name === 'Estimacio F&B & beauty') $budget_fmcg = $amt; @@ -328,7 +329,7 @@ foreach ($allExpenses as $exp) { $cat = $exp['category']; if (!isset($totals[$cat])) $cat = 'Autres'; - + // Le Vase d'Expansion fonctionne ici : if ($cat === 'Income') { $totals[$cat] += abs($exp['amount']); } else { @@ -338,39 +339,41 @@ foreach ($allExpenses as $exp) { $totals[$cat] += $exp['amount']; } } - $expensesByCategory[$cat][] = $exp; } -$globalSpent = array_sum($totals); -$globalBudget = array_sum(array_column($categoriesConfig, 'budget')); +// --- Calculs pour la Barre de Déchargement --- +$solde_actuel = max(0, $snapshot['amount']); +$charges_a_venir = max(0, $reste_a_venir); +$solde_net = max(0, $solde_actuel - $charges_a_venir); +$charges_visibles = min($solde_actuel, $charges_a_venir); // On ne peut pas afficher plus de charges que ce qu'il reste sur le compte + +// L'échelle max (100% de la barre) s'adapte à ton solde max ou ton budget prévu +$max_scale = max($solde_actuel, $solde_theorique, $budget_income_prevu, 1); + +// Pourcentages (largeurs) +$pct_net = ($solde_net / $max_scale) * 100; +$pct_charges = ($charges_visibles / $max_scale) * 100; + +// Placements des repères (bloqués entre 0 et 100%) +$pct_theorique = min(100, max(0, ($solde_theorique / $max_scale) * 100)); +$pct_actuel = min(100, max(0, ($solde_actuel / $max_scale) * 100)); function getDisplayLogic($spent, $bg, $type) { if ($type === 'credit') { - // Pour les revenus : $spent = revenu perçu, $bg = revenu prévu $pct = ($bg > 0) ? min(100, ($spent / $bg) * 100) : ($spent > 0 ? 100 : 0); - $isOver = false; // On n'est jamais "dans le rouge" avec les revenus ! - - if ($bg > 0) { - $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; - } else { - $text = number_format(ceil($spent), 0, ',', ' ') . ' €'; - } + $isOver = false; + if ($bg > 0) { $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; } + else { $text = number_format(ceil($spent), 0, ',', ' ') . ' €'; } } else { - // Pour les dépenses $pct = ($bg > 0) ? min(100, ($spent / $bg) * 100) : ($spent > 0 ? 100 : 0); $isOver = ($spent > $bg && $bg > 0); - - if ($bg > 0) { - $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; - } else { - $text = number_format(ceil($spent), 0, ',', ' ') . ' €'; - } + if ($bg > 0) { $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; } + else { $text = number_format(ceil($spent), 0, ',', ' ') . ' €'; } } return ['pct' => $pct, 'isOver' => $isOver, 'text' => $text]; } -// Nom du mois en français (Compatible PHP 8+) $moisFr = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']; $monthName = $moisFr[(int)$currentMonth] . ' ' . $currentYear; ?> @@ -378,54 +381,87 @@ $monthName = $moisFr[(int)$currentMonth] . ' ' . $currentYear;
-
-
-
-
-

Suivi :

-
- - Auj. - -
-
- -
- Charges à venir : -
- -
- Solde au : - - -
- -
- Solde théorique au : - -
-
-
- Total Mouvements +
+ +
+

Suivi :

+
+ + Auj. +
-
- $conf): - $logic = getDisplayLogic($totals[$key], $conf['budget'], $conf['type']); - $barCol = ($key === 'Income') ? '#10b981' : ($logic['isOver'] ? '#ef4444' : $conf['color']); - ?> -
-
- - +
+ +
+
Solde bancaire (au )
+
+
-
-
+ +
+ +
+
Solde théorique (au )
+
+
- + +
+
Charges fixes à venir
+
+ - € +
+
+
+ + + +
+ +
0 €
+
+ Capacité Max : € +
+ +
+
Actuel
+
+
+
+ +
+
+
+
+ +
+
+
+
Théorique
+
+
+ +
+ Dispo Net : + Provisions : +
+
@@ -474,9 +510,7 @@ $monthName = $moisFr[(int)$currentMonth] . ' ' . $currentYear;

Valider l'importation

-
-
@@ -504,9 +538,7 @@ $monthName = $moisFr[(int)$currentMonth] . ' ' . $currentYear;
-