mon-entreprise/source/components/SchemeComparaison.js

660 lines
18 KiB
JavaScript
Raw Normal View History

import { setSituationBranch } from 'Actions/actions'
import {
defineDirectorStatus,
isAutoentrepreneur
} from 'Actions/companyStatusActions'
import classnames from 'classnames'
2019-05-09 16:40:14 +00:00
import { T } from 'Components'
import Conversation from 'Components/conversation/Conversation'
import SeeAnswersButton from 'Components/conversation/SeeAnswersButton'
import PeriodSwitch from 'Components/PeriodSwitch'
// $FlowFixMe
import ComparaisonConfig from 'Components/simulationConfigs/rémunération-dirigeant.yaml'
import withSimulationConfig from 'Components/simulationConfigs/withSimulationConfig'
import withSitePaths from 'Components/utils/withSitePaths'
import Value from 'Components/Value'
import { encodeRuleName, getRuleFromAnalysis } from 'Engine/rules.js'
import revenusSVG from 'Images/revenus.svg'
import { compose } from 'ramda'
import React, { useCallback, useState } from 'react'
import emoji from 'react-easy-emoji'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import {
analysisWithDefaultsSelector,
branchAnalyseSelector
} from 'Selectors/analyseSelectors'
import Animate from 'Ui/animate'
import InfoBulle from 'Ui/InfoBulle'
import './SchemeComparaison.css'
type OwnProps = {
hideAutoEntrepreneur?: boolean,
hideAssimiléSalarié?: boolean
}
type Props = OwnProps & {
setSituationBranch: number => void,
defineDirectorStatus: string => void,
sitePaths: any,
isAutoentrepreneur: boolean => void,
plafondAutoEntrepreneurDépassé: boolean
}
let getBranchIndex = branch =>
({ assimilé: 0, indépendant: 1, 'auto-entrepreneur': 2 }[branch])
let getRuleFrom = analyses => (branch, dottedName) => {
let i = getBranchIndex(branch)
return getRuleFromAnalysis(analyses[i])(dottedName)
}
const SchemeComparaison = ({
/* Own Props */
hideAutoEntrepreneur = false,
hideAssimiléSalarié = false,
/* Injected Props */
plafondAutoEntrepreneurDépassé,
defineDirectorStatus,
isAutoentrepreneur,
analyses
}: Props) => {
let getRule = getRuleFrom(analyses)
const [showMore, setShowMore] = useState(false)
const [conversationStarted, setConversationStarted] = useState(
!!getRule('assimilé', 'revenu net après impôt')?.nodeValue
)
const startConversation = useCallback(() => setConversationStarted(true), [
setConversationStarted
])
return (
<>
<div
className={classnames('comparaison-grid', {
hideAutoEntrepreneur,
hideAssimiléSalarié
})}>
<h2 className="AS">
{emoji('☂')} <T>Assimilé salarié</T>
<small>
<T k="comparaisonRégimes.AS.tagline">Le régime tout compris</T>
</small>
</h2>
<h2 className="indep">
{emoji('👩‍🔧')}{' '}
2019-05-21 12:48:06 +00:00
{hideAssimiléSalarié ? (
<T>Entreprise Individuelle</T>
2019-05-21 12:48:06 +00:00
) : (
<T>Indépendant</T>
2019-05-21 12:48:06 +00:00
)}
<small>
<T k="comparaisonRégimes.indep.tagline">
La protection sociale à la carte
</T>
</small>
</h2>
<h2 className="auto">
{emoji('🚶‍♂️')} <T>Auto-entrepreneur</T>
<small>
<T k="comparaisonRégimes.auto.tagline">
Pour commencer sans risques
</T>
</small>
</h2>
2019-05-21 12:48:06 +00:00
<h3 className="legend">
<T k="comparaisonRégimes.status.legend">
Statuts juridiques possibles
2019-05-09 16:40:14 +00:00
</T>
</h3>
<div className="AS">
<div>
<T k="comparaisonRégimes.status.AS">
SAS, SASU ou SARL avec gérant minoritaire
</T>
</div>
</div>
<div className="indep">
<div>
{hideAssimiléSalarié ? (
<T k="comparaisonRégimes.status.indep.2">EI ou EIRL</T>
) : (
<T k="comparaisonRégimes.status.indep.1">
EI, EIRL, EURL ou SARL avec gérant majoritaire
</T>
)}
</div>
</div>
<div className="auto">
<T k="comparaisonRégimes.status.auto">Auto-entreprise</T>
</div>
<T k="comparaisonRégimes.sécuritéSociale">
<h3 className="legend">Sécurité sociale</h3>
<div className="AS">
Régime général <small />
</div>
<div className="indep-et-auto">
Sécurité sociale des indépendants <small />
</div>
</T>
<T k="comparaisonRégimes.AT">
<h3 className="legend">Couverture accidents du travail</h3>
</T>
<div className="AS">
<T>
<T>Oui</T>
</T>
</div>
<div className="indep-et-auto">
<T>Non</T>
</div>
<T k="comparaisonRégimes.assuranceMaladie">
2019-05-27 16:23:44 +00:00
<h3 className="legend">
Assurance maladie{' '}
<small>(médicaments, soins, hospitalisations)</small>
2019-05-27 16:23:44 +00:00
</h3>
<div className="AS-indep-et-auto">Identique pour tous</div>
</T>
<T k="comparaisonRégimes.mutuelle">
<h3 className="legend">
Mutuelle santé
<small />
</h3>
<div className="AS">Obligatoire</div>
<div className="indep-et-auto">Fortement conseillée</div>
</T>
<T k="comparaisonRégimes.indemnités">
<h3 className="legend">Indemnités journalières</h3>
</T>
<div className="green AS">++</div>
<div className="green indep">++</div>
<div className="green auto">+</div>
<T k="comparaisonRégimes.retraite">
<h3 className="legend">Retraite</h3>
</T>
<div className="green AS">+++</div>
<div className="green indep">++</div>
<div className="green auto">+</div>
{showMore ? (
<>
<T k="comparaisonRégimes.ACRE">
<h3 className="legend">ACRE</h3>
<div className="AS-et-indep">
1 an <small>(exonération partielle de cotisations)</small>
</div>
<div className="auto">
3 ans
<small>(application de taux réduits de cotisations)</small>
</div>
</T>
<T k="comparaisonRégimes.déduction">
<h3 className="legend">Déduction des charges</h3>
<div className="AS-et-indep">
Oui <small>(régime fiscal du réel)</small>
</div>
<div className="auto">
Non
<small>
(mais abattement forfaitaire pour le calcul de l'impôt sur le
revenu)
</small>
</div>
</T>
<T k="comparaisonRégimes.cotisations">
<h3 className="legend">Paiement des cotisations</h3>
<div className="AS">Mensuel</div>
<div className="indep">
Provision mensuelle ou trimestrielle
<small>
(avec régularisation après coup en fonction du revenu réel)
</small>
</div>
<div className="auto">Mensuel ou trimestriel</div>
</T>
<T k="comparaisonRégimes.complémentaireDeductible">
<h3 className="legend">
Contrats prévoyance et retraite facultatives déductibles
</h3>
<div className="AS">
Oui <small>(sous certaines conditions)</small>
</div>
<div className="indep">
Oui <small>(Loi Madelin)</small>
</div>
</T>
<div className="auto">
<T>Non</T>
</div>
<T k="comparaisonRégimes.cotisationMinimale">
<h3 className="legend">Paiement de cotisations minimales</h3>
</T>
<div className="AS">
<T>Non</T>
</div>
<div className="indep">
<T>Oui</T>
</div>
<div className="auto">
<T>Non</T>
</div>
<T k="comparaisonRégimes.seuil">
<h3 className="legend">
Revenu minimum pour l'ouverture des droits aux prestations
</h3>
<div className="AS">Oui</div>
<div className="indep">
Non <small>(cotisations minimales obligatoires)</small>
</div>
<div className="auto">Oui</div>
</T>
{!hideAutoEntrepreneur && (
<T k="comparaisonRégimes.plafondCA">
<h3 className="legend">Plafond de chiffre d'affaires</h3>
<div className="AS-et-indep">
<T>Non</T>
</div>
<div className="auto">
<T>Oui</T>
<small>
(70 000 en services / 170 000 en vente de biens,
restauration ou hébergement)
</small>
</div>
</T>
)}
<T k="comparaisonRégimes.comptabilité">
<h3 className="legend">
Gestion comptable, sociale, juridique...
</h3>
<div className="AS-et-indep">
Accompagnement fortement conseillé
<small>
(expert comptable, comptable, centre de gestion agrée...)
</small>
</div>
<div className="auto">
Simplifiée{' '}
<small>(peut être gérée par l'auto-entrepreneur)</small>
</div>
</T>
</>
) : (
<T k="comparaisonRégimes.comparaisonDétaillée">
<div className="all">
<button
onClick={() => setShowMore(true)}
className="ui__ simple small button">
Afficher plus d'informations
</button>
</div>
</T>
)}
{conversationStarted && (
<>
<T k="comparaisonRégimes.période">
<h3 className="legend">Période</h3>
</T>
<div className="AS-indep-et-auto" style={{ alignSelf: 'start' }}>
<PeriodSwitch />
</div>
</>
)}
<div className="all colored">
{!conversationStarted ? (
<>
<T k="comparaisonRégimes.simulationText">
<h3>
Comparer mes revenus, pension de retraite et indemnité maladie
</h3>
<img src={revenusSVG} css="height: 8rem" />
<button
className="ui__ cta plain button"
onClick={startConversation}>
Lancer la simulation
</button>
</T>
</>
) : (
<div className="ui__ container">
<SeeAnswersButton />
<Conversation />
</div>
)}
</div>
{conversationStarted &&
!!getRule('assimilé', 'revenu net après impôt')?.nodeValue && (
<>
<T k="comparaisonRégimes.revenuNetApresImpot">
<h3 className="legend">Revenu net après impôt</h3>
</T>
<div className="AS">
<Animate.appear className="ui__ plain card">
<RuleValueLink
branch="assimilé"
rule="revenu net après impôt"
/>
</Animate.appear>
</div>
<div className="indep">
<Animate.appear className="ui__ plain card">
<RuleValueLink
branch="indépendant"
rule="revenu net après impôt"
/>
</Animate.appear>
</div>
<div className="auto">
<Animate.appear
className={classnames(
'ui__ plain card',
plafondAutoEntrepreneurDépassé && 'disabled'
)}>
{plafondAutoEntrepreneurDépassé ? (
'Plafond de CA dépassé'
) : (
<RuleValueLink
branch="auto-entrepreneur"
rule="revenu net après impôt"
/>
)}
</Animate.appear>
</div>
<T k="comparaisonRégimes.revenuNetAvantImpot">
<h3 className="legend">
Revenu net de cotisations <small>(avant impôts)</small>
</h3>
</T>
<div className="AS">
<RuleValueLink
branch="assimilé"
rule="contrat salarié . rémunération . net"
/>
</div>
<div className="indep">
<RuleValueLink
branch="indépendant"
rule="indépendant . revenu net de cotisations"
/>
</div>
<div className="auto">
{plafondAutoEntrepreneurDépassé ? (
'—'
) : (
<RuleValueLink
branch="auto-entrepreneur"
rule="auto-entrepreneur . revenu net de cotisations"
/>
)}
</div>
<h3 className="legend">
<T k="comparaisonRégimes.retraiteEstimation.legend">
<span>Pension de retraite</span>
<small>(avant impôts)</small>
</T>
</h3>
<div className="AS">
<span>
<RuleValueLink
branch="assimilé"
rule="protection sociale . retraite"
/>{' '}
<InfoBulle>
<T k="comparaisonRégimes.retraiteEstimation.infobulles.AS">
Pension calculée pour 172 trimestres cotisés au régime
général sans variations de revenus.
</T>
</InfoBulle>
</span>
</div>
<div className="indep">
{getRule('indépendant', 'protection sociale . retraite')
.applicable !== false ? (
<span>
<RuleValueLink
branch="indépendant"
rule="protection sociale . retraite"
/>{' '}
<InfoBulle>
<T k="comparaisonRégimes.retraiteEstimation.infobulles.indep">
Pension calculée pour 172 trimestres cotisés au régime
des indépendants sans variations de revenus.
</T>
</InfoBulle>
</span>
) : (
<span className="ui__ notice">
<T>Pas implémenté</T>
</span>
)}
</div>
<div className="auto">
{plafondAutoEntrepreneurDépassé ? (
'—'
) : getRule(
'auto-entrepreneur',
'protection sociale . retraite'
).applicable !== false ? (
<span>
<RuleValueLink
branch="auto-entrepreneur"
rule="protection sociale . retraite"
/>{' '}
<InfoBulle>
<T k="comparaisonRégimes.retraiteEstimation.infobulles.auto">
Pension calculée pour 172 trimestres cotisés en
auto-entrepreneur sans variations de revenus.
</T>
</InfoBulle>
</span>
) : (
<span className="ui__ notice">
<T>Pas implémenté</T>
</span>
)}
</div>
<T k="comparaisonRégimes.trimestreValidés">
<h3 className="legend">
Nombre de trimestres validés <small>(pour la retraite)</small>
</h3>
</T>
<div className="AS">
<RuleValueLink
branch="assimilé"
rule="protection sociale . retraite . trimestres validés par an"
appendText={<T>trimestres</T>}
unit={null}
/>
</div>
<div className="indep">
<RuleValueLink
branch="indépendant"
rule="protection sociale . retraite . trimestres validés par an"
appendText={<T>trimestres</T>}
unit={null}
/>
</div>
<div className="auto">
{plafondAutoEntrepreneurDépassé ? (
'—'
) : (
<RuleValueLink
branch="auto-entrepreneur"
rule="protection sociale . retraite . trimestres validés par an"
appendText={<T>trimestres</T>}
unit={null}
/>
)}
</div>
<T k="comparaisonRégimes.indemnités">
<h3 className="legend">
Indemnités journalières{' '}
<small>(en cas d'arrêt maladie)</small>
</h3>
</T>
<div className="AS">
<span>
<RuleValueLink
branch="assimilé"
appendText={
<>
/ <T>jour</T>
</>
}
rule="protection sociale . santé . indemnités journalières"
/>
</span>
<small>
(
<RuleValueLink
branch="assimilé"
rule="protection sociale . accidents du travail et maladies professionnelles"
/>{' '}
<T>pour les accidents de trajet/travail et maladie pro</T>)
</small>
</div>
<div className="indep">
<span>
<RuleValueLink
appendText={
<>
/ <T>jour</T>
</>
}
branch="indépendant"
rule="protection sociale . santé . indemnités journalières"
/>
</span>
</div>
<div className="auto">
{plafondAutoEntrepreneurDépassé ? (
'—'
) : (
<span>
<RuleValueLink
branch="auto-entrepreneur"
rule="protection sociale . santé . indemnités journalières"
appendText={
<>
/ <T>jour</T>
</>
}
/>
</span>
)}
</div>
</>
)}
</div>
<div className="ui__ container">
<br />
<h3>
<T k="comparaisonRégimes.titreSelection">
Créer mon entreprise en tant que :
</T>
</h3>
<div className="ui__ answer-group">
{!hideAssimiléSalarié && (
<button
className="ui__ button"
onClick={() => {
defineDirectorStatus('SALARIED')
!hideAutoEntrepreneur && isAutoentrepreneur(false)
}}>
<T k="comparaisonRégimes.choix.AS">Assimilé&nbsp;salarié</T>
</button>
)}
<button
className="ui__ button"
onClick={() => {
!hideAssimiléSalarié && defineDirectorStatus('SELF_EMPLOYED')
!hideAutoEntrepreneur && isAutoentrepreneur(false)
}}>
{hideAssimiléSalarié ? (
<T k="comparaisonRégimes.choix.EI">Entreprise individuelle</T>
) : (
<T k="comparaisonRégimes.choix.indep">Indépendant</T>
)}
</button>
{!hideAutoEntrepreneur && (
<button
className="ui__ button"
onClick={() => {
!hideAssimiléSalarié && defineDirectorStatus('SELF_EMPLOYED')
isAutoentrepreneur(true)
}}>
<T k="comparaisonRégimes.choix.auto">Auto-entrepreneur</T>
</button>
)}
</div>
</div>
</>
)
}
const RuleValueLink = compose(
withSitePaths,
connect(
state => ({
analyses: analysisWithDefaultsSelector(state)
}),
{
setSituationBranch
}
)
)(
({
analyses,
branch,
rule: dottedName,
sitePaths,
appendText,
setSituationBranch,
unit
}) => {
let rule = getRuleFrom(analyses)(branch, dottedName)
return !rule ? null : (
<Link
onClick={() => setSituationBranch(getBranchIndex(branch))}
to={
sitePaths.documentation.index + '/' + encodeRuleName(rule.dottedName)
}>
<Value
maximumFractionDigits={0}
{...rule}
unit={
/* //TODO the unit should be integrated in the leaf rules of base.yaml and infered by mecanisms. Will be done in a future release*/
unit !== undefined ? unit : '€'
}
/>
{appendText && <> {appendText}</>}
</Link>
)
}
)
🔥 Migration vers TypeScript Outils ====== Ce commit retire le tooling de Flow, et ajoute le support de TypeScript pour les fichiers .ts et .tsx. Il n'est pas nécessaire de tout migrer d'un coup ce qui facilite la transition. On garde en effet le compilateur Babel avec un preset TypeScript (ce qui permet donc de retirer à la fois les types Flow et TypeScript) plutôt que d'utiliser le compilateur standard pour la conversion du code. Cela permet aussi de mieux s'intégrer avec d'autres outils, notamment les test-runners. Ajout d'une nouvelle commande `yarn run type-check`, intégrée dans CircleCI. Par ailleurs ajout du support de l'opérateur ?? pour donner des valeurs par défaut (nullish-coalescing-operator). Typage des libraires tierces ============================ Les principales libraires que nous utilisons ont un typage TypeScript de bon niveau, ce qui facilite l'intégration. J'ai mis à jour react-i18next et i18next afin de corriger un problème de typage. Typage du code ============== Le typage est loin d'être complet dans ce commit, en particulier il manque les types relatifs au state Redux, ainsi qu'au moteur (règle, explication). Néanmoins le typage des contextes fonctionne, en particulier sitePaths (avec un type récursif non trivial !) qui a déjà permis de détecter un lien mort. Le typage des "paths" (Components/, Règles/, etc.) fonctionne bien, y compris avec l'auto-complétion automatique des import par Typescript. TypeScript se révèle déjà bien agréable dans VSCode (auto-complétion, refacto, etc.) ! Reste à migrer progressivement le reste du code !
2019-10-26 16:21:09 +00:00
export default compose(
withSimulationConfig(ComparaisonConfig),
connect(
state => ({
analyses: analysisWithDefaultsSelector(state),
plafondAutoEntrepreneurDépassé: branchAnalyseSelector(state, {
situationBranchName: 'Auto-entrepreneur'
}).controls?.find(
({ test }) =>
test.includes && test.includes('base des cotisations > plafond')
)
}),
{
defineDirectorStatus,
isAutoentrepreneur,
setSituationBranch
}
)
🔥 Migration vers TypeScript Outils ====== Ce commit retire le tooling de Flow, et ajoute le support de TypeScript pour les fichiers .ts et .tsx. Il n'est pas nécessaire de tout migrer d'un coup ce qui facilite la transition. On garde en effet le compilateur Babel avec un preset TypeScript (ce qui permet donc de retirer à la fois les types Flow et TypeScript) plutôt que d'utiliser le compilateur standard pour la conversion du code. Cela permet aussi de mieux s'intégrer avec d'autres outils, notamment les test-runners. Ajout d'une nouvelle commande `yarn run type-check`, intégrée dans CircleCI. Par ailleurs ajout du support de l'opérateur ?? pour donner des valeurs par défaut (nullish-coalescing-operator). Typage des libraires tierces ============================ Les principales libraires que nous utilisons ont un typage TypeScript de bon niveau, ce qui facilite l'intégration. J'ai mis à jour react-i18next et i18next afin de corriger un problème de typage. Typage du code ============== Le typage est loin d'être complet dans ce commit, en particulier il manque les types relatifs au state Redux, ainsi qu'au moteur (règle, explication). Néanmoins le typage des contextes fonctionne, en particulier sitePaths (avec un type récursif non trivial !) qui a déjà permis de détecter un lien mort. Le typage des "paths" (Components/, Règles/, etc.) fonctionne bien, y compris avec l'auto-complétion automatique des import par Typescript. TypeScript se révèle déjà bien agréable dans VSCode (auto-complétion, refacto, etc.) ! Reste à migrer progressivement le reste du code !
2019-10-26 16:21:09 +00:00
)(SchemeComparaison)