interne-utils/src/routes/SalaireInterne.svelte

397 lines
13 KiB
Svelte

<script lang="ts">
import { RadioGroup, RadioItem, SlideToggle } from '@skeletonlabs/skeleton';
import { monthPicker } from 'svelte-flatpickr-plus';
import type { Options } from 'svelte-flatpickr-plus';
import { French } from 'flatpickr_plus/dist/l10n/fr.js';
import PlusMoins from './PlusMoins.svelte';
import { recupBasesLegales } from './basesLegales';
import LigneSalaire from './LigneSalaire.svelte';
let anneeInternat: string = '1re';
let isLogé: boolean = false;
let isNourri: boolean = true;
let medGeOuSpe: string = 'spe';
let aPrimeSaspas: boolean = false;
let aIndemnite15km: boolean = false;
let aIndemniteZipZac: boolean = false;
let estUltramarin: boolean = false;
let nbDemiGardesSem: number = 0;
let nbDemiGardesWE: number = 0;
let nbGardesSem: number = 0;
let nbGardesWE: number = 0;
let nbAstreintes: number = 0;
let nbDeplacements: number = 0;
let dateCourante: Date = new Date();
$: bases = recupBasesLegales(dateCourante);
// Options pour le monthpicker flatpicker-plus
const mp_options: Options = {
altInput: true,
altFormat: 'M Y',
defaultDate: Date.now(),
locale: French,
minDate: '2020-01-01',
maxDate: new Date(),
onChange: (dates: Date[], currentDateString: string, self: any, data?: any) => {
dateCourante = dates[0];
}
};
const annees = ['FFI', '1re', '2e', '3e', '4e', '5e', 'DJ1', 'DJ2'];
// Salaire de base et idemnités
let base: number = 0;
let indemniteSujetion: number = 0;
let indemniteSujetionLabel: string = '';
let indemniteSujetionRetraite: number = 0;
$: switch (anneeInternat) {
case 'FFI':
base = parseFloat(bases.baseFFI) / 12;
indemniteSujetion = parseFloat(bases.indemniteSujetion);
indemniteSujetionLabel = 'Indemnité de sujetion';
indemniteSujetionRetraite = 0;
break;
case '1re':
base = parseFloat(bases.base1ere) / 12;
indemniteSujetion = parseFloat(bases.indemniteSujetion);
indemniteSujetionLabel = 'Indemnité de sujetion';
indemniteSujetionRetraite = 0;
break;
case '2e':
base = parseFloat(bases.base2e) / 12;
indemniteSujetion = parseFloat(bases.indemniteSujetion);
indemniteSujetionLabel = 'Indemnité de sujetion';
indemniteSujetionRetraite = 0;
break;
case '3e':
base = parseFloat(bases.base3e) / 12;
indemniteSujetion = 0;
indemniteSujetionLabel = '';
indemniteSujetionRetraite = 0;
break;
case '4e':
base = parseFloat(bases.base4e) / 12;
indemniteSujetion = parseFloat(bases.primeResp4eme) / 12;
indemniteSujetionLabel = 'Prime de responsabilité';
indemniteSujetionRetraite = indemniteSujetion;
break;
case '5e':
base = parseFloat(bases.base5e) / 12;
indemniteSujetion = parseFloat(bases.primeResp5eme) / 12;
indemniteSujetionLabel = 'Prime de responsabilité';
indemniteSujetionRetraite = indemniteSujetion;
break;
case 'DJ1':
base = parseFloat(bases.baseDJ1) / 12;
indemniteSujetion = parseFloat(bases.primeDJ1re) / 12;
indemniteSujetionLabel = "Prime d'autonomie supervisée";
indemniteSujetionRetraite = indemniteSujetion;
break;
case 'DJ2':
base = parseFloat(bases.baseDJ2) / 12;
indemniteSujetion = parseFloat(bases.primeDJ2eme) / 12;
indemniteSujetionLabel = "Prime d'autonomie supervisée";
indemniteSujetionRetraite = indemniteSujetion;
break;
default:
console.error("Année d'internat mal choisie");
}
// Prime logé - nourri
let primeLogeNourri: number = 0;
$: if (!isNourri) {
if (!isLogé) {
primeLogeNourri = parseFloat(bases.primeNonLogeNonNourri) / 12;
} else {
primeLogeNourri = parseFloat(bases.primeLogeNonNourri) / 12;
}
} else {
if (!isLogé) {
primeLogeNourri = parseFloat(bases.primeNonLogeNourri) / 12;
} else {
primeLogeNourri = 0;
}
}
// Prime SASPAS et indemnité transport et indemnité d'hébergement
$: primeSASPAS = medGeOuSpe === 'mg' && aPrimeSaspas ? parseFloat(bases.primeSASPAS) : 0;
$: indemniteTransport =
medGeOuSpe === 'mg' && aIndemnite15km ? parseFloat(bases.indemniteDeplacement) : 0;
$: indemniteHebergement =
medGeOuSpe === 'mg' && aIndemniteZipZac ? parseFloat(bases.indemniteHebergement) : 0;
// Prime Outremer
$: tauxPrimeOutremer = estUltramarin ? parseFloat(bases.tauxPrimeOutremer) : 0;
// Gardes et astreintes
$: valDemiGardeSem = parseFloat(bases.demigardeSem);
$: valDemiGardeWE = parseFloat(bases.demigardeWE);
$: valGardeSem = parseFloat(bases.gardeSem); // Pas de gestion des gardes au dela du service normal pour le moment
$: valGardeWE = parseFloat(bases.gardeWE); // Pas de gestion des gardes au dela du service normal pour le moment
$: valAstreinte = parseFloat(bases.astreinte);
$: valDeplacement = parseFloat(bases.deplaAstreinte);
// == TOTAL BRUT ==
$: totalBrut =
base +
indemniteSujetion +
primeLogeNourri +
primeSASPAS +
indemniteTransport +
indemniteHebergement +
(tauxPrimeOutremer * base) / 100 +
nbDemiGardesSem * valDemiGardeSem +
nbDemiGardesWE * valDemiGardeWE +
nbGardesSem * valGardeSem +
nbGardesWE * valGardeWE +
nbAstreintes * valAstreinte +
nbDeplacements * valDeplacement;
// Contribution sociale généralisée (CSG) et remboursement de la dette sociale (CRDS)
$: baseCsgRds = totalBrut * parseFloat(bases.assietteCSGCRDS);
$: tauxCSGDeductible = parseFloat(bases.tauxCSGDeductible);
$: tauxCSGNonDeductible = parseFloat(bases.tauxCSGNonDeductible);
$: tauxCRDS = parseFloat(bases.tauxCRDS);
// Retraite IRCANTEC
$: baseRetraite =
((base + indemniteSujetionRetraite + indemniteTransport + indemniteHebergement + primeSASPAS) *
2) /
3;
$: plafondSS = parseFloat(bases.plafondSS);
$: baseIrcantecA = baseRetraite > plafondSS ? plafondSS : baseRetraite;
$: baseIrcantecB = baseRetraite > plafondSS ? baseRetraite - plafondSS : 0;
$: tauxIrcantecA = parseFloat(bases.IRCANTECTrA);
$: tauxIrcantecB = parseFloat(bases.IRCANTECTrB);
// Sécurité sociale
$: basePlafonnee = totalBrut > plafondSS ? plafondSS : totalBrut;
$: tauxVieillessePlafonee = parseFloat(bases.tauxAssuranceViellessePlafonee);
$: tauxVieillesseDeplafonee = parseFloat(bases.tauxAssuranceViellesseDeplaf);
// == TOTAL NET AVANT IMPOTS ==
$: totalNetFiscal =
totalBrut -
(baseCsgRds * tauxCSGDeductible) / 100 -
(baseIrcantecA * tauxIrcantecA) / 100 -
(baseIrcantecB * tauxIrcantecB) / 100 -
(totalBrut * tauxVieillesseDeplafonee) / 100 -
(basePlafonnee * tauxVieillessePlafonee) / 100;
$: totalNetAvantImpots =
totalNetFiscal - (baseCsgRds * tauxCSGNonDeductible) / 100 - (baseCsgRds * tauxCRDS) / 100;
// Impots à la source
let tauxSource: number = 0;
$: totalNet = totalNetAvantImpots - (totalNetFiscal * tauxSource) / 100;
</script>
{@debug base, totalBrut, totalNet}
<p>
Bienvenue sur le simulateur de bulletin de salaire d'interne ! Utilisez-le pour vérifier vos bulletins,
faire une prévision de vos revenus, où juste pour rêver...
</p>
<p>
Le simulateur comprend les valeurs historiques des taux et montants de base jusqu'en janvier 2020.
Pour remonter à des dates antérieures, utilisez le simulateur de salaire de doc (qui a été une grande inspiration !).
N'hésitez pas à faire remonter des difficultés ou des bugs par mail (cf. en haut à droite).
</p>
<hr />
<!-- ENTREES -->
<div class="gap-3 flex flex-wrap md:grid md:grid-cols-5 md:items-start">
<div>
<label class="label pl-3 py-1" for="dateSalaire"><span>Date d'effet</span></label>
<input class="input cursor-pointer" id="dateSalaire" use:monthPicker={mp_options} readonly />
</div>
<div class="col-span-3 overflow-x-auto flex flex-col text-center">
<label class="label me-80 pl-3 py-1" for="anneeInternat"><span>Année</span></label>
<RadioGroup id="anneeInternat" display="inline-flex mx-auto" padding="px-3 py-1">
{#each annees as a}
<RadioItem bind:group={anneeInternat} name="anneeInternat" value={a}>{a}</RadioItem>
{/each}
</RadioGroup>
</div>
<div class="flex flex-col gap-1">
<SlideToggle name="slider-logé" bind:checked={isLogé}>Logé</SlideToggle>
<SlideToggle name="slider-nourri" bind:checked={isNourri}>Nourri</SlideToggle>
<SlideToggle name="slider-outremer" bind:checked={estUltramarin}>Outremer</SlideToggle>
</div>
<div class="col-span-2 flex flex-col gap-1">
<div class="grow pb-2 flex justify-left">
<RadioGroup id="medGeOuSpe">
<RadioItem bind:group={medGeOuSpe} name="medGeOuSpe" value="mg">Med Gé</RadioItem>
<RadioItem bind:group={medGeOuSpe} name="medGeOuSpe" value="spe">Spé</RadioItem>
</RadioGroup>
</div>
<div class:invisible={medGeOuSpe === 'spe'} class="flex justify-left pl-3">
<SlideToggle name="slider-saspas" bind:checked={aPrimeSaspas}>SASPAS</SlideToggle>
</div>
<div class:invisible={medGeOuSpe === 'spe'} class="flex justify-left pl-3">
<SlideToggle name="slider-15km" bind:checked={aIndemnite15km}>Stage &gt; 15 km</SlideToggle>
</div>
<div class:invisible={medGeOuSpe === 'spe'} class="flex justify-left pl-3">
<SlideToggle name="slider-zipzac" bind:checked={aIndemniteZipZac}
>Indemnité hébergement</SlideToggle
>
</div>
</div>
<div class="flex flex-col gap-2">
<PlusMoins bind:compteur={nbDemiGardesSem}>Demi-gardes sem.</PlusMoins>
<PlusMoins bind:compteur={nbDemiGardesWE}>Demi-gardes WE</PlusMoins>
</div>
<div class="flex flex-col gap-2">
<PlusMoins bind:compteur={nbGardesSem}>Gardes semaine</PlusMoins>
<PlusMoins bind:compteur={nbGardesWE}>Gardes WE & JF (12h)</PlusMoins>
</div>
<div class="flex flex-col gap-2">
<PlusMoins bind:compteur={nbAstreintes}>Astreintes (12h)</PlusMoins>
<PlusMoins bind:compteur={nbDeplacements}>Déplacements</PlusMoins>
</div>
<div class="grow md:col-span-5 flex flex-col md:flex-row">
<span>Taux de prélèvement à la source : {tauxSource} %</span>
<input class="py-1" type="range" bind:value={tauxSource} max="20" step="0.5" />
</div>
</div>
<!-- BULLETIN DE SALAIRE -->
<div class="ficheSalaire flex flex-col bg-surface-700 font-mono divide-y divide-gray-500">
<div class="grow flex text-center bg-surface-600 px-1 font-bold">
<div class="grow text-left">&nbsp;Description</div>
<div class="flex-none w-24">Base</div>
<div class="flex-none w-16">Nb/Taux</div>
<div class="flex-none w-24">Total</div>
</div>
<LigneSalaire type="brut" label="Traitement de base" {base} />
<LigneSalaire type="taux" label="Prime outremer" opt={true} {base} taux={tauxPrimeOutremer} />
<LigneSalaire type="brut" label={indemniteSujetionLabel} opt={true} base={indemniteSujetion} />
<LigneSalaire type="brut" label="Logé / Nourri" opt={true} base={primeLogeNourri} />
<LigneSalaire type="brut" label="Prime de responsabilité SASPAS" opt={true} base={primeSASPAS} />
<LigneSalaire
type="brut"
label="Indemnité de transport > 15 km"
opt={true}
base={indemniteTransport}
/>
<LigneSalaire
type="brut"
label="Indemnité d'hébergement (ZIP/ZAC)"
opt={true}
base={indemniteHebergement}
/>
<LigneSalaire
type="nombre"
label="Demi-garde semaine"
opt={true}
base={valDemiGardeSem}
nombre={nbDemiGardesSem}
/>
<LigneSalaire
type="nombre"
label="Demi-garde WE & JF"
opt={true}
base={valDemiGardeWE}
nombre={nbDemiGardesWE}
/>
<LigneSalaire
type="nombre"
label="Garde de semaine (12h)"
opt={true}
base={valGardeSem}
nombre={nbGardesSem}
/>
<LigneSalaire
type="nombre"
label="Garde de week-end (12h)"
opt={true}
base={valGardeWE}
nombre={nbGardesWE}
/>
<LigneSalaire
type="nombre"
label="Astreinte"
opt={true}
base={valAstreinte}
nombre={nbAstreintes}
/>
<LigneSalaire
type="nombre"
label="Déplacement sur astreinte"
opt={true}
base={valDeplacement}
nombre={nbDeplacements}
/>
<LigneSalaire type="brut" emphase={true} label="&nbsp;TOTAL BRUT" base={totalBrut} />
<LigneSalaire type="taux" label="CSG déductible" base={baseCsgRds} taux={-tauxCSGDeductible} />
<LigneSalaire
type="taux"
label="CSG non déductible"
base={baseCsgRds}
taux={-tauxCSGNonDeductible}
/>
<LigneSalaire type="taux" label="C(RDS)" base={baseCsgRds} taux={-tauxCRDS} />
<LigneSalaire type="taux" label="IRCANTEC tranche A" base={baseIrcantecA} taux={-tauxIrcantecA} />
<LigneSalaire
type="taux"
label="IRCANTEC tranche B"
opt={true}
base={baseIrcantecB}
taux={-tauxIrcantecB}
/>
<LigneSalaire
type="taux"
label="S.S Vieillesse déplafonnée"
base={baseRetraite}
taux={-tauxVieillesseDeplafonee}
/>
<LigneSalaire
type="taux"
label="S.S Vieillesse plafonnée"
base={basePlafonnee}
taux={-tauxVieillessePlafonee}
/>
{#if tauxSource}
<LigneSalaire
type="brut"
emphase={true}
label="&nbsp;TOTAL NET AVANT IMPOTS"
base={totalNetAvantImpots}
/>
{/if}
<LigneSalaire
type="taux"
label="Prélèvement à la source"
opt={true}
base={totalNetFiscal}
taux={-tauxSource}
/>
<LigneSalaire type="brut" emphase={true} label="&nbsp;TOTAL NET" base={totalNet} />
</div>
<style>
.cursor-pointer {
cursor: pointer !important;
}
</style>