diff --git a/règles/rémunération-travail/cotisations/ok/apec.yaml b/règles/rémunération-travail/cotisations/ok/apec.yaml index 137c42d8e..6e9aa7896 100644 --- a/règles/rémunération-travail/cotisations/ok/apec.yaml +++ b/règles/rémunération-travail/cotisations/ok/apec.yaml @@ -1,11 +1,11 @@ - espace: contrat salarié nom: APEC cotisation: - branche: retraite + branche: chômage type de retraite: complémentaire destinataire: APEC description: | - Cotisation de retraite complémentaire cadre, pour le fonctionnement de l'APEC + Cotisation chômage complémentaire cadre, pour le fonctionnement de l'APEC (Association Pour l’Emploi des Cadres) références: chiffres clés: http://www.agirc-arrco.fr/l-agirc-et-larrco/chiffres-cles diff --git a/règles/rémunération-travail/cotisations/ok/complémentaire-santé.yaml b/règles/rémunération-travail/cotisations/ok/complémentaire-santé.yaml index bc1162c45..53aede3da 100644 --- a/règles/rémunération-travail/cotisations/ok/complémentaire-santé.yaml +++ b/règles/rémunération-travail/cotisations/ok/complémentaire-santé.yaml @@ -1,5 +1,7 @@ - espace: contrat salarié nom: complémentaire santé + cotisation: + branche: maladie références: Complémentaire santé d'entreprise: https://www.service-public.fr/particuliers/vosdroits/F20739 formule: diff --git a/règles/rémunération-travail/cotisations/ok/csg.yaml b/règles/rémunération-travail/cotisations/ok/csg.yaml index 29b38b5d2..31e6a309a 100644 --- a/règles/rémunération-travail/cotisations/ok/csg.yaml +++ b/règles/rémunération-travail/cotisations/ok/csg.yaml @@ -26,7 +26,7 @@ - espace: contrat salarié nom: CSG - attributs: + cotisation: impôt: oui dû par: salarié description: | @@ -46,7 +46,7 @@ - espace: contrat salarié nom: CRDS - attributs: + cotisation: impôt: oui dû par: salarié description: Contribution pour le remboursement de la dette sociale diff --git a/règles/rémunération-travail/cotisations/ok/formation-professionnelle.yaml b/règles/rémunération-travail/cotisations/ok/formation-professionnelle.yaml index 9f2176a77..44bfa87e8 100644 --- a/règles/rémunération-travail/cotisations/ok/formation-professionnelle.yaml +++ b/règles/rémunération-travail/cotisations/ok/formation-professionnelle.yaml @@ -1,6 +1,6 @@ - espace: contrat salarié nom: formation professionnelle - attributs: + cotisation: dû par: employeur collecteur: OPCA # TODO majoration pour les entreprises de travail temporaire diff --git a/règles/rémunération-travail/cotisations/ok/prévoyance-obligatoire-cadre.yaml b/règles/rémunération-travail/cotisations/ok/prévoyance-obligatoire-cadre.yaml index 4a0e40e29..244f4c2cc 100644 --- a/règles/rémunération-travail/cotisations/ok/prévoyance-obligatoire-cadre.yaml +++ b/règles/rémunération-travail/cotisations/ok/prévoyance-obligatoire-cadre.yaml @@ -4,7 +4,7 @@ # situation affinée - espace: contrat salarié nom: prévoyance obligatoire cadre - attributs: + cotisation: dû par: employeur références: minimum: http://www.axios.fr/150-tranche-a-evitez-une-erreur-a-160-000-euros diff --git a/règles/rémunération-travail/cotisations/ok/pénibilité.yaml b/règles/rémunération-travail/cotisations/ok/pénibilité.yaml index 2fbb67aab..b482db80a 100644 --- a/règles/rémunération-travail/cotisations/ok/pénibilité.yaml +++ b/règles/rémunération-travail/cotisations/ok/pénibilité.yaml @@ -23,8 +23,8 @@ - espace: contrat salarié nom: cotisation pénibilité - attributs: - branche: santé # ou vieillesse car ouvrant droit à une retraite anticipée ? + cotisation: + branche: maladie # ou vieillesse car ouvrant droit à une retraite anticipée ? description: Les dépenses liées à l'utilisation du compte pénibilité par le salarié sont prises en charge par un fonds financé par l'employeur références: - https://www.service-public.fr/professionnels-entreprises/vosdroits/F33777 diff --git a/source/components/ResultsGrid.js b/source/components/ResultsGrid.js index 90bc468d4..937505a54 100644 --- a/source/components/ResultsGrid.js +++ b/source/components/ResultsGrid.js @@ -6,22 +6,38 @@ import {connect} from 'react-redux' import { withRouter } from 'react-router' import './Results.css' +import '../engine/mecanismViews/Somme.css' import {clearDict} from 'Engine/traverse' import {encodeRuleName} from 'Engine/rules' import RuleValueVignette from './rule/RuleValueVignette' +import { humanFigure } from "./rule/RuleValueVignette" -export let branches = (parsedRules, analysis) => { +import { capitalise0 } from '../utils' + +// Filtered variables and rules can't be filtered in a uniform way, for now +let paidBy = which => R.pathEq(['explanation', 'cotisation','dû par'], which) +let filteredBy = which => R.pathEq(['cotisation','dû par'], which) + +export let byName = branch => R.groupBy(R.prop('dottedName'), branch) + +export let cell = (branch, payer, analysis) => { + let row = byBranch(analysis)[branch], + items = R.filter(item => paidBy(payer)(item) || filteredBy(payer)(item),row), + values = R.map(R.prop('nodeValue'),items) + + return R.sum(values) +} + +export let byBranch = (analysis) => { let sal = analysis.dict['contrat salarié . cotisations salariales'] let pat = analysis.dict['contrat salarié . cotisations patronales'] - let l1 = sal ? sal.explanation.formule.explanation.explanation : [] - let l2 = pat ? pat.explanation.formule.explanation.explanation : [] + let l1 = sal ? sal.explanation.formule.explanation.explanation : [], + l2 = pat ? pat.explanation.formule.explanation.explanation : [], + explanations = R.concat(l1, l2), + byBranch = R.groupBy(R.pathOr('autre',['explanation','cotisation','branche']), explanations) - let explanations = R.concat(l1, l2), - names = R.map(R.pathOr('autre',['explanation','cotisation','branche']), explanations), - result = R.uniq(names) - - return result + return byBranch } @withRouter @@ -29,6 +45,7 @@ export let branches = (parsedRules, analysis) => { state => ({ analysis: state.analysis, targetNames: state.targetNames, + situationGate : state.situationGate }) ) export default class ResultsGrid extends Component { @@ -36,21 +53,63 @@ export default class ResultsGrid extends Component { let { analysis, targetNames, + situationGate, location } = this.props if (!analysis) return null + let extract = x => (x && x.nodeValue) || 0, + fromSituation = name => situationGate(name), + fromEval = name => R.find(R.propEq('dottedName',name),analysis.targets), + get = name => extract(fromSituation(name) || fromEval(name)) + let results = byBranch(analysis), + brut = get('contrat salarié . salaire brut'), + net = get('contrat salarié . salaire net'), + total = get('contrat salarié . salaire total') + return ( +
- - - - + + + + + - + + {R.keys(results).map( + branch => { + let props = {branch, analysis} + return + })} + + + + + + + +
BranchePart salariéPart employeur
{humanFigure(2)(brut)} (salaire brut){humanFigure(2)(brut)} (salaire brut)
={humanFigure(2)(net)} (salaire net)={humanFigure(2)(total)} (salaire total)
+
+ ) + } +} + +class Row extends Component { + render() { + let { branch, analysis } = this.props + + return ( + + {capitalise0(branch)} + - + {humanFigure(2)(cell(branch,"salarié",analysis))} + + + {humanFigure(2)(cell(branch,"employeur",analysis))} + ) } } diff --git a/source/components/Simulateur.js b/source/components/Simulateur.js index d2df0c04f..12e2542b3 100644 --- a/source/components/Simulateur.js +++ b/source/components/Simulateur.js @@ -15,6 +15,7 @@ import { makeQuestion } from 'Engine/generateQuestions' import ReactPiwik from './Tracker' import Results from 'Components/Results' +import ResultsGrid from 'Components/ResultsGrid' @withRouter @connect( @@ -114,6 +115,7 @@ export default class extends Component { textColourOnWhite: themeColours.textColourOnWhite }} /> + ) } diff --git a/source/engine/traverse.js b/source/engine/traverse.js index 5283ce047..658339fb7 100644 --- a/source/engine/traverse.js +++ b/source/engine/traverse.js @@ -76,14 +76,19 @@ par exemple ainsi : https://github.com/Engelberg/instaparse#transforming-the-tre */ +// TODO - this is becoming overly specific let fillFilteredVariableNode = (rules, rule) => (filter, parseResult) => { let evaluateFiltered = originalEval => (situation, parsedRules, node) => { let newSituation = name => (name == 'sys.filter' ? filter : situation(name)) return originalEval(newSituation, parsedRules, node) } - let node = fillVariableNode(rules, rule, filter)(parseResult) + let node = fillVariableNode(rules, rule, filter)(parseResult), + // Decorate node with who's paying + cotisation = {...node.cotisation, "dû par":filter} + return { ...node, + cotisation, evaluate: evaluateFiltered(node.evaluate) } } diff --git a/test/results.grid.test.js b/test/results.grid.test.js index cac45d560..de33962cc 100644 --- a/test/results.grid.test.js +++ b/test/results.grid.test.js @@ -4,7 +4,7 @@ import {rules as realRules, enrichRule} from '../source/engine/rules' import {analyse, parseAll} from '../source/engine/traverse' import {reduceSteps} from '../source/reducers' -import {branches} from '../source/components/ResultsGrid.js' +import {byBranch, byName, cell} from '../source/components/ResultsGrid.js' let tracker = {push: array => null} @@ -22,14 +22,14 @@ describe('results grid', function() { var step2 = reducer(step1,{type:'STEP_ACTION', name: 'fold', step: 'contrat salarié . salaire de base'}) let analysis = step2.analysis, - parsedRules = step2.parsedRules, - result = branches(parsedRules,analysis) + result = byBranch(analysis), + branches = R.keys(result) - expect(result).to.have.lengthOf(4) - expect(result).to.include("chômage") - expect(result).to.include("maladie") - expect(result).to.include("retraite") - expect(result).to.include("autre") + expect(branches).to.have.lengthOf(4) + expect(branches).to.include("chômage") + expect(branches).to.include("maladie") + expect(branches).to.include("retraite") + expect(branches).to.include("autre") }); it('should collect branches with both targets', function() { @@ -44,18 +44,55 @@ describe('results grid', function() { var step2 = reducer(step1,{type:'STEP_ACTION', name: 'fold', step: 'contrat salarié . salaire de base'}) let analysis = step2.analysis, - parsedRules = step2.parsedRules, - result = branches(parsedRules,analysis) + result = byBranch(analysis), + branches = R.keys(result) - expect(result).to.have.lengthOf(6) - expect(result).to.include("chômage") - expect(result).to.include("maladie") - expect(result).to.include("retraite") - expect(result).to.include("logement") - expect(result).to.include("famille") - expect(result).to.include("autre") + expect(branches).to.have.lengthOf(6) + expect(branches).to.include("chômage") + expect(branches).to.include("maladie") + expect(branches).to.include("retraite") + expect(branches).to.include("logement") + expect(branches).to.include("famille") + expect(branches).to.include("autre") }); - // expect(cell("maladie","salarié",result)).to.be.closeTo(37, 2) + it('should collect cells by name', function() { + let fakeState = {} + let stateSelector = state => name => fakeState[name] + + let rules = realRules.map(enrichRule), + reducer = reduceSteps(tracker, rules, stateSelector) + + var step1 = reducer({foldedSteps: []},{type:'START_CONVERSATION', targetNames: ['salaire net', 'salaire total']}) + fakeState['contrat salarié . salaire de base'] = 2300 + var step2 = reducer(step1,{type:'STEP_ACTION', name: 'fold', step: 'contrat salarié . salaire de base'}) + + let analysis = step2.analysis, + result = byBranch(analysis), + maladie = byName(result.maladie), + names = R.keys(maladie) + + expect(names).to.have.lengthOf(3) + expect(names).to.include("contrat salarié . maladie") + expect(names).to.include("contrat salarié . ATMP") + expect(names).to.include("contrat salarié . cotisation pénibilité") + }); + + it('should sum cells by branch and payer', function() { + let fakeState = {} + let stateSelector = state => name => fakeState[name] + + let rules = realRules.map(enrichRule), + reducer = reduceSteps(tracker, rules, stateSelector) + + var step1 = reducer({foldedSteps: []},{type:'START_CONVERSATION', targetNames: ['salaire net', 'salaire total']}) + fakeState['contrat salarié . salaire de base'] = 2300 + var step2 = reducer(step1,{type:'STEP_ACTION', name: 'fold', step: 'contrat salarié . salaire de base'}) + + let analysis = step2.analysis + + expect(cell("retraite","salarié",analysis)).to.be.closeTo(257, 5) + expect(cell("autre","salarié",analysis)).to.be.closeTo(200, 5) + }); });