Fermer les listes de pays et de départements

Nationalité, pays de naissance, département de naissance et pays de l'adresse
étaient des champs libres. « France », « FRANCE », « Française », « FR » et
« france » désignent la même chose et se retrouvaient toutes en base. Le
registre unique du personnel les imprime telles quelles, et un contrôle y lit
cinq pays.

Le code ISO 3166-1 est désormais stocké, jamais le libellé : il ne change pas
d'orthographe, tient dans deux caractères, et c'est lui que reprennent les
déclarations sociales. Le libellé n'est plus qu'un affichage, et le registre
imprime le gentilé résolu depuis le code.

La liste des pays couvre ce qu'un commerce de détail français rencontre —
Europe, pourtour méditerranéen, Afrique francophone, principaux pays d'origine
relevés par l'INSEE. Elle n'est pas universelle et ne le prétend pas : un pays
absent s'ajoute en une ligne, plutôt que de rouvrir la saisie libre pour tout le
monde. La France est en tête du sélecteur, le reste par ordre alphabétique ; un
classement par fréquence supposée aurait été deviné, et se lirait comme un
jugement.

Les départements portent leur code officiel, Corse comprise — 2A et 2B, jamais
20, qui n'existe plus depuis 1976 mais survit dans les saisies manuelles. Les
trois départements du régime local d'Alsace-Moselle sont identifiés à part :
deux jours fériés de plus et un régime de maintien de salaire distinct, ce qui
change la planification d'un établissement à quinze kilomètres d'un autre.

Un dossier saisi avant la liste porte encore un libellé en clair. Le sélecteur
n'y trouve pas d'option, affiche « non renseigné », et la première correction du
dossier écrit le code. La valeur n'est ni perdue en silence ni opposée à qui
vient corriger un champ voisin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5 committed 2026-08-11 15:00:07 +02:00
1 parent fab497acdc
commit ef3dd5c027
5 files changed
+384 -14

No files matched your search

+13 -4
View File
@@ -4,6 +4,8 @@ import { revalidatePath } from 'next/cache';
import { z } from 'zod';
import { AuthorizationError, can } from '@/domain/access/authorize';
import { COUNTRIES } from '@/domain/legal/countries';
import { DEPARTMENT_CODES } from '@/domain/legal/departments';
import { recordAudit } from '@/server/audit';
import { mutate } from '@/server/context';
import { encryptOptional } from '@/server/crypto';
@@ -44,6 +46,8 @@ const optionalEnum = <T extends readonly string[]>(values: T) =>
)
.transform((value) => (value === '' ? null : value));
const COUNTRY_CODES = COUNTRIES.map((country) => country.code);
const profileInput = z.object({
membershipId: z.string().min(1),
gender: optionalEnum(['FEMALE', 'MALE', 'UNSPECIFIED'] as const),
@@ -52,9 +56,14 @@ const profileInput = z.object({
lastName: z.string().trim().min(1, 'Nom requis').max(80),
birthDate: optionalText(10),
birthPlace: optionalText(120),
birthCountry: optionalText(80),
birthDepartment: optionalText(80),
nationality: optionalText(80),
// Codes de liste fermée, pas des libellés. Un dossier saisi avant la liste
// porte encore « France » en clair : le sélecteur ne trouve pas d'option
// correspondante, affiche « non renseigné », et la première correction du
// dossier remplace la valeur par son code. Elle n'est ni perdue en silence
// ni opposée à qui vient corriger un champ voisin.
birthCountry: optionalEnum(COUNTRY_CODES),
birthDepartment: optionalEnum(DEPARTMENT_CODES),
nationality: optionalEnum(COUNTRY_CODES),
maritalStatus: optionalEnum([
'SINGLE',
'MARRIED',
@@ -87,7 +96,7 @@ const profileInput = z.object({
addressLine2: optionalText(180),
postalCode: optionalText(12),
city: optionalText(120),
country: optionalText(80),
country: optionalEnum(COUNTRY_CODES),
emergencyContactName: optionalText(120),
emergencyContactPhone: optionalText(30),
});