diff --git a/package.json b/package.json index b80e0864f..d07bcc146 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "css-loader": "^0.28.1", "csv-loader": "^2.1.1", "daggy": "^1.1.0", - "eslint": "^4.4.1", + "eslint": "^4.8.0", "eslint-plugin-react": "^7.0.1", "express": "^4.15.3", "fantasy-combinators": "0.0.1", diff --git a/règles/rémunération-travail/aides/ok/cice.yaml b/règles/rémunération-travail/aides/ok/cice.yaml index 7401683a2..1c712233a 100644 --- a/règles/rémunération-travail/aides/ok/cice.yaml +++ b/règles/rémunération-travail/aides/ok/cice.yaml @@ -10,13 +10,28 @@ calcul: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31326 # TODO - exlure stage, apprentissage - non applicable si: salaire brut > plafond cice + non applicable si: + une de ces conditions: + - assiette cotisations sociales > plafond cice + - entreprise . association non lucrative formule: multiplication: assiette: assiette cotisations sociales taux: 7% + exemples: + - nom: SMIC + situation: + assiette cotisations sociales: 2300 + entreprise . association non lucrative: non + valeur attendue: 161 + - nom: Non applicable si association + situation: + assiette cotisations sociales: 2300 + entreprise . association non lucrative: oui + valeur attendue: 0 + - espace: contrat salarié nom: plafond cice # TODO: calcul du smic proratisé diff --git a/règles/rémunération-travail/aides/ok/cits.yaml b/règles/rémunération-travail/aides/ok/cits.yaml new file mode 100644 index 000000000..b78399130 --- /dev/null +++ b/règles/rémunération-travail/aides/ok/cits.yaml @@ -0,0 +1,42 @@ +- espace: contrat salarié + nom: CITS + titre: Crédit d'impôt de taxe sur les salaire + aide: + type: avantage fiscal + thème: aide bas salaires + démarches: non + description: | + La loi de finances pour 2017 instaure, au bénéfice des associations et organismes sans but lucratif (OSBL), + un dispositif de crédit d'impôt de taxe sur les salaires (CITS). + références: + fiche: https://www.service-public.fr/associations/actualites/A11012 + + non applicable si: + une de ces conditions: + - assiette cotisations sociales > plafond CITS + - ≠ entreprise . association non lucrative + + formule: + multiplication: + assiette: assiette cotisations sociales + taux: 4% + + exemples: + - nom: SMIC + situation: + assiette cotisations sociales: 2300 + entreprise . association non lucrative: oui + valeur attendue: 92 + - nom: Non applicable si organisme lucratif + situation: + assiette cotisations sociales: 2300 + entreprise . association non lucrative: non + valeur attendue: 0 + +- espace: contrat salarié + nom: plafond CITS + formule: multiplicateur CITS * smic mensuel + +- espace: contrat salarié + nom: multiplicateur CITS + formule: 2.5 diff --git a/règles/rémunération-travail/cdd/CIF.yaml b/règles/rémunération-travail/cdd/CIF.yaml index 97843aa33..fb0add15a 100644 --- a/règles/rémunération-travail/cdd/CIF.yaml +++ b/règles/rémunération-travail/cdd/CIF.yaml @@ -23,14 +23,30 @@ Code du travail - Article L6322-37 : https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000022234996&cidTexte=LEGITEXT000006072050 exemples: + + - nom: Non applicable si CDI + situation: + type de contrat: CDI + assiette cotisations sociales: 1480 + valeur attendue: 0 + - nom: SMIC situation: + type de contrat: CDD + événement: aucun + motif: accroissement activité + contrat jeune vacances: non + assiette cotisations sociales: 1480 valeur attendue: 14.8 - nom: salaire médian situation: - contrat salarié . CDD . motif: classique . usage + type de contrat: CDD + événement: aucun + motif: accroissement activité + contrat jeune vacances: non + assiette cotisations sociales: 2300 valeur attendue: 23 diff --git a/règles/rémunération-travail/cotisations/ok/taxe-apprentissage.yaml b/règles/rémunération-travail/cotisations/ok/taxe-apprentissage.yaml index 3c46e7539..27246c331 100644 --- a/règles/rémunération-travail/cotisations/ok/taxe-apprentissage.yaml +++ b/règles/rémunération-travail/cotisations/ok/taxe-apprentissage.yaml @@ -10,7 +10,9 @@ notes: Taxe complexe, comportant notamment des exonérations non prises en compte ici. - # non applicable si: Entreprise . association non lucrative + non applicable si: Entreprise . association non lucrative + # L'association a but non lucratif ne paie pas d'IS de droit commun article 206 du Code général des impôts + # -> pas de taxe ni contribution d'apprentissage formule: somme: - taxe d'apprentissage de base diff --git a/règles/rémunération-travail/cotisations/ok/taxe-salaires.yaml b/règles/rémunération-travail/cotisations/ok/taxe-salaires.yaml index b3d14a397..fde77fa0a 100644 --- a/règles/rémunération-travail/cotisations/ok/taxe-salaires.yaml +++ b/règles/rémunération-travail/cotisations/ok/taxe-salaires.yaml @@ -4,6 +4,8 @@ description: | Sont assujetties les associations à but non lucratif et les entreprises non soumises à la TVA ou payant la TVA sur moins de 10% de leur chiffre. Les particuliers employeurs, les employeurs agricoles, les établissements d'enseignement supérieur, les auto-entrepreneurs ne sont pas concernés. question: L'entreprise est-elle assujettie à la taxe sur les salaires ? + # variable non utilisée pour l'instant, comme dans le simulateur v1 + # à ajouter quand nous aurons des mécanismes logiques plus évolués (notamment 'applicable si') - espace: contrat salarié nom: taxe sur les salaires annuelle @@ -26,6 +28,13 @@ taux: 13.6% - au-dessus de: 152279 taux: 20% + exemples: + - nom: salaire médian + situation: + assiette taxe sur les salaires: 2300 + valeur attendue: 2639.16 # calcul annuel : 7721×4.25%+(15417−7721)×8.5%+(27600−15417)×13.6% + + - espace: contrat salarié nom: assiette taxe sur les salaires @@ -33,5 +42,18 @@ - espace: contrat salarié nom: taxe sur les salaires - non applicable si: ≠ assujettie à la taxe sur les salaires + description: La taxe sur les salaires en France est un impôt progressif créé en 1948 que certains employeurs doivent acquitter sur les salaires qu'ils distribuent. + non applicable si: ≠ Entreprise . association non lucrative + formule: taxe sur les salaires annuelle / 12 + exemples: + - nom: non applicable par défaut + situation: + salaire brut: 2300 + valeur attendue: 0 + - nom: association non lucrative + # Ce test ne sert qu'à tester la condition "association non lucrative", tant que nous faisons face à la limitation des calculs temporels + situation: + Entreprise . association non lucrative: oui + taxe sur les salaires annuelle: 2639.16 + valeur attendue: 219.93 diff --git a/règles/rémunération-travail/entités/ok/contrat-salarié.yaml b/règles/rémunération-travail/entités/ok/contrat-salarié.yaml index 9e4ae8d82..cd99351e1 100644 --- a/règles/rémunération-travail/entités/ok/contrat-salarié.yaml +++ b/règles/rémunération-travail/entités/ok/contrat-salarié.yaml @@ -196,6 +196,7 @@ formule: somme: - CICE + - CITS - réduction générale - exonération JEI @@ -237,9 +238,6 @@ - - - diff --git a/règles/rémunération-travail/entités/ok/entreprise.yaml b/règles/rémunération-travail/entités/ok/entreprise.yaml index 46dc7aa0e..fdfbe025c 100644 --- a/règles/rémunération-travail/entités/ok/entreprise.yaml +++ b/règles/rémunération-travail/entités/ok/entreprise.yaml @@ -28,3 +28,8 @@ suggestions: 1: 1 5: 5 + +- espace: entreprise + nom: association non lucrative + description: L'entreprise est une association non lucrative + question: S'agit-il d'une association à but non lucratif ? diff --git a/source/components/Results.css b/source/components/Results.css index 80ba36482..926314f9b 100644 --- a/source/components/Results.css +++ b/source/components/Results.css @@ -78,6 +78,7 @@ + #results ul { display: inline-flex; justify-content: space-around; @@ -89,78 +90,6 @@ } -#results li { - margin: 0 1em 0; - text-align: center; - width: 25%; -} - -#results li a { - text-decoration: none; -} - -#results .rule-type { - color: white; - border: none; - font-size: 85%; - line-height: 2em; - font-weight: 600; - margin: .6em 0 .1em; -} - -#results .rule-box { - padding: .6em 1em; - color: #333350; - background: white; - border-radius: 3px; - white-space: nowrap; - color: #333350; - height: 6em; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; -} - -#results .rule-name { - font-size: 175%; - font-weight: 600; -} - -#results li p { - margin: 0; - padding: 0 0; - font-size: 120%; - color: inherit; - width: 100%; -} - -#results li.number p { - color: #4A89DC; - font-weight: bold; -} - - - -#results li.irrelevant .rule-type { - color: rgba(255, 255, 255, 0.35); -} - -#results li.irrelevant .rule-name { - text-decoration: line-through; -} - - -#results li.unsatisfied p { - font-style: italic; -} -#results li.irrelevant p { - font-weight: 600; -} -#results li p .figure { - font-size: 250%; -} - @media (max-width: 1280px) { @@ -201,12 +130,6 @@ width: 100%; font-size: 90%; } - #results ul li .rule-box p { - padding: 0.6em; - } - #results .rule-name { - font-size: 150%; - } } diff --git a/source/components/Results.js b/source/components/Results.js index 2e8a85234..9ef6b4c91 100644 --- a/source/components/Results.js +++ b/source/components/Results.js @@ -4,16 +4,12 @@ import classNames from 'classnames' import {Link} from 'react-router-dom' import {connect} from 'react-redux' import { withRouter } from 'react-router' -import {formValueSelector} from 'redux-form' import './Results.css' -import {capitalise0} from '../utils' -import {computeRuleValue, clearDict} from 'Engine/traverse' +import {clearDict} from 'Engine/traverse' import {encodeRuleName} from 'Engine/rules' import {getObjectives} from 'Engine/generateQuestions' - -let fmt = new Intl.NumberFormat('fr-FR').format -let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits)) +import RuleValueVignette from './rule/RuleValueVignette' @withRouter @connect( @@ -49,54 +45,17 @@ export default class Results extends Component { Reprendre la simulation - :
+ :

