Skip to content

BDD-146 - Colonnes numériques mal typées à l'export CSV et vides à l'export Excel

1. User Story

En tant qu'utilisateur exportant les données d'une source,
Je veux que les colonnes numériques soient correctement typées dans les fichiers exportés,
Afin de pouvoir utiliser directement les valeurs sans retraitement manuel.

2. Critères d'acceptance

  1. La prévisualisation d'une source affiche les valeurs numériques réelles (pas le type number).
  2. L'export CSV sérialise les nombres en format numérique (virgule décimale, reconnue par Excel fr-FR).
  3. L'export Excel place les valeurs dans des cellules de type nombre (non vides).
  4. Le correctif est validé sur plusieurs types numériques : entier, décimal, négatif.

3. Analyse des bugs

Bug 1 — Libellé "number" en anglais dans la page de consultation

Fichier : front/src/pages/admin/sources/AdminSourceDetailPage.vue

La fonction getColumnTypeLabel() retournait 'number' (anglais) pour les types PostgreSQL numériques (numeric, integer, bigint, double precision, real). Ce libellé s'affichait dans l'en-tête de chaque colonne numérique.

Bug 2 — Export CSV : nombres traités comme texte dans Excel fr-FR

Fichier : back/src/api/sources/sources.service.ts — méthode serializeCsvValue()

La méthode retournait un number JavaScript (ex. 1234.56). PapaParse l'écrivait avec le séparateur décimal anglais (point). Excel en locale française utilise la virgule comme séparateur décimal — il traitait donc 1234.56 comme texte.

Bug 3 — Export Excel : cellules numériques vides

Fichier : back/src/api/sources/services/source-data-format.util.tsformatSourceTableCellValue()

Pour une colonne PostgreSQL NUMERIC, Prisma retourne un objet Decimal (decimal.js). La fonction ne traitait pas ce cas et retournait l'objet tel quel. SheetJS (XLSX.utils.json_to_sheet) ne sait pas sérialiser un objet Decimal → cellule vide.

4. Périmètre technique

Backend

FichierModification
back/src/api/sources/services/source-data-format.util.tsformatSourceTableCellValue : gestion des objets Decimal Prisma → number JS
back/src/api/sources/sources.service.tsserializeCsvValue : format décimal français (virgule) pour les number

Frontend

FichierModification
front/src/pages/admin/sources/AdminSourceDetailPage.vuegetColumnTypeLabel : 'number''nombre'

5. Détails d'implémentation

Correction formatSourceTableCellValue (Excel)

Ajout d'un bloc avant le return value final pour convertir les objets Decimal Prisma en number JavaScript :

typescript
if (typeof value === 'object') {
  const primitive = (value as { valueOf?: () => unknown }).valueOf?.();
  if (typeof primitive === 'number' && Number.isFinite(primitive)) return primitive;
  if (typeof primitive === 'string') {
    const n = Number(primitive.replace(',', '.'));
    if (Number.isFinite(n)) return n;
  }
}

SheetJS reçoit un vrai number → cellule de type nombre dans Excel.

Correction serializeCsvValue (CSV fr-FR)

Remplacement de la branche number :

typescript
if (typeof value === 'number') {
  if (!Number.isFinite(value)) return '';
  if (Number.isInteger(value)) return String(value);
  return String(value).replace('.', ','); // virgule décimale fr-FR
}

PapaParse écrit 1234,56. Excel fr-FR reconnaît la virgule décimale → cellule nombre.

Correction getColumnTypeLabel (affichage)

javascript
if (normalizedType === 'numeric') return 'nombre'
if (normalizedType === 'integer') return 'nombre'
// ...

6. Tests associés

Tests unitaires back

  • back/src/api/sources/services/source-data-format.util.spec.ts

    • Conversion objet Decimal décimal → number JS
    • Conversion objet Decimal entier → number JS
    • Conversion objet Decimal négatif → number JS
    • Conversion avec virgule décimale (1234,56) → number JS
    • Conversion quand valueOf() retourne directement un number
  • back/src/api/sources/sources.service.spec.ts

    • Export CSV : colonnes décimales sérialisées avec virgule (1234,56)
    • Export XLSX : cellules numériques de type 'n' (SheetJS) avec valeur correcte

Tests unitaires front

  • front/src/__tests__/pages/admin/sources/AdminSourceDetailPage.spec.js
    • getColumnTypeLabel : numeric, integer, bigint, double precision, real'nombre'
    • Aucun libellé 'number' (anglais) affiché

7. Liens