Estimez le coût de vos diagnostics

Une estimation détaillée tenant compte du bien, du temps d’intervention, de la distance depuis Vélizy-Villacoublay et des moyennes de marché.

1. Votre projet

Sélectionnez la mission et décrivez le bien.

2. Caractéristiques du bien

Ces renseignements déterminent les diagnostics probablement applicables.

À confirmer à partir de l’adresse exacte et de l’arrêté préfectoral.

3. Déplacement depuis Vélizy-Villacoublay

Indiquez une estimation du trajet aller. Le moteur calcule automatiquement l’aller-retour.

Le déplacement est inclus dans un rayon de proximité. Au-delà, seule la part excédentaire est prise en compte dans l’estimation.

4. Diagnostics à inclure

Décochez les diagnostics déjà disponibles et encore valides.

Les anciens rapports devront être contrôlés avant établissement du devis définitif.

Votre estimation personnalisée

Diagnostics probablement nécessaires

    Positionnement marché

    Estimation indicative TTC
    Sur place
    Trajet A/R
    Total mission
    Base diagnostics / marché
    Ajustement bien et surface
    Déplacement / contraintes
    Remise pack
    Recevoir mon devis personnalisé
    Estimation non contractuelle. Le prix définitif dépendra de l’adresse exacte, des documents existants, de la configuration réelle du bien et, pour les missions avant travaux ou démolition, du périmètre et des prélèvements éventuels.
    Réglages internes de calcul
    `).join(""); } function selectedServices(){ return [...document.querySelectorAll('#checks input[type="checkbox"]:checked')].map(x=>x.value); } function typeCoefficient(){ return {"Appartement":1,"Maison":1.08,"Local professionnel":1.25,"Immeuble entier":1.60}[val("type")]||1; } function surfaceCoefficient(){ const a=num("surface"); if(a<=50)return .92;if(a<=100)return 1;if(a<=150)return 1.12;if(a<=250)return 1.25;if(a<=500)return 1.42;return 1.65; } function accessCoefficient(){ return {"normal":1,"parking":1.04,"stairs":1.07,"occupied":1.10}[val("access")]||1; } function urgencyCoefficient(){ return {"standard":1,"fast":1.08,"urgent":1.15}[val("urgency")]||1; } function packRate(count){ if(count>=7)return .17;if(count>=5)return .13;if(count>=3)return .08;if(count>=2)return .04;return 0; } function formatDuration(minutes){ const m=Math.max(0,Math.round(minutes)); const h=Math.floor(m/60),r=m%60; return h ? `${h} h ${String(r).padStart(2,"0")}` : `${r} min`; } function compute(){ const services=selectedServices(),vat=num("vat")||1.2; let avgHT=0,lowHT=0,highHT=0,baseMinutes=0; services.forEach(s=>{ avgHT+=MARKET[s].avg;lowHT+=MARKET[s].low;highHT+=MARKET[s].high;baseMinutes+=MARKET[s].time; }); const propertyCoef=typeCoefficient()*surfaceCoefficient()*accessCoefficient()*urgencyCoefficient(); const adjustedHT=avgHT*propertyCoef; const propertyAdjHT=adjustedHT-avgHT; const distance=num("distance"),travelMin=num("travelTime"); const includedKm=num("includedKm"),includedMinutes=num("includedMinutes"); const excessRoundKm=Math.max(0,(distance-includedKm)*2); const excessRoundMinutes=Math.max(0,(travelMin-includedMinutes)*2); const kmCostTTC=excessRoundKm*(num("kmRate")||.65); const timeCostTTC=(excessRoundMinutes/60)*(num("travelHourly")||42); // Évite de facturer intégralement deux fois le même déplacement. const travelTTC=Math.max(kmCostTTC,timeCostTTC); const discountRate=packRate(services.length); const discountHT=adjustedHT*discountRate; const totalTTC=Math.max(0,(adjustedHT-discountHT)*vat+travelTTC); const surfaceTimeCoef=Math.max(.85,Math.min(1.75,surfaceCoefficient())); const typeTimeCoef=val("type")==="Immeuble entier"?1.35:val("type")==="Local professionnel"?1.18:1; const siteMinutes=Math.round(baseMinutes*surfaceTimeCoef*typeTimeCoef*accessCoefficient()); const reportMinutes=Math.round(siteMinutes*.28); const roundTravelMinutes=travelMin*2; const totalMinutes=siteMinutes+reportMinutes+roundTravelMinutes; const rangePct=(num("rangePercent")||7)/100; const estimateLow=Math.round(totalTTC*(1-rangePct)/5)*5; const estimateHigh=Math.round(totalTTC*(1+rangePct)/5)*5; const marketLowTTC=lowHT*vat; const marketHighTTC=highHT*vat; const marketPosition=marketHighTTC>marketLowTTC ?Math.max(0,Math.min(100,((totalTTC-marketLowTTC)/(marketHighTTC-marketLowTTC))*100)) :50; return {services,avgHT,lowHT,highHT,vat,propertyAdjHT,travelTTC,discountHT,totalTTC, estimateLow,estimateHigh,siteMinutes,reportMinutes,roundTravelMinutes,totalMinutes, marketLowTTC,marketHighTTC,marketPosition}; } function showResult(){ const r=compute(); document.getElementById("diagnosticList").innerHTML=r.services.length ?r.services.map(s=>`
  • ${LABELS[s]}${Math.round(MARKET[s].avg*r.vat)} € moy.
  • `).join("") :"
  • Aucun diagnostic sélectionné
  • "; document.getElementById("estimatedPrice").textContent=`${Math.round(r.totalTTC)} €`; document.getElementById("estimatedRange").textContent=`Fourchette conseillée : ${r.estimateLow} à ${r.estimateHigh} € TTC`; document.getElementById("siteTime").textContent=formatDuration(r.siteMinutes); document.getElementById("roundTravel").textContent=formatDuration(r.roundTravelMinutes); document.getElementById("totalTime").textContent=formatDuration(r.totalMinutes); document.getElementById("basePrice").textContent=`${Math.round(r.avgHT*r.vat)} €`; document.getElementById("propertyAdjustment").textContent=`${r.propertyAdjHT>=0?"+":""}${Math.round(r.propertyAdjHT*r.vat)} €`; document.getElementById("travelAdjustment").textContent=`${r.travelTTC>0?"+":""}${Math.round(r.travelTTC)} €`; document.getElementById("packDiscount").textContent=`-${Math.round(r.discountHT*r.vat)} €`; document.getElementById("marketDot").style.left=`${r.marketPosition}%`; let position="dans la moyenne du marché"; if(r.marketPosition<33)position="dans la partie basse du marché"; else if(r.marketPosition>67)position="dans la partie haute du marché"; document.getElementById("marketText").textContent= `Pour cette combinaison de diagnostics, les repères étudiés se situent approximativement entre ${Math.round(r.marketLowTTC)} € et ${Math.round(r.marketHighTTC)} € TTC avant ajustements. L'estimation Facility Diagnostic se place ${position}.`; buildMail(r); goToStep(5); } function buildMail(r){ const body=`Bonjour, Je souhaite recevoir un devis personnalisé Facility Diagnostic. Projet : ${project()} Type de bien : ${val("type")} Surface : ${val("surface")} m² Pièces : ${val("rooms")} Code postal : ${val("cp")||"Non renseigné"} Distance aller estimée depuis Vélizy : ${val("distance")} km Temps de trajet aller : ${val("travelTime")} minutes Diagnostics estimés : ${r.services.map(s=>"- "+LABELS[s]).join("\n")} Estimation affichée : ${Math.round(r.totalTTC)} € TTC Fourchette indicative : ${r.estimateLow} à ${r.estimateHigh} € TTC Temps total estimé de mission : ${formatDuration(r.totalMinutes)} Cette estimation est indicative et doit être confirmée après vérification du bien et des documents. Merci de me recontacter. Cordialement,`; document.getElementById("requestButton").href= `mailto:contact@facility-diagnostic.fr?subject=${encodeURIComponent("Demande de devis — "+project())}&body=${encodeURIComponent(body)}`; } function nextStep(){ if(currentStep===1 && num("surface")<=0){alert("Merci d’indiquer une surface valide.");return} if(currentStep===4){showResult();return} if(currentStep===3)renderChecks(); goToStep(currentStep+1); } function prevStep(){goToStep(currentStep-1)} function goToStep(n){ currentStep=Math.max(1,Math.min(5,n)); document.querySelectorAll(".step").forEach(s=>s.classList.toggle("active",Number(s.dataset.step)===currentStep)); document.getElementById("progressBar").style.width=`${currentStep*20}%`; document.getElementById("app").scrollIntoView({behavior:"smooth",block:"start"}); } goToStep(1);