Checkpoint: Migration complète vers authentification locale (JWT + bcrypt) : schéma DB (10 tables), routes tRPC (auth, users, etablissements, inventaire, capex, opex, parametres), page de login avec branding Itinova/Santinova, protection des routes, onglets Établissements et Utilisateurs branchés sur tRPC, import inventaire/établissements/utilisateurs via tRPC, 14 tests Vitest passants.
This commit is contained in:
162
drizzle/schema.ts
Normal file
162
drizzle/schema.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import {
|
||||
boolean,
|
||||
decimal,
|
||||
int,
|
||||
mysqlEnum,
|
||||
mysqlTable,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
} from "drizzle-orm/mysql-core";
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// USERS — Auth locale (email/password), 3 profils : admin | standard | readonly
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const users = mysqlTable("users", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
/** Identifiant de connexion (email ou login court comme adminItinova) */
|
||||
login: varchar("login", { length: 255 }).notNull().unique(),
|
||||
email: varchar("email", { length: 320 }),
|
||||
passwordHash: varchar("passwordHash", { length: 255 }).notNull(),
|
||||
firstName: varchar("firstName", { length: 100 }),
|
||||
lastName: varchar("lastName", { length: 100 }),
|
||||
role: mysqlEnum("role", ["admin", "standard", "readonly"]).default("standard").notNull(),
|
||||
isActive: boolean("isActive").default(true).notNull(),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
lastSignedIn: timestamp("lastSignedIn"),
|
||||
});
|
||||
|
||||
export type User = typeof users.$inferSelect;
|
||||
export type InsertUser = typeof users.$inferInsert;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// ÉTABLISSEMENTS
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const etablissements = mysqlTable("etablissements", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
code: varchar("code", { length: 50 }).notNull().unique(),
|
||||
nom: varchar("nom", { length: 255 }).notNull(),
|
||||
groupe: varchar("groupe", { length: 100 }),
|
||||
ville: varchar("ville", { length: 100 }),
|
||||
actif: boolean("actif").default(true).notNull(),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
export type Etablissement = typeof etablissements.$inferSelect;
|
||||
export type InsertEtablissement = typeof etablissements.$inferInsert;
|
||||
|
||||
// Rattachement utilisateur ↔ établissements (pour profil standard/readonly)
|
||||
export const userEtablissements = mysqlTable("user_etablissements", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
userId: int("userId").notNull(),
|
||||
etablissementCode: varchar("etablissementCode", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// PARAMÈTRES APPLICATION
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const parametresApp = mysqlTable("parametres_app", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
cle: varchar("cle", { length: 100 }).notNull().unique(),
|
||||
valeur: text("valeur"),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// OPEX — Postes budgétaires
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const opexPostes = mysqlTable("opex_postes", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull(),
|
||||
colIdx: int("colIdx").notNull(),
|
||||
libelle: varchar("libelle", { length: 255 }).notNull(),
|
||||
libelleCourt: varchar("libelleCourt", { length: 100 }),
|
||||
libelleDetail: varchar("libelleDetail", { length: 255 }),
|
||||
fournisseur: varchar("fournisseur", { length: 255 }),
|
||||
categorie: varchar("categorie", { length: 100 }),
|
||||
type: varchar("type", { length: 100 }),
|
||||
facturation: varchar("facturation", { length: 100 }),
|
||||
modeVentilation: varchar("modeVentilation", { length: 50 }).default("Prorata C 6"),
|
||||
compte: varchar("compte", { length: 50 }),
|
||||
detail: text("detail"),
|
||||
budgetN1: decimal("budgetN1", { precision: 12, scale: 2 }),
|
||||
montant: decimal("montant", { precision: 12, scale: 2 }),
|
||||
isCustom: boolean("isCustom").default(false),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
export type OpexPoste = typeof opexPostes.$inferSelect;
|
||||
export type InsertOpexPoste = typeof opexPostes.$inferInsert;
|
||||
|
||||
// Overrides manuels par établissement × poste × année
|
||||
export const opexMontantsEtab = mysqlTable("opex_montants_etab", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull(),
|
||||
etablissementCode: varchar("etablissementCode", { length: 50 }).notNull(),
|
||||
libellePoste: varchar("libellePoste", { length: 255 }).notNull(),
|
||||
montant: decimal("montant", { precision: 12, scale: 2 }),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
export type OpexMontantEtab = typeof opexMontantsEtab.$inferSelect;
|
||||
export type InsertOpexMontantEtab = typeof opexMontantsEtab.$inferInsert;
|
||||
|
||||
// Validation définitive par année
|
||||
export const opexValidated = mysqlTable("opex_validated", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull().unique(),
|
||||
validatedAt: timestamp("validatedAt").defaultNow().notNull(),
|
||||
validatedBy: int("validatedBy"),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// INVENTAIRE PC
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const inventairePostes = mysqlTable("inventaire_postes", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull(),
|
||||
etablissementCode: varchar("etablissementCode", { length: 50 }).notNull(),
|
||||
libelle: varchar("libelle", { length: 255 }),
|
||||
typePoste: mysqlEnum("typePoste", ["fixe", "portable"]).notNull(),
|
||||
dateRef: varchar("dateRef", { length: 20 }),
|
||||
ageAns: decimal("ageAns", { precision: 5, scale: 2 }),
|
||||
modele: varchar("modele", { length: 255 }),
|
||||
fabricant: varchar("fabricant", { length: 100 }),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export type InventairePoste = typeof inventairePostes.$inferSelect;
|
||||
export type InsertInventairePoste = typeof inventairePostes.$inferInsert;
|
||||
|
||||
// Métadonnées d'import inventaire
|
||||
export const inventaireMeta = mysqlTable("inventaire_meta", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull().unique(),
|
||||
filename: varchar("filename", { length: 255 }),
|
||||
dateImport: timestamp("dateImport").defaultNow().notNull(),
|
||||
nbEtablissements: int("nbEtablissements").default(0),
|
||||
nbFixes: int("nbFixes").default(0),
|
||||
nbPortables: int("nbPortables").default(0),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// CAPEX — Lignes budgétaires par établissement
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export const capexLignes = mysqlTable("capex_lignes", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
annee: int("annee").notNull(),
|
||||
etablissementCode: varchar("etablissementCode", { length: 50 }).notNull(),
|
||||
cle: varchar("cle", { length: 100 }).notNull(),
|
||||
// cles possibles: renouvellement_2027 | machines_supplementaires | appel_malade
|
||||
// | telephonie | wifi | video_surveillance | copieurs | visio | autres | commentaires
|
||||
montant: decimal("montant", { precision: 12, scale: 2 }),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
export type CapexLigne = typeof capexLignes.$inferSelect;
|
||||
export type InsertCapexLigne = typeof capexLignes.$inferInsert;
|
||||
Reference in New Issue
Block a user