Show the link-status badge on all linkable types, not just versement

Hébergement (and achat/sous-traitant) were already linkable to an
entrée client -- LINKABLE_TYPES already included them, so the "Lier"
button and the reconciliation math worked fine -- but versementStatus()
hardcoded item.type !== 'versement' and returned null for every other
type, so those rows never got the Non liée/Lié/Reste à verser/Sur-versé
badge at all, and had no way to open the link form except the small
actions-column icon.

Renamed versementStatus()/versementStatusLabel()/versementStatusClasses()
to linkStatus()/linkStatusLabel()/linkStatusClasses() and generalized
the type check to LINKABLE_TYPES.includes(item.type). Verified live: a
hébergement row linked to an entrée now shows the same badge, status
math, and drill-down modal as a versement.
This commit is contained in:
2026-09-05 21:17:18 +02:00
parent 8f65a9097a
commit 0c9f8ca41f
2 changed files with 28 additions and 27 deletions
@@ -417,7 +417,7 @@
let surVerse = 0;
const detail = [];
// Kept per-compte (not just folded into the two totals above) --
// versementStatus() below needs to check a single sortie's own
// linkStatus() below needs to check a single sortie's own
// compte(s) against the entrée, not the entrée's overall
// reconciliation, which can span *other* comptes tied to other
// sorties linked to the same entrée.
@@ -567,37 +567,38 @@
const detail = relevant.map((compte) => compte + ' : ' + this.formatEur(ecarts[compte])).join(', ');
return { detail, comptes: relevant.length };
},
// Status badge for a "versement freelance" row -- always present
// when the row is a versement, specifically so a linked-but-settled
// versement still gets a badge to drill down through (it used to
// return null there, silently losing the only way to open the
// linked entrée's filtered view for versements with nothing wrong
// to report). "kind" distinguishes not-linked-at-all, a residual
// against at least one linked entrée, or linked-and-settled:
// Status badge for any linkable sortie row (versement, achat,
// hébergement, sous-traitant -- see LINKABLE_TYPES) -- always
// present for one of those types, specifically so a
// linked-but-settled row still gets a badge to drill down through
// (it used to return null there, silently losing the only way to
// open the linked entrée's filtered view for a row with nothing
// wrong to report). "kind" distinguishes not-linked-at-all, a
// residual against at least one linked entrée, or linked-and-settled:
// - not linked at all
// - linked but its own compte(s) still show a residual against at
// least one linked entrée -- deliberately scoped to just the
// compte(s) this versement's own répartition touches
// compte(s) this row's own répartition touches
// (reconciliationByEntree's parCompteResidual), not the
// entrée's overall resteAVerser/surVerse, which can be driven
// entirely by a *different* compte tied to some other sortie
// linked to the same entrée and would say nothing about
// whether this versement's own répartition is settled. When
// linked to several entrées (see reconciliationByEntree's
// equal-split note), each entrée's residual for these compte(s)
// counts separately -- they're independent invoices, each with
// its own outstanding amount.
// whether this row's own répartition is settled. When linked
// to several entrées (see reconciliationByEntree's equal-split
// note), each entrée's residual for these compte(s) counts
// separately -- they're independent invoices, each with its
// own outstanding amount.
// - linked and settled
// - inconnu: at least one linked entrée's reconciliation couldn't
// be resolved (not yet fetched into groupExtraRows -- see
// allKnownRows()/loadEntreeGroup()). Deliberately distinct from
// "ok": defaulting an unresolved entrée to "settled" would show
// a false all-clear for a versement that's actually fine, or
// one that owes money, purely because its linked entrée hasn't
// a false all-clear for a row that's actually fine, or one
// that owes money, purely because its linked entrée hasn't
// been fetched yet -- opening the badge resolves it and flips
// the status to whatever it actually is.
versementStatus(item) {
if (item.type !== 'versement') return null;
linkStatus(item) {
if (!LINKABLE_TYPES.includes(item.type)) return null;
if (!item.entreeLieeIds.length) {
return { kind: 'non-liee', detail: 'Aucune entrée client liée.' };
}
@@ -628,7 +629,7 @@
const n = item.entreeLieeIds.length;
return { kind: 'ok', detail: 'Lié à ' + n + ' entrée' + (n > 1 ? 's' : '') + ' client' + (n > 1 ? 's' : '') + '.' };
},
versementStatusLabel(kind) {
linkStatusLabel(kind) {
if (kind === 'non-liee') return 'Non liée';
if (kind === 'reste') return 'Reste à verser';
if (kind === 'sur-verse') return 'Sur-versé';
@@ -639,7 +640,7 @@
// inconnu are their own softer amber; ok gets neither, falling back
// to the badge's default green -- same "all clear" green the
// entrée side already uses for a fully-settled "N sorties liées".
versementStatusClasses(kind) {
linkStatusClasses(kind) {
return {
'is-anomalie': kind === 'non-liee' || kind === 'sur-verse',
'is-reste': kind === 'reste' || kind === 'inconnu',
@@ -157,12 +157,12 @@
:title="'Écart avec la clôture calculée de ' + (item.date.slice(0, 4) - 1) + ' : ' + ouvertureEcart(item).detail"
>⚠ écart clôture {{ item.date.slice(0, 4) - 1 }} ({{ ouvertureEcart(item).comptes }} compte{{ ouvertureEcart(item).comptes > 1 ? 's' : '' }})</span>
<span
v-if="versementStatus(item)"
v-if="linkStatus(item)"
class="figli-recon-badge is-clickable"
:class="versementStatusClasses(versementStatus(item).kind)"
:title="item.entreeLieeIds.length ? versementStatus(item).detail + ' -- cliquer pour voir la ou les entrées liées' : versementStatus(item).detail + ' -- cliquer pour lier une entrée client'"
:class="linkStatusClasses(linkStatus(item).kind)"
:title="item.entreeLieeIds.length ? linkStatus(item).detail + ' -- cliquer pour voir la ou les entrées liées' : linkStatus(item).detail + ' -- cliquer pour lier une entrée client'"
@click="item.entreeLieeIds.length ? toggleEntreeFilter(item.entreeLieeIds[0]) : openLinkForm(item.nid)"
>{{ versementStatus(item).kind === 'ok' ? '' : '⚠ ' }}{{ versementStatusLabel(versementStatus(item).kind) }}</span>
>{{ linkStatus(item).kind === 'ok' ? '' : '⚠ ' }}{{ linkStatusLabel(linkStatus(item).kind) }}</span>
</td>
<td class="amount" :class="montantClass(item.montant_ht)">{{ formatEur(item.montant_ht) }}</td>
<td class="amount">{{ formatEur(item.montant_ttc) }}</td>
@@ -234,10 +234,10 @@
:class="{'is-anomalie': reconciliationByEntree.get(item.id).surVerse > 0, 'is-reste': reconciliationByEntree.get(item.id).resteAVerser > 0}"
>{{ reconciliationByEntree.get(item.id).count }} sortie{{ reconciliationByEntree.get(item.id).count > 1 ? 's' : '' }} liée{{ reconciliationByEntree.get(item.id).count > 1 ? 's' : '' }}<template v-if="reconciliationByEntree.get(item.id).resteAVerser > 0"> · reste {{ formatEur(reconciliationByEntree.get(item.id).resteAVerser) }}</template><template v-if="reconciliationByEntree.get(item.id).surVerse > 0"> · sur-versé {{ formatEur(reconciliationByEntree.get(item.id).surVerse) }}</template></span>
<span
v-if="versementStatus(item)"
v-if="linkStatus(item)"
class="figli-recon-badge"
:class="versementStatusClasses(versementStatus(item).kind)"
>{{ versementStatus(item).kind === 'ok' ? '' : '⚠ ' }}{{ versementStatusLabel(versementStatus(item).kind) }}</span>
:class="linkStatusClasses(linkStatus(item).kind)"
>{{ linkStatus(item).kind === 'ok' ? '' : '⚠ ' }}{{ linkStatusLabel(linkStatus(item).kind) }}</span>
</td>
<td class="amount" :class="montantClass(item.montant_ht)">{{ formatEur(item.montant_ht) }}</td>
<td class="amount">{{ formatEur(item.montant_ttc) }}</td>