diff --git a/source/components/SchemeComparaison.js b/source/components/SchemeComparaison.js index 6f269aa96..e82462cfa 100644 --- a/source/components/SchemeComparaison.js +++ b/source/components/SchemeComparaison.js @@ -14,23 +14,21 @@ import ComparaisonConfig from 'Components/simulationConfigs/rémunération-dirig import withSimulationConfig from 'Components/simulationConfigs/withSimulationConfig' import withSitePaths from 'Components/utils/withSitePaths' import revenusSVG from 'Images/revenus.svg' -import { compose, tryCatch } from 'ramda' +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 { branchAnalyseSelector } from 'Selectors/analyseSelectors' import { - règleAvecMontantSelector, - règleAvecValeurSelector -} from 'Selectors/regleSelectors' + branchAnalyseSelector, + analysisWithDefaultsSelector, + getRuleFromAnalysis +} from 'Selectors/analyseSelectors' import Animate from 'Ui/animate' -import AnimatedTargetValue from 'Ui/AnimatedTargetValue' import InfoBulle from 'Ui/InfoBulle' import Value from 'Components/Value' import './SchemeComparaison.css' - -import type { RègleAvecMontant, RègleAvecValeur } from 'Types/RegleTypes' +import { encodeRuleName } from 'Engine/rules.js' type OwnProps = { hideAutoEntrepreneur?: boolean, @@ -38,9 +36,6 @@ type OwnProps = { } type Props = OwnProps & { - assimiléSalarié: SimulationResult, - indépendant: SimulationResult, - autoEntrepreneur: SimulationResult, setSituationBranch: number => void, defineDirectorStatus: string => void, sitePaths: any, @@ -48,14 +43,11 @@ type Props = OwnProps & { plafondAutoEntrepreneurDépassé: boolean } -type SimulationResult = { - retraite: RègleAvecMontant, - trimestreValidés: RègleAvecValeur, - indemnitésJournalières: RègleAvecMontant, - indemnitésJournalièresATMP?: RègleAvecMontant, - revenuNetDeCotisations: RègleAvecMontant, - revenuNetAprèsImpôt: RègleAvecMontant, - plafondDé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 = ({ @@ -63,21 +55,21 @@ const SchemeComparaison = ({ hideAutoEntrepreneur = false, hideAssimiléSalarié = false, /* Injected Props */ - assimiléSalarié, - indépendant, - autoEntrepreneur, + plafondAutoEntrepreneurDépassé, defineDirectorStatus, isAutoentrepreneur, - setSituationBranch + analyses }: Props) => { + let getRule = getRuleFrom(analyses) const [showMore, setShowMore] = useState(false) const [conversationStarted, setConversationStarted] = useState( - !!assimiléSalarié.revenuNetAprèsImpôt.montant + !!getRule('assimilé', 'revenu net après impôt')?.nodeValue ) const startConversation = useCallback(() => setConversationStarted(true), [ setConversationStarted ]) + return ( <>
)}
- {conversationStarted && !!assimiléSalarié.revenuNetAprèsImpôt.montant && ( - <> - -

Revenu net après impôt

-
-
- + {conversationStarted && + !!getRule('assimilé', 'revenu net après impôt') && ( + <> + +

Revenu net après impôt

+
+
+ + + +
+
+ + + +
+
+ + {plafondAutoEntrepreneurDépassé ? ( + 'Plafond de CA dépassé' + ) : ( + + )} + +
+ +

+ Revenu net de cotisations (avant impôts) +

+
+
setSituationBranch(0)} - {...assimiléSalarié.revenuNetAprèsImpôt} + branch="assimilé" + rule="contrat salarié . salaire . net" /> - -
-
- +
+
setSituationBranch(1)} - {...indépendant.revenuNetAprèsImpôt} + branch="indépendant" + rule="indépendant . revenu net de cotisations" /> - -
-
- - {autoEntrepreneur.plafondDépassé ? ( - 'Plafond de CA dépassé' +
+
+ {plafondAutoEntrepreneurDépassé ? ( + '—' ) : ( setSituationBranch(2)} - {...autoEntrepreneur.revenuNetAprèsImpôt} + branch="auto entrepreneur" + rule="auto entrepreneur . revenu net de cotisations" /> )} - -
- +

- Revenu net de cotisations (avant impôts) + + Pension de retraite + (avant impôts) +

- -
- setSituationBranch(0)} - {...assimiléSalarié.revenuNetDeCotisations} - /> -
-
- setSituationBranch(1)} - {...indépendant.revenuNetDeCotisations} - /> -
-
- {autoEntrepreneur.plafondDépassé ? ( - '—' - ) : ( - setSituationBranch(2)} - {...autoEntrepreneur.revenuNetDeCotisations} - /> - )} -
-

