126 lines
3.1 KiB
TypeScript
126 lines
3.1 KiB
TypeScript
|
import { bonus, evaluateNode, mergeMissing } from 'Engine/evaluation'
|
||
|
import { map, mergeAll, pick, pipe } from 'ramda'
|
||
|
import { Rule } from 'Types/rule'
|
||
|
import { typeWarning } from './error'
|
||
|
import { convertNodeToUnit } from './nodeUnits'
|
||
|
import { anyNull, undefOrTruthy, val } from './traverse-common-functions'
|
||
|
import { areUnitConvertible } from './units'
|
||
|
|
||
|
export const evaluateApplicability = (
|
||
|
cache,
|
||
|
situationGate,
|
||
|
parsedRules,
|
||
|
node: Rule
|
||
|
) => {
|
||
|
let evaluatedAttributes = pipe(
|
||
|
pick(['non applicable si', 'applicable si', 'rendu non applicable']) as (
|
||
|
x: any
|
||
|
) => any,
|
||
|
map(value => evaluateNode(cache, situationGate, parsedRules, value))
|
||
|
)(node) as any,
|
||
|
{
|
||
|
'non applicable si': notApplicable,
|
||
|
'applicable si': applicable,
|
||
|
'rendu non applicable': disabled
|
||
|
} = evaluatedAttributes,
|
||
|
parentDependencies = node.parentDependencies.map(parent =>
|
||
|
evaluateNode(cache, situationGate, parsedRules, parent)
|
||
|
),
|
||
|
isApplicable =
|
||
|
parentDependencies.some(parent => val(parent) === false) ||
|
||
|
val(notApplicable) === true ||
|
||
|
val(applicable) === false ||
|
||
|
val(disabled) === true
|
||
|
? false
|
||
|
: anyNull([notApplicable, applicable, ...parentDependencies])
|
||
|
? null
|
||
|
: !val(notApplicable) && undefOrTruthy(val(applicable)),
|
||
|
missingVariables =
|
||
|
isApplicable === false
|
||
|
? {}
|
||
|
: mergeAll([
|
||
|
...parentDependencies.map(parent => parent.missingVariables),
|
||
|
notApplicable?.missingVariables || {},
|
||
|
disabled?.missingVariables || {},
|
||
|
applicable?.missingVariables || {}
|
||
|
])
|
||
|
|
||
|
return {
|
||
|
nodeValue: isApplicable,
|
||
|
missingVariables,
|
||
|
...evaluatedAttributes
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default (cache, situationGate, parsedRules, node) => {
|
||
|
cache._meta.contextRule.push(node.dottedName)
|
||
|
const applicabilityEvaluation = evaluateApplicability(
|
||
|
cache,
|
||
|
situationGate,
|
||
|
parsedRules,
|
||
|
node
|
||
|
)
|
||
|
const {
|
||
|
missingVariables: condMissing,
|
||
|
nodeValue: isApplicable
|
||
|
} = applicabilityEvaluation
|
||
|
|
||
|
const evaluateFormula = () =>
|
||
|
node.formule
|
||
|
? evaluateNode(cache, situationGate, parsedRules, node.formule)
|
||
|
: {}
|
||
|
// evaluate the formula lazily, only if the applicability is known and true
|
||
|
const evaluatedFormula = isApplicable
|
||
|
? evaluateFormula()
|
||
|
: isApplicable === false
|
||
|
? {
|
||
|
...node.formule,
|
||
|
missingVariables: {},
|
||
|
nodeValue: 0
|
||
|
}
|
||
|
: {
|
||
|
...node.formule,
|
||
|
missingVariables: {},
|
||
|
nodeValue: null
|
||
|
}
|
||
|
let {
|
||
|
missingVariables: formulaMissingVariables,
|
||
|
nodeValue
|
||
|
} = evaluatedFormula
|
||
|
const missingVariables = mergeMissing(
|
||
|
bonus(condMissing, !!Object.keys(condMissing).length),
|
||
|
formulaMissingVariables
|
||
|
)
|
||
|
const unit =
|
||
|
node.unit ||
|
||
|
(node.defaultUnit &&
|
||
|
cache._meta.defaultUnits.find(unit =>
|
||
|
areUnitConvertible(node.defaultUnit, unit)
|
||
|
)) ||
|
||
|
node.defaultUnit ||
|
||
|
evaluatedFormula.unit
|
||
|
|
||
|
if (unit) {
|
||
|
try {
|
||
|
nodeValue = convertNodeToUnit(unit, evaluatedFormula).nodeValue
|
||
|
} catch (e) {
|
||
|
typeWarning(
|
||
|
node.dottedName,
|
||
|
`L'unité de la règle est incompatible avec celle de sa formule`,
|
||
|
e
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cache._meta.contextRule.pop()
|
||
|
return {
|
||
|
...node,
|
||
|
...applicabilityEvaluation,
|
||
|
...(node.formule && { formule: evaluatedFormula }),
|
||
|
nodeValue,
|
||
|
unit,
|
||
|
isApplicable,
|
||
|
missingVariables
|
||
|
}
|
||
|
}
|