virement todo
This commit is contained in:
+239
-5
@@ -575,11 +575,10 @@ input[type="number"] {
|
|||||||
}
|
}
|
||||||
.prev-section-header h2 {
|
.prev-section-header h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.2rem;
|
font-size: 1.5rem;
|
||||||
color: var(--text-main);
|
font-weight: 800;
|
||||||
font-weight: 700;
|
color: #0f172a;
|
||||||
text-transform: uppercase;
|
letter-spacing: -0.025em;
|
||||||
letter-spacing: 0.05em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inputs "Invisible" */
|
/* Inputs "Invisible" */
|
||||||
@@ -766,3 +765,238 @@ input[type="number"] {
|
|||||||
color: var(--text-main);
|
color: var(--text-main);
|
||||||
border-color: #94a3b8;
|
border-color: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- ACTIONS SUR LIGNES (Edit / Delete) --- */
|
||||||
|
.row-actions {
|
||||||
|
display: none; /* Masqué par défaut */
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
gap: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.9); /* Petit fond pour lisibilité */
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On affiche au survol de la cellule */
|
||||||
|
td:hover .row-actions {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-action {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-action.edit {
|
||||||
|
color: #3b82f6;
|
||||||
|
} /* Bleu */
|
||||||
|
.btn-icon-action.edit:hover {
|
||||||
|
background: #eff6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-action.delete {
|
||||||
|
color: #ef4444;
|
||||||
|
} /* Rouge */
|
||||||
|
.btn-icon-action.delete:hover {
|
||||||
|
background: #fef2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 11. RÉCAPITULATIF VIREMENTS (Tableau Unique) --- */
|
||||||
|
|
||||||
|
.recap-wrapper {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Centré */
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-card {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px; /* Pas trop large pour rester lisible */
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-header {
|
||||||
|
background: #1e293b;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-table th,
|
||||||
|
.recap-table td {
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-table th {
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #64748b;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-bottom: 2px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colonne Labels (Gauche) */
|
||||||
|
.recap-table td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colonne Alex (Cyan) */
|
||||||
|
.col-alex {
|
||||||
|
background-color: #ecfeff;
|
||||||
|
color: #0891b2;
|
||||||
|
border-left: 1px solid #cffafe;
|
||||||
|
}
|
||||||
|
th.col-alex {
|
||||||
|
background-color: #cffafe;
|
||||||
|
border-bottom-color: #a5f3fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colonne Laia (Ambre) */
|
||||||
|
.col-laia {
|
||||||
|
background-color: #fffbeb;
|
||||||
|
color: #d97706;
|
||||||
|
border-left: 1px solid #fef3c7;
|
||||||
|
}
|
||||||
|
th.col-laia {
|
||||||
|
background-color: #fef3c7;
|
||||||
|
border-bottom-color: #fde68a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colonne Global */
|
||||||
|
.col-global {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
color: #475569;
|
||||||
|
font-weight: 700;
|
||||||
|
border-left: 2px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ligne Grand Total */
|
||||||
|
.row-grand-total td {
|
||||||
|
background-color: #1e293b;
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 15px;
|
||||||
|
}
|
||||||
|
/* On garde les couleurs de texte même sur fond sombre pour la lisibilité */
|
||||||
|
.row-grand-total .col-alex {
|
||||||
|
background: #164e63;
|
||||||
|
color: #67e8f9;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.row-grand-total .col-laia {
|
||||||
|
background: #78350f;
|
||||||
|
color: #fcd34d;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.row-grand-total .col-global {
|
||||||
|
background: #334155;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 12. OPTIMISATIONS MOBILES (Responsive) --- */
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* 1. Header du Budget Prévisionnel : On empile verticalement */
|
||||||
|
.prev-section-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On s'assure que les sous-groupes (Titre + Nav) prennent toute la largeur */
|
||||||
|
.prev-section-header > div {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Colonne Fixe (Sticky) : On réduit la largeur pour laisser voir les données */
|
||||||
|
.col-sticky {
|
||||||
|
min-width: 140px !important; /* Réduit de 220px à 140px */
|
||||||
|
max-width: 140px;
|
||||||
|
font-size: 0.85rem; /* Texte un peu plus petit */
|
||||||
|
white-space: normal; /* Autorise le retour à la ligne du nom */
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ajustement du petit texte (cible) */
|
||||||
|
.col-sticky small {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Actions (Edit/Delete) : Toujours visibles sur mobile */
|
||||||
|
/* Car pas de "hover" avec le doigt */
|
||||||
|
.row-actions {
|
||||||
|
display: flex !important; /* Force l'affichage */
|
||||||
|
background: rgba(255, 255, 255, 0.8); /* Fond semi-transparent */
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
right: 2px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-action {
|
||||||
|
width: 28px; /* Zone de touche un peu plus grande */
|
||||||
|
height: 28px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. Tableau Récapitulatif */
|
||||||
|
.recap-wrapper {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-card {
|
||||||
|
border-radius: 8px; /* Coins moins arrondis pour gagner de la place */
|
||||||
|
border-left: none;
|
||||||
|
border-right: none; /* Enlever les bordures latérales sur tout petits écrans */
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-table th,
|
||||||
|
.recap-table td {
|
||||||
|
padding: 8px 6px; /* Padding réduit */
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 5. Inputs du tableau : Plus facile à toucher */
|
||||||
|
.prev-input {
|
||||||
|
font-size: 16px; /* Évite que l'iPhone zoom quand on clique dessus */
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// modules/budget/includes/api/save-budget.php
|
||||||
|
|
||||||
require __DIR__ . '/../../../../includes/auth.php';
|
require __DIR__ . '/../../../../includes/auth.php';
|
||||||
require __DIR__ . '/../../../../includes/db.php';
|
require __DIR__ . '/../../../../includes/db.php';
|
||||||
require_login();
|
require_login();
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
// Note : On ne force pas le header JSON partout car add/delete/update utilisent des redirections
|
||||||
|
// header('Content-Type: application/json');
|
||||||
|
|
||||||
$action = $_POST['action'] ?? '';
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
// 1. MISE A JOUR TABLEAU SALAIRES
|
// 1. MISE A JOUR TABLEAU SALAIRES (AJAX)
|
||||||
if ($action === 'update_salary_config') {
|
if ($action === 'update_salary_config') {
|
||||||
|
header('Content-Type: application/json'); // On précise JSON ici
|
||||||
$year = $_POST['year'];
|
$year = $_POST['year'];
|
||||||
$person = $_POST['person'];
|
$person = $_POST['person'];
|
||||||
$field = $_POST['field']; // salary, mensualite, etc.
|
$field = $_POST['field']; // salary, mensualite, etc.
|
||||||
@@ -24,8 +28,9 @@ if ($action === 'update_salary_config') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. MISE A JOUR TABLEAU REPARTITION
|
// 2. MISE A JOUR TABLEAU REPARTITION (AJAX)
|
||||||
if ($action === 'update_allocation') {
|
if ($action === 'update_allocation') {
|
||||||
|
header('Content-Type: application/json'); // On précise JSON ici
|
||||||
$date = $_POST['month_date'];
|
$date = $_POST['month_date'];
|
||||||
$catId = $_POST['cat_id'];
|
$catId = $_POST['cat_id'];
|
||||||
$person = $_POST['person']; // 'amount_alex' ou 'amount_laia'
|
$person = $_POST['person']; // 'amount_alex' ou 'amount_laia'
|
||||||
@@ -37,18 +42,43 @@ if ($action === 'update_allocation') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. GESTION DES CATEGORIES (Ajout / Suppression)
|
// 3. GESTION DES CATEGORIES (Ajout)
|
||||||
if ($action === 'add_category') {
|
if ($action === 'add_category') {
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name']);
|
||||||
$target = trim($_POST['target']);
|
$target = trim($_POST['target']);
|
||||||
$stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target) VALUES (?, ?)");
|
|
||||||
$stmt->execute([$name, $target]);
|
if (!empty($name)) {
|
||||||
header("Location: /budget.php?tab=budget_prev"); exit;
|
$stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$name, $target]);
|
||||||
|
}
|
||||||
|
// Redirection vers la page précédente
|
||||||
|
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. MODIFICATION D'UNE CATEGORIE (Nom / Cible) - NOUVEAU BLOC
|
||||||
|
if ($action === 'update_category') {
|
||||||
|
$id = (int)$_POST['cat_id'];
|
||||||
|
$name = trim($_POST['name']);
|
||||||
|
$target = trim($_POST['target']);
|
||||||
|
|
||||||
|
if ($id > 0 && !empty($name)) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE pf_alloc_categories SET name = ?, target = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$name, $target, $id]);
|
||||||
|
}
|
||||||
|
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. SUPPRESSION CATEGORIE
|
||||||
if ($action === 'delete_category') {
|
if ($action === 'delete_category') {
|
||||||
$id = (int)$_POST['id'];
|
$id = (int)$_GET['id'] ?? (int)$_POST['id']; // Peut venir du GET (lien) ou POST
|
||||||
$pdo->prepare("DELETE FROM pf_alloc_categories WHERE id = ?")->execute([$id]);
|
|
||||||
$pdo->prepare("DELETE FROM pf_alloc_values WHERE cat_id = ?")->execute([$id]); // Nettoyage valeurs
|
if ($id > 0) {
|
||||||
header("Location: /budget.php?tab=budget_prev"); exit;
|
$pdo->prepare("DELETE FROM pf_alloc_categories WHERE id = ?")->execute([$id]);
|
||||||
|
$pdo->prepare("DELETE FROM pf_alloc_values WHERE cat_id = ?")->execute([$id]); // Nettoyage valeurs
|
||||||
|
}
|
||||||
|
// Redirection vers la page précédente
|
||||||
|
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
@@ -156,44 +156,124 @@ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<?php foreach ($cats as $cat): ?>
|
<?php foreach ($cats as $cat):
|
||||||
<tr>
|
// Détection des lignes indicatives
|
||||||
<td class="col-sticky" style="position:relative;">
|
$isIndicative = ($cat['name'] === 'Eco Alex' || $cat['name'] === 'Eco Laia');
|
||||||
<div style="font-weight:600; color:var(--text-main); "><?= htmlspecialchars($cat['name']) ?></div>
|
$rowClass = $isIndicative ? 'row-indicative' : '';
|
||||||
<div style="font-size:0.75rem; color:var(--text-muted); font-style:italic; text-align:right;"><?= htmlspecialchars($cat['target']) ?></div>
|
$inputClass = $isIndicative ? 'ignore-calc' : ''; // Classe pour le JS
|
||||||
|
$rowStyle = $isIndicative ? 'background:#f8fafc; color:#94a3b8;' : '';
|
||||||
|
?>
|
||||||
|
<tr class="<?= $rowClass ?>" style="<?= $rowStyle ?>">
|
||||||
|
<td class="col-sticky" style="position:relative; <?= $isIndicative ? 'opacity:0.8;' : '' ?>">
|
||||||
|
<div style="font-weight:600; color:<?= $isIndicative ? '#64748b' : 'var(--text-main)' ?>;">
|
||||||
|
<?= htmlspecialchars($cat['name']) ?>
|
||||||
|
<?php if($isIndicative): ?><span style="font-size:0.7rem; border:1px solid #cbd5e1; border-radius:4px; padding:0 4px; margin-left:5px;">Info</span><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:0.75rem; color:var(--text-muted); font-style:italic; text-align:right;">
|
||||||
|
<?= htmlspecialchars($cat['target']) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-actions">
|
||||||
|
<button type="button"
|
||||||
|
class="btn-icon-action edit"
|
||||||
|
title="Modifier"
|
||||||
|
data-id="<?= $cat['id'] ?>"
|
||||||
|
data-name="<?= htmlspecialchars($cat['name']) ?>"
|
||||||
|
data-target="<?= htmlspecialchars($cat['target']) ?>"
|
||||||
|
onclick="openEditModal(this)">
|
||||||
|
✎
|
||||||
|
</button>
|
||||||
|
|
||||||
<a href="?tab=budget_prev&id=<?= $cat['id'] ?>&action=delete_category"
|
<a href="?tab=budget_prev&id=<?= $cat['id'] ?>&action=delete_category"
|
||||||
onclick="return confirm('Supprimer cette ligne et tout son historique ?')"
|
onclick="return confirm('Supprimer cette ligne et tout son historique ?')"
|
||||||
class="btn-cell-delete"
|
class="btn-icon-action delete" title="Supprimer">
|
||||||
title="Supprimer la ligne">
|
|
||||||
×
|
×
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<?php foreach ($months as $m):
|
||||||
|
$val = $allocs[$m][$cat['id']] ?? ['amount_alex'=>0, 'amount_laia'=>0];
|
||||||
|
?>
|
||||||
|
<td class="txt-global" style="border-left:2px solid #e2e8f0; <?= $isIndicative?'color:#94a3b8; font-weight:normal;':'' ?>" id="g_<?= $m ?>_<?= $cat['id'] ?>">0</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<input type="number" step="1" class="prev-input txt-alex inp-alex-<?= $m ?> <?= $inputClass ?>"
|
||||||
|
style="<?= $isIndicative ? 'color:#64748b;' : '' ?>"
|
||||||
|
value="<?= $val['amount_alex'] == 0 ? '' : round($val['amount_alex']) ?>"
|
||||||
|
placeholder="-"
|
||||||
|
data-target="<?= htmlspecialchars($cat['target']) ?>"
|
||||||
|
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, 'amount_alex', this)">
|
||||||
</td>
|
</td>
|
||||||
<?php foreach ($months as $m):
|
|
||||||
$val = $allocs[$m][$cat['id']] ?? ['amount_alex'=>0, 'amount_laia'=>0];
|
|
||||||
?>
|
|
||||||
<td class="txt-global" style="border-left:2px solid #e2e8f0;" id="g_<?= $m ?>_<?= $cat['id'] ?>">0</td>
|
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<input type="number" step="1" class="prev-input txt-alex inp-alex-<?= $m ?>"
|
<input type="number" step="1" class="prev-input txt-laia inp-laia-<?= $m ?> <?= $inputClass ?>"
|
||||||
value="<?= $val['amount_alex'] == 0 ? '' : round($val['amount_alex']) ?>"
|
style="<?= $isIndicative ? 'color:#64748b;' : '' ?>"
|
||||||
placeholder="-"
|
value="<?= $val['amount_laia'] == 0 ? '' : round($val['amount_laia']) ?>"
|
||||||
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, 'amount_alex', this)">
|
placeholder="-"
|
||||||
</td>
|
data-target="<?= htmlspecialchars($cat['target']) ?>"
|
||||||
|
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, 'amount_laia', this)">
|
||||||
<td>
|
</td>
|
||||||
<input type="number" step="1" class="prev-input txt-laia inp-laia-<?= $m ?>"
|
|
||||||
value="<?= $val['amount_laia'] == 0 ? '' : round($val['amount_laia']) ?>"
|
|
||||||
placeholder="-"
|
|
||||||
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, 'amount_laia', this)">
|
|
||||||
</td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
$focusMonth = $months[0];
|
||||||
|
|
||||||
|
// On initialise toutes les cibles possibles pour avoir les lignes prêtes
|
||||||
|
// Note : On utilise md5() sur la cible pour créer des ID compatibles HTML (sans espaces/points)
|
||||||
|
$targetsOrder = ['vers commune', 'vers L.Pol', 'vers L.Pep', 'vers L.Perso'];
|
||||||
|
|
||||||
|
// On récupère toutes les cibles uniques utilisées dans les catégories + celles par défaut
|
||||||
|
$allTargets = $targetsOrder;
|
||||||
|
foreach($cats as $c) {
|
||||||
|
if(!empty($c['target']) && !in_array($c['target'], $allTargets)) $allTargets[] = $c['target'];
|
||||||
|
}
|
||||||
|
$allTargets = array_unique($allTargets);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="recap-wrapper">
|
||||||
|
<div class="recap-card">
|
||||||
|
<div class="recap-header">
|
||||||
|
Virements à effectuer - <?= date('F Y', strtotime($focusMonth)) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="recap-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align:left;">Destination</th>
|
||||||
|
<th class="col-alex">Alex</th>
|
||||||
|
<th class="col-laia">Laia</th>
|
||||||
|
<th class="col-global">Global</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach($allTargets as $target):
|
||||||
|
$tId = md5($target); // ID unique pour le JS
|
||||||
|
?>
|
||||||
|
<tr id="row_summary_<?= $tId ?>">
|
||||||
|
<td><?= htmlspecialchars($target) ?></td>
|
||||||
|
<td class="col-alex" id="sum_alex_<?= $tId ?>">0 €</td>
|
||||||
|
<td class="col-laia" id="sum_laia_<?= $tId ?>">0 €</td>
|
||||||
|
<td class="col-global" id="sum_global_<?= $tId ?>">0 €</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="row-grand-total">
|
||||||
|
<td>GRAND TOTAL</td>
|
||||||
|
<td class="col-alex" id="grand_total_alex">0 €</td>
|
||||||
|
<td class="col-laia" id="grand_total_laia">0 €</td>
|
||||||
|
<td class="col-global" id="grand_total_global">0 €</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addCatModal" class="pf-modal" style="display:none; position:fixed; inset:0; z-index:9999; background:rgba(15, 23, 42, 0.6); backdrop-filter:blur(4px); align-items:center; justify-content:center;">
|
<div id="addCatModal" class="pf-modal" style="display:none; position:fixed; inset:0; z-index:9999; background:rgba(15, 23, 42, 0.6); backdrop-filter:blur(4px); align-items:center; justify-content:center;">
|
||||||
@@ -209,13 +289,14 @@ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|||||||
<input type="text" name="name" class="pf-input" required>
|
<input type="text" name="name" class="pf-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" style="margin-bottom:20px;">
|
<div class="form-group" style="margin-bottom:20px;">
|
||||||
<label class="pf-label">Cible (ex: vers L.Pol)</label>
|
<label class="pf-label">Cible (Destination)</label>
|
||||||
<input type="text" name="target" class="pf-input" list="targets">
|
<select name="target" class="pf-input" required>
|
||||||
<datalist id="targets">
|
<option value="" disabled selected>-- Choisir --</option>
|
||||||
<option value="vers L.Pol">
|
<option value="vers L.Pol">vers L.Pol</option>
|
||||||
<option value="vers L.Perso">
|
<option value="vers L.Pep">vers L.Pep</option>
|
||||||
<option value="vers commune">
|
<option value="vers L.Perso">vers L.Perso</option>
|
||||||
</datalist>
|
<option value="vers commune">vers commune</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" onclick="document.getElementById('addCatModal').style.display='none'" class="pf-btn btn-secondary">Annuler</button>
|
<button type="button" onclick="document.getElementById('addCatModal').style.display='none'" class="pf-btn btn-secondary">Annuler</button>
|
||||||
@@ -224,6 +305,59 @@ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="editCatModal" class="pf-modal">
|
||||||
|
<div class="pf-modal-content" style="max-width:400px;">
|
||||||
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
||||||
|
<h3 class="pf-modal-title" style="margin:0; border:none; font-size:1.2rem;">Modifier la ligne</h3>
|
||||||
|
<button onclick="document.getElementById('editCatModal').style.display='none'" style="border:none; background:none; font-size:1.5rem; cursor:pointer; color:var(--text-muted);">×</button>
|
||||||
|
</div>
|
||||||
|
<form action="/modules/budget/includes/api/save-budget.php" method="POST">
|
||||||
|
<input type="hidden" name="action" value="update_category">
|
||||||
|
<input type="hidden" name="cat_id" id="edit_cat_id">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Nom</label>
|
||||||
|
<input type="text" name="name" id="edit_cat_name" class="pf-input" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Cible (Destination)</label>
|
||||||
|
<select name="target" id="edit_cat_target" class="pf-input" required>
|
||||||
|
<option value="vers L.Pol">vers L.Pol</option>
|
||||||
|
<option value="vers L.Pep">vers L.Pep</option>
|
||||||
|
<option value="vers L.Perso">vers L.Perso</option>
|
||||||
|
<option value="vers commune">vers commune</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" onclick="document.getElementById('editCatModal').style.display='none'" class="pf-btn btn-secondary">Annuler</button>
|
||||||
|
<button type="submit" class="pf-btn">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Fonction pour ouvrir la modale et pré-remplir les valeurs
|
||||||
|
function openEditModal(btn) {
|
||||||
|
// 1. Récupération des données depuis le bouton
|
||||||
|
// getAttribute est plus sûr pour éviter les bugs si des données sont manquantes
|
||||||
|
const id = btn.getAttribute('data-id');
|
||||||
|
const name = btn.getAttribute('data-name');
|
||||||
|
const target = btn.getAttribute('data-target');
|
||||||
|
|
||||||
|
// 2. Remplissage du formulaire
|
||||||
|
document.getElementById('edit_cat_id').value = id;
|
||||||
|
document.getElementById('edit_cat_name').value = name;
|
||||||
|
|
||||||
|
// Pour le SELECT, définir la .value sélectionne automatiquement la bonne option
|
||||||
|
// Si la valeur actuelle en base n'existe pas dans la liste, ça sélectionnera la 1ère par défaut
|
||||||
|
document.getElementById('edit_cat_target').value = target;
|
||||||
|
|
||||||
|
// 3. Affichage
|
||||||
|
document.getElementById('editCatModal').style.display = 'flex';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const currentYear = <?= $currentYear ?>;
|
const currentYear = <?= $currentYear ?>;
|
||||||
@@ -311,10 +445,16 @@ function recalcAllAllocations() {
|
|||||||
let sumAlex = 0;
|
let sumAlex = 0;
|
||||||
let sumLaia = 0;
|
let sumLaia = 0;
|
||||||
|
|
||||||
|
// Boucle sur colonne Alex
|
||||||
document.querySelectorAll('.inp-alex-' + m).forEach(inp => {
|
document.querySelectorAll('.inp-alex-' + m).forEach(inp => {
|
||||||
const val = parseFloat(inp.value) || 0;
|
const val = parseFloat(inp.value) || 0;
|
||||||
sumAlex += val;
|
|
||||||
|
|
||||||
|
// 1. On ajoute au TOTAL COLONNE seulement si ce n'est PAS une ligne indicative
|
||||||
|
if (!inp.classList.contains('ignore-calc')) {
|
||||||
|
sumAlex += val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Calcul du GLOBAL LIGNE (On le fait toujours, même pour les indicatifs)
|
||||||
const row = inp.closest('tr');
|
const row = inp.closest('tr');
|
||||||
const laiaVal = parseFloat(row.querySelector('.inp-laia-' + m).value) || 0;
|
const laiaVal = parseFloat(row.querySelector('.inp-laia-' + m).value) || 0;
|
||||||
const globalSum = val + laiaVal;
|
const globalSum = val + laiaVal;
|
||||||
@@ -327,13 +467,20 @@ function recalcAllAllocations() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelectorAll('.inp-laia-' + m).forEach(inp => sumLaia += (parseFloat(inp.value) || 0));
|
// Boucle sur colonne Laia (Juste pour le total colonne)
|
||||||
|
document.querySelectorAll('.inp-laia-' + m).forEach(inp => {
|
||||||
|
const val = parseFloat(inp.value) || 0;
|
||||||
|
if (!inp.classList.contains('ignore-calc')) {
|
||||||
|
sumLaia += val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Totaux arrondis
|
// Totaux
|
||||||
document.getElementById('total_alex_' + m).innerText = Math.round(sumAlex) + ' €';
|
document.getElementById('total_alex_' + m).innerText = Math.round(sumAlex) + ' €';
|
||||||
document.getElementById('total_laia_' + m).innerText = Math.round(sumLaia) + ' €';
|
document.getElementById('total_laia_' + m).innerText = Math.round(sumLaia) + ' €';
|
||||||
document.getElementById('total_global_' + m).innerText = Math.round(sumAlex + sumLaia) + ' €';
|
document.getElementById('total_global_' + m).innerText = Math.round(sumAlex + sumLaia) + ' €';
|
||||||
|
|
||||||
|
// Restants
|
||||||
const restAlex = budgetAlex - sumAlex;
|
const restAlex = budgetAlex - sumAlex;
|
||||||
const restLaia = budgetLaia - sumLaia;
|
const restLaia = budgetLaia - sumLaia;
|
||||||
|
|
||||||
@@ -346,6 +493,76 @@ function recalcAllAllocations() {
|
|||||||
elRestLaia.innerText = Math.round(restLaia) + ' €';
|
elRestLaia.innerText = Math.round(restLaia) + ' €';
|
||||||
elRestLaia.className = 'val-' + (restLaia >= 0 ? 'ok' : 'ko');
|
elRestLaia.className = 'val-' + (restLaia >= 0 ? 'ok' : 'ko');
|
||||||
});
|
});
|
||||||
|
updateSummaryTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSummaryTable() {
|
||||||
|
// 1. On identifie le mois focus (colonne de gauche)
|
||||||
|
const focusMonth = months[0];
|
||||||
|
|
||||||
|
// 2. Objet pour stocker les sommes par cible
|
||||||
|
// Structure : { 'md5_target': { alex: 0, laia: 0 } }
|
||||||
|
const sums = {};
|
||||||
|
|
||||||
|
// 3. On parcourt les inputs ALEX du mois focus
|
||||||
|
document.querySelectorAll('.inp-alex-' + focusMonth).forEach(inp => {
|
||||||
|
const val = parseFloat(inp.value) || 0;
|
||||||
|
const target = inp.getAttribute('data-target'); // Récupéré de l'étape 1
|
||||||
|
|
||||||
|
if (target) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
// On reset les totaux
|
||||||
|
let grandTotalAlex = 0;
|
||||||
|
let grandTotalLaia = 0;
|
||||||
|
const dataByTarget = {};
|
||||||
|
|
||||||
|
// Calcul Alex
|
||||||
|
document.querySelectorAll('.inp-alex-' + focusMonth).forEach(inp => {
|
||||||
|
const target = inp.getAttribute('data-target');
|
||||||
|
if(target) {
|
||||||
|
if(!dataByTarget[target]) dataByTarget[target] = { alex: 0, laia: 0 };
|
||||||
|
dataByTarget[target].alex += (parseFloat(inp.value) || 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calcul Laia
|
||||||
|
document.querySelectorAll('.inp-laia-' + focusMonth).forEach(inp => {
|
||||||
|
const target = inp.getAttribute('data-target');
|
||||||
|
if(target) {
|
||||||
|
if(!dataByTarget[target]) dataByTarget[target] = { alex: 0, laia: 0 };
|
||||||
|
dataByTarget[target].laia += (parseFloat(inp.value) || 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mise à jour du tableau
|
||||||
|
// On parcourt toutes les lignes du tbody du récap
|
||||||
|
const tbody = document.querySelector('.recap-table tbody');
|
||||||
|
if(tbody) {
|
||||||
|
Array.from(tbody.rows).forEach(row => {
|
||||||
|
const targetName = row.cells[0].innerText.trim(); // "vers L.Pol"
|
||||||
|
|
||||||
|
const alexSum = dataByTarget[targetName] ? dataByTarget[targetName].alex : 0;
|
||||||
|
const laiaSum = dataByTarget[targetName] ? dataByTarget[targetName].laia : 0;
|
||||||
|
const globalSum = alexSum + laiaSum;
|
||||||
|
|
||||||
|
// Mise à jour des cellules (Index 1=Alex, 2=Laia, 3=Global)
|
||||||
|
row.cells[1].innerText = Math.round(alexSum).toLocaleString('fr-FR') + ' €';
|
||||||
|
row.cells[2].innerText = Math.round(laiaSum).toLocaleString('fr-FR') + ' €';
|
||||||
|
row.cells[3].innerText = Math.round(globalSum).toLocaleString('fr-FR') + ' €';
|
||||||
|
|
||||||
|
// Gestion visuelle : Masquer la ligne si tout est à 0 (Optionnel)
|
||||||
|
// row.style.display = globalSum === 0 ? 'none' : '';
|
||||||
|
|
||||||
|
grandTotalAlex += alexSum;
|
||||||
|
grandTotalLaia += laiaSum;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mise à jour du Footer Grand Total
|
||||||
|
document.getElementById('grand_total_alex').innerText = Math.round(grandTotalAlex).toLocaleString('fr-FR') + ' €';
|
||||||
|
document.getElementById('grand_total_laia').innerText = Math.round(grandTotalLaia).toLocaleString('fr-FR') + ' €';
|
||||||
|
document.getElementById('grand_total_global').innerText = Math.round(grandTotalAlex + grandTotalLaia).toLocaleString('fr-FR') + ' €';
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveData(action, data) {
|
function saveData(action, data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user