Globinours/app/Views/accounting.php

315 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<style>
.accounting-dashboard-structures>.card>.table-responsive {
padding: .75rem 1rem 1rem
}
</style>
<?php $sum = (int)($accountingTotals['billed'] ?? array_sum(array_column($invoices, 'total_ttc_cents')));
$due = (int)($accountingTotals['due'] ?? array_sum(array_column($invoices, 'amount_due_cents')));
$paid = $sum - $due;
$money = static fn (int$c) => number_format($c / 100, 2, ',', ' ') . ' €';
$statusLabels = ['paid' => 'Payée','to_pay' => 'À payer','partial' => 'Partiellement payée','disputed' => 'Contestée','cancelled' => 'Annulée','credit' => 'Avoir'];
$methodLabels = ['transfer' => 'Virement','direct_debit' => 'Prélèvement','card' => 'Carte','check' => 'Chèque','cash' => 'Espèces','other' => 'Autre','unknown' => 'Non renseigné'];?>
<?php $accountingFilters = $accountingFilters ?? ['q' => '','vendor_id' => 0,'status' => '','type' => '','payment_method' => ''];
$filterQuery = array_merge(['year' => (int)$year], $accountingFilters);?>
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3 d-print-none">
<div class="text-muted">Pilotage des factures, paiements et partenaires</div>
<div class="btn-list"><a class="btn btn-outline-primary"
href="/accounting/export.csv?<?= h(http_build_query($filterQuery)) ?>">⬇ Export CSV</a><button
class="btn btn-outline-secondary" type="button" onclick="window.print()">🖨 Imprimer / PDF</button></div>
</div>
<form method="get" action="/accounting" class="card mb-3 d-print-none">
<div class="card-body">
<div class="row g-2 align-items-end">
<div class="col-md-4"><label class="form-label">Rechercher</label><input class="form-control" name="q"
value="<?= h($accountingFilters['q']) ?>" placeholder="Référence, structure ou notes…"></div>
<div class="col-md-2"><label class="form-label">Année</label><select class="form-select" name="year">
<option value="0">Toutes les années</option><?php foreach ($years as $y):?><option
value="<?= h($y) ?>" <?= (int)$y === $year ? 'selected' : ''?>><?= h($y) ?></option>
<?php endforeach?>
</select></div>
<div class="col-md-3"><label class="form-label">Structure</label><select class="form-select"
name="vendor_id">
<option value="0">Toutes les structures</option><?php foreach ($vendors as $v):?><option
value="<?= (int)$v['id'] ?>"
<?= (int)$v['id'] === (int)$accountingFilters['vendor_id'] ? 'selected' : ''?>>
<?= h($v['name']) ?></option><?php endforeach?>
</select></div>
<div class="col-md-3"><label class="form-label">Statut</label><select class="form-select" name="status">
<option value="">Tous les statuts</option><?php foreach ($statusLabels as $k => $label):?><option
value="<?= h($k) ?>" <?= $accountingFilters['status'] === $k ? 'selected' : ''?>>
<?= h($label) ?></option><?php endforeach?>
</select></div>
<div class="col-md-3"><label class="form-label">Type de pièce</label><select class="form-select"
name="type">
<option value="">Tous les types</option>
<?php foreach (['invoice' => 'Facture','credit' => 'Avoir','adjustment' => 'Régularisation'] as $k => $label):?>
<option value="<?= h($k) ?>" <?= $accountingFilters['type'] === $k ? 'selected' : ''?>>
<?= h($label) ?></option><?php endforeach?>
</select></div>
<div class="col-md-3"><label class="form-label">Règlement</label><select class="form-select"
name="payment_method">
<option value="">Tous les règlements</option><?php foreach ($methodLabels as $k => $label):?><option
value="<?= h($k) ?>" <?= $accountingFilters['payment_method'] === $k ? 'selected' : ''?>>
<?= h($label) ?></option><?php endforeach?>
</select></div>
<div class="col-md-6">
<div class="btn-list justify-content-end"><a class="btn btn-outline-secondary"
href="/accounting?year=0">Réinitialiser</a><button class="btn btn-primary">Filtrer</button>
</div>
</div>
</div>
</div>
</form>
<div class="row g-3 mb-3">
<div class="col-sm-6 col-xl-3">
<div class="card card-sm h-100">
<div class="card-body">
<div class="text-muted">Payé net</div>
<div class="h2 mb-0 text-green"><?= $money($paid) ?></div>
<div class="small text-muted">
<?= $sum ? number_format(max(0, $paid / $sum * 100), 1, ',', ' ') . ' % du facturé' : '—' ?></div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card card-sm h-100">
<div class="card-body">
<div class="text-muted">À régler</div>
<div class="h2 mb-0 text-orange"><?= $money($due) ?></div>
<div class="small text-muted"><?= (int)($accountingTotals['to_pay'] ?? 0) ?> pièce(s)</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card card-sm h-100">
<div class="card-body">
<div class="text-muted">Échéances dépassées</div>
<div class="h2 mb-0 <?= !empty($accountingTotals['overdue']) ? 'text-red' : '' ?>">
<?= (int)($accountingTotals['overdue'] ?? 0) ?></div>
<div class="small text-muted">Avec une date déchéance connue</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card card-sm h-100">
<div class="card-body">
<div class="text-muted">Taux restant</div>
<div class="h2 mb-0"><?= $sum ? number_format(max(0, $due / $sum * 100), 1, ',', ' ') . ' %' : '—' ?>
</div>
<div class="small text-muted">Sur la période choisie</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-xl-7">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">Coût et paiements par structure</h3>
</div>
<div class="table-responsive">
<table class="table table-sm table-vcenter mb-0">
<thead>
<tr>
<th>Structure</th>
<th class="text-end">Facturé</th>
<th class="text-end">Payé</th>
<th class="text-end">Reste dû</th>
<th class="text-end">Animaux</th>
<th class="text-end">Coût/animal</th>
</tr>
</thead>
<tbody><?php foreach (($vendorStats ?? []) as $v):$vendorPaid = $v['billed'] - $v['due'];?><tr>
<td><?php if ($v['id']):?><a class="fw-semibold"
href="/directory/view?id=<?= (int)$v['id'] ?>&amp;year=<?= $year ?: 'all' ?>"><?= h($v['label']) ?></a><?php else:?><?= h($v['label']) ?><?php endif?>
<div class="small text-muted">
<?= (int)$v['paid_documents'] ?>/<?= (int)$v['documents'] ?> pièce(s) soldée(s)
</div>
</td>
<td class="text-end"><?= $money((int)$v['billed']) ?></td>
<td class="text-end text-green"><?= $money((int)$vendorPaid) ?></td>
<td class="text-end <?= $v['due'] ? 'text-orange fw-bold' : '' ?>">
<?= $money((int)$v['due']) ?></td>
<td class="text-end"><?= (int)$v['animals'] ?: '—' ?></td>
<td class="text-end">
<?= $v['animals'] ? $money((int)round($v['animal_cost'] / $v['animals'])) : '—' ?></td>
</tr><?php endforeach?></tbody>
</table>
</div>
<div class="card-footer small text-muted">Le coût par animal utilise les actes déjà rapprochés des fiches
animales ; il peut différer du total des factures.</div>
</div>
</div>
<div class="col-xl-5">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">Évolution annuelle des paiements</h3>
</div>
<div class="card-body">
<div style="height:250px"><canvas id="accountingAnnualChart"
data-series="<?= h(json_encode($annualStats ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?>"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">État des pièces</h3>
</div>
<div class="card-body d-flex flex-wrap gap-2"><?php foreach (($statusStats ?? []) as $key => $count):?><span
class="badge <?= $key === 'paid' ? 'bg-green-lt' : ($key === 'to_pay' ? 'bg-orange-lt' : 'bg-secondary-lt') ?> p-2"><?= h($statusLabels[$key] ?? $key) ?>
· <?= (int)$count ?></span><?php endforeach?></div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">Modes de règlement</h3>
</div>
<div class="card-body d-flex flex-wrap gap-2"><?php foreach (($methodStats ?? []) as $key => $count):?><span
class="badge bg-blue-lt p-2"><?= h($methodLabels[$key] ?? $key) ?> ·
<?= (int)$count ?></span><?php endforeach?></div>
</div>
</div>
</div>
<?php $credits = array_sum(array_map(static fn ($r) => $r['document_type'] === 'credit' ? (int)$r['total_ttc_cents'] : 0, $invoices));?>
<div class="row g-3 mb-3">
<div class="col-md-4">
<div class="card card-sm">
<div class="card-body">
<div class="text-muted">Facturé TTC net</div>
<div class="h2 mb-0"><?= $money($sum) ?></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card card-sm">
<div class="card-body">
<div class="text-muted">Avoirs inclus</div>
<div class="h2 mb-0 text-green"><?= $money($credits) ?></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card card-sm">
<div class="card-body">
<div class="text-muted">Pièces comptables</div>
<div class="h2 mb-0"><?= count($invoices) ?></div>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<h3 class="card-title">🧾 Nouvelle facture ou avoir</h3>
</div>
<form method="post" action="/accounting/save" enctype="multipart/form-data"><input type="hidden" name="csrf"
value="<?= h(Auth::csrf()) ?>">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3"><label class="form-label">Référence *</label><input class="form-control"
name="reference" required></div>
<div class="col-md-3"><label class="form-label">Fournisseur / structure</label><select
class="form-select" name="vendor_contact_id">
<option value="">—</option><?php foreach ($vendors as $v):?><option
value="<?= (int)$v['id'] ?>"><?= h($v['name']) ?></option><?php endforeach?>
</select></div>
<div class="col-md-3"><label class="form-label">Date *</label><input class="form-control" type="date"
name="invoice_date" value="<?= h(date('Y-m-d')) ?>" required></div>
<div class="col-md-3"><label class="form-label">Échéance</label><input class="form-control" type="date"
name="due_date"></div>
<div class="col-md-2"><label class="form-label">Type</label><select class="form-select"
name="document_type">
<option value="invoice">Facture</option>
<option value="credit">Avoir</option>
<option value="adjustment">Régularisation</option>
</select></div>
<div class="col-md-2"><label class="form-label">Montant HT</label><input class="form-control"
name="total_ht" inputmode="decimal"></div>
<div class="col-md-2"><label class="form-label">Montant TTC *</label><input class="form-control"
name="total_ttc" inputmode="decimal" required></div>
<div class="col-md-2"><label class="form-label">Statut</label><select class="form-select" name="status">
<option value="to_pay">À payer</option>
<option value="paid">Payée</option>
<option value="partial">Partiellement payée</option>
<option value="disputed">Contestée</option>
<option value="credit">Avoir</option>
<option value="cancelled">Annulée</option>
</select></div>
<div class="col-md-2"><label class="form-label">Règlement</label><select class="form-select"
name="payment_method">
<option value="">—</option>
<option value="transfer">Virement</option>
<option value="direct_debit">Prélèvement</option>
<option value="card">Carte</option>
<option value="check">Chèque</option>
<option value="cash">Espèces</option>
<option value="other">Autre</option>
</select></div>
<div class="col-md-2"><label class="form-label">Reste dû</label><input class="form-control"
name="amount_due" inputmode="decimal"></div>
<div class="col-md-6"><label class="form-label">Facture (PDF ou image, 20 Mo max.)</label><input
class="form-control" type="file" name="document"
accept="application/pdf,image/jpeg,image/png,image/webp"></div>
<div class="col-md-6"><label class="form-label">Notes</label><input class="form-control" name="notes">
</div>
</div>
</div>
<div class="card-footer text-end"><button class="btn btn-primary">Ajouter</button></div>
</form>
</div>
<div class="card">
<div class="card-header d-flex align-items-center">
<div>
<h3 class="card-title mb-0">Journal des factures</h3>
<div class="text-muted small">
<?= $year ? 'Période : ' . (int)$year : 'Toutes les années · historique complet' ?></div>
</div>
<form class="ms-auto"><select class="form-select" name="year" onchange="this.form.submit()">
<option value="0">Toutes les années</option><?php foreach ($years as $y):?><option value="<?= h($y) ?>"
<?= (int)$y === $year ? 'selected' : ''?>><?= h($y) ?></option><?php endforeach?>
</select></form>
</div>
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th>Date</th>
<th>Référence</th>
<th>Type</th>
<th>Structure</th>
<th>HT</th>
<th>TTC</th>
<th>Statut</th>
<th>Règlement</th>
<th></th>
</tr>
</thead>
<tbody><?php foreach ($invoices as $r):?><tr>
<td><?= h(date('d/m/Y', strtotime($r['invoice_date']))) ?></td>
<td><?= h($r['reference']) ?></td>
<td><span
class="badge <?= $r['document_type'] === 'credit' ? 'bg-green-lt' : 'bg-blue-lt' ?>"><?= h(['invoice' => 'Facture','credit' => 'Avoir','adjustment' => 'Régularisation'][$r['document_type']] ?? $r['document_type']) ?></span>
</td>
<td><?= h($r['vendor_name'] ?? '—') ?></td>
<td><?= $r['total_ht_cents'] === null ? '—' : number_format($r['total_ht_cents'] / 100, 2, ',', ' ') . ' €' ?>
</td>
<td class="fw-bold <?= $r['total_ttc_cents'] < 0 ? 'text-green' : '' ?>">
<?= number_format($r['total_ttc_cents'] / 100, 2, ',', ' ') ?> €</td>
<td><span
class="badge <?= $r['status'] === 'paid' ? 'bg-green-lt' : ($r['status'] === 'to_pay' ? 'bg-orange-lt' : 'bg-secondary-lt') ?>"><?= h(['paid' => 'Payée','to_pay' => 'À payer','partial' => 'Partielle','disputed' => 'Contestée','cancelled' => 'Annulée','credit' => 'Avoir'][$r['status']] ?? $r['status']) ?></span>
</td>
<td><?= h(['transfer' => 'Virement','direct_debit' => 'Prélèvement','card' => 'Carte','check' => 'Chèque','cash' => 'Espèces','other' => 'Autre'][$r['payment_method']] ?? '—') ?>
</td>
<td><?php if ($r['stored_filename']):?><a class="btn btn-sm btn-outline-primary" target="_blank"
href="/accounting/document?id=<?= (int)$r['id'] ?>">Voir</a><?php endif?></td>
</tr><?php endforeach?><?php if (!$invoices):?><tr>
<td colspan="9" class="text-center text-muted py-4">Aucune facture pour cette période.</td>
</tr><?php endif?></tbody>
</table>
</div>
</div>