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:
Manus
2026-08-27 13:10:47 +00:00
parent 2f4b1f2e8e
commit da407db4de
21 changed files with 4390 additions and 4 deletions

View File

@@ -201,3 +201,67 @@ export const opexBasesRepartition = mysqlTable("opex_bases_repartition", {
export type OpexBaseRepartition = typeof opexBasesRepartition.$inferSelect;
export type InsertOpexBaseRepartition = typeof opexBasesRepartition.$inferInsert;
// ─────────────────────────────────────────────────────────────────────────────
// MASSE SALARIALE SANTINOVA — Données sensibles, accès administrateur uniquement
// ─────────────────────────────────────────────────────────────────────────────
export const masseSalarialeSalaries = mysqlTable("masse_salariale_salaries", {
id: int("id").autoincrement().primaryKey(),
/** Identifiant paie stable, distinct des données personnelles superflues. */
matricule: varchar("matricule", { length: 32 }).notNull().unique(),
nom: varchar("nom", { length: 150 }).notNull(),
prenom: varchar("prenom", { length: 150 }).notNull(),
poste: varchar("poste", { length: 255 }).notNull(),
/** Date civile ISO (YYYY-MM-DD), sans fuseau horaire. */
dateEmbauche: varchar("dateEmbauche", { length: 10 }).notNull(),
actif: boolean("actif").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type MasseSalarialeSalarie = typeof masseSalarialeSalaries.$inferSelect;
export type InsertMasseSalarialeSalarie = typeof masseSalarialeSalaries.$inferInsert;
/**
* Une ligne par salarié et exercice. Tous les montants sont en centimes et les
* taux en points de base (ex. 42,75 % = 4 275) pour éviter les erreurs d'arrondi.
*/
export const masseSalarialeRemunerations = mysqlTable("masse_salariale_remunerations", {
id: int("id").autoincrement().primaryKey(),
salarieId: int("salarieId").notNull(),
annee: int("annee").notNull(),
salaireAnnuelBrutHorsPrimesCents: int("salaireAnnuelBrutHorsPrimesCents").notNull(),
salaireAnnuelBrutAvecPrimesCents: int("salaireAnnuelBrutAvecPrimesCents").notNull(),
salaireMensuelBrutHorsPrimesCents: int("salaireMensuelBrutHorsPrimesCents").notNull(),
tauxChargeBps: int("tauxChargeBps").notNull(),
/** Mois de paie ayant servi de référence, ex. 2025-12 ou 2026-08. */
periodeReference: varchar("periodeReference", { length: 7 }),
/** `annuel` pour un exercice clos ; `cumul_provisoire` pour une année en cours. */
statut: varchar("statut", { length: 32 }).notNull().default("annuel"),
source: varchar("source", { length: 64 }).notNull().default("bulletin"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
}, (t) => ({
/** Rend les imports idempotents pour un salarié et un exercice. */
uniqSalarieAnnee: uniqueIndex("masse_salariale_salarie_annee_idx").on(t.salarieId, t.annee),
}));
export type MasseSalarialeRemuneration = typeof masseSalarialeRemunerations.$inferSelect;
export type InsertMasseSalarialeRemuneration = typeof masseSalarialeRemunerations.$inferInsert;
/** Évolution salariale manuelle, stockée même en l'absence de bulletin pour l'année concernée. */
export const masseSalarialeEvolutions = mysqlTable("masse_salariale_evolutions", {
id: int("id").autoincrement().primaryKey(),
salarieId: int("salarieId").notNull(),
annee: int("annee").notNull(),
/** Pourcentage exprimé en points de base, ex. 1,50 % = 150. */
evolutionSalarialeBps: int("evolutionSalarialeBps").notNull(),
createdBy: int("createdBy").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
}, (t) => ({
uniqSalarieAnnee: uniqueIndex("masse_salariale_evolution_salarie_annee_idx").on(t.salarieId, t.annee),
}));
export type MasseSalarialeEvolution = typeof masseSalarialeEvolutions.$inferSelect;
export type InsertMasseSalarialeEvolution = typeof masseSalarialeEvolutions.$inferInsert;