Checkpoint: Fenêtre administrateur Salaires finalisée : import multipart limité, chiffrement AES-256-GCM avant stockage persistant hors conteneur, métadonnées/index métier minimisés en base, consultation PDF déchiffrée côté serveur avec no-store, filtres année/mois et comparaison M-1 expliquée. Réindexation sûre des archives partielles sans écrasement du PDF. Validation réelle sur deux liasses mensuelles : index partiel signalé sans invention de données, comparaison affichée et lecture après redémarrage. 55 tests Vitest, TypeScript et build de production validés.
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 1m54s
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 1m54s
This commit is contained in:
@@ -265,3 +265,64 @@ export const masseSalarialeEvolutions = mysqlTable("masse_salariale_evolutions",
|
||||
|
||||
export type MasseSalarialeEvolution = typeof masseSalarialeEvolutions.$inferSelect;
|
||||
export type InsertMasseSalarialeEvolution = typeof masseSalarialeEvolutions.$inferInsert;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// SALAIRES MENSUELS — Liasses PDF chiffrées et données extraites
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Une liasse représente un dépôt mensuel. Le fichier est chiffré avant envoi
|
||||
* vers le stockage persistant ; la base ne conserve ni octet de PDF ni clé.
|
||||
*/
|
||||
export const salairesLiasses = mysqlTable("salaires_liasses", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull(),
|
||||
mois: int("mois").notNull(),
|
||||
nomFichier: varchar("nomFichier", { length: 255 }).notNull(),
|
||||
stockageKey: varchar("stockageKey", { length: 512 }).notNull().unique(),
|
||||
empreinteSha256: varchar("empreinteSha256", { length: 64 }).notNull().unique(),
|
||||
tailleOctets: int("tailleOctets").notNull(),
|
||||
/** Paramètres AES-256-GCM nécessaires au déchiffrement côté serveur. */
|
||||
ivBase64: varchar("ivBase64", { length: 32 }).notNull(),
|
||||
authTagBase64: varchar("authTagBase64", { length: 32 }).notNull(),
|
||||
versionCle: int("versionCle").notNull().default(1),
|
||||
/** `ready`, `partiel` ou `a_controler` : une liasse est conservée même si l'extraction est incomplète. */
|
||||
statutExtraction: varchar("statutExtraction", { length: 32 }).notNull().default("ready"),
|
||||
erreurExtraction: text("erreurExtraction"),
|
||||
importePar: int("importePar").notNull(),
|
||||
importeLe: timestamp("importeLe").defaultNow().notNull(),
|
||||
}, (t) => ({
|
||||
/** Une liasse unique par mois : toutes les fiches mensuelles restent ainsi cohérentes. */
|
||||
uniqPeriode: uniqueIndex("salaires_liasses_periode_idx").on(t.annee, t.mois),
|
||||
/** Empêche de traiter deux fois le même fichier sous une période distincte. */
|
||||
uniqPeriodeEmpreinte: uniqueIndex("salaires_liasses_periode_empreinte_idx").on(t.annee, t.mois, t.empreinteSha256),
|
||||
}));
|
||||
|
||||
export type SalaireLiasse = typeof salairesLiasses.$inferSelect;
|
||||
export type InsertSalaireLiasse = typeof salairesLiasses.$inferInsert;
|
||||
|
||||
/**
|
||||
* Données utiles par bulletin. `explicationEcart` conserve uniquement les
|
||||
* rubriques de rémunération nécessaires à expliquer une variation mensuelle.
|
||||
*/
|
||||
export const salairesBulletins = mysqlTable("salaires_bulletins", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
liasseId: int("liasseId").notNull(),
|
||||
matricule: varchar("matricule", { length: 32 }).notNull(),
|
||||
nom: varchar("nom", { length: 150 }).notNull(),
|
||||
prenom: varchar("prenom", { length: 150 }).notNull(),
|
||||
poste: varchar("poste", { length: 255 }).notNull(),
|
||||
brutMensuelCents: int("brutMensuelCents").notNull(),
|
||||
brutAvecPrimesCents: int("brutAvecPrimesCents").notNull(),
|
||||
primeAstreinteCents: int("primeAstreinteCents").notNull().default(0),
|
||||
explicationEcart: text("explicationEcart"),
|
||||
numeroPage: int("numeroPage"),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
}, (t) => ({
|
||||
/** Un seul bulletin indexé par matricule dans une liasse donnée. */
|
||||
uniqLiasseMatricule: uniqueIndex("salaires_bulletins_liasse_matricule_idx").on(t.liasseId, t.matricule),
|
||||
}));
|
||||
|
||||
export type SalaireBulletin = typeof salairesBulletins.$inferSelect;
|
||||
export type InsertSalaireBulletin = typeof salairesBulletins.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user