<script lang="ts"> import Banque from '../../Banque.svelte' import Simulation from './Simulation.svelte' import Description from './Description.svelte' import { page } from '$app/stores' import { assets as a } from '$app/paths' import { enhance, applyAction } from '$app/forms' import { onMount } from 'svelte' import type { SubmitFunction } from '@sveltejs/kit' import type { Grain } from '../../../helpers' import * as H from '../../../helpers' import type { Modal, Offcanvas } from 'bootstrap' import { email } from '../../../stores' export let prmsFeature: boolean = false export let assets: string = a export let server = 'https://coturnix.fr' type Prm = { prm: number | null; nom: string; prenom: string } export let data: { nom: string porteur: string adresse: string tel: string id: string centrales: Grain.Centrale[] img?: string simulation: Grain.SimulationCommunaute guest?: boolean prms: Prm[] email?: string } let centrale = { position: data.centrales[0]?.position, azimuth: data.centrales[0]?.azimuth, inclinaison: data.centrales[0]?.inclinaison, } let prodWeekly: number[] = [] let maxProd = 0 let pvgis_loading = false const fmt2 = Intl.NumberFormat('fr-FR', { maximumFractionDigits: 2 }) const fmt0 = Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 }) $: p = data.simulation onMount(async () => { email.set(data.email || null) email.subscribe((value) => { if (value) data.email = value }) for (let i of document.body.children) { if (i.id == 'grainSaveModal') { document.body.removeChild(i) } } let modal = document.getElementById('grainSaveModal') console.log('mount modal', modal) if (modal) { document.body.appendChild(modal) } if (!window.bootstrap) { console.log('no window bootstrap, load') window.bootstrap = await import('bootstrap') } menuElt = document.getElementById('menuoff') if (!menuElt) { return } menu = new window.bootstrap.Offcanvas(menuElt) const popoverTriggerList = document.querySelectorAll( '[data-bs-toggle="popover"]' ) for (let t of popoverTriggerList) { new window.bootstrap.Popover(t) } }) let saveModal_: null | Modal = null let menu: null | Offcanvas = null let menuElt: null | HTMLElement = null async function saveModal(_e: Event) { console.log('saveModal', _e) if (!saveModal_) { console.log('no saveModal') saveModal_ = new window.bootstrap.Modal('#grainSaveModal', { focus: false, keyboard: true, }) } showSaveModalAfterMenuHide() menu?.hide() } function showSaveModalAfterMenuHide() { // menuElt?.removeEventListener('hidden.bs.offcanvas', showSaveModalAfterMenuHide) let c = document.getElementsByClassName('offcanvas-backdrop') for (const elt of c) { elt.parentElement?.removeChild(elt) } saveModal_?.show() } let save: SubmitFunction = async ({ formData }) => { console.log('save called') formData.set('data', JSON.stringify(p)) formData.set('prms', JSON.stringify(data.prms)) formData.set('centrales', JSON.stringify([centrale])) saveModal_?.hide() return async ({ result }) => { console.log(JSON.stringify(result)) await applyAction(result) } } let expert = $page.url.hash == '#expert' $: compteActive = $page.url.hash == '#compte' ? 'active' : '' $: descriptionActive = $page.url.hash == '#description' ? 'active' : '' $: banqueActive = $page.url.hash == '#banque' ? 'active' : '' $: expertActive = expert && $page.url.hash == '#expert' ? 'active' : '' $: simuActive = (true || !data.guest) && $page.url.hash == '#simu' ? 'active' : '' $: syntheseActive = !compteActive && !banqueActive && !simuActive && !expertActive && !descriptionActive ? 'active' : '' $: compteShow = compteActive ? 'show' : '' $: banqueShow = banqueActive ? 'show' : '' $: descriptionShow = descriptionActive ? 'show' : '' $: simuShow = simuActive ? 'show' : '' $: syntheseShow = syntheseActive ? 'show' : '' $: expertShow = expertActive ? 'show' : '' let N = 30 const arr = 0 let an = new Date().getFullYear() function tarifSurplus_(puissance: number): number { let resultat = p.tarifCRE.prix[0] for (let i = 0; i < p.tarifCRE.limites.length; i++) { if (puissance < p.tarifCRE.limites[i]) { return resultat } else { resultat = p.tarifCRE.prix[i] } } return resultat } $: investissement = p.puissance ? p.puissance * 1000 * p.ratio + p.enedis : 0 $: apport = Math.min(investissement, p.apport) $: primeInvestissement = p.puissance <= 36 ? 0.21 * 1000 * p.puissance : p.puissance <= 100 ? 0.11 * 1000 * p.puissance : 0 $: productionAnnuelle = (i: number) => H.productionAnnuelle(p, i) const surface_kwc = 2.5 let surface = surface_kwc * (p?.puissance || 0) function onTauxChange(ev: Event) { let t = ev.target as HTMLInputElement console.log(ev, t.valueAsNumber) if (t.valueAsNumber > 100) { t.valueAsNumber = 100 } } function onSurfaceChange(ev: Event) { surface = (ev.target as HTMLInputElement).valueAsNumber p.puissance = surface / surface_kwc } function onPuissanceChange(ev: Event) { p.puissance = (ev.target as HTMLInputElement).valueAsNumber surface = p.puissance * surface_kwc } $: autoconso = p.autoconso / 100 $: tarifSurplus = (i: number) => tarifSurplus_(p.puissance) * Math.pow(1 + p.tarifCRE.inflation / 100, i) $: tarifLocal = (i: number) => H.tarifLocal(p, i) $: tarifAllo = (i: number) => p.coutElec * Math.pow(1 + p.inflationElec / 100, i) $: chiffreAffaires = (i: number) => (i == 0 ? primeInvestissement : 0) + productionAnnuelle(i) * (autoconso * tarifLocal(i) + (1 - autoconso) * tarifSurplus(i) + (p.autoprod * tarifAllo(i)) / 100) $: exploitation = (i: number) => p.puissance * p.maintenance * Math.pow(1 + p.inflation / 100, i) $: turpe = (i: number) => (p.puissance * p.turpe + (1 - autoconso) * productionAnnuelle(i) * p.turpeInjection) * Math.pow(1 + p.inflation / 100, i) $: assurance = (i: number) => ((investissement * p.assurance) / 100) * Math.pow(1 + p.inflation / 100, i) $: divers = (i: number) => p.divers * p.puissance * Math.pow(1 + p.inflation / 100, i) $: charges = (i: number) => (investissement && i == 0 ? p.fraisDivers : 0) + exploitation(i) + turpe(i) + assurance(i) + divers(i) $: valeurAjoutee = (i: number) => chiffreAffaires(i) - charges(i) // Impots // Accise: taux dérogatoire d'1€/MWh jusqu'au 31/1/2025 $: accise = (i: number) => productionAnnuelle(i) * 0.001 $: ifer = (_: number) => p.puissance < 100 ? 0 : 3.394 * p.puissance * p.ratioDcAc // Exonération de taxe foncière: https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000044996150 $: taxeFonciere = (_: number) => 0 // Formule compliquée, exonéré sur les cas concrets $: cfe = (_: number) => 0 $: impots = (i: number) => ifer(i) + taxeFonciere(i) + cfe(i) // EBE $: ebe = (i: number) => valeurAjoutee(i) - impots(i) // Déductions fiscales $: amortissement = (i: number) => i < p.dureeAmortissement ? investissement / p.dureeAmortissement : 0 $: provisionOnduleurs = (i: number) => (p.puissance * 1000 * p.provisionOnduleurs * Math.pow(1 + p.inflation / 100, i)) / 10 let remboursementAnnuel: Array<number> = [] let interetsAnnuels: Array<number> = [] $: dette = Math.max(0, investissement - apport) $: interetsDetteSenior = (i: number) => i < interetsAnnuels.length ? interetsAnnuels[i] : 0 $: interetsDSRA = (i: number) => (interetsDetteSenior(i) * p.tauxDSRA) / 100 $: tresorerieDisponibleDetteSenior = (i: number) => ebe(i) - is(i) $: serviceDetteSenior = (i: number) => interetsDSRA(i) + interetsDetteSenior(i) + remboursementAnnuel[i] $: dscr = (i: number) => i < remboursementAnnuel.length ? tresorerieDisponibleDetteSenior(i) / serviceDetteSenior(i) : 0 $: resultatFiscal = (i: number) => ebe(i) - amortissement(i) - provisionOnduleurs(i) - interetsDetteSenior(i) - interetsDSRA(i) $: is = (i: number) => resultatFiscal(i) <= 0 ? 0 : (resultatFiscal(i) * p.tauxIS) / 100 $: resultatNet = (i: number) => resultatFiscal(i) - is(i) let resultatNetData: { id: string; data: { x: number; y: number }[] }[] = [ { id: '', data: [] }, ] $: { resultatNetData[0] = { id: '', data: new Array(N).fill(0).map((_, i) => { return { x: an + i, y: resultatNet(i) } }), } } // Synthèse $: consoMoyenneFoyer = (i: number) => H.consoMoyenneFoyer(p, i) $: coutElec = () => { let cout = 0 for (let i = 0; i < N; i++) { cout += p.coutElec * Math.pow(1 + p.inflationElec / 100, i) * consoMoyenneFoyer(i) } return cout } $: coutElecAuto = () => { let cout = 0 for (let i = 0; i < N; i++) { cout += (tarifLocal(i) * consoMoyenneFoyer(i) * p.autoprod) / 100 + (p.coutElec * Math.pow(1 + p.inflationElec / 100, i) * consoMoyenneFoyer(i) * (100 - p.autoprod)) / 100 } return cout } $: coutElec_ = coutElec() $: coutElecAuto_ = coutElecAuto() function sum(x: (_: number) => number, n: number) { return new Array(n).fill(0).reduce((a, _, i) => a + x(i), 0) } function format(x: number) { let f = 0 let s = '' if (Math.abs(x) <= 1000) { s = ' €' f = x } else if (Math.abs(x) < 1000000) { s = ' k€' f = x / 1000 } else if (Math.abs(x) < 1000000000) { s = ' M€' f = x / 1000000 } else { s = ' mds€' f = x / 1000000000 } return fmt2.format(f) + s } function rentabiliteTaux(resultat: number): string { return ( fmt2.format( investissement ? (resultat * 100) / investissement - 100 : 0 ) + '%' ) } let maxa = -1 / 0 let mina = 1 / 0 let margin = 1 function traceArray(f: (_: number) => number, n: number) { let c = '' let ai = 0 maxa = -1 / 0 mina = 1 / 0 for (let i = 0; i < n; i++) { ai += f(i) maxa = Math.max(maxa, ai) mina = Math.min(mina, ai) c += ` ${c ? 'L' : 'M'} ${i.toFixed(arr)}, ${-ai.toFixed(arr)}` } margin = Math.max(1, (maxa - mina) / 20) return c } $: roiDate = () => { let sum = 0 let lastNeg = 0 for (let i = 0; i < N; i += 1) { sum += resultatNet(i) if (sum < 0) lastNeg = i } lastNeg += 1 if (lastNeg < N) { if (lastNeg <= 1 && sum >= 0) { return `Tout de suite` } else if (lastNeg == 1) { return `1 an` } else { return `${lastNeg} ans` } } else { return 'À la saint Glinglin' } } $: rembPret = (n: number) => { let totalRembAnnuel_n = remboursementAnnuel .slice(0, n) .reduce((a, b) => a + b, 0) let totalInteret_n = sum(interetsDetteSenior, n) let totalRembAnnuel = remboursementAnnuel.reduce((a, b) => a + b, 0) let totalInteret = sum(interetsDetteSenior, n) return fmt2.format( totalRembAnnuel + totalInteret ? ((totalRembAnnuel_n + totalInteret_n) * 100) / (totalRembAnnuel + totalInteret) : 0 ) } $: economieFoyerMois = fmt2.format( coutElec_ ? (coutElec_ - coutElecAuto_) / (coutElec_ / (N * 12)) : 0 ) </script> <div class="container-lg p-3 pb-5 overflow-auto"> <div class="d-flex flex-lg-row flex-column"> <nav class="navbar navbar-expand-lg start-0 w-lg-25"> <button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#menuoff" aria-label="Toggle navigation"> <span class="navbar-toggler-icon" /> </button> <div class="d-lg-none ms-3 me-auto"> {#if data.nom} <h1 class="my-0">{data.nom}</h1> {/if} </div> </nav> <div class="offcanvas-lg offcanvas-start mb-auto" data-bs-scroll="true" tabindex="-1" id="menuoff"> <div class="offcanvas-body off-padding-md"> <div class="p-3 p-lg-0 overflow-auto"> {#if data.nom} <h1>{data.nom}</h1> {/if} <div class="my-3 form-check form-switch"> <input class="form-check-input" type="checkbox" role="switch" id="expert" bind:checked={expert} /> <label class="form-check-label" for="expert" >Niveau expert</label> </div> <h2 class="mt-5">Communauté</h2> <div class="my-3"> <label class="form-label" for="puissance" >Puissance totale (kWc)</label> <input class="form-control form-control-sm" type="number" id="puissance" min="1" max="3000" value={p.puissance} on:change={onPuissanceChange} /> {#if p.puissance > 3000}<span class="text-warning"> <i class="bi bi-exclamation-circle-fill me-2" /> Une communauté d'autoconsommation ne peut dépasser plus de 3MWc. Les résultats obtenus seront donc purement hypothétiques.</span >{/if} </div> <div class="my-3"> <label class="form-label" for="puissance" >Surface totale (m²)</label> <input class="form-control form-control-sm" type="number" id="surface" min="1" value={surface} on:change={onSurfaceChange} /> </div> <div class="mb-3"> <label class="form-label" for="autoconso" >Taux d'autoconsommation (%)<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Pourcentage de la production consommée localement, à valider plus finement dans l'onglet Simulation. C'est la valeur que l'application Coturnix optimise." ><i class="bi bi-question-circle" /></button> </label> <div class="d-flex"> <input class="form-range me-4" type="range" id="autoconso" bind:value={p.autoconso} />{p.autoconso}% </div> </div> <div class="mb-3"> <label class="form-label" for="autoprod" >Taux d'autoproduction (%)<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Pourcentage des consommations venant de sources locales, à valider plus finement dans l'onglet Simulation." ><i class="bi bi-question-circle" /></button ></label> <div class="d-flex"> <input class="form-range me-4" type="range" id="autoprod" bind:value={p.autoprod} />{p.autoprod}% </div> </div> <div class="mb-3"> <label class="form-label" for="prix" >Prix d'achat fournisseur (€/kWh)<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Ce prix dépend du contrat de fourniture d'électricité du producteur." ><i class="bi bi-question-circle" /></button ></label> <input class="form-control form-control-sm" type="number" step="0.01" min="0" id="coutElec" bind:value={p.coutElec} /> </div> <div class="mb-3"> <label class="form-label" for="prix" >Prix de vente local (€/kWh)<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Dans Coturnix, chaque producteur choisit son prix, et Coturnix propose le moins cher au consommateur." ><i class="bi bi-question-circle" /></button ></label> <input class="form-control form-control-sm" type="number" step="0.01" min="0" id="prix" bind:value={p.prix} /> </div> <div class="mb-3"> <label class="form-label" for="inflationElec" >Inflation nationale de l'électricité (%)</label> <input class="form-control form-control-sm" type="number" bind:value={p.inflationElec} min="0" id="inflationElec" /> </div> <div class="mb-3"> <label class="form-label" for="echeances" >Durée d'analyse (an)</label> <input class="form-control form-control-sm" type="number" id="echeances" min="0" bind:value={N} /> </div> <h2 class="mt-5">Financement</h2> <div class="my-3"> <label class="form-label" for="apport" >Apport initial (€)</label> <input class="form-control form-control-sm" type="number" id="apport" min="0" step="1000" bind:value={p.apport} /> </div> <div class="mb-3"> <label class="form-label" for="interet" >Taux d'intérêt(%)</label> <input class="form-control form-control-sm" type="number" step="0.1" id="interet" min="0" max="100" on:change={onTauxChange} on:keyup={onTauxChange} bind:value={p.interet} /> </div> <div class="mb-3"> <label class="form-label" for="echeances" >Nombre d'échéances (en mois)</label> <input class="form-control form-control-sm" type="number" id="echeances" min="0" bind:value={p.echeances} /> </div> {#if data.id} <form id="delForm" method="POST" action="?/del" /> <button class="my-2 me-2 btn btn-primary" on:click={saveModal}>Sauvegarder</button> <button class="my-2 btn btn-outline-primary" form="delForm">Supprimer</button> {:else if data.email} <button class="my-2 btn btn-primary" on:click={saveModal}>Sauvegarder</button> {/if} </div> </div> </div> <div class="ps-lg-5 w-100 ms-0 z0"> <ul class="mt-5 mt-lg-3 nav nav-tabs" id="tabs" role="tablist"> <li class="nav-item" role="presentation"> <a href="#synthese" class="nav-link {syntheseActive}" id="synthese-tab" type="button" role="tab" aria-controls="synthese-tab-pane" aria-selected="true">Synthèse</a> </li> <li class="nav-item" role="presentation"> <a href="#compte" class="nav-link {compteActive}" id="profile-tab" type="button" role="tab" aria-controls="compte-tab-pane" aria-selected="false">Détails financiers</a> </li> <li class="nav-item" role="presentation"> <a href="#description" class="nav-link {descriptionActive}" id="profile-tab" type="button" role="tab" aria-controls="description-tab-pane" aria-selected="false">Description</a> </li> <li class="nav-item" role="presentation"> <a href="#banque" class="nav-link {banqueActive}" id="banque-tab" type="button" role="tab" aria-controls="banque-tab-pane" aria-selected="false">Emprunt</a> </li> {#if expert} <li class="nav-item" role="presentation"> <a href="#expert" class="nav-link {expertActive}" id="expert-tab" type="button" role="tab" aria-controls="expert-tab-pane" aria-selected="false">Mode expert</a> </li> {/if} {#if true || !data.guest} <li class="nav-item" role="presentation"> <a href="#simu" class="nav-link {simuActive}" id="prm-tab" type="button" role="tab" aria-controls="prm-tab-pane" aria-selected="false">Simulation</a> </li> {/if} </ul> <div class="tab-content" id="syntheseTabContent"> <div class="tab-pane fade {syntheseActive} {syntheseShow} p-3" id="synthese-tab-pane" role="tabpanel" aria-labelledby="synthese-tab" tabindex="0"> <h3 class="my-3 text-body fs-1 fw-bold"> En un regard, <span class="text-primary">tout</span> votre projet. </h3> <div class="my-5 py-2 total"> <h5>Synthèse durée totale {N} ans</h5> <div class="row px-4"> <div class="d-flex justify-content-between p-5"> <div class="flex-column text-center"> <div class="donnee"> {format(sum(chiffreAffaires, N))} </div> <div>Chiffre d'affaire</div> </div> <div class="mx-3 flex-column text-center"> <div class="donnee"> {format(sum(charges, N))} </div> <div>Charges d'exploitation</div> </div> <div class="flex-column text-center"> <div class="donnee"> {format(sum(resultatNet, N))} </div> <div>Résultat net</div> </div> </div> </div> <div class="row px-4"> <div class="d-flex justify-content-around p-5"> <div class="me-3 flex-column text-center"> <div class="donnee"> {rentabiliteTaux(sum(resultatNet, N))} </div> <div>Retour sur investissement</div> </div> <div class="ms-3 flex-column text-center"> <div class="donnee">{roiDate()}</div> <div>Point de rentabilité</div> </div> </div> </div> <div class="row flex-row-reverse px-4"> <div class="d-flex justify-content-around p-5"> <div class="d-flex flex-column justify-content-between text-center me-3"> <div class="py-3 my-auto"> <svg viewBox="{Math.floor( -N * 0.05 )} {Math.floor( -maxa - margin )} {Math.ceil(N * 1.1)} {Math.ceil( maxa - mina + 2 * margin )}" xmlns="http://www.w3.org/2000/svg" width="250px" height="100px" preserveAspectRatio="none"> <path d={traceArray(resultatNet, N)} fill="transparent" stroke-width="2" vector-effect="non-scaling-stroke" stroke="#b71515" /> </svg> </div> <div>Résultat net</div> </div> <div class="d-flex flex-column justify-content-between text-center ms-3"> <div style="width:250px" class="py-3 my-auto"> <div class="spinner-border ms-3 loading" class:d-none={!pvgis_loading} /> <svg class:d-none={pvgis_loading || !maxProd} viewBox="-10 0 270 {maxProd}" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" width="250px" height="100px"> {#each prodWeekly as p, i} <path d="M {i * 5}, {maxProd} L {i * 5}, {maxProd - p}" fill="transparent" stroke-width="2" stroke="#ffbf00" /> {/each} </svg> <svg class="donnee" class:d-none={pvgis_loading || maxProd} xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 16 16"> <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278" /> </svg> </div> <div>Production annuelle</div> </div> </div> </div> <div class="row flex-row-reverse px-4"> <div class="d-flex justify-content-around p-5"> <div class="me-3 flex-column text-center"> <div class="donnee"> {N ? format(sum(resultatNet, N) / N) : '0€'} </div> <div>Bénéfice annuel moyen</div> </div> <div class="ms-3 flex-column text-center"> <div class="donnee"> {#if coutElec() - coutElecAuto() > 0} {format( coutElec() - coutElecAuto() )} ({economieFoyerMois} mois) {:else} Aucune 😢 {/if} </div> <div>Economies consommateur</div> </div> </div> </div> </div> <div class="my-5 py-2 dix"> <h5>Synthèse 10 ans</h5> <div class="row flex-row-reverse px-4"> <div class="d-flex justify-content-between p-5"> <div class="flex-column text-center"> <div class="donnee"> {format(sum(chiffreAffaires, 10))} </div> <div>Chiffre d'affaire</div> </div> <div class="mx-3 flex-column text-center"> <div class="donnee"> {format(sum(charges, 10))} </div> <div>Coût total</div> </div> <div class="flex-column text-center"> <div class="donnee"> {format(sum(resultatNet, 10))} </div> <div>Résultat net</div> </div> </div> </div> <div class="row flex-row-reverse px-4 dix"> <div class="d-flex justify-content-around p-5"> <div class="flex-column justify-content-between text-center"> <div class="donnee">{rembPret(10)}</div> <div>Prêt remboursé</div> </div> <div class="flex-column justify-content-between text-center"> <div class="donnee"> {rentabiliteTaux(sum(resultatNet, 10))} </div> <div>Rentabilité</div> </div> </div> </div> </div> <div class="my-5 py-2 cinq"> <h5>Synthèse 5 ans</h5> <div class="row flex-row-reverse px-4"> <div class="d-flex justify-content-between p-5"> <div class="flex-column text-center"> <div class="donnee"> {format(sum(chiffreAffaires, 5))} </div> <div>Chiffre d'affaire</div> </div> <div class="mx-3 flex-column text-center"> <div class="donnee"> {format(sum(charges, 5))} </div> <div>Coût total</div> </div> <div class="flex-column text-center"> <div class="donnee"> {format(sum(resultatNet, 5))} </div> <div>Résultat net</div> </div> </div> </div> <div class="row flex-row-reverse px-4"> <div class="d-flex justify-content-around p-5"> <div class="flex-column text-center"> <div class="donnee">{rembPret(5)}</div> <div>Prêt remboursé</div> </div> <div class="flex-column text-center"> <div class="donnee"> {rentabiliteTaux(sum(resultatNet, 5))} </div> <div>Rentabilité</div> </div> </div> </div> </div> <div class="my-5 py-2 moyenne"> <h5>Synthèse année moyenne</h5> <div class="row flex-row flex-wrap px-4"> <div class="d-flex justify-content-between py-5"> <div class="flex-column text-center mx-3"> <div class="donnee"> {format(sum(resultatNet, N) / N)} </div> <div>Bénéfices moyen</div> </div> <div class="flex-column text-center mx-3"> <div class="donnee"> {format(sum(chiffreAffaires, N) / N)} </div> <div>Chiffre d'affaire</div> </div> <div class="flex-column text-center mx-3"> <div class="donnee"> {fmt2.format( (investissement ? (sum(resultatNet, N) * 100) / investissement - 100 : 0) / N )}% </div> <div>Rentabilité annuelle</div> </div> <div class="flex-column text-center mx-3"> <div class="donnee"> {#if coutElec() - coutElecAuto() > 0} {fmt0.format( ((coutElec() - coutElecAuto()) * 100) / coutElec() )}% {:else} Aucune 😢 {/if} </div> <div>Économies consommateur</div> </div> </div> </div> </div> </div> <div class="tab-pane fade {compteActive} {compteShow} p-3" id="compte-tab-pane" role="tabpanel" aria-labelledby="compte-tab" tabindex="0"> <h3 class="my-3 fs-1 fw-bold"> <span class="text-primary">Projetez-vous</span> dans votre projet. </h3> Durée d'analyse : {N} ans <h4 class="my-3 mt-5">Résultat</h4> <div class="ms-3 row"> <h5 class="mt-5">Résultat net</h5> <div class="fw-bold fs-4"> Cumulé : <span class="text-primary" >{format(sum(resultatNet, N))}</span> </div> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-3"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt"> Résultat net </div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-3"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {resultatNet(i).toFixed(arr)} </div> </div> {/each} </div> </div> <h4 class="my-3">Détail</h4> <div class="ms-3"> <h5 class="mt-5">Chiffre d'affaires</h5> <div class="fw-bold fs-4"> Cumulé : <span class="text-primary" >{format(sum(chiffreAffaires, N))}</span> </div> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-4"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt"> Autoproduction (€) </div> <div class="titreligne tcellt"> Vente locale (€) </div> <div class="titreligne tcellt"> Vente de surplus (€) </div> <div class="titreligne tcellt">Prime</div> <div class="titreligne tcellt"> Chiffre d'affaires </div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-4"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {( productionAnnuelle(i) * ((p.autoprod * tarifAllo(i)) / 100) ).toFixed(0)} </div> <div class="tcellt"> {( productionAnnuelle(i) * (autoconso * tarifLocal(i)) ).toFixed(0)} </div> <div class="tcellt"> {( productionAnnuelle(i) * ((1 - autoconso) * tarifSurplus(i)) ).toFixed(0)} </div> <div class="tcellt"> </div> <div class="tcellt"> {chiffreAffaires(i).toFixed(arr)} </div> </div> {/each} </div> <h5 class="my-3">Charges d'exploitation</h5> <div class="fw-bold fs-4"> Cumulé : <span class="text-primary" >{format(sum(charges, N))}</span> </div> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-4"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt"> Maintenance (€) </div> <div class="titreligne tcellt">TURPE (€)</div> <div class="titreligne tcellt"> Assurance (€) </div> <div class="titreligne tcellt">Divers</div> <div class="titreligne tcellt"> Total charges </div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-4"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {exploitation(i).toFixed(arr)} </div> <div class="tcellt"> {turpe(i).toFixed(arr)} </div> <div class="tcellt"> {assurance(i).toFixed(arr)} </div> <div class="tcellt"> {divers(i).toFixed(arr)} </div> <div class="tcellt"> {charges(i).toFixed(arr)} </div> </div> {/each} </div> <h5 class="my-3">Excédent brut d'exploitation</h5> <div class="fw-bold fs-4"> Cumulé : <span class="text-primary" >{format(sum(ebe, N))}</span> </div> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-4"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt"> Valeur ajoutée (€) </div> <div class="titreligne tcellt">Accise (€)</div> <div class="titreligne tcellt">IFER (€)</div> <div class="titreligne tcellt"> Taxe foncière </div> <div class="titreligne tcellt"> Cotisation foncière des entreprises </div> <div class="titreligne tcellt"> Excédent brut d'exploitation </div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-4"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {valeurAjoutee(i).toFixed(arr)} </div> <div class="tcellt"> {accise(i).toFixed(arr)} </div> <div class="tcellt"> {ifer(i).toFixed(arr)} </div> <div class="tcellt"> {taxeFonciere(i).toFixed(arr)} </div> <div class="tcellt"> {cfe(i).toFixed(arr)} </div> <div class="tcellt"> {ebe(i).toFixed(arr)} </div> </div> {/each} </div> <h5 class="my-3">Résultat net</h5> <div class="fw-bold fs-4"> Cumulé : <span class="text-primary" >{format(sum(resultatNet, N))}</span> </div> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-4"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt">Onduleurs</div> <div class="titreligne tcellt"> Amortissement </div> <div class="titreligne tcellt"> Intérêts dette senior </div> <div class="titreligne tcellt"> Intérêts DSRA </div> <div class="titreligne tcellt"> Résultat fiscal </div> <div class="titreligne tcellt"> Impôt sur les sociétés </div> <div class="titreligne tcellt"> Résultat net </div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-4"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {provisionOnduleurs(i).toFixed(arr)} </div> <div class="tcellt"> {amortissement(i).toFixed(arr)} </div> <div class="tcellt"> {interetsDetteSenior(i).toFixed(arr)} </div> <div class="tcellt"> {interetsDSRA(i).toFixed(arr)} </div> <div class="tcellt"> {resultatFiscal(i).toFixed(arr)} </div> <div class="tcellt"> {is(i).toFixed(arr)} </div> <div class="tcellt"> {resultatNet(i).toFixed(arr)} </div> </div> {/each} </div> {#if investissement > apport} <h5 class="my-3">Couverture de la dette</h5> <div class="d-flex flex-wrap"> <div class="d-flex flex-column my-3"> <div class="titreligne tcellt">Année</div> <div class="titreligne tcellt">DSCR</div> </div> {#each Array(Math.ceil(N)) as _, i} <div class="d-flex flex-column my-3"> <div class="tcellt">{an + i}</div> <div class="tcellt"> {fmt2.format(dscr(i))} </div> </div> {/each} </div> {/if} </div> </div> <div class="tab-pane fade {descriptionActive} {descriptionShow} p-3" id="description-tab-pane" role="tabpanel" aria-labelledby="description-tab" tabindex="0"> <h3 class="my-3 fs-1 fw-bold"> Quelques <span class="text-primary">détails</span> de plus. </h3> {#if descriptionActive} <Description bind:position={centrale.position} bind:azimuth={centrale.azimuth} bind:inclinaison={centrale.inclinaison} /> {/if} </div> <div class="tab-pane fade {banqueActive} {banqueShow} p-3" id="banque-tab-pane" role="tabpanel" aria-labelledby="banque-tab" tabindex="0"> <h3 class="my-3 fs-1 fw-bold"> Tous <span class="text-primary">les recoins</span> de votre emprunt. </h3> <Banque {dette} taux={p.interet / 100} duree={p.echeances} bind:interetsAnnuels bind:remboursementAnnuel /> </div> {#if expert} <div class="tab-pane fade {expertActive} {expertShow} py-3" id="expert-tab-pane" role="tabpanel" aria-labelledby="expert-tab" tabindex="0"> <div style="max-width:300px"> <div class="mb-3"> <label class="form-label" for="enedis" >Coût de raccordement Enedis</label> <input class="form-control form-control-sm" type="number" id="enedis" min="0" bind:value={p.enedis} /> </div> <div class="mb-3"> <label class="form-label" for="productible" >Productible annuel (kWh/kWc)</label> <input class="form-control form-control-sm" type="number" id="productible" min="0" bind:value={p.productible} /> </div> <div class="mb-3"> <label class="form-label" for="ratio" >Ratio installation (€/W)</label> <input class="form-control form-control-sm" type="number" id="ratio" min="0" bind:value={p.ratio} /> </div> <div class="mb-3"> <label class="form-label" for="degradation" >Taux de dégradation PV(%)</label> <input class="form-control form-control-sm" type="number" id="degradation" min="0" bind:value={p.degradation} /> </div> <div class="mb-3"> <label class="form-label" for="inflation" >Inflation tarif local</label> <input class="form-control form-control-sm" type="number" id="inflation" min="0" bind:value={p.inflation} /> </div> </div> </div> {/if} </div> <div class="tab-content" id="simuTabContent"> <div class="tab-pane fade {simuActive} {simuShow} show p-3" id="simu-tab-pane" role="tabpanel" aria-labelledby="simu-tab" tabindex="0"> <h4 class="my-3">Simulation</h4> <Simulation id={data.id} {server} {assets} {prmsFeature} position={centrale.position} azimuth={centrale.azimuth} inclinaison={centrale.inclinaison} bind:prms={data.prms} puissance={p.puissance} bind:prodWeekly bind:maxProd bind:pvgis_loading /> </div> </div> </div> </div> <div class="modal" id="grainSaveModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <h5 class="modal-title pb-3" id="saveModalLabel"> Sauvegarder </h5> <form method="POST" id="saveForm" action="?/save" use:enhance={save}> <div class="mb-3"> <label class="form-label" for="nom" >Nom du projet</label> <input class="form-control" type="text" id="nom" name="nom" bind:value={data.nom} required /> </div> <div class="mb-3"> <label class="form-label" for="porteur" >Prénom et nom du porteur du projet</label> <input class="form-control" type="text" id="porteur" name="porteur" bind:value={data.porteur} /> </div> <div class="mb-3"> <label class="form-label" for="adresse" >Adresse</label> <input class="form-control" type="text" id="adresse" name="adresse" bind:value={data.adresse} /> </div> <div class="mb-3"> <label class="form-label" for="tel" >Téléphone</label> <input class="form-control" type="text" id="tel" name="tel" bind:value={data.tel} /> </div> <div class="mb-3"> <label class="form-label" for="photo">Photo</label> <input class="form-control" type="file" id="photo" name="photo" /> </div> </form> </div> <div class="modal-footer"> <button form="saveForm" class="btn btn-primary" >Sauvegarder</button> <button type="button" class="btn btn-outline-primary" data-bs-dismiss="modal">Annuler</button> </div> </div> </div> </div> </div> <style lang="scss"> @media (prefers-color-scheme: light) { nav { background-color: #fff !important; } } .z0 { z-index: 0; } .loading { color: #ffbf00; } .offcanvas-body h1 { font-size: 1.5em; } .offcanvas-body h2 { font-size: 1.2em; } h5 { font-size: 1.5rem; font-weight: bold; } h4 { font-size: 2rem; font-weight: bold; } .titreligne { font-size: 1rem; font-weight: bold; } #syntheseTabContent h5 { border-bottom: 3px solid; font-size: 1.75rem; padding-bottom: 5px; } .donnee { font-size: 2rem; font-weight: bold; } .total .donnee { color: $gp-1; } .total h5 { color: $gp-1; border-color: $gp-1; } .dix .donnee { color: $gp-2; } .dix h5 { color: $gp-2; border-color: $gp-2; } .cinq .donnee { color: $gp-3; } .cinq h5 { color: $gp-3; border-color: $gp-3; } .moyenne .donnee { color: $gs-1; } .moyenne h5 { color: $gs-1; border-color: $gs-1; } .tcellt { border-bottom: 1px solid $primary; } .tcellt:last-child { border-bottom: unset; } .tcellt { padding: 8px 10px; } input.prix::-webkit-outer-spin-button, input.prix::-webkit-inner-spin-button { /* display: none; <- Crashes Chrome on hover */ -webkit-appearance: none; margin: 0; /* <-- Apparently some margin are still there even though it's hidden */ } input.prix[type='number'] { -moz-appearance: textfield; /* Firefox */ } </style>