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-11 14:00:10 +00:00
import { evaluateNode , collectNodeMissing } from './traverse'
2017-06-28 07:31:37 +00:00
let transformPercentage = s =>
R . contains ( '%' ) ( s ) ?
+ s . replace ( '%' , '' ) / 100
: + s
2017-07-11 14:39:05 +00:00
let evaluateArray = ( reducer , start ) => ( situationGate , parsedRules , node ) => {
let evaluateOne = child => evaluateNode ( situationGate , parsedRules , child ) ,
explanation = R . map ( evaluateOne , node . explanation ) ,
values = R . pluck ( "nodeValue" , explanation ) ,
nodeValue = R . any ( R . equals ( null ) , values ) ? null : R . reduce ( reducer , start , values )
let collectMissing = node => R . chain ( collectNodeMissing , node . explanation )
return {
... node ,
nodeValue ,
collectMissing ,
explanation ,
jsx : {
... node . jsx ,
value : nodeValue
}
}
}
2017-06-29 06:51:40 +00:00
export let decompose = ( recurse , k , v ) => {
let
subProps = R . dissoc ( 'composantes' ) ( v ) ,
2017-06-29 10:03:01 +00:00
filter = val ( recurse ( "sys . filter" ) ) ,
isRelevant = c => ! filter || ! c . attributs || c . attributs [ 'dû par' ] == filter ,
2017-07-11 14:39:05 +00:00
explanation = v . composantes . filter ( isRelevant ) . 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
return {
2017-07-11 14:39:05 +00:00
evaluate : evaluateArray ( R . add , 0 ) ,
2017-06-29 06:51:40 +00:00
category : 'mecanism' ,
name : 'composantes' ,
type : 'numeric' ,
2017-07-11 14:39:05 +00:00
explanation ,
2017-06-29 06:51:40 +00:00
jsx : < Node
classes = "mecanism composantes"
name = "composantes"
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 } : < / s p a n >
< span > { v } < / s p a n >
< / l i >
) }
< / u l >
< div className = "content" >
{ c . jsx }
< / d i v >
< / l i > ,
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 > < / l i >
2017-06-29 06:51:40 +00:00
]
)
}
< / u l >
}
/ >
}
}
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 )
return {
2017-07-11 14:39:05 +00:00
evaluate : evaluateArray ( R . or , false ) ,
2017-07-09 17:42:45 +00:00
explanation ,
category : 'mecanism' ,
name : 'une de ces conditions' ,
type : 'boolean' ,
2017-06-28 10:08:32 +00:00
jsx : < Node
classes = "mecanism conditions list"
2017-07-09 17:42:45 +00:00
name = 'une de ces conditions'
2017-06-28 10:08:32 +00:00
child = {
< ul >
2017-07-09 17:42:45 +00:00
{ explanation . map ( item => < li key = { item . name || item . text } > { item . jsx } < / l i > ) }
2017-06-28 10:08:32 +00:00
< / u l >
2017-06-28 07:31:37 +00:00
}
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 )
return {
2017-07-11 14:39:05 +00:00
evaluate : evaluateArray ( R . and , true ) ,
2017-07-09 17:42:45 +00:00
explanation ,
category : 'mecanism' ,
name : 'toutes ces conditions' ,
type : 'boolean' ,
jsx : < Node
classes = "mecanism conditions list"
name = 'toutes ces conditions'
child = {
< ul >
{ explanation . map ( item => < li key = { item . name || item . text } > { item . jsx } < / l i > ) }
< / u l >
}
/ >
}
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 mecanismNumericalLogic = ( recurse , k , v ) => {
return R . ifElse (
R . is ( String ) ,
rate => ( { //TODO unifier ce code
nodeValue : transformPercentage ( rate ) ,
type : 'numeric' ,
category : 'percentage' ,
percentage : rate ,
explanation : null ,
jsx :
< span className = "percentage" >
< span className = "name" > { rate } < / s p a n >
< / s p a n >
} ) ,
R . pipe (
R . unless (
v => R . is ( Object ) ( v ) && R . keys ( v ) . length >= 1 ,
( ) => { throw 'Le mécanisme "logique numérique" et ses sous-logiques doivent contenir au moins une proposition' }
) ,
R . toPairs ,
R . reduce ( ( memo , [ condition , consequence ] ) => {
let
{ nodeValue , explanation } = memo ,
conditionNode = recurse ( condition ) , // can be a 'comparison', a 'variable', TODO a 'negation'
childNumericalLogic = mecanismNumericalLogic ( recurse , condition , consequence ) ,
nextNodeValue = conditionNode . nodeValue == null ?
// Si la proposition n'est pas encore résolvable
null
// Si la proposition est résolvable
: conditionNode . nodeValue == true ?
// Si elle est vraie
childNumericalLogic . nodeValue
// Si elle est fausse
: false
return { ... memo ,
nodeValue : nodeValue == null ?
null
: nodeValue !== false ?
nodeValue // l'une des propositions renvoie déjà une valeur numérique donc différente de false
: nextNodeValue ,
explanation : [ ... explanation , {
nodeValue : nextNodeValue ,
category : 'condition' ,
text : condition ,
condition : conditionNode ,
conditionValue : conditionNode . nodeValue ,
type : 'boolean' ,
explanation : childNumericalLogic ,
jsx : < div className = "condition" >
{ conditionNode . jsx }
< div >
{ childNumericalLogic . jsx }
< / d i v >
< / d i v >
} ] ,
}
} , {
nodeValue : false ,
2017-06-28 07:31:37 +00:00
category : 'mecanism' ,
2017-06-28 10:08:32 +00:00
name : "logique numérique" ,
type : 'boolean || numeric' , // lol !
explanation : [ ]
} ) ,
node => ( { ... node ,
2017-06-28 07:31:37 +00:00
jsx : < Node
2017-06-28 10:08:32 +00:00
classes = "mecanism numericalLogic list"
name = "logique numérique"
value = { node . nodeValue }
2017-06-28 07:31:37 +00:00
child = {
< ul >
2017-06-28 10:08:32 +00:00
{ node . explanation . map ( item => < li key = { item . name || item . text } > { item . jsx } < / l i > ) }
2017-06-28 07:31:37 +00:00
< / u l >
}
/ >
2017-06-28 10:08:32 +00:00
} )
) ) ( v )
}
export let mecanismPercentage = ( recurse , k , v ) => {
let reg = /^(\d+(\.\d+)?)\%$/
if ( R . test ( reg ) ( v ) )
return {
category : 'percentage' ,
type : 'numeric' ,
percentage : v ,
nodeValue : R . match ( reg ) ( v ) [ 1 ] / 100 ,
explanation : null ,
jsx :
< span className = "percentage" >
< span className = "name" > { v } < / s p a n >
< / s p a n >
}
else {
let node = recurse ( v )
return {
type : 'numeric' ,
category : 'percentage' ,
percentage : node . nodeValue ,
nodeValue : node . nodeValue ,
explanation : node ,
jsx : node . jsx
}
}
}
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
return {
2017-07-11 14:39:05 +00:00
evaluate : evaluateArray ( R . add , 0 ) ,
2017-07-09 22:14:22 +00:00
explanation ,
2017-06-28 10:08:32 +00:00
category : 'mecanism' ,
name : 'somme' ,
type : 'numeric' ,
jsx : < Node
classes = "mecanism somme"
name = "somme"
child = {
< ul >
2017-07-09 22:14:22 +00:00
{ explanation . map ( v => < li key = { v . name || v . text } > { v . jsx } < / l i > ) }
2017-06-28 10:08:32 +00:00
< / u l >
2017-06-28 07:31:37 +00:00
}
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-11 15:39:50 +00:00
let evaluate = ( situationGate , parsedRules , node ) => {
let mult = ( base , rate , facteur , plafond ) => Math . min ( base , plafond ) * rate * facteur ,
evaluateOne = child => evaluateNode ( situationGate , parsedRules , child ) ,
collectMissing = node => R . chain ( collectNodeMissing , R . values ( node . explanation ) )
let { taux , assiette , facteur , plafond } = node . explanation ,
explanation = R . evolve ( {
taux : evaluateOne ,
assiette : evaluateOne ,
facteur : evaluateOne ,
plafond : evaluateOne ,
} ) ( node . explanation )
let nodeValue = ( 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
return {
... node ,
nodeValue ,
collectMissing ,
explanation ,
jsx : {
... node . jsx ,
value : nodeValue
}
}
}
let constantNode = constant => ( { nodeValue : constant } )
let assiette = recurse ( v [ 'assiette' ] ) ,
taux = v [ 'taux' ] ? recurse ( { taux : v [ 'taux' ] } ) : constantNode ( 1 ) ,
facteur = v [ 'facteur' ] ? recurse ( v [ 'facteur' ] ) : constantNode ( 1 ) ,
plafond = v [ 'plafond' ] ? recurse ( v [ 'plafond' ] ) : constantNode ( Infinity )
2017-06-28 10:08:32 +00:00
return {
2017-07-11 15:39:50 +00:00
evaluate ,
2017-06-28 10:08:32 +00:00
category : 'mecanism' ,
name : 'multiplication' ,
type : 'numeric' ,
explanation : {
assiette ,
taux ,
facteur ,
plafond
//TODO introduire 'prorata' ou 'multiplicateur', pour sémantiser les opérandes ?
2017-06-28 07:31:37 +00:00
} ,
2017-06-28 10:08:32 +00:00
jsx : < Node
classes = "mecanism multiplication"
name = "multiplication"
child = {
< ul className = "properties" >
< li key = "assiette" >
< span className = "key" > assiette : < / s p a n >
< span className = "value" > { assiette . jsx } < / s p a n >
< / l i >
{ taux . nodeValue != 1 &&
< li key = "taux" >
< span className = "key" > taux : < / s p a n >
< span className = "value" > { taux . jsx } < / s p a n >
< / l i > }
{ facteur . nodeValue != 1 &&
< li key = "facteur" >
< span className = "key" > facteur : < / s p a n >
< span className = "value" > { facteur . jsx } < / s p a n >
< / l i > }
{ plafond . nodeValue != Infinity &&
< li key = "plafond" >
< span className = "key" > plafond : < / s p a n >
< span className = "value" > { plafond . jsx } < / s p a n >
< / l i > }
< / u l >
2017-06-28 07:31:37 +00:00
}
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-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
}
2017-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
if ( v [ 'multiplicateur des tranches' ] == null )
throw "un barème nécessite pour l'instant une propriété 'multiplicateur des tranches'"
2017-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
let
assiette = recurse ( v [ 'assiette' ] ) ,
multiplicateur = recurse ( v [ 'multiplicateur des tranches' ] ) ,
2017-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
/ * o n r é é c r i t e n p l u s b a s n i v e a u l e s t r a n c h e s :
` 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 ,
2017-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
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 )
2017-06-28 07:31:37 +00:00
2017-06-28 10:08:32 +00:00
return {
nodeValue ,
category : 'mecanism' ,
name : 'barème' ,
barème : 'en taux marginaux' ,
type : 'numeric' ,
explanation : {
assiette ,
multiplicateur ,
tranches
2017-06-28 07:31:37 +00:00
} ,
2017-06-28 10:08:32 +00:00
jsx : < Node
classes = "mecanism barème"
name = "barème"
value = { nodeValue }
child = {
< ul className = "properties" >
< li key = "assiette" >
< span className = "key" > assiette : < / s p a n >
< span className = "value" > { assiette . jsx } < / s p a n >
< / l i >
< li key = "multiplicateur" >
< span className = "key" > multiplicateur des tranches : < / s p a n >
< span className = "value" > { multiplicateur . jsx } < / s p a n >
< / l i >
< table className = "tranches" >
< thead >
< tr >
< th > Tranches de l ' assiette < / t h >
< th > Taux < / t h >
< / t r >
{ v [ 'tranches' ] . map ( ( { 'en-dessous de' : maxOnly , 'au-dessus de' : minOnly , de : min , 'à' : max , taux } ) =>
< tr key = { min || minOnly || 0 } >
< td >
{ maxOnly ? 'En dessous de ' + maxOnly
: minOnly ? 'Au dessus de ' + minOnly
: ` De ${ min } à ${ max } ` }
< / t d >
< td > { taux } < / t d >
< / t r >
) }
< / t h e a d >
< / t a b l e >
< / u l >
}
/ >
}
}
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
return {
2017-07-11 15:39:50 +00:00
evaluate : evaluateArray ( R . max , Number . NEGATIVE _INFINITY ) ,
2017-06-28 10:08:32 +00:00
type : 'numeric' ,
category : 'mecanism' ,
name : 'le maximum de' ,
2017-07-11 15:39:50 +00:00
explanation ,
2017-06-28 10:08:32 +00:00
jsx : < Node
classes = "mecanism list maximum"
name = "le maximum de"
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 } < / d i v >
{ item . jsx }
< / l i >
) }
< / u l >
2017-06-28 07:31:37 +00:00
}
2017-06-28 10:08:32 +00:00
/ >
}
}
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 )
}
if ( v [ 'cible' ] == null )
throw "un complément nécessite une propriété 'cible'"
let cible = recurse ( v [ 'cible' ] ) ,
mini = recurse ( v [ 'montant' ] ) ,
nulled = val ( cible ) == null ,
nodeValue = nulled ? null : R . subtract ( val ( mini ) , R . min ( val ( cible ) , val ( mini ) ) )
return {
type : 'numeric' ,
category : 'mecanism' ,
name : 'complément pour atteindre' ,
nodeValue ,
explanation : {
cible ,
mini
} ,
jsx : < Node
classes = "mecanism list complement"
name = "complément pour atteindre"
value = { nodeValue }
child = {
< ul className = "properties" >
< li key = "cible" >
< span className = "key" > montant calculé : < / s p a n >
< span className = "value" > { cible . jsx } < / s p a n >
< / l i >
< li key = "mini" >
< span className = "key" > montant à atteindre : < / s p a n >
< span className = "value" > { mini . jsx } < / s p a n >
< / l i >
< / u l >
}
/ >
}
}
2017-06-28 10:08:32 +00:00
export let mecanismError = ( recurse , k , v ) => {
throw "Le mécanisme est inconnu !"
}