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 resteAVerser = 0;
|
||||||
let surVerse = 0;
|
let surVerse = 0;
|
||||||
const detail = [];
|
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) {
|
for (const c of comptes) {
|
||||||
const residual = Math.round(((entreeRow.parCompte[c] || 0) + (versementsParCompte[c] || 0)) * 100) / 100;
|
const residual = Math.round(((entreeRow.parCompte[c] || 0) + (versementsParCompte[c] || 0)) * 100) / 100;
|
||||||
|
parCompteResidual[c] = residual;
|
||||||
if (residual > 0.01) resteAVerser += residual;
|
if (residual > 0.01) resteAVerser += residual;
|
||||||
else if (residual < -0.01) surVerse += -residual;
|
else if (residual < -0.01) surVerse += -residual;
|
||||||
if (Math.abs(residual) > 0.01) detail.push(c + ' : ' + this.formatEur(residual));
|
if (Math.abs(residual) > 0.01) detail.push(c + ' : ' + this.formatEur(residual));
|
||||||
@@ -315,6 +322,7 @@
|
|||||||
resteAVerser: Math.round(resteAVerser * 100) / 100,
|
resteAVerser: Math.round(resteAVerser * 100) / 100,
|
||||||
surVerse: Math.round(surVerse * 100) / 100,
|
surVerse: Math.round(surVerse * 100) / 100,
|
||||||
detail: detail.join(', ') || 'Entièrement soldé',
|
detail: detail.join(', ') || 'Entièrement soldé',
|
||||||
|
parCompteResidual,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
@@ -423,13 +431,16 @@
|
|||||||
},
|
},
|
||||||
// Flags a "versement freelance" row that isn't (fully) backed by
|
// Flags a "versement freelance" row that isn't (fully) backed by
|
||||||
// the entrée client it pays out against: either not linked at all,
|
// the entrée client it pays out against: either not linked at all,
|
||||||
// or linked but reconciliationByEntree still shows a residual on
|
// or linked but its own compte(s) still show a residual against
|
||||||
// that entrée. The residual belongs to the entrée as a whole, not
|
// that entrée. Deliberately scoped to just the compte(s) this
|
||||||
// to any one sortie -- when several versements share an entrée,
|
// versement's own répartition touches (reconciliationByEntree's
|
||||||
// each shows the same aggregate residual, since there's no way to
|
// parCompteResidual), not the entrée's overall resteAVerser/
|
||||||
// say which specific one is "the" shortfall. Null (no highlight)
|
// surVerse -- those can be driven entirely by a *different* compte
|
||||||
// when the linked entrée isn't in the currently loaded window --
|
// tied to some other sortie linked to the same entrée, which says
|
||||||
// same accepted trade-off as reconciliationByEntree itself.
|
// 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) {
|
versementStatus(item) {
|
||||||
if (item.type !== 'versement') return null;
|
if (item.type !== 'versement') return null;
|
||||||
if (!item.entreeLieeId) {
|
if (!item.entreeLieeId) {
|
||||||
@@ -437,11 +448,18 @@
|
|||||||
}
|
}
|
||||||
const recon = this.reconciliationByEntree.get(item.entreeLieeId);
|
const recon = this.reconciliationByEntree.get(item.entreeLieeId);
|
||||||
if (!recon) return null;
|
if (!recon) return null;
|
||||||
if (recon.resteAVerser > 0.01) {
|
let resteAVerser = 0;
|
||||||
return { kind: 'reste', detail: 'Reste à verser sur l’entrée liée : ' + this.formatEur(recon.resteAVerser) };
|
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) {
|
if (resteAVerser > 0.01) {
|
||||||
return { kind: 'sur-verse', detail: 'Sur-versé sur l’entrée liée : ' + this.formatEur(recon.surVerse) };
|
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;
|
return null;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user