2019-06-19 09:54:47 +00:00
// Reference to a variable
2019-02-06 07:44:48 +00:00
import React from 'react'
import { Trans } from 'react-i18next'
import { evaluateNode , makeJsx , rewriteNode } from './evaluation'
import { Leaf , Node } from './mecanismViews/common'
import {
disambiguateRuleReference ,
findParentDependency ,
findRuleByDottedName
} from './rules'
2019-07-09 09:12:34 +00:00
import { getSituationValue } from './getSituationValue'
2019-06-13 16:17:22 +00:00
import parseRule from 'Engine/parseRule'
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-19 17:51:36 +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)
( ! 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 ||
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 ) => {
2018-06-19 09:06:30 +00:00
cache [ cacheName ] = rewriteNode (
2019-06-12 09:46:36 +00:00
node ,
2018-06-19 09:06:30 +00:00
nodeValue ,
2019-07-03 16:49:31 +00:00
customExplanation || explanation ,
2018-06-19 09:06:30 +00:00
missingVariables
)
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 => (
< 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 }
2019-06-20 16:51:40 +00:00
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
let result = rewriteNode (
{
... filteredNode ,
periodTransform : periodTransform ,
... ( periodTransform ? { originPeriodValue : nodeValue } : { } )
} ,
2018-10-01 15:41:55 +00:00
transformedNodeValue ,
filteredNode . explanation ,
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
}
}