@@ -5163,7 +5328,7 @@ function getTranslatedMonthName($dateString) {
-
+
= tr('bud_prev_new_line_title') ?>
@@ -5553,33 +5718,23 @@ function updateSumResult() {
}
async function deleteCategory(id) {
- if (!confirm(window.I18N['bud_prev_confirm_del_line'] || "Confirmer la suppression ?")) return;
+ if (!confirm(tr('bud_prev_confirm_del_line'))) return;
const formData = new FormData();
formData.append('action', 'delete_category');
formData.append('id', id);
- formData.append('ajax', '1');
+ formData.append('ajax', '1'); //
try {
- // đĄ CORRECTION : Chemin relatif (sans le "/" au dĂ©but) pour s'adapter Ă ton localhost
- const response = await fetch('modules/budget/includes/api/save-budget.php', {
+ const result = await pachaFetch('modules/budget/includes/api/save-budget.php', {
method: 'POST',
body: formData
});
- if (!response.ok) throw new Error(`Erreur réseau HTTP ${response.status}`);
-
- const result = await response.json();
-
if (result.success) {
window.location.reload();
- } else {
- alert((window.I18N['bud_err_tech'] || 'Erreur technique') + " : Suppression impossible.");
}
- } catch(e) {
- console.error("Erreur Fetch Suppression:", e);
- alert("Erreur de communication avec le serveur. Regarde la console (F12) pour plus de détails.");
- }
+ } catch(e) { console.error(e); }
}
document.addEventListener('click', function(e) {
@@ -5607,7 +5762,7 @@ document.addEventListener('DOMContentLoaded', recalcAllAllocations);
// --- INTERCEPTION ASYNCHRONE DES FORMULAIRES DE MODALES ---
document.querySelectorAll('#addCatModal form, #editCatModal form').forEach(form => {
form.addEventListener('submit', async (e) => {
- e.preventDefault(); // đ Bloque le rechargement HTML par dĂ©faut
+ e.preventDefault();
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.innerText;
@@ -5619,31 +5774,24 @@ document.querySelectorAll('#addCatModal form, #editCatModal form').forEach(form
const formData = new FormData(form);
formData.append('ajax', '1');
- // đĄ CORRECTION DU PIĂGE : On utilise getAttribute() pour forcer la lecture de l'URL !
const actionUrl = form.getAttribute('action');
- // On s'assure du bon chemin (chemin relatif depuis budget.php)
- const finalUrl = actionUrl.startsWith('/') ? actionUrl.substring(1) : actionUrl;
-
- const response = await fetch(finalUrl, {
+ // On utilise pachaFetch au lieu de fetch
+ const result = await pachaFetch(actionUrl, {
method: 'POST',
body: formData
});
- if (!response.ok) throw new Error(`Erreur HTTP: ${response.status}`);
-
- const result = await response.json();
-
if (result.success) {
form.closest('.pf-modal').style.display = 'none';
document.body.classList.remove('no-scroll');
window.location.reload();
} else {
- alert((window.I18N['bud_err_tech'] || 'Erreur technique') + " : " + (result.error || "Inconnue"));
+ alert((window.I18N['bud_err_tech'] || 'Erreur') + " : " + (result.error || "Inconnue"));
}
} catch (error) {
console.error("Erreur Fetch Modale:", error);
- alert("Une erreur technique est survenue lors de l'enregistrement.");
+ alert("Une erreur technique est survenue.");
} finally {
submitBtn.disabled = false;
submitBtn.innerText = originalText;
@@ -6235,18 +6383,30 @@ $currentMonth = isset($_GET['m']) ? str_pad((int)$_GET['m'], 2, '0', STR_PAD_LEF
$currentYear = isset($_GET['y']) ? (int)$_GET['y'] : date('Y', strtotime($defaultActiveMonth));
$viewMonthDate = "$currentYear-$currentMonth-01";
-// Conservation du tableau d'origine pour la correspondance en base de données
-$moisFr = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];
+$sqlReal = "SELECT budget_item_id, SUM(amount) as total_real
+ FROM pf_expenses
+ WHERE gestion_month = ? AND budget_item_id IS NOT NULL
+ GROUP BY budget_item_id";
+$stmtReal = $pdo->prepare($sqlReal);
+$stmtReal->execute([$viewMonthDate]);
+$realTotals = $stmtReal->fetchAll(PDO::FETCH_KEY_PAIR); // Retourne un tableau [id => total]
-// Affichage dynamique traduit
+$sqlCatReal = "SELECT category, SUM(amount) as total_real
+ FROM pf_expenses
+ WHERE gestion_month = ? AND budget_item_id IS NULL
+ GROUP BY category";
+$stmtCatReal = $pdo->prepare($sqlCatReal);
+$stmtCatReal->execute([$viewMonthDate]);
+$catTotals = $stmtCatReal->fetchAll(PDO::FETCH_KEY_PAIR);
+
+$stmtLabels = $pdo->prepare("SELECT label, amount FROM pf_expenses WHERE gestion_month = ? AND budget_item_id IS NULL");
+$stmtLabels->execute([$viewMonthDate]);
+$unlinkedExpenses = $stmtLabels->fetchAll(PDO::FETCH_ASSOC);
+
+$moisFr = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];
$monthTranslationKey = 'month_' . str_pad((int)$currentMonth, 2, '0', STR_PAD_LEFT);
$currentMonthName = tr($monthTranslationKey) . ' ' . $currentYear;
-// 4. Récupération des Dépenses Réelles du mois (Basé sur le Mois de Gestion !)
-$stmtExp = $pdo->prepare("SELECT amount, label, category, budget_item_id FROM pf_expenses WHERE gestion_month = ?");
-$stmtExp->execute([$viewMonthDate]);
-$allExpenses = $stmtExp->fetchAll(PDO::FETCH_ASSOC);
-
$totalDepenses = 0;
$totalRevenus = 0;
?>
@@ -6272,59 +6432,50 @@ $totalRevenus = 0;
= ($targetAbs - 0.10)));
- // --- 3. LOGIQUE D'ĂTAT ---
- $isAutoChecked = false;
- if ($hasMatchingExpense && ($realAbs >= ($targetAbs - 0.10))) {
- $isAutoChecked = true;
- }
-
- // Styles
$rowClass = ($item['category'] === 'income') ? 'row-income' : 'row-expense';
if ($item['is_estimate']) $rowClass .= ' row-estimate';
?>
@@ -7474,29 +7625,19 @@ window.addEventListener('click', (e) => {
// --- 2. SUPPRESSION ASYNCHRONE ---
async function deleteExpense(id) {
- if (!confirm(window.I18N['bud_confirm_delete'] || "Confirmer ?")) return;
+ const confirmed = await pachaConfirm("Suppression", tr('bud_confirm_delete'));
+ if (!confirmed) return;
const formData = new FormData();
formData.append("action", "delete_expense");
formData.append("id", id);
try {
- // Envoi au handler PHP que nous venons de créer en haut de suivi.php
- const response = await fetch("budget.php?tab=suivi", { method: "POST", body: formData });
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
-
- const textResult = await response.text();
- try {
- const result = JSON.parse(textResult);
- if (result.success) window.location.reload();
- else alert((window.I18N['error_occured'] || 'Erreur') + " : " + (result.error || ""));
- } catch (e) {
- console.error("Réponse non-JSON :", textResult);
- window.location.reload(); // SĂ©curitĂ© : on recharge quand mĂȘme si le serveur dĂ©rive
- }
+ await pachaFetch("budget.php?tab=suivi", { method: "POST", body: formData });
+ showToast("Dépense supprimée !"); //
+ setTimeout(() => window.location.reload(), 800);
} catch (err) {
- console.error(err);
- alert(window.I18N['bud_err_tech'] || 'Erreur technique');
+ showToast("Erreur lors de la suppression", "error");
}
}
@@ -7514,31 +7655,22 @@ document.addEventListener('DOMContentLoaded', () => {
submitBtn.innerText = 'âł ...';
const formData = new FormData(formExpense);
+ // Force l'action ici pour ĂȘtre sĂ»r
formData.set('action', 'save_expense_manual');
- // đĄ Chemin relatif propre !
- const response = await fetch('modules/budget/includes/api/manage-item.php', {
+ const actionUrl = formExpense.getAttribute('action') || 'modules/budget/includes/api/manage-item.php';
+
+ const result = await pachaFetch(actionUrl, {
method: 'POST',
body: formData
});
- if (!response.ok) throw new Error(`Erreur HTTP: ${response.status}`);
-
- // đĄïž Double vĂ©rification JSON contre les warnings PHP
- const textResult = await response.text();
- try {
- const result = JSON.parse(textResult);
- if (result.success) {
- closeSuiviModal('manualExpenseModal');
- formExpense.reset();
- window.location.reload();
- } else {
- const errorMsg = result.error || 'Erreur inconnue';
- alert((window.I18N['error_occured'] || 'Erreur') + ' : ' + errorMsg);
- }
- } catch (jsonErr) {
- console.error("Réponse non-JSON :", textResult);
- alert("Erreur PHP. Vérifie la console (F12).");
+ if (result.success) {
+ closeSuiviModal('manualExpenseModal');
+ formExpense.reset();
+ window.location.reload();
+ } else {
+ alert((window.I18N['error_occured'] || 'Erreur') + ' : ' + (result.error || 'Inconnue'));
}
} catch (error) {
console.error("Erreur réseau :", error);
@@ -9425,9 +9557,9 @@ document.addEventListener("DOMContentLoaded", () => {
const dayLeaves = this.leaves.filter((l) => l.leave_date === iso);
if (dayLeaves.length) {
let html = ``;
- if (dayLeaves.some((l) => l.person_id === 2))
+ if (dayLeaves.some((l) => l.person_id === window.CONFIG.ID_ALEX))
html += `A`;
- if (dayLeaves.some((l) => l.person_id === 3))
+ if (dayLeaves.some((l) => l.person_id === window.CONFIG.ID_LAIA))
html += `L`;
html += ` `;
td.innerHTML += html;
@@ -9787,17 +9919,17 @@ document.addEventListener("DOMContentLoaded", () => {
}
async fetchApi(url) {
- return fetch(url).then((r) => r.json());
+ return pachaFetch(url);
}
async postApi(url, data) {
- const res = await fetch(url, {
+ return pachaFetch(url, {
method: "POST",
- headers: { "Content-Type": "application/json" },
+ headers: {
+ "Content-Type": "application/json",
+ },
body: JSON.stringify(data),
});
- if (!res.ok) throw new Error(await res.text());
- return res.json();
}
async changeSchoolYear(delta) {
@@ -10214,7 +10346,6 @@ document.addEventListener("DOMContentLoaded", () => {
payload,
);
} else if (action === "clear-leaves-person") {
- // --- NOUVELLE ACTION : Suppression ciblée (Alex OU Laia selon le pid) ---
await this.postApi(
"/modules/family-calendar/includes/api/manage-leaf.php",
{
@@ -10224,14 +10355,21 @@ document.addEventListener("DOMContentLoaded", () => {
},
);
} else if (action === "clear-leaves") {
- // --- ACTION GLOBALE : GardĂ©e au cas oĂč vous en auriez besoin ailleurs ---
await this.postApi(
"/modules/family-calendar/includes/api/manage-leaf.php",
- { action: "bulk_delete_day_person", dates, person_id: 2 },
+ {
+ action: "bulk_delete_day_person",
+ dates,
+ person_id: window.CONFIG.ID_ALEX,
+ },
);
await this.postApi(
"/modules/family-calendar/includes/api/manage-leaf.php",
- { action: "bulk_delete_day_person", dates, person_id: 3 },
+ {
+ action: "bulk_delete_day_person",
+ dates,
+ person_id: window.CONFIG.ID_LAIA,
+ },
);
}
@@ -15114,12 +15252,14 @@ if ($selectedYear !== 'all') {
// 2. Récupération des voyages + Calculs
$sql = "
SELECT h.*,
- (COALESCE(h.budget_food, 0) + COALESCE(h.budget_extra, 0) + COALESCE((SELECT SUM(amount) FROM pf_holidays_items WHERE holiday_id = h.id), 0)) as total_cost,
- ((SELECT COALESCE(SUM(ABS(amount)), 0) FROM pf_expenses WHERE holiday_id = h.id) + (SELECT COALESCE(SUM(amount), 0) FROM pf_holidays_items WHERE holiday_id = h.id AND is_paid = 1)) as total_paid,
- (SELECT COALESCE(SUM(amount), 0) FROM pf_savings WHERE holiday_id = h.id) as total_saved
+ (COALESCE(h.budget_food, 0) + COALESCE(h.budget_extra, 0) + COALESCE(SUM(hi.amount), 0)) as total_cost,
+ (SELECT COALESCE(SUM(ABS(amount)), 0) FROM pf_expenses WHERE holiday_id = h.id) as real_expenses_sum,
+ SUM(CASE WHEN hi.is_paid = 1 THEN hi.amount ELSE 0 END) as items_paid_sum
FROM pf_holidays h
+ LEFT JOIN pf_holidays_items hi ON h.id = hi.holiday_id
$whereSQL
- ORDER BY COALESCE(start_date, '2999-12-31') ASC, FIELD(status, 'booked', 'planned', 'draft', 'passed', 'archived')
+ GROUP BY h.id
+ ORDER BY COALESCE(start_date, '2999-12-31') ASC
";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
|