2018-06-19 08:25:53 +00:00
import React from 'react'
2018-06-19 09:06:30 +00:00
import { Node , Leaf } from './mecanismViews/common'
2018-06-19 08:25:53 +00:00
import { findRuleByDottedName , disambiguateRuleReference } from './rules'
import { evaluateNode , rewriteNode , makeJsx } from './evaluation'
2018-09-07 15:30:50 +00:00
import { getSituationValue } from './variables'
2018-06-19 09:06:30 +00:00
import { Trans } from 'react-i18next'
2018-10-01 15:41:55 +00:00
import { isNumeric } from '../utils'
2018-09-30 18:38:57 +00:00
2018-10-01 10:56:18 +00:00
export let treatVariable = ( rules , rule , filter ) => parseResult => {
2018-06-19 08:25:53 +00:00
let evaluate = ( cache , situation , parsedRules , node ) => {
let dottedName = node . dottedName ,
// On va vérifier dans le cache courant, dict, si la variable n'a pas été déjà évaluée
// En effet, l'évaluation dans le cas d'une variable qui a une formule, est coûteuse !
cacheName = dottedName + ( filter ? '.' + filter : '' ) ,
cached = cache [ cacheName ]
2018-06-19 09:06:30 +00:00
if ( cached ) return cached
2018-06-19 08:25:53 +00:00
let variable = findRuleByDottedName ( parsedRules , dottedName ) ,
variableHasFormula = variable . formule != null ,
variableHasCond =
variable [ 'applicable si' ] != null ||
variable [ 'non applicable si' ] != null ,
2018-09-07 15:30:50 +00:00
situationValue = getSituationValue ( situation , dottedName , variable ) ,
2018-06-19 08:25:53 +00:00
needsEvaluation =
situationValue == null && ( variableHasCond || variableHasFormula ) ,
explanation = needsEvaluation
? evaluateNode ( cache , situation , parsedRules , variable )
: variable
2018-06-19 09:06:30 +00:00
let cacheAndNode = ( nodeValue , missingVariables ) => {
cache [ cacheName ] = rewriteNode (
node ,
nodeValue ,
explanation ,
missingVariables
)
return cache [ cacheName ]
}
2018-06-19 08:25:53 +00:00
// SITUATION 1 : La variable est directement renseignée
2018-06-19 09:06:30 +00:00
if ( situationValue != null ) return cacheAndNode ( situationValue , { } )
2018-06-19 08:25:53 +00:00
// SITUATION 2 : La variable est calculée
2018-06-19 09:06:30 +00:00
if ( situationValue == null && variableHasFormula )
return cacheAndNode ( explanation . nodeValue , explanation . missingVariables )
2018-06-19 08:25:53 +00:00
// SITUATION 3 : La variable est une question sans condition dont la valeur n'a pas été renseignée
2018-06-19 09:06:30 +00:00
if ( situationValue == null && ! variableHasFormula && ! variableHasCond )
return cacheAndNode ( null , { [ dottedName ] : 1 } )
2018-06-19 08:25:53 +00:00
// SITUATION 4 : La variable est une question avec conditions
if ( situationValue == null && ! variableHasFormula && variableHasCond ) {
// SITUATION 4.1 : La condition est connue et vrai
2018-06-19 09:06:30 +00:00
if ( explanation . isApplicable )
2018-10-15 13:38:41 +00:00
return variable . question
? cacheAndNode ( null , { [ dottedName ] : 1 } )
: cacheAndNode ( true , { } )
2018-06-19 09:06:30 +00:00
2018-06-19 08:25:53 +00:00
// SITUATION 4.2 : La condition est connue et fausse
2018-10-15 13:38:41 +00:00
if ( explanation . isApplicable === false ) return cacheAndNode ( false , { } )
2018-06-19 08:25:53 +00:00
// SITUATION 4.3 : La condition n'est pas connue
2018-06-19 09:06:30 +00:00
if ( explanation . isApplicable == null )
2018-10-15 13:38:41 +00:00
return cacheAndNode ( null , {
... explanation . missingVariables ,
... ( variable . question ? { [ dottedName ] : 1 } : { } )
} )
2018-06-19 08:25:53 +00:00
}
}
let { fragments } = parseResult ,
variablePartialName = fragments . join ( ' . ' ) ,
dottedName = disambiguateRuleReference ( rules , rule , variablePartialName )
return {
evaluate ,
2018-06-19 09:06:30 +00:00
//eslint-disable-next-line react/display-name
2018-06-19 08:25:53 +00:00
jsx : nodeValue => (
< Leaf
2018-06-19 14:10:53 +00:00
classes = "variable filtered"
filter = { filter }
2018-06-19 08:25:53 +00:00
name = { fragments . join ( ' . ' ) }
dottedName = { dottedName }
value = { nodeValue }
/ >
) ,
name : variablePartialName ,
category : 'variable' ,
fragments ,
dottedName ,
type : 'boolean | numeric'
}
}
2018-10-01 15:41:55 +00:00
// This function is a wrapper that can apply :
// - temporal transformations to the value of the variable.
// See the période.yaml test suite for details
// - filters on the variable to select one part of the variable's 'composantes'
// TODO - the implementations of filters is really bad. It injects a hack in the situation to make the composante mecanism compute only one of its branch. It is then stored in the cache under a new key, dottedName.filter. This mecanism should just query the variable tree to get the active composante's value...
export let treatVariableTransforms = ( rules , rule ) => parseResult => {
let evaluateTransforms = originalEval => (
2018-06-19 08:25:53 +00:00
cache ,
situation ,
parsedRules ,
node
) => {
2018-10-01 15:41:55 +00:00
// Filter transformation
let filteringSituation = name =>
2018-10-01 08:51:12 +00:00
name == 'sys.filter' ? parseResult . filter : situation ( name )
2018-10-01 15:41:55 +00:00
let filteredNode = originalEval (
cache ,
parseResult . filter ? filteringSituation : situation ,
parsedRules ,
node
)
let nodeValue = filteredNode . nodeValue
// Temporal transformation
if ( nodeValue == null || ! isNumeric ( nodeValue ) ) return filteredNode
let ruleToTransform = findRuleByDottedName (
rules ,
filteredNode . explanation . dottedName
)
let environmentPeriod = situation ( 'période' ) || 'mois'
let callingPeriod = rule [ 'période' ] || environmentPeriod
let calledPeriod = ruleToTransform [ 'période' ] || environmentPeriod
let transformedNodeValue =
callingPeriod === 'mois' && calledPeriod === 'année'
? nodeValue / 12
: callingPeriod === 'année' && calledPeriod === 'mois'
? nodeValue * 12
: nodeValue
return rewriteNode (
filteredNode ,
transformedNodeValue ,
filteredNode . explanation ,
filteredNode . missingVariables
)
2018-06-19 08:25:53 +00:00
}
2018-10-01 08:51:12 +00:00
let node = treatVariable ( rules , rule , parseResult . filter ) (
2018-10-01 15:41:55 +00:00
parseResult . variable || parseResult
)
2018-06-19 08:25:53 +00:00
return {
... node ,
2018-10-01 15:41:55 +00:00
// Decorate node with the composante filter (either who is paying, either tax free)
... ( parseResult . filter
? {
cotisation : {
... node . cotisation ,
'dû par' : parseResult . filter ,
'impôt sur le revenu' : parseResult . filter
}
}
: { } ) ,
evaluate : evaluateTransforms ( node . evaluate )
2018-06-19 08:25:53 +00:00
}
}
export let treatNegatedVariable = variable => {
let evaluate = ( cache , situation , parsedRules , node ) => {
let explanation = evaluateNode (
cache ,
situation ,
parsedRules ,
node . explanation
) ,
nodeValue = explanation . nodeValue == null ? null : ! explanation . nodeValue ,
missingVariables = explanation . missingVariables
return rewriteNode ( node , nodeValue , explanation , missingVariables )
}
let jsx = ( nodeValue , explanation ) => (
< Node
classes = "inlineExpression negation"
value = { nodeValue }
child = {
< span className = "nodeContent" >
< Trans i18nKey = "inlineExpressionNegation" > Non < / T r a n s > { ' ' }
{ makeJsx ( explanation ) }
< / s p a n >
}
/ >
)
return {
evaluate ,
jsx ,
category : 'mecanism' ,
name : 'négation' ,
type : 'boolean' ,
explanation : variable
}
}