⚙️ Ajoute la vue détaillée des résultats

pull/138/head
Laurent Bossavit 2017-11-28 23:26:04 +01:00 committed by mama
parent 64963afa2c
commit 776aaa1e99
10 changed files with 145 additions and 40 deletions

View File

@ -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 lEmploi des Cadres)
références:
chiffres clés: http://www.agirc-arrco.fr/l-agirc-et-larrco/chiffres-cles

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 (
<div className="somme">
<table>
<thead>
<td class="blank indent"></td>
<td class="blank indent">Branche</td>
<td class="blank indent">Part salarié</td>
<td class="blank indent">Part employeur</td>
<tr>
<td className="element"></td>
<td colSpan="2" className="element">{humanFigure(2)(brut)} (salaire brut)</td>
<td colSpan="2" className="element">{humanFigure(2)(brut)} (salaire brut)</td>
</tr>
</thead>
<tbody></tbody>
<tbody>
{R.keys(results).map(
branch => {
let props = {branch, analysis}
return <Row key={branch} {...props} />
})}
<tr>
<td className="element"></td>
<td className="operator">=</td>
<td className="element">{humanFigure(2)(net)} (salaire net)</td>
<td className="operator">=</td>
<td className="element">{humanFigure(2)(total)} (salaire total)</td>
</tr>
</tbody>
</table>
</div>
)
}
}
class Row extends Component {
render() {
let { branch, analysis } = this.props
return (
<tr>
<td className="element">{capitalise0(branch)}</td>
<td className="operator">-</td>
<td className="element">{humanFigure(2)(cell(branch,"salarié",analysis))}</td>
<td className="operator">+</td>
<td className="element">{humanFigure(2)(cell(branch,"employeur",analysis))}</td>
</tr>
)
}
}

View File

@ -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
}}
/>
<ResultsGrid />
</div>
)
}

View File

@ -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)
}
}

View File

@ -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)
});
});