2017-06-28 07:31:37 +00:00
|
|
|
import R from 'ramda'
|
|
|
|
import React from 'react'
|
|
|
|
import {anyNull, val} from './traverse-common-functions'
|
|
|
|
import {Node, Leaf} from './traverse-common-jsx'
|
2017-07-16 21:58:06 +00:00
|
|
|
import {makeJsx, evaluateNode, rewriteNode, evaluateArray, evaluateArrayWithFilter, evaluateObject, parseObject, collectNodeMissing} from './evaluation'
|
2017-09-18 22:17:02 +00:00
|
|
|
import {findRuleByName} from './rules'
|
2017-07-13 19:55:59 +00:00
|
|
|
|
2017-09-19 16:56:29 +00:00
|
|
|
import 'react-virtualized/styles.css'
|
|
|
|
import {Table, Column} from 'react-virtualized'
|
2017-10-02 19:29:07 +00:00
|
|
|
import taux_versement_transport from 'Règles/rémunération-travail/cotisations/ok/liste-taux.json'
|
2017-09-19 16:56:29 +00:00
|
|
|
|
2017-09-30 19:11:33 +00:00
|
|
|
let constantNode = constant => ({nodeValue: constant, jsx: nodeValue => <span className="value">{nodeValue}</span>})
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-07-28 12:24:29 +00:00
|
|
|
let decompose = (recurse, k, v) => {
|
2017-06-29 06:51:40 +00:00
|
|
|
let
|
|
|
|
subProps = R.dissoc('composantes')(v),
|
2017-07-16 21:58:06 +00:00
|
|
|
explanation = v.composantes.map(c =>
|
2017-06-29 06:51:40 +00:00
|
|
|
({
|
|
|
|
... recurse(
|
|
|
|
R.objOf(k,
|
|
|
|
{
|
|
|
|
... subProps,
|
|
|
|
... R.dissoc('attributs')(c)
|
|
|
|
})
|
|
|
|
),
|
|
|
|
composante: c.nom ? {nom: c.nom} : c.attributs
|
|
|
|
})
|
2017-07-11 14:39:05 +00:00
|
|
|
)
|
2017-06-29 06:51:40 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-29 06:51:40 +00:00
|
|
|
classes="mecanism composantes"
|
|
|
|
name="composantes"
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-06-29 06:51:40 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-11 14:39:05 +00:00
|
|
|
{ explanation.map((c, i) =>
|
2017-06-29 06:51:40 +00:00
|
|
|
[<li className="composante" key={JSON.stringify(c.composante)}>
|
|
|
|
<ul className="composanteAttributes">
|
|
|
|
{R.toPairs(c.composante).map(([k,v]) =>
|
|
|
|
<li>
|
|
|
|
<span>{k}: </span>
|
|
|
|
<span>{v}</span>
|
|
|
|
</li>
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
<div className="content">
|
2017-07-13 19:55:59 +00:00
|
|
|
{makeJsx(c)}
|
2017-06-29 06:51:40 +00:00
|
|
|
</div>
|
|
|
|
</li>,
|
2017-07-11 14:39:05 +00:00
|
|
|
i < (explanation.length - 1) && <li className="composantesSymbol"><i className="fa fa-plus-circle" aria-hidden="true"></i></li>
|
2017-07-13 19:55:59 +00:00
|
|
|
])
|
2017-06-29 06:51:40 +00:00
|
|
|
}
|
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
2017-07-28 11:42:48 +00:00
|
|
|
let filter = situationGate => c => (!situationGate("sys.filter") || !c.composante || !c.composante['dû par']) || c.composante['dû par'] == situationGate("sys.filter")
|
2017-07-17 12:01:48 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
return {
|
|
|
|
explanation,
|
|
|
|
jsx,
|
2017-07-16 21:58:06 +00:00
|
|
|
evaluate: evaluateArrayWithFilter(filter,R.add,0),
|
2017-07-13 19:55:59 +00:00
|
|
|
category: 'mecanism',
|
|
|
|
name: 'composantes',
|
|
|
|
type: 'numeric'
|
2017-06-29 06:51:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-28 12:24:29 +00:00
|
|
|
let devariate = (recurse, k, v) => {
|
|
|
|
let
|
|
|
|
subProps = R.dissoc('variations')(v),
|
|
|
|
explanation = v.variations.map(c =>
|
|
|
|
({
|
|
|
|
... recurse(
|
|
|
|
R.objOf(k,
|
|
|
|
{
|
|
|
|
... subProps,
|
|
|
|
... R.dissoc('si')(c)
|
|
|
|
})
|
|
|
|
),
|
|
|
|
condition: recurse(c.si)
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
let evaluate = (situationGate, parsedRules, node) => {
|
|
|
|
let evaluateOne = child => {
|
|
|
|
let condition = evaluateNode(situationGate, parsedRules, child.condition)
|
|
|
|
return {
|
|
|
|
...evaluateNode(situationGate, parsedRules, child),
|
|
|
|
condition
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let explanation = R.map(evaluateOne, node.explanation),
|
|
|
|
choice = R.find(node => node.condition.nodeValue, explanation),
|
|
|
|
nodeValue = choice ? choice.nodeValue : null
|
|
|
|
|
|
|
|
let collectMissing = node => {
|
2017-07-28 13:33:35 +00:00
|
|
|
let choice = R.find(node => node.condition.nodeValue, node.explanation),
|
|
|
|
leftMissing = choice ? [] : R.chain(collectNodeMissing,R.pluck("condition",node.explanation)),
|
|
|
|
rightMissing = choice ? collectNodeMissing(choice) : R.chain(collectNodeMissing,node.explanation)
|
|
|
|
return R.concat(leftMissing,rightMissing)
|
2017-07-28 12:24:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rewriteNode(node,nodeValue,explanation,collectMissing)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO - find an appropriate representation
|
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
|
|
|
classes="mecanism variations"
|
|
|
|
name="variations"
|
|
|
|
value={nodeValue}
|
|
|
|
child={
|
|
|
|
<ul>
|
|
|
|
{ explanation.map((c, i) =>
|
|
|
|
[<li className="variation" key={JSON.stringify(c.variation)}>
|
2017-09-30 13:04:11 +00:00
|
|
|
<div className="condition">
|
|
|
|
{makeJsx(c.condition)}
|
|
|
|
<div className="content">
|
|
|
|
{makeJsx(c)}
|
|
|
|
</div>
|
2017-07-28 12:24:29 +00:00
|
|
|
</div>
|
2017-09-30 13:04:11 +00:00
|
|
|
</li>
|
2017-07-28 12:24:29 +00:00
|
|
|
])
|
|
|
|
}
|
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
|
|
|
|
return {
|
|
|
|
explanation,
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'variations',
|
|
|
|
type: 'numeric'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-28 10:08:32 +00:00
|
|
|
export let mecanismOneOf = (recurse, k, v) => {
|
2017-07-09 17:42:45 +00:00
|
|
|
if (!R.is(Array,v)) throw 'should be array'
|
|
|
|
|
|
|
|
let explanation = R.map(recurse, v)
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-28 10:08:32 +00:00
|
|
|
classes="mecanism conditions list"
|
2017-07-09 17:42:45 +00:00
|
|
|
name='une de ces conditions'
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-06-28 10:08:32 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-13 19:55:59 +00:00
|
|
|
{explanation.map(item => <li key={item.name || item.text}>{makeJsx(item)}</li>)}
|
2017-06-28 10:08:32 +00:00
|
|
|
</ul>
|
2017-06-28 07:31:37 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
2017-08-18 15:21:43 +00:00
|
|
|
let evaluate = (situationGate, parsedRules, node) => {
|
|
|
|
let evaluateOne = child => evaluateNode(situationGate, parsedRules, child),
|
2017-09-19 16:56:29 +00:00
|
|
|
explanation = R.map(evaluateOne, node.explanation),
|
2017-08-18 15:21:43 +00:00
|
|
|
values = R.pluck("nodeValue",explanation),
|
|
|
|
nodeValue = R.any(R.equals(true),values) ? true :
|
|
|
|
(R.any(R.equals(null),values) ? null : false)
|
|
|
|
|
|
|
|
let collectMissing = node => node.nodeValue == null ? R.chain(collectNodeMissing,node.explanation) : []
|
|
|
|
return rewriteNode(node,nodeValue,explanation,collectMissing)
|
|
|
|
}
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
return {
|
2017-08-18 15:21:43 +00:00
|
|
|
evaluate,
|
2017-07-13 19:55:59 +00:00
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'une de ces conditions',
|
|
|
|
type: 'boolean'
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export let mecanismAllOf = (recurse, k,v) => {
|
2017-07-09 17:42:45 +00:00
|
|
|
if (!R.is(Array,v)) throw 'should be array'
|
|
|
|
|
|
|
|
let explanation = R.map(recurse, v)
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-07-09 17:42:45 +00:00
|
|
|
classes="mecanism conditions list"
|
|
|
|
name='toutes ces conditions'
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-07-09 17:42:45 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-13 19:55:59 +00:00
|
|
|
{explanation.map(item => <li key={item.name || item.text}>{makeJsx(item)}</li>)}
|
2017-07-09 17:42:45 +00:00
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate: evaluateArray(R.and,true),
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'toutes ces conditions',
|
|
|
|
type: 'boolean'
|
2017-07-09 17:42:45 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-08-02 13:28:33 +00:00
|
|
|
export let mecanismNumericalSwitch = (recurse, k,v) => {
|
2017-09-20 15:25:46 +00:00
|
|
|
// Si "l'aiguillage" est une constante ou une référence directe à une variable;
|
|
|
|
// l'utilité de ce cas correspond à un appel récursif au mécanisme
|
|
|
|
if (R.is(String,v)) return recurse(v)
|
2017-07-12 19:57:54 +00:00
|
|
|
|
|
|
|
if (!R.is(Object,v) || R.keys(v).length == 0) {
|
2017-08-02 13:28:33 +00:00
|
|
|
throw 'Le mécanisme "aiguillage numérique" et ses sous-logiques doivent contenir au moins une proposition'
|
2017-07-12 19:57:54 +00:00
|
|
|
}
|
|
|
|
|
2017-09-20 15:25:46 +00:00
|
|
|
// les termes sont les couples (condition, conséquence) de l'aiguillage numérique
|
2017-08-16 09:21:42 +00:00
|
|
|
let terms = R.toPairs(v)
|
|
|
|
|
|
|
|
// la conséquence peut être un 'string' ou un autre aiguillage numérique
|
2017-07-12 19:57:54 +00:00
|
|
|
let parseCondition = ([condition, consequence]) => {
|
|
|
|
let
|
|
|
|
conditionNode = recurse(condition), // can be a 'comparison', a 'variable', TODO a 'negation'
|
2017-08-02 13:28:33 +00:00
|
|
|
consequenceNode = mecanismNumericalSwitch(recurse, condition, consequence)
|
2017-07-12 19:57:54 +00:00
|
|
|
|
2017-08-02 12:41:00 +00:00
|
|
|
let evaluate = (situationGate, parsedRules, node) => {
|
|
|
|
let collectMissing = node => {
|
|
|
|
let missingOnTheLeft = collectNodeMissing(node.explanation.condition),
|
|
|
|
investigate = node.explanation.condition.nodeValue !== false,
|
|
|
|
missingOnTheRight = investigate ? collectNodeMissing(node.explanation.consequence) : []
|
|
|
|
return R.concat(missingOnTheLeft, missingOnTheRight)
|
2017-07-14 09:31:39 +00:00
|
|
|
}
|
2017-07-12 19:57:54 +00:00
|
|
|
|
2017-08-02 12:41:00 +00:00
|
|
|
let explanation = R.evolve({
|
|
|
|
condition: R.curry(evaluateNode)(situationGate, parsedRules),
|
|
|
|
consequence: R.curry(evaluateNode)(situationGate, parsedRules)
|
2017-08-16 09:21:42 +00:00
|
|
|
}, node.explanation)
|
2017-08-02 12:41:00 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
...node,
|
|
|
|
collectMissing,
|
|
|
|
explanation,
|
|
|
|
nodeValue: explanation.consequence.nodeValue,
|
|
|
|
condValue: explanation.condition.nodeValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, {condition, consequence}) =>
|
|
|
|
<div className="condition">
|
|
|
|
{makeJsx(condition)}
|
|
|
|
<div>
|
|
|
|
{makeJsx(consequence)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2017-07-12 19:57:54 +00:00
|
|
|
return {
|
|
|
|
evaluate,
|
2017-07-13 19:55:59 +00:00
|
|
|
jsx,
|
|
|
|
explanation: {condition: conditionNode, consequence: consequenceNode},
|
2017-07-12 19:57:54 +00:00
|
|
|
category: 'condition',
|
|
|
|
text: condition,
|
|
|
|
condition: conditionNode,
|
|
|
|
type: 'boolean',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let evaluateTerms = (situationGate, parsedRules, node) => {
|
2017-08-16 11:41:30 +00:00
|
|
|
let
|
|
|
|
evaluateOne = child => evaluateNode(situationGate, parsedRules, child),
|
|
|
|
explanation = R.map(evaluateOne, node.explanation),
|
2017-07-12 19:57:54 +00:00
|
|
|
choice = R.find(node => node.condValue, explanation),
|
2017-08-16 09:21:42 +00:00
|
|
|
nonFalsyTerms = R.filter(node => node.condValue !== false, explanation),
|
|
|
|
getFirst = (prop) => R.pipe(R.head, R.prop(prop))(nonFalsyTerms),
|
|
|
|
nodeValue =
|
|
|
|
// voilà le "numérique" dans le nom de ce mécanisme : il renvoie zéro si aucune condition n'est vérifiée
|
|
|
|
R.isEmpty(nonFalsyTerms) ? 0 :
|
|
|
|
// c'est un 'null', on renvoie null car des variables sont manquantes
|
|
|
|
getFirst('condValue') == null ? null :
|
|
|
|
// c'est un true, on renvoie la valeur de la conséquence
|
|
|
|
getFirst('nodeValue')
|
2017-07-12 19:57:54 +00:00
|
|
|
|
2017-07-28 09:42:57 +00:00
|
|
|
let collectMissing = node => {
|
|
|
|
let choice = R.find(node => node.condValue, node.explanation)
|
|
|
|
return choice ? collectNodeMissing(choice) : R.chain(collectNodeMissing,node.explanation)
|
|
|
|
}
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
return rewriteNode(node,nodeValue,explanation,collectMissing)
|
2017-07-12 19:57:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let explanation = R.map(parseCondition,terms)
|
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-08-02 13:28:33 +00:00
|
|
|
classes="mecanism numericalSwitch list"
|
|
|
|
name="aiguillage numérique"
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-07-12 19:57:54 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-13 19:55:59 +00:00
|
|
|
{explanation.map(item => <li key={item.name || item.text}>{makeJsx(item)}</li>)}
|
2017-07-12 19:57:54 +00:00
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate: evaluateTerms,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
2017-08-02 13:28:33 +00:00
|
|
|
name: "aiguillage numérique",
|
2017-07-13 19:55:59 +00:00
|
|
|
type: 'boolean || numeric' // lol !
|
2017-07-12 19:57:54 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export let mecanismSum = (recurse,k,v) => {
|
2017-07-09 22:14:22 +00:00
|
|
|
let explanation = v.map(recurse)
|
2017-06-28 10:08:32 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let evaluate = evaluateArray(R.add,0)
|
|
|
|
|
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-28 10:08:32 +00:00
|
|
|
classes="mecanism somme"
|
|
|
|
name="somme"
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-06-28 10:08:32 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-13 19:55:59 +00:00
|
|
|
{explanation.map(v => <li key={v.name || v.text}>{makeJsx(v)}</li>)}
|
2017-06-28 10:08:32 +00:00
|
|
|
</ul>
|
2017-06-28 07:31:37 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'somme',
|
|
|
|
type: 'numeric'
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export let mecanismProduct = (recurse,k,v) => {
|
2017-06-29 06:51:40 +00:00
|
|
|
if (v.composantes) { //mécanisme de composantes. Voir known-mecanisms.md/composantes
|
|
|
|
return decompose(recurse,k,v)
|
|
|
|
}
|
2017-07-30 11:51:22 +00:00
|
|
|
if (v.variations) {
|
|
|
|
return devariate(recurse,k,v)
|
|
|
|
}
|
2017-06-29 06:51:40 +00:00
|
|
|
|
2017-07-12 09:50:34 +00:00
|
|
|
let objectShape = {
|
|
|
|
assiette:false,
|
|
|
|
taux:constantNode(1),
|
|
|
|
facteur:constantNode(1),
|
|
|
|
plafond:constantNode(Infinity)
|
|
|
|
}
|
|
|
|
let effect = ({assiette,taux,facteur,plafond}) => {
|
|
|
|
let mult = (base, rate, facteur, plafond) => Math.min(base, plafond) * rate * facteur
|
|
|
|
return (val(taux) === 0 || val(taux) === false || val(assiette) === 0 || val(facteur) === 0) ?
|
2017-06-28 10:08:32 +00:00
|
|
|
0
|
|
|
|
: anyNull([taux, assiette, facteur, plafond]) ?
|
|
|
|
null
|
|
|
|
: mult(val(assiette), val(taux), val(facteur), val(plafond))
|
2017-07-11 15:39:50 +00:00
|
|
|
}
|
|
|
|
|
2017-09-20 15:25:46 +00:00
|
|
|
let explanation = parseObject(recurse,objectShape,v),
|
2017-07-12 09:50:34 +00:00
|
|
|
evaluate = evaluateObject(objectShape,effect)
|
2017-07-11 15:39:50 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-28 10:08:32 +00:00
|
|
|
classes="mecanism multiplication"
|
|
|
|
name="multiplication"
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-06-28 10:08:32 +00:00
|
|
|
child={
|
|
|
|
<ul className="properties">
|
|
|
|
<li key="assiette">
|
|
|
|
<span className="key">assiette: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.assiette)}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>
|
2017-09-29 21:22:13 +00:00
|
|
|
{(explanation.taux.nodeValue != 1 || explanation.taux.category == 'calcExpression') &&
|
2017-06-28 10:08:32 +00:00
|
|
|
<li key="taux">
|
|
|
|
<span className="key">taux: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.taux)}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>}
|
2017-09-29 21:22:13 +00:00
|
|
|
{(explanation.facteur.nodeValue != 1 || explanation.taux.category == 'calcExpression') &&
|
2017-06-28 10:08:32 +00:00
|
|
|
<li key="facteur">
|
|
|
|
<span className="key">facteur: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.facteur)}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>}
|
2017-07-12 09:50:34 +00:00
|
|
|
{explanation.plafond.nodeValue != Infinity &&
|
2017-06-28 10:08:32 +00:00
|
|
|
<li key="plafond">
|
|
|
|
<span className="key">plafond: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.plafond)}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>}
|
|
|
|
</ul>
|
2017-06-28 07:31:37 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'multiplication',
|
|
|
|
type: 'numeric'
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export let mecanismScale = (recurse,k,v) => {
|
|
|
|
// Sous entendu : barème en taux marginaux.
|
|
|
|
// A étendre (avec une propriété type ?) quand les règles en contiendront d'autres.
|
|
|
|
if (v.composantes) { //mécanisme de composantes. Voir known-mecanisms.md/composantes
|
2017-06-29 06:51:40 +00:00
|
|
|
return decompose(recurse,k,v)
|
2017-07-28 12:24:29 +00:00
|
|
|
}
|
2017-07-30 11:51:22 +00:00
|
|
|
if (v.variations) {
|
2017-07-28 12:24:29 +00:00
|
|
|
return devariate(recurse,k,v)
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
/* on réécrit en plus bas niveau les tranches :
|
|
|
|
`en-dessous de: 1`
|
|
|
|
devient
|
|
|
|
```
|
|
|
|
de: 0
|
|
|
|
à: 1
|
|
|
|
```
|
|
|
|
*/
|
|
|
|
let tranches = v['tranches'].map(t =>
|
2017-06-28 10:08:32 +00:00
|
|
|
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}
|
2017-07-13 19:55:59 +00:00
|
|
|
: t)
|
|
|
|
|
|
|
|
|
|
|
|
let objectShape = {
|
|
|
|
assiette:false,
|
2017-09-23 16:59:14 +00:00
|
|
|
'multiplicateur des tranches':constantNode(1)
|
2017-07-13 19:55:59 +00:00
|
|
|
}
|
|
|
|
|
2017-09-26 15:25:26 +00:00
|
|
|
let effect = ({assiette, 'multiplicateur des tranches': multiplicateur, tranches}) => {
|
2017-09-23 16:59:14 +00:00
|
|
|
// TODO traiter la récursion 'de', 'à', 'taux' pour qu'ils puissent contenir des calculs
|
2017-09-20 15:25:46 +00:00
|
|
|
// 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 :
|
2017-06-28 10:08:32 +00:00
|
|
|
/*
|
|
|
|
nulled = assiette.nodeValue == null || R.any(
|
|
|
|
R.pipe(
|
|
|
|
R.values, R.map(val), R.any(R.equals(null))
|
|
|
|
)
|
|
|
|
)(tranches),
|
|
|
|
*/
|
|
|
|
// nulled = anyNull([assiette, multiplicateur]),
|
2017-07-13 19:55:59 +00:00
|
|
|
let nulled = val(assiette) == null || val(multiplicateur) == null
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
return nulled ?
|
2017-06-28 10:08:32 +00:00
|
|
|
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)) )
|
2017-09-20 15:25:46 +00:00
|
|
|
* recurse(taux).nodeValue
|
2017-06-28 10:08:32 +00:00
|
|
|
, 0)
|
2017-07-13 19:55:59 +00:00
|
|
|
}
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let explanation = {
|
2017-09-26 15:25:26 +00:00
|
|
|
...parseObject(recurse,objectShape,v),
|
2017-07-13 19:55:59 +00:00
|
|
|
tranches
|
|
|
|
},
|
|
|
|
evaluate = evaluateObject(objectShape,effect)
|
|
|
|
|
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-28 10:08:32 +00:00
|
|
|
classes="mecanism barème"
|
|
|
|
name="barème"
|
|
|
|
value={nodeValue}
|
|
|
|
child={
|
|
|
|
<ul className="properties">
|
|
|
|
<li key="assiette">
|
|
|
|
<span className="key">assiette: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.assiette)}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>
|
|
|
|
<li key="multiplicateur">
|
|
|
|
<span className="key">multiplicateur des tranches: </span>
|
2017-09-30 19:03:01 +00:00
|
|
|
<span className="value">{makeJsx(explanation['multiplicateur des tranches'])}</span>
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>
|
|
|
|
<table className="tranches">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Tranches de l'assiette</th>
|
|
|
|
<th>Taux</th>
|
|
|
|
</tr>
|
2017-07-13 19:55:59 +00:00
|
|
|
{explanation.tranches.map(({'en-dessous de': maxOnly, 'au-dessus de': minOnly, de: min, 'à': max, taux}) =>
|
2017-09-30 19:26:45 +00:00
|
|
|
<tr key={min || minOnly || 0}
|
|
|
|
style={{fontWeight: (explanation.assiette.nodeValue * explanation['multiplicateur des tranches'].nodeValue > min ? ' bold' : '')}}
|
|
|
|
>
|
2017-06-28 10:08:32 +00:00
|
|
|
<td>
|
|
|
|
{ maxOnly ? 'En dessous de ' + maxOnly
|
|
|
|
: minOnly ? 'Au dessus de ' + minOnly
|
|
|
|
: `De ${min} à ${max}` }
|
|
|
|
</td>
|
|
|
|
<td> {taux} </td>
|
|
|
|
</tr>
|
|
|
|
)}
|
|
|
|
</thead>
|
|
|
|
</table>
|
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'barème',
|
|
|
|
barème: 'en taux marginaux',
|
|
|
|
type: 'numeric'
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-28 07:31:37 +00:00
|
|
|
|
2017-06-28 10:08:32 +00:00
|
|
|
export let mecanismMax = (recurse,k,v) => {
|
2017-07-11 15:39:50 +00:00
|
|
|
let explanation = v.map(recurse)
|
2017-06-28 10:08:32 +00:00
|
|
|
|
2017-07-13 19:55:59 +00:00
|
|
|
let evaluate = evaluateArray(R.max,Number.NEGATIVE_INFINITY)
|
|
|
|
|
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
2017-06-28 10:08:32 +00:00
|
|
|
classes="mecanism list maximum"
|
|
|
|
name="le maximum de"
|
2017-07-13 19:55:59 +00:00
|
|
|
value={nodeValue}
|
2017-06-28 10:08:32 +00:00
|
|
|
child={
|
|
|
|
<ul>
|
2017-07-11 15:39:50 +00:00
|
|
|
{explanation.map((item, i) =>
|
2017-06-28 10:08:32 +00:00
|
|
|
<li key={i}>
|
|
|
|
<div className="description">{v[i].description}</div>
|
2017-07-13 19:55:59 +00:00
|
|
|
{makeJsx(item)}
|
2017-06-28 10:08:32 +00:00
|
|
|
</li>
|
|
|
|
)}
|
|
|
|
</ul>
|
2017-06-28 07:31:37 +00:00
|
|
|
}
|
2017-06-28 10:08:32 +00:00
|
|
|
/>
|
2017-07-13 19:55:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
type: 'numeric',
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'le maximum de'
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-30 21:50:36 +00:00
|
|
|
export let mecanismMin = (recurse,k,v) => {
|
|
|
|
let explanation = v.map(recurse)
|
|
|
|
|
|
|
|
let evaluate = evaluateArray(R.min,Infinity)
|
|
|
|
|
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
|
|
|
classes="mecanism list minimum"
|
|
|
|
name="le minimum de"
|
|
|
|
value={nodeValue}
|
|
|
|
child={
|
|
|
|
<ul>
|
|
|
|
{explanation.map((item, i) =>
|
|
|
|
<li key={i}>
|
|
|
|
<div className="description">{v[i].description}</div>
|
|
|
|
{makeJsx(item)}
|
|
|
|
</li>
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
explanation,
|
|
|
|
type: 'numeric',
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'le minimum de'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 12:26:24 +00:00
|
|
|
export let mecanismComplement = (recurse,k,v) => {
|
|
|
|
if (v.composantes) { //mécanisme de composantes. Voir known-mecanisms.md/composantes
|
|
|
|
return decompose(recurse,k,v)
|
|
|
|
}
|
|
|
|
|
2017-07-12 09:50:34 +00:00
|
|
|
let objectShape = {cible:false,montant:false}
|
|
|
|
let effect = ({cible,montant}) => {
|
|
|
|
let nulled = val(cible) == null
|
|
|
|
return nulled ? null : R.subtract(val(montant), R.min(val(cible), val(montant)))
|
|
|
|
}
|
|
|
|
let explanation = parseObject(recurse,objectShape,v)
|
2017-06-29 12:26:24 +00:00
|
|
|
|
|
|
|
return {
|
2017-07-12 09:50:34 +00:00
|
|
|
evaluate: evaluateObject(objectShape,effect),
|
|
|
|
explanation,
|
2017-06-29 12:26:24 +00:00
|
|
|
type: 'numeric',
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'complément pour atteindre',
|
|
|
|
jsx: <Node
|
|
|
|
classes="mecanism list complement"
|
|
|
|
name="complément pour atteindre"
|
|
|
|
child={
|
|
|
|
<ul className="properties">
|
|
|
|
<li key="cible">
|
|
|
|
<span className="key">montant calculé: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.cible)}</span>
|
2017-06-29 12:26:24 +00:00
|
|
|
</li>
|
|
|
|
<li key="mini">
|
|
|
|
<span className="key">montant à atteindre: </span>
|
2017-07-13 19:55:59 +00:00
|
|
|
<span className="value">{makeJsx(explanation.montant)}</span>
|
2017-06-29 12:26:24 +00:00
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-18 22:17:02 +00:00
|
|
|
export let mecanismSelection = (recurse,k,v) => {
|
|
|
|
if (v.composantes) { //mécanisme de composantes. Voir known-mecanisms.md/composantes
|
|
|
|
return decompose(recurse,k,v)
|
|
|
|
}
|
|
|
|
|
|
|
|
let dataSourceName = v['données']
|
|
|
|
let dataSearchField = v['dans']
|
|
|
|
let dataTargetName = v['renvoie']
|
|
|
|
let explanation = recurse(v['cherche'])
|
|
|
|
|
2017-09-18 22:58:29 +00:00
|
|
|
let evaluate = (situationGate, parsedRules, node) => {
|
2017-09-18 22:17:02 +00:00
|
|
|
let collectMissing = node => collectNodeMissing(node.explanation),
|
|
|
|
explanation = evaluateNode(situationGate, parsedRules, node.explanation),
|
|
|
|
dataSource = findRuleByName(parsedRules, dataSourceName),
|
|
|
|
data = dataSource ? dataSource['data'] : null,
|
|
|
|
dataKey = explanation.nodeValue,
|
|
|
|
dataItems = (data && dataKey && dataSearchField) ? R.filter(item => item[dataSearchField] == dataKey, data) : null,
|
2017-09-19 15:23:45 +00:00
|
|
|
dataItemValues = (dataItems && !R.isEmpty(dataItems)) ? R.values(dataItems) : null,
|
2017-09-18 22:17:02 +00:00
|
|
|
// TODO - over-specific! transform the JSON instead
|
|
|
|
dataItemSubValues = dataItemValues && dataItemValues[0][dataTargetName] ? dataItemValues[0][dataTargetName]["taux"] : null,
|
|
|
|
sortedSubValues = dataItemSubValues ? R.sortBy(pair => pair[0], R.toPairs(dataItemSubValues)) : null,
|
2017-09-18 22:58:29 +00:00
|
|
|
// return 0 if we found a match for the lookup but not for the specific field,
|
|
|
|
// so that component sums don't sum to null
|
|
|
|
nodeValue = dataItems ? (sortedSubValues ? Number.parseFloat(R.last(sortedSubValues)[1])/100 : 0) : null
|
2017-09-18 22:17:02 +00:00
|
|
|
return rewriteNode(node,nodeValue,explanation,collectMissing)
|
|
|
|
}
|
|
|
|
|
2017-10-02 19:29:07 +00:00
|
|
|
let indexOf = explanation => explanation.nodeValue ? R.findIndex(x => x['nomLaposte'] == explanation.nodeValue, taux_versement_transport) : 0
|
2017-10-01 14:24:55 +00:00
|
|
|
let indexOffset = 8
|
|
|
|
|
2017-09-18 22:17:02 +00:00
|
|
|
let jsx = (nodeValue, explanation) =>
|
|
|
|
<Node
|
|
|
|
classes="mecanism"
|
|
|
|
name="sélection"
|
|
|
|
value={nodeValue}
|
|
|
|
child={
|
2017-09-19 16:56:29 +00:00
|
|
|
<Table
|
|
|
|
width={300}
|
|
|
|
height={300}
|
|
|
|
headerHeight={20}
|
|
|
|
rowHeight={30}
|
2017-10-02 19:29:07 +00:00
|
|
|
rowCount={taux_versement_transport.length}
|
2017-10-01 14:24:55 +00:00
|
|
|
scrollToIndex={indexOf(explanation)+indexOffset}
|
|
|
|
rowStyle={
|
|
|
|
({ index }) => index == indexOf(explanation) ? { fontWeight: "bold" } : {}
|
|
|
|
}
|
2017-09-19 16:56:29 +00:00
|
|
|
rowGetter={
|
|
|
|
({ index }) => {
|
|
|
|
// transformation de données un peu crade du fichier taux.json qui gagnerait à être un CSV
|
2017-10-02 19:29:07 +00:00
|
|
|
let line = taux_versement_transport[index],
|
2017-09-19 16:56:29 +00:00
|
|
|
getLastTaux = dataTargetName => {
|
|
|
|
let lastTaux = R.values(R.path([dataTargetName, 'taux'], line))
|
|
|
|
return (lastTaux && lastTaux.length && lastTaux[0]) || 0
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
nom: line['nomLaposte'],
|
|
|
|
taux: getLastTaux(dataTargetName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Column
|
|
|
|
label='Nom de commune'
|
|
|
|
dataKey='nom'
|
|
|
|
width={200}
|
|
|
|
/>
|
|
|
|
<Column
|
|
|
|
width={100}
|
|
|
|
label={'Taux ' + dataTargetName}
|
|
|
|
dataKey="taux"
|
|
|
|
/>
|
|
|
|
</Table>
|
2017-09-18 22:17:02 +00:00
|
|
|
}
|
|
|
|
/>
|
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
explanation,
|
|
|
|
jsx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-28 10:08:32 +00:00
|
|
|
export let mecanismError = (recurse,k,v) => {
|
2017-09-20 15:25:46 +00:00
|
|
|
throw "Le mécanisme '"+k+"' est inconnu !"+v
|
2017-06-28 10:08:32 +00:00
|
|
|
}
|