charge
This commit is contained in:
@@ -19,9 +19,8 @@ $nextLink = "?tab=suivi&m=$nextM&y=$nextY";
|
|||||||
$todayLink = "?tab=suivi";
|
$todayLink = "?tab=suivi";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// NOUVEAU : CONFIGURATION DU CYCLE (Dates personnalisées)
|
// CONFIGURATION DU CYCLE (Dates personnalisées)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 1. On lit la configuration du mois en cours
|
|
||||||
$stmtConfig = $pdo->prepare("SELECT content FROM pf_notes WHERE note_type = 'month_config' AND reference_id = ?");
|
$stmtConfig = $pdo->prepare("SELECT content FROM pf_notes WHERE note_type = 'month_config' AND reference_id = ?");
|
||||||
$stmtConfig->execute([$currentMonthKey]);
|
$stmtConfig->execute([$currentMonthKey]);
|
||||||
$configJson = $stmtConfig->fetchColumn();
|
$configJson = $stmtConfig->fetchColumn();
|
||||||
@@ -30,14 +29,12 @@ $monthConfig = $configJson ? json_decode($configJson, true) : null;
|
|||||||
$customStartDate = $monthConfig['start_date'] ?? "$currentYear-$currentMonth-01";
|
$customStartDate = $monthConfig['start_date'] ?? "$currentYear-$currentMonth-01";
|
||||||
$customStartBalance = $monthConfig['start_balance'] ?? 0;
|
$customStartBalance = $monthConfig['start_balance'] ?? 0;
|
||||||
|
|
||||||
// 2. On lit la configuration du mois SUIVANT pour fermer le cycle proprement
|
|
||||||
$nextMonthKey = str_pad($nextM, 2, '0', STR_PAD_LEFT) . '-' . $nextY;
|
$nextMonthKey = str_pad($nextM, 2, '0', STR_PAD_LEFT) . '-' . $nextY;
|
||||||
$stmtNextConfig = $pdo->prepare("SELECT content FROM pf_notes WHERE note_type = 'month_config' AND reference_id = ?");
|
$stmtNextConfig = $pdo->prepare("SELECT content FROM pf_notes WHERE note_type = 'month_config' AND reference_id = ?");
|
||||||
$stmtNextConfig->execute([$nextMonthKey]);
|
$stmtNextConfig->execute([$nextMonthKey]);
|
||||||
$nextConfigJson = $stmtNextConfig->fetchColumn();
|
$nextConfigJson = $stmtNextConfig->fetchColumn();
|
||||||
$nextConfig = $nextConfigJson ? json_decode($nextConfigJson, true) : null;
|
$nextConfig = $nextConfigJson ? json_decode($nextConfigJson, true) : null;
|
||||||
|
|
||||||
// La date de fin est soit la veille du prochain cycle, soit la fin du mois calendaire
|
|
||||||
if ($nextConfig && !empty($nextConfig['start_date'])) {
|
if ($nextConfig && !empty($nextConfig['start_date'])) {
|
||||||
$customEndDate = date('Y-m-d', strtotime($nextConfig['start_date'] . ' -1 day'));
|
$customEndDate = date('Y-m-d', strtotime($nextConfig['start_date'] . ' -1 day'));
|
||||||
} else {
|
} else {
|
||||||
@@ -177,8 +174,8 @@ $reste_a_venir = 0;
|
|||||||
$today_day = (int)date('j');
|
$today_day = (int)date('j');
|
||||||
$fixedChargesList = [];
|
$fixedChargesList = [];
|
||||||
$incomeList = [];
|
$incomeList = [];
|
||||||
|
$pending_charges = []; // Stockage détaillé des charges à venir
|
||||||
|
|
||||||
// MODIFICATION : Les requêtes ciblent désormais les bornes du CYCLE personnalisé, plus le mois strict !
|
|
||||||
$stmtIds = $pdo->prepare("SELECT DISTINCT budget_item_id FROM pf_expenses WHERE date_exp >= ? AND date_exp <= ? AND budget_item_id IS NOT NULL");
|
$stmtIds = $pdo->prepare("SELECT DISTINCT budget_item_id FROM pf_expenses WHERE date_exp >= ? AND date_exp <= ? AND budget_item_id IS NOT NULL");
|
||||||
$stmtIds->execute([$customStartDate, $customEndDate]);
|
$stmtIds->execute([$customStartDate, $customEndDate]);
|
||||||
$paidItemIds = $stmtIds->fetchAll(PDO::FETCH_COLUMN);
|
$paidItemIds = $stmtIds->fetchAll(PDO::FETCH_COLUMN);
|
||||||
@@ -234,7 +231,10 @@ while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$isPaid) $reste_a_venir += $absAmount;
|
if (!$isPaid) {
|
||||||
|
$reste_a_venir += $absAmount;
|
||||||
|
$pending_charges[] = ['name' => $name, 'amount' => $absAmount];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($name === 'Estimacio F&B & beauty') $budget_fmcg = $amt;
|
if ($name === 'Estimacio F&B & beauty') $budget_fmcg = $amt;
|
||||||
@@ -283,7 +283,7 @@ $categoriesConfig['LivretA'] = ['type'=>'debit', 'label'=>'Epargne', 'budget'=>0
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
$csvData = [];
|
$csvData = [];
|
||||||
$showPreview = false;
|
$showPreview = false;
|
||||||
// ... (Gestion CSV Upload inchangée ici)
|
|
||||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
||||||
$file = $_FILES['csv_file']['tmp_name'];
|
$file = $_FILES['csv_file']['tmp_name'];
|
||||||
$handle = fopen($file, "r");
|
$handle = fopen($file, "r");
|
||||||
@@ -311,7 +311,6 @@ if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
|||||||
$showPreview = true;
|
$showPreview = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODIFICATION : Dépenses ciblées sur les bornes du cycle !
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM pf_expenses WHERE date_exp >= ? AND date_exp <= ? ORDER BY date_exp DESC");
|
$stmt = $pdo->prepare("SELECT * FROM pf_expenses WHERE date_exp >= ? AND date_exp <= ? ORDER BY date_exp DESC");
|
||||||
$stmt->execute([$customStartDate, $customEndDate]);
|
$stmt->execute([$customStartDate, $customEndDate]);
|
||||||
$allExpenses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$allExpenses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
@@ -330,7 +329,6 @@ foreach ($allExpenses as $exp) {
|
|||||||
|
|
||||||
$val = (float)$exp['amount'];
|
$val = (float)$exp['amount'];
|
||||||
|
|
||||||
// 1. UI Cards
|
|
||||||
if ($cat === 'Income') {
|
if ($cat === 'Income') {
|
||||||
$totals[$cat] += $val;
|
$totals[$cat] += $val;
|
||||||
} else {
|
} else {
|
||||||
@@ -339,26 +337,29 @@ foreach ($allExpenses as $exp) {
|
|||||||
}
|
}
|
||||||
$expensesByCategory[$cat][] = $exp;
|
$expensesByCategory[$cat][] = $exp;
|
||||||
|
|
||||||
// 2. Mathématiques pures
|
|
||||||
if ($val > 0) {
|
if ($val > 0) {
|
||||||
if ($cat === 'Income' || $cat !== 'Frais') {
|
if ($cat === 'Income' || $cat !== 'Frais') {
|
||||||
$total_rentrees += $val; // Rentrées d'argent
|
$total_rentrees += $val;
|
||||||
} else {
|
} else {
|
||||||
$depenses_reelles -= $val; // Remboursement d'une charge fixe
|
$depenses_reelles -= $val;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$depenses_reelles += abs($val);
|
$depenses_reelles += abs($val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Calcul de la Capacité Max Dynamique
|
|
||||||
$rentrees_salaires_reels = $totals['Income'];
|
$rentrees_salaires_reels = $totals['Income'];
|
||||||
$rentrees_autres = $total_rentrees - $rentrees_salaires_reels;
|
$rentrees_autres = $total_rentrees - $rentrees_salaires_reels;
|
||||||
|
|
||||||
// Capacité Max = Solde de départ + (le plus grand entre le salaire prévu et reçu) + rentrées annexes
|
|
||||||
$capacite_max = $solde_initial + max($rentrees_salaires_reels, $budget_income_prevu) + $rentrees_autres;
|
$capacite_max = $solde_initial + max($rentrees_salaires_reels, $budget_income_prevu) + $rentrees_autres;
|
||||||
|
|
||||||
// SOLDE THÉORIQUE = Solde initial + Toutes les rentrées réelles - Dépenses - Charges à venir
|
// Ajout du reste estimé pour l'École dans les charges à venir
|
||||||
|
$ecole_depense = isset($totals['School']) ? $totals['School'] : 0;
|
||||||
|
$reste_ecole = max(0, $budget_school - $ecole_depense);
|
||||||
|
if ($reste_ecole > 0) {
|
||||||
|
$reste_a_venir += $reste_ecole;
|
||||||
|
$pending_charges[] = ['name' => 'École / Garde (Reste estimé)', 'amount' => $reste_ecole];
|
||||||
|
}
|
||||||
|
|
||||||
$solde_theorique = ($solde_initial + $total_rentrees) - $depenses_reelles - $reste_a_venir;
|
$solde_theorique = ($solde_initial + $total_rentrees) - $depenses_reelles - $reste_a_venir;
|
||||||
|
|
||||||
$solde_net = max(0, $solde_actuel - $reste_a_venir);
|
$solde_net = max(0, $solde_actuel - $reste_a_venir);
|
||||||
@@ -428,11 +429,27 @@ $cycleDisplay = date('d/m', strtotime($customStartDate)) . ' au ' . date('d/m',
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding:15px; background:#fef2f2; border-radius:12px; border:1px solid #fecaca;">
|
<div style="padding:15px; background:#fef2f2; border-radius:12px; border:1px solid #fecaca; position: relative;">
|
||||||
<div style="font-size:0.85rem; color:#991b1b; margin-bottom:4px;">Charges fixes à venir</div>
|
<div style="font-size:0.85rem; color:#991b1b; margin-bottom:4px; display:flex; justify-content:space-between; align-items:center;">
|
||||||
|
<span>Charges à venir</span>
|
||||||
|
<button onclick="toggleDiv('pendingDetailsList')" style="background:none; border:none; cursor:pointer; font-size:1rem; padding:0; filter: grayscale(1); opacity: 0.7; transition: 0.2s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.7" title="Voir le détail">👁️</button>
|
||||||
|
</div>
|
||||||
<div style="font-size:1.4rem; font-weight:700; color:#b91c1c;">
|
<div style="font-size:1.4rem; font-weight:700; color:#b91c1c;">
|
||||||
- <?= number_format($reste_a_venir, 2, ',', ' ') ?> €
|
- <?= number_format($reste_a_venir, 2, ',', ' ') ?> €
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="pendingDetailsList" style="display:none; margin-top: 12px; padding-top: 12px; border-top: 1px dashed #fca5a5; font-size: 0.8rem; color: #7f1d1d;">
|
||||||
|
<?php if (empty($pending_charges)): ?>
|
||||||
|
<div style="text-align:center; font-style:italic; opacity:0.8;">Tout est payé ! 🎉</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach($pending_charges as $pc): ?>
|
||||||
|
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
|
||||||
|
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 70%;"><?= htmlspecialchars($pc['name']) ?></span>
|
||||||
|
<strong><?= number_format($pc['amount'], 0, ',', ' ') ?> €</strong>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -822,13 +839,11 @@ $cycleDisplay = date('d/m', strtotime($customStartDate)) . ' au ' . date('d/m',
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// --- UI ---
|
|
||||||
function toggleDiv(id) { const el = document.getElementById(id); el.style.display = (el.style.display === 'none') ? 'block' : 'none'; }
|
function toggleDiv(id) { const el = document.getElementById(id); el.style.display = (el.style.display === 'none') ? 'block' : 'none'; }
|
||||||
function openSuiviModal(id) { document.getElementById(id).style.display = 'flex'; }
|
function openSuiviModal(id) { document.getElementById(id).style.display = 'flex'; }
|
||||||
function closeSuiviModal(id) { document.getElementById(id).style.display = 'none'; }
|
function closeSuiviModal(id) { document.getElementById(id).style.display = 'none'; }
|
||||||
window.onclick = function(event) { if (event.target.classList.contains('pf-modal')) event.target.style.display = 'none'; }
|
window.onclick = function(event) { if (event.target.classList.contains('pf-modal')) event.target.style.display = 'none'; }
|
||||||
|
|
||||||
// --- SAUVEGARDE CONFIGURATION CYCLE (Utilise la route save_note existante) ---
|
|
||||||
function saveMonthConfig() {
|
function saveMonthConfig() {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'save_note');
|
formData.append('action', 'save_note');
|
||||||
@@ -850,7 +865,6 @@ function saveMonthConfig() {
|
|||||||
.catch(e => alert("Erreur lors de la sauvegarde du cycle."));
|
.catch(e => alert("Erreur lors de la sauvegarde du cycle."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SAISIE / EDITION MANUELLE ---
|
|
||||||
const suggestions = <?= json_encode(array_map(fn($c) => $c['suggestions'], $categoriesConfig)) ?>;
|
const suggestions = <?= json_encode(array_map(fn($c) => $c['suggestions'], $categoriesConfig)) ?>;
|
||||||
|
|
||||||
function handleModalCatChange(select) {
|
function handleModalCatChange(select) {
|
||||||
@@ -918,7 +932,6 @@ function handleModalCatChange(select) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ouvre la modale pour AJOUTER
|
|
||||||
function openAddModal(catKey, catLabel) {
|
function openAddModal(catKey, catLabel) {
|
||||||
openSuiviModal('manualExpenseModal');
|
openSuiviModal('manualExpenseModal');
|
||||||
document.getElementById('modalTitle').innerText = "Ajouter : " + catLabel;
|
document.getElementById('modalTitle').innerText = "Ajouter : " + catLabel;
|
||||||
@@ -938,7 +951,6 @@ function openAddModal(catKey, catLabel) {
|
|||||||
setTimeout(() => document.getElementById('modalLabelInput').focus(), 100);
|
setTimeout(() => document.getElementById('modalLabelInput').focus(), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ouvre la modale pour MODIFIER
|
|
||||||
function openEditModal(expenseData) {
|
function openEditModal(expenseData) {
|
||||||
openSuiviModal('manualExpenseModal');
|
openSuiviModal('manualExpenseModal');
|
||||||
document.getElementById('modalTitle').innerText = "Modifier la transaction";
|
document.getElementById('modalTitle').innerText = "Modifier la transaction";
|
||||||
@@ -963,7 +975,6 @@ function openEditModal(expenseData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- IMPORT CSV ---
|
|
||||||
let newCatIndex = 0;
|
let newCatIndex = 0;
|
||||||
function confirmNewCat() {
|
function confirmNewCat() {
|
||||||
const name = document.getElementById('newCatName').value.trim();
|
const name = document.getElementById('newCatName').value.trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user