[ERP-62] Page Répertoire clients (datatable + Ajouter / Exporter) #44

Merged
tristan merged 8 commits from feature/ERP-62-page-repertoire-clients into develop 2026-06-02 14:16:30 +00:00
4 changed files with 40 additions and 16 deletions
Showing only changes of commit 93aa22594d - Show all commits
+2 -1
View File
@@ -86,7 +86,8 @@
"updateSuccess": "Client mis à jour avec succès",
"archiveSuccess": "Client archivé avec succès",
"restoreSuccess": "Client restauré avec succès",
"error": "Une erreur est survenue. Réessayez."
"error": "Une erreur est survenue. Réessayez.",
"exportError": "L'export du répertoire clients a échoué. Réessayez."
},
"validation": {
"informationRequiredForCommercial": "Les informations de l'entreprise sont obligatoires pour le rôle Commerciale.",
@@ -228,7 +228,13 @@ function formatLastActivity(item: Record<string, unknown>): string {
return ''
}
return new Date(value).toLocaleDateString('fr-FR')
// Garde-fou date invalide : un updatedAt mal forme donnerait « Invalid Date ».
Outdated
Review

[Robustesse] formatLastActivity ne se protège pas d'une date invalide.

Context : rendu de la colonne « Dernière activité » depuis updatedAt.

Cause : si updatedAt arrive mal formé, new Date(value) produit Invalid Date et la cellule affiche littéralement Invalid Date — le if (!value) ne couvre que null/vide.

Reco : garder le résultat derrière Number.isNaN(date.getTime()) → retour '', cohérent avec la cellule vide déjà prévue.

**[Robustesse] `formatLastActivity` ne se protège pas d'une date invalide.** **Context** : rendu de la colonne « Dernière activité » depuis `updatedAt`. **Cause** : si `updatedAt` arrive mal formé, `new Date(value)` produit `Invalid Date` et la cellule affiche littéralement `Invalid Date` — le `if (!value)` ne couvre que null/vide. **Reco** : garder le résultat derrière `Number.isNaN(date.getTime())` → retour `''`, cohérent avec la cellule vide déjà prévue.
const date = new Date(value)
if (Number.isNaN(date.getTime())) {
return ''
}
return date.toLocaleDateString('fr-FR')
}
/** Clic sur une ligne → ecran Consultation (route a plat /clients/{id}). */
@@ -383,7 +389,7 @@ async function exportXlsx(): Promise<void> {
catch {
toast.error({
title: t('commercial.clients.toast.error'),
message: t('commercial.clients.toast.error'),
message: t('commercial.clients.toast.exportError'),
})
}
finally {
+5 -5
View File
@@ -2,11 +2,11 @@
* Formatage d'un numero de telephone francais en groupes de 2 chiffres
* (`XX XX XX XX XX`).
*
* Signature cible partagee avec le ticket 1.13 / ERP-66 : si ce dernier livre
* une version plus riche (validation, indicatif international), elle remplacera
* cette implementation minimale. En attendant, on couvre le besoin du Repertoire
* clients (ERP-62) : afficher un telephone lisible a partir de la valeur stockee
* en base (deja normalisee en 10 chiffres par le ClientProcessor, RG-1.20).
* Helper PARTAGE volontaire : les telephones sont presents un peu partout dans
* l'app (fiches clients, contacts, fournisseurs, prestataires...). Introduit ici
* comme util transverse stable plutot que duplique a chaque ecran. La signature
* `formatPhoneFR(value): string` est coordonnee avec ERP-66, qui pourra enrichir
* l'implementation (validation, indicatif international) sans casser les appelants.
*
* - Ne garde que les chiffres puis groupe par 2 (tolere une saisie deja espacee
* ou pointee, ex: `06.12.34.56.78` ou `0612345678`).
1
2
@@ -144,30 +144,47 @@ class DoctrineClientRepository extends ServiceEntityRepository implements Client
/**
* Nettoie une liste de chaines : trim, retrait des vides, reindexation.
* Defensive : tolere des elements scalaires non-string (cast) et ignore le
* reste sans lever de TypeError, le contrat etant justement de normaliser une
* entree potentiellement brute (query params).
*
* @param list<string> $values
* @param array<mixed> $values
*
* @return list<string>
*/
private function normalizeStringList(array $values): array
{
$cleaned = array_filter(
array_map(static fn (string $v): string => trim($v), $values),
static fn (string $v): bool => '' !== $v,
);
$out = [];
foreach ($values as $value) {
if (is_string($value) || is_int($value) || is_float($value)) {
$trimmed = trim((string) $value);
if ('' !== $trimmed) {
$out[] = $trimmed;
}
}
}
return array_values($cleaned);
return $out;
}
Review

[Robustesse] normalizeIntList peut lever un TypeError strict.

Context : la closure type son paramètre int, mais la signature publique de l'interface est array $siteIds (éléments non typés).

Cause : aujourd'hui sauvé car ClientProvider::readIntList caste en amont. Mais un appelant direct (test futur, autre module) passant ['1','2'] (strings) déclencherait un TypeError en strict_types — fragile pour une méthode censée justement normaliser.

Reco : caster dans la closure ((int) $v > 0 après is_numeric) plutôt que de typer le paramètre, pour une normalisation réellement défensive.

**[Robustesse] `normalizeIntList` peut lever un `TypeError` strict.** **Context** : la closure type son paramètre `int`, mais la signature publique de l'interface est `array $siteIds` (éléments non typés). **Cause** : aujourd'hui sauvé car `ClientProvider::readIntList` caste en amont. Mais un appelant direct (test futur, autre module) passant `['1','2']` (strings) déclencherait un `TypeError` en `strict_types` — fragile pour une méthode censée justement normaliser. **Reco** : caster dans la closure (`(int) $v > 0` après `is_numeric`) plutôt que de typer le paramètre, pour une normalisation réellement défensive.
/**
* Nettoie une liste d'identifiants : cast int, retrait des <= 0, reindexation.
* Defensive (cf. normalizeStringList) : accepte des entiers ou des chaines
* numeriques ('1', '2') sans TypeError, ignore le reste.
*
* @param list<int> $values
* @param array<mixed> $values
*
* @return list<int>
*/
private function normalizeIntList(array $values): array
{
return array_values(array_filter($values, static fn (int $v): bool => $v > 0));
$out = [];
foreach ($values as $value) {
if (is_numeric($value) && (int) $value > 0) {
$out[] = (int) $value;
}
}
return $out;
}
}