Éditer le registre unique du personnel
Le bouton menait au registre de paramétrage juridique — un autre document, qui ne répond à aucune demande d'inspection. Le registre unique du personnel n'existait pas. Il se tient par établissement : celui-ci est demandé avant l'édition plutôt que déduit, car en tirer un au hasard pour une entreprise qui compte trente sites ne répondrait à rien. Une ligne par contrat et non par personne : un salarié réembauché a deux entrées et deux sorties, et les fondre effacerait l'interruption. Les partis y figurent aussi — c'est l'historique que l'inspection vient chercher. Les mentions manquantes sont annoncées avant le téléchargement, et comptées par salarié parce que la contravention l'est aussi. Découvrir un registre incomplet en l'ouvrant, c'est le découvrir devant l'inspection. L'édition est journalisée : le document rassemble l'identité, la nationalité et le parcours de tout le personnel d'un site. Une réserve, portée par le code et par le document : PlanFlow ne collecte pas le sexe, mention pourtant exigée. La colonne reste vide et alimente le décompte des dossiers incomplets, en attendant le champ. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8808fcdb95
commit
3bf2cbabff
12 files changed
+827
-6
No files matched your search
@@ -0,0 +1,119 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
RUP_COLUMNS,
|
||||
missingMentions,
|
||||
peopleWithGaps,
|
||||
type RupPerson,
|
||||
} from '@/domain/legal/rup';
|
||||
|
||||
/**
|
||||
* Registre unique du personnel — mentions obligatoires.
|
||||
*
|
||||
* La sanction se compte **par salarié concerné**, pas par registre : c'est ce
|
||||
* que le décompte doit refléter, et c'est ce qui décide de l'avertissement
|
||||
* affiché avant l'édition.
|
||||
*/
|
||||
|
||||
const complete: RupPerson = {
|
||||
lastName: 'Ferrand',
|
||||
firstName: 'Camille',
|
||||
sex: 'F',
|
||||
nationality: 'France',
|
||||
birthDate: new Date('1990-04-12T00:00:00Z'),
|
||||
jobTitle: 'Hôte de caisse',
|
||||
qualification: 'Niveau 3',
|
||||
contractLabel: 'CDI',
|
||||
entryDate: new Date('2024-09-01T00:00:00Z'),
|
||||
exitDate: null,
|
||||
};
|
||||
|
||||
describe('registre unique du personnel', () => {
|
||||
it('énumère les mentions fixées par le texte', () => {
|
||||
expect(RUP_COLUMNS.map((column) => column.key)).toEqual([
|
||||
'lastName',
|
||||
'firstName',
|
||||
'sex',
|
||||
'nationality',
|
||||
'birthDate',
|
||||
'jobTitle',
|
||||
'qualification',
|
||||
'contractLabel',
|
||||
'entryDate',
|
||||
'exitDate',
|
||||
]);
|
||||
});
|
||||
|
||||
it('répartit la largeur sur la totalité de la page', () => {
|
||||
const total = RUP_COLUMNS.reduce((sum, column) => sum + column.width, 0);
|
||||
expect(total).toBeCloseTo(1, 5);
|
||||
});
|
||||
|
||||
it('ne réclame rien quand toutes les mentions sont là', () => {
|
||||
expect(missingMentions(complete)).toEqual([]);
|
||||
});
|
||||
|
||||
it('tient une date de sortie absente pour une présence, pas pour un oubli', () => {
|
||||
// Un salarié en poste n'a pas de date de sortie : la réclamer ferait
|
||||
// apparaître tout l'effectif courant comme incomplet.
|
||||
expect(missingMentions({ ...complete, exitDate: null })).toEqual([]);
|
||||
});
|
||||
|
||||
it('nomme les mentions manquantes', () => {
|
||||
expect(
|
||||
missingMentions({ ...complete, nationality: null, qualification: '' }),
|
||||
).toEqual(['Nationalité', 'Qualification']);
|
||||
});
|
||||
|
||||
it('compte les personnes concernées, pas les champs vides', () => {
|
||||
const bancal: RupPerson = {
|
||||
...complete,
|
||||
sex: null,
|
||||
nationality: null,
|
||||
birthDate: null,
|
||||
};
|
||||
// Trois manques sur une seule personne : une contravention, pas trois.
|
||||
expect(peopleWithGaps([complete, bancal])).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendu du registre', () => {
|
||||
it('produit un PDF paginé qui porte les manques en pied', async () => {
|
||||
const { renderRegisterPdf } = await import('@/server/employees/rup-pdf');
|
||||
|
||||
// Assez de lignes pour dépasser une page : c'est la rupture que le rendu
|
||||
// rate le plus volontiers.
|
||||
const people: RupPerson[] = Array.from({ length: 60 }, (_, index) => ({
|
||||
...complete,
|
||||
lastName: `Nom manifestement trop long pour la colonne ${index}`,
|
||||
sex: index % 2 === 0 ? null : 'F',
|
||||
}));
|
||||
|
||||
const pdf = await renderRegisterPdf({
|
||||
locationId: 'loc',
|
||||
locationName: 'La Foir’Fouille',
|
||||
siret: '12345678901234',
|
||||
people,
|
||||
incomplete: 30,
|
||||
});
|
||||
|
||||
expect(Buffer.from(pdf).subarray(0, 5).toString()).toBe('%PDF-');
|
||||
expect(pdf.length).toBeGreaterThan(1000);
|
||||
});
|
||||
|
||||
it('édite un registre vide plutôt que de refuser', async () => {
|
||||
const { renderRegisterPdf } = await import('@/server/employees/rup-pdf');
|
||||
|
||||
// Un établissement qui vient d'ouvrir n'a personne : le registre existe
|
||||
// quand même, et son absence serait plus embarrassante que sa vacuité.
|
||||
const pdf = await renderRegisterPdf({
|
||||
locationId: 'loc',
|
||||
locationName: 'Nouvel établissement',
|
||||
siret: null,
|
||||
people: [],
|
||||
incomplete: 0,
|
||||
});
|
||||
|
||||
expect(Buffer.from(pdf).subarray(0, 5).toString()).toBe('%PDF-');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user