From 910d21da5a961b9873103c49b695c1c76762cf49 Mon Sep 17 00:00:00 2001 From: "fefe.clochette" Date: Mon, 16 Feb 2026 10:07:33 +0100 Subject: [PATCH] income --- modules/budget/views/suivi.php | 116 ++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 24 deletions(-) diff --git a/modules/budget/views/suivi.php b/modules/budget/views/suivi.php index f710895..f8d83c2 100644 --- a/modules/budget/views/suivi.php +++ b/modules/budget/views/suivi.php @@ -58,6 +58,7 @@ 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; @@ -92,12 +93,19 @@ if (isset($_POST['action']) && $_POST['action'] === 'add_expense') { if ($cat === 'School' && !empty($_POST['label_select'])) { $label = trim($_POST['label_select']); } - elseif ($cat === 'Frais' && !empty($_POST['budget_item_id'])) { + // 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); + } + $pdo->prepare("INSERT INTO pf_expenses (date_exp, category, label, amount, import_ref, budget_item_id) VALUES (?, ?, ?, ?, ?, ?)") ->execute([$date, $cat, $label, $amount, $uniqueRef, $budgetItemId]); header("Location: ?tab=suivi"); exit; @@ -111,16 +119,17 @@ if (isset($_GET['delete_expense'])) { } // ============================================================================ -// 2. CALCUL DES BUDGETS & CHARGES FIXES +// 2. CALCUL DES BUDGETS & LISTES DÉROULANTES // ============================================================================ -$budget_fmcg = 0; $budget_school = 0; $budget_essence = 0; $budget_frais = 0; +$budget_fmcg = 0; $budget_school = 0; $budget_essence = 0; $budget_frais = 0; $budget_income_prevu = 0; $total_income = 0; $total_expenses_prevues = 0; $reste_a_venir = 0; $today_day = (int)date('j'); -// Liste des charges pour le selecteur +// Listes pour les selecteurs $fixedChargesList = []; +$incomeList = []; // NOUVEAU : Liste des revenus // Snapshot $snapshot = ['date' => date('Y-m-d'), 'amount' => 0]; @@ -154,13 +163,16 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) { if ($item['category'] === 'expense' && $item['type'] === 'Mensuel' && (int)$item['is_estimate'] === 0) { $fixedChargesList[] = $item; } - + // Remplissage liste Revenus if ($item['category'] === 'income') { + $incomeList[] = $item; $total_income += $amt; + // On peut vouloir suivre le budget des revenus aussi + $budget_income_prevu += $amt; } else { $total_expenses_prevues += $amt; - // On additionne si c'est une dépense mensuelle NON cochée + // Calcul Reste à venir if ($item['category'] === 'expense' && $item['type'] === 'Mensuel') { if ($isChecked === 0 && (int)$item['is_estimate'] === 0) { $reste_a_venir += $rawAmount; @@ -193,10 +205,13 @@ 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'=>[]], - 'Frais' => ['type'=>'debit', 'label'=>'Charges Fixes', 'budget'=>$budget_frais, 'color'=>'#ef4444', 'suggestions'=>[]], // Suggestions vides car gérées par ID + 'Frais' => ['type'=>'debit', 'label'=>'Charges Fixes', 'budget'=>$budget_frais, 'color'=>'#ef4444', 'suggestions'=>[]], ]; $tempColors = ['#ec4899', '#06b6d4', '#84cc16', '#d946ef', '#f97316']; @@ -256,8 +271,17 @@ foreach ($allExpenses as $exp) { $cat = $exp['category']; if (!isset($totals[$cat])) $cat = 'Autres'; - if ($exp['amount'] < 0) $categoriesConfig[$cat]['budget'] += abs($exp['amount']); - else $totals[$cat] += $exp['amount']; + // 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 + } + } else { + $totals[$cat] += $exp['amount']; + } $expensesByCategory[$cat][] = $exp; } @@ -267,10 +291,14 @@ $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, ($remaining / $bg) * 100)) : 0; - $isOver = ($remaining < 0); - $text = number_format(ceil($remaining), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; + $pct = ($bg > 0) ? max(0, min(100, ($spent / $bg) * 100)) : 0; // % Reçu + $isOver = false; + $text = number_format(ceil($spent), 0, ',', ' ') . ' / ' . number_format(ceil($bg), 0, ',', ' ') . ' €'; } else { $pct = ($bg > 0) ? min(100, ($spent / $bg) * 100) : ($spent > 0 ? 100 : 0); $isOver = ($spent > $bg && $bg > 0); @@ -313,15 +341,15 @@ function getDisplayLogic($spent, $bg, $type) {
- - / + Total Mouvements
$conf): $logic = getDisplayLogic($totals[$key], $conf['budget'], $conf['type']); - $barCol = $logic['isOver'] ? '#ef4444' : $conf['color']; + // Couleur barre : Vert pour Income, Rouge si dépassement Debit + $barCol = ($key === 'Income') ? '#10b981' : ($logic['isOver'] ? '#ef4444' : $conf['color']); ?>
@@ -414,18 +442,25 @@ function getDisplayLogic($spent, $bg, $type) {
- + +
@@ -533,6 +568,16 @@ function getDisplayLogic($spent, $bg, $type) {
+ +
@@ -612,15 +657,21 @@ function openAddModal(catKey, catLabel) { const blockText = document.getElementById('blockInputText'); const blockSelect = document.getElementById('blockInputSelect'); const blockFrais = document.getElementById('blockInputFrais'); + const blockIncome = document.getElementById('blockInputIncome'); + const inputLabel = document.getElementById('modalLabelInput'); const selectFrais = document.getElementById('fraisSelect'); + const selectIncome = document.getElementById('incomeSelect'); // Reset visibility blockText.style.display = 'none'; blockSelect.style.display = 'none'; blockFrais.style.display = 'none'; + blockIncome.style.display = 'none'; + inputLabel.required = false; selectFrais.required = false; + selectIncome.required = false; if (catKey === 'School') { blockSelect.style.display = 'block'; @@ -630,6 +681,11 @@ function openAddModal(catKey, catLabel) { blockFrais.style.display = 'block'; selectFrais.required = true; } + else if (catKey === 'Income') { + blockText.style.display = 'block'; + blockIncome.style.display = 'block'; + selectIncome.required = true; + } else { blockText.style.display = 'block'; inputLabel.required = true; @@ -661,14 +717,22 @@ function confirmNewCat() { function handleLineCatChange(select) { const row = select.closest('tr'); - const secondarySelect = row.querySelector('.budget-item-select'); + const fraisSelect = row.querySelector('.select-frais'); + const incomeSelect = row.querySelector('.select-income'); + // Reset + fraisSelect.style.display = 'none'; + incomeSelect.style.display = 'none'; + fraisSelect.value = ''; + incomeSelect.value = ''; + if (select.value === 'Frais') { - secondarySelect.style.display = 'block'; - } else { - secondarySelect.style.display = 'none'; - secondarySelect.value = ''; + fraisSelect.style.display = 'block'; + } + else if (select.value === 'Income') { + incomeSelect.style.display = 'block'; } + checkValidation(); } @@ -681,7 +745,8 @@ function checkValidation() { const row = cb.closest('tr'); const isCredit = row.querySelector('.is-credit-flag').value === '1'; const mainCat = row.querySelector('.line-select').value; - const subCatSelect = row.querySelector('.budget-item-select'); + const fraisSelect = row.querySelector('.select-frais'); + const incomeSelect = row.querySelector('.select-income'); let rowValid = true; @@ -689,7 +754,10 @@ function checkValidation() { if(!isCredit) rowValid = false; } else if (mainCat === 'Frais') { - if (subCatSelect.value === "") rowValid = false; + if (fraisSelect.value === "") rowValid = false; + } + else if (mainCat === 'Income') { + if (incomeSelect.value === "") rowValid = false; } if (!rowValid) {