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'
|
|
|
|
import { getSituationValue } from './variables'
|
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-06-14 08:43:09 +00:00
|
|
|
let parsedRule =
|
2019-06-13 16:17:22 +00:00
|
|
|
parsedRules[dottedName] ||
|
|
|
|
(console.log('uncached : from `', rule.dottedName, '` to `', dottedName) ||
|
|
|
|
parseRule(rules, findRuleByDottedName(rules, dottedName), parsedRules))
|
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
|
|
|
|
2018-06-19 09:06:30 +00:00
|
|
|
let cacheAndNode = (nodeValue, missingVariables) => {
|
|
|
|
cache[cacheName] = rewriteNode(
|
2019-06-12 09:46:36 +00:00
|
|
|
node,
|
2018-06-19 09:06:30 +00:00
|
|
|
nodeValue,
|
|
|
|
explanation,
|
|
|
|
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
|
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)
|
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-13 16:17:22 +00:00
|
|
|
name: partialReference,
|
2018-06-19 08:25:53 +00:00
|
|
|
category: 'variable',
|
|
|
|
fragments,
|
2019-06-12 09:46:36 +00:00
|
|
|
dottedName
|
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(
|
|
|
|
`Attention, une variable sans période, ${
|
|
|
|
rule.dottedName
|
2019-03-01 17:25:45 +00:00
|
|
|
}, qui appelle une variable à période, ${
|
2018-11-07 16:55:42 +00:00
|
|
|
ruleToTransform.dottedName
|
2019-01-31 15:18:14 +00:00
|
|
|
}, 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 16:01:49 +00:00
|
|
|
export let parseNegatedReference = variable => {
|
2018-06-19 08:25:53 +00:00
|
|
|
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</Trans>{' '}
|
|
|
|
{makeJsx(explanation)}
|
|
|
|
</span>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
|
|
|
return {
|
|
|
|
evaluate,
|
|
|
|
jsx,
|
|
|
|
category: 'mecanism',
|
|
|
|
name: 'négation',
|
|
|
|
type: 'boolean',
|
|
|
|
explanation: variable
|
|
|
|
}
|
|
|
|
}
|