SPB Git forge

spb/vrai-prix

Public

Vrai-Prix — l'évaluation du vrai prix des propriétés résidentielles au Québec.

60commits 1branches 0releases
12.3 MBsize
maindefault branch
17 days agolast push
TypeScript 90.2% JavaScript 3.5% Python 3.4% CSS 1.9% HTML 0.6%
11.2 KB · 183 lines typescript
Raw Blame History
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