Simulateur Fiscal Immobilier — Multi-biens, abattement micro, export PDF (fix)
*{box-sizing:border-box}
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:0;color:#222;background:linear-gradient(135deg,#0ea5e9,#8b5cf6) fixed}
.container{max-width:1400px;margin:0 auto;padding:20px}
.header{color:#fff;text-align:center;margin-bottom:22px}
.header h1{font-size:2rem;margin:0 0 8px}
.grid{display:grid;grid-template-columns:1fr 1.2fr;gap:20px}
@media (max-width: 1080px){.grid{grid-template-columns:1fr}}
.panel{background:#fff;border-radius:14px;padding:16px;box-shadow:0 12px 35px rgba(0,0,0,.18)}
.panel h2{font-size:1.05rem;margin:0 0 10px}
.toolbar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.btn{border:1px solid #e5e7eb;background:#fff;border-radius:10px;padding:8px 12px;font-weight:600;cursor:pointer}
.btn-primary{background:linear-gradient(135deg,#2563eb,#7c3aed);color:#fff;border:none}
.btn-danger{background:#dc2626;color:#fff;border:none}
.btn-sm{padding:6px 10px;font-size:.9rem}
.row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.row-3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.group{margin-bottom:12px}
label{display:block;font-weight:700;font-size:.9rem;margin-bottom:6px;color:#374151}
select,input{width:100%;padding:10px;border:2px solid #e5e7eb;border-radius:10px;font-size:.95rem}
input:focus,select:focus{outline:none;border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.15)}
.bien{border:1px solid #eef2ff;border-left:4px solid #6366f1;border-radius:12px;padding:14px;margin-bottom:12px;background:#fff}
.bien-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
.bien-title{font-weight:800}
.muted{color:#6b7280}
.kpi{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:12px;margin:10px 0}
.kpi .item{text-align:center}
.kpi .item .lab{font-size:.78rem;color:#6b7280}
.kpi .item .val{font-weight:900}
.table{width:100%;border-collapse:collapse;margin-top:10px}
.table th,.table td{padding:8px;border-bottom:1px solid #eee;text-align:right}
.table th:first-child,.table td:first-child{text-align:left}
.pos{color:#16a34a;font-weight:800}
.neg{color:#dc2626;font-weight:800}
.note{font-size:.9rem;color:#4b5563;background:#f8fafc;border:1px solid #e5e7eb;border-left:4px solid #6366f1;border-radius:10px;padding:10px;margin-top:12px}
.micro-only{opacity:1; transition: opacity .2s ease}
.hidden{display:none}
🏠Simulateur Fiscal Immobilier
Paramètres globaux
0%
11%
30%
41%
45%
20 ans
25 ans
30 ans
LMNP/LMP/SCI IS: amortissement linéaire sur base achat+frais+travaux. LMP cotisations sociales 45% du bénéfice imposable.
Les plafonds, seuils d’éligibilité et regroupements par foyer ne sont pas gérés. Modèle simplifié.
Résultats du portefeuille
| Bien | Statut | Loyers | Charges | Intérêts | Base imposable | IR/IS | PS/Cot. | Net | Rendement |
|---|---|---|---|---|---|---|---|---|---|
| Ajoutez vos biens puis cliquez sur Calculer. | |||||||||
let idx=0;
let lastResults=null;
function euro(x){return new Intl.NumberFormat(‘fr-FR’,{style:’currency’,currency:’EUR’,minimumFractionDigits:0,maximumFractionDigits:0}).format(x||0)}
function pct(x){return (x||0).toFixed(2)+’%’}
function isMicro(statut){return statut===’NU-MICRO’ || statut===’LMNP-MICRO50′ || statut===’LMNP-MICRO71′}
function defaultAbattement(statut){
switch(statut){
case ‘NU-MICRO’: return 30;
case ‘LMNP-MICRO50’: return 50;
case ‘LMNP-MICRO71’: return 71;
default: return 0;
}
}
function addBien(p={}){
const i = idx++;
const d = Object.assign({
nom:’Bien ‘+i,
statut:’NU-MICRO’,
loyer:1100,charges:2000,interets:1500,
base:230000,
abat: defaultAbattement(‘NU-MICRO’)
},p);
const wrap = document.createElement(‘div’);
wrap.className=’bien’;
wrap.dataset.index=i;
wrap.innerHTML = `
Supprimer
Location nue — Micro-foncier
Location nue — Réel
LMNP — Micro-BIC 50%
LMNP — Micro-BIC 71% (MT/CH)
LMNP — Réel
LMP — Réel
SCI à l’IR
SCI à l’IS
`;
document.getElementById(‘biens’).appendChild(wrap);
const select = wrap.querySelector(‘.statut’);
const abatInput = wrap.querySelector(‘.abat’);
const microBlock = wrap.querySelector(‘.micro-only’);
select.addEventListener(‘change’, () => {
if(isMicro(select.value)){
microBlock.classList.remove(‘hidden’);
abatInput.value = defaultAbattement(select.value);
}else{
microBlock.classList.add(‘hidden’);
abatInput.value = 0;
}
});
}
function dup(i){
const el = […document.querySelectorAll(‘.bien’)].find(n=>Number(n.dataset.index)===i);
if(!el) return;
addBien({
nom: el.querySelector(‘.nom’).value+’ (copie)’,
statut: el.querySelector(‘.statut’).value,
loyer: +el.querySelector(‘.loyer’).value||0,
charges: +el.querySelector(‘.charges’).value||0,
interets: +el.querySelector(‘.interets’).value||0,
base: +el.querySelector(‘.base’).value||0,
abat: +el.querySelector(‘.abat’)?.value||defaultAbattement(el.querySelector(‘.statut’).value)
});
}
function delBien(i){
const el = […document.querySelectorAll(‘.bien’)].find(n=>Number(n.dataset.index)===i);
if(el) el.remove();
}
function resetBiens(){
document.getElementById(‘biens’).innerHTML= »;
idx=0;
addBien();
}
function readBiens(){
return […document.querySelectorAll(‘.bien’)].map(b=>{
return {
nom: b.querySelector(‘.nom’).value.trim()||’Bien’,
statut: b.querySelector(‘.statut’).value,
loyer: +b.querySelector(‘.loyer’).value||0,
charges: +b.querySelector(‘.charges’).value||0,
interets: +b.querySelector(‘.interets’).value||0,
base: +b.querySelector(‘.base’).value||0,
abat: +b.querySelector(‘.abat’)?.value||defaultAbattement(b.querySelector(‘.statut’).value)
}
});
}
function labelStatut(v){
switch(v){
case ‘NU-MICRO’: return ‘Nue — Micro-foncier’;
case ‘NU-REEL’: return ‘Nue — RĂ©el’;
case ‘LMNP-MICRO50’: return ‘LMNP — Micro-BIC 50%’;
case ‘LMNP-MICRO71’: return ‘LMNP — Micro-BIC 71%’;
case ‘LMNP-REEL’: return ‘LMNP — RĂ©el’;
case ‘LMP-REEL’: return ‘LMP — RĂ©el’;
case ‘SCI-IR’: return ‘SCI Ă l’IR’;
case ‘SCI-IS’: return ‘SCI Ă l’IS’;
default: return v;
}
}
function simulate(){
const tmi = +document.getElementById(‘tmi’).value||0;
const years = +document.getElementById(‘amortYears’).value||25;
const biens = readBiens();
const tbody = document.getElementById(‘tbody’);
if(biens.length===0){
tbody.innerHTML = `
`;
document.getElementById(‘kpi’).style.display=’none’;
lastResults = null;
return;
}
let rows = »;
let inv=0, loy=0, totalNet=0;
lastResults = [];
biens.forEach(b=>{
const revenus = b.loyer*12;
const charges = b.charges;
const interets = b.interets;
const amort = b.base/years;
let base = 0, ir_is = 0, ps_cot = 0, net = 0;
switch(b.statut){
case ‘NU-MICRO’: {
const taux = Math.min(Math.max(b.abat,0),100);
base = revenus * (1 – taux/100);
ir_is = base * (tmi/100);
ps_cot = base * 0.172;
net = revenus – charges – interets – ir_is – ps_cot;
} break;
case ‘NU-REEL’:
base = revenus – charges – interets;
if(base < 0){ base = 0; }
ir_is = base * (tmi/100);
ps_cot = base * 0.172;
net = revenus – charges – interets – ir_is – ps_cot;
break;
case 'LMNP-MICRO50': {
const taux = Math.min(Math.max(b.abat,0),100);
base = revenus * (1 – taux/100);
ir_is = base * (tmi/100);
ps_cot = base * 0.172;
net = revenus – charges – interets – ir_is – ps_cot;
} break;
case 'LMNP-MICRO71': {
const taux = Math.min(Math.max(b.abat,0),100);
base = revenus * (1 – taux/100);
ir_is = base * (tmi/100);
ps_cot = base * 0.172;
net = revenus – charges – interets – ir_is – ps_cot;
} break;
case 'LMNP-REEL': {
const benefAvantAmort = revenus – charges – interets;
const imposable = Math.max(benefAvantAmort – amort, 0);
base = imposable;
ir_is = base * (tmi/100);
ps_cot = base * 0.172;
net = revenus – charges – interets – ir_is – ps_cot;
} break;
case 'LMP-REEL': {
const benefAvantAmort = revenus – charges – interets;
const imposable = Math.max(benefAvantAmort – amort, 0);
base = imposable;
ir_is = base * (tmi/100);
ps_cot = base * 0.45; // cotisations sociales
net = revenus – charges – interets – ir_is – ps_cot;
} break;
case 'SCI-IR':
base = revenus – charges – interets;
if(base 0 ? (net/b.base*100) : null,
investissement: b.base
});
rows += `
`;
});
document.getElementById(‘tbody’).innerHTML = rows;
document.getElementById(‘kpi’).style.display=’grid’;
document.getElementById(‘kpiBien’).textContent = biens.length;
document.getElementById(‘kpiInv’).textContent = euro(inv);
document.getElementById(‘kpiLoy’).textContent = euro(loy);
document.getElementById(‘kpiNet’).textContent = euro(totalNet);
}
function exportCSV(){
if(!lastResults){ simulate(); if(!lastResults) return; }
const sep = ‘;’;
const head = [‘Bien’,’Statut’,’Loyers’,’Charges’,’Interets’,’Base_imposable’,’IR_IS’,’PS_Cot’,’Net’,’Rendement_%’,’Investissement’];
const rows = lastResults.map(r=>[
r.nom, r.statut, r.revenus, r.charges, r.interets, r.base, r.ir_is, r.ps_cot, r.net, r.rendement!=null?r.rendement.toFixed(2): », r.investissement
].join(sep));
const csv = [head.join(sep), …rows].join(‘\n’);
download(‘resultats_simulation.csv’, csv, ‘text/csv;charset=utf-8;’);
}
function exportHTML(){
if(!lastResults){ simulate(); if(!lastResults) return; }
const now = new Date().toLocaleString(‘fr-FR’);
let rows = »;
lastResults.forEach(r=>{
rows += `
`;
});
const totalNet = lastResults.reduce((s,r)=>s+(r.net||0),0);
const inv = lastResults.reduce((s,r)=>s+(r.investissement||0),0);
const loy = lastResults.reduce((s,r)=>s+(r.revenus||0),0);
const html = `Rapport de simulation
body{font-family:Arial,Helvetica,sans-serif;margin:24px;color:#111}
h1{font-size:20px;margin:0 0 8px}
.muted{color:#555;margin-bottom:16px}
.kpi{display:flex;gap:16px;margin:12px 0;flex-wrap:wrap}
.card{border:1px solid #e5e7eb;border-radius:10px;padding:10px;min-width:180px}
.lab{font-size:12px;color:#6b7280}
.val{font-size:18px;font-weight:800}
table{width:100%;border-collapse:collapse;margin-top:10px}
th,td{padding:8px;border-bottom:1px solid #eee}
th{text-align:left;background:#f8fafc}
td{text-align:right}
td:first-child, th:first-child{text-align:left}
Rapport de simulation — Portefeuille immobilier
| Bien | Statut | Loyers | Charges | Intérêts | Base imposable | IR/IS | PS/Cot. | Net | Rendement |
|---|
`;
download(‘rapport_simulation.html’, html, ‘text/html;charset=utf-8;’);
}
function exportPDF(){
if(!lastResults){ simulate(); if(!lastResults) return; }
const now = new Date().toLocaleString(‘fr-FR’);
let rows = »;
lastResults.forEach(r=>{
rows += `
`;
});
const totalNet = lastResults.reduce((s,r)=>s+(r.net||0),0);
const inv = lastResults.reduce((s,r)=>s+(r.investissement||0),0);
const loy = lastResults.reduce((s,r)=>s+(r.revenus||0),0);
const report = `
Rapport PDF — Simulation immobilière
@page{ size: A4; margin: 12mm }
*{box-sizing:border-box}
body{font-family:Arial,Helvetica,sans-serif;color:#111;margin:0}
header{padding:8px 0 12px;border-bottom:2px solid #000;margin-bottom:10px}
h1{font-size:18px;margin:0}
.muted{color:#444;font-size:12px;margin-top:4px}
.kpi{display:flex;gap:10px;flex-wrap:wrap;margin:10px 0}
.card{border:1px solid #999;border-radius:6px;padding:8px;min-width:140px}
.lab{font-size:11px;color:#555}
.val{font-size:16px;font-weight:800}
table{width:100%;border-collapse:collapse;margin-top:8px;font-size:12px}
th,td{border:1px solid #bbb;padding:6px}
th{background:#f0f0f0;text-align:left}
td{text-align:right}
td:first-child, th:first-child{text-align:left}
footer{position:fixed;bottom:6mm;left:12mm;right:12mm;font-size:10px;color:#555;display:flex;justify-content:space-between}
.pagebreak{page-break-after:always}
Rapport de simulation — Portefeuille immobilier
| Bien | Statut | Loyers | Charges | Intérêts | Base imposable | IR/IS | PS/Cot. | Net | Rendement |
|---|
`;
const blob = new Blob([report], {type:’text/html’});
const url = URL.createObjectURL(blob);
const w = window.open(url, ‘_blank’,’noopener’);
setTimeout(()=>URL.revokeObjectURL(url), 60000);
}
function escapeHtml(str){
return String(str).replace(/[& »‘]/g, s=>({ ‘&’:’&’, »:’>’,’ »‘:’"’, »‘ »:’'’ }[s]));
}
function download(filename, content, mime){
const blob = new Blob([content], {type: mime});
const url = URL.createObjectURL(blob);
const a = document.createElement(‘a’);
a.href = url; a.download = filename;
document.body.appendChild(a); a.click(); a.remove();
setTimeout(()=>URL.revokeObjectURL(url), 1500);
}
window.onload = resetBiens;