Vos résultats

{do {let text = R.path(['simulateur', 'résultats'])(analysedSituation.root) text &&

{text}

}}

Cliquez pour comprendre chaque calcul

-
+
} ) diff --git a/source/components/rule/Examples.css b/source/components/rule/Examples.css index 1313372f4..c2a11d134 100644 --- a/source/components/rule/Examples.css +++ b/source/components/rule/Examples.css @@ -65,12 +65,3 @@ #examples .example.ok i { color: #2ecc71 } - -#reportError { - background: #c0392b; - color: white; - border: none; - font-size: 100%; - padding: .3em .6em; - margin-top: 3em; -} diff --git a/source/components/rule/Examples.js b/source/components/rule/Examples.js index 15e11c24d..c8a8e1a21 100644 --- a/source/components/rule/Examples.js +++ b/source/components/rule/Examples.js @@ -26,14 +26,11 @@ export default class Examples extends Component { )(ex.situation) let runExemple = analyseSituation(rules, rule.name)(v => exempleSituation[v]), - exempleCalculatedValue = runExemple["non applicable si"] && - runExemple["non applicable si"].nodeValue - ? null - : runExemple.formule.nodeValue + exempleValue = runExemple.nodeValue return { ...ex, - ok: Math.abs( ex['valeur attendue'] - exempleCalculatedValue ) < .1, //TODO on peut sûrement faire mieux... + ok: Math.abs( ex['valeur attendue'] - exempleValue ) < .1, //TODO on peut sûrement faire mieux... rule: runExemple } }) @@ -61,11 +58,11 @@ export default class Examples extends Component { : } {nom} - {!ok && + {!ok && focusedExample == nom &&
Ce test ne passe pas {showValues && - : la valeur attendue était {' '} + : le résultat attendu était {' '} {expected} }
@@ -83,12 +80,6 @@ export default class Examples extends Component { } - ) } diff --git a/source/components/rule/Rule.css b/source/components/rule/Rule.css index bb169b599..644e9a4fe 100644 --- a/source/components/rule/Rule.css +++ b/source/components/rule/Rule.css @@ -96,6 +96,16 @@ text-align: center; } +#ruleValue { + margin-left: 3em; + font-weight: 600; +} + + +/* + Règles CSS d'affichage des algorithmes +*/ + #rule-rules { display: inline-flex; justify-content: start; @@ -114,7 +124,6 @@ } #algorithm { - margin-top: 3em; margin-right: 10em; } .dictionaryPanelWrapper { @@ -348,3 +357,14 @@ justify-content: space-between; align-items: baseline; } + + +#reportError { + background: #c0392b; + color: white; + border: none; + font-size: 100%; + padding: .3em .6em; + margin: 3em auto 0; + display: block; +} diff --git a/source/components/rule/Rule.js b/source/components/rule/Rule.js index ffe286451..8c66eb84b 100644 --- a/source/components/rule/Rule.js +++ b/source/components/rule/Rule.js @@ -14,6 +14,7 @@ import References from './References' import Algorithm from './Algorithm' import Examples from './Examples' import Helmet from 'react-helmet' +import {humanFigure} from './RuleValueVignette' @connect( state => ({ @@ -54,12 +55,15 @@ export default class Rule extends Component { // } let - situationExists = !R.isEmpty(this.props.form) + conversationStarted = !R.isEmpty(this.props.form), + situationExists = conversationStarted || this.state.example != null let {type, name, description} = this.rule, destinataire = R.path([type, 'destinataire'])(this.rule), - destinataireData = possiblesDestinataires[destinataire] + destinataireData = possiblesDestinataires[destinataire], + situationOrExampleRule = R.path(['example', 'rule'])(this.state) || this.rule, + ruleValue = situationOrExampleRule.nodeValue return (
@@ -80,17 +84,18 @@ export default class Rule extends Component {

Destinataire

{!destinataireData ? -

Non renseigné

- :
- - {destinataireData.image && - } - {!destinataireData.image && -
{destinataire}
- } -
- {destinataireData.nom &&
{destinataireData.nom}
} -
+

Non renseigné

+ : +
+ + {destinataireData.image && + } + {!destinataireData.image && +
{destinataire}
+ } +
+ {destinataireData.nom &&
{destinataireData.nom}
} +
}
@@ -99,15 +104,32 @@ export default class Rule extends Component { {this.renderReferences(this.rule)}
+
+

Résultat

+

+ {ruleValue == 0 + ? 'Règle non applicable' + : ruleValue == null + ? 'Situation incomplète' + : humanFigure(2)(ruleValue) + ' €'} +

+
+
- + this.setState({example, showValues: true})}/>
+ ) } diff --git a/source/components/rule/RuleValueVignette.css b/source/components/rule/RuleValueVignette.css new file mode 100644 index 000000000..e3ba6ff88 --- /dev/null +++ b/source/components/rule/RuleValueVignette.css @@ -0,0 +1,94 @@ + +.RuleValueVignette { + margin: 0 1em 0; + text-align: center; + width: 25%; +} + +.RuleValueVignette li a { + text-decoration: none; +} +.RuleValueVignette .rule-type { + color: white; + border: none; + font-size: 85%; + line-height: 2em; + font-weight: 600; + margin: .6em 0 .1em; +} + +.RuleValueVignette .rule-box { + padding: .6em 1em; + color: #333350; + background: white; + border-radius: 3px; + white-space: nowrap; + height: 6em; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; +} + +.RuleValueVignette .rule-name { + font-size: 175%; + font-weight: 600; +} + +.RuleValueVignette p { + margin: 0; + padding: 0 0; + font-size: 120%; + color: inherit; + width: 100%; +} +.RuleValueVignette.number p { + color: #4A89DC; + font-weight: bold; +} +.RuleValueVignette.unsatisfied p { + font-style: italic; +} +.RuleValueVignette.irrelevant p { + font-weight: 600; +} +.RuleValueVignette p .figure { + font-size: 250%; +} + + +.RuleValueVignette:not(.unsatisfied):not(.irrelevant) .rule-box { + border-bottom: .8em solid #4A89DC; +} + +.RuleValueVignette:hover .rule-box { + background: #ddd; +} +.RuleValueVignette.irrelevant .rule-box { + background: rgba(255, 255, 255, 0.35); +} + + + +.RuleValueVignette.irrelevant .rule-type { + color: rgba(255, 255, 255, 0.35); +} + +.RuleValueVignette.irrelevant .rule-name { + text-decoration: line-through; +} + + + + +@media (max-width: 1280px) { + + + .RuleValueVignette .rule-box p { + padding: 0.6em; + } + + .RuleValueVignette .rule-name { + font-size: 150%; + } +} diff --git a/source/components/rule/RuleValueVignette.js b/source/components/rule/RuleValueVignette.js new file mode 100644 index 000000000..08fef6d27 --- /dev/null +++ b/source/components/rule/RuleValueVignette.js @@ -0,0 +1,47 @@ +import React from "react" +import {Link} from 'react-router-dom' +import {encodeRuleName} from 'Engine/rules' +import classNames from 'classnames' +import {capitalise0} from '../../utils' +let fmt = new Intl.NumberFormat('fr-FR').format +export let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits)) +import './RuleValueVignette.css' + +export default ({ + name, + type, + conversationStarted, + nodeValue: ruleValue +}) => + do { + let + unsatisfied = ruleValue == null, + irrelevant = ruleValue == 0, + number = typeof ruleValue == 'number' && ruleValue > 0 + + ; + +
+ {type} +
+
+
+ {capitalise0(name)} +
+

+ {conversationStarted && + (irrelevant + ? "Vous n'êtes pas concerné" + : unsatisfied + ? "En attente de vos réponses..." + : + {humanFigure(2)(ruleValue) + "€"} + )} +

+
+ +
+ } diff --git a/source/engine/traverse.js b/source/engine/traverse.js index 88762e651..d4e9454ab 100644 --- a/source/engine/traverse.js +++ b/source/engine/traverse.js @@ -328,20 +328,17 @@ let treat = (rules, rule) => rawNode => { let parsedNode = onNodeType(rawNode) return parsedNode.evaluate ? parsedNode : - {...parsedNode, evaluate: defaultEvaluate} + {...parsedNode, evaluate: defaultEvaluate} } //TODO c'est moche : export let computeRuleValue = (formuleValue, condValue) => condValue === undefined - ? formuleValue - : formuleValue === 0 - ? 0 - : condValue === null - ? null - : condValue === true + ? formuleValue + : formuleValue === 0 ? 0 - : formuleValue + : condValue === null ? null : condValue === true ? 0 : formuleValue + export let treatRuleRoot = (rules, rule) => { let evaluate = (situationGate, parsedRules, r) => {