2019-03-25 18:14:27 +00:00
import { desugarScale } from 'Engine/mecanisms/barème'
import { decompose , devariateExplanation } from 'Engine/mecanisms/utils'
import {
add ,
any ,
aperture ,
curry ,
equals ,
evolve ,
filter ,
find ,
head ,
is ,
isEmpty ,
isNil ,
keys ,
last ,
map ,
max ,
mergeWith ,
min ,
path ,
pipe ,
pluck ,
prop ,
propEq ,
reduce ,
reduced ,
reject ,
sort ,
subtract ,
toPairs
} from 'ramda'
import React from 'react'
import { Trans } from 'react-i18next'
import 'react-virtualized/styles.css'
import {
bonus ,
collectNodeMissing ,
defaultNode ,
evaluateArray ,
evaluateNode ,
evaluateObject ,
makeJsx ,
mergeAllMissing ,
mergeMissing ,
parseObject ,
rewriteNode
} from './evaluation'
import Allègement from './mecanismViews/Allègement'
import Barème from './mecanismViews/Barème'
import BarèmeContinu from './mecanismViews/BarèmeContinu'
import { Node , SimpleRuleLink } from './mecanismViews/common'
import InversionNumérique from './mecanismViews/InversionNumérique'
import Product from './mecanismViews/Product'
import Somme from './mecanismViews/Somme'
import Variations from './mecanismViews/Variations'
2019-04-08 09:56:56 +00:00
import { disambiguateRuleReference , findRuleByDottedName } from './rules'
2019-03-25 18:14:27 +00:00
import { anyNull , val } from './traverse-common-functions'
import uniroot from './uniroot'
2018-08-07 16:20:08 +00:00
/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */
export let mecanismVariations = ( recurse , k , v , devariate ) => {
let explanation = devariate
? devariateExplanation ( recurse , k , v )
2018-11-15 15:21:53 +00:00
: v . map ( ( { si , alors , sinon } ) =>
sinon !== undefined
? { consequence : recurse ( sinon ) , condition : undefined }
: { consequence : recurse ( alors ) , condition : recurse ( si ) }
2018-08-07 16:57:03 +00:00
)
2018-08-07 16:20:08 +00:00
2017-12-12 19:10:22 +00:00
let evaluate = ( cache , situationGate , parsedRules , node ) => {
2019-01-15 15:10:51 +00:00
let evaluateVariationProp = prop =>
2018-11-15 15:21:53 +00:00
prop === undefined
? undefined
2019-01-15 15:10:51 +00:00
: evaluateNode ( cache , situationGate , parsedRules , prop ) ,
2018-09-07 10:51:30 +00:00
// mark the satisfied variation if any in the explanation
[ , resolvedExplanation ] = reduce (
2019-01-15 15:10:51 +00:00
( [ resolved , result ] , variation ) => {
if ( resolved ) return [ true , [ ... result , variation ] ]
// evaluate the condition
let evaluatedCondition = evaluateVariationProp ( variation . condition )
if ( evaluatedCondition == undefined ) {
// We've reached the eventual defaut case
let evaluatedVariation = {
consequence : evaluateVariationProp ( variation . consequence ) ,
satisfied : true
}
return [ true , [ ... result , evaluatedVariation ] ]
}
if ( evaluatedCondition . nodeValue === null )
// one case has missing variables => we can't go further
2019-01-21 15:13:18 +00:00
return [ true , [ ... result , { condition : evaluatedCondition } ] ]
2019-01-15 15:10:51 +00:00
if ( evaluatedCondition . nodeValue === true ) {
let evaluatedVariation = {
condition : evaluatedCondition ,
consequence : evaluateVariationProp ( variation . consequence ) ,
satisfied : true
}
return [ true , [ ... result , evaluatedVariation ] ]
}
return [ false , [ ... result , variation ] ]
} ,
2018-09-07 10:51:30 +00:00
[ false , [ ] ]
2019-01-15 15:10:51 +00:00
) ( node . explanation ) ,
2018-09-07 10:51:30 +00:00
satisfiedVariation = resolvedExplanation . find ( v => v . satisfied ) ,
2018-08-07 16:57:03 +00:00
nodeValue = satisfiedVariation
? satisfiedVariation . consequence . nodeValue
: null
2017-07-28 12:24:29 +00:00
2018-10-15 13:37:36 +00:00
let leftMissing = mergeAllMissing (
2019-01-15 15:10:51 +00:00
reject ( isNil , pluck ( 'condition' , resolvedExplanation ) )
2018-10-15 13:37:36 +00:00
) ,
2018-08-07 16:57:03 +00:00
candidateVariations = filter (
node => ! node . condition || node . condition . nodeValue !== false ,
2019-01-15 15:10:51 +00:00
resolvedExplanation
2018-08-07 16:57:03 +00:00
) ,
2019-01-21 15:13:18 +00:00
rightMissing = mergeAllMissing (
reject ( isNil , pluck ( 'consequence' , candidateVariations ) )
) ,
2018-10-15 13:37:36 +00:00
missingVariables = satisfiedVariation
? collectNodeMissing ( satisfiedVariation . consequence )
: mergeMissing ( bonus ( leftMissing ) , rightMissing )
2017-07-28 12:24:29 +00:00
2018-09-07 11:15:02 +00:00
return rewriteNode ( node , nodeValue , resolvedExplanation , missingVariables )
2017-07-28 12:24:29 +00:00
}
// TODO - find an appropriate representation
return {
explanation ,
evaluate ,
2018-09-06 10:00:53 +00:00
jsx : Variations ,
2017-07-28 12:24:29 +00:00
category : 'mecanism' ,
name : 'variations' ,
type : 'numeric'
}
}
2017-06-28 10:08:32 +00:00
export let mecanismOneOf = ( recurse , k , v ) => {
2018-02-15 17:12:04 +00:00
if ( ! is ( Array , v ) ) throw new Error ( 'should be array' )
2017-07-09 17:42:45 +00:00
2018-01-08 15:07:26 +00:00
let explanation = map ( recurse , v )
2017-07-09 17:42:45 +00:00
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , explanation ) => (
2017-07-13 19:55:59 +00:00
< Node
2017-06-28 10:08:32 +00:00
classes = "mecanism conditions list"
2017-10-24 16:25:31 +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-10-24 16:25:31 +00:00
{ explanation . map ( item => (
< li key = { item . name || item . text } > { makeJsx ( item ) } < / 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
/ >
2017-10-24 16:25:31 +00:00
)
2017-07-13 19:55:59 +00:00
2017-12-12 19:10:22 +00:00
let evaluate = ( cache , situationGate , parsedRules , node ) => {
2018-01-03 15:54:19 +00:00
let evaluateOne = child =>
evaluateNode ( cache , situationGate , parsedRules , child ) ,
2018-01-08 15:07:26 +00:00
explanation = map ( evaluateOne , node . explanation ) ,
values = pluck ( 'nodeValue' , explanation ) ,
nodeValue = any ( equals ( true ) , values )
2017-10-24 16:25:31 +00:00
? true
2018-05-09 16:41:57 +00:00
: any ( equals ( null ) , values )
2018-11-15 15:21:53 +00:00
? null
: false ,
2018-04-13 09:44:49 +00:00
// Unlike most other array merges of missing variables this is a "flat" merge
// because "one of these conditions" tend to be several tests of the same variable
// (e.g. contract type is one of x, y, z)
2018-05-09 16:41:57 +00:00
missingVariables =
nodeValue == null
? reduce ( mergeWith ( max ) , { } , map ( collectNodeMissing , explanation ) )
: { }
2017-10-24 16:25:31 +00:00
2018-04-10 12:13:37 +00:00
return rewriteNode ( node , nodeValue , explanation , missingVariables )
2017-08-18 15:21:43 +00:00
}
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
}
}
2017-10-24 16:25:31 +00:00
export let mecanismAllOf = ( recurse , k , v ) => {
2018-02-15 17:12:04 +00:00
if ( ! is ( Array , v ) ) throw new Error ( 'should be array' )
2017-07-09 17:42:45 +00:00
2018-01-08 15:07:26 +00:00
let explanation = map ( recurse , v )
2017-07-09 17:42:45 +00:00
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , explanation ) => (
2017-07-13 19:55:59 +00:00
< Node
2017-07-09 17:42:45 +00:00
classes = "mecanism conditions list"
2017-10-24 16:25:31 +00:00
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-10-24 16:25:31 +00:00
{ explanation . map ( item => (
< li key = { item . name || item . text } > { makeJsx ( item ) } < / l i >
) ) }
2017-07-09 17:42:45 +00:00
< / u l >
}
/ >
2017-10-24 16:25:31 +00:00
)
2017-07-13 19:55:59 +00:00
2017-12-12 19:10:22 +00:00
let evaluate = ( cache , situationGate , parsedRules , node ) => {
2018-01-03 15:54:19 +00:00
let evaluateOne = child =>
evaluateNode ( cache , situationGate , parsedRules , child ) ,
2018-01-08 15:07:26 +00:00
explanation = map ( evaluateOne , node . explanation ) ,
values = pluck ( 'nodeValue' , explanation ) ,
nodeValue = any ( equals ( false ) , values )
2017-10-24 16:30:44 +00:00
? false // court-circuit
2018-05-09 16:41:57 +00:00
: any ( equals ( null ) , values )
2018-11-15 15:21:53 +00:00
? null
: true ,
2018-04-12 13:43:02 +00:00
missingVariables = nodeValue == null ? mergeAllMissing ( explanation ) : { }
2017-10-24 16:30:44 +00:00
2018-04-10 12:13:37 +00:00
return rewriteNode ( node , nodeValue , explanation , missingVariables )
2017-10-24 16:30:44 +00:00
}
2017-07-13 19:55:59 +00:00
return {
2017-10-24 16:30:44 +00:00
evaluate : evaluate ,
2017-07-13 19:55:59 +00:00
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-10-24 16:25:31 +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
2018-01-08 15:07:26 +00:00
if ( is ( String , v ) ) return recurse ( v )
2017-07-12 19:57:54 +00:00
2018-01-08 15:07:26 +00:00
if ( ! is ( Object , v ) || keys ( v ) . length == 0 ) {
2018-02-15 17:12:04 +00:00
throw new Error (
'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
2018-01-08 15:07:26 +00:00
let terms = toPairs ( v )
2017-08-16 09:21:42 +00:00
// 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 ] ) => {
2017-10-24 16:25:31 +00:00
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-12-12 19:10:22 +00:00
let evaluate = ( cache , situationGate , parsedRules , node ) => {
2018-01-08 15:07:26 +00:00
let explanation = evolve (
2018-05-09 16:41:57 +00:00
{
condition : curry ( evaluateNode ) ( cache , situationGate , parsedRules ) ,
consequence : curry ( evaluateNode ) ( cache , situationGate , parsedRules )
} ,
node . explanation
) ,
2018-04-13 09:44:49 +00:00
leftMissing = explanation . condition . missingVariables ,
2018-04-10 11:44:15 +00:00
investigate = explanation . condition . nodeValue !== false ,
2018-04-13 09:44:49 +00:00
rightMissing = investigate
2018-04-10 12:13:37 +00:00
? explanation . consequence . missingVariables
2018-04-12 13:43:02 +00:00
: { } ,
2018-04-13 16:41:45 +00:00
missingVariables = mergeMissing ( bonus ( leftMissing ) , rightMissing )
2017-08-02 12:41:00 +00:00
return {
... node ,
explanation ,
2018-04-10 11:44:15 +00:00
missingVariables ,
2017-08-02 12:41:00 +00:00
nodeValue : explanation . consequence . nodeValue ,
condValue : explanation . condition . nodeValue
}
}
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , { condition , consequence } ) => (
2017-07-13 19:55:59 +00:00
< div className = "condition" >
2017-10-24 16:25:31 +00:00
{ makeJsx ( condition ) }
< div > { makeJsx ( consequence ) } < / d i v >
< / d i v >
)
2017-07-13 19:55:59 +00:00
2017-07-12 19:57:54 +00:00
return {
2017-10-24 16:25:31 +00:00
evaluate ,
jsx ,
explanation : { condition : conditionNode , consequence : consequenceNode } ,
category : 'condition' ,
text : condition ,
condition : conditionNode ,
type : 'boolean'
}
2017-07-12 19:57:54 +00:00
}
2017-12-12 19:10:22 +00:00
let evaluateTerms = ( cache , situationGate , parsedRules , node ) => {
2018-01-03 15:54:19 +00:00
let evaluateOne = child =>
evaluateNode ( cache , situationGate , parsedRules , child ) ,
2018-01-08 15:07:26 +00:00
explanation = map ( evaluateOne , node . explanation ) ,
nonFalsyTerms = filter ( node => node . condValue !== false , explanation ) ,
2018-06-19 14:45:42 +00:00
getFirst = o =>
pipe (
head ,
prop ( o )
) ( nonFalsyTerms ) ,
2017-08-16 09:21:42 +00:00
nodeValue =
// voilà le "numérique" dans le nom de ce mécanisme : il renvoie zéro si aucune condition n'est vérifiée
2018-01-08 15:07:26 +00:00
isEmpty ( nonFalsyTerms )
2017-10-24 16:25:31 +00:00
? 0
: // c'est un 'null', on renvoie null car des variables sont manquantes
2018-11-15 15:21:53 +00:00
getFirst ( 'condValue' ) == null
? null
: // c'est un true, on renvoie la valeur de la conséquence
getFirst ( 'nodeValue' ) ,
2018-04-10 11:44:15 +00:00
choice = find ( node => node . condValue , explanation ) ,
missingVariables = choice
2018-04-10 12:13:37 +00:00
? choice . missingVariables
2018-04-12 13:43:02 +00:00
: mergeAllMissing ( explanation )
2017-07-28 09:42:57 +00:00
2018-04-10 12:13:37 +00:00
return rewriteNode ( node , nodeValue , explanation , missingVariables )
2017-07-12 19:57:54 +00:00
}
2018-01-08 15:07:26 +00:00
let explanation = map ( parseCondition , terms )
2017-07-12 19:57:54 +00:00
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , explanation ) => (
2017-07-13 19:55:59 +00:00
< 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-10-24 16:25:31 +00:00
{ explanation . map ( item => (
< li key = { item . name || item . text } > { makeJsx ( item ) } < / l i >
) ) }
2017-07-12 19:57:54 +00:00
< / u l >
}
/ >
2017-10-24 16:25:31 +00:00
)
2017-07-13 19:55:59 +00:00
return {
evaluate : evaluateTerms ,
jsx ,
explanation ,
category : 'mecanism' ,
2017-10-24 16:25:31 +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
}
2019-01-08 21:38:04 +00:00
export let findInversion = ( situationGate , parsedRules , v , dottedName ) => {
2017-11-20 12:16:18 +00:00
let inversions = v . avec
if ( ! inversions )
2018-02-15 17:12:04 +00:00
throw new Error (
"Une formule d'inversion doit préciser _avec_ quoi on peut inverser la variable"
)
2017-11-20 12:16:18 +00:00
/ *
Quelle variable d ' inversion possible a sa valeur renseignée dans la situation courante ?
Ex . s 'il nous est demandé de calculer le salaire de base, est-ce qu' un candidat à l ' inversion , comme
le salaire net , a été renseigné ?
* /
2019-01-10 17:47:39 +00:00
let candidates = inversions
. map ( i =>
disambiguateRuleReference (
parsedRules ,
parsedRules . find ( propEq ( 'dottedName' , dottedName ) ) ,
i
)
2018-01-03 15:54:19 +00:00
)
2019-01-10 17:47:39 +00:00
. map ( name => {
let userInput = situationGate ( name ) != undefined
let rule = findRuleByDottedName ( parsedRules , name )
/ * W h e n t h e f i x e d O b j e c t i v e V a l u e i s n u l l , t h e i n v e r s i o n c a n ' t b e d o n e : t h e u s e r n e e d s t o s e t t h e t a r g e t ' s v a l u e
* But the objectiveRule can also have an 'alternative' property ,
* which must point to a rule whose value either is set by the user ,
* or is calculated according to a formula that does not depend on the rule being inversed .
* This alternative ' s value will be used as a target .
* * /
let alternativeRule =
! userInput &&
rule . alternative &&
findRuleByDottedName ( parsedRules , rule . alternative )
if ( ! userInput && ! alternativeRule ) return null
return {
fixedObjectiveRule : rule ,
userInput ,
fixedObjectiveValue : situationGate ( name ) ,
alternativeRule
}
} ) ,
candidateWithUserInput = candidates . find ( c => c && c . userInput )
2017-11-20 12:16:18 +00:00
2019-01-10 17:47:39 +00:00
return (
candidateWithUserInput || candidates . find ( candidate => candidate != null )
)
2017-11-20 12:16:18 +00:00
}
2018-04-12 10:25:01 +00:00
let doInversion = ( oldCache , situationGate , parsedRules , v , dottedName ) => {
2017-11-20 12:16:18 +00:00
let inversion = findInversion ( situationGate , parsedRules , v , dottedName )
2019-01-08 21:38:04 +00:00
if ( ! inversion )
2018-01-03 15:54:19 +00:00
return {
2018-05-09 16:41:57 +00:00
missingVariables : { [ dottedName ] : 1 } ,
2018-01-03 15:54:19 +00:00
nodeValue : null
}
2019-01-08 21:38:04 +00:00
let { fixedObjectiveValue , fixedObjectiveRule , alternativeRule } = inversion
let evaluatedAlternative =
alternativeRule &&
evaluateNode ( oldCache , situationGate , parsedRules , alternativeRule )
if ( evaluatedAlternative && evaluatedAlternative . nodeValue == null )
return {
missingVariables : evaluatedAlternative . missingVariables ,
nodeValue : null
}
let objectiveValue = evaluatedAlternative
? evaluatedAlternative . nodeValue
: fixedObjectiveValue
2017-12-12 19:10:22 +00:00
let inversionCache = { }
let fx = x => {
2018-05-09 16:41:57 +00:00
inversionCache = { parseLevel : oldCache . parseLevel + 1 , op : '<' }
2019-04-09 15:07:17 +00:00
let v = evaluateNode (
2018-01-03 15:54:19 +00:00
inversionCache , // with an empty cache
n => ( dottedName === n ? x : situationGate ( n ) ) ,
2017-11-20 12:16:18 +00:00
parsedRules ,
fixedObjectiveRule
2018-04-10 15:16:27 +00:00
)
2019-04-09 15:07:17 +00:00
return v
2017-12-12 19:10:22 +00:00
}
2017-11-20 12:16:18 +00:00
// si fx renvoie null pour une valeur numérique standard, disons 1000, on peut
// considérer que l'inversion est impossible du fait de variables manquantes
// TODO fx peut être null pour certains x, et valide pour d'autres : on peut implémenter ici le court-circuit
2018-04-10 15:16:27 +00:00
let attempt = fx ( 1000 )
if ( attempt . nodeValue == null ) {
return attempt
}
2017-11-23 15:19:24 +00:00
2018-11-15 15:21:53 +00:00
let tolerance = 0.1 ,
2017-11-20 12:16:18 +00:00
// cette fonction détermine la racine d'une fonction sans faire trop d'itérations
nodeValue = uniroot (
2018-12-19 16:26:11 +00:00
x => {
let y = fx ( x )
2019-01-08 21:38:04 +00:00
return y . nodeValue - objectiveValue
2018-12-19 16:26:11 +00:00
} ,
1 ,
2017-11-20 12:16:18 +00:00
1000000000 ,
2018-11-15 15:21:53 +00:00
tolerance ,
10
2017-11-20 12:16:18 +00:00
)
return {
nodeValue ,
2018-04-12 13:43:02 +00:00
missingVariables : { } ,
2019-01-30 17:53:57 +00:00
inversionCache ,
inversedWith : {
rule : fixedObjectiveRule ,
value : fixedObjectiveValue
}
2017-11-20 12:16:18 +00:00
}
}
export let mecanismInversion = dottedName => ( recurse , k , v ) => {
2017-12-12 19:10:22 +00:00
let evaluate = ( cache , situationGate , parsedRules , node ) => {
2017-11-20 12:16:18 +00:00
let inversion =
2018-05-09 16:41:57 +00:00
// avoid the inversion loop !
2018-04-10 11:44:15 +00:00
situationGate ( dottedName ) == undefined &&
2018-04-12 10:25:01 +00:00
doInversion ( cache , situationGate , parsedRules , v , dottedName ) ,
2018-04-27 09:52:41 +00:00
// TODO - ceci n'est pas vraiment satisfaisant
2018-05-09 16:41:57 +00:00
nodeValue = situationGate ( dottedName )
? Number . parseFloat ( situationGate ( dottedName ) )
: inversion . nodeValue ,
2018-04-12 13:43:02 +00:00
missingVariables = inversion . missingVariables
2019-04-09 15:07:17 +00:00
if ( nodeValue === undefined )
cache . inversionFail = {
given : inversion . inversedWith . rule . dottedName ,
estimated : dottedName
}
2019-01-30 17:53:57 +00:00
let evaluatedNode = rewriteNode (
node ,
nodeValue ,
{
... evolve ( { avec : map ( recurse ) } , v ) ,
inversedWith : inversion ? . inversedWith
} ,
missingVariables
)
2018-04-12 16:06:54 +00:00
// TODO - we need this so that ResultsGrid will work, but it's
// just not right
toPairs ( inversion . inversionCache ) . map ( ( [ k , v ] ) => ( cache [ k ] = v ) )
2017-12-12 19:10:22 +00:00
return evaluatedNode
2017-11-20 12:16:18 +00:00
}
return {
2018-02-06 16:02:13 +00:00
... v ,
2017-11-20 12:16:18 +00:00
evaluate ,
2019-01-30 17:53:57 +00:00
explanation : evolve ( { avec : map ( recurse ) } , v ) ,
jsx : InversionNumérique ,
2017-11-20 12:16:18 +00:00
category : 'mecanism' ,
2019-01-30 17:53:57 +00:00
name : 'inversion numérique' ,
2017-11-20 12:16:18 +00:00
type : 'numeric'
}
}
2017-10-24 16:25:31 +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
2018-01-08 15:07:26 +00:00
let evaluate = evaluateArray ( add , 0 )
2017-07-13 19:55:59 +00:00
return {
evaluate ,
2018-05-22 17:12:13 +00:00
// eslint-disable-next-line
2017-10-24 16:25:31 +00:00
jsx : ( nodeValue , explanation ) => (
< Somme nodeValue = { nodeValue } explanation = { explanation } / >
) ,
2017-07-13 19:55:59 +00:00
explanation ,
category : 'mecanism' ,
name : 'somme' ,
type : 'numeric'
2017-06-28 10:08:32 +00:00
}
}
2018-02-14 16:50:47 +00:00
export let mecanismReduction = ( recurse , k , v ) => {
let objectShape = {
assiette : false ,
2019-02-14 17:00:31 +00:00
abattement : defaultNode ( 0 ) ,
franchise : defaultNode ( 0 )
2018-02-14 16:50:47 +00:00
}
2018-02-15 14:40:10 +00:00
let effect = ( { assiette , abattement , franchise , décote } ) => {
2018-02-22 18:47:27 +00:00
let v _assiette = val ( assiette )
2018-02-14 16:50:47 +00:00
2018-02-22 18:47:27 +00:00
if ( v _assiette == null ) return null
let montantFranchiséDécoté =
val ( franchise ) && v _assiette < val ( franchise )
2018-02-14 16:50:47 +00:00
? 0
: décote
2018-11-15 15:21:53 +00:00
? do {
let plafond = val ( décote . plafond ) ,
taux = val ( décote . taux )
2018-02-14 16:50:47 +00:00
2018-11-15 15:21:53 +00:00
v _assiette > plafond
? v _assiette
: max ( 0 , ( 1 + taux ) * v _assiette - taux * plafond )
}
: v _assiette
2018-02-22 17:00:44 +00:00
return abattement
? val ( abattement ) == null
2018-05-09 16:41:57 +00:00
? montantFranchiséDécoté === 0
? 0
: null
2018-07-27 13:28:42 +00:00
: abattement . category === 'percentage'
2018-11-15 15:21:53 +00:00
? max (
0 ,
montantFranchiséDécoté - val ( abattement ) * montantFranchiséDécoté
)
: max ( 0 , montantFranchiséDécoté - val ( abattement ) )
2018-02-22 17:00:44 +00:00
: montantFranchiséDécoté
2018-02-14 16:50:47 +00:00
}
let base = parseObject ( recurse , objectShape , v ) ,
2018-02-15 14:40:10 +00:00
explanation = v . décote
? {
... base ,
décote : map ( recurse , v . décote )
2018-05-09 16:41:57 +00:00
}
2018-02-15 14:40:10 +00:00
: base ,
2018-02-14 16:50:47 +00:00
evaluate = evaluateObject ( objectShape , effect )
return {
evaluate ,
2018-02-21 12:46:38 +00:00
jsx : Allègement ,
2018-02-14 16:50:47 +00:00
explanation ,
category : 'mecanism' ,
name : 'allègement' ,
type : 'numeric'
}
}
2017-10-24 16:25:31 +00:00
export let mecanismProduct = ( recurse , k , v ) => {
if ( v . composantes ) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose ( recurse , k , v )
2017-06-29 06:51:40 +00:00
}
2017-07-30 11:51:22 +00:00
if ( v . variations ) {
2018-08-07 16:20:08 +00:00
return mecanismVariations ( recurse , k , v , true )
2017-07-30 11:51:22 +00:00
}
2017-06-29 06:51:40 +00:00
2017-07-12 09:50:34 +00:00
let objectShape = {
2017-10-24 16:25:31 +00:00
assiette : false ,
2019-02-14 17:00:31 +00:00
taux : defaultNode ( 1 ) ,
facteur : defaultNode ( 1 ) ,
plafond : defaultNode ( Infinity )
2017-07-12 09:50:34 +00:00
}
2017-10-24 16:25:31 +00:00
let effect = ( { assiette , taux , facteur , plafond } ) => {
let mult = ( base , rate , facteur , plafond ) =>
Math . min ( base , plafond ) * rate * facteur
2019-03-25 18:14:27 +00:00
return {
nodeValue :
val ( taux ) === 0 ||
val ( taux ) === false ||
val ( assiette ) === 0 ||
val ( facteur ) === 0
? 0
: anyNull ( [ taux , assiette , facteur , plafond ] )
? null
: mult ( val ( assiette ) , val ( taux ) , val ( facteur ) , val ( plafond ) ) ,
additionalExplanation : { plafondActif : val ( assiette ) > val ( plafond ) }
}
2017-07-11 15:39:50 +00:00
}
2017-10-24 16:25:31 +00:00
let explanation = parseObject ( recurse , objectShape , v ) ,
evaluate = evaluateObject ( objectShape , effect )
2017-07-11 15:39:50 +00:00
2017-07-13 19:55:59 +00:00
return {
evaluate ,
2019-03-20 15:59:55 +00:00
jsx : Product ,
2017-07-13 19:55:59 +00:00
explanation ,
category : 'mecanism' ,
name : 'multiplication' ,
type : 'numeric'
2017-06-28 10:08:32 +00:00
}
}
2018-07-09 10:01:06 +00:00
/ * o n r é é c r i t e n u n e s y n t a x e p l u s b a s n i v e a u m a i s p l u s r é g u l i è r e l e s t r a n c h e s :
` en-dessous de: 1 `
devient
` ` `
de : 0
à : 1
` ` `
* /
2017-07-13 19:55:59 +00:00
2018-07-09 10:01:06 +00:00
export let mecanismLinearScale = ( recurse , k , v ) => {
2018-09-06 12:50:23 +00:00
if ( v . composantes ) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose ( recurse , k , v )
}
if ( v . variations ) {
return mecanismVariations ( recurse , k , v , true )
}
2018-07-09 10:01:06 +00:00
let tranches = desugarScale ( recurse ) ( v [ 'tranches' ] ) ,
objectShape = {
assiette : false
}
let effect = ( { assiette , tranches } ) => {
if ( val ( assiette ) === null ) return null
2018-10-23 17:03:56 +00:00
let roundedAssiette = Math . round ( val ( assiette ) )
2018-07-09 10:01:06 +00:00
let matchedTranche = tranches . find (
2018-10-23 17:03:56 +00:00
( { de : min , à : max } ) => roundedAssiette >= min && roundedAssiette <= max
2018-07-09 10:01:06 +00:00
)
2018-11-04 18:28:36 +00:00
if ( ! matchedTranche ) return 0
2018-12-05 13:40:52 +00:00
if ( matchedTranche . taux )
return matchedTranche . taux . nodeValue * val ( assiette )
return matchedTranche . montant
2017-07-13 19:55:59 +00:00
}
2018-07-09 10:01:06 +00:00
let explanation = {
... parseObject ( recurse , objectShape , v ) ,
tranches
} ,
evaluate = evaluateObject ( objectShape , effect )
return {
evaluate ,
2019-01-06 14:16:43 +00:00
jsx : Barème ( 'linéaire' ) ,
2018-07-09 10:01:06 +00:00
explanation ,
category : 'mecanism' ,
name : 'barème linéaire' ,
barème : 'en taux' ,
type : 'numeric'
}
}
2018-12-05 17:19:11 +00:00
export let mecanismContinuousScale = ( recurse , k , v ) => {
let objectShape = {
assiette : false ,
2019-02-14 17:00:31 +00:00
multiplicateur : defaultNode ( 1 )
2018-12-05 17:19:11 +00:00
}
2019-03-01 11:32:09 +00:00
let returnRate = v [ 'retourne seulement le taux' ] === 'oui'
2018-12-05 17:19:11 +00:00
let effect = ( { assiette , multiplicateur , points } ) => {
if ( anyNull ( [ assiette , multiplicateur ] ) ) return null
//We'll build a linear function given the two constraints that must be respected
2019-01-14 14:14:57 +00:00
let result = pipe (
2018-12-05 17:19:11 +00:00
toPairs ,
// we don't rely on the sorting of objects
sort ( ( [ k1 ] , [ k2 ] ) => k1 - k2 ) ,
points => [ ... points , [ Infinity , last ( points ) [ 1 ] ] ] ,
aperture ( 2 ) ,
reduce ( ( _ , [ [ lowerLimit , lowerRate ] , [ upperLimit , upperRate ] ] ) => {
let x1 = val ( multiplicateur ) * lowerLimit ,
x2 = val ( multiplicateur ) * upperLimit ,
y1 = val ( assiette ) * val ( recurse ( lowerRate ) ) ,
y2 = val ( assiette ) * val ( recurse ( upperRate ) )
if ( val ( assiette ) > x1 && val ( assiette ) <= x2 ) {
// Outside of these 2 limits, it's a linear function a * x + b
let a = ( y2 - y1 ) / ( x2 - x1 ) ,
2019-01-14 14:14:57 +00:00
b = y1 - x1 * a ,
2019-02-06 07:44:48 +00:00
nodeValue = a * val ( assiette ) + b ,
taux = nodeValue / val ( assiette )
2019-01-14 14:14:57 +00:00
return reduced ( {
2019-03-01 11:32:09 +00:00
nodeValue : returnRate ? taux : nodeValue ,
2019-01-14 14:14:57 +00:00
additionalExplanation : {
seuil : val ( assiette ) / val ( multiplicateur ) ,
2019-02-06 07:44:48 +00:00
taux
2019-01-14 14:14:57 +00:00
}
} )
2018-12-05 17:19:11 +00:00
}
} , 0 )
) ( points )
2019-01-14 14:14:57 +00:00
return result
2018-12-05 17:19:11 +00:00
}
let explanation = {
... parseObject ( recurse , objectShape , v ) ,
2019-03-01 11:32:09 +00:00
points : v . points ,
returnRate
2018-12-05 17:19:11 +00:00
} ,
evaluate = evaluateObject ( objectShape , effect )
return {
evaluate ,
2019-01-14 11:26:48 +00:00
jsx : BarèmeContinu ,
2018-12-05 17:19:11 +00:00
explanation ,
category : 'mecanism' ,
2018-12-07 15:28:39 +00:00
name : 'barème continu' ,
2018-12-05 17:19:11 +00:00
type : 'numeric'
}
}
2017-10-24 16:25:31 +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
2018-01-08 15:07:26 +00:00
let evaluate = evaluateArray ( max , Number . NEGATIVE _INFINITY )
2017-07-13 19:55:59 +00:00
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , explanation ) => (
2017-07-13 19:55:59 +00:00
< 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-10-24 16:25:31 +00:00
{ explanation . map ( ( item , i ) => (
< li key = { i } >
< div className = "description" > { v [ i ] . description } < / d i v >
{ makeJsx ( item ) }
< / 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
/ >
2017-10-24 16:25:31 +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-10-24 16:25:31 +00:00
export let mecanismMin = ( recurse , k , v ) => {
2017-07-30 21:50:36 +00:00
let explanation = v . map ( recurse )
2018-01-08 15:07:26 +00:00
let evaluate = evaluateArray ( min , Infinity )
2017-07-30 21:50:36 +00:00
2017-10-24 16:25:31 +00:00
let jsx = ( nodeValue , explanation ) => (
2017-07-30 21:50:36 +00:00
< Node
classes = "mecanism list minimum"
name = "le minimum de"
value = { nodeValue }
child = {
< ul >
2017-10-24 16:25:31 +00:00
{ explanation . map ( ( item , i ) => (
< li key = { i } >
< div className = "description" > { v [ i ] . description } < / d i v >
{ makeJsx ( item ) }
< / l i >
) ) }
2017-07-30 21:50:36 +00:00
< / u l >
}
/ >
2017-10-24 16:25:31 +00:00
)
2017-07-30 21:50:36 +00:00
return {
evaluate ,
jsx ,
explanation ,
type : 'numeric' ,
category : 'mecanism' ,
name : 'le minimum de'
}
}
2017-10-24 16:25:31 +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-06-29 12:26:24 +00:00
}
2017-10-24 16:25:31 +00:00
let objectShape = { cible : false , montant : false }
let effect = ( { cible , montant } ) => {
2017-07-12 09:50:34 +00:00
let nulled = val ( cible ) == null
2018-01-08 15:07:26 +00:00
return nulled ? null : subtract ( val ( montant ) , min ( val ( cible ) , val ( montant ) ) )
2017-07-12 09:50:34 +00:00
}
2017-10-24 16:25:31 +00:00
let explanation = parseObject ( recurse , objectShape , v )
2017-06-29 12:26:24 +00:00
return {
2017-10-24 16:25:31 +00:00
evaluate : evaluateObject ( objectShape , effect ) ,
2017-07-12 09:50:34 +00:00
explanation ,
2017-06-29 12:26:24 +00:00
type : 'numeric' ,
category : 'mecanism' ,
name : 'complément pour atteindre' ,
2018-05-22 17:12:13 +00:00
// eslint-disable-next-line
2017-10-24 16:25:31 +00:00
jsx : ( nodeValue , explanation ) => (
< Node
classes = "mecanism list complement"
name = "complément"
value = { nodeValue }
child = {
< ul className = "properties" >
< li key = "cible" >
2018-05-09 16:41:57 +00:00
< span className = "key" >
< Trans > cible < / T r a n s > : { ' ' }
< / s p a n >
2017-10-24 16:25:31 +00:00
< span className = "value" > { makeJsx ( explanation . cible ) } < / s p a n >
< / l i >
< li key = "mini" >
2018-05-09 16:41:57 +00:00
< span className = "key" >
< Trans > montant à atteindre < / T r a n s > : { ' ' }
< / s p a n >
2017-10-24 16:25:31 +00:00
< span className = "value" > { makeJsx ( explanation . montant ) } < / s p a n >
< / l i >
< / u l >
}
/ >
)
2017-06-29 12:26:24 +00:00
}
}
2018-09-06 14:45:50 +00:00
export let mecanismSynchronisation = ( recurse , k , v ) => {
let evaluate = ( cache , situationGate , parsedRules , node ) => {
let APIExplanation = evaluateNode (
cache ,
situationGate ,
parsedRules ,
node . explanation . API
)
2018-09-06 16:33:12 +00:00
2019-04-08 09:56:56 +00:00
let valuePath = v . chemin . split ( ' . ' )
2018-09-06 14:45:50 +00:00
let nodeValue =
2019-04-08 09:56:56 +00:00
val ( APIExplanation ) == null ? null : path ( valuePath , val ( APIExplanation ) )
// If the API gave a non null value, then some of its props may be null (the API can be composed of multiple API, some failing). Then this prop will be set to the default value defined in the API's rule
let safeNodeValue =
nodeValue == null && val ( APIExplanation ) != null
? path ( valuePath , APIExplanation . explanation . defaultValue )
: nodeValue
2018-09-06 16:33:12 +00:00
let missingVariables =
val ( APIExplanation ) === null ? { [ APIExplanation . dottedName ] : 1 } : { }
2018-09-06 14:45:50 +00:00
let explanation = { ... v , API : APIExplanation }
2019-04-08 09:56:56 +00:00
return rewriteNode ( node , safeNodeValue , explanation , missingVariables )
2018-09-06 14:45:50 +00:00
}
return {
explanation : { ... v , API : recurse ( v . API ) } ,
evaluate ,
2018-09-07 14:56:36 +00:00
jsx : function Synchronisation ( nodeValue , explanation ) {
return (
< p >
Obtenu à partir de la saisie < SimpleRuleLink rule = { explanation . API } / >
< / p >
)
} ,
2018-09-06 14:45:50 +00:00
category : 'mecanism' ,
name : 'synchronisation'
}
}
2017-10-24 16:25:31 +00:00
export let mecanismError = ( recurse , k , v ) => {
2018-02-15 17:12:04 +00:00
throw new Error ( "Le mécanisme '" + k + "' est inconnu !" + v )
2017-06-28 10:08:32 +00:00
}