⚙️ Implémentation du mécanisme barème
parent
370e2a61be
commit
54e06324a0
|
@ -10,7 +10,6 @@ https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages
|
|||
- 💚 `:green_heart:` when fixing the CI build
|
||||
- ✅ `:white_check_mark:` when adding tests
|
||||
- ⬆️ `:arrow_up:` when upgrading dependencies
|
||||
- ⬇️ `:arrow_down:` when downgrading dependencies
|
||||
|
||||
|
||||
Et ceux spécifiques au projet :
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
|
||||
Version 'OpenFisca'
|
||||
|
||||
```yaml
|
||||
- seuil: 0
|
||||
taux: 0%
|
||||
- seuil: 1
|
||||
taux: 7.8%
|
||||
- seuil: 8
|
||||
taux: 0%
|
||||
```
|
||||
Le problème de cette syntaxe, c'est que ce ne sont pas des tranches : la tranche 1 ne contient pas assez d'informations pour être calculée !
|
||||
|
||||
Version plus explicite :
|
||||
|
||||
```yaml
|
||||
- de: 0
|
||||
à: 1
|
||||
taux: 0%
|
||||
- de: 1
|
||||
à: 8
|
||||
taux: 7.8%
|
||||
- de: 8
|
||||
taux: 0%
|
||||
```
|
||||
|
||||
Ou, étant donné que les barèmes se suivent a priori toujours et commencent à zéro :
|
||||
|
||||
```yaml
|
||||
- en-dessous de: 1
|
||||
taux: 0%
|
||||
- de: 1
|
||||
à: 8
|
||||
taux: 7.8%
|
||||
- au-dessus de: 8
|
||||
taux: 0%
|
||||
```
|
||||
|
||||
Autre version possible :
|
||||
|
||||
```yaml
|
||||
seuils:
|
||||
0: 0%
|
||||
1: 7.8%
|
||||
8: 0%
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -7,3 +7,6 @@ URSSAF:
|
|||
salarié:
|
||||
nom: Le salarié
|
||||
image: salarié.png
|
||||
AGIRC:
|
||||
image: agirc.png
|
||||
lien: https://fr.wikipedia.org/wiki/Agirc_et_Arrco
|
||||
|
|
|
@ -24,10 +24,11 @@
|
|||
qui apporte à l'employeur plus de souplesse dans un cadre législatif précis, comportant en particulier des contreparties financières.
|
||||
formule:
|
||||
somme: #TODO à l'avenir, exprimer une somme par requête de type : obligation applicable au CDD
|
||||
- CDD . CIF CDD #cotisation
|
||||
- CDD . majoration chômage CDD #cotisation
|
||||
- CDD . prime de fin de contrat #indemnité
|
||||
- CDD . compensation des congés payés #indemnité
|
||||
# - CDD . CIF CDD #cotisation
|
||||
# - CDD . majoration chômage CDD #cotisation
|
||||
# - CDD . prime de fin de contrat #indemnité
|
||||
# - CDD . compensation des congés payés #indemnité
|
||||
- AGIRC simple
|
||||
|
||||
- Variable: assiette cotisations sociales
|
||||
attache: Salariat
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
- Cotisation: AGIRC simple
|
||||
attache: Salariat
|
||||
attributs:
|
||||
branche: retraite
|
||||
type de retraite: complémentaire
|
||||
destinataire: AGIRC
|
||||
description: |
|
||||
Cotisation de retraite complémentaire cadre. Complète le régime ARRCO
|
||||
(Association Générale des Institutions de Retraite des Cadres)
|
||||
références:
|
||||
calcul des cotisations: http://www.agirc-arrco.fr/entreprises/gerer-les-salaries/calcul-des-cotisations/
|
||||
garantie minimale de points: http://www.journaldunet.com/management/pratique/primes-et-avantages/5079/gmp-2016-la-garantie-minimale-de-points-calcul-et-montant.html
|
||||
# concerne:
|
||||
# - statut cadre = oui
|
||||
|
||||
formule:
|
||||
barème:
|
||||
assiette: salaire de base
|
||||
multiplicateur des tranches: plafond sécurité sociale
|
||||
tranches:
|
||||
- en-dessous de: 1
|
||||
taux: 0%
|
||||
- de: 1
|
||||
à: 8
|
||||
taux: 7.8%
|
||||
# 2016: 7.8%
|
||||
# 2015: 7.8%
|
||||
# 2014: 7.75%
|
||||
# 2006: 7.7%
|
||||
# 1999: 7.5%
|
||||
# 1998: 6.875%
|
||||
# 1997: 6.25%
|
||||
# 1996: 5.625%
|
||||
# 1995: 5%
|
||||
# 1994: 3.63%
|
||||
# 1993-07: 2.34%
|
||||
- au-dessus de: 8
|
||||
taux: 0%
|
||||
|
||||
|
||||
notes: |
|
||||
Il éxiste une tranche C, de 4 à 8 fois la base, sur laquelle la répartition des cotisations est décidée au sein de l’entreprise jusqu’à 20 %. De 20 % à 20,30 %, la répartition est la suivante : 66,67 % à la charge du salarié et 33,33 % pour l’employeur.
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
formule:
|
||||
barème:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
composantes:
|
||||
- attributs:
|
||||
dû par: employeur
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
formule:
|
||||
barème:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
composantes:
|
||||
- attributs:
|
||||
dû par: employeur
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
Attention: les tranches du barème sont différentes pour les cadres et non-cadres, en valeur et en nombres.
|
||||
|
||||
marginalRateTaxScale:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
|
||||
- variable: AGFF
|
||||
tags:
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
Il éxiste une tranche C, de 4 à 8 fois la base, sur laquelle la répartition des cotisations est décidée au sein de l’entreprise jusqu’à 20 %. De 20 % à 20,30 %, la répartition est la suivante : 66,67 % à la charge du salarié et 33,33 % pour l’employeur.
|
||||
|
||||
marginalRateTaxScale:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
|
||||
- variable: AGIRC
|
||||
tags:
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
destinataire: ARRCO
|
||||
|
||||
marginalRateTaxScale:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
|
||||
|
||||
- variable: ARRCO
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
formule:
|
||||
barème:
|
||||
assiette: assiette sécurité sociale
|
||||
assiette: assiette cotisations sociales
|
||||
multiplicateur: plafond sécurité sociale
|
||||
composantes:
|
||||
- attributs:
|
||||
|
|
|
@ -16,10 +16,10 @@ let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits))
|
|||
export default class Results extends Component {
|
||||
render() {
|
||||
let {analysedSituation, pointedOutObjectives} = this.props
|
||||
|
||||
// On travaille sur un objectif qui est une somme de plusieurs variables, et c'est ces variables que nous affichons comme résultats
|
||||
// On travaille pour l'instant sur un objectif qui est une somme de plusieurs variables, et c'est ces variables que nous affichons comme résultats. D'où ce chemin :
|
||||
let explanation = R.path(['formule', 'explanation', 'explanation'])(analysedSituation)
|
||||
if (!explanation) return null
|
||||
console.log('explanation', explanation)
|
||||
|
||||
return (
|
||||
<section id="results">
|
||||
|
@ -29,12 +29,13 @@ export default class Results extends Component {
|
|||
</div>
|
||||
<ul>
|
||||
{explanation.map(
|
||||
({variableName, nodeValue, explanation: {name, type, 'non applicable si': {nodeValue: nonApplicable}, formule: {nodeValue: computedValue}}}) =>
|
||||
({variableName, nodeValue, explanation: {name, type, 'non applicable si': nonApplicable, formule: {nodeValue: computedValue}}}) =>
|
||||
do {
|
||||
let
|
||||
unsatisfied = nodeValue == null,
|
||||
irrelevant = nonApplicable === true || computedValue == 0,
|
||||
number = nonApplicable == false && computedValue != null,
|
||||
nonApplicableValue = nonApplicable ? nonApplicable.nodeValue : false,
|
||||
irrelevant = nonApplicableValue === true || computedValue == 0,
|
||||
number = nonApplicableValue == false && computedValue != null,
|
||||
pointedOut = pointedOutObjectives.find(objective => objective == variableName)
|
||||
|
||||
;<li key={name} className={classNames({unsatisfied, irrelevant, number, pointedOut})}>
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
}
|
||||
#references img {
|
||||
vertical-align: sub;
|
||||
height: 1.2em;
|
||||
height: 1.2em;
|
||||
border-radius: .3em;
|
||||
margin-left: .6em;
|
||||
}
|
||||
|
@ -238,6 +238,7 @@
|
|||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-bottom: 1em;
|
||||
margin-right: 0.8em;
|
||||
}
|
||||
|
||||
.multiplication li .key {
|
||||
|
@ -290,10 +291,10 @@
|
|||
.variable .name {
|
||||
color: #5B5B73;
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
padding: .05em 1em;
|
||||
line-height: 1.8em;
|
||||
border-radius: .4em;
|
||||
margin-top: 1em;
|
||||
padding: .05em 1em;
|
||||
line-height: 1.8em;
|
||||
border-radius: .4em;
|
||||
font-weight: 600;
|
||||
font-size: 90%;
|
||||
border: 1px solid rgba(51, 51, 80, 0.25);
|
||||
|
@ -316,3 +317,15 @@
|
|||
.json {
|
||||
font-size: 60%;
|
||||
}
|
||||
|
||||
.barème table {
|
||||
width: 80%;
|
||||
text-align: left;
|
||||
}
|
||||
.barème table td {
|
||||
padding: .1em .4em;
|
||||
/*border: 1px solid #aaa;*/
|
||||
}
|
||||
.barème table tr:nth-child(2n) {
|
||||
background: #f1f1f1
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {connect} from 'react-redux'
|
|||
import mockSituation from '../engine/mockSituation.yaml'
|
||||
import {START_CONVERSATION} from '../actions'
|
||||
import classNames from 'classnames'
|
||||
import destinataires from '../../règles/destinataires/destinataires.yaml'
|
||||
import possiblesDestinataires from '../../règles/destinataires/destinataires.yaml'
|
||||
import references from '../../règles/références/références.yaml'
|
||||
import {capitalise0} from '../utils'
|
||||
import knownMecanisms from '../engine/known-mecanisms.yaml'
|
||||
|
@ -55,7 +55,7 @@ export default class Rule extends Component {
|
|||
situationExists = !R.isEmpty(form)
|
||||
|
||||
let destinataire = R.path(['attributs', 'destinataire'])(rule),
|
||||
destinataireData = destinataires[destinataire]
|
||||
destinataireData = possiblesDestinataires[destinataire]
|
||||
|
||||
|
||||
return (
|
||||
|
@ -73,14 +73,20 @@ export default class Rule extends Component {
|
|||
</div>
|
||||
<div id="destinataire">
|
||||
<h2>Destinataire</h2>
|
||||
<a href={destinataireData.lien} target="_blank">
|
||||
{destinataireData.image &&
|
||||
<img src={require('../../règles/destinataires/' + destinataireData.image)} /> }
|
||||
{!destinataireData.image &&
|
||||
<div id="calligraphy">{destinataire}</div>
|
||||
}
|
||||
</a>
|
||||
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
|
||||
{!destinataireData ?
|
||||
<p>Non renseigné</p>
|
||||
: <div>
|
||||
<a href={destinataireData.lien} target="_blank">
|
||||
{destinataireData.image &&
|
||||
<img src={require('../../règles/destinataires/' + destinataireData.image)} /> }
|
||||
{!destinataireData.image &&
|
||||
<div id="calligraphy">{destinataire}</div>
|
||||
}
|
||||
</a>
|
||||
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h2>Références</h2>
|
||||
|
@ -169,8 +175,9 @@ class Algorithm extends React.Component {
|
|||
<div id="algorithm">
|
||||
<section id="rule-rules" className={classNames({showValues})}>
|
||||
{ do {
|
||||
// TODO ce let est incompréhensible !
|
||||
let [,cond] =
|
||||
R.toPairs(rule).find(([,v]) => v.rulePropType == 'cond') || []
|
||||
R.toPairs(rule).find(([,v]) => v && v.rulePropType == 'cond') || []
|
||||
cond != null &&
|
||||
<section id="declenchement">
|
||||
<h2>Conditions de déclenchement</h2>
|
||||
|
|
|
@ -70,17 +70,8 @@ non applicable si:
|
|||
|
||||
La formule est donc à ignorer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# à venir
|
||||
# barème:
|
||||
# description: |
|
||||
# C'est un barème en taux marginaux, mécanisme de calcul connu pour l'impôt sur le revenu.
|
||||
# L'assiette est divisée en plusieurs tranches, multipliées par un taux prope à chaque tranche.
|
||||
# Les tranches sont très souvent exprimées sous forme de facteurs (par exemple [1, 2, 4]) d'une variable, couramment le plafonde de la sécurité sociale.
|
||||
barème:
|
||||
description: |
|
||||
C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation dans le calcul de l'impôt sur le revenu.
|
||||
L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un taux spécifique.
|
||||
Les tranches sont très souvent exprimées sous forme de facteurs (par exemple [1, 2, 4]) d'une variable que l'on appelle multiplicateur, par exemple le plafond de la sécurité sociale.
|
||||
|
|
|
@ -2,10 +2,10 @@ import React from 'react'
|
|||
import R from 'ramda'
|
||||
import classNames from 'classnames'
|
||||
|
||||
let treatValue = data =>
|
||||
let treatValue = data => console.log('data', data) ||
|
||||
data == null
|
||||
? '?'
|
||||
: R.is(Number)(data) ? Math.round(data) : data ? 'oui' : 'non'
|
||||
: !isNaN(data) ? Math.round(+data) : data ? 'oui' : 'non'
|
||||
|
||||
let NodeValue = ({data}) => (
|
||||
<span className={'situationValue ' + treatValue(data)}>
|
||||
|
|
|
@ -497,6 +497,97 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
}
|
||||
}
|
||||
|
||||
if (k === 'barème') {
|
||||
// Sous entendu : barème en taux marginaux. A étendre (avec une propriété type ?) quand les règles en contiendront d'autres.
|
||||
let
|
||||
val = node => node.nodeValue,
|
||||
assiette = reTreat(v['assiette']),
|
||||
multiplicateur = reTreat(v['multiplicateur des tranches']),
|
||||
|
||||
/* on réécrit en plus bas niveau les tranches :
|
||||
`en-dessous de: 1`
|
||||
devient
|
||||
```
|
||||
de: 0
|
||||
à: 1
|
||||
```
|
||||
*/
|
||||
tranches = v['tranches'].map(t =>
|
||||
R.has('en-dessous de')(t) ? {de: 0, 'à': t['en-dessous de'], taux: t.taux}
|
||||
: R.has('au-dessus de')(t) ? {de: t['au-dessus de'], 'à': Infinity, taux: t.taux}
|
||||
: t
|
||||
),
|
||||
//TODO appliquer retreat() à de, à, taux pour qu'ils puissent contenir des calculs ou pour les cas où toutes les tranches n'ont pas un multiplicateur commun (ex. plafond sécurité sociale). Il faudra alors vérifier leur nullité comme ça :
|
||||
/*
|
||||
nulled = assiette.nodeValue == null || R.any(
|
||||
R.pipe(
|
||||
R.values, R.map(val), R.any(R.equals(null))
|
||||
)
|
||||
)(tranches),
|
||||
*/
|
||||
// nulled = anyNull([assiette, multiplicateur]),
|
||||
nulled = val(assiette) == null || val(multiplicateur) == null,
|
||||
|
||||
nodeValue =
|
||||
nulled ?
|
||||
null
|
||||
: tranches.reduce((memo, {de: min, 'à': max, taux}) =>
|
||||
( val(assiette) < ( min * val(multiplicateur) ) )
|
||||
? memo + 0
|
||||
: memo
|
||||
+ ( Math.min(val(assiette), max * val(multiplicateur)) - (min * val(multiplicateur)) )
|
||||
* transformPercentage(taux)
|
||||
, 0)
|
||||
|
||||
return {
|
||||
nodeValue,
|
||||
category: 'mecanism',
|
||||
name: 'barème',
|
||||
barème: 'en taux marginaux',
|
||||
type: 'numeric',
|
||||
explanation: {
|
||||
assiette,
|
||||
multiplicateur,
|
||||
tranches
|
||||
},
|
||||
jsx: <Node
|
||||
classes="mecanism barème"
|
||||
name="barème"
|
||||
value={nodeValue}
|
||||
child={
|
||||
<ul>
|
||||
<li key="assiette">
|
||||
<span className="key">assiette: </span>
|
||||
<span className="value">{assiette.jsx}</span>
|
||||
</li>
|
||||
<li key="multiplicateur">
|
||||
<span className="key">multiplicateur des tranches: </span>
|
||||
<span className="value">{multiplicateur.jsx}</span>
|
||||
</li>
|
||||
<table className="tranches">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tranche de l'assiette</th>
|
||||
<th>Taux</th>
|
||||
</tr>
|
||||
{v['tranches'].map(({'en-dessous de': maxOnly, 'au-dessus de': minOnly, de: min, 'à': max, taux}) =>
|
||||
<tr key={min || minOnly}>
|
||||
<td>
|
||||
{ maxOnly ? 'En dessous de ' + maxOnly
|
||||
: minOnly ? 'Au dessus de ' + minOnly
|
||||
: `De ${min} à ${max}` }
|
||||
</td>
|
||||
<td> {taux} </td>
|
||||
</tr>
|
||||
)}
|
||||
</thead>
|
||||
</table>
|
||||
</ul>
|
||||
}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
if (k === 'le maximum de') {
|
||||
let contenders = v.map(treat(situationGate, rule)),
|
||||
contenderValues = R.pluck('nodeValue')(contenders),
|
||||
|
|
Loading…
Reference in New Issue