tricount
This commit is contained in:
+131
-14
@@ -156,15 +156,30 @@ foreach ($gifts as $gift) {
|
|||||||
$desc = htmlspecialchars($gift['gift_description']);
|
$desc = htmlspecialchars($gift['gift_description']);
|
||||||
$amt = (float)$gift['amount'];
|
$amt = (float)$gift['amount'];
|
||||||
$plink = trim($gift['product_link'] ?? '');
|
$plink = trim($gift['product_link'] ?? '');
|
||||||
|
$payer = $gift['payer_name'] ?? $gift['adult_name']; // sécurité si colonne manquante
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<div class="cl-gift-line">
|
||||||
<?php if ($plink !== ''): ?>
|
<?php if ($plink !== ''): ?>
|
||||||
<a href="<?= htmlspecialchars($plink) ?>" target="_blank" rel="noopener noreferrer" class="cl-gift-link"><?= $desc ?></a>
|
<a href="<?= htmlspecialchars($plink) ?>"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="cl-gift-link"><?= $desc ?></a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<span class="cl-gift-desc"><?= $desc ?></span>
|
<span class="cl-gift-desc"><?= $desc ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($amt > 0): ?>
|
<?php if ($amt > 0): ?>
|
||||||
<span class="cl-gift-amount">(<?= number_format($amt, 0, ',', ' ') ?> €)</span>
|
<span class="cl-gift-amount">(<?= number_format($amt, 0, ',', ' ') ?> €)</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($payer) && $payer !== $gift['adult_name']): ?>
|
||||||
|
<small style="color:#b91c1c; font-style:italic;">
|
||||||
|
(pagat per <?= htmlspecialchars($payer) ?>)
|
||||||
|
</small>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<span class="cl-empty">—</span>
|
<span class="cl-empty">—</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -222,6 +237,103 @@ foreach ($gifts as $gift) {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================================== -->
|
||||||
|
<!-- Calcul des dettes pour l'année en cours -->
|
||||||
|
<!-- =============================================================== -->
|
||||||
|
<?php
|
||||||
|
// Calcul des dettes pour l'année en cours
|
||||||
|
$people = $adults;
|
||||||
|
// Inclure tout adulte qui apparaîtrait en base (sécurité)
|
||||||
|
$adultsInDb = array_values(array_unique(array_column($gifts, 'adult_name')));
|
||||||
|
$payersInDb = array_values(array_unique(array_column($gifts, 'payer_name')));
|
||||||
|
$people = array_values(array_unique(array_merge($people, $adultsInDb, $payersInDb)));
|
||||||
|
|
||||||
|
// Matrice brute: qui doit à qui (adult -> payer)
|
||||||
|
$matrix = [];
|
||||||
|
foreach ($people as $p1) {
|
||||||
|
foreach ($people as $p2) {
|
||||||
|
if (!isset($matrix[$p1])) $matrix[$p1] = [];
|
||||||
|
$matrix[$p1][$p2] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($gifts as $g) {
|
||||||
|
$adult = $g['adult_name'];
|
||||||
|
$payer = $g['payer_name'] ?? $g['adult_name'];
|
||||||
|
$amt = (float)$g['amount'];
|
||||||
|
if ($amt > 0 && $adult !== $payer) {
|
||||||
|
$matrix[$adult][$payer] += $amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Net pairwise (A<->B)
|
||||||
|
$settlements = []; // [ [from, to, amount], ... ]
|
||||||
|
for ($i = 0; $i < count($people); $i++) {
|
||||||
|
for ($j = $i + 1; $j < count($people); $j++) {
|
||||||
|
$a = $people[$i];
|
||||||
|
$b = $people[$j];
|
||||||
|
$net = $matrix[$a][$b] - $matrix[$b][$a];
|
||||||
|
if ($net > 0.009) {
|
||||||
|
$settlements[] = [$a, $b, $net]; // a doit à b
|
||||||
|
} elseif ($net < -0.009) {
|
||||||
|
$settlements[] = [$b, $a, -$net]; // b doit à a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="pf-section pf-section--panel">
|
||||||
|
<h2>Tricount</h2>
|
||||||
|
|
||||||
|
<div class="cl-budget-wrapper">
|
||||||
|
<table class="pf-table pf-table--compact cl-debt-matrix">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="cl-matrix-corner">
|
||||||
|
<span>Deutor ↓</span>
|
||||||
|
<span>Creditor →</span>
|
||||||
|
</th>
|
||||||
|
<?php foreach ($people as $p): ?>
|
||||||
|
<th><?= htmlspecialchars($p) ?></th>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($people as $debtor): ?>
|
||||||
|
<tr>
|
||||||
|
<th><?= htmlspecialchars($debtor) ?></th>
|
||||||
|
<?php foreach ($people as $creditor): ?>
|
||||||
|
<?php
|
||||||
|
$val = $matrix[$debtor][$creditor] ?? 0;
|
||||||
|
$isDiag = ($debtor === $creditor);
|
||||||
|
$display = $isDiag || $val == 0 ? '—' : number_format($val, 0, ',', ' ') . ' €';
|
||||||
|
$cls = $isDiag ? 'cl-mtx-diag' : ($val > 0 ? 'cl-mtx-owe' : 'cl-mtx-empty');
|
||||||
|
?>
|
||||||
|
<td class="<?= $cls ?>"><?= $display ?></td>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 style="margin-top:10px;">Liquidacions</h3>
|
||||||
|
<?php if (empty($settlements)): ?>
|
||||||
|
<p class="cl-legend">Cap deute pendent.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($settlements as [$from, $to, $amt]): ?>
|
||||||
|
<li><?= htmlspecialchars($from) ?> ha de pagar <?= number_format($amt, 0, ',', ' ') ?> € a <?= htmlspecialchars($to) ?></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif; ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- =============================================================== -->
|
<!-- =============================================================== -->
|
||||||
<!-- LISTE DÉTAILLÉE DES CADEAUX (vue "debug"/admin") -->
|
<!-- LISTE DÉTAILLÉE DES CADEAUX (vue "debug"/admin") -->
|
||||||
<!-- =============================================================== -->
|
<!-- =============================================================== -->
|
||||||
@@ -273,21 +385,21 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const year = btn.getAttribute('data-year');
|
const year = btn.getAttribute('data-year');
|
||||||
const child = btn.getAttribute('data-child');
|
const child = btn.getAttribute('data-child');
|
||||||
const occasion = btn.getAttribute('data-occasion');
|
const occasion = btn.getAttribute('data-occasion');
|
||||||
|
|
||||||
modal.classList.add('cl-open');
|
modal.classList.add('cl-open');
|
||||||
|
// Contexte
|
||||||
// Remplir les champs cachés
|
|
||||||
document.getElementById('clm-year').value = year;
|
document.getElementById('clm-year').value = year;
|
||||||
document.getElementById('clm-child').value = child;
|
document.getElementById('clm-child').value = child;
|
||||||
document.getElementById('clm-occasion').value = occasion;
|
document.getElementById('clm-occasion').value = occasion;
|
||||||
|
// Reset
|
||||||
// Reset des champs visibles
|
|
||||||
document.getElementById('clm-adult').selectedIndex = 0;
|
document.getElementById('clm-adult').selectedIndex = 0;
|
||||||
document.getElementById('clm-gift').value = '';
|
document.getElementById('clm-gift').value = '';
|
||||||
document.getElementById('clm-amount').value = '';
|
document.getElementById('clm-amount').value = '';
|
||||||
document.getElementById('clm-link').value = '';
|
document.getElementById('clm-link').value = '';
|
||||||
|
// Titre dynamique
|
||||||
|
const titleEl = document.getElementById('cl-modal-title');
|
||||||
|
titleEl.textContent = `Afegeix un regal per ${child}`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
modal.classList.remove('cl-open');
|
modal.classList.remove('cl-open');
|
||||||
@@ -304,12 +416,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
<form method="post" action="/modules/christmas-list/save-gift.php" class="cl-modal-form">
|
<form method="post" action="/modules/christmas-list/save-gift.php" class="cl-modal-form">
|
||||||
<h3 id="cl-modal-title">Afegeix un regal</h3>
|
<h3 id="cl-modal-title">Afegeix un regal</h3>
|
||||||
|
|
||||||
<!-- Champs cachés (contexte) -->
|
|
||||||
<input type="hidden" name="year" id="clm-year" value="<?= $year ?>">
|
<input type="hidden" name="year" id="clm-year" value="<?= $year ?>">
|
||||||
<input type="hidden" name="child_name" id="clm-child" value="">
|
<input type="hidden" name="child_name" id="clm-child" value="">
|
||||||
<input type="hidden" name="occasion" id="clm-occasion" value="">
|
<input type="hidden" name="occasion" id="clm-occasion" value="">
|
||||||
|
|
||||||
<!-- Adulte -->
|
|
||||||
<label class="clm-label">
|
<label class="clm-label">
|
||||||
Adult
|
Adult
|
||||||
<select name="adult_name" id="clm-adult" required>
|
<select name="adult_name" id="clm-adult" required>
|
||||||
@@ -319,19 +429,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- Cadeau -->
|
<label class="clm-label">
|
||||||
|
Pagat per
|
||||||
|
<select name="payer_name" id="clm-payer" required>
|
||||||
|
<?php foreach ($adults as $adultName): ?>
|
||||||
|
<option value="<?= htmlspecialchars($adultName) ?>"><?= htmlspecialchars($adultName) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label class="clm-label">
|
<label class="clm-label">
|
||||||
Nom del regal
|
Nom del regal
|
||||||
<input type="text" name="gift_description" id="clm-gift" placeholder="p. ex., Lego Star Wars" required>
|
<input type="text" name="gift_description" id="clm-gift" placeholder="p. ex., Lego Star Wars" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- Montant -->
|
|
||||||
<label class="clm-label">
|
<label class="clm-label">
|
||||||
Preu (€)
|
Preu (€)
|
||||||
<input type="number" name="amount" id="clm-amount" placeholder="p. ex., 49,99" step="0.01" min="0">
|
<input type="number" name="amount" id="clm-amount" placeholder="p. ex., 49,99" step="0.01" min="0">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- Lien produit (optionnel) -->
|
|
||||||
<label class="clm-label">
|
<label class="clm-label">
|
||||||
Enllaç (opcional)
|
Enllaç (opcional)
|
||||||
<input type="url" name="product_link" id="clm-link" placeholder="https://exemple.com/producte">
|
<input type="url" name="product_link" id="clm-link" placeholder="https://exemple.com/producte">
|
||||||
@@ -339,9 +455,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
<div class="cl-modal-actions">
|
<div class="cl-modal-actions">
|
||||||
<button type="button" class="clm-cancel">Cancel·la</button>
|
<button type="button" class="clm-cancel">Cancel·la</button>
|
||||||
<button type="submit" class="clm-ok">Desa</button>
|
<button type="submit" class="clm-ok">OK</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@ label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
width: 3.1rem;
|
width: 150px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 2px 3px;
|
padding: 2px 3px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,10 +176,25 @@
|
|||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prix du cadeau (dans la cellule) */
|
/* Ligne cadeau: description à gauche, montant complètement à droite */
|
||||||
|
.cl-gift-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.cl-gift-desc,
|
||||||
|
.cl-gift-link {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0; /* autorise le wrapping du texte si très long */
|
||||||
|
}
|
||||||
|
|
||||||
.cl-gift-amount {
|
.cl-gift-amount {
|
||||||
|
margin-left: auto; /* pousse le montant à droite */
|
||||||
|
text-align: right;
|
||||||
color: #627d98;
|
color: #627d98;
|
||||||
white-space: nowrap;
|
white-space: nowrap; /* reste sur une seule ligne */
|
||||||
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code couleur par enfant (bordure + header/caption) */
|
/* Code couleur par enfant (bordure + header/caption) */
|
||||||
@@ -307,13 +322,28 @@
|
|||||||
color: #1f2933;
|
color: #1f2933;
|
||||||
}
|
}
|
||||||
.clm-cancel {
|
.clm-cancel {
|
||||||
color: #9fb3c8;
|
color: #d03050;
|
||||||
}
|
}
|
||||||
.clm-cancel:hover,
|
.clm-cancel:hover,
|
||||||
.clm-ok:hover {
|
.clm-ok:hover {
|
||||||
background: #d9e2ec;
|
background: #d9e2ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prix: sans spinners, largeur fixe */
|
||||||
|
#clm-amount {
|
||||||
|
width: 150px;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 3px;
|
||||||
|
-moz-appearance: textfield; /* Firefox legacy */
|
||||||
|
appearance: textfield; /* Standard property (modern browsers) */
|
||||||
|
}
|
||||||
|
/* Chrome/Safari */
|
||||||
|
#clm-amount::-webkit-outer-spin-button,
|
||||||
|
#clm-amount::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================= */
|
/* ========================================================= */
|
||||||
/* Résumé du budget et Liste détaillée */
|
/* Résumé du budget et Liste détaillée */
|
||||||
/* ========================================================= */
|
/* ========================================================= */
|
||||||
@@ -350,3 +380,32 @@
|
|||||||
flex: 0 0 20px;
|
flex: 0 0 20px;
|
||||||
filter: none; /* garde les couleurs des SVG/PNG */
|
filter: none; /* garde les couleurs des SVG/PNG */
|
||||||
}
|
}
|
||||||
|
/* clarté de lecture */
|
||||||
|
.cl-debt-matrix .cl-matrix-corner {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #6b7280;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 1.2;
|
||||||
|
min-width: 92px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-debt-matrix td.cl-mtx-diag {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
#111827 0,
|
||||||
|
#111827 6px,
|
||||||
|
#1f2937 6px,
|
||||||
|
#1f2937 12px
|
||||||
|
);
|
||||||
|
color: transparent; /* masque le “—” pour un rendu propre */
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-debt-matrix td.cl-mtx-owe {
|
||||||
|
background: #fff7ed; /* léger orange */
|
||||||
|
color: #7c2d12; /* brun/orange foncé */
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-debt-matrix td.cl-mtx-empty {
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,32 +7,37 @@ require __DIR__ . '/../../includes/db.php';
|
|||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$year = (int)($_POST['year'] ?? date('Y'));
|
$year = (int)($_POST['year'] ?? date('Y'));
|
||||||
$adult_name = trim($_POST['adult_name'] ?? '');
|
$adult_name = trim($_POST['adult_name'] ?? '');
|
||||||
|
$payer_name = trim($_POST['payer_name'] ?? '');
|
||||||
$child_name = trim($_POST['child_name'] ?? '');
|
$child_name = trim($_POST['child_name'] ?? '');
|
||||||
$occasion = trim($_POST['occasion'] ?? ''); // TIO / NADAL / REIS
|
$occasion = trim($_POST['occasion'] ?? '');
|
||||||
$gift_desc = trim($_POST['gift_description'] ?? '');
|
$gift_desc = trim($_POST['gift_description'] ?? '');
|
||||||
$product_link = trim($_POST['product_link'] ?? '');
|
$product_link = trim($_POST['product_link'] ?? '');
|
||||||
$amount = $_POST['amount'] !== '' ? (float)$_POST['amount'] : 0.0;
|
$amount = $_POST['amount'] !== '' ? (float)$_POST['amount'] : 0.0;
|
||||||
|
|
||||||
if ($adult_name && $child_name && $occasion && $gift_desc) {
|
if ($payer_name === '') {
|
||||||
|
$payer_name = $adult_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($adult_name && $payer_name && $child_name && $occasion && $gift_desc) {
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
INSERT INTO pf_christmas_gifts
|
INSERT INTO pf_christmas_gifts
|
||||||
(year, adult_name, child_name, occasion, gift_description, product_link, amount)
|
(year, adult_name, payer_name, child_name, occasion, gift_description, product_link, amount)
|
||||||
VALUES
|
VALUES
|
||||||
(:year, :adult_name, :child_name, :occasion, :gift_description, :product_link, :amount)
|
(:year, :adult_name, :payer_name, :child_name, :occasion, :gift_description, :product_link, :amount)
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
'year' => $year,
|
'year' => $year,
|
||||||
'adult_name' => $adult_name,
|
'adult_name' => $adult_name,
|
||||||
|
'payer_name' => $payer_name,
|
||||||
'child_name' => $child_name,
|
'child_name' => $child_name,
|
||||||
'occasion' => $occasion,
|
'occasion' => $occasion,
|
||||||
'gift_description'=> $gift_desc,
|
'gift_description' => $gift_desc,
|
||||||
'product_link' => $product_link ?: null,
|
'product_link' => $product_link ?: null,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retour à la page Christmas list
|
|
||||||
header('Location: /christmas-list.php');
|
header('Location: /christmas-list.php');
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
Reference in New Issue
Block a user