SPB Git

Repository Public

types.ts 14.2 KB 424 lines typescript

1// UQO Éval — Métré estimatif / intelligence de la construction (module « metre »)2/**3 * Types partagés du module Métré. Module PUR (importable côté client).4 *5 * Vocabulaire :6 *  - projet (project)          : un bâtiment / une propriété à chiffrer7 *  - plan (plan_file/page)     : PDF ou image importé, calibré par page8 *  - objet (plan_object)       : polygone, polyligne, ouverture, annotation dessinés sur une page9 *  - mesure (measurement)      : ligne du registre de métré (formule + entrées + valeur + unité)10 *  - lien (quantity_link)      : mesure → assemblage × facteur d'ajustement explicite11 *  - estimation (estimate)     : état de travail mutable ; révision (version) = instantané immuable12 *  - preuve de prix            : classe d'évidence (barème publié, contrat octroyé, soumission…)13 */1415export type Role = "student" | "instructor" | "admin" | "professional";16export type Lang = "fr" | "en";17export type UnitSystem = "metric" | "imperial";1819/* ---------------------------------------------------------------- unités */2021export type LengthUnit = "m" | "ft" | "mm" | "cm" | "in";22export type AreaUnit = "m2" | "ft2";23export type VolumeUnit = "m3" | "ft3" | "yd3";24export type MeasureUnit = LengthUnit | AreaUnit | VolumeUnit | "unit" | "h" | "lump";2526/* ------------------------------------------------------------- utilisateur */2728export interface MetreUser {29  id: string;30  displayName: string;31  role: Role;32  createdAt: string;33}3435/* ----------------------------------------------------------------- projet */3637export type BuildingKind = "detached" | "semi_detached" | "row" | "plex" | "condo" | "chalet" | "commercial" | "other";3839export interface Project {40  id: string;41  ownerId: string;42  name: string;43  address: string | null;44  municipality: string | null;45  regionCode: string | null; // cost_locations.code (QC-MTL, QC-QUE, QC-GAT…)46  propertyId: string | null; // id_provinc MAMH (lien facultatif avec UQO Éval)47  valuationDate: string; // YYYY-MM-DD48  buildingKind: BuildingKind;49  stories: number;50  unitSystem: UnitSystem;51  notes: string | null;52  isTemplate: boolean;53  /** verrouillé par son créateur : lecture seule pour les autres (plateforme ouverte) */54  locked: boolean;55  /** fiche technique pour le relevé des quantités (null = défauts) */56  spec: Record<string, unknown> | null;57  createdAt: string;58  updatedAt: string;59}6061/* ------------------------------------------------------------------ plans */6263export interface PlanFile {64  id: string;65  projectId: string;66  filename: string;67  mime: string;68  bytes: number;69  sha256: string;70  pageCount: number;71  createdAt: string;72}7374export interface PlanPage {75  id: string;76  fileId: string;77  pageNo: number; // 1-based78  widthPx: number;79  heightPx: number;80  label: string | null;81  calibration: Calibration | null; // révision courante82  /** origine : import (pdf/png/jpg/dxf), vierge (plan dessiné), carte (satellite géoréférencée), gabarit */83  source?: "import" | "blank" | "map" | "template";84  /** niveau (sous-sol, rez-de-chaussée, étage, toit, site) */85  level?: string | null;86}8788/** Calibration d'échelle par deux points connus (révisionnée, jamais écrasée). */89export interface Calibration {90  id: string;91  pageId: string;92  revision: number;93  p1: Point;94  p2: Point;95  realLength: number;96  unit: LengthUnit;97  /** pixels par mètre (dérivé) */98  pxPerMeter: number;99  /** incertitude relative estimée (clic ± 1,5 px sur chaque point) */100  uncertaintyPct: number;101  createdBy: string;102  createdAt: string;103}104105export interface Point { x: number; y: number }106107export type ObjectKind = "room" | "slab" | "wall" | "opening" | "roof" | "foundation" | "line" | "area" | "annotation" | "dimension" | "text" | "stairs" | "column" | "footprint" | "lot";108109export interface ObjectProps {110  /** hauteur (m) — murs */111  height?: number;112  /** épaisseur (m) — dalles, fondations */113  thickness?: number;114  /** pente en pouces de hausse par 12 po (toits) */115  pitch?: number;116  /** largeur × hauteur (m) — ouvertures dessinées comme un point/segment */117  openingWidth?: number;118  openingHeight?: number;119  /** id du mur dont l'ouverture se déduit (une seule déduction possible) */120  deductFrom?: string | null;121  /** nombre d'unités (ouvertures identiques, etc.) */122  count?: number;123  /** calque d'affichage */124  color?: string;125  note?: string;126  /** ouvertures : porte | fenêtre | baie libre ; battement ±1 (côté), ±2 (charnière à droite) */127  openingKind?: "door" | "window" | "opening";128  swing?: number;129  /** mur extérieur (compte dans l'enveloppe) */130  exterior?: boolean;131  /** texte d'annotation / taille (px page) */132  text?: string;133  fontSize?: number;134  /** cotation : décalage perpendiculaire (px page) */135  offset?: number;136  /** pièce : usage (chambre, cuisine…) */137  usage?: string;138}139140export interface PlanObject {141  id: string;142  pageId: string;143  kind: ObjectKind;144  layer: string;145  name: string;146  points: Point[]; // pixels page147  closed: boolean;148  props: ObjectProps;149  /** révision de calibration au moment de la dernière mesure */150  createdAt: string;151  updatedAt: string;152}153154/* ------------------------------------------------------------ registre */155156export type FormulaKey =157  | "polygon_area"158  | "polyline_length"159  | "wall_gross_area"160  | "wall_net_area"161  | "slab_volume"162  | "roof_surface_area"163  | "opening_area"164  | "count"165  | "manual";166167export interface Measurement {168  id: string;169  projectId: string;170  objectId: string | null; // null = saisie manuelle171  pageId: string | null;172  name: string;173  formula: FormulaKey;174  formulaText: string; // forme lisible, ex. « mur_brut = longueur × hauteur »175  inputs: Record<string, number | string | null>;176  value: number;177  unit: MeasureUnit;178  calibrationRevision: number | null;179  /** la calibration a changé depuis le calcul → recalcul requis */180  stale: boolean;181  confidence: "measured" | "estimated" | "assumed";182  verifiedBy: string | null;183  verifiedAt: string | null;184  computedAt: string;185  /** méthodes de dérivation (ex. identifiants des ouvertures déduites) */186  lineage: string[];187}188189/* -------------------------------------------------------------- preuves */190191export type EvidenceClass =192  | "published_schedule" // barème publié (DTNI, MTMD, grille APCHQ…)193  | "awarded_contract_total" // montant d'un contrat octroyé (SEAO) — JAMAIS un prix unitaire194  | "awarded_unit_bid" // prix unitaire d'une soumission retenue (bordereau)195  | "supplier_quote" // prix de fournisseur / détaillant observé196  | "contractor_quote" // soumission d'entrepreneur197  | "permit_declared_value" // valeur déclarée au permis (≠ coût réel)198  | "modeled" // estimation de modèle (étiquetée)199  | "user_assumption"; // hypothèse explicite de l'utilisateur200201/** Nature du prix unitaire utilisé par une ligne d'estimation. */202export type PriceBasis =203  | "catalogue" // coût d'assemblage du catalogue UQO Éval (prix observés / officiels / référence)204  | "custom_assembly" // assemblage défini dans le module (recette)205  | "assumption" // prix unitaire saisi par l'utilisateur206  | "observation" // observation de prix normalisée du module (classe d'évidence)207  | "rsmeans"; // ligne de coût unitaire RSMeans (Gordian) localisée pour le Québec — coûts « bare » (sans O&P de l'installateur)208209export interface Provenance {210  basis: PriceBasis;211  /** nature des prix sous-jacents (observé/officiel/référence/hypothèse) */212  kind: "observed" | "official" | "indexed" | "derived" | "reference" | "assumption" | "modeled";213  evidenceClass: EvidenceClass | null;214  source: string;215  sourceUrl: string | null;216  observedAt: string | null;217  note: string | null;218  confidence: number; // 0-100219  /** part du coût matériau reposant sur des prix observés (0-1) */220  observedShare: number;221}222223/* ------------------------------------------------------------ assemblages */224225export interface CustomComponent {226  id: string;227  /** article du catalogue (cost_items.canonical_code) OU assemblage imbriqué (code d'assemblage du module/catalogue) */228  refType: "item" | "assembly";229  refCode: string;230  quantity: number; // par unité de sortie de l'assemblage231  wasteRate: number; // 0.05 = 5 % (appliqué une seule fois, sur les matériaux)232  labourHours: number;233  trade: string | null;234  equipmentHours: number;235  equipmentRate: number; // $/h (hypothèse documentée)236  subcontract: number; // $ par unité de sortie237  transport: number; // $ par unité de sortie238  /** prix tout compris (installé) : ne pas ajouter de main-d'œuvre ni de pertes */239  installed: boolean;240  notes: string | null;241}242243export interface CustomAssembly {244  id: string;245  projectId: string | null; // null = partagé (enseignant/admin)246  code: string;247  nameFr: string;248  nameEn: string;249  unit: MeasureUnit;250  outputQuantity: number; // quantité de sortie produite par la recette (≥ 0)251  category: string;252  version: number;253  components: CustomComponent[];254  exclusions: string | null;255  createdBy: string;256  createdAt: string;257  updatedAt: string;258}259260/* ------------------------------------------------------------- estimation */261262export interface EstimateLineInput {263  id: string;264  wbs: string; // division / structure de répartition (ex. « 03 — Béton »)265  description: string;266  assemblyCode: string | null; // catalogue ou assemblage du module267  measurementId: string | null;268  quantity: number;269  unit: MeasureUnit | string;270  adjustmentFactor: number; // assembly_quantity = takeoff_quantity × facteur explicite271  basis: PriceBasis;272  /** prix unitaire saisi (basis = assumption) — $/unité, direct (matériau + MO + équipement)273   *  ou coût installé si `installed` */274  unitPrice: number | null;275  installed: boolean;276  assumptionNote: string | null;277  excluded: boolean;278  sortOrder: number;279  /** origine : relevé des quantités automatique (remplacé à chaque génération) ou saisie */280  origin?: "qs" | "manual";281  /** numéro de ligne RSMeans (12 chiffres, MasterFormat 2018) lorsque basis = "rsmeans" */282  rsmeansLine?: string | null;283}284285export interface EstimateParams {286  regionCode: string | null;287  priceDate: string; // date des prix (instantané)288  generalConditionsPct: number;289  overheadPct: number;290  profitPct: number;291  contingencyPct: number;292  /** actualisation (indice StatCan) : date cible ; null = aucune */293  escalateTo: string | null;294  escalationFactor: number | null; // calculé (indice) ou saisi295  escalationSource: string | null;296  applyGst: boolean; // TPS 5 %297  applyQst: boolean; // TVQ 9,975 %298  roundingCents: number; // 1 = cent, 100 = dollar299  /** localisation RSMeans (id de ville ; null = déduite du projet) et type de main-d'œuvre pour les lignes « rsmeans » */300  rsmeansLocationId?: number | null;301  rsmeansLabor?: "STD" | "OPN" | "RES" | "FMR";302}303304export interface EstimateLineResult {305  id: string;306  wbs: string;307  description: string;308  assemblyCode: string | null;309  measurementId: string | null;310  takeoffQuantity: number;311  adjustmentFactor: number;312  quantity: number;313  unit: string;314  basis: PriceBasis;315  installed: boolean;316  unitMaterial: number; // $ par unité (cents → $ arrondis à 0,01)317  unitLabour: number;318  unitEquipment: number;319  unitOther: number; // sous-traitance + transport320  unitDirect: number;321  material: number;322  labour: number;323  equipment: number;324  other: number;325  direct: number; // avant facteur régional326  located: number; // après facteur régional (matériau/MO/équipement distincts)327  provenance: Provenance;328  warnings: string[];329  excluded: boolean;330}331332export interface EstimateTotals {333  direct: number;334  located: number;335  locationAdjustment: number;336  generalConditions: number;337  overhead: number;338  profit: number;339  contingency: number;340  subtotalBeforeEscalation: number;341  escalation: number;342  subtotalBeforeTax: number;343  gst: number;344  qst: number;345  total: number;346  material: number;347  labour: number;348  equipment: number;349  other: number;350}351352export interface EstimateResult {353  computedAt: string;354  params: EstimateParams;355  location: { code: string; nameFr: string; nameEn: string; materialFactor: number; labourFactor: number; equipmentFactor: number; confidence: number; method: string };356  lines: EstimateLineResult[];357  byWbs: { wbs: string; direct: number; located: number; sharePct: number }[];358  totals: EstimateTotals;359  /** ordre d'application documenté (pour le rapport) */360  applicationOrder: string[];361  warnings: string[];362  coverage: { linesPriced: number; linesTotal: number; observedShare: number; assumptionShare: number };363  catalogueVersion: string;364  formulaVersion: string;365}366367export interface EstimateVersion {368  id: string;369  estimateId: string;370  projectId: string;371  number: number;372  label: string;373  scenario: string | null;374  createdBy: string;375  createdAt: string;376  contentHash: string;377  total: number;378  result: EstimateResult;379  lines: EstimateLineInput[];380}381382/* ------------------------------------------------------------- évaluation */383384export interface FunctionalItem {385  id: string;386  label: string;387  curable: boolean;388  amount: number; // coût de correction (curable) ou perte de valeur (incurable)389  /** la conception de remplacement élimine déjà cette déficience → ne pas déduire deux fois */390  eliminatedByReplacement: boolean;391}392393export interface ValuationInput {394  basis: "replacement" | "reproduction";395  /** coût à neuf retenu (de la révision d'estimation liée) */396  costNew: number;397  /** ajustement explicite reproduction ↔ remplacement (ex. −8 % matériaux modernes) */398  basisAdjustmentPct: number;399  siteImprovements: number;400  physical: { method: "age_life" | "percent"; effectiveAge: number | null; economicLife: number; percent: number | null };401  functional: FunctionalItem[];402  external: { amount: number; note: string | null };403  land: { value: number; source: "role" | "user" | "market" | "residual"; note: string | null };404}405406export interface ValuationResult {407  costNewUsed: number;408  basisAdjustment: number;409  siteImprovements: number;410  totalCostNew: number;411  physicalPct: number;412  physical: number;413  functionalCurable: number;414  functionalIncurable: number;415  functionalSkipped: { id: string; label: string; amount: number }[];416  external: number;417  totalDepreciation: number;418  depreciatedImprovements: number;419  landValue: number;420  indicatedValue: number;421  steps: { label: string; amount: number }[];422  warnings: string[];423}424
Files main
Menu

Theme