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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user