Vrai-Prix — l'évaluation du vrai prix des propriétés résidentielles au Québec.
TypeScript 90.2%
JavaScript 3.5%
Python 3.4%
CSS 1.9%
HTML 0.6%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2/**3 * Taxonomie de la méthode du coût : divisions MasterFormat, catégories UX,4 * qualités, conditions, matériaux normalisés, métiers, règles de dépréciation.5 * Module PUR (client + serveur).6 */7import type { Condition, Quality, UxCategory, BuildingType, IndirectParams, CostParams } from "./types";89export const METHOD_VERSION = "1.0.0";1011/** Divisions CSI MasterFormat retenues. */12export const MASTERFORMAT: Record<string, { fr: string; en: string }> = {13 "01": { fr: "Exigences générales", en: "General Requirements" },14 "02": { fr: "Conditions existantes", en: "Existing Conditions" },15 "03": { fr: "Béton", en: "Concrete" },16 "04": { fr: "Maçonnerie", en: "Masonry" },17 "05": { fr: "Métaux", en: "Metals" },18 "06": { fr: "Bois, plastiques et composites", en: "Wood, Plastics and Composites" },19 "07": { fr: "Isolation et étanchéité", en: "Thermal and Moisture Protection" },20 "08": { fr: "Ouvertures", en: "Openings" },21 "09": { fr: "Finitions", en: "Finishes" },22 "10": { fr: "Ouvrages spéciaux", en: "Specialties" },23 "11": { fr: "Équipement", en: "Equipment" },24 "12": { fr: "Ameublement", en: "Furnishings" },25 "21": { fr: "Protection incendie", en: "Fire Suppression" },26 "22": { fr: "Plomberie", en: "Plumbing" },27 "23": { fr: "CVCA", en: "HVAC" },28 "26": { fr: "Électricité", en: "Electrical" },29 "31": { fr: "Terrassement", en: "Earthwork" },30 "32": { fr: "Aménagements extérieurs", en: "Exterior Improvements" },31 "33": { fr: "Services publics", en: "Utilities" },32};3334export const UX_CATEGORIES: { key: UxCategory; fr: string; en: string; order: number }[] = [35 { key: "site", fr: "Préparation du site", en: "Site preparation", order: 1 },36 { key: "excavation", fr: "Excavation", en: "Excavation", order: 2 },37 { key: "foundation", fr: "Fondations", en: "Foundations", order: 3 },38 { key: "structure", fr: "Structure", en: "Structure", order: 4 },39 { key: "roofing", fr: "Toiture", en: "Roofing", order: 5 },40 { key: "envelope", fr: "Enveloppe", en: "Envelope", order: 6 },41 { key: "openings", fr: "Portes et fenêtres", en: "Doors and windows", order: 7 },42 { key: "insulation", fr: "Isolation", en: "Insulation", order: 8 },43 { key: "finishes_ext", fr: "Revêtements", en: "Cladding", order: 9 },44 { key: "interior", fr: "Finition intérieure", en: "Interior finishes", order: 10 },45 { key: "kitchen", fr: "Cuisine", en: "Kitchen", order: 11 },46 { key: "bathroom", fr: "Salle de bain", en: "Bathroom", order: 12 },47 { key: "plumbing", fr: "Plomberie", en: "Plumbing", order: 13 },48 { key: "hvac", fr: "Chauffage / ventilation", en: "HVAC", order: 14 },49 { key: "electrical", fr: "Électricité", en: "Electrical", order: 15 },50 { key: "garage", fr: "Garage", en: "Garage", order: 16 },51 { key: "basement", fr: "Sous-sol", en: "Basement", order: 17 },52 { key: "exterior", fr: "Aménagement extérieur", en: "Exterior improvements", order: 18 },53];5455export const categoryLabel = (key: UxCategory, fr: boolean): string => {56 const c = UX_CATEGORIES.find((x) => x.key === key);57 return c ? (fr ? c.fr : c.en) : key;58};5960export const QUALITIES: { key: Quality; fr: string; en: string }[] = [61 { key: "economy", fr: "Économique", en: "Economy" },62 { key: "standard", fr: "Standard", en: "Standard" },63 { key: "superior", fr: "Supérieur", en: "Superior" },64 { key: "prestige", fr: "Prestige", en: "Prestige" },65];6667export const CONDITIONS: { key: Condition; fr: string; en: string; effectiveAgeRatio: number }[] = [68 { key: "poor", fr: "Mauvais", en: "Poor", effectiveAgeRatio: 0.85 },69 { key: "below_average", fr: "Sous la moyenne", en: "Below average", effectiveAgeRatio: 0.7 },70 { key: "average", fr: "Moyen", en: "Average", effectiveAgeRatio: 0.55 },71 { key: "good", fr: "Bon", en: "Good", effectiveAgeRatio: 0.35 },72 { key: "very_good", fr: "Très bon", en: "Very good", effectiveAgeRatio: 0.2 },73 { key: "excellent", fr: "Excellent", en: "Excellent", effectiveAgeRatio: 0.1 },74 { key: "renovated", fr: "Rénové", en: "Renovated", effectiveAgeRatio: 0.12 },75 { key: "new", fr: "Neuf", en: "New", effectiveAgeRatio: 0.0 },76];7778/**79 * Règles condition → âge effectif d'une composante (component_condition_rules,80 * §144). L'âge effectif = ratio × vie économique de la composante. Hypothèses81 * documentées, modifiables en base (table `component_condition_rules`).82 */83export const CONDITION_GROUPS: { key: string; fr: string; en: string; economicLife: number }[] = [84 { key: "structure", fr: "Structure et fondations", en: "Structure and foundations", economicLife: 75 },85 { key: "roof", fr: "Toiture", en: "Roof", economicLife: 22 },86 { key: "exterior", fr: "Enveloppe et revêtements", en: "Envelope and cladding", economicLife: 40 },87 { key: "windows", fr: "Portes et fenêtres", en: "Doors and windows", economicLife: 30 },88 { key: "interior", fr: "Finition intérieure", en: "Interior finishes", economicLife: 25 },89 { key: "kitchen", fr: "Cuisine", en: "Kitchen", economicLife: 20 },90 { key: "bathroom", fr: "Salles de bain", en: "Bathrooms", economicLife: 22 },91 { key: "mechanical", fr: "Mécanique (plomberie, CVCA)", en: "Mechanical (plumbing, HVAC)", economicLife: 22 },92 { key: "electrical", fr: "Électricité", en: "Electrical", economicLife: 35 },93 { key: "site", fr: "Site et extérieur", en: "Site and exterior", economicLife: 25 },94];9596export const BUILDING_TYPES: { key: BuildingType; fr: string; en: string; economicLife: number }[] = [97 { key: "detached", fr: "Maison unifamiliale détachée", en: "Detached single-family", economicLife: 60 },98 { key: "semi_detached", fr: "Jumelé", en: "Semi-detached", economicLife: 60 },99 { key: "row", fr: "Maison en rangée", en: "Row house", economicLife: 60 },100 { key: "plex", fr: "Plex (2-5 logements)", en: "Plex (2-5 units)", economicLife: 60 },101 { key: "condo", fr: "Condo (unité)", en: "Condo (unit)", economicLife: 60 },102 { key: "chalet", fr: "Chalet", en: "Cottage", economicLife: 50 },103 { key: "mobile", fr: "Maison mobile", en: "Mobile home", economicLife: 35 },104 { key: "other", fr: "Autre", en: "Other", economicLife: 55 },105];106107/** Métiers (codes internes) ↔ libellés APCHQ/CCQ. */108export const TRADES: { code: string; fr: string; en: string; apchq: string[] }[] = [109 { code: "charpentier", fr: "Charpentier-menuisier", en: "Carpenter", apchq: ["Charpentier-menuisier"] },110 { code: "briqueteur", fr: "Briqueteur-maçon", en: "Bricklayer-mason", apchq: ["Briqueteur-maçon"] },111 { code: "carreleur", fr: "Carreleur", en: "Tile setter", apchq: ["Carreleur"] },112 { code: "cimentier", fr: "Cimentier-applicateur", en: "Cement finisher", apchq: ["Cimentier-applicateur"] },113 { code: "coffreur", fr: "Coffreur à béton", en: "Concrete formworker", apchq: ["Coffreur à béton"] },114 { code: "couvreur", fr: "Couvreur", en: "Roofer", apchq: ["Couvreur"] },115 { code: "electricien", fr: "Électricien", en: "Electrician", apchq: ["Électricien", "Electricien"] },116 { code: "plombier", fr: "Tuyauteur (plombier)", en: "Pipefitter (plumber)", apchq: ["Tuyauteur", "Plombier"] },117 { code: "ferblantier", fr: "Ferblantier", en: "Sheet-metal worker", apchq: ["Ferblantier"] },118 { code: "frigoriste", fr: "Frigoriste", en: "Refrigeration mechanic", apchq: ["Frigoriste"] },119 { code: "peintre", fr: "Peintre", en: "Painter", apchq: ["Peintre"] },120 { code: "platrier", fr: "Plâtrier (tireur de joints)", en: "Plasterer (taper)", apchq: ["Plâtrier", "Platrier"] },121 { code: "poseur_systemes", fr: "Poseur de systèmes intérieurs", en: "Interior systems installer", apchq: ["Poseur de systèmes intérieurs", "Poseur de systemes interieurs"] },122 { code: "poseur_revetements", fr: "Poseur de revêtements souples", en: "Floor-covering installer", apchq: ["Poseur de revêtements souples", "Poseur de revetements souples"] },123 { code: "calorifugeur", fr: "Calorifugeur", en: "Insulator", apchq: ["Calorifugeur"] },124 { code: "ferrailleur", fr: "Ferrailleur", en: "Rebar worker", apchq: ["Ferrailleur"] },125 { code: "vitrier", fr: "Monteur-mécanicien (vitrier)", en: "Glazier", apchq: ["Monteur-mécanicien (vitrier)", "Vitrier"] },126 { code: "manoeuvre", fr: "Manœuvre", en: "Labourer", apchq: ["Manœuvre", "Manoeuvre"] },127 { code: "operateur", fr: "Opérateur de pelles mécaniques", en: "Excavator operator", apchq: ["Opérateur de pelles mécaniques", "Operateur de pelles mecaniques", "Opérateur de pelles"] },128 { code: "poseur_bardeaux", fr: "Poseur de bardeaux", en: "Shingle installer", apchq: ["Couvreur"] },129];130131export const tradeLabel = (code: string | null, fr: boolean): string => {132 const t = TRADES.find((x) => x.code === code);133 return t ? (fr ? t.fr : t.en) : (code ?? "—");134};135136/**137 * Paramètres par défaut — HYPOTHÈSES DOCUMENTÉES (aucun pourcentage n'est138 * universel) : visibles, modifiables et expliqués dans l'interface.139 */140export const DEFAULT_INDIRECT: IndirectParams = {141 plansPct: 3.0,142 permitsPct: 0.8,143 inspectionPct: 0.5,144 insurancePct: 1.5,145 mobilizationPct: 1.5,146 siteManagementPct: 3.0,147 financingPct: 2.0,148 adminPct: 1.0,149};150151export const DEFAULT_PARAMS: CostParams = {152 indirect: DEFAULT_INDIRECT,153 overheadPct: 7.0,154 profitPct: 8.0,155 contingencyPct: 3.0,156};157158export const INDIRECT_LABELS: Record<keyof IndirectParams, { fr: string; en: string; noteFr: string }> = {159 plansPct: { fr: "Plans et professionnels", en: "Plans and professionals", noteFr: "Architecte/technologue, ingénieur, arpenteur — hypothèse 3 % du coût direct." },160 permitsPct: { fr: "Permis", en: "Permits", noteFr: "Permis de construction municipal — hypothèse 0,8 %." },161 inspectionPct: { fr: "Inspection et essais", en: "Inspection and testing", noteFr: "Inspections, essais de sol/béton — hypothèse 0,5 %." },162 insurancePct: { fr: "Assurances chantier", en: "Builder's insurance", noteFr: "Assurance chantier et responsabilité — hypothèse 1,5 %." },163 mobilizationPct: { fr: "Mobilisation", en: "Mobilization", noteFr: "Roulotte, clôture, installations temporaires — hypothèse 1,5 %." },164 siteManagementPct: { fr: "Gestion de chantier", en: "Site management", noteFr: "Surintendant, coordination — hypothèse 3 %." },165 financingPct: { fr: "Financement", en: "Financing", noteFr: "Intérêts intercalaires pendant la construction — hypothèse 2 %." },166 adminPct: { fr: "Administration", en: "Administration", noteFr: "Frais administratifs du projet — hypothèse 1 %." },167};168169/** Matériaux normalisés (taxonomie canonique renvoyée par l'IA et l'UI). */170export const MATERIALS = [171 "quartz", "granite", "laminate", "butcher_block", "solid_surface", "marble",172 "hardwood", "engineered_wood", "vinyl_plank", "laminate_floor", "ceramic_tile", "porcelain_tile", "carpet", "concrete_floor",173 "brick", "stone", "vinyl_siding", "fiber_cement", "wood_siding", "stucco", "aluminum_siding", "steel_siding",174 "asphalt_shingle", "metal_roof", "elastomeric_membrane", "epdm", "cedar_shake",175 "pvc_window", "hybrid_window", "aluminum_window", "wood_window",176 "poured_concrete", "concrete_block", "stone_foundation",177 "melamine", "thermofoil", "painted_mdf", "wood_veneer", "solid_wood",178] as const;179export type Material = (typeof MATERIALS)[number];180181/** Économie de lecture des cuisines : pieds linéaires d'armoires typiques par qualité. */182export const KITCHEN_LINEAR_FT: Record<Quality, number> = { economy: 20, standard: 26, superior: 32, prestige: 40 };183