- - Pension de retraite - (avant impôts) +
+ + {' '} + + + Pension calculée pour 172 trimestres cotisés au régime + général sans variations de revenus. + + + +
+
+ {getRule('indépendant', 'protection sociale . retraite') + .applicable !== false ? ( + + {' '} + + + Pension calculée pour 172 trimestres cotisés au régime + des indépendants sans variations de revenus. + + + + ) : ( + + Pas implémenté + + )} +
+
+ {plafondAutoEntrepreneurDépassé ? ( + '—' + ) : getRule( + 'auto entrepreneur', + 'protection sociale . retraite' + ).applicable !== false ? ( + + {' '} + + + Pension calculée pour 172 trimestres cotisés en + auto-entrepreneur sans variations de revenus. + + + + ) : ( + + Pas implémenté + + )} +
+ +

+ Nombre de trimestres validés (pour la retraite) +

-

-
- +
setSituationBranch(0)} - {...assimiléSalarié.retraite} - />{' '} - - - Pension calculée pour 172 trimestres cotisés au régime - général sans variations de revenus. - - - -
-
- {indépendant.retraite.applicable !== false ? ( - - setSituationBranch(1)} - {...indépendant.retraite} - />{' '} - - - Pension calculée pour 172 trimestres cotisés au régime des - indépendants sans variations de revenus. - - - - ) : ( - - Pas implémenté - - )} -
-
- {autoEntrepreneur.plafondDépassé ? ( - '—' - ) : autoEntrepreneur.retraite.applicable !== false ? ( - - setSituationBranch(2)} - {...autoEntrepreneur.retraite} - />{' '} - - - Pension calculée pour 172 trimestres cotisés en - auto-entrepreneur sans variations de revenus. - - - - ) : ( - - Pas implémenté - - )} -
- -

- Nombre de trimestres validés (pour la retraite) -

-
-
- setSituationBranch(0)} - appendText={trimestres} - {...assimiléSalarié.trimestreValidés} - /> -
-
- setSituationBranch(1)} - appendText={trimestres} - {...indépendant.trimestreValidés} - /> -
-
- {autoEntrepreneur.plafondDépassé ? ( - '—' - ) : ( - setSituationBranch(2)} + branch="assimilé" + rule="protection sociale . retraite . trimestres validés par an" appendText={trimestres} - {...autoEntrepreneur.trimestreValidés} + unit={null} /> - )} -
- -

- Indemnités journalières (en cas d'arrêt maladie) -

-
-
- +
+
setSituationBranch(0)} - appendText={ - <> - / jour - - } - {...assimiléSalarié.indemnitésJournalières} + branch="indépendant" + rule="protection sociale . retraite . trimestres validés par an" + appendText={trimestres} + unit={null} /> - - - ( - setSituationBranch(0)} - {...assimiléSalarié.indemnitésJournalièresATMP} - />{' '} - pour les accidents de trajet/travail et maladie pro) - -
-
- - setSituationBranch(1)} - appendText={ - <> - / jour - - } - {...indépendant.indemnitésJournalières} - /> - -
-
- {autoEntrepreneur.plafondDépassé ? ( - '—' - ) : ( +
+
+ {plafondAutoEntrepreneurDépassé ? ( + '—' + ) : ( + trimestres} + unit={null} + /> + )} +
+ +

+ Indemnités journalières{' '} + (en cas d'arrêt maladie) +

+
+
setSituationBranch(2)} + branch="assimilé" appendText={ <> / jour } - {...autoEntrepreneur.indemnitésJournalières} + rule="protection sociale . santé . indemnités journalières" /> - )} -
- - )} + + ( + {' '} + pour les accidents de trajet/travail et maladie pro) + +
+
+ + + / jour + + } + branch="indépendant" + rule="protection sociale . santé . indemnités journalières" + /> + +
+
+ {plafondAutoEntrepreneurDépassé ? ( + '—' + ) : ( + + + / jour + + } + /> + + )} +
+ + )}

