1 parent
a69a8950b3
commit
693b277002
8 files changed
+189
-20
No files matched your search
+10
-2
@@ -586,7 +586,6 @@ return [
|
|||||||
'bs_currency_label' => 'Divisa de la llar',
|
'bs_currency_label' => 'Divisa de la llar',
|
||||||
'bs_currency_desc' => 'Divisa principal utilitzada per mostrar els vostres comptes i pressupostos.',
|
'bs_currency_desc' => 'Divisa principal utilitzada per mostrar els vostres comptes i pressupostos.',
|
||||||
'bs_currency_updated' => 'Divisa actualitzada correctament!',
|
'bs_currency_updated' => 'Divisa actualitzada correctament!',
|
||||||
'bud_settings_dyn_title' => 'Estimacions Dinàmiques (Nens)',
|
|
||||||
'bud_dyn_nanny_fixed' => 'Sou fix Cangur (€)',
|
'bud_dyn_nanny_fixed' => 'Sou fix Cangur (€)',
|
||||||
'bud_dyn_nanny_daily' => 'Despeses diàries de manteniment (€)',
|
'bud_dyn_nanny_daily' => 'Despeses diàries de manteniment (€)',
|
||||||
'bud_dyn_nanny_aid' => 'Ajuda del govern (€)',
|
'bud_dyn_nanny_aid' => 'Ajuda del govern (€)',
|
||||||
@@ -600,7 +599,16 @@ return [
|
|||||||
'bud_audit_child' => '👦 %s :',
|
'bud_audit_child' => '👦 %s :',
|
||||||
'bud_audit_school_fee' => 'Menjador (%dd) + Acollida (%dd) + Casal (%dd) = %.2f€',
|
'bud_audit_school_fee' => 'Menjador (%dd) + Acollida (%dd) + Casal (%dd) = %.2f€',
|
||||||
'bud_audit_sick' => 'Dies de malaltia deduïts : %dd',
|
'bud_audit_sick' => 'Dies de malaltia deduïts : %dd',
|
||||||
'bud_audit_nanny_full' => '💼 %s : Fix (%.2f€) + Despeses (%dd x %.2f€ = %.2f€) - Ajut (%.2f€) - CESU (%.2f€) = %.2f€',
|
'bud_audit_nanny_full' => '💼 %s : Fix (%.2f€) + Despeses (%dd x %.2f€ = %.2f€) - CMG (%.2f€) - %s (%.2f€) = %.2f€',
|
||||||
|
'bud_audit_cesu_avg' => 'CESU (Mitj.)',
|
||||||
|
'bud_audit_cesu_adj' => 'CESU (Ajustat)',
|
||||||
|
'bud_dyn_cesu_overrides' => 'Ajustaments puntuals del CESU',
|
||||||
|
'bud_dyn_cesu_add' => 'Afegir un mes',
|
||||||
|
'bud_dyn_cesu_empty' => 'Cap ajustament registrat.',
|
||||||
|
'bud_settings_dyn_title' => 'Cura i Escola',
|
||||||
|
'bud_settings_dyn_desc' => 'Tarifes per al càlcul automatitzat de les despeses.',
|
||||||
|
'bud_dyn_sub_nanny' => 'Cangur',
|
||||||
|
'bud_dyn_sub_school' => 'Extraescolars i Casal',
|
||||||
|
|
||||||
// CSV
|
// CSV
|
||||||
'bs_csv_title' => 'Format de la importació CSV',
|
'bs_csv_title' => 'Format de la importació CSV',
|
||||||
|
|||||||
+10
-2
@@ -588,7 +588,6 @@ return [
|
|||||||
'bs_currency_label' => 'Household Currency',
|
'bs_currency_label' => 'Household Currency',
|
||||||
'bs_currency_desc' => 'Main currency used for displaying your accounts and budgets.',
|
'bs_currency_desc' => 'Main currency used for displaying your accounts and budgets.',
|
||||||
'bs_currency_updated' => 'Currency updated successfully!',
|
'bs_currency_updated' => 'Currency updated successfully!',
|
||||||
'bud_settings_dyn_title' => 'Dynamic Estimates (Children)',
|
|
||||||
'bud_dyn_nanny_fixed' => 'Nanny fixed salary (€)',
|
'bud_dyn_nanny_fixed' => 'Nanny fixed salary (€)',
|
||||||
'bud_dyn_nanny_daily' => 'Daily maintenance fee (€)',
|
'bud_dyn_nanny_daily' => 'Daily maintenance fee (€)',
|
||||||
'bud_dyn_nanny_aid' => 'Government aid (€)',
|
'bud_dyn_nanny_aid' => 'Government aid (€)',
|
||||||
@@ -602,7 +601,16 @@ return [
|
|||||||
'bud_audit_child' => '👦 %s :',
|
'bud_audit_child' => '👦 %s :',
|
||||||
'bud_audit_school_fee' => 'Canteen (%dd) + Aftercare (%dd) + Center (%dd) = %.2f€',
|
'bud_audit_school_fee' => 'Canteen (%dd) + Aftercare (%dd) + Center (%dd) = %.2f€',
|
||||||
'bud_audit_sick' => 'Sick days deducted : %dd',
|
'bud_audit_sick' => 'Sick days deducted : %dd',
|
||||||
'bud_audit_nanny_full' => '💼 %s : Fixed (%.2f€) + Fees (%dd x %.2f€ = %.2f€) - Aid (%.2f€) - CESU (%.2f€) = %.2f€',
|
'bud_audit_nanny_full' => '💼 %s : Fixed (%.2f€) + Fees (%dd x %.2f€ = %.2f€) - Aid (%.2f€) - %s (%.2f€) = %.2f€',
|
||||||
|
'bud_audit_cesu_avg' => 'CESU (Avg.)',
|
||||||
|
'bud_audit_cesu_adj' => 'CESU (Adjusted)',
|
||||||
|
'bud_dyn_cesu_overrides' => 'Specific CESU adjustments',
|
||||||
|
'bud_dyn_cesu_add' => 'Add a month',
|
||||||
|
'bud_dyn_cesu_empty' => 'No adjustments recorded.',
|
||||||
|
'bud_settings_dyn_title' => 'Care & School',
|
||||||
|
'bud_settings_dyn_desc' => 'Rates for automated childcare fee calculations.',
|
||||||
|
'bud_dyn_sub_nanny' => 'Nanny',
|
||||||
|
'bud_dyn_sub_school' => 'Afterschool & Center',
|
||||||
|
|
||||||
// CSV Mapping (Budget)
|
// CSV Mapping (Budget)
|
||||||
'bs_csv_title' => 'CSV Import Format',
|
'bs_csv_title' => 'CSV Import Format',
|
||||||
|
|||||||
+10
-2
@@ -588,7 +588,6 @@ return [
|
|||||||
'bs_currency_label' => 'Devise du Foyer',
|
'bs_currency_label' => 'Devise du Foyer',
|
||||||
'bs_currency_desc' => 'Devise principale utilisée pour l\'affichage de vos comptes et budgets.',
|
'bs_currency_desc' => 'Devise principale utilisée pour l\'affichage de vos comptes et budgets.',
|
||||||
'bs_currency_updated' => 'Devise mise à jour avec succès !',
|
'bs_currency_updated' => 'Devise mise à jour avec succès !',
|
||||||
'bud_settings_dyn_title' => 'Estimations Dynamiques (Enfants)',
|
|
||||||
'bud_dyn_nanny_fixed' => 'Salaire fixe Assistante Maternelle (€)',
|
'bud_dyn_nanny_fixed' => 'Salaire fixe Assistante Maternelle (€)',
|
||||||
'bud_dyn_nanny_daily' => 'Frais d\'entretien journaliers (€)',
|
'bud_dyn_nanny_daily' => 'Frais d\'entretien journaliers (€)',
|
||||||
'bud_dyn_nanny_aid' => 'Aide de l\'État (CMG) (€)',
|
'bud_dyn_nanny_aid' => 'Aide de l\'État (CMG) (€)',
|
||||||
@@ -602,7 +601,16 @@ return [
|
|||||||
'bud_audit_child' => '👦 %s :',
|
'bud_audit_child' => '👦 %s :',
|
||||||
'bud_audit_school_fee' => 'Cantine (%dj) + Garderie (%dj) + Centre (%dj) = %.2f€',
|
'bud_audit_school_fee' => 'Cantine (%dj) + Garderie (%dj) + Centre (%dj) = %.2f€',
|
||||||
'bud_audit_sick' => 'Jours maladie déduits : %dj',
|
'bud_audit_sick' => 'Jours maladie déduits : %dj',
|
||||||
'bud_audit_nanny_full' => '💼 %s : Fixe (%.2f€) + Frais (%dj x %.2f€ = %.2f€) - Aide (%.2f€) - CESU (%.2f€) = %.2f€',
|
'bud_audit_nanny_full' => '💼 %s : Fixe (%.2f€) + Frais (%dj x %.2f€ = %.2f€) - CMG (%.2f€) - %s (%.2f€) = %.2f€',
|
||||||
|
'bud_audit_cesu_avg' => 'CESU (Moy.)',
|
||||||
|
'bud_audit_cesu_adj' => 'CESU (Ajusté)',
|
||||||
|
'bud_dyn_cesu_overrides' => 'Ajustements ponctuels du CESU',
|
||||||
|
'bud_dyn_cesu_add' => 'Ajouter un mois',
|
||||||
|
'bud_dyn_cesu_empty' => 'Aucun ajustement enregistré.',
|
||||||
|
'bud_settings_dyn_title' => 'Garde & École',
|
||||||
|
'bud_settings_dyn_desc' => 'Tarifs pour le calcul automatisé des frais de garde.',
|
||||||
|
'bud_dyn_sub_nanny' => 'Nounou',
|
||||||
|
'bud_dyn_sub_school' => 'Périscolaire',
|
||||||
|
|
||||||
// CSV Mapping (Budget)
|
// CSV Mapping (Budget)
|
||||||
'bs_csv_title' => 'Format de l\'import CSV',
|
'bs_csv_title' => 'Format de l\'import CSV',
|
||||||
|
|||||||
@@ -2701,3 +2701,36 @@ body.sum-mode-active .sum-target {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- 16. AJUSTEMENTS CESU (JSON OVERRIDES) --- */
|
||||||
|
.bs-cesu-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--bg-page);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
}
|
||||||
|
.bs-cesu-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.bs-cesu-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.bs-cesu-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.bs-cesu-row input[type="month"] {
|
||||||
|
width: 140px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.bs-cesu-row input[type="number"] {
|
||||||
|
width: 100px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
@@ -32,6 +32,10 @@ class DynamicBudgetCalculator {
|
|||||||
return isset($this->settings[$key]) ? (float)$this->settings[$key] : (float)$default;
|
return isset($this->settings[$key]) ? (float)$this->settings[$key] : (float)$default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getSettingString(string $key, string $default = ''): string {
|
||||||
|
return isset($this->settings[$key]) ? (string)$this->settings[$key] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
private function loadPeople(): void {
|
private function loadPeople(): void {
|
||||||
// 1. Chargement des enfants sans doublons
|
// 1. Chargement des enfants sans doublons
|
||||||
$stmt = $this->pdo->query("SELECT id, name, care_modes FROM pf_people WHERE role IN ('enfant', 'child') AND is_active = 1");
|
$stmt = $this->pdo->query("SELECT id, name, care_modes FROM pf_people WHERE role IN ('enfant', 'child') AND is_active = 1");
|
||||||
@@ -85,10 +89,11 @@ class DynamicBudgetCalculator {
|
|||||||
|
|
||||||
$zone = $this->foyerSettings['zone_scolaire'] ?? 'C';
|
$zone = $this->foyerSettings['zone_scolaire'] ?? 'C';
|
||||||
try {
|
try {
|
||||||
// 🔥 FIX : On récupère les DEUX années scolaires qui chevauchent l'année calendaire (ex: 2025-2026 ET 2026-2027)
|
// 🔥 FIX STATE OF THE ART : On cible uniquement la population "Élèves"
|
||||||
$prevYear = $this->year - 1;
|
$prevYear = $this->year - 1;
|
||||||
$nextYear = $this->year + 1;
|
$nextYear = $this->year + 1;
|
||||||
$where = "(annee_scolaire='{$prevYear}-{$this->year}' OR annee_scolaire='{$this->year}-{$nextYear}') AND zones LIKE '%Zone {$zone}%'";
|
$where = "(annee_scolaire='{$prevYear}-{$this->year}' OR annee_scolaire='{$this->year}-{$nextYear}') AND zones LIKE '%Zone {$zone}%' AND population = 'Élèves'";
|
||||||
|
|
||||||
$url = "https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=" . urlencode($where) . "&limit=100";
|
$url = "https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=" . urlencode($where) . "&limit=100";
|
||||||
|
|
||||||
$jsonVacances = @file_get_contents($url);
|
$jsonVacances = @file_get_contents($url);
|
||||||
@@ -127,7 +132,16 @@ class DynamicBudgetCalculator {
|
|||||||
$nannyFixed = $this->getSetting('budget_nanny_fixed', 700);
|
$nannyFixed = $this->getSetting('budget_nanny_fixed', 700);
|
||||||
$nannyDaily = $this->getSetting('budget_nanny_daily', 4);
|
$nannyDaily = $this->getSetting('budget_nanny_daily', 4);
|
||||||
$nannyAid = $this->getSetting('budget_nanny_aid', 120);
|
$nannyAid = $this->getSetting('budget_nanny_aid', 120);
|
||||||
|
|
||||||
$cesuAvg = $this->getSetting('budget_nanny_cesu_avg', 170);
|
$cesuAvg = $this->getSetting('budget_nanny_cesu_avg', 170);
|
||||||
|
$overridesJson = $this->getSettingString('budget_cesu_overrides', '{}');
|
||||||
|
$cesuOverrides = json_decode($overridesJson, true) ?: [];
|
||||||
|
|
||||||
|
$monthKey = sprintf("%04d-%02d", $this->year, $this->month);
|
||||||
|
$isCesuOverride = isset($cesuOverrides[$monthKey]);
|
||||||
|
$cesuApplied = $isCesuOverride ? (float)$cesuOverrides[$monthKey] : $cesuAvg;
|
||||||
|
$cesuLabelText = $isCesuOverride ? tr('bud_audit_cesu_adj') : tr('bud_audit_cesu_avg');
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
$schoolMeal = $this->getSetting('budget_school_meal', 5.62);
|
$schoolMeal = $this->getSetting('budget_school_meal', 5.62);
|
||||||
$schoolAftercare = $this->getSetting('budget_school_aftercare', 1.97);
|
$schoolAftercare = $this->getSetting('budget_school_aftercare', 1.97);
|
||||||
@@ -168,6 +182,8 @@ class DynamicBudgetCalculator {
|
|||||||
if (in_array($dateStr, $offDays['feries'])) continue;
|
if (in_array($dateStr, $offDays['feries'])) continue;
|
||||||
|
|
||||||
$isVacances = in_array($dateStr, $offDays['vacances']);
|
$isVacances = in_array($dateStr, $offDays['vacances']);
|
||||||
|
|
||||||
|
|
||||||
$events = $this->calendarEvents[$dateStr] ?? [];
|
$events = $this->calendarEvents[$dateStr] ?? [];
|
||||||
|
|
||||||
$nannyOffToday = false;
|
$nannyOffToday = false;
|
||||||
@@ -216,13 +232,15 @@ class DynamicBudgetCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$totalNannyFees = $totalNannyDays * $nannyDaily;
|
$totalNannyFees = $totalNannyDays * $nannyDaily;
|
||||||
$nannyGlobalCost = $nannyFixed + $totalNannyFees - $nannyAid - $cesuAvg;
|
// On soustrait $cesuApplied au lieu de $cesuAvg
|
||||||
|
$nannyGlobalCost = $nannyFixed + $totalNannyFees - $nannyAid - $cesuApplied;
|
||||||
|
|
||||||
// 3. Rédaction du Bilan
|
// 3. Rédaction du Bilan
|
||||||
if ($hasNanny) {
|
if ($hasNanny) {
|
||||||
$totalCost += max(0, $nannyGlobalCost);
|
$totalCost += max(0, $nannyGlobalCost);
|
||||||
|
// On passe $cesuLabelText au sprintf
|
||||||
$details .= sprintf(tr('bud_audit_nanny_full') . "\n",
|
$details .= sprintf(tr('bud_audit_nanny_full') . "\n",
|
||||||
$this->helperName, $nannyFixed, $totalNannyDays, $nannyDaily, $totalNannyFees, $nannyAid, $cesuAvg, max(0, $nannyGlobalCost));
|
$this->helperName, $nannyFixed, $totalNannyDays, $nannyDaily, $totalNannyFees, $nannyAid, $cesuLabelText, $cesuApplied, max(0, $nannyGlobalCost));
|
||||||
|
|
||||||
if ($nannyOffDays > 0) {
|
if ($nannyOffDays > 0) {
|
||||||
$details .= sprintf(tr('bud_audit_nanny_abs') . "\n", $this->helperName, $nannyOffDays);
|
$details .= sprintf(tr('bud_audit_nanny_abs') . "\n", $this->helperName, $nannyOffDays);
|
||||||
|
|||||||
@@ -205,7 +205,8 @@ try {
|
|||||||
if ($action === 'save_dynamic_estimates') {
|
if ($action === 'save_dynamic_estimates') {
|
||||||
$keys = [
|
$keys = [
|
||||||
'budget_nanny_fixed', 'budget_nanny_daily', 'budget_nanny_aid', 'budget_nanny_cesu_avg',
|
'budget_nanny_fixed', 'budget_nanny_daily', 'budget_nanny_aid', 'budget_nanny_cesu_avg',
|
||||||
'budget_school_meal', 'budget_school_aftercare', 'budget_school_fullday'
|
'budget_school_meal', 'budget_school_aftercare', 'budget_school_fullday',
|
||||||
|
'budget_cesu_overrides'
|
||||||
];
|
];
|
||||||
|
|
||||||
$pdo->beginTransaction();
|
$pdo->beginTransaction();
|
||||||
|
|||||||
@@ -224,10 +224,11 @@ if (!defined('CURRENCY')) {
|
|||||||
<!-- NOUVEAU PANE ENFANTS -->
|
<!-- NOUVEAU PANE ENFANTS -->
|
||||||
<div id="pane-children" class="bs-pane">
|
<div id="pane-children" class="bs-pane">
|
||||||
<h4 class="bs-section-title">👶 <?= tr('bud_settings_dyn_title') ?></h4>
|
<h4 class="bs-section-title">👶 <?= tr('bud_settings_dyn_title') ?></h4>
|
||||||
<p class="pf-muted-tiny bs-desc">Configurez ici les tarifs pour le calcul dynamique des frais de garde et de cantine.</p>
|
<p class="pf-muted-tiny bs-desc"><?= tr('bud_settings_dyn_desc') ?></p>
|
||||||
|
|
||||||
<form id="form-dynamic-estimates" onsubmit="saveDynamicEstimates(event)">
|
<form id="form-dynamic-estimates" onsubmit="saveDynamicEstimates(event)">
|
||||||
<h5 class="bs-section-subtitle bordered">Assistante Maternelle</h5>
|
|
||||||
|
<h5 class="bs-section-subtitle bordered"><?= tr('bud_dyn_sub_nanny') ?></h5>
|
||||||
<div class="bs-grid-2">
|
<div class="bs-grid-2">
|
||||||
<div>
|
<div>
|
||||||
<label class="pf-label"><?= tr('bud_dyn_nanny_fixed') ?></label>
|
<label class="pf-label"><?= tr('bud_dyn_nanny_fixed') ?></label>
|
||||||
@@ -247,7 +248,17 @@ if (!defined('CURRENCY')) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="bs-section-subtitle bordered" style="margin-top: 20px;">École & Centre de loisirs</h5>
|
<!-- Ajustements ponctuels du CESU -->
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
<div class="bs-cesu-header" style="display:flex; justify-content:space-between; margin-bottom:12px;">
|
||||||
|
<label class="pf-label pf-m-0"><?= tr('bud_dyn_cesu_overrides') ?></label>
|
||||||
|
<button type="button" class="pf-btn btn-secondary pf-btn-sm" onclick="addCesuOverride()">+ <?= tr('bud_dyn_cesu_add') ?></button>
|
||||||
|
</div>
|
||||||
|
<div id="cesu-overrides-list" class="bs-cesu-list"></div>
|
||||||
|
<input type="hidden" name="budget_cesu_overrides" id="budget_cesu_overrides_input" value="{}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="bs-section-subtitle bordered pf-mt-md"><?= tr('bud_dyn_sub_school') ?></h5>
|
||||||
<div class="bs-grid-2">
|
<div class="bs-grid-2">
|
||||||
<div>
|
<div>
|
||||||
<label class="pf-label"><?= tr('bud_dyn_school_meal') ?></label>
|
<label class="pf-label"><?= tr('bud_dyn_school_meal') ?></label>
|
||||||
@@ -263,8 +274,8 @@ if (!defined('CURRENCY')) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bs-text-right" style="margin-top: 20px;">
|
<div class="bs-text-right pf-mt-md">
|
||||||
<button type="submit" class="btn btn-secondary">💾 <?= tr('btn_save') ?></button>
|
<button type="submit" class="pf-btn btn-secondary">💾 <?= tr('btn_save') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,6 +352,10 @@ async function loadBudgetSettingsData() {
|
|||||||
if (document.getElementById('budget_school_aftercare')) document.getElementById('budget_school_aftercare').value = bs.budget_school_aftercare || 1.97;
|
if (document.getElementById('budget_school_aftercare')) document.getElementById('budget_school_aftercare').value = bs.budget_school_aftercare || 1.97;
|
||||||
if (document.getElementById('budget_school_fullday')) document.getElementById('budget_school_fullday').value = bs.budget_school_fullday || 21.89;
|
if (document.getElementById('budget_school_fullday')) document.getElementById('budget_school_fullday').value = bs.budget_school_fullday || 21.89;
|
||||||
|
|
||||||
|
try { cesuOverrides = JSON.parse(bs.budget_cesu_overrides || '{}'); }
|
||||||
|
catch (e) { cesuOverrides = {}; }
|
||||||
|
renderCesuOverrides();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Erreur chargement paramètres :", err);
|
console.error("Erreur chargement paramètres :", err);
|
||||||
const errorMsg = `<div class="pf-alert pf-alert--error bs-input-mb">❌ ${tr('error_loading_settings')} : ${err.message}</div>`;
|
const errorMsg = `<div class="pf-alert pf-alert--error bs-input-mb">❌ ${tr('error_loading_settings')} : ${err.message}</div>`;
|
||||||
@@ -657,4 +672,79 @@ async function saveDynamicEstimates(e) {
|
|||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- LOGIQUE DES AJUSTEMENTS CESU ---
|
||||||
|
let cesuOverrides = {};
|
||||||
|
|
||||||
|
function renderCesuOverrides() {
|
||||||
|
const list = document.getElementById('cesu-overrides-list');
|
||||||
|
const input = document.getElementById('budget_cesu_overrides_input');
|
||||||
|
if (!list || !input) return;
|
||||||
|
|
||||||
|
list.innerHTML = '';
|
||||||
|
// Tri décroissant pour afficher le mois le plus lointain en haut
|
||||||
|
const keys = Object.keys(cesuOverrides).sort().reverse();
|
||||||
|
|
||||||
|
if (keys.length === 0) {
|
||||||
|
list.innerHTML = `<div class="pf-empty-dashed" style="margin:0; padding:12px;"><em class="pf-muted-tiny">${window.I18N['bud_dyn_cesu_empty'] || 'Aucun ajustement enregistré.'}</em></div>`;
|
||||||
|
} else {
|
||||||
|
keys.forEach(k => {
|
||||||
|
const btnDelText = window.I18N['btn_delete'] || 'Supprimer';
|
||||||
|
// UI : Design aéré type "Carte HouseHub" avec des inputs sans bordures agressives
|
||||||
|
list.innerHTML += `
|
||||||
|
<div style="display: flex; gap: 10px; align-items: center; background: var(--bg-panel); padding: 8px 12px; border-radius: 8px; border: 1px solid var(--border-light); box-shadow: var(--shadow-sm);">
|
||||||
|
<input type="month" class="pf-input" style="width: 150px; margin: 0; padding: 6px 10px;" value="${k}" onchange="updateCesuOverrideKey('${k}', this.value)">
|
||||||
|
<input type="number" step="0.01" class="pf-input" style="flex: 1; margin: 0; padding: 6px 10px; text-align: right;" value="${cesuOverrides[k]}" onchange="updateCesuOverrideValue('${k}', this.value)">
|
||||||
|
<span style="color: var(--text-muted); font-weight: 600; margin-right: 5px;">€</span>
|
||||||
|
<button type="button" class="btn-icon-action delete" style="margin: 0; background: var(--bg-danger-soft); width: 32px; height: 32px;" onclick="removeCesuOverride('${k}')" title="${btnDelText}">🗑️</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
input.value = JSON.stringify(cesuOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCesuOverride() {
|
||||||
|
const keys = Object.keys(cesuOverrides).sort();
|
||||||
|
let nextDate = new Date();
|
||||||
|
nextDate.setMonth(nextDate.getMonth() + 1); // Base : le mois prochain
|
||||||
|
|
||||||
|
// S'il y a déjà des mois dans la liste, on prend le plus lointain et on ajoute 1 mois
|
||||||
|
if (keys.length > 0) {
|
||||||
|
const lastKey = keys[keys.length - 1]; // ex: "2026-10"
|
||||||
|
const parts = lastKey.split('-');
|
||||||
|
// L'astuce : Le mois en JS commence à 0. Donc en lui passant "10", on pointe déjà sur le mois d'après (Novembre) !
|
||||||
|
nextDate = new Date(parts[0], parseInt(parts[1], 10), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sécurité anti-boucle infinie
|
||||||
|
let m = nextDate.getFullYear() + '-' + String(nextDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
let safety = 0;
|
||||||
|
while (cesuOverrides[m] !== undefined && safety < 24) {
|
||||||
|
nextDate.setMonth(nextDate.getMonth() + 1);
|
||||||
|
m = nextDate.getFullYear() + '-' + String(nextDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
safety++;
|
||||||
|
}
|
||||||
|
|
||||||
|
cesuOverrides[m] = 0;
|
||||||
|
renderCesuOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCesuOverrideKey(oldK, newK) {
|
||||||
|
if (oldK !== newK && newK) {
|
||||||
|
cesuOverrides[newK] = cesuOverrides[oldK];
|
||||||
|
delete cesuOverrides[oldK];
|
||||||
|
renderCesuOverrides();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCesuOverrideValue(k, val) {
|
||||||
|
cesuOverrides[k] = parseFloat(val) || 0;
|
||||||
|
renderCesuOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCesuOverride(k) {
|
||||||
|
delete cesuOverrides[k];
|
||||||
|
renderCesuOverrides();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1078,7 +1078,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
),
|
),
|
||||||
this.fetchApi("/modules/family-calendar/includes/api/get-events.php"),
|
this.fetchApi("/modules/family-calendar/includes/api/get-events.php"),
|
||||||
this.fetchPublicHolidays(),
|
this.fetchPublicHolidays(),
|
||||||
this.fetchSchoolHolidays(zone), // 🏖️ NOUVEAU : On appelle l'API
|
this.fetchSchoolHolidays(zone),
|
||||||
this.fetchApi("/modules/family-calendar/includes/api/get-leaves.php"),
|
this.fetchApi("/modules/family-calendar/includes/api/get-leaves.php"),
|
||||||
this.fetchApi(
|
this.fetchApi(
|
||||||
"/modules/family-calendar/includes/api/get-leave-snapshots.php",
|
"/modules/family-calendar/includes/api/get-leave-snapshots.php",
|
||||||
@@ -1132,8 +1132,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
if (!zone || zone === "Autre") return [];
|
if (!zone || zone === "Autre") return [];
|
||||||
try {
|
try {
|
||||||
const yearStr = `${this.currentSchoolYearStart}-${this.currentSchoolYearStart + 1}`;
|
const yearStr = `${this.currentSchoolYearStart}-${this.currentSchoolYearStart + 1}`;
|
||||||
// Ton URL qui utilise le LIKE, beaucoup plus robuste !
|
|
||||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%'&limit=100`;
|
const whereClause = `annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%' AND population='Élèves'`;
|
||||||
|
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=${encodeURIComponent(whereClause)}&limit=100`;
|
||||||
|
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@@ -1196,7 +1197,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
try {
|
try {
|
||||||
const year = this.modalSelectedYear || this.currentSchoolYearStart;
|
const year = this.modalSelectedYear || this.currentSchoolYearStart;
|
||||||
const yearStr = `${year}-${year + 1}`;
|
const yearStr = `${year}-${year + 1}`;
|
||||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%'&limit=100&order_by=start_date`;
|
|
||||||
|
const whereClause = `annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%' AND population='Élèves'`;
|
||||||
|
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=${encodeURIComponent(whereClause)}&limit=100&order_by=start_date`;
|
||||||
|
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|||||||
Reference in new issue
Block a user