Files
itinova-budget-si/drizzle/schema.ts
Manus 14a660892a Checkpoint: - Ajout table opex_bases_repartition (11e table) avec 58 bases de répartition 2026 seedées depuis le JSON source
- Ajout procédure tRPC opex.getBasesRepartition et opex.setBaseRepartition
- DsiOpex utilise maintenant les bases de répartition depuis la BDD (priorité BDD > JSON fallback pour 2025)
- Total en bas à droite de la vue établissements affiche totalGlobal (somme postes BDD = 1 257 678 €) au lieu de la somme des arrondis par établissement (1 259 641 €)
- 0 erreur TypeScript
2026-06-11 06:24:31 +00:00

179 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
// ───────────────────────────────────────────────────────────────────────────────
// OPEX — Bases de répartition par établissement et par année
// (charges de classe 6 utilisées pour le prorata)
// ───────────────────────────────────────────────────────────────────────────────
export const opexBasesRepartition = mysqlTable("opex_bases_repartition", {
id: int("id").autoincrement().primaryKey(),
annee: int("annee").notNull(),
etablissementCode: varchar("etablissementCode", { length: 50 }).notNull(),
etablissementNom: varchar("etablissementNom", { length: 255 }),
baseRepartition: decimal("baseRepartition", { precision: 20, scale: 6 }).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type OpexBaseRepartition = typeof opexBasesRepartition.$inferSelect;
export type InsertOpexBaseRepartition = typeof opexBasesRepartition.$inferInsert;