@@ -598,92 +599,58 @@ const SchemeComparaison = ({ ) } -const RuleValueLink = withSitePaths( - ({ lien, montant, valeur, sitePaths, onClick, appendText }) => ( - - {montant != undefined && } - {valeur != undefined && ( - - {valeur} - - )} - {appendText && <> {appendText}} - +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 : ( + setSituationBranch(getBranchIndex(branch))} + to={ + sitePaths.documentation.index + '/' + encodeRuleName(rule.dottedName) + }> + + {appendText && <> {appendText}} + + ) + } ) export default (compose( withSimulationConfig(ComparaisonConfig), connect( - tryCatch( - state => ({ - autoEntrepreneur: { - retraite: règleAvecMontantSelector(state, { - situationBranchName: 'Auto-entrepreneur' - })('protection sociale . retraite'), - trimestreValidés: règleAvecValeurSelector(state, { - situationBranchName: 'Auto-entrepreneur' - })('protection sociale . retraite . trimestres validés par an'), - indemnitésJournalières: règleAvecMontantSelector(state, { - situationBranchName: 'Auto-entrepreneur' - })('protection sociale . santé . indemnités journalières'), - revenuNetAprèsImpôt: règleAvecMontantSelector(state, { - situationBranchName: 'Auto-entrepreneur' - })('revenu net après impôt'), - revenuNetDeCotisations: règleAvecMontantSelector(state, { - situationBranchName: 'Auto-entrepreneur' - })('auto entrepreneur . revenu net de cotisations'), - // $FlowFixMe - plafondDépassé: branchAnalyseSelector(state, { - situationBranchName: 'Auto-entrepreneur' - }).controls?.find( - ({ test }) => - test.includes && test.includes('base des cotisations > plafond') - ) - }, - indépendant: { - retraite: règleAvecMontantSelector(state, { - situationBranchName: 'Indépendant' - })('protection sociale . retraite'), - trimestreValidés: règleAvecValeurSelector(state, { - situationBranchName: 'Indépendant' - })('protection sociale . retraite . trimestres validés par an'), - indemnitésJournalières: règleAvecMontantSelector(state, { - situationBranchName: 'Indépendant' - })('protection sociale . santé . indemnités journalières'), - revenuNetAprèsImpôt: règleAvecMontantSelector(state, { - situationBranchName: 'Indépendant' - })('revenu net après impôt'), - revenuNetDeCotisations: règleAvecMontantSelector(state, { - situationBranchName: 'Indépendant' - })('indépendant . revenu net de cotisations') - }, - assimiléSalarié: { - retraite: règleAvecMontantSelector(state, { - situationBranchName: 'Assimilé salarié' - })('protection sociale . retraite'), - trimestreValidés: règleAvecValeurSelector(state, { - situationBranchName: 'Assimilé salarié' - })('protection sociale . retraite . trimestres validés par an'), - indemnitésJournalières: règleAvecMontantSelector(state, { - situationBranchName: 'Assimilé salarié' - })('protection sociale . santé . indemnités journalières'), - indemnitésJournalièresATMP: règleAvecMontantSelector(state, { - situationBranchName: 'Assimilé salarié' - })( - 'protection sociale . accidents du travail et maladies professionnelles' - ), - revenuNetAprèsImpôt: règleAvecMontantSelector(state, { - situationBranchName: 'Assimilé salarié' - })('revenu net après impôt'), - revenuNetDeCotisations: règleAvecMontantSelector(state, { - situationBranchName: 'Assimilé salarié' - })('contrat salarié . salaire . net') - } - }), - e => console.log(e) || {} - ), - + state => ({ + analyses: analysisWithDefaultsSelector(state), + plafondAutoEntrepreneurDépassé: branchAnalyseSelector(state, { + situationBranchName: 'Auto-entrepreneur' + }).controls?.find( + ({ test }) => + test.includes && test.includes('base des cotisations > plafond') + ) + }), { defineDirectorStatus, isAutoentrepreneur, diff --git a/source/components/Value.js b/source/components/Value.js index 2b3fbe6cd..e6e0583d8 100644 --- a/source/components/Value.js +++ b/source/components/Value.js @@ -28,6 +28,7 @@ let booleanTranslations = { let style = ` border: 2px dashed chartreuse font-family: 'Courier New', Courier, monospace; + ` export default withLanguage( diff --git a/source/selectors/analyseSelectors.js b/source/selectors/analyseSelectors.js index 2091976fa..37f1c01d4 100644 --- a/source/selectors/analyseSelectors.js +++ b/source/selectors/analyseSelectors.js @@ -322,11 +322,6 @@ export let getRuleFromAnalysis = analysis => dottedName => { analysis.cache[dottedName]?.explanation || // the cache stores a reference to a variable, the variable is contained in the 'explanation' attribute analysis.targets.find(propEq('dottedName', dottedName)) - if (dottedName.includes('avanta')) - console.log( - analysis.cache[dottedName] // the cache stores a reference to a variable, the variable is contained in the 'explanation' attribute - ) - if (!rule) { throw new Error( `[getRuleFromAnalysis] Unable to find the rule ${dottedName}` diff --git a/source/selectors/regleSelectors.js b/source/selectors/regleSelectors.js deleted file mode 100644 index 9ddf8065a..000000000 --- a/source/selectors/regleSelectors.js +++ /dev/null @@ -1,155 +0,0 @@ -/* @flow */ - -import { findRuleByDottedName } from 'Engine/rules' -import { encodeRuleName } from 'Engine/rules.js' -import { isNil } from 'ramda' -import { createSelector } from 'reselect' -import { coerceArray } from '../utils' -import { - branchAnalyseSelector, - flatRulesSelector, - situationsWithDefaultsSelector -} from './analyseSelectors' -import type { FlatRules } from 'Types/State' -import type { - Règle, - RègleAvecMontant, - RègleValeur, - RègleAvecValeur -} from 'Types/RegleTypes' -import type { Analysis } from 'Types/Analysis' -import type { InputSelector } from 'reselect' - -export const règleLocaliséeSelector: InputSelector< - { lang: string }, - { rules?: FlatRules }, - (dottedName: string) => Règle -> = createSelector( - flatRulesSelector, - (localizedFlatRules: ?FlatRules) => (dottedName: string): Règle => { - if (!localizedFlatRules) { - throw new Error( - `[LocalizedRègleSelector] Les localizedFlatRules ne doivent pas être 'undefined' ou 'null'` - ) - } - const localizedRule = findRuleByDottedName(localizedFlatRules, dottedName) - if (!localizedRule) { - throw new Error( - `[LocalizedRègleSelector] Impossible de trouver la règle "${dottedName}" dans les flatRules. Pensez à vérifier l'orthographe et que l'écriture est bien sous forme dottedName, et que la règles est bien calculée (dans les objectifs de la simulation)` - ) - } - return { - nom: localizedRule.titre || localizedRule.nom, - lien: encodeRuleName(dottedName), - - id: dottedName, - ...(localizedRule.summary ? { résumé: localizedRule.summary } : {}), - ...(localizedRule.icon ? { icône: localizedRule.icon } : {}), - ...(localizedRule.format ? { type: localizedRule.format } : {}) - } - } -) - -export const règleValeurSelector: InputSelector< - { lang: string }, - { rules?: FlatRules }, - (dottedName: string) => RègleValeur -> = createSelector( - branchAnalyseSelector, - situationsWithDefaultsSelector, - règleLocaliséeSelector, - (analysis: Analysis, situation, règleLocalisée: string => Règle) => ( - dottedName: string - ): RègleValeur => { - if (!analysis) { - throw new Error( - `[règleValeurSelector] L'analyse fournie ne doit pas être 'undefined' ou 'null'` - ) - } - const rule = coerceArray(analysis) - .map( - analysis => - analysis.cache[dottedName] || - analysis.targets.find(target => target.dottedName === dottedName) - ) - .filter(Boolean)[0] - - let valeur = - rule && !isNil(rule.nodeValue) - ? rule.nodeValue - : Array.isArray(situation) - ? situation[0][dottedName] - : situation[dottedName] - if (isNil(valeur)) { - console.warn( - `[règleValeurSelector] Impossible de trouver la valeur associée à la règle "${dottedName}". Pensez à vérifier l'orthographe et que l'écriture est bien sous forme dottedName. Vérifiez aussi qu'il ne manque pas une valeur par défaut à une règle nécessaire au calcul.` - ) - } - if (valeur === 'oui') { - valeur = true - } - if (valeur === 'non') { - valeur = false - } - if (typeof valeur === 'boolean') { - return { type: 'boolean', valeur } - } - if (rule && (rule.API || rule.explanation?.API)) { - //TODO This code is specific to the géo API - return { type: 'string', valeur: valeur.nom } - } - const type = - (rule && - (rule.format || (rule.explanation && rule.explanation.format))) || - (!Number.isNaN(valeur) && Number.isNaN(Number.parseFloat(valeur)) - ? 'string' - : 'number') - - return { - type, - ...(rule && 'isApplicable' in rule - ? { applicable: rule.isApplicable } - : {}), - valeur: - type === 'string' - ? règleLocalisée(`${dottedName} . ${valeur}`).nom - : Number.parseFloat(valeur) - } - } -) - -export const règleAvecMontantSelector: InputSelector< - { lang: string }, - { rules?: FlatRules }, - (dottedName: string) => RègleAvecMontant -> = createSelector( - règleValeurSelector, - règleLocaliséeSelector, - (règleValeur, règleLocalisée) => (dottedName: string): RègleAvecMontant => { - const valeur = règleValeur(dottedName) - if (!valeur || valeur.type !== 'euros') { - throw new Error( - `[règleAvecMontantSelector] Le type de valeur de "${dottedName}" n'est pas celui d'un montant. \n Pour faire disparaitre cette erreur, ajouter la propriété 'format: euros' à cette règle ` - ) - } - return { - ...règleLocalisée(dottedName), - ...('applicable' in valeur ? { applicable: valeur.applicable } : {}), - montant: valeur.valeur - } - } -) -export const règleAvecValeurSelector: InputSelector< - { lang: string }, - { rules?: FlatRules }, - (dottedName: string) => RègleAvecValeur -> = createSelector( - règleValeurSelector, - règleLocaliséeSelector, - (règleValeur, règleLocalisée) => (dottedName: string): RègleAvecValeur => - // $FlowFixMe - ({ - ...règleLocalisée(dottedName), - ...règleValeur(dottedName) - }) -) diff --git a/source/types/RegleTypes.js b/source/types/RegleTypes.js deleted file mode 100644 index a193f6635..000000000 --- a/source/types/RegleTypes.js +++ /dev/null @@ -1,23 +0,0 @@ -/* @flow */ - -export type Règle = { - nom: string, - type: string, - id: string, - lien: string, - icône?: string, - résumé?: string -} - -export type RègleAvecMontant = Règle & { - montant: number, - applicable?: boolean -} - -export type RègleValeur = { - valeur: boolean | number | string, - applicable?: boolean, - type: 'boolean' | 'number' | 'string' -} - -export type RègleAvecValeur = RègleValeur & Règle