2019-06-19 09:54:47 +00:00
// Reference to a variable
2019-07-29 07:13:05 +00:00
import parseRule from 'Engine/parseRule'
2019-02-06 07:44:48 +00:00
import React from 'react'
2019-09-10 14:58:26 +00:00
import { evaluateNode } from './evaluation'
2019-07-29 07:13:05 +00:00
import { getSituationValue } from './getSituationValue'
2019-07-03 14:33:24 +00:00
import { Leaf } from './mecanismViews/common'
2019-02-06 07:44:48 +00:00
import {
disambiguateRuleReference ,
findParentDependency ,
findRuleByDottedName
} from './rules'
2019-06-13 16:17:22 +00:00
export let parseReference = ( rules , rule , parsedRules , filter ) => ( {
fragments
} ) => {
let partialReference = fragments . join ( ' . ' ) ,
dottedName = disambiguateRuleReference ( rules , rule , partialReference )
2019-07-19 17:51:36 +00:00
let inInversionFormula = rule . formule ? . [ 'inversion numérique' ]
2019-06-14 08:43:09 +00:00
let parsedRule =
2019-06-13 16:17:22 +00:00
parsedRules [ dottedName ] ||
2019-07-20 16:30:42 +00:00
// the 'inversion numérique' formula should not exist. The instructions to the evaluation should be enough to infer that an inversion is necessary (assuming it is possible, the client decides this)
2019-07-19 17:51:36 +00:00
( ! inInversionFormula &&
parseRule ( rules , findRuleByDottedName ( rules , dottedName ) , parsedRules ) )
2019-06-19 09:54:47 +00:00
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
2019-06-14 08:43:09 +00:00
let variable =
typeof parsedRule === 'object' ? parsedRule : parsedRules [ dottedName ] ,
variableHasFormula = variable . formule != null ,
2018-06-19 08:25:53 +00:00
variableHasCond =
variable [ 'applicable si' ] != null ||
2019-06-20 13:53:30 +00:00
variable [ 'non applicable si' ] != null ||
2019-09-08 10:17:30 +00:00
variable . isDisabledBy . length > 1 ||
2019-06-20 13:53:30 +00:00
findParentDependency ( parsedRules , variable ) ,
2018-09-07 15:30:50 +00:00
situationValue = getSituationValue ( situation , dottedName , variable ) ,
2018-06-19 08:25:53 +00:00
needsEvaluation =
2019-06-20 13:53:30 +00:00
situationValue == null && ( variableHasCond || variableHasFormula )
2018-11-26 17:09:14 +00:00
2018-11-15 15:21:53 +00:00
let explanation = needsEvaluation
? evaluateNode ( cache , situation , parsedRules , variable )
: variable
2018-06-19 08:25:53 +00:00
2019-07-03 16:49:31 +00:00
let cacheAndNode = ( nodeValue , missingVariables , customExplanation ) => {
2019-09-10 14:58:26 +00:00
cache [ cacheName ] = {
... node ,
2018-06-19 09:06:30 +00:00
nodeValue ,
2019-09-10 14:58:26 +00:00
explanation : customExplanation || explanation ,
2018-06-19 09:06:30 +00:00
missingVariables
2019-09-10 14:58:26 +00:00
}
2018-06-19 09:06:30 +00:00
return cache [ cacheName ]
}
2019-02-06 07:44:48 +00:00
const variableScore = variable . defaultValue ? 1 : 2
2018-06-19 08:25:53 +00:00
// SITUATION 1 : La variable est directement renseignée
2019-07-03 16:49:31 +00:00
if ( situationValue != null ) {
return cacheAndNode (
situationValue ,
{ } ,
{ ... explanation , nodeValue : situationValue }
)
}
2018-06-19 09:06:30 +00:00
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 )
2019-02-01 12:31:57 +00:00
return cacheAndNode ( null , { [ dottedName ] : variableScore } )
2018-06-19 09:06:30 +00:00
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
2019-02-01 12:31:57 +00:00
? cacheAndNode ( null , { [ dottedName ] : variableScore } )
2018-10-15 13:38:41 +00:00
: 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 , { } )
2019-06-20 13:53:30 +00:00
2018-06-19 08:25:53 +00:00
// SITUATION 4.3 : La condition n'est pas connue
2019-06-20 13:53:30 +00:00
return cacheAndNode ( null , explanation . missingVariables )
2018-06-19 08:25:53 +00:00
}
}
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 => (
2019-07-20 16:30:42 +00:00
< >
< Leaf
classes = "variable filtered"
filter = { filter }
name = { fragments . join ( ' . ' ) }
dottedName = { dottedName }
nodeValue = { nodeValue }
unit = { parsedRule . unit }
/ >
< / >
2018-06-19 08:25:53 +00:00
) ,
2019-06-13 16:17:22 +00:00
name : partialReference ,
2019-07-01 15:59:57 +00:00
category : 'reference' ,
2018-06-19 08:25:53 +00:00
fragments ,
2019-06-19 09:54:47 +00:00
dottedName ,
unit : parsedRule . unit
2018-06-19 08:25:53 +00:00
}
}
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'
2019-06-13 16:17:22 +00:00
export let parseReferenceTransforms = (
rules ,
rule ,
parsedRules
) => parseResult => {
2018-10-01 15:41:55 +00:00
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
2019-03-04 14:33:51 +00:00
let supportedPeriods = [ 'mois' , 'année' , 'flexible' ]
2018-11-07 16:55:42 +00:00
if ( nodeValue == null ) return filteredNode
2018-10-01 15:41:55 +00:00
let ruleToTransform = findRuleByDottedName (
2019-05-14 11:52:23 +00:00
parsedRules ,
2018-10-01 15:41:55 +00:00
filteredNode . explanation . dottedName
)
2018-11-13 15:14:57 +00:00
let inlinePeriodTransform = { mensuel : 'mois' , annuel : 'année' } [
parseResult . temporalTransform
]
2018-11-26 14:20:19 +00:00
// Exceptions
2018-11-13 15:14:57 +00:00
if ( ! rule . période && ! inlinePeriodTransform ) {
2019-03-04 14:33:51 +00:00
if ( supportedPeriods . includes ( ruleToTransform . période ) )
2018-11-07 16:55:42 +00:00
throw new Error (
2019-07-19 17:51:36 +00:00
` Attention, une variable sans période, ${ rule . dottedName } , qui appelle une variable à période, ${ ruleToTransform . dottedName } , c'est suspect !
2019-03-04 14:33:51 +00:00
Si la période de la variable appelée est neutralisée dans la formule de calcul , par exemple un montant mensuel divisé par 30 ( comprendre 30 jours ) , utilisez "période: aucune" pour taire cette erreur et rassurer tout le monde .
2018-11-07 16:55:42 +00:00
`
)
return filteredNode
}
if ( ! ruleToTransform . période ) return filteredNode
2018-10-01 15:41:55 +00:00
let environmentPeriod = situation ( 'période' ) || 'mois'
2018-11-07 16:55:42 +00:00
let callingPeriod =
2019-02-06 07:44:48 +00:00
inlinePeriodTransform ||
( rule . période === 'flexible' ? environmentPeriod : rule . période )
2018-11-07 16:55:42 +00:00
let calledPeriod =
2019-02-06 07:44:48 +00:00
ruleToTransform . période === 'flexible'
? environmentPeriod
: ruleToTransform . période
2019-02-12 14:59:01 +00:00
2018-10-01 15:41:55 +00:00
let transformedNodeValue =
2018-10-23 10:58:27 +00:00
callingPeriod === 'mois' && calledPeriod === 'année'
? nodeValue / 12
: callingPeriod === 'année' && calledPeriod === 'mois'
2018-11-13 15:14:57 +00:00
? nodeValue * 12
: nodeValue ,
2018-10-23 10:58:27 +00:00
periodTransform = nodeValue !== transformedNodeValue
2019-09-10 14:58:26 +00:00
let result = {
... filteredNode ,
2019-09-10 16:24:05 +00:00
periodTransform ,
2019-09-10 14:58:26 +00:00
... ( periodTransform ? { originPeriodValue : nodeValue } : { } ) ,
nodeValue : transformedNodeValue ,
explanation : filteredNode . explanation ,
missingVariables : filteredNode . missingVariables
}
2019-02-14 17:00:52 +00:00
2018-10-23 10:58:27 +00:00
return result
2018-06-19 08:25:53 +00:00
}
2019-06-13 16:17:22 +00:00
let node = parseReference ( rules , rule , parsedRules , parseResult . filter ) (
2019-05-15 08:52:20 +00:00
parseResult . variable
2018-10-01 15:41:55 +00:00
)
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
}
}