Recueillir à l'embauche ce qu'exige le contrat
Créer un salarié demandait cinq champs. Le contrat, la déclaration et le registre en réclament une vingtaine, et rien ne permettait de les saisir : le sexe, le nom de naissance, le pays et le département de naissance, la situation de famille, les personnes à charge, le téléphone fixe, le complément d'adresse et l'heure d'embauche n'existaient pas en base. La migration les ajoute, toutes facultatives : un dossier incomplet doit pouvoir exister — c'est au registre de signaler ce qui lui manque, pas à la base de refuser l'embauche. Seul l'envoi des plannings par SMS fait exception : c'est un consentement, donc faux par défaut, la charge de la preuve pesant sur l'employeur. Le pays et le département de naissance sont des colonnes à part et non une commune saisie librement : la déclaration sociale les demande séparément, et les rétro-extraire échouerait au premier « Bar-le-Duc (Meuse) ». Le formulaire devient un panneau latéral : une trentaine de champs posés au centre masquaient l'annuaire, et on embauche en regardant qui est déjà là. Le matricule y est proposé à la suite du dernier, attribué dans la transaction pour que deux embauches simultanées ne tombent pas sur le même rang. Le nom de naissance vaut le nom de famille quand il n'en diffère pas, plutôt que d'imposer une ressaisie à l'immense majorité des dossiers. Le responsable hiérarchique et la politique RTT se posent enfin à l'embauche : les deux modèles existaient sans qu'aucun écran ne les alimente. Le registre unique du personnel gagne sa colonne « sexe » et se tient désormais au nom de naissance — le nom d'usage peut changer sans que la personne change. Migration écrite et validée contre le schéma, non appliquée : aucune base n'était ouverte ici. À passer avec `pnpm db:migrate`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e27d61dd4c
commit
06198ddbce
20 files changed
+791
-155
No files matched your search
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Libellés d'état civil.
|
||||
*
|
||||
* Les codes vivent en base, les mots à l'écran. Les séparer évite qu'un
|
||||
* changement de formulation devienne une migration, et qu'une valeur stockée
|
||||
* dépende de la langue de l'interface.
|
||||
*/
|
||||
|
||||
export const GENDERS = [
|
||||
['FEMALE', 'Femme'],
|
||||
['MALE', 'Homme'],
|
||||
['UNSPECIFIED', 'Non précisé'],
|
||||
] as const;
|
||||
|
||||
export const MARITAL_STATUSES = [
|
||||
['SINGLE', 'Célibataire'],
|
||||
['MARRIED', 'Marié(e)'],
|
||||
['PACS', 'Pacsé(e)'],
|
||||
['COHABITING', 'Concubinage'],
|
||||
['DIVORCED', 'Divorcé(e)'],
|
||||
['WIDOWED', 'Veuf(ve)'],
|
||||
] as const;
|
||||
|
||||
export type GenderCode = (typeof GENDERS)[number][0];
|
||||
export type MaritalStatusCode = (typeof MARITAL_STATUSES)[number][0];
|
||||
|
||||
const GENDER_LABELS = new Map<string, string>(GENDERS);
|
||||
const MARITAL_LABELS = new Map<string, string>(MARITAL_STATUSES);
|
||||
|
||||
export function genderLabel(code: string | null): string | null {
|
||||
return code ? (GENDER_LABELS.get(code) ?? code) : null;
|
||||
}
|
||||
|
||||
export function maritalStatusLabel(code: string | null): string | null {
|
||||
return code ? (MARITAL_LABELS.get(code) ?? code) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abrégé porté au registre du personnel.
|
||||
*
|
||||
* La colonne y est étroite et la mention se lit d'un coup d'œil ; « Non
|
||||
* précisé » y devient un tiret, qui dit la même chose sans déborder.
|
||||
*/
|
||||
export function genderShort(code: string | null): string | null {
|
||||
if (code === 'FEMALE') return 'F';
|
||||
if (code === 'MALE') return 'M';
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matricule proposé à l'embauche.
|
||||
*
|
||||
* Une lettre et un rang, et non un horodatage : un matricule se dicte au
|
||||
* téléphone et se recopie à la main. Le rang suit le dernier attribué de la
|
||||
* même forme, ce qui laisse coexister les matricules repris d'un autre outil.
|
||||
*/
|
||||
export function nextEmployeeNumber(
|
||||
existing: string[],
|
||||
prefix = 'E',
|
||||
): string {
|
||||
const pattern = new RegExp(`^${prefix}(\\d+)$`);
|
||||
const highest = existing.reduce((max, value) => {
|
||||
const match = pattern.exec(value.trim());
|
||||
if (!match) return max;
|
||||
return Math.max(max, Number(match[1]));
|
||||
}, 0);
|
||||
|
||||
return `${prefix}${String(highest + 1).padStart(4, '0')}`;
|
||||
}
|
||||
Reference in new issue
Block a user