import React from 'react' import { Node, Leaf } from './mecanismViews/common' import { findRuleByDottedName, disambiguateRuleReference } from './rules' import { evaluateNode, rewriteNode, makeJsx } from './evaluation' import { getSituationValue } from './variables' import { Trans } from 'react-i18next' import { isNumeric } from '../utils' export let treatVariable = (rules, rule, filter) => parseResult => { 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] if (cached) return cached let variable = findRuleByDottedName(parsedRules, dottedName), variableHasFormula = variable.formule != null, variableHasCond = variable['applicable si'] != null || variable['non applicable si'] != null, situationValue = getSituationValue(situation, dottedName, variable), needsEvaluation = situationValue == null && (variableHasCond || variableHasFormula), explanation = needsEvaluation ? evaluateNode(cache, situation, parsedRules, variable) : variable let cacheAndNode = (nodeValue, missingVariables) => { cache[cacheName] = rewriteNode( node, nodeValue, explanation, missingVariables ) return cache[cacheName] } // SITUATION 1 : La variable est directement renseignée if (situationValue != null) return cacheAndNode(situationValue, {}) // SITUATION 2 : La variable est calculée if (situationValue == null && variableHasFormula) return cacheAndNode(explanation.nodeValue, explanation.missingVariables) // SITUATION 3 : La variable est une question sans condition dont la valeur n'a pas été renseignée if (situationValue == null && !variableHasFormula && !variableHasCond) return cacheAndNode(null, { [dottedName]: 1 }) // SITUATION 4 : La variable est une question avec conditions if (situationValue == null && !variableHasFormula && variableHasCond) { // SITUATION 4.1 : La condition est connue et vrai if (explanation.isApplicable) return variable.question ? cacheAndNode(null, { [dottedName]: 1 }) : cacheAndNode(true, {}) // SITUATION 4.2 : La condition est connue et fausse if (explanation.isApplicable === false) return cacheAndNode(false, {}) // SITUATION 4.3 : La condition n'est pas connue if (explanation.isApplicable == null) return cacheAndNode(null, { ...explanation.missingVariables, ...(variable.question ? { [dottedName]: 1 } : {}) }) } } let { fragments } = parseResult, variablePartialName = fragments.join(' . '), dottedName = disambiguateRuleReference(rules, rule, variablePartialName) return { evaluate, //eslint-disable-next-line react/display-name jsx: nodeValue => ( ), name: variablePartialName, category: 'variable', fragments, dottedName, type: 'boolean | numeric' } } // 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 => ( cache, situation, parsedRules, node ) => { // Filter transformation let filteringSituation = name => name == 'sys.filter' ? parseResult.filter : situation(name) 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 ) } let node = treatVariable(rules, rule, parseResult.filter)( parseResult.variable || parseResult ) return { ...node, // 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) } } 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) => ( Non{' '} {makeJsx(explanation)} } /> ) return { evaluate, jsx, category: 'mecanism', name: 'négation', type: 'boolean', explanation: variable } }