Scope versement reste-à-verser to its own compte(s), not the entrée total
versementStatus() was reusing reconciliationByEntree()'s aggregate
resteAVerser/surVerse, which sums residuals across every compte the
entrée touches -- including comptes tied to *other* sorties linked to
the same entrée. A versement paid entirely through Maud could show
"reste à verser" driven by an unrelated Sandrine/Chloé shortfall on
the same entrée, or vice versa mask its own compte's sur-versement
behind an unrelated compte's surplus.
reconciliationByEntree() now also keeps a per-compte residual map
(parCompteResidual), and versementStatus() sums only the residuals for
the compte(s) this specific versement's own répartition touches.
Verified against a real case: entrée EPAU F2549-50-51 (répartition
across 8 comptes) with one linked "Versement Maud" of -10 000€ against
an entrée-side Maud share of 5 632,86€. The entrée's own badge still
correctly shows the aggregate ("reste 24 325,54 € · sur-versé
4 367,14 €"), but the versement row itself now shows "Sur-versé :
4 367,14 €" -- its actual Maud-only residual -- instead of the
previous "Reste à verser", which was purely an artifact of the other
7 comptes' unrelated shortfalls.
This commit is contained in:
@@ -304,8 +304,15 @@
|
||||
let resteAVerser = 0;
|
||||
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
|
||||
// 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.
|
||||
const parCompteResidual = {};
|
||||
for (const c of comptes) {
|
||||
const residual = Math.round(((entreeRow.parCompte[c] || 0) + (versementsParCompte[c] || 0)) * 100) / 100;
|
||||
parCompteResidual[c] = residual;
|
||||
if (residual > 0.01) resteAVerser += residual;
|
||||
else if (residual < -0.01) surVerse += -residual;
|
||||
if (Math.abs(residual) > 0.01) detail.push(c + ' : ' + this.formatEur(residual));
|
||||
@@ -315,6 +322,7 @@
|
||||
resteAVerser: Math.round(resteAVerser * 100) / 100,
|
||||
surVerse: Math.round(surVerse * 100) / 100,
|
||||
detail: detail.join(', ') || 'Entièrement soldé',
|
||||
parCompteResidual,
|
||||
});
|
||||
}
|
||||
return map;
|
||||
@@ -423,13 +431,16 @@
|
||||
},
|
||||
// Flags a "versement freelance" row that isn't (fully) backed by
|
||||
// the entrée client it pays out against: either not linked at all,
|
||||
// or linked but reconciliationByEntree still shows a residual on
|
||||
// that entrée. The residual belongs to the entrée as a whole, not
|
||||
// to any one sortie -- when several versements share an entrée,
|
||||
// each shows the same aggregate residual, since there's no way to
|
||||
// say which specific one is "the" shortfall. Null (no highlight)
|
||||
// when the linked entrée isn't in the currently loaded window --
|
||||
// same accepted trade-off as reconciliationByEntree itself.
|
||||
// or linked but its own compte(s) still show a residual against
|
||||
// that entrée. Deliberately scoped to just the compte(s) this
|
||||
// versement's own répartition touches (reconciliationByEntree's
|
||||
// parCompteResidual), not the entrée's overall resteAVerser/
|
||||
// surVerse -- those can be driven entirely by a *different* compte
|
||||
// tied to some other sortie linked to the same entrée, which says
|
||||
// nothing about whether this versement's own répartition is
|
||||
// settled. Null (no highlight) when the linked entrée isn't in the
|
||||
// currently loaded window -- same accepted trade-off as
|
||||
// reconciliationByEntree itself.
|
||||
versementStatus(item) {
|
||||
if (item.type !== 'versement') return null;
|
||||
if (!item.entreeLieeId) {
|
||||
@@ -437,11 +448,18 @@
|
||||
}
|
||||
const recon = this.reconciliationByEntree.get(item.entreeLieeId);
|
||||
if (!recon) return null;
|
||||
if (recon.resteAVerser > 0.01) {
|
||||
return { kind: 'reste', detail: 'Reste à verser sur l’entrée liée : ' + this.formatEur(recon.resteAVerser) };
|
||||
let resteAVerser = 0;
|
||||
let surVerse = 0;
|
||||
for (const c of Object.keys(item.parCompte)) {
|
||||
const residual = recon.parCompteResidual[c] || 0;
|
||||
if (residual > 0.01) resteAVerser += residual;
|
||||
else if (residual < -0.01) surVerse += -residual;
|
||||
}
|
||||
if (recon.surVerse > 0.01) {
|
||||
return { kind: 'sur-verse', detail: 'Sur-versé sur l’entrée liée : ' + this.formatEur(recon.surVerse) };
|
||||
if (resteAVerser > 0.01) {
|
||||
return { kind: 'reste', detail: 'Reste à verser (comptes de cette ligne) : ' + this.formatEur(Math.round(resteAVerser * 100) / 100) };
|
||||
}
|
||||
if (surVerse > 0.01) {
|
||||
return { kind: 'sur-verse', detail: 'Sur-versé (comptes de cette ligne) : ' + this.formatEur(Math.round(surVerse * 100) / 100) };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user