Checkpoint: Ajout du module Masse salariale SANTINOVA : tables de salariés, rémunérations et évolutions manuelles sécurisées par rôle administrateur ; import contrôlé des bulletins 2025 et janvier–août 2026 ; écran tableau 2025–2027 ; tests d'autorisations, d'agrégats et des quatre états UI ; documentation des sources et de la politique d'import.
This commit is contained in:
@@ -248,6 +248,47 @@ export const appRouter = router({
|
||||
}),
|
||||
}),
|
||||
|
||||
// Les données de rémunération sont sensibles : aucune procédure de ce
|
||||
// sous-routeur n'est accessible aux profils standard ou lecture seule.
|
||||
masseSalariale: router({
|
||||
get: adminProcedure.query(async () => db.getMasseSalariale()),
|
||||
|
||||
import: adminProcedure
|
||||
.input(z.object({
|
||||
salaries: z.array(z.object({
|
||||
matricule: z.string().trim().min(1).max(32),
|
||||
nom: z.string().trim().min(1).max(150),
|
||||
prenom: z.string().trim().min(1).max(150),
|
||||
poste: z.string().trim().min(1).max(255),
|
||||
dateEmbauche: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date d'embauche invalide"),
|
||||
})).min(1).max(500),
|
||||
remunerations: z.array(z.object({
|
||||
matricule: z.string().trim().min(1).max(32),
|
||||
annee: z.number().int().min(2000).max(2100),
|
||||
salaireAnnuelBrutHorsPrimesCents: z.number().int().min(0).max(100_000_000),
|
||||
salaireAnnuelBrutAvecPrimesCents: z.number().int().min(0).max(100_000_000),
|
||||
salaireMensuelBrutHorsPrimesCents: z.number().int().min(0).max(10_000_000),
|
||||
tauxChargeBps: z.number().int().min(0).max(100_000),
|
||||
periodeReference: z.string().regex(/^\d{4}-\d{2}$/).nullable().optional(),
|
||||
statut: z.enum(["annuel", "cumul_provisoire"]),
|
||||
source: z.string().trim().min(1).max(64),
|
||||
})).max(1_500),
|
||||
}))
|
||||
.mutation(async ({ input }) => db.importMasseSalariale(input.salaries, input.remunerations)),
|
||||
|
||||
setEvolution: adminProcedure
|
||||
.input(z.object({
|
||||
salarieId: z.number().int().positive(),
|
||||
annee: z.number().int().min(2000).max(2100),
|
||||
// ±100 % maximum : protège contre les erreurs de virgule dans la saisie.
|
||||
evolutionSalarialeBps: z.number().int().min(-10_000).max(10_000),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await db.setMasseSalarialeEvolution({ ...input, createdBy: ctx.user.id });
|
||||
return { success: true };
|
||||
}),
|
||||
}),
|
||||
|
||||
inventaire: router({
|
||||
get: protectedProcedure
|
||||
.input(z.object({ annee: z.number() }))
|
||||
|
||||
Reference in New Issue
Block a user