refactor: donne une source unique aux colonnes du compte rendu

L'ordre et les libellés des colonnes étaient retapés à six endroits : l'en-tête
du tableau, deux boucles de cellules, la ligne « Totaux », la table des libellés
et la liste des colonnes recoupées. Ces copies avaient déjà divergé — le solde
antérieur, rendu hors de la boucle, était comparé aux lignes sans que son écart
puisse s'afficher dans sa cellule. `COLONNES_CRG` devient la seule déclaration,
et tout le reste en dérive ; l'écart du solde antérieur s'affiche du coup là où
on le cherche.

`ecartsAvecLignes` rapporte maintenant l'extrait, le calculé et ce qui manque
entre les deux. Le composant refaisait cette soustraction pour son infobulle,
avec un `|| 0` là où l'utilitaire emploie `Number` et `Number.isFinite` : deux
règles de coercition pour un même calcul, libres de diverger.

Le contenu déplié d'un lot passe de `v-show` à `v-if`. Le tableau des lignes
compte une centaine de champs éditables ; les garder montés pour la vingtaine de
lots d'un document faisait re-rendre à chaque frappe deux mille champs que
personne ne regardait.

`setNestedValue` était recopié à l'identique dans trois composants et
`formatCurrency` redéfini dans le composant alors que `utils/format.js` existe
et dit lui-même que les nouveaux affichages passent par lui. Le premier part
dans `utils/chemin.js`, le second cède la place à `formatMontantPrecis`, dont le
formateur `Intl` est construit une fois pour toutes.

`EditableField` affirmait en dur que sa valeur venait du compte rendu. C'est vrai
des trois écrans qui l'emploient aujourd'hui, mais un formulaire de saisie
manuelle mentirait sans le savoir : l'origine devient une prop, avec cette
valeur par défaut.

Le type des lignes de report est défini des deux côtés de l'application sans
lien entre eux ; chacun renvoie désormais à l'autre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 14:18:58 +02:00
parent 6a638df1ab
commit 8c959f5d5f
9 changed files with 186 additions and 150 deletions

View File

@@ -43,9 +43,10 @@ function lotCharlot(surcharges = {}) {
// Les montants sont formatés avec des espaces insécables (séparateur de milliers,
// espace avant €) et le template en insère aux sauts de ligne : tous les blancs
// sont ramenés à un espace simple pour pouvoir chercher un montant.
// `highlighted` déplie la carte : le tableau n'est monté qu'à l'ouverture.
async function rendre(locataire) {
const app = createSSRApp({
render: () => h(LocataireCard, { locataire }),
render: () => h(LocataireCard, { locataire, highlighted: true }),
})
const html = await renderToString(app)
return html.replace(/\s+/g, ' ')
@@ -109,6 +110,17 @@ describe('LocataireCard', () => {
expect(html).toContain('bg-amber-500/10')
})
it('signale aussi le solde antérieur, qui na pas de colonne à lui', async () => {
// Il occupe la colonne « Période » de la ligne Totaux : son écart doit y
// apparaître comme celui des colonnes rendues par la boucle.
const lot = lotCharlot()
lot.totaux.solde_anterieur = 49170.47
const html = await rendre(lot)
expect(html).toContain('calculé : 0,00 €')
expect(html).toContain('A verifier')
})
it('dit au survol ce qui est extrait et ce qui est calculé', async () => {
const lot = lotCharlot()
lot.totaux.regles = 707.29

View File

@@ -138,7 +138,10 @@ describe('ecartsAvecLignes', () => {
},
calcules
)
expect(ecarts).toEqual({ total: 0, regles: 0 })
expect(ecarts).toEqual({
total: { extrait: 707.29, calcule: 0, manquant: 707.29 },
regles: { extrait: 707.29, calcule: 0, manquant: 707.29 },
})
})
it('signale un divers absent des lignes extraites', () => {
@@ -146,18 +149,22 @@ describe('ecartsAvecLignes', () => {
{ type: 'divers', divers: { montant: -455 } },
{ type: 'divers', divers: { montant: 23.02 } },
])
expect(ecartsAvecLignes({ divers: -123.22 }, calcules)).toEqual({ divers: -431.98 })
expect(ecartsAvecLignes({ divers: -123.22 }, calcules)).toEqual({
divers: { extrait: -123.22, calcule: -431.98, manquant: 308.76 },
})
})
it('signale un lot dont aucune ligne na été extraite', () => {
// Lot 08 de S_33680000_2025-08-26 : 100 € réglés, aucune ligne.
const ecarts = ecartsAvecLignes({ loyers: 0, regles: 100 }, totauxCalcules([]))
expect(ecarts).toEqual({ regles: 0 })
expect(ecarts).toEqual({ regles: { extrait: 100, calcule: 0, manquant: 100 } })
})
it('renvoie la valeur déduite des lignes, jamais celle du compte rendu', () => {
it('rapporte lextrait, le calculé et ce qui manque entre les deux', () => {
const calcules = totauxCalcules([{ type: 'loyer', loyers: 500 }])
expect(ecartsAvecLignes({ loyers: 800 }, calcules)).toEqual({ loyers: 500 })
expect(ecartsAvecLignes({ loyers: 800 }, calcules)).toEqual({
loyers: { extrait: 800, calcule: 500, manquant: 300 },
})
})
it('tolère un écart darrondi sous le centime', () => {