Fermer les listes du dossier, et déclarer les titres de séjour
CI / verify (pull_request) Canceled after 0s

Quatre champs se saisissaient en texte libre là où la valeur appartient à
une liste : nationalité, pays de naissance, pays de l'adresse, département.
« Frnace » y entrait aussi bien que « France », et le registre imprimait la
faute.

Les pays sont stockés par leur code ISO 3166-1 et nommés à l'affichage par
`Intl.DisplayNames` : un nom dépend de la langue et des versions d'ICU, un
code ne bouge pas, et deux cent cinquante traductions n'ont pas à être
entretenues à la main. Les valeurs héritées — « France » en toutes lettres
— sont reconnues à la lecture puis normalisées au premier enregistrement,
plutôt que blanchies par un menu qui ne les retrouve pas.

Les départements portent la Corse et l'outre-mer : « 2A » n'est pas un
nombre, et le département de naissance sert à la déclaration sociale, qui
attend le code.

Les téléphones gagnent leur indicatif, choisi à part et recollé en un seul
champ. L'enregistrer séparément obligerait chaque lecture — annuaire,
export, SMS — à le recomposer, et un numéro à moitié lu ne joint personne.
La découpe prend l'indicatif le plus long : « +352 » commence par « +3 ».

Le titre de séjour se déclare enfin. `WorkPermit` existait depuis le début
sans qu'aucun écran ne l'alimente. L'échéance est exigée dès qu'un titre est
posé — employer quelqu'un dont l'autorisation a expiré est un délit, et sans
date rien ne peut le signaler à temps. Décocher retire le titre plutôt que
de le laisser alerter pour quelqu'un qui n'en relève plus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5 committed 2026-08-10 17:48:51 +02:00
1 parent b608aa87cf
commit 481af181fa
9 files changed
+766 -24

No files matched your search

+34 -3
View File
@@ -69,9 +69,13 @@ test('le dossier personnel se saisit et se conserve', async ({ page }) => {
.click();
await page.getByLabel('Date de naissance').fill('1988-03-12');
await page.getByLabel('Lieu de naissance').fill('Nancy');
await page.getByLabel('Nationalité').fill('France');
await page.getByLabel('Téléphone mobile').fill('+33 6 12 34 56 78');
await page.getByLabel('Commune de naissance').fill('Nancy');
// Pays et département viennent de listes fermées : la saisie libre ne peut
// plus y introduire « Frnace ».
await page.getByLabel('Nationalité').selectOption('FR');
await page.getByLabel('Département de naissance').selectOption('54');
// L'indicatif est un contrôle à part ; le champ ne porte que le reste.
await page.getByLabel('Téléphone mobile', { exact: true }).fill('6 12 34 56 78');
await page.getByLabel('Ville').fill('Frouard');
await page.getByRole('button', { name: 'Enregistrer les modifications' }).click();
@@ -82,6 +86,33 @@ test('le dossier personnel se saisit et se conserve', async ({ page }) => {
await page.goto(employee.url);
await expect(page.getByText('Nancy')).toBeVisible();
await expect(page.getByText('Frouard')).toBeVisible();
await expect(page.getByText('54 - Meurthe-et-Moselle')).toBeVisible();
await expect(page.getByText('+33 6 12 34 56 78')).toBeVisible();
});
test('un titre de séjour expiré est signalé sur la fiche', async ({ page }) => {
await createEmployee(page);
await page
.locator('form')
.filter({ hasText: 'Travailleur étranger' })
.or(page.getByText('Autorisation de travail'))
.first()
.scrollIntoViewIfNeeded();
const card = page
.locator('section')
.filter({ hasText: 'Autorisation de travail' });
await card.getByRole('button', { name: 'Modifier' }).click();
await card.getByLabel(/Travailleur étranger/).check();
// Sans échéance, rien ne peut alerter : le refus est le comportement voulu.
await card.getByLabel('Nature du titre').fill('Carte de séjour');
await card.getByLabel('Référence').fill('AGDREF-123456');
await card.getByLabel('Expire le').fill('2020-01-01');
await card.getByRole('button', { name: 'Enregistrer' }).click();
await expect(page.getByText(/Ce titre a expiré/)).toBeVisible();
});
test('une date de naissance à venir est refusée', async ({ page }) => {
+51
View File
@@ -136,3 +136,54 @@ describe('matricule proposé', () => {
expect(nextEmployeeNumber(['SILAE-42', '00012', 'E0002'])).toBe('E0003');
});
});
describe('référentiels géographiques', () => {
it('nomme les pays en français depuis leur code', async () => {
const { countryLabel } = await import('@/domain/hr/geo');
expect(countryLabel('FR')).toBe('France');
expect(countryLabel('DE')).toBe('Allemagne');
});
it('laisse passer une valeur héritée plutôt que de la blanchir', async () => {
const { countryLabel } = await import('@/domain/hr/geo');
// Les dossiers saisis avant le référentiel portent le nom en clair.
expect(countryLabel('Nouvelle-Calédonie')).toBe('Nouvelle-Calédonie');
});
it('ramène un nom hérité à son code', async () => {
const { toCountryCode } = await import('@/domain/hr/geo');
expect(toCountryCode('France')).toBe('FR');
expect(toCountryCode('FR')).toBe('FR');
expect(toCountryCode('Pays imaginaire')).toBe('');
expect(toCountryCode(null)).toBe('');
});
it('porte la Corse et l’outre-mer', async () => {
const { departmentLabel, isKnownDepartment } = await import('@/domain/hr/geo');
expect(departmentLabel('2A')).toBe('2A - Corse-du-Sud');
expect(departmentLabel('974')).toBe('974 - La Réunion');
expect(isKnownDepartment('20')).toBe(false);
});
it('coupe un numéro sur l’indicatif le plus long', async () => {
const { splitDial } = await import('@/domain/hr/geo');
// « +352 » commence par « +3 » : tester dans l'ordre de la table
// donnerait la France pour un numéro luxembourgeois.
expect(splitDial('+352 621 12 34 56')).toEqual({
dial: '+352',
rest: '621 12 34 56',
});
expect(splitDial('+33 6 12 34 56 78')).toEqual({
dial: '+33',
rest: '6 12 34 56 78',
});
});
it('retombe sur la France pour un numéro sans indicatif', async () => {
const { splitDial } = await import('@/domain/hr/geo');
expect(splitDial('06 12 34 56 78')).toEqual({
dial: '+33',
rest: '06 12 34 56 78',
});
});
});