Files
bachir 8dfb4af98a Nouvelle page /dashboard/repartition, renomme "Dashboard" en "SAS" dans le menu
Nouvelle page "Répartition/Soldes" entre "SAS" (ex-"Dashboard") et "Par
compte" dans le menu : reprend "Solde par compte" et "Évolution du solde
par compte", retirés du dashboard général pour le recentrer sur
l'activité/CA/type/client. Mêmes données déjà exposées par
/dashboard/api/stats (solde_par_compte, solde_par_compte_par_annee),
aucun changement backend nécessaire pour cette page.

js/dashboard-repartition.js reprend le HBarChart/MiniTrend de
dashboard.js -- dupliqués plutôt que partagés, même convention que
dashboard-compte.js. Racine Vue volontairement le même id
#figli-dashboard-app que les deux autres pages dashboard (pas un id
dédié) : dashboard.css scope ses variables CSS (thème clair/sombre) sur
ce sélecteur, réutiliser le même id est comment les trois pages héritent
du même thème sans feuille de style séparée -- vérifié en dark mode.

Le lien de menu "Dashboard" devient "SAS" partout (les 3 templates Twig
+ le nav en render array de HistoryController, qui n'a pas de template
Twig propre).

dashboard.js : MiniTrend/soldeParCompteItems/comptesOrdonnes/
trendValues supprimés (code mort après le déplacement, plus rien ne les
utilise sur le dashboard général).
2026-09-08 15:00:22 +02:00

194 lines
11 KiB
Twig

{#
Dashboard par compte associé (freelance) : un compte à la fois, choisi
dans le sélecteur. Se concentre sur entrée client / versement freelance
uniquement -- l'objectif principal est de faire ressortir les versements
pas (encore) compensés par une entrée, pas de donner une vue comptable
complète (voir /dashboard pour ça).
{% verbatim %} ci-dessous : syntaxe Vue, pas Twig -- voir
figli-compta-dashboard.html.twig pour la même remarque.
#}
<nav class="figli-page-nav">
<a href="{{ path('figli_compta_ledger.home') }}" class="{{ current_route == 'figli_compta_ledger.home' ? 'is-active' : '' }}">Grand livre</a>
<a href="{{ path('figli_compta_ledger.dashboard') }}" class="{{ current_route == 'figli_compta_ledger.dashboard' ? 'is-active' : '' }}">SAS</a>
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_repartition' ? 'is-active' : '' }}">Répartition/Soldes</a>
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}" class="{{ current_route == 'figli_compta_ledger.dashboard_compte' ? 'is-active' : '' }}">Par compte</a>
</nav>
{% verbatim %}
<div id="figli-dashboard-app">
<p v-if="loading">Chargement des données…</p>
<p v-else-if="error" class="figli-error">Erreur de chargement du tableau de bord : {{ error }}</p>
<template v-else>
<div class="figli-compte-selector">
<label for="figli-compte-select">Compte associé</label>
<select id="figli-compte-select" :value="selectedCompte" @change="selectCompte($event.target.value)">
<option v-for="c in comptes" :key="c" :value="c">{{ c }}</option>
</select>
</div>
<div class="figli-summary-row">
<div class="figli-summary-card">
<div class="figli-summary-label">Solde actuel</div>
<div class="figli-summary-value" :class="soldeActuel < 0 ? 'is-negative' : 'is-positive'">{{ formatEurRound(soldeActuel) }}</div>
</div>
<div class="figli-summary-card" :class="{'is-alert': totalResteAVerser > 0.01}">
<div class="figli-summary-label">Reste à verser</div>
<div class="figli-summary-value" :class="totalResteAVerser > 0.01 ? 'is-negative' : ''">{{ formatEurRound(totalResteAVerser) }}</div>
</div>
<div class="figli-summary-card" v-if="totalSurVerse > 0.01">
<div class="figli-summary-label">Sur-versé</div>
<div class="figli-summary-value is-negative">{{ formatEurRound(totalSurVerse) }}</div>
</div>
<div class="figli-summary-card" :class="{'is-alert': totalNonLies > 0.01}" v-if="versementsNonLies.length">
<div class="figli-summary-label">Versements non liés</div>
<div class="figli-summary-value is-negative">{{ formatEurRound(totalNonLies) }}</div>
</div>
</div>
<section class="figli-chart-section">
<h2>Reste à verser -- entrées non (entièrement) compensées</h2>
<p class="figli-note">Pour {{ selectedCompte }} : entrées client dont la part attribuée n'a pas encore été entièrement reversée. Triées de la plus ancienne à la plus récente.</p>
<table v-if="resteAVerserPositif.length" class="figli-recon-table">
<thead>
<tr><th>Date</th><th>Client</th><th>Libellé</th><th>Attribué</th><th>Déjà versé</th><th>Reste à verser</th></tr>
</thead>
<tbody>
<tr v-for="r in resteAVerserPositif" :key="r.entree.id" :class="{'figli-flag-row': r.entree.hasFlag}">
<td>{{ formatDate(r.entree.date) }}</td>
<td><a :href="ligneHref(r.entree.client)">{{ r.entree.client || '—' }}</a></td>
<td>{{ r.entree.libelle || '—' }}<span v-for="f in r.entree.flags" :key="f" class="figli-flag-badge">{{ f }}</span></td>
<td>{{ formatEur(r.montantAttribue) }}</td>
<td>{{ formatEur(r.dejaVerse) }}</td>
<td class="is-negative">{{ formatEur(r.residual) }}</td>
</tr>
</tbody>
<tfoot>
<tr><td colspan="5">Total</td><td class="is-negative">{{ formatEurRound(totalResteAVerser) }}</td></tr>
</tfoot>
</table>
<p v-else class="figli-note">Rien en attente -- toutes les entrées de ce compte sont compensées.</p>
</section>
<section class="figli-chart-section" v-if="surVerseRows.length">
<h2>Entrées sur-versées</h2>
<p class="figli-note">Le montant versé pour ces entrées dépasse la part attribuée à {{ selectedCompte }} -- à vérifier.</p>
<table class="figli-recon-table">
<thead>
<tr><th>Date</th><th>Client</th><th>Libellé</th><th>Attribué</th><th>Versé</th><th>Sur-versé</th></tr>
</thead>
<tbody>
<tr v-for="r in surVerseRows" :key="r.entree.id" :class="{'figli-flag-row': r.entree.hasFlag}">
<td>{{ formatDate(r.entree.date) }}</td>
<td><a :href="ligneHref(r.entree.client)">{{ r.entree.client || '—' }}</a></td>
<td>{{ r.entree.libelle || '—' }}<span v-for="f in r.entree.flags" :key="f" class="figli-flag-badge">{{ f }}</span></td>
<td>{{ formatEur(r.montantAttribue) }}</td>
<td>{{ formatEur(r.dejaVerse) }}</td>
<td class="is-negative">{{ formatEur(-r.residual) }}</td>
</tr>
</tbody>
<tfoot>
<tr><td colspan="5">Total</td><td class="is-negative">{{ formatEurRound(totalSurVerse) }}</td></tr>
</tfoot>
</table>
</section>
<section class="figli-chart-section" v-if="versementsNonLies.length">
<h2>Versements sans entrée liée</h2>
<p class="figli-note">Versements de {{ selectedCompte }} qui ne pointent vers aucune entrée client -- ni "reste à verser" ni "sur-versé" ci-dessus ne les couvre.</p>
<table class="figli-recon-table">
<thead>
<tr><th>Date</th><th>Client</th><th>Libellé</th><th>Montant</th></tr>
</thead>
<tbody>
<tr v-for="r in versementsNonLies" :key="r.id" :class="{'figli-flag-row': r.hasFlag}">
<td>{{ formatDate(r.date) }}</td>
<td><a :href="ligneHref(r.client)">{{ r.client || '—' }}</a></td>
<td>{{ r.libelle || '—' }}<span v-for="f in r.flags" :key="f" class="figli-flag-badge">{{ f }}</span></td>
<td class="is-negative">{{ formatEur(r.parCompte[selectedCompte]) }}</td>
</tr>
</tbody>
<tfoot>
<tr><td colspan="3">Total</td><td class="is-negative">{{ formatEurRound(-totalNonLies) }}</td></tr>
</tfoot>
</table>
</section>
<section class="figli-chart-section" v-if="lignesSignalees.length">
<h2>Lignes signalées</h2>
<p class="figli-note">Entrées et versements de {{ selectedCompte }} portant un signalement (voir la colonne "Signalement" dans le grand livre) -- indépendant du solde, une ligne déjà réglée peut quand même être signalée pour une autre raison.</p>
<label>Signalement
<span class="figli-filter-row">
<div class="figli-multiselect">
<div class="figli-multiselect-trigger">{{ flagFilter.length ? flagFilter.join(', ') : 'Tous' }} <span class="figli-multiselect-arrow">▾</span></div>
<div class="figli-multiselect-panel">
<label v-for="f in flagsDisponibles" :key="f" class="figli-multiselect-option">
<input type="checkbox" :value="f" v-model="flagFilter" /> {{ f }}
</label>
</div>
</div>
<button v-if="flagFilter.length" type="button" class="figli-filter-clear" @click="flagFilter = []" title="Effacer ce filtre">✕</button>
</span>
</label>
<table class="figli-recon-table">
<thead>
<tr><th>Date</th><th>Type</th><th>Client</th><th>Libellé</th><th>Signalement</th><th>Montant</th></tr>
</thead>
<tbody>
<tr v-for="r in lignesSignaleesFiltrees" :key="r.id" class="figli-flag-row">
<td>{{ formatDate(r.date) }}</td>
<td>{{ r.type === 'entree' ? 'Entrée' : 'Versement' }}</td>
<td><a :href="ligneHref(r.client)">{{ r.client || '—' }}</a></td>
<td>{{ r.libelle || '—' }}</td>
<td><span v-for="f in r.flags" :key="f" class="figli-flag-badge">{{ f }}</span></td>
<td :class="r.parCompte[selectedCompte] < 0 ? 'is-negative' : ''">{{ formatEur(r.parCompte[selectedCompte]) }}</td>
</tr>
<tr v-if="!lignesSignaleesFiltrees.length"><td colspan="6" class="figli-note">Aucune ligne pour ce signalement.</td></tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">Solde</td>
<td :class="totalLignesSignalees < 0 ? 'is-negative' : ''">{{ formatEur(totalLignesSignalees) }}</td>
</tr>
</tfoot>
</table>
</section>
<section class="figli-chart-section">
<h2>Entrées vs versements par année</h2>
<p class="figli-note">Part de {{ selectedCompte }} dans les entrées client (vert) et ses versements (rouge), année par année.</p>
<year-bars-chart :years="entreeVsVersementYears" :format-value="formatEurRound"></year-bars-chart>
</section>
<section class="figli-chart-section">
<h2>Évolution du solde</h2>
<p class="figli-note">Solde de clôture de {{ selectedCompte }}, année par année (ouverture comprise).</p>
<year-bars-chart :years="evolutionSoldeYears" :format-value="formatEurRound"></year-bars-chart>
</section>
<section class="figli-chart-section" v-if="typeItems.length">
<h2>Répartition de l'activité par type</h2>
<p class="figli-note">Montant total (HT, valeur absolue) par type de ligne pour {{ selectedCompte }}, hors ouvertures -- contrairement aux tableaux ci-dessus, tous les types comptent ici (pas seulement entrée/versement).</p>
<h-bar-chart :items="typeItems" :format-value="formatEurRound" :color-for="typeColor"></h-bar-chart>
<div class="figli-year-hbar-grid">
<div class="figli-year-hbar-card" v-for="y in typeItemsParAnnee" :key="y.annee">
<div class="figli-year-hbar-title">{{ y.annee }}</div>
<h-bar-chart :items="y.items" :format-value="formatEurRound" :color-for="typeColor" compact></h-bar-chart>
</div>
</div>
</section>
<section class="figli-chart-section" v-if="topClientsItems.length">
<h2>Top clients</h2>
<p class="figli-note">Clients ayant généré le plus d'entrées attribuées à {{ selectedCompte }}, toutes années confondues.</p>
<h-bar-chart :items="topClientsItems" :format-value="formatEurRound"></h-bar-chart>
<div class="figli-year-hbar-grid">
<div class="figli-year-hbar-card" v-for="y in topClientsParAnnee" :key="y.annee">
<div class="figli-year-hbar-title">{{ y.annee }}</div>
<h-bar-chart :items="y.items" :format-value="formatEurRound" compact></h-bar-chart>
</div>
</div>
</section>
</template>
</div>
{% endverbatim %}