Files
pdf_oralia_vibe/frontend/tests/locataireCard.test.js
Bertrand Benjamin 8c959f5d5f 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>
2026-07-31 14:18:58 +02:00

158 lines
5.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// La carte locataire est la vue où l'on vérifie une extraction avant de
// l'enregistrer : elle ne doit afficher que des champs extraits, tous modifiables,
// et ne jamais substituer un calcul à ce que le compte rendu porte.
import { describe, expect, it } from 'vitest'
import { createSSRApp, h } from 'vue'
import { renderToString } from 'vue/server-renderer'
import LocataireCard from '../src/components/LocataireCard.vue'
// Lot 11 de M_33670000_2025-09-22, avec le règlement mal extrait observé sur un
// compte rendu ultérieur : le PDF annonce 7 878,00 € réglés pour 867,45 € dus.
function lotCharlot(surcharges = {}) {
return {
lot: { numero: '11', type: 'Appartement T3' },
locataire: { nom: 'CHARLOT ANDREE' },
lignes: [
{
type: 'loyer',
periode: { debut: '2025-09-01', fin: '2025-09-30' },
loyers: 798.45,
taxes: 0,
provisions: 69,
divers: { montant: 0, libelle: null },
total: 867.45,
regles: 867.45,
impayes: 0,
},
],
totaux: {
solde_anterieur: 0,
loyers: 798.45,
taxes: 0,
provisions: 69,
divers: 0,
total: 867.45,
regles: 867.45,
impayes: 0,
},
...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, highlighted: true }),
})
const html = await renderToString(app)
return html.replace(/\s+/g, ' ')
}
/** Les lignes seules, sans la ligne « Totaux » qui porte les valeurs déduites. */
function corpsDuTableau(html) {
return html.match(/<tbody>(.*)<\/tbody>/)[1]
}
describe('LocataireCard', () => {
it('reprend les colonnes du compte rendu, ligne « Totaux » comprise', async () => {
const html = await rendre(lotCharlot())
for (const colonne of ['Loyers', 'Taxes', 'Provisions', 'Divers', 'Total', 'Regles', 'Impaye']) {
expect(html).toContain(colonne)
}
expect(html).toContain('Totaux')
expect(html).toContain('Solde ant.')
})
it('laisse la ligne porter le total du compte rendu, sans le recalculer', async () => {
// Lot 13 de M_33670000_2025-08-26 : colonne « total » vide sur cette ligne,
// le compte rendu la porte sur une autre ligne du même bloc.
const lot = lotCharlot()
lot.lignes[0].loyers = 997.02
lot.lignes[0].provisions = 64
lot.lignes[0].total = 0
const lignes = corpsDuTableau(await rendre(lot))
expect(lignes).toContain('997,02 €')
expect(lignes).toContain('64,00 €')
// La somme des colonnes n'a pas à apparaître : la ligne n'est pas déduite.
expect(lignes).not.toContain('1 061,02 €')
})
it('naffiche aucune valeur déduite quand lextraction se recoupe', async () => {
const html = await rendre(lotCharlot())
expect(html).not.toContain('calc.')
expect(html).not.toContain('A verifier')
})
it('signale la colonne qui ne somme pas, sans toucher au montant extrait', async () => {
// Cas du lot 07 de S_33680000_2025-11-25 : la colonne Total des lignes reste
// à 0 alors que le compte rendu annonce 707,29 € pour le lot.
const lot = lotCharlot()
lot.lignes[0].total = 0
lot.totaux.total = 707.29
const html = await rendre(lot)
expect(html).toContain('A verifier')
expect(html).toContain('calculé : 0,00 €')
// Le montant du compte rendu reste affiché tel quel.
expect(html).toContain('707,29 €')
})
it('met la colonne en défaut en évidence', async () => {
const lot = lotCharlot()
lot.totaux.regles = 707.29
const html = await rendre(lot)
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
const html = await rendre(lot)
expect(html).toContain('Valeur extraite du compte rendu — cliquer pour la modifier')
expect(html).toContain('Extrait du compte rendu : 707,29 €')
expect(html).toContain('Calculé sur les 1 ligne(s) : 867,45 €')
})
it('signale un lot dont aucune ligne na été extraite', async () => {
const lot = lotCharlot({ lignes: [] })
const html = await rendre(lot)
expect(html).toContain('Aucune ligne extraite')
expect(html).toContain('A verifier')
})
it('montre limpayé du compte rendu dans le bandeau replié', async () => {
const lot = lotCharlot()
lot.totaux.impayes = 67.45
const html = await rendre(lot)
expect(html).toContain('Impayes: 67,45 €')
})
it('distingue un trop-perçu dun impayé', async () => {
const lot = lotCharlot()
lot.totaux.impayes = -32.55
const html = await rendre(lot)
expect(html).toContain('Trop-percu: 32,55 €')
})